diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c150e81..3d87b44b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,17 @@ the file it came from. ### Added +- **A scanned multi-agent system comes out as its agents.** The repository scan reads + each agent the code defines, the tools it is handed and which agent hands work to + which (OpenAI Agents SDK handoffs and `as_tool`, LangGraph supervisors, swarms and + `StateGraph` agent nodes, CrewAI crews and delegation, AutoGen teams and group chats, + Google ADK `sub_agents`), statically. `scan --json` gains `agents` and `agent_edges`. + A repository with two or more agents is proposed as one draft per agent with its own + tools, joined by declared delegation edges, so the guardrail map shows sub-agents + ("From code, no calls yet") and the agent that calls each one, and "apply to + sub-agents" reaches them, before any traffic. The Proposed list groups a system under + its orchestrator with an "Add all" action. A re-scan updates the drafts in place and + never turns an agent someone added back into a proposal. - **`agentfox up`, the guided setup.** One command from a fresh install to a governed project: it scans the repository, asks where AgentFox runs (this machine; self-hosted, either started here with Docker Compose and a first operator, or an existing server; diff --git a/api/vendor/agentfox-0.4.0-py3-none-any.whl b/api/vendor/agentfox-0.4.0-py3-none-any.whl index 92210f4b..b7695077 100644 Binary files a/api/vendor/agentfox-0.4.0-py3-none-any.whl and b/api/vendor/agentfox-0.4.0-py3-none-any.whl differ diff --git a/dashboard/app/(product)/app/approvals/[id]/page.tsx b/dashboard/app/(product)/app/approvals/[id]/page.tsx index b6580c37..8e61d9e1 100644 --- a/dashboard/app/(product)/app/approvals/[id]/page.tsx +++ b/dashboard/app/(product)/app/approvals/[id]/page.tsx @@ -81,7 +81,7 @@ export default async function ApprovalDetail({ {agent.name || agent.slug} : "—"], + ["Agent", agent ? {agent.name || agent.slug} : "—"], ["Requested", when(a.requested_at)], pending ? ["Expires", ] diff --git a/dashboard/app/(product)/app/approvals/page.tsx b/dashboard/app/(product)/app/approvals/page.tsx index 372681a0..1457eacf 100644 --- a/dashboard/app/(product)/app/approvals/page.tsx +++ b/dashboard/app/(product)/app/approvals/page.tsx @@ -23,7 +23,15 @@ function source(s?: string) { export default async function Approvals({ searchParams }: { searchParams: Promise }) { const sp = await searchParams; const tab = sp.tab === "history" ? "history" : sp.tab === "handoffs" || sp.tab === "escalation" ? "handoffs" : "pending"; - const pending = await safeApi("/api/approvals?status=pending", { approvals: [] }); + const [all, agents] = await Promise.all([ + safeApi("/api/approvals?status=pending", { approvals: [] }), + safeApi("/api/agents", { agents: [] }), + ]); + // In a project's scope, only that project's requests. + const project = sp.agent ? (agents.agents || []).find((a: any) => a.slug === sp.agent || a.id === sp.agent) : null; + const mine = (list: any[]) => (sp.agent ? list.filter((a) => a.agent_id === (project?.id ?? sp.agent)) : list); + const pending = { approvals: mine(all.approvals || []) }; + const q = sp.agent ? `agent=${encodeURIComponent(sp.agent)}` : ""; return ( <> @@ -31,15 +39,15 @@ export default async function Approvals({ searchParams }: { searchParams: Promis {sp.review_error &&
{sp.review_error}
} {sp.review_notice &&
Request {sp.review_notice}.
} {tab === "pending" && } - {tab === "history" && } + {tab === "history" && } {tab === "handoffs" && } ); @@ -121,22 +129,22 @@ async function Pending({ approvals }: { approvals: any[] }) { ); } -async function History({ status }: { status?: string }) { +async function History({ status, agent, only }: { status?: string; agent?: string; only: (list: any[]) => any[] }) { const statuses = status && HISTORY.includes(status) ? [status] : HISTORY; const [lists, agents] = await Promise.all([ Promise.all(statuses.map((s) => safeApi(`/api/approvals?status=${s}`, { approvals: [] }))), safeApi("/api/agents", { agents: [] }), ]); const name: Record = Object.fromEntries((agents.agents || []).map((a: any) => [a.id, a])); - const rows = lists.flatMap((l) => l.approvals || []).sort((a, b) => String(b.requested_at).localeCompare(String(a.requested_at))); + const rows = only(lists.flatMap((l) => l.approvals || [])).sort((a, b) => String(b.requested_at).localeCompare(String(a.requested_at))); return ( <>
- All + All {HISTORY.map((s) => ( - + {s[0].toUpperCase() + s.slice(1)} ))} diff --git a/dashboard/app/(product)/app/compliance/controls/[key]/page.tsx b/dashboard/app/(product)/app/compliance/controls/[key]/page.tsx index 202ec1fe..d5c2a4df 100644 --- a/dashboard/app/(product)/app/compliance/controls/[key]/page.tsx +++ b/dashboard/app/(product)/app/compliance/controls/[key]/page.tsx @@ -21,7 +21,7 @@ export const dynamic = "force-dynamic"; */ /** Where the thing a control measures is set, for the controls a new workspace fails first. */ const FIX: Record = { - "NOM-DSC-03": { label: "Assign owners on Agents", href: "/app/agents" }, + "NOM-DSC-03": { label: "Assign owners on Agents", href: "/app/projects" }, "NOM-GOV-03": { label: "Assess risk in the risk register", href: "/app/compliance?tab=risk" }, "NOM-AUD-05": { label: "Set retention periods", href: "/app/compliance?tab=retention" }, "NOM-RTG-05": { label: "Turn on content checks", href: "/app/policies?tab=checks" }, diff --git a/dashboard/app/(product)/app/compliance/frameworks/[key]/page.tsx b/dashboard/app/(product)/app/compliance/frameworks/[key]/page.tsx index a33c47ec..05bbcaca 100644 --- a/dashboard/app/(product)/app/compliance/frameworks/[key]/page.tsx +++ b/dashboard/app/(product)/app/compliance/frameworks/[key]/page.tsx @@ -167,7 +167,7 @@ export default async function FrameworkPage({ params }: { params: Promise<{ key: o.agents_in_scope.slice(0, 6).map((slug: string, i: number) => ( {i > 0 && ", "} - {slug} + {slug} )) )} diff --git a/dashboard/app/(product)/app/compliance/page.tsx b/dashboard/app/(product)/app/compliance/page.tsx index 80467b4d..93a1a8f7 100644 --- a/dashboard/app/(product)/app/compliance/page.tsx +++ b/dashboard/app/(product)/app/compliance/page.tsx @@ -231,7 +231,7 @@ export default async function Compliance({ {register.register.map((r: any) => ( - {r.agent} + {r.agent} {r.risk_tier} {r.eu_ai_act_class || not assessed} diff --git a/dashboard/app/(product)/app/escalation/conversations/[sessionId]/page.tsx b/dashboard/app/(product)/app/escalation/conversations/[sessionId]/page.tsx index b5ffc33d..ca7ac1cc 100644 --- a/dashboard/app/(product)/app/escalation/conversations/[sessionId]/page.tsx +++ b/dashboard/app/(product)/app/escalation/conversations/[sessionId]/page.tsx @@ -61,7 +61,7 @@ export default async function EscalationConversation({ fire, rather than trusting a summary of it. {data.agent_slug && ( <> - {" "}Agent: {data.agent_name || data.agent_slug}. + {" "}Agent: {data.agent_name || data.agent_slug}. )}

diff --git a/dashboard/app/(product)/app/evals/page.tsx b/dashboard/app/(product)/app/evals/page.tsx index 38be1842..e122e6c8 100644 --- a/dashboard/app/(product)/app/evals/page.tsx +++ b/dashboard/app/(product)/app/evals/page.tsx @@ -420,7 +420,7 @@ export default async function Evals({ threshold below is each scorer’s built-in default, not a setting. The number you actually choose is a reliability objective, set per agent against a scorer, and visible on an agent’s{" "} - Activity tab. + Activity tab.

Scorers that ship

diff --git a/dashboard/app/(product)/app/features/page.tsx b/dashboard/app/(product)/app/features/page.tsx new file mode 100644 index 00000000..d4bc530d --- /dev/null +++ b/dashboard/app/(product)/app/features/page.tsx @@ -0,0 +1,34 @@ +import type { Metadata } from "next"; +import { appPageMetadata } from "@/lib/site"; +import { api, apiErrorProps } from "@/lib/product/api"; +import { ApiDown } from "@/components/ui"; +import { Header } from "@/components/kit"; +import { FeaturesEditor } from "@/components/product/features/FeaturesEditor"; + +export const metadata: Metadata = appPageMetadata("Features", "What every project gets, unless it changes it."); +export const dynamic = "force-dynamic"; + +/** + * The workspace's switches: each protection's level, whether projects may lower it, + * and which checks run. Every project starts from these and changes them on its own + * Features view. + */ +export default async function WorkspaceFeatures() { + let features: any; + try { + features = await api("/api/features"); + } catch (e: any) { + return ( + <> +
+ + + ); + } + return ( + <> +
+ + + ); +} diff --git a/dashboard/app/(product)/app/findings/[id]/page.tsx b/dashboard/app/(product)/app/findings/[id]/page.tsx index b4af785b..e192d6b4 100644 --- a/dashboard/app/(product)/app/findings/[id]/page.tsx +++ b/dashboard/app/(product)/app/findings/[id]/page.tsx @@ -69,7 +69,7 @@ export default async function IssueDetail({ /> {f.agent_slug} : f.subject_id || "—"], + ["Agent", f.agent_slug ? {f.agent_slug} : f.subject_id || "—"], ["Kind", type.label], ["Times", num(f.occurrences || 1)], ["First", ago(f.created_at)], diff --git a/dashboard/app/(product)/app/findings/page.tsx b/dashboard/app/(product)/app/findings/page.tsx index c70964cf..536dda65 100644 --- a/dashboard/app/(product)/app/findings/page.tsx +++ b/dashboard/app/(product)/app/findings/page.tsx @@ -99,7 +99,7 @@ export default async function Issues({ searchParams }: { searchParams: Promise{findingTypeInfo(f.type).label} - {f.agent_slug ? {f.agent_slug} : —} + {f.agent_slug ? {f.agent_slug} : —} {num(f.occurrences || 1)} {ago(f.last_seen_at || f.created_at)} diff --git a/dashboard/app/(product)/app/glossary/page.tsx b/dashboard/app/(product)/app/glossary/page.tsx index e63731d9..40179bff 100644 --- a/dashboard/app/(product)/app/glossary/page.tsx +++ b/dashboard/app/(product)/app/glossary/page.tsx @@ -188,7 +188,7 @@ export default function Glossary() { optionally a required human approval, and an expiry. Default deny: an agent with no grant for a tool cannot call it. Made with{" "} agentfox permit grant, explained on - the Agents page. + the Agents page. @@ -248,7 +248,7 @@ export default function Glossary() { capability grant that decides whether this agent may call this tool at all, and the impact tier declared on the tool that says how much damage it could do. See{" "} - Policies and Agents. + Policies and Projects. diff --git a/dashboard/app/(product)/app/layout.tsx b/dashboard/app/(product)/app/layout.tsx index 9b4d09cc..d478f686 100644 --- a/dashboard/app/(product)/app/layout.tsx +++ b/dashboard/app/(product)/app/layout.tsx @@ -7,7 +7,10 @@ import { Wordmark } from "@/components/product/Logo"; import { AccountMenu } from "@/components/product/AccountMenu"; import { NotificationsBell } from "@/components/product/NotificationsBell"; import { CommandSearch } from "@/components/product/CommandSearch"; -import { SideNav, type NavItem } from "@/components/product/SideNav"; +import { SideNav } from "@/components/product/SideNav"; +import { SetupBanner } from "@/components/product/SetupBanner"; +import { WORKSPACE_NAV } from "@/lib/product/nav"; +import { PROJECT_COOKIE } from "@/lib/product/project"; import { TopbarStats } from "@/components/product/TopbarStats"; import { getEdition } from "@/lib/product/edition"; @@ -32,42 +35,6 @@ import { getEdition } from "@/lib/product/edition"; * redirects a signed-out request for a private path to /login, so this is the * belt to that braces, not the gate. */ -/** - * The sidebar is the customer's jobs, in the order they reach for them: is - * anything wrong (Home), what is happening (Observe), what is broken (Issues), - * which agent (Agents), what is allowed (Policies), what needs me (Approvals), - * does it hold up (Test), show others (Reports). Setup sits at the bottom. - * - * A row lists the other pages it stands for, so it stays lit on all of them. - */ -const NAV: { group: string; items: NavItem[] }[] = [ - { - group: "", - items: [ - ["Home", "/app"], - ["Observe", "/app/observe", ["/app/traces"]], - ["Issues", "/app/findings"], - ["Agents", "/app/agents"], - ["Policies", "/app/policies"], - ["Map", "/app/map"], - ["Approvals", "/app/approvals"], - ["Test", "/app/test", ["/app/evals"]], - ["Reports", "/app/reports", ["/app/compliance", "/app/coverage"]], - ], - }, - { - group: "Setup", - items: [ - ["Get started", "/app/start"], - [ - "Settings", - "/app/settings", - ["/app/start?tab=connect", "/app/start?tab=tokens", "/app/sources", "/app/entitlement"], - ], - ], - }, -]; - /** * Destinations that used to be their own sidebar item before the nav was * consolidated into tabs — findable by name in search even though the @@ -97,7 +64,7 @@ const NAV_SEARCH_ONLY: { label: string; href: string; group: string; feature?: s { label: "Glossary", href: "/app/glossary", group: "Reference" }, ]; -const NAV_FLAT: { label: string; href: string; group: string; feature?: string }[] = NAV.flatMap( +const NAV_FLAT: { label: string; href: string; group: string; feature?: string }[] = WORKSPACE_NAV.flatMap( ({ group, items }) => items.map(([label, href]) => ({ label, href, group: group || "Home" })), ).concat(NAV_SEARCH_ONLY); /** @@ -122,8 +89,27 @@ async function TopbarData() { ); } +/** + * The sidebar, with the projects its switcher offers. In its own Suspense + * boundary for the same reason as the topbar; until the list answers, the + * sidebar is already there in the scope the cookie names. + */ +async function SideNavData({ initial }: { initial: string | null }) { + const { agents } = await safeApi("/api/agents", { agents: [] }); + const projects = (agents || []).map((a: any) => ({ slug: a.slug, name: a.name || a.slug, status: a.status })); + return ; +} + +/** The setup banner, with the workspace checklist it starts from. */ +async function SetupBannerData({ project }: { project: string | null }) { + const onboarding = await safeApi("/api/onboarding", null); + return ; +} + export default async function AppLayout({ children }: { children: React.ReactNode }) { - const signedIn = Boolean((await cookies()).get(SESSION_COOKIE)?.value); + const jar = await cookies(); + const signedIn = Boolean(jar.get(SESSION_COOKIE)?.value); + const project = jar.get(PROJECT_COOKIE)?.value || null; return (
@@ -136,7 +122,9 @@ export default async function AppLayout({ children }: { children: React.ReactNod - + }> + {signedIn ? : } +
@@ -147,7 +135,14 @@ export default async function AppLayout({ children }: { children: React.ReactNod
)} -
{children}
+
+ {signedIn && ( + + + + )} + {children} +
); diff --git a/dashboard/app/(product)/app/map/page.tsx b/dashboard/app/(product)/app/map/page.tsx deleted file mode 100644 index 79cdd7f7..00000000 --- a/dashboard/app/(product)/app/map/page.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type { Metadata } from "next"; -import Link from "next/link"; -import { appPageMetadata } from "@/lib/site"; -import { safeApi } from "@/lib/product/api"; -import { Card, Empty, Header, href } from "@/components/kit"; -import { GuardrailMapLazy as GuardrailMap } from "@/components/product/agent/GuardrailMapLazy"; - -export const metadata: Metadata = appPageMetadata("Guardrail map", "Which guardrails apply at each step of an agent's requests."); -export const dynamic = "force-dynamic"; - -/** Every agent's guardrails, one map at a time: pick an agent, see where each rule acts. */ -export default async function MapPage({ searchParams }: { searchParams: Promise> }) { - const sp = await searchParams; - const agents = await safeApi("/api/agents", { agents: [] }); - const live = (agents.agents || []).filter((a: any) => a.status !== "draft"); - const slug = sp.agent || live[0]?.slug; - const map = slug ? await safeApi(`/api/agents/${encodeURIComponent(slug)}/map`, null) : null; - return ( - <> -
- {live.length > 1 && ( -
- {live.map((a: any) => ( - - {a.name || a.slug} - - ))} -
- )} - {map ? : Connect an agent}>No agents yet.} - - ); -} diff --git a/dashboard/app/(product)/app/observe/page.tsx b/dashboard/app/(product)/app/observe/page.tsx index 2a2d6c1b..6bf7520b 100644 --- a/dashboard/app/(product)/app/observe/page.tsx +++ b/dashboard/app/(product)/app/observe/page.tsx @@ -120,8 +120,8 @@ async function Overview({ f }: { f: Filters }) { - All agents}> - `/app/agents/${encodeURIComponent(k)}`)} /> + All agents}> + `/app/projects/${encodeURIComponent(k)}`)} /> runsHref(f, { tool: k }))} empty="No tool calls" /> @@ -315,7 +315,7 @@ async function Security({ f, agents }: { f: Filters; agents: any }) { - + @@ -335,7 +335,7 @@ async function Security({ f, agents }: { f: Filters; agents: any }) { ({ key, label: key, value, segments: { held: r.held, blocked: r.blocked }, href: `/app/agents/${encodeURIComponent(key)}` }))} + rows={stopped(byAgent.rows).map(({ key, value, r }) => ({ key, label: key, value, segments: { held: r.held, blocked: r.blocked }, href: `/app/projects/${encodeURIComponent(key)}` }))} /> diff --git a/dashboard/app/(product)/app/page.tsx b/dashboard/app/(product)/app/page.tsx index 8e1895b2..aa515b60 100644 --- a/dashboard/app/(product)/app/page.tsx +++ b/dashboard/app/(product)/app/page.tsx @@ -135,7 +135,7 @@ export default async function Home({ searchParams }: { searchParams: Promise Observe}> runsHref(f, { start, end })} /> - All agents} flush> + All projects} flush> {live.slice(0, 6).map((a: any) => { @@ -145,7 +145,7 @@ export default async function Home({ searchParams }: { searchParams: Promise diff --git a/dashboard/app/(product)/app/policies/new/page.tsx b/dashboard/app/(product)/app/policies/new/page.tsx index 3f5836a9..72d03382 100644 --- a/dashboard/app/(product)/app/policies/new/page.tsx +++ b/dashboard/app/(product)/app/policies/new/page.tsx @@ -108,7 +108,7 @@ async function FromRun({ id }: { id: string }) { {tool}Require approval, add a limit, or remove it for {t.agent} - + Open access @@ -137,7 +137,7 @@ async function ToolAccess({ agent }: { agent?: string }) { {a.name || a.slug}{a.declared_tools?.length || 0} tools declared - + Edit access @@ -189,7 +189,7 @@ function Start({ agent }: { agent?: string }) {
Set up everything for {agent} in one go: protections, words, topics and the message users see. - + Protect {agent}
diff --git a/dashboard/app/(product)/app/policies/page.tsx b/dashboard/app/(product)/app/policies/page.tsx index c2cbc5b0..27623f95 100644 --- a/dashboard/app/(product)/app/policies/page.tsx +++ b/dashboard/app/(product)/app/policies/page.tsx @@ -318,7 +318,7 @@ async function PerformanceTab({ sp }: { sp: SP }) {
{live.map((a: any, i: number) => ( - + {cats.map((c) => ( ))} diff --git a/dashboard/app/(product)/app/policies/rules/[ruleId]/page.tsx b/dashboard/app/(product)/app/policies/rules/[ruleId]/page.tsx index 5f69c7c0..1f62b517 100644 --- a/dashboard/app/(product)/app/policies/rules/[ruleId]/page.tsx +++ b/dashboard/app/(product)/app/policies/rules/[ruleId]/page.tsx @@ -82,7 +82,7 @@ export default async function RulePage({ params, searchParams }: { params: Promi - ({ key: k, label: k, value: v as number, href: `/app/agents/${encodeURIComponent(k)}` }))} empty="No hits" /> + ({ key: k, label: k, value: v as number, href: `/app/projects/${encodeURIComponent(k)}` }))} empty="No hits" /> ({ key: k, label: k, value: v as number, href: runsHref({ range }, { rule: ruleId, tool: k }) }))} empty="Not tool-specific" /> diff --git a/dashboard/app/(product)/app/agents/[slug]/page.tsx b/dashboard/app/(product)/app/projects/[slug]/page.tsx similarity index 77% rename from dashboard/app/(product)/app/agents/[slug]/page.tsx rename to dashboard/app/(product)/app/projects/[slug]/page.tsx index e5c7a677..d0d67500 100644 --- a/dashboard/app/(product)/app/agents/[slug]/page.tsx +++ b/dashboard/app/(product)/app/projects/[slug]/page.tsx @@ -5,7 +5,6 @@ import { ApiError, api, apiErrorProps, safeApi } from "@/lib/product/api"; import { ApiDown, NotFound } from "@/components/ui"; import { ActionPill, - BarList, Card, Empty, Grid, @@ -26,41 +25,44 @@ import { Act } from "@/components/kit/Act"; import { FilterBar } from "@/components/kit/FilterBar"; import { RunsTable } from "@/components/kit/RunsTable"; import { AgentMap } from "@/components/product/AgentMap"; +import { Modal } from "@/components/product/Modal"; import { AccessEditor } from "@/components/product/agent/AccessEditor"; +import { FeaturesEditor } from "@/components/product/features/FeaturesEditor"; import { RANGE_DAYS, metricsQs, runsHref, verdictsFor, type Filters } from "@/lib/product/observe"; import { CATEGORIES, categoryLabel, rangeOf, ruleCategory, ruleTitle, sentence } from "@/lib/product/vocab"; import { ensureRange } from "@/lib/product/range"; import { GuardrailMapLazy as GuardrailMap } from "@/components/product/agent/GuardrailMapLazy"; import { RangeProvider } from "@/components/kit/RangeContext"; +import { PROJECT_VIEWS, projectHref, projectView } from "@/lib/product/project"; -export const metadata: Metadata = appPageMetadata("Agent"); +export const metadata: Metadata = appPageMetadata("Project"); export const dynamic = "force-dynamic"; -const TABS = [ - { key: "overview", label: "Overview" }, - { key: "map", label: "Map" }, - { key: "access", label: "Access" }, - { key: "rules", label: "Rules" }, - { key: "activity", label: "Activity" }, +/** Activity's parts, under `?view=activity&tab=`. The old agent tabs of the same names land here. */ +const ACTIVITY_TABS = [ + { key: "runs", label: "Runs" }, + { key: "rules", label: "Guardrails fired" }, { key: "quality", label: "Quality" }, - { key: "settings", label: "Settings" }, ]; -/** Old tab names, so bookmarked links still land somewhere sensible. */ -const LEGACY: Record = { permissions: "rules", registration: "settings" }; - type SP = Record; -export default async function AgentDetail({ params, searchParams }: { params: Promise<{ slug: string }>; searchParams: Promise }) { +/** + * One project. Five views, chosen in the sidebar: the canvas (its guardrails drawn + * where they act, with the project's state in the inspector when nothing on the + * canvas is selected), its activity, its access, the features it changed from the + * workspace's, its settings. + */ +export default async function ProjectDetail({ params, searchParams }: { params: Promise<{ slug: string }>; searchParams: Promise }) { const { slug } = await params; const sp = await searchParams; - const requested = LEGACY[sp.tab || ""] || sp.tab; - const tab = TABS.some((t) => t.key === requested) ? requested! : "overview"; + const view = projectView(sp); + const sub = view === "activity" ? (ACTIVITY_TABS.some((t) => t.key === sp.tab) ? sp.tab! : sp.tab === "permissions" ? "rules" : "runs") : null; // One round of requests, in parallel: the range (when the URL names none), the - // agent, and its kill-switch state. - const needsRange = ["overview", "rules", "activity", "quality"].includes(tab); + // project, and its kill-switch state. + const needsRange = view === "canvas" || view === "activity"; const [, outcome, controls] = await Promise.all([ - needsRange ? ensureRange(`/app/agents/${encodeURIComponent(slug)}`, sp, slug) : Promise.resolve(), + needsRange ? ensureRange(`/app/projects/${encodeURIComponent(slug)}`, sp, slug) : Promise.resolve(), api(`/api/agents/${encodeURIComponent(slug)}/posture`).then( (posture: any) => ({ posture, error: null as any }), (error: any) => ({ posture: null, error }), @@ -73,9 +75,9 @@ export default async function AgentDetail({ params, searchParams }: { params: Pr const e = outcome.error; return ( <> -
+
{e instanceof ApiError && e.status === 404 ? ( - + ) : ( )} @@ -87,17 +89,23 @@ export default async function AgentDetail({ params, searchParams }: { params: Pr const control = (controls.controls || []).find((c: any) => c.agent === a.slug); const state = control?.state || "active"; const breakerOpen = state === "active" && control?.breaker?.state === "open"; - const tabHref = (k: string) => href(`/app/agents/${encodeURIComponent(slug)}`, { tab: k === "overview" ? undefined : k, range: sp.range }); + const label = PROJECT_VIEWS.find((v) => v.key === view)!.label; return ( <>
+ {a.name || a.slug} + {view !== "canvas" && / {label}} + + } meta={ <> - {breakerOpen ? ( + {a.status === "draft" ? ( + Proposed + ) : breakerOpen ? ( Paused by breaker ) : state === "active" ? ( Active @@ -105,109 +113,159 @@ export default async function AgentDetail({ params, searchParams }: { params: Pr {state === "killed" ? "Stopped" : "Paused"} )} {a.risk_tier === "high" && High risk} - {!a.registered && Unregistered} + {!a.registered && a.status !== "draft" && Unregistered} {a.is_seed && Sample} } actions={ <> - + Protect
} /> - Assign], - ["Team", a.owner_team || "—"], - ["Environment", a.environment], - ["Framework", a.framework || "—"], - ["Last active", ago(a.last_seen_at)], - ]} - /> -
{sp.review_error &&
{sp.review_error}
} {sp.review_notice &&
Done: {sp.review_notice}.
} - ({ ...t, href: tabHref(t.key) }))} active={tab} /> - {tab === "overview" && } - {tab === "map" && } - {tab === "access" && } - {tab === "rules" && } - {tab === "activity" && } - {tab === "quality" && } - {tab === "settings" && } + {view === "canvas" && } + {view === "activity" && ( + <> + ({ ...t, href: projectHref(slug, "activity", { tab: t.key === "runs" ? undefined : t.key, range: sp.range }) }))} + active={sub!} + /> + {sub === "runs" && } + {sub === "rules" && } + {sub === "quality" && } + + )} + {view === "access" && } + {view === "features" && } + {view === "settings" && ( + <> + +
+ + + )} ); } -async function Overview({ f, posture }: { f: Filters; posture: any }) { - const [s, rules, lineage] = await Promise.all([ +/** The workspace's switches as this project has them, with its own changes. */ +async function ProjectFeatures({ slug }: { slug: string }) { + const features = await safeApi(`/api/agents/${encodeURIComponent(slug)}/features`, null); + return features ? : Features could not be loaded.; +} + +/** + * The canvas: the guardrail map, and in its inspector, when nothing is selected, + * the project at a glance: its traffic, its open issues, the guardrails that fired + * most, who owns it. The rest is a click away in the sidebar. + */ +async function Canvas({ f, posture }: { f: Filters; posture: any }) { + const slug = f.agent!; + const [map, s, rules, lineage] = await Promise.all([ + safeApi(`/api/agents/${encodeURIComponent(slug)}/map`, null), safeApi(`/api/metrics/summary?${metricsQs(f)}`, null), safeApi(`/api/metrics/rules?${metricsQs(f)}`, { rules: [] }), - safeApi(`/api/agents/${encodeURIComponent(f.agent!)}/lineage?depth=2`, { nodes: [], links: [], blast_radius: 0 }), + safeApi(`/api/agents/${encodeURIComponent(slug)}/lineage?depth=2`, { nodes: [], links: [], blast_radius: 0 }), ]); + const reach: number = lineage.blast_radius ?? 0; + const a = posture.agent; const t = s?.totals || {}; - const p = s?.previous || {}; - const b = s?.buckets || []; const findings: any[] = posture.open_findings || []; - return ( - <> - - - x.allowed + x.masked + x.held + x.blocked)} /> - x.blocked)} /> - x.held)} /> - - - {s && ( - - runsHref(f, { start, end })} /> - - )} - - - ({ - key: r.rule_id, - label: ruleTitle(r.rule_id), - href: `/app/policies/rules/${encodeURIComponent(r.rule_id)}`, - value: r.fires, - note: r.watched && !r.enforced ? "watching" : undefined, - }))} - /> - - All} flush> - {findings.length ? ( -
    - {findings.slice(0, 6).map((x) => ( -
  • -
    - {sentence(x.title)} -
    - -
  • - ))} -
- ) : ( - No open issues. + const fired: any[] = (rules.rules || []).slice(0, 4); + const stat = (label: string, value: number, to: string, bad = false) => ( + + {num(value || 0)} + {label} + + ); + const summary = ( +
+
+ {a.name || a.slug} + Last {RANGE_DAYS[f.range]} days · nothing selected +
+
+ {stat("Requests", t.requests, runsHref(f))} + {stat("Blocked", t.blocked, runsHref(f, { outcome: "blocked" }))} + {stat("Held for a person", t.held, runsHref(f, { outcome: "held" }))} + {stat("Errors", t.errors, runsHref(f, { errors: true }), true)} +
+

Open issues

+ {findings.length ? ( +
    + {findings.slice(0, 3).map((x) => ( +
  • + {sentence(x.title)} + +
  • + ))} + {findings.length > 3 && ( +
  • + All {findings.length} issues → +
  • )} - - +
+ ) : ( +

None open.

+ )} + {fired.length > 0 && ( + <> +

Fired most

+
    + {fired.map((r) => ( +
  • + {ruleTitle(r.rule_id)} + {num(r.fires)} +
  • + ))} +
+ + )} {lineage.links?.length > 0 && ( - + <> +

Lineage

+

+ Blast radius {reach}: reachable within two hops, how far a bad decision here could travel. +

+ + + + )} - +

About

+
+
Owner
+
{a.owner_email || Assign}
+
Environment
+
{a.environment || "—"}
+
Framework
+
{a.framework || "—"}
+
Last active
+
{ago(a.last_seen_at)}
+
+
); + return map ? : The canvas could not be loaded.; } async function Access({ slug, prefill }: { slug: string; prefill?: string }) { @@ -304,12 +362,12 @@ async function Rules({ f, slug }: { f: Filters; slug: string }) {
- Add rule for this agent + Add a guardrail
{changes.length > 0 && ( - +
- {a.name || a.slug} + {a.name || a.slug} {!a.registered && Unregistered}
{a.name || a.slug}{a.name || a.slug}{cell(effective[i].rules || [], c.key)}
{changes.map((c) => { @@ -321,7 +379,7 @@ async function Rules({ f, slug }: { f: Filters; slug: string }) { {ruleTitle(c.rule_id)} - {c.kind === "added" ? "Only on this agent" : c.has_effect ? "Workspace default replaced for this agent" : "Looser than the workspace rule, which still applies"} + {c.kind === "added" ? "Only on this project" : c.has_effect ? "Workspace default replaced for this project" : "Looser than the workspace rule, which still applies"} @@ -331,7 +389,7 @@ async function Rules({ f, slug }: { f: Filters; slug: string }) { url={`/api/agents/${encodeURIComponent(slug)}/rules/${encodeURIComponent(c.rule_id)}`} method="DELETE" className="k-btn-ghost" - confirm={c.kind === "added" ? "Remove this rule from this agent?" : "Reset this rule to the workspace default for this agent?"} + confirm={c.kind === "added" ? "Remove this rule from this project?" : "Reset this rule to the workspace default for this project?"} done="Done" > {c.kind === "added" ? "Remove" : "Reset to workspace default"} @@ -357,9 +415,9 @@ async function Rules({ f, slug }: { f: Filters; slug: string }) { {ruleTitle(r.rule_id)} {r.changed_for_agent ? ( - Changed for this agent + Changed for this project ) : r.added_for_agent || (r.source && !String(r.source).startsWith("org:")) ? ( - Custom for this agent + Custom for this project ) : null} @@ -382,12 +440,18 @@ async function Activity({ f, outcome }: { f: Filters; outcome?: string }) { const v = verdictsFor(outcome); if (v) qs.set("verdict", v); if (outcome === "errors") qs.set("errors", "true"); - const runs = await safeApi(`/api/traces?${qs}`, { traces: [] }); + const [runs, s] = await Promise.all([ + safeApi(`/api/traces?${qs}`, { traces: [] }), + safeApi(`/api/metrics/summary?${metricsQs(f)}`, null), + ]); + const t = s?.totals || {}; + const p = s?.previous || {}; + const b: any[] = s?.buckets || []; const chip = (key: string, label: string) => ( {label} @@ -403,6 +467,18 @@ async function Activity({ f, outcome }: { f: Filters; outcome?: string }) { {chip("errors", "Errors")} + {/* The trend over the range, against the one before it; a window opens its runs. */} + + x.allowed + x.masked + x.held + x.blocked)} /> + x.blocked)} /> + x.held)} /> + + + {s && ( + + runsHref(f, { start, end })} /> + + )} @@ -424,7 +500,7 @@ async function Quality({ f }: { f: Filters }) { ); } - const agentHref = (tab: string) => href(`/app/agents/${encodeURIComponent(slug)}`, { tab, range: f.range === "7d" ? undefined : f.range }); + const agentHref = (tab: string) => projectHref(slug, "activity", { tab, range: f.range === "7d" ? undefined : f.range }); const wrong = q.wrong_answers; const failed = q.failed_steps; const runs: any[] = q.evals.runs || []; @@ -451,7 +527,7 @@ async function Quality({ f }: { f: Filters }) { value={q.checked ? pct(wrong.rate) : "Not checked"} tone={wrong.requests ? "bad" : undefined} spark={q.trend.wrong_answers} - hint={q.checked ? rateHint(wrong.requests) : "No rule checks answers for this agent."} + hint={q.checked ? rateHint(wrong.requests) : "No rule checks answers for this project."} href="#wrong" /> @@ -502,9 +578,9 @@ async function Quality({ f }: { f: Filters }) {
{c.default ? Workspace{what(c.default)} : null}
) : q.checked ? ( - Set what it may answer}>No wrong answers caught in this period. + Set what it may answer}>No wrong answers caught in this period. ) : ( - Set what it may answer}>Nothing checks this agent's answers yet. + Set what it may answer}>Nothing checks this project's answers yet. )}
@@ -534,7 +610,7 @@ async function Quality({ f }: { f: Filters }) { ) : q.requests ? ( No failed tool or model calls in this period. ) : ( - Activity}>No requests in this period. + Activity}>No requests in this period. )}
@@ -588,7 +664,7 @@ async function Quality({ f }: { f: Filters }) { ))} ) : ( - Open a run}>No feedback yet. Label a run from its detail page. + Open a run}>No feedback yet. Label a run from its detail page. )}
@@ -686,7 +762,7 @@ async function Settings({ a, state }: { a: any; state: string }) {
- +
{state === "active" ? Active : {state}}
@@ -714,7 +790,7 @@ const BREAKER_STATE: Record = { }; /** - * The circuit breaker: when a burst of this agent's calls is blocked, pause it (or + * The circuit breaker: when a burst of this project's calls is blocked, pause it (or * only raise an issue) without anyone watching, then let calls through again once a * few pass after the cool-down. */ @@ -725,7 +801,7 @@ function Breaker({ slug, b }: { slug: string; b: any }) { return (
{label} @@ -799,8 +875,3 @@ const QUESTION_HINT: Record = { opinion: "Recommendations: \"Which plan is best for me?\"", procedure: "Steps to follow: \"How do I update my address?\"", }; - -async function MapTab({ slug }: { slug: string }) { - const map = await safeApi(`/api/agents/${encodeURIComponent(slug)}/map`, null); - return map ? : The map could not be loaded.; -} diff --git a/dashboard/app/(product)/app/agents/[slug]/protect/page.tsx b/dashboard/app/(product)/app/projects/[slug]/protect/page.tsx similarity index 90% rename from dashboard/app/(product)/app/agents/[slug]/protect/page.tsx rename to dashboard/app/(product)/app/projects/[slug]/protect/page.tsx index b8945284..eb962c23 100644 --- a/dashboard/app/(product)/app/agents/[slug]/protect/page.tsx +++ b/dashboard/app/(product)/app/projects/[slug]/protect/page.tsx @@ -18,7 +18,7 @@ export default async function Protect({ params }: { params: Promise<{ slug: stri const name = posture?.agent?.name || slug; return ( <> -
+
{state ? ( ; -export default async function Agents({ searchParams }: { searchParams: Promise }) { +export default async function Projects({ searchParams }: { searchParams: Promise }) { const sp = await searchParams; let agents: any; try { @@ -21,7 +22,7 @@ export default async function Agents({ searchParams }: { searchParams: Promise -
+
); @@ -34,6 +35,9 @@ export default async function Agents({ searchParams }: { searchParams: Promise a.status === "draft"); + // A scanned multi-agent system is proposed as one agent per agent; shown together, + // with the agents each one hands work to indented under it. + const proposedGroups = groupProposed(proposed); const live = all.filter((a) => a.status !== "draft"); const attention = live.filter((a) => !a.registered || !a.owner_email); const tab = sp.tab === "attention" ? "attention" : sp.tab === "proposed" ? "proposed" : "all"; @@ -46,13 +50,13 @@ export default async function Agents({ searchParams }: { searchParams: Promise -
} /> +
} /> {sp.review_error &&
{sp.review_error}
} @@ -61,22 +65,44 @@ export default async function Agents({ searchParams }: { searchParams: Promise {proposed.length ? (
    - {proposed.map((a) => ( -
  • -
    - {a.name || a.slug} - Found by a repo scan{a.framework ? ` · ${a.framework}` : ""} -
    -
    - - - -
    - -
    -
    -
  • - ))} + {proposedGroups.flatMap((g) => { + const system = g.members.length > 1; + const rows = g.members.map(({ agent: a, depth, relation }) => ( +
  • +
    + {a.name || a.slug} + + {depth ? memberRelation(relation) : "Found by a repo scan"} + {a.framework ? ` · ${a.framework}` : ""} + {(a.declared_tools || []).length ? ` · ${(a.declared_tools || []).length} tool${(a.declared_tools || []).length === 1 ? "" : "s"}` : ""} + +
    +
    +
    + +
    +
    + +
    +
    +
  • + )); + if (!system) return rows; + return [ +
  • +
    + Multi-agent system · {g.members.length} projects + The agents its code hands work to are listed under it +
    +
    +
    + +
    +
    +
  • , + ...rows, + ]; + })}
) : ( Scan a repo}>Nothing proposed. @@ -88,7 +114,7 @@ export default async function Agents({ searchParams }: { searchParams: Promise - Agent + Project Owner Risk Last {RANGE_WORDS[range]} @@ -106,7 +132,7 @@ export default async function Agents({ searchParams }: { searchParams: Promise - + {a.name || a.slug} {!a.registered && Unregistered} @@ -117,7 +143,7 @@ export default async function Agents({ searchParams }: { searchParams: Promise{a.owner_email} ) : ( - Assign + Assign )} @@ -139,7 +165,7 @@ export default async function Agents({ searchParams }: { searchParams: Promise ) : ( Connect an agent}> - {tab === "attention" ? "Every agent is registered and owned." : "No agents yet."} + {tab === "attention" ? "Every project is registered and owned." : "No projects yet."} )} @@ -148,9 +174,9 @@ export default async function Agents({ searchParams }: { searchParams: Promise +
@@ -158,7 +184,7 @@ function RegisterAgent() {
- +
diff --git a/dashboard/app/(product)/app/reports/page.tsx b/dashboard/app/(product)/app/reports/page.tsx index b5411a02..edb0d13c 100644 --- a/dashboard/app/(product)/app/reports/page.tsx +++ b/dashboard/app/(product)/app/reports/page.tsx @@ -63,8 +63,8 @@ async function Summary() { return ( <> - - + + diff --git a/dashboard/app/(product)/app/start/page.tsx b/dashboard/app/(product)/app/start/page.tsx index 0f7d89c4..c8532f7c 100644 --- a/dashboard/app/(product)/app/start/page.tsx +++ b/dashboard/app/(product)/app/start/page.tsx @@ -109,7 +109,7 @@ async function Checklist({ agentParam, gateway }: { agentParam?: string; gateway }; const agents: string[] = onboarding.agents || []; const agent = agentParam && agents.includes(agentParam) ? agentParam : agents[0] || null; - const agentHref = (suffix = "") => (agent ? `/app/agents/${encodeURIComponent(agent)}${suffix}` : "/app/agents"); + const agentHref = (suffix = "") => (agent ? `/app/projects/${encodeURIComponent(agent)}${suffix}` : "/app/projects"); const body: Record = { agent: , @@ -385,7 +385,7 @@ function ScanResult({ title, scan }: { title: string; scan: any }) { {scan.summary?.policies_proposed?.length === 1 ? "policy" : "policies"}
- + Review agents diff --git a/dashboard/app/(product)/app/test/page.tsx b/dashboard/app/(product)/app/test/page.tsx index fb3a3769..a468114a 100644 --- a/dashboard/app/(product)/app/test/page.tsx +++ b/dashboard/app/(product)/app/test/page.tsx @@ -311,7 +311,7 @@ async function Reliability({ agents }: { agents: any[] }) { {slos.slos.map((s: any) => ( - {s.agent} + {s.agent} {s.scorer}{s.objective && {s.objective}} {s.target != null ? `${Math.round(s.target * 100)}%` : "—"} {s.attainment != null ? `${Math.round(s.attainment * 100)}%` : No data} diff --git a/dashboard/app/(product)/app/traces/[id]/page.tsx b/dashboard/app/(product)/app/traces/[id]/page.tsx index 9ce17bac..0e427a34 100644 --- a/dashboard/app/(product)/app/traces/[id]/page.tsx +++ b/dashboard/app/(product)/app/traces/[id]/page.tsx @@ -112,7 +112,7 @@ export default async function RunDetail({ actions={ <> {(outcome === "blocked" || outcome === "held") && denied && t.agent && ( - + Allow this tool )} @@ -129,7 +129,7 @@ export default async function RunDetail({ /> {t.agent_name || t.agent} : "—"], + ["Agent", t.agent ? {t.agent_name || t.agent} : "—"], ["When", when(t.started_at)], ["Environment", t.environment], ["Checks", `${Math.round(duration)} ms`], diff --git a/dashboard/app/api/agents/[slug]/approve-system/route.ts b/dashboard/app/api/agents/[slug]/approve-system/route.ts new file mode 100644 index 00000000..006ed69c --- /dev/null +++ b/dashboard/app/api/agents/[slug]/approve-system/route.ts @@ -0,0 +1,12 @@ +import { NextRequest } from "next/server"; +import { proxyReviewAction } from "@/lib/product/proxy"; + +export const dynamic = "force-dynamic"; + +// Adds a proposed agent and every proposed agent it hands work to, in one step: the +// "Add all" button on a multi-agent system in the Proposed list. Like the sibling +// approve route, the value in `[slug]` is the draft agent's *id*. +export async function POST(req: NextRequest, { params }: { params: Promise<{ slug: string }> }) { + const { slug: agentId } = await params; + return proxyReviewAction(req, `/api/agents/${agentId}/approve-system`, "/app/projects?tab=proposed"); +} diff --git a/dashboard/app/api/agents/[slug]/approve/route.ts b/dashboard/app/api/agents/[slug]/approve/route.ts index a221fbb8..774f294d 100644 --- a/dashboard/app/api/agents/[slug]/approve/route.ts +++ b/dashboard/app/api/agents/[slug]/approve/route.ts @@ -8,5 +8,5 @@ export const dynamic = "force-dynamic"; // actually lands here is the draft agent's *id*, per the caller in agents/page.tsx. export async function POST(req: NextRequest, { params }: { params: Promise<{ slug: string }> }) { const { slug: agentId } = await params; - return proxyReviewAction(req, `/api/agents/${agentId}/approve`, "/app/agents"); + return proxyReviewAction(req, `/api/agents/${agentId}/approve`, "/app/projects"); } diff --git a/dashboard/app/api/agents/[slug]/boundary/route.ts b/dashboard/app/api/agents/[slug]/boundary/route.ts index 5abaa38a..dd4013b0 100644 --- a/dashboard/app/api/agents/[slug]/boundary/route.ts +++ b/dashboard/app/api/agents/[slug]/boundary/route.ts @@ -33,5 +33,5 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ slu mode: (form.get("mode") as string) || "observe", }; - return proxyCustomBody(req, "PUT", "/api/answerability/boundary", `/app/agents/${slug}?tab=access`, body); + return proxyCustomBody(req, "PUT", "/api/answerability/boundary", `/app/projects/${slug}?view=access`, body); } diff --git a/dashboard/app/api/agents/[slug]/breaker/route.ts b/dashboard/app/api/agents/[slug]/breaker/route.ts index a2586c7e..227fb39d 100644 --- a/dashboard/app/api/agents/[slug]/breaker/route.ts +++ b/dashboard/app/api/agents/[slug]/breaker/route.ts @@ -9,7 +9,7 @@ import { proxyCustomBody } from "@/lib/product/proxy"; export async function POST(req: NextRequest, { params }: { params: Promise<{ slug: string }> }) { const { slug } = await params; const form = await req.formData(); - const back = `/app/agents/${encodeURIComponent(slug)}?tab=settings`; + const back = `/app/projects/${encodeURIComponent(slug)}?view=settings`; const base = `/api/agents/${encodeURIComponent(slug)}/breaker`; if (form.get("action") === "reset") { return proxyCustomBody(req, "POST", `${base}/reset`, back, { reason: String(form.get("reason") || "") }, { diff --git a/dashboard/app/api/agents/[slug]/control/route.ts b/dashboard/app/api/agents/[slug]/control/route.ts index 0a74d651..df3c52f8 100644 --- a/dashboard/app/api/agents/[slug]/control/route.ts +++ b/dashboard/app/api/agents/[slug]/control/route.ts @@ -15,7 +15,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ slu const form = await req.formData(); const action = String(form.get("action") || ""); if (!ACTIONS.has(action)) { - const target = new URL(`/app/agents/${slug}?tab=settings`, requestOrigin(req)); + const target = new URL(`/app/projects/${slug}?view=settings`, requestOrigin(req)); target.searchParams.set("review_error", "invalid control action"); return NextResponse.redirect(target); } @@ -25,7 +25,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ slu req, "POST", `/api/agents/${encodeURIComponent(slug)}/${action}`, - `/app/agents/${slug}?tab=settings`, + `/app/projects/${slug}?view=settings`, { reason }, { successNotice: `agent ${PAST[action]}` }, ); diff --git a/dashboard/app/api/agents/[slug]/features/checks/[key]/route.ts b/dashboard/app/api/agents/[slug]/features/checks/[key]/route.ts new file mode 100644 index 00000000..0c43feab --- /dev/null +++ b/dashboard/app/api/agents/[slug]/features/checks/[key]/route.ts @@ -0,0 +1,9 @@ +import { proxyJson } from "@/lib/product/proxy"; + +export const dynamic = "force-dynamic"; + +/** This project goes back to the workspace's setting. */ +export async function DELETE(_req: Request, { params }: { params: Promise<{ slug: string; key: string }> }) { + const { slug, key } = await params; + return proxyJson(`/api/agents/${encodeURIComponent(slug)}/features/checks/${encodeURIComponent(key)}`, "DELETE"); +} diff --git a/dashboard/app/api/agents/[slug]/features/protections/[key]/route.ts b/dashboard/app/api/agents/[slug]/features/protections/[key]/route.ts new file mode 100644 index 00000000..8fb807a5 --- /dev/null +++ b/dashboard/app/api/agents/[slug]/features/protections/[key]/route.ts @@ -0,0 +1,9 @@ +import { proxyJson } from "@/lib/product/proxy"; + +export const dynamic = "force-dynamic"; + +/** This project goes back to the workspace's setting. */ +export async function DELETE(_req: Request, { params }: { params: Promise<{ slug: string; key: string }> }) { + const { slug, key } = await params; + return proxyJson(`/api/agents/${encodeURIComponent(slug)}/features/protections/${encodeURIComponent(key)}`, "DELETE"); +} diff --git a/dashboard/app/api/agents/[slug]/owner/route.ts b/dashboard/app/api/agents/[slug]/owner/route.ts index 7ba43219..fe21899c 100644 --- a/dashboard/app/api/agents/[slug]/owner/route.ts +++ b/dashboard/app/api/agents/[slug]/owner/route.ts @@ -5,5 +5,5 @@ export const dynamic = "force-dynamic"; export async function POST(req: NextRequest, { params }: { params: Promise<{ slug: string }> }) { const { slug } = await params; - return proxyFormPatch(req, `/api/agents/${slug}`, `/app/agents/${slug}?tab=settings`); + return proxyFormPatch(req, `/api/agents/${slug}`, `/app/projects/${slug}?view=settings`); } diff --git a/dashboard/app/api/agents/[slug]/reject/route.ts b/dashboard/app/api/agents/[slug]/reject/route.ts index 8b33663e..261d2d76 100644 --- a/dashboard/app/api/agents/[slug]/reject/route.ts +++ b/dashboard/app/api/agents/[slug]/reject/route.ts @@ -8,5 +8,5 @@ export const dynamic = "force-dynamic"; // actually lands here is the draft agent's *id*, per the caller in agents/page.tsx. export async function POST(req: NextRequest, { params }: { params: Promise<{ slug: string }> }) { const { slug: agentId } = await params; - return proxyReviewAction(req, `/api/agents/${agentId}/reject`, "/app/agents"); + return proxyReviewAction(req, `/api/agents/${agentId}/reject`, "/app/projects"); } diff --git a/dashboard/app/api/agents/[slug]/setup/route.ts b/dashboard/app/api/agents/[slug]/setup/route.ts new file mode 100644 index 00000000..ff262fd4 --- /dev/null +++ b/dashboard/app/api/agents/[slug]/setup/route.ts @@ -0,0 +1,9 @@ +import { proxyJson } from "@/lib/product/proxy"; + +export const dynamic = "force-dynamic"; + +/** What one project still needs, for its setup banner; polled while it waits for traffic. */ +export async function GET(_req: Request, { params }: { params: Promise<{ slug: string }> }) { + const { slug } = await params; + return proxyJson(`/api/agents/${encodeURIComponent(slug)}/setup`, "GET", undefined, { cache: "no-store" }); +} diff --git a/dashboard/app/api/agents/route.ts b/dashboard/app/api/agents/route.ts index aab619af..71e9d677 100644 --- a/dashboard/app/api/agents/route.ts +++ b/dashboard/app/api/agents/route.ts @@ -9,5 +9,5 @@ export const dynamic = "force-dynamic"; * no way onto this page at all otherwise. */ export async function POST(req: NextRequest) { - return proxyFormPost(req, "/api/agents", "/app/agents"); + return proxyFormPost(req, "/api/agents", "/app/projects"); } diff --git a/dashboard/app/api/features/checks/[key]/route.ts b/dashboard/app/api/features/checks/[key]/route.ts new file mode 100644 index 00000000..87300f77 --- /dev/null +++ b/dashboard/app/api/features/checks/[key]/route.ts @@ -0,0 +1,15 @@ +import { NextRequest } from "next/server"; +import { proxyJson } from "@/lib/product/proxy"; + +export const dynamic = "force-dynamic"; + +/** Switch a check on or off for every project, or (with `agent`) for one. */ +export async function POST(req: NextRequest, { params }: { params: Promise<{ key: string }> }) { + const { key } = await params; + const { enabled, agent, reason } = await req.json(); + return proxyJson(`/api/features/checks/${encodeURIComponent(key)}`, "POST", { + enabled: Boolean(enabled), + agent: String(agent || ""), + reason: String(reason || ""), + }); +} diff --git a/dashboard/app/api/features/protections/[key]/route.ts b/dashboard/app/api/features/protections/[key]/route.ts new file mode 100644 index 00000000..48b6527f --- /dev/null +++ b/dashboard/app/api/features/protections/[key]/route.ts @@ -0,0 +1,16 @@ +import { NextRequest } from "next/server"; +import { proxyJson } from "@/lib/product/proxy"; + +export const dynamic = "force-dynamic"; + +/** Set a protection for every project, or (with `agent`) for one. */ +export async function POST(req: NextRequest, { params }: { params: Promise<{ key: string }> }) { + const { key } = await params; + const { level, agent, open_to_projects, reason } = await req.json(); + return proxyJson(`/api/features/protections/${encodeURIComponent(key)}`, "POST", { + level: level ?? null, + agent: String(agent || ""), + open_to_projects: typeof open_to_projects === "boolean" ? open_to_projects : null, + reason: String(reason || ""), + }); +} diff --git a/dashboard/app/docs/app/access-and-sources/page.tsx b/dashboard/app/docs/app/access-and-sources/page.tsx index 40a8d1c9..92f7ff29 100644 --- a/dashboard/app/docs/app/access-and-sources/page.tsx +++ b/dashboard/app/docs/app/access-and-sources/page.tsx @@ -236,7 +236,7 @@ source changes. diff --git a/dashboard/app/docs/app/agents/page.tsx b/dashboard/app/docs/app/agents/page.tsx deleted file mode 100644 index 62de0b07..00000000 --- a/dashboard/app/docs/app/agents/page.tsx +++ /dev/null @@ -1,320 +0,0 @@ -import type { Metadata } from "next"; -import Link from "next/link"; - -import { Callout, Code, InTheApp, NextSteps, Output, TaskTable } from "@/components/docs/blocks"; -import { publicPageMetadata } from "@/lib/site"; - -export const metadata: Metadata = publicPageMetadata({ - title: "Agents", - description: - "The agent register and the agent page: owners, risk classification, knowledge boundary, kill switch, effective policy and lineage.", - path: "/docs/app/agents", -}); - -export default function Page() { - return ( -
-

Web app

-

Agents

-

- The register of every agent in the workspace, and one page per agent for deciding - whether it is safe to leave running as it is. -

- Agents - -

When to use this

-
    -
  • To approve or reject agents a scan proposed.
  • -
  • To find agents sending traffic that nobody registered, or that nobody owns.
  • -
  • To stop an agent now, and to see what policy actually applies to it.
  • -
- -

The agents list

-

- /app/agents, built from GET /api/agents and{" "} - GET /api/discovery/shadow. Top to bottom: -

-
    -
  • - A strip of counts: agents, registered, unregistered, without an - owner, tools, lineage edges. -
  • -
  • - Pending review: draft agents proposed by a repository or hosted-API - scan, with Approve and Reject. A draft is inert: - approving makes it active (POST /api/agents/{id}/approve), - rejecting discards it. Both are written to the audit chain. -
  • -
  • - Unregistered agents: slugs seen in traffic that were never - registered, with environment, call count, models, framework and first seen. These - are shadow agents; each also appears in the attention queue as high severity. -
  • -
  • - All agents: name and slug, purpose, owner (or an{" "} - unowned — assign link), environment, risk tier, framework, and last - seen ("—" means no traffic yet, not a fault). Tags mark{" "} - unregistered, draft and sample data. -
  • -
  • - Agents whose slug looks like a test, spec, script, example, demo or fixture are - folded into a collapsed "look like tests, scripts or examples" list, so a - scan of a repository's test directory does not bury production agents. -
  • -
  • - What an agent is allowed to do: a reminder that what an agent was{" "} - seen calling and what it is permitted to call are separate. - Capability grants are default deny and have no screen; make them with{" "} - agentfox permit grant (the - explainer in the app shows the older command name). -
  • -
- -

Registering an agent by hand

-

- + Register an agent manually is for an agent that does not live in a - repository you can connect. Fields: Slug (unique, lowercase),{" "} - Name, Purpose, Owner email,{" "} - Risk tier (minimal, limited, high, prohibited; default limited). - It posts to POST /api/agents and the agent is registered at once. An - agent that calls the gateway before anyone registers it shows up as unregistered - instead; registering the same slug claims it. -

-

The CLI reads the same register:

- {`agentfox agents list`} - {`agent env risk registered owner framework -127-0-0-1 production limited SHADOW unowned hosted_api -hr-screening production limited yes unowned crewai -marketing-copy… production limited SHADOW unowned — -payments-ops production high yes marcus@examp… claude-agent-s… -research-bot production limited yes dana@example… — -support-triage production limited yes priya@exampl… langgraph - 6 agents · 1 shadow · 3 unowned · 11 lineage edges`} -

- research-bot was registered from the web app a moment earlier.{" "} - 127-0-0-1 is a draft from a hosted-API scan; the CLI and the attention - queue both count a draft as unregistered until it is approved. -

- -

The agent page

- Agents → an agent -

- /app/agents/<slug>. Above the tabs, always visible: the name, a{" "} - sample data tag for seeded agents, a red banner if the agent is - quarantined or killed (with who did it, when and why), and a strip of counts: traces, - decisions, blocked, escalated, hand-offs and blast radius. Each count links to the - filtered list behind it. The page is built from{" "} - GET /api/agents/{slug}/posture, /lineage,{" "} - /api/traces?agent=, /api/risk/classify/{slug},{" "} - /api/answerability/boundaries, /api/agent-controls and{" "} - /api/policies/effective?agent=. -

- -

Activity tab

-
    -
  • Open findings for this agent: severity, type, title, each linking to the finding.
  • -
  • - Recent traces: the last 15, with verdict, model, intent and time. If - there are none but the agent has hand-offs, it says so: hand-offs are logged apart - from traced calls. -
  • -
  • - Reliability objectives: any SLOs declared for this agent on{" "} - Evaluation, with attainment and error budget. -
  • -
- -

What it may do tab

-

- Effective policy is the policy actually in force for this agent, - composed from every level that reaches it, with where each rule came from. The header - line gives the mode, the default effect and the layers applied. Each row is a rule: - what it checks, its effect, its source (for example org:*), and the - layer's compose setting (in the column headed "mode": extend,{" "} - restrict or override). A custom tag marks a rule - from a narrower level than the org default, and loosened marks a rule a - narrower level relaxed. Rules rejected during composition are listed underneath. -

-

- + Customize for this agent opens a new policy called{" "} - <slug>-overrides, pre-scoped to the agent level. See{" "} - the policy editor. -

- {`agentfox policy effective --agent payments-ops`} - {`effective policy in development — default allow - layers: - org:*(extend) baseline observe - org:*(extend) eu-ai-act-high-risk observe - org:*(extend) tool-containment enforce - -rule effect mode from overrides -access.undeclared_table escalate enforce org:* — -access.unscoped_table block enforce org:* — -…`} - -

Knowledge boundary

-

- On the same tab. A knowledge boundary is what the agent may answer from; without one, - nothing stops it inventing an answer to a question it has no data for. Fields: -

-
    -
  • Systems of record it may answer from (comma-separated)
  • -
  • Coverage (months of history) and Freshness (hours)
  • -
  • Entity types it knows about (comma-separated)
  • -
  • Topics it must refuse even if it has data (comma-separated)
  • -
  • Question types it may answer: fact, aggregate, prediction, opinion, procedure
  • -
-

- Declare boundary (or Update boundary) sends{" "} - PUT /api/answerability/boundary. The form has no mode field: a boundary - saved from the web app is in observe, so refusals are recorded, not - applied. Enforce it from the CLI once the dry runs look right. -

- {`agentfox declare boundary research-bot --systems wiki,ticket-history --coverage-months 12 --answerable fact,procedure --out-of-scope "legal advice,medical diagnosis" -agentfox test boundary research-bot "Can you give me legal advice about this contract?"`} - {`✓ boundary declared for research-bot - answerable: fact, procedure - coverage: last 12 months - observe mode — refusals are recorded, not applied. Re-run with --mode enforce when the dry runs -look right. -╭─ would abstain — out_of_domain ───────────────────────────────────────────────╮ -│ That topic is outside what this agent is set up to cover (legal advice). │ -╰───────────────────────────────────────────────────────────────────────────────╯ - recorded only (observe mode)`} - -

Registration tab

-
    -
  • - Purpose, editable inline (tagged not set when empty). - Saves with PATCH /api/agents/{slug}. -
  • -
  • - Agent map: everything this agent reached in observed traffic within - two hops, drawn as a circle, with the blast radius on it. An edge that was observed - but never declared is drawn as such; that gap is registry drift. -
  • -
  • - Registration: owner (with an email and optional team field and{" "} - Assign owner / Update), team, environment, risk - tier, framework, registered, declared models, declared tools, data classes, last - seen. -
  • -
  • - Observed lineage: the same edges as a table (from, relation, to, - times seen), with each tool's impact tier (read, write, high_impact, - irreversible) and, for MCP tools, the server's trust level. Declared models and - tools are what someone typed in; observed lineage is what happened. A mismatch is - worth reading. -
  • -
  • - Proposed risk classification: an EU AI Act class proposed from the - purpose text and observed behaviour, with the signals behind it (for example - "can invoke high-impact or irreversible tools"). It is advisory and says it - requires human confirmation. When it differs from the recorded tier,{" "} - Accept — set risk tier to … writes it; leaving it is rejecting it. - The formal record (class, residual risk, signer) is on{" "} - Compliance → Risk register. -
  • -
  • - Kill switch, below. -
  • -
- -

Kill switch, quarantine and resume

-

- The current state is shown as a tag: active, quarantined or{" "} - killed. Any state other than active refuses every governed call from the - agent immediately, and each change goes to the audit chain. -

-
    -
  • - Quarantine (with an optional reason) is the reversible "stop - while I investigate". Allowed for owner, admin, security and developer. -
  • -
  • - Kill is the incident action, with no reason field in the web app. - It needs owner, admin or security. -
  • -
  • - Resume (optional reason) returns the agent to active. It needs the - same roles as Kill: restarting something stopped for cause is not a lesser decision. -
  • -
-

- The form posts to the web app's /api/agents/<slug>/control, which - calls POST /api/agents/{slug}/quarantine, /kill or{" "} - /resume. The CLI does the same: -

- {`agentfox agents quarantine research-bot --reason "checking an odd tool call" -agentfox agents resume research-bot --reason "incident closed"`} - {`research-bot killed → active incident closed`} -

(That resume followed a kill made in the web app.)

- - The kill switch only stops calls that go through AgentFox. An agent that calls a model - or a tool directly, outside agentfox.auto(), the guard API or the proxy, - is not stopped by it. - - -

Owners and shadow agents

-

- An agent with no owner shows unowned — assign on the list and an{" "} - unowned tag on its Registration tab; assign one there. An unregistered - agent is one the gateway saw in traffic under a slug nobody registered. It raises a - high-severity attention item ("'slug' is running and was never - registered") and a finding. Register it with the same slug, give it an owner, - then resolve the finding. -

- -

Common tasks

- - -

What can go wrong

-
    -
  • - Kill or Resume shows an error banner. Your role is developer; - those need owner, admin or security. Quarantine is still available to you. -
  • -
  • - "no draft agent with that id" on Approve or Reject. Someone - already decided it; reload. -
  • -
  • - Tools count is high but nothing is allowed. Tools are declared; - grants are separate and default deny. See{" "} - Contain tool calls. -
  • -
  • - Blast radius reads 0. Lineage is built from observed traffic only; - an agent with no traces has none. -
  • -
- -

Limits

-
    -
  • No screen for capability grants or tool declarations.
  • -
  • Boundaries saved from the web app are always observe mode.
  • -
  • The risk classification is a heuristic over purpose text and behaviour, not a legal determination.
  • -
- - -
- ); -} diff --git a/dashboard/app/docs/app/approvals/page.tsx b/dashboard/app/docs/app/approvals/page.tsx index 37c5f36e..215c5b8a 100644 --- a/dashboard/app/docs/app/approvals/page.tsx +++ b/dashboard/app/docs/app/approvals/page.tsx @@ -220,7 +220,7 @@ arguments: amount 250 · currency USD · to acct_attacker_991`} diff --git a/dashboard/app/docs/app/page.tsx b/dashboard/app/docs/app/page.tsx index eaccce24..4acc2f5f 100644 --- a/dashboard/app/docs/app/page.tsx +++ b/dashboard/app/docs/app/page.tsx @@ -288,7 +288,7 @@ AGENTFOX_API_URL=http://127.0.0.1:8080 npm run dev`} Discover - Agents /app/agents + Projects /app/projects The register: owners, risk, boundary, kill switch. agentfox agents list diff --git a/dashboard/app/docs/app/projects/page.tsx b/dashboard/app/docs/app/projects/page.tsx new file mode 100644 index 00000000..a6364934 --- /dev/null +++ b/dashboard/app/docs/app/projects/page.tsx @@ -0,0 +1,293 @@ +import type { Metadata } from "next"; +import Link from "next/link"; + +import { Callout, Code, InTheApp, NextSteps, Output, TaskTable } from "@/components/docs/blocks"; +import { publicPageMetadata } from "@/lib/site"; + +export const metadata: Metadata = publicPageMetadata({ + title: "Projects", + description: + "One project per agent: the project switcher, the canvas and its inspector, features a project inherits from the workspace, the setup banner, access, the knowledge boundary and the kill switch.", + path: "/docs/app/projects", +}); + +export default function Page() { + return ( +
+

Web app

+

Projects

+

+ Each agent is a project: its guardrails, its traffic, what it may do and what it + changed from the workspace's settings, on one set of pages. The API still calls + a project an agent (/api/agents). +

+ Projects + +

When to use this

+
    +
  • To see and change what protects one agent, where it acts.
  • +
  • To add projects a scan proposed, and find agents nobody registered or owns.
  • +
  • To switch a feature on or off for one project without changing the others.
  • +
  • To stop an agent now.
  • +
+ +

All projects, or one

+

+ The switcher at the top of the sidebar picks the scope. All projects{" "} + is the workspace: Home, Projects, Observe, Issues, Policies, Features, Approvals, + Test and Reports. A project shows only its own pages: Canvas,{" "} + Activity, Access, Features,{" "} + Issues, Approvals and Settings. + Issues and Approvals are the workspace pages filtered to the project + (?agent=<slug>). Opening a project's link switches to it; the + choice is remembered. +

+ +

The projects list

+

+ /app/projects, built from GET /api/agents: every project with + its owner, risk, requests and how many were stopped, open issues and last activity.{" "} + Needs attention lists unregistered and unowned ones;{" "} + Proposed lists the drafts a repository scan found, with{" "} + Add project (POST /api/agents/{id}/approve) and{" "} + Dismiss. A draft is inert until it is added. A multi-agent system + (OpenAI Agents SDK, LangGraph, CrewAI, AutoGen, Google ADK) is proposed as one + project per agent, each with its own tools, listed under the agent that hands it + work; Add all adds the whole system + (POST /api/agents/{id}/approve-system). The hand-offs its code + declares show on each project's canvas before any traffic. +

+

Creating a project by hand

+

+ New project is for an agent that does not live in a repository you can + connect: an ID (unique, lowercase), name, purpose, owner and risk level, posted to{" "} + POST /api/agents. An agent that calls the gateway before anyone registers + it shows up as unregistered; creating the same ID claims it. +

+ {`agentfox agents list`} + +

The canvas

+ Projects → a project +

+ A project opens on its canvas: its guardrails drawn where they act. The user's + message, the agent, its reply, documents and memory, tool calls, the tools, and what + tools return. Each step is coloured by what applies there: enforcing, watching only, + or nothing set. Agents it hands work to are drawn beside it and open their own + project. Built from GET /api/agents/{slug}/map. +

+
    +
  • + Many tools are one Tools node until you choose{" "} + Expand. Then they are drawn as groups (by risk, access, source or + server), at most six, and an opened group shows at most six of its tools. A{" "} + +N more node opens the full, searchable list in the inspector. +
  • +
  • + The inspector on the right shows what is selected: a step's rules + and checks, or a tool's access, rules and limits, each changeable for this project + there. With nothing selected it shows the project: its requests, blocked, held and + errors, its open issues, the guardrails that fired most, its lineage (the blast radius, + and Open lineage map for everything within two hops), who owns it, + its tools and the rules that apply at every step. +
  • +
+

+ Activity has the project's requests over the chosen range against + the range before it (a window in the chart opens its runs), its runs, the guardrails + that fired, and answer quality. Access is its tool permissions and limits and the + knowledge boundary below. Settings has its owner and registration, + the circuit breaker and the kill switch. +

+ +

Features

+ Features +

+ The switches a developer reaches for, set once for every project on{" "} + /app/features and changed per project on its own Features view. +

+
    +
  • + Protections: prompt attacks, personal data, secrets, harmful + content, off-task actions, unsupported answers and insecure code, each off or at a + sensitivity (low, medium, high), or off and on. Each shows whether it is blocking or + only watching. +
  • +
  • + Checks: the detectors that run on traffic, on or off. A check that + is not installed in this deployment cannot be switched on. +
  • +
+

+ A project starts with the workspace's value for every switch and says so + ("Inherited from the workspace"). Changing one marks it{" "} + changed here; Reset goes back, and so does setting it to the + workspace's value. A later change for every project reaches every project that + inherits it. +

+
    +
  • + A project can always be stricter than the workspace. +
  • +
  • + It can be lower (less sensitive, or off) only where the workspace + ticked Projects may lower for that protection, which needs a role + that may enforce policies. Until then the lower levels are shown locked, with the + reason. Protections that keep the others from being switched off are never lowered + per project. +
  • +
  • + A check can be switched either way per project, except the always-on safety net. + The gateway applies the project's choice on every call, not only on the screen. +
  • +
+ {`GET /api/features +GET /api/agents/{slug}/features +POST /api/features/protections/{key} {"level": "high", "agent": "support-triage"} +POST /api/features/checks/{key} {"enabled": false, "agent": "support-triage"}`} +

+ Every change is an operator action in the audit log. Under the hood a protection is a + set of rules in a shipped pack; a project's change is a copy in its own layer (see{" "} + Policies). +

+ +

The setup banner

+

+ Until setup is done, a line at the top of each page says what to do next, sized to the + one missing step. In a project, in order: add it (it was proposed by a scan); send its + first request (with a test request that names it, and the banner clears itself when + that request arrives); allow the tools it should use; and turn blocking on, after + seeing how many of last week's requests it would have stopped. With all projects + in view it is about the workspace: connect the first agent, then the first request, + then the next step on Get started. Built from GET /api/onboarding and{" "} + GET /api/agents/{slug}/setup. Hiding it keeps it hidden for a + week. +

+ +

Knowledge boundary

+

+ On the Access view. A knowledge boundary is what the agent may answer from; without one, + nothing stops it inventing an answer to a question it has no data for. Fields: +

+
    +
  • Systems of record it may answer from (comma-separated)
  • +
  • Coverage (months of history) and Freshness (hours)
  • +
  • Entity types it knows about (comma-separated)
  • +
  • Topics it must refuse even if it has data (comma-separated)
  • +
  • Question types it may answer: fact, aggregate, prediction, opinion, procedure
  • +
+

+ Declare boundary (or Update boundary) sends{" "} + PUT /api/answerability/boundary. The form has no mode field: a boundary + saved from the web app is in observe, so refusals are recorded, not + applied. Enforce it from the CLI once the dry runs look right. +

+ {`agentfox declare boundary research-bot --systems wiki,ticket-history --coverage-months 12 --answerable fact,procedure --out-of-scope "legal advice,medical diagnosis" +agentfox test boundary research-bot "Can you give me legal advice about this contract?"`} + {`✓ boundary declared for research-bot + answerable: fact, procedure + coverage: last 12 months + observe mode — refusals are recorded, not applied. Re-run with --mode enforce when the dry runs +look right. +╭─ would abstain — out_of_domain ───────────────────────────────────────────────╮ +│ That topic is outside what this agent is set up to cover (legal advice). │ +╰───────────────────────────────────────────────────────────────────────────────╯ + recorded only (observe mode)`} + +

Kill switch, quarantine and resume

+

+ The current state is shown as a tag: active, quarantined or{" "} + killed. Any state other than active refuses every governed call from the + agent immediately, and each change goes to the audit chain. +

+
    +
  • + Quarantine (with an optional reason) is the reversible "stop + while I investigate". Allowed for owner, admin, security and developer. +
  • +
  • + Kill is the incident action, with no reason field in the web app. + It needs owner, admin or security. +
  • +
  • + Resume (optional reason) returns the agent to active. It needs the + same roles as Kill: restarting something stopped for cause is not a lesser decision. +
  • +
+

+ The form posts to the web app's /api/agents/<slug>/control, which + calls POST /api/agents/{slug}/quarantine, /kill or{" "} + /resume. The CLI does the same: +

+ {`agentfox agents quarantine research-bot --reason "checking an odd tool call" +agentfox agents resume research-bot --reason "incident closed"`} + {`research-bot killed → active incident closed`} +

(That resume followed a kill made in the web app.)

+ + The kill switch only stops calls that go through AgentFox. An agent that calls a model + or a tool directly, outside agentfox.auto(), the guard API or the proxy, + is not stopped by it. + + +

Owners and shadow agents

+

+ An agent with no owner shows unowned — assign on the list and an{" "} + unowned tag; assign one on its Settings view. An unregistered + agent is one the gateway saw in traffic under a slug nobody registered. It raises a + high-severity attention item ("'slug' is running and was never + registered") and a finding. Register it with the same slug, give it an owner, + then resolve the finding. +

+ +

Common tasks

+ + +

What can go wrong

+
    +
  • + Kill or Resume shows an error banner. Your role is developer; + those need owner, admin or security. Quarantine is still available to you. +
  • +
  • + "no draft agent with that id" on Approve or Reject. Someone + already decided it; reload. +
  • +
  • + Tools count is high but nothing is allowed. Tools are declared; + grants are separate and default deny. Allow them on Access, or see{" "} + Contain tool calls. +
  • +
  • + A lower level is locked on a project's Features. The workspace + has not let projects lower that protection. Tick Projects may lower{" "} + for it on the workspace's Features, or change it for every project. +
  • +
+ +

Limits

+
    +
  • Boundaries saved from the web app are always observe mode.
  • +
  • The risk classification is a heuristic over purpose text and behaviour, not a legal determination.
  • +
+ + +
+ ); +} diff --git a/dashboard/app/docs/app/start/page.tsx b/dashboard/app/docs/app/start/page.tsx index b2e20073..80034ee0 100644 --- a/dashboard/app/docs/app/start/page.tsx +++ b/dashboard/app/docs/app/start/page.tsx @@ -79,7 +79,7 @@ export default function Page() { Declare what your agent can answer At least one knowledge boundary exists. - A link to the boundary form on the agent's Access tab (Knowledge boundary). + A link to the boundary form on the agent's Access tab (Knowledge boundary). Turn enforcement on @@ -291,7 +291,7 @@ agentfox admin auth revoke tok_01m469mm9pt4qcfghd`}

- Used in: Agents in the web app,{" "} + Used in: Agents in the web app,{" "} Approvals and the kill switch.

diff --git a/dashboard/app/docs/guides/approvals/page.tsx b/dashboard/app/docs/guides/approvals/page.tsx index 6a891df4..e38218f3 100644 --- a/dashboard/app/docs/guides/approvals/page.tsx +++ b/dashboard/app/docs/guides/approvals/page.tsx @@ -515,7 +515,7 @@ No open findings at severity 'critical'.`} in its default state. The CLI acts on the database directly and records the actor as{" "} cli.

- Agents → an agent → Kill switch + Agents → an agent → Kill switch

Incident runbook

Every command below was run against the example deployment above.

diff --git a/dashboard/app/docs/guides/rag/page.tsx b/dashboard/app/docs/guides/rag/page.tsx index 5dfe9d8b..7c65f362 100644 --- a/dashboard/app/docs/guides/rag/page.tsx +++ b/dashboard/app/docs/guides/rag/page.tsx @@ -561,7 +561,7 @@ say: That asks for a projection rather than a recorded fact. I can only repo - Agents → an agent → Knowledge boundary + Agents → an agent → Knowledge boundary

Where these show up

diff --git a/dashboard/app/docs/page.tsx b/dashboard/app/docs/page.tsx index 2a38cba4..63e787db 100644 --- a/dashboard/app/docs/page.tsx +++ b/dashboard/app/docs/page.tsx @@ -25,7 +25,7 @@ const AREAS: { { what: "First look at this machine, including local AI-tool sessions", run: "agentfox scan --sessions", href: "/docs/guides/scan-a-repo" }, { what: "MCP servers: reach, version pinning, auth, tool descriptions", run: "agentfox scan mcp", href: "/docs/guides/mcp" }, { what: "Agent skills: planted instructions and declared danger", run: "agentfox scan skills", href: "/docs/guides/scan-a-repo" }, - { what: "Shadow and unowned agents seen in traffic", run: "agentfox agents list", href: "/docs/app/agents" }, + { what: "Shadow and unowned agents seen in traffic", run: "agentfox agents list", href: "/docs/app/projects" }, ], }, { diff --git a/dashboard/app/globals.css b/dashboard/app/globals.css index 143058c4..e5266355 100644 --- a/dashboard/app/globals.css +++ b/dashboard/app/globals.css @@ -482,6 +482,45 @@ nav.side a.active { } nav.side a.active svg { opacity: 1; } +/* The project switcher (components/product/ProjectSwitcher.tsx): the scope the + sidebar is in, at the top of it, like Figma's file switcher. */ +.project-switch { position: relative; margin: 0 0 10px; flex: none; } +.project-switch-btn { + display: flex; align-items: center; gap: 9px; width: 100%; + padding: 7px 8px; border: 1px solid var(--border); border-radius: var(--r-2); + background: var(--panel-2); color: var(--text); font: inherit; font-size: var(--t-small); + cursor: pointer; text-align: left; +} +.project-switch-btn:hover { border-color: var(--text-2); } +.project-switch-btn svg { margin-left: auto; flex: none; opacity: .6; } +.project-switch-name { display: flex; flex-direction: column; min-width: 0; font-weight: 600; line-height: 1.25; } +.project-switch-name, .project-switch-opt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.project-switch-name small { font-size: var(--t-micro); font-weight: 500; color: var(--faint); text-transform: uppercase; letter-spacing: .06em; } +.project-switch-dot { + flex: none; width: 22px; height: 22px; border-radius: 6px; display: inline-grid; place-items: center; + background: var(--accent); color: var(--accent-ink); font-size: 11px; font-weight: 700; +} +.project-switch-dot[data-all] { background: var(--panel); color: var(--text-2); border: 1px solid var(--border); } +.project-switch-menu { + position: absolute; z-index: 60; top: calc(100% + 4px); left: 0; right: 0; + padding: 5px; border: 1px solid var(--border); border-radius: var(--r-2); + background: var(--panel); box-shadow: var(--e-2, 0 8px 24px rgba(0,0,0,.18)); +} +.project-switch-menu button, .project-switch-menu a.project-switch-foot { + display: flex; align-items: center; gap: 9px; width: 100%; padding: 6px 7px; border: 0; border-radius: 6px; + background: none; color: var(--text-2); font: inherit; font-size: var(--t-small); text-align: left; cursor: pointer; +} +.project-switch-menu button small { margin-left: auto; color: var(--faint); font-size: var(--t-micro); } +.project-switch-menu button:hover, .project-switch-menu a.project-switch-foot:hover { background: var(--panel-2); color: var(--text); text-decoration: none; } +.project-switch-menu button.on { color: var(--text); font-weight: 600; } +nav.side .project-switch-menu a.project-switch-foot { padding-left: 38px; } +.project-switch-list { max-height: 280px; overflow-y: auto; } +.project-switch-sep { height: 1px; margin: 4px 2px; background: var(--border); } +.project-switch-filter { + width: 100%; margin: 0 0 4px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; + background: var(--panel-2); color: var(--text); font: inherit; font-size: var(--t-small); +} + /* * nav.side is a 100vh column flex container, so every child is shrinkable by * default. Once the nav had enough rows to overflow that height, this control @@ -1447,7 +1486,11 @@ input[type="checkbox"], input[type="radio"] { the divs wrapped instead of joining one scrollable row. display:contents removes the wrapper from the box tree so its children become direct flex items of nav.side itself. */ - nav.side > div:not(.brand):not(.theme-toggle) { display: contents; } + nav.side > div:not(.brand):not(.theme-toggle):not(.project-switch) { display: contents; } + .project-switch { margin: 0 6px 0 0; } + .project-switch-btn { padding: 5px 7px; } + .project-switch-name small { display: none; } + .project-switch-menu { right: auto; position: fixed; top: 52px; left: 10px; } nav.side .brand { padding: 4px 10px 4px 4px; flex: none; } nav.side .brand small { display: none; } nav.side .group { display: none; } @@ -3216,12 +3259,12 @@ textarea.k-input { height: auto; padding: 8px 11px; line-height: 1.5; resize: ve --gm-off: var(--viz-allowed); --gm-off-bg: var(--panel); } -.gm { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 14px; } +.gm { --gm-h: max(520px, calc(100vh - 200px)); display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 14px; } @media (max-width: 960px) { .gm { grid-template-columns: 1fr; } } -.gm-side { display: flex; flex-direction: column; gap: 8px; min-width: 0; height: 640px; } +.gm-side { display: flex; flex-direction: column; gap: 8px; min-width: 0; height: var(--gm-h); } .gm-side-tabs { align-self: flex-start; } .gm-side > .gm-panel, .gm-side > .gm-list { flex: 1; min-height: 0; max-height: none; } -.gm-canvas { position: relative; display: flex; flex-direction: column; height: 640px; border: 1px solid var(--border); border-radius: var(--r-2); background: var(--panel); overflow: hidden; } +.gm-canvas { position: relative; display: flex; flex-direction: column; height: var(--gm-h); border: 1px solid var(--border); border-radius: var(--r-2); background: var(--panel); overflow: hidden; } .gm-canvas .react-flow__background { opacity: .5; } .gm-node { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); color: var(--text); font-size: var(--t-small); box-shadow: 0 1px 2px rgba(0,0,0,.04); cursor: pointer; } .gm-node strong { font-weight: 600; } @@ -3248,16 +3291,46 @@ textarea.k-input { height: auto; padding: 8px 11px; line-height: 1.5; resize: ve .gm-perm-bad { background: var(--bad-bg); color: var(--bad); } .gm-more { width: 190px; border-style: dashed; color: var(--muted); font-size: var(--t-micro); cursor: default; align-items: center; } .gm-sub-agent { width: 190px; text-decoration: none; border-style: dashed; } +/* Known from the code only: no traffic has taken this delegation yet. */ +.gm-sub-agent.gm-unseen { background: var(--gm-off-bg); } .gm-selected { outline: 2px solid var(--text); outline-offset: 2px; } .gm-flow { position: relative; flex: 1; min-height: 0; } -.gm-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; padding: 8px 12px; border-bottom: 1px solid var(--border); font-size: var(--t-micro); color: var(--text); } +.gm-bar { display: flex; align-items: center; gap: 10px; padding: 6px 8px 6px 0; border-bottom: 1px solid var(--border); } +.gm-bar-end { margin-left: auto; display: flex; align-items: center; gap: 6px; flex: none; } +.gm-bar .k-btn { padding: 3px 10px; font-size: var(--t-micro); } +.gm-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; padding: 2px 12px; font-size: var(--t-micro); color: var(--muted); } +.gm-more { cursor: pointer; } +.gm-more:hover, .gm-tools:hover { border-color: var(--text); } +.gm-expand { font-size: var(--t-micro); color: var(--muted); } +.gm-back { align-self: flex-start; border: 0; padding: 0; background: none; color: var(--muted); font: inherit; font-size: var(--t-micro); cursor: pointer; } +.gm-back:hover { color: var(--text); } +.gm-panel-list { padding: 12px 10px; } +.gm-panel-list .gm-list { border: 0; flex: 1; min-height: 0; } +.gm-panel-list .gm-list-head { padding: 0 2px 10px; } +.gm-tools-card { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--r-1); background: var(--panel-2); color: var(--text); font: inherit; font-size: var(--t-small); text-align: left; cursor: pointer; } +.gm-tools-card:hover { border-color: var(--text); } +/* The inspector's project summary (projects/[slug]/page.tsx). */ +.gm-summary { display: flex; flex-direction: column; gap: 10px; } +.gm-summary-head { display: flex; flex-direction: column; gap: 2px; } +.gm-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; } +.gm-stat { display: flex; flex-direction: column; gap: 1px; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--r-1); color: var(--text); text-decoration: none; } +.gm-stat:hover { border-color: var(--text); text-decoration: none; } +.gm-stat b { font-size: var(--t-body); font-weight: 650; font-variant-numeric: tabular-nums; } +.gm-stat span { font-size: var(--t-micro); color: var(--muted); } +.gm-stat.bad b { color: var(--bad); } +.gm-facts { display: grid; grid-template-columns: auto 1fr; gap: 3px 12px; margin: 0; font-size: var(--t-micro); } +.gm-facts dt { color: var(--muted); } +.gm-facts dd { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.gm-issues { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; font-size: var(--t-small); } +.gm-issues li { display: flex; align-items: center; justify-content: space-between; gap: 8px; } +.gm-issues a { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .gm-legend span { display: inline-flex; align-items: center; gap: 6px; } .gm-swatch { display: inline-block; flex-shrink: 0; width: 14px; height: 12px; border: 1px solid var(--border); border-left-width: 4px; border-radius: 3px; box-sizing: border-box; } .gm-swatch.gm-on { border-left: 4px solid var(--gm-on); background: var(--gm-on-bg); } .gm-swatch.gm-watch { border-left: 4px solid var(--gm-watch); background: var(--gm-watch-bg); } .gm-swatch.gm-off { border-left: 4px solid var(--gm-off); background: var(--gm-off-bg); } .gm-swatch-denied { border: 1px dashed var(--viz-blocked); border-left-width: 1px; } -.gm-panel { display: flex; flex-direction: column; gap: 10px; padding: 14px 16px; border: 1px solid var(--border); border-radius: var(--r-2); background: var(--panel); max-height: 640px; overflow: auto; min-width: 0; } +.gm-panel { display: flex; flex-direction: column; gap: 10px; padding: 14px 16px; border: 1px solid var(--border); border-radius: var(--r-2); background: var(--panel); max-height: var(--gm-h); overflow: auto; min-width: 0; } .gm-panel h4 { margin: 6px 0 0; font-size: var(--t-micro); color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: .04em; } .gm-panel p { margin: 0; font-size: var(--t-small); } .gm-rules { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; } @@ -3410,3 +3483,49 @@ ol.gs-steps li.done .gs-title { color: var(--muted); } .wf-row dd { margin: 0; } .wf-row.wf-bad dd { color: var(--bad); font-weight: 600; } .wf-row.wf-warn dd { color: var(--warn); } + +/* --- Features (components/product/features/FeaturesEditor.tsx) ----------------- */ +.ft { display: flex; flex-direction: column; gap: 16px; } +.ft-intro { margin: 0; font-size: var(--t-small); color: var(--text-2); } +.ft-card { padding: 0; } +.ft-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; padding: 14px 16px 10px; border-bottom: 1px solid var(--border); } +.ft-head h3 { margin: 0; font-size: var(--t-body); font-weight: 650; } +.ft-head .k-muted { font-size: var(--t-small); } +.ft-group h4 { margin: 0; padding: 12px 16px 2px; font-size: var(--t-micro); color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: .05em; } +.ft-list { list-style: none; margin: 0; padding: 0; } +.ft-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 6px 16px; padding: 10px 16px; border-top: 1px solid var(--border); } +.ft-list > .ft-row:first-child { border-top: 0; } +.ft-name { display: flex; flex-direction: column; gap: 2px; min-width: 0; } +.ft-name strong { font-size: var(--t-small); font-weight: 600; } +.ft-sub { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: var(--t-micro); color: var(--muted); } +.ft-control { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px 12px; } +.ft-control .k-seg button:disabled { opacity: .45; cursor: not-allowed; } +.ft-inherited .ft-control .k-seg { opacity: .72; } +.ft-inherited .ft-control .k-seg:hover { opacity: 1; } +.ft-reset { border: 0; padding: 0; background: none; color: var(--muted); font: inherit; font-size: var(--t-micro); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; } +.ft-reset:hover { color: var(--text); } +.ft-open { font-size: var(--t-micro); color: var(--text-2); } +.ft-mode { padding: 0 6px; border-radius: var(--r-pill); line-height: 1.6; } +.ft-mode-blocking { background: var(--ok-bg); color: var(--ok); } +.ft-mode-watching { background: var(--escalate-bg); color: var(--escalate); } +.ft-error { grid-column: 1 / -1; margin: 0; } +.ft-missing { padding: 4px 16px 12px; font-size: var(--t-small); } +.ft-missing > summary { cursor: pointer; color: var(--muted); padding: 6px 0; } +.ft-missing .ft-row { padding-left: 0; padding-right: 0; opacity: .7; } +@media (max-width: 720px) { .ft-row { grid-template-columns: 1fr; } .ft-control { justify-content: flex-start; } } + +/* --- Setup banner (components/product/SetupBanner.tsx) ------------------------- */ +.setup-banner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 18px; margin: 0 0 18px; padding: 12px 14px 12px 16px; border: 1px solid var(--border); border-left: 4px solid var(--accent); border-radius: var(--r-2); background: var(--panel); box-shadow: var(--e-1); } +.setup-wait { border-left-color: var(--escalate); } +.setup-act { border-left-color: var(--text-2); } +.setup-main { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1 1 380px; font-size: var(--t-small); } +.setup-main > span { color: var(--text-2); } +.setup-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; } +.setup-close { border: 0; background: none; color: var(--muted); font-size: 18px; line-height: 1; padding: 2px 4px; cursor: pointer; } +.setup-close:hover { color: var(--text); } +.setup-snippet { display: flex; align-items: flex-start; gap: 8px; margin-top: 6px; } +.setup-snippet pre { flex: 1; min-width: 0; margin: 0; padding: 8px 10px; overflow-x: auto; border-radius: var(--r-1); background: var(--panel-2); font-size: var(--t-micro); line-height: 1.5; } +.setup-waiting i { display: inline-block; width: 7px; height: 7px; margin-right: 4px; border-radius: 50%; background: var(--escalate); animation: k-skel 1.4s ease-in-out infinite; } +.setup-waiting { display: inline-flex; align-items: center; color: var(--muted); font-size: var(--t-micro); } +.setup-preview { font-size: var(--t-small); color: var(--text-2); } +.ft-lock { color: var(--faint); } diff --git a/dashboard/components/kit/RunsTable.tsx b/dashboard/components/kit/RunsTable.tsx index c5fe809d..7a9658d2 100644 --- a/dashboard/components/kit/RunsTable.tsx +++ b/dashboard/components/kit/RunsTable.tsx @@ -34,7 +34,7 @@ export function RunsTable({ runs, showAgent = true }: { runs: any[]; showAgent?: {showAgent && ( {t.agent ? ( - {t.agent} + {t.agent} ) : ( — )} diff --git a/dashboard/components/product/NavIcons.tsx b/dashboard/components/product/NavIcons.tsx index 68a34f0b..527b82d9 100644 --- a/dashboard/components/product/NavIcons.tsx +++ b/dashboard/components/product/NavIcons.tsx @@ -11,12 +11,13 @@ const PATHS: Record = { "/app": <>, "/app/start": <>, - "/app/agents": <>, + "/app/projects": <>, "/app/findings": <>, "/app/traces": , "/app/policies": <>, "/app/entitlement": <>, - "/app/map": <>, + "canvas": <>, + "/app/features": <>, "/app/approvals": <>, "/app/evals": <>, "/app/sources": <>, diff --git a/dashboard/components/product/PageHeader.tsx b/dashboard/components/product/PageHeader.tsx index 7c6a6928..858c9ab8 100644 --- a/dashboard/components/product/PageHeader.tsx +++ b/dashboard/components/product/PageHeader.tsx @@ -4,7 +4,7 @@ import type { ReactNode } from "react"; * The top of every signed-in page. * * There was no such component. Ten pages hand-rolled `

` + `

`, - * and two of them (`/app/agents`, `/app/sources`) wrapped the heading in a `.row` with an + * and two of them (`/app/projects`, `/app/sources`) wrapped the heading in a `.row` with an * action button and set `marginBottom: 0` on the `

` to cancel a margin the * other eight kept — so the gap between a title and its subtitle was one value on * eight pages and another on two, and the action button existed in two shapes. diff --git a/dashboard/components/product/ProductMap.tsx b/dashboard/components/product/ProductMap.tsx index 8bdeb9b7..ea0604aa 100644 --- a/dashboard/components/product/ProductMap.tsx +++ b/dashboard/components/product/ProductMap.tsx @@ -16,7 +16,7 @@ const AREAS: { }, { name: "Agents", - href: "/app/agents", + href: "/app/projects", answers: "Which agents exist, who is accountable for each one, and which models and tools each one actually calls.", doThere: @@ -130,7 +130,7 @@ const OFF_SCREEN: { name: string; where: string; body: React.ReactNode }[] = [ <> What an agent is allowed to do, decided from the action, not the prompt.{" "} {" "} - Agents. + Projects. ), }, diff --git a/dashboard/components/product/ProjectSwitcher.tsx b/dashboard/components/product/ProjectSwitcher.tsx new file mode 100644 index 00000000..876b6543 --- /dev/null +++ b/dashboard/components/product/ProjectSwitcher.tsx @@ -0,0 +1,95 @@ +"use client"; + +import Link from "next/link"; +import { useEffect, useRef, useState } from "react"; +import { useRouter } from "next/navigation"; +import { projectHref } from "@/lib/product/project"; + +export type ProjectOption = { slug: string; name: string; status?: string }; + +/** + * The scope picker at the top of the sidebar, as in Figma's file switcher: the + * project you are in, and a menu of the others plus All projects. Choosing one + * opens it; choosing All projects opens the workspace's Home. + */ +export function ProjectSwitcher({ + projects, + current, + onChoose, +}: { + projects: ProjectOption[]; + current: string | null; + onChoose: (slug: string | null) => void; +}) { + const router = useRouter(); + const [open, setOpen] = useState(false); + const [filter, setFilter] = useState(""); + const box = useRef(null); + + useEffect(() => { + if (!open) return; + const away = (e: MouseEvent) => box.current && !box.current.contains(e.target as Node) && setOpen(false); + const esc = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false); + document.addEventListener("mousedown", away); + document.addEventListener("keydown", esc); + return () => { + document.removeEventListener("mousedown", away); + document.removeEventListener("keydown", esc); + }; + }, [open]); + + const here = projects.find((p) => p.slug === current); + const label = current ? here?.name || current : "All projects"; + const shown = filter + ? projects.filter((p) => `${p.name} ${p.slug}`.toLowerCase().includes(filter.toLowerCase())) + : projects; + + const go = (slug: string | null) => { + setOpen(false); + setFilter(""); + onChoose(slug); + router.push(slug ? projectHref(slug) : "/app"); + }; + + return ( +
+ + {open && ( +
+ {projects.length > 7 && ( + setFilter(e.target.value)} /> + )} + + {shown.length > 0 &&
} +
+ {shown.map((p) => ( + + ))} +
+
+ { setOpen(false); onChoose(null); }} className="project-switch-foot">Manage projects + setOpen(false)} className="project-switch-foot">Add a project +
+ )} +
+ ); +} diff --git a/dashboard/components/product/SetupBanner.tsx b/dashboard/components/product/SetupBanner.tsx new file mode 100644 index 00000000..5d449496 --- /dev/null +++ b/dashboard/components/product/SetupBanner.tsx @@ -0,0 +1,236 @@ +"use client"; + +import Link from "next/link"; +import { usePathname, useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import { projectHref } from "@/lib/product/project"; +import { bannerFor, dismissKey, type Banner, type Onboarding, type ProjectSetup } from "@/lib/product/setupBanner"; +import { useProjectScope } from "@/lib/product/useProjectScope"; + +/** Pages that are the setup themselves, or already show the checklist. */ +const QUIET = ["/app/start", "/app/welcome"]; + +const POLL_MS = 4000; +const GIVE_UP_MS = 10 * 60_000; +const DISMISS_DAYS = 7; + +function dismissed(key: string): boolean { + try { + const at = Number(window.localStorage.getItem(key) || 0); + return Boolean(at) && Date.now() - at < DISMISS_DAYS * 86_400_000; + } catch { + return false; + } +} + +/** + * One line at the top of every page saying what to do next, sized to the next + * missing step and gone when there is none. In a project it is about that project + * (proposed, no traffic, no tool permissions, only watching); otherwise about the + * workspace (no agents, nothing received). While it waits for a first request it + * polls, and clears itself the moment one arrives. + */ +export function SetupBanner({ onboarding, initialProject }: { onboarding: Onboarding | null; initialProject: string | null }) { + const pathname = usePathname(); + const router = useRouter(); + const [scope] = useProjectScope(initialProject); + const [workspace, setWorkspace] = useState(onboarding); + const [project, setProject] = useState(null); + const [hidden, setHidden] = useState>({}); + + useEffect(() => setWorkspace(onboarding), [onboarding]); + + // The project's own state, fetched when the scope changes and on each navigation. + useEffect(() => { + if (!scope) return setProject(null); + let live = true; + fetch(`/api/agents/${encodeURIComponent(scope)}/setup`, { cache: "no-store" }) + .then((r) => (r.ok ? r.json() : null)) + .then((body) => live && setProject(body)) + .catch(() => live && setProject(null)); + return () => { + live = false; + }; + }, [scope, pathname]); + + const banner = bannerFor(scope ? project : null, scope ? null : workspace); + const shownKey = banner ? dismissKey(scope, banner.kind) : ""; + + // Storage is read after mounting, so the server's render and the first client + // render agree; a dismissed banner then goes away. + useEffect(() => { + if (shownKey && dismissed(shownKey)) setHidden((h) => ({ ...h, [shownKey]: true })); + }, [shownKey]); + const waiting = banner?.kind === "traffic" || banner?.kind === "first-request"; + + // Waiting for a first request: ask until it arrives, then refresh the page under it. + useEffect(() => { + if (!waiting) return; + const started = Date.now(); + const timer = setInterval(async () => { + if (Date.now() - started > GIVE_UP_MS) return clearInterval(timer); + if (document.hidden) return; + try { + if (scope) { + const r = await fetch(`/api/agents/${encodeURIComponent(scope)}/setup`, { cache: "no-store" }); + const body = r.ok ? await r.json() : null; + if (body && body.traces > 0) { + setProject(body); + router.refresh(); + } + } else { + const r = await fetch("/api/onboarding", { cache: "no-store" }); + const body = r.ok ? await r.json() : null; + if (body?.connected) { + setWorkspace(body); + router.refresh(); + } + } + } catch { + // Transient: the next tick tries again. + } + }, POLL_MS); + return () => clearInterval(timer); + }, [waiting, scope, router]); + + if (!banner || QUIET.some((p) => pathname === p || pathname.startsWith(`${p}/`))) return null; + // Home already carries the workspace checklist. + if (!scope && pathname === "/app" && banner.kind === "next-step") return null; + const key = dismissKey(scope, banner.kind); + if (hidden[key] ?? false) return null; + + const hide = () => { + try { + window.localStorage.setItem(key, String(Date.now())); + } catch { + // Without storage it hides for this page view only. + } + setHidden({ ...hidden, [key]: true }); + }; + + return ( +
+
+ {banner.title} + {banner.body} + {banner.snippet && } + {waiting && ( + + Waiting for the first request. This clears by itself. + + )} +
+
+ {banner.kind === "enforce" && scope ? : null} + {banner.kind === "add" && project ? : null} + {banner.actions.map((a, i) => ( + + {a.label} + + ))} + +
+
+ ); +} + +function Snippet({ text }: { text: string }) { + const [copied, setCopied] = useState(false); + return ( +
+
{text}
+ +
+ ); +} + +/** Add a proposed project, staying on its page. */ +function AddProject({ id }: { id: string }) { + const router = useRouter(); + const [busy, setBusy] = useState(false); + return ( + + ); +} + +/** Two steps: what blocking would have stopped last week, then switch it on. */ +function Enforce({ slug }: { slug: string }) { + const router = useRouter(); + const [preview, setPreview] = useState<{ would_stop: number } | null>(null); + const [busy, setBusy] = useState(false); + const [error, setError] = useState(""); + const base = `/api/agents/${encodeURIComponent(slug)}/enforcement`; + if (!preview) + return ( + + ); + return ( + <> + + Last 7 days: would have stopped {preview.would_stop} request{preview.would_stop === 1 ? "" : "s"}. + + + {error && {error}} + + Review on canvas + + + ); +} + +export type { Banner }; diff --git a/dashboard/components/product/SideNav.tsx b/dashboard/components/product/SideNav.tsx index 9fc9029c..4245d9ec 100644 --- a/dashboard/components/product/SideNav.tsx +++ b/dashboard/components/product/SideNav.tsx @@ -3,64 +3,69 @@ import Link from "next/link"; import { usePathname, useSearchParams } from "next/navigation"; import { NavIcon } from "@/components/product/NavIcons"; +import { ProjectSwitcher, type ProjectOption } from "@/components/product/ProjectSwitcher"; +import { WORKSPACE_NAV, projectNav, type NavGroup, type NavItem } from "@/lib/product/nav"; +import { useProjectScope } from "@/lib/product/useProjectScope"; + +export type { NavItem }; /** The dashboard's own URL, which is also every other row's prefix. */ const APP_ROOT = "/app"; -/** Label, href, and any other pages (path, or path?tab=key) the row stands for. */ -export type NavItem = [label: string, href: string, also?: string[]]; - -type Pattern = { path: string; tab: string | null }; +type Pattern = { path: string; query: [string, string][] }; function parse(href: string): Pattern { const [path, query] = href.split("?"); - return { path, tab: query ? new URLSearchParams(query).get("tab") : null }; + return { path, query: query ? [...new URLSearchParams(query).entries()] : [] }; } /** + * The sidebar: the project switcher, then the rows of the scope it is in. + * * The active-link highlight is computed on the client: `usePathname()` follows the * router on every kind of navigation, where a server-computed highlight stuck on - * the page you had just left after browser back/forward. + * the page you had just left after browser back/forward. The scope is too, for the + * same reason: the layout is not re-rendered on a client-side navigation, so a + * scope it read from the cookie would be the one you had before following a link + * into another project. * - * A row matches every page in `[href, ...also]`. A pattern with a `?tab=` matches - * only that tab; a bare path matches its subtree, unless another row claims the - * open tab of that same path — so "Get started" (/app/start) and "Settings" - * (/app/start?tab=connect, among others) never light up together. `/app` is the - * prefix of everything, so it only ever matches exactly. + * A row matches every page in `[href, ...also]`. A pattern with a query matches + * only when the page has those values; on a page below the pattern's path the + * query is not asked for (an issue's own page is still Issues). A bare path + * matches its subtree, unless another row claims the page's query on that same + * path — so "Get started" (/app/start) and "Settings" (/app/start?tab=connect) + * never light up together, nor Canvas and Activity. `/app` is the prefix of + * everything, so it only ever matches exactly. */ -export function SideNav({ - nav, -}: { - nav: { group: string; items: NavItem[] }[]; -}) { +export function SideNav({ projects, initial }: { projects: ProjectOption[]; initial: string | null }) { const pathname = usePathname(); const searchParams = useSearchParams(); - const currentTab = searchParams.get("tab"); + const [scope, choose] = useProjectScope(initial); + const nav: NavGroup[] = scope ? projectNav(scope) : WORKSPACE_NAV; + const patterns = (item: NavItem) => [item[1], ...(item[2] || [])].map(parse); - const tabClaims = nav.flatMap(({ items }) => items.flatMap(patterns)).filter((p) => p.tab); + const claims = nav.flatMap(({ items }) => items.flatMap(patterns)).filter((p) => p.query.length); + const has = (q: [string, string][]) => q.every(([k, v]) => searchParams.get(k) === v); - const matches = ({ path, tab }: Pattern) => { - const pathMatches = - path === APP_ROOT ? pathname === APP_ROOT : pathname === path || pathname.startsWith(`${path}/`); - if (!pathMatches) return false; - if (tab) return currentTab === tab; - return !(currentTab && tabClaims.some((c) => c.path === path && c.tab === currentTab)); + const matches = ({ path, query }: Pattern) => { + if (path === APP_ROOT) return pathname === APP_ROOT && !query.length; + if (pathname.startsWith(`${path}/`)) return true; + if (pathname !== path) return false; + if (query.length) return has(query); + return !claims.some((c) => c.path === path && has(c.query)); }; return ( <> + {nav.map(({ group, items }) => (
{group &&
{group}
} {items.map((item) => { - const [label, href] = item; + const [label, href, , icon] = item; return ( - - + + {label} ); diff --git a/dashboard/components/product/agent/GuardrailMap.tsx b/dashboard/components/product/agent/GuardrailMap.tsx index e5f1f017..26547a3c 100644 --- a/dashboard/components/product/agent/GuardrailMap.tsx +++ b/dashboard/components/product/agent/GuardrailMap.tsx @@ -5,28 +5,36 @@ import "@xyflow/react/dist/style.css"; import { Background, Controls, Handle, MarkerType, Position, ReactFlow, type Edge, type Node, type NodeProps } from "@xyflow/react"; import Link from "next/link"; import { useRouter } from "next/navigation"; -import { useMemo, useState } from "react"; +import { useMemo, useState, type ReactNode } from "react"; import { ActionPill, ModePill, Pill } from "@/components/kit"; import { DRAW_EACH, DRAW_GROUPS, DRAW_OPEN, + DRAW_SUBAGENTS, GROUP_BY, PERMISSION, RULE_SETTINGS, TONE_LABEL, TOOL_FILTERS, counts, + drawTools, + drawnSubagents, filterTools, groupSummary, groupTools, + linkActivity, + parentLabel, permissionCall, + relationLabel, ruleChange, ruleSetting, stageTone, toolTone, type AgentMap, + type Drawn, type GroupBy, + type MapLink, type MapRule, type MapTool, type Permission, @@ -41,7 +49,8 @@ import { IMPACT, detectorName, ruleTitle } from "@/lib/product/vocab"; export type { AgentMap, MapRule } from "@/lib/product/guardrailMap"; -type Selected = { kind: "stage" | "tool" | "group"; key: string } | null; +/** What the inspector shows: a step, a tool, a group of tools, or every tool. Nothing: the project. */ +type Selected = { kind: "stage" | "tool" | "group" | "tools"; key: string } | null; const SOURCE: Record = { code: "In code", granted: "Granted", seen: "Called" }; @@ -93,15 +102,8 @@ function StageNode({ data }: NodeProps {s.title} - - {c.enforcing} enforcing{c.watching ? ` · ${c.watching} watching` : ""} - - {plural(s.detectors.length, "check")} run here - {s.stats.total ? ( - - {s.stats.total} seen · {stopped(s.stats)} stopped - - ) : null} + {c.enforcing ? plural(c.enforcing, "guardrail") : c.watching || s.detectors.length ? "Watching" : "Nothing set"} + {stopped(s.stats) ? {stopped(s.stats)} stopped : null}
); } @@ -116,16 +118,11 @@ function ToolNode({ data }: NodeProps {shortKey(tl.key)} - {p.label} - - {IMPACT[tl.impact || ""] || "Risk not set"} - {rules ? ` · ${plural(rules, "rule")}` : ""} + + {p.label} + {rules ? {plural(rules, "rule")} : null} - {tl.stats.total ? ( - - {tl.stats.total} calls · {stopped(tl.stats)} stopped - - ) : null} + {tl.stats.total ? {plural(tl.stats.total, "call")} : null}
); } @@ -144,38 +141,80 @@ function GroupNode({ data }: NodeProps{s.ask} ask : null} {s.denied ? {s.denied} not allowed : null} - {s.calls ? ( - - {s.calls} calls · {s.stopped} stopped - - ) : null} - {data.open ? "Open" : "Click to open"}
); } -function MoreNode({ data }: NodeProps>) { +function MoreNode({ data }: NodeProps>) { return ( -
- +
+ {data.label}
); } -function SubagentNode({ data }: NodeProps>) { +/** Every tool as one node, until the canvas is asked to show them. */ +function ToolsNode({ data }: NodeProps>) { + const s = groupSummary(data.tools); return ( - +
+ + + Tools + {plural(data.tools.length, "tool")} + + {s.allowed ? {s.allowed} allowed : null} + {s.ask ? {s.ask} ask : null} + {s.denied ? {s.denied} not allowed : null} + + Expand ▾ +
+ ); +} + +function linkHref(slug: string) { + return `/app/projects/${encodeURIComponent(slug)}`; +} + +/** A sub-agent this agent hands work to: from its code, from traffic, or both. */ +function SubagentNode({ data }: NodeProps>) { + const s = data.link; + return ( + - Hands off to - {data.slug} - {data.calls} times · open its map + {relationLabel(s.relation, data.framework)} + {s.name || s.slug} + {linkActivity(s)} · open project + + ); +} + +/** An agent that hands this one work, so a sub-agent's map links back up. */ +function ParentNode({ data }: NodeProps>) { + const s = data.link; + return ( + + {parentLabel(s.relation)} + {s.name || s.slug} + {linkActivity(s)} · open project + ); } // "group" is a built-in React Flow type with its own styling, so ours is "toolgroup". -const NODE_TYPES = { end: EndNode, agent: AgentNode, stage: StageNode, tool: ToolNode, toolgroup: GroupNode, more: MoreNode, subagent: SubagentNode }; +const NODE_TYPES = { + end: EndNode, + agent: AgentNode, + stage: StageNode, + tool: ToolNode, + toolgroup: GroupNode, + tools: ToolsNode, + more: MoreNode, + subagent: SubagentNode, + parent: ParentNode, +}; // --- layout ---------------------------------------------------------------------- @@ -185,20 +224,7 @@ const PER_ROW = 4; const GROUPS_PER_ROW = 5; const GROUP_ROW = 180; -type Drawn = { tools: MapTool[]; groups: ToolGroup[] | null; open: ToolGroup | null }; - -/** - * What the canvas draws for the tools: each one when there are few; otherwise one - * node per group, with the opened group's tools under it. Everything else is in the - * list beside the canvas. - */ -function drawn(visible: MapTool[], by: GroupBy, openKey: string | null): Drawn { - if (visible.length <= DRAW_EACH) return { tools: visible, groups: null, open: null }; - const groups = groupTools(visible, by); - return { tools: [], groups, open: groups.find((g) => g.key === openKey) || null }; -} - -function layout(map: AgentMap, d: Drawn, hidden: number, selected: Selected): { nodes: Node[]; edges: Edge[] } { +function layout(map: AgentMap, d: Drawn, visible: MapTool[], hidden: number, selected: Selected): { nodes: Node[]; edges: Edge[] } { const stage = (key: string) => map.stages.find((s) => s.key === key); const isSel = (kind: string, key: string) => selected?.kind === kind && selected.key === key; const nodes: Node[] = []; @@ -237,7 +263,7 @@ function layout(map: AgentMap, d: Drawn, hidden: number, selected: Selected): { let bottom = 380; const lastIds: string[] = []; - if (call && (d.tools.length || d.groups?.length || hidden)) { + if (call && (d.collapsed || d.tools.length || d.groups?.length || hidden)) { nodes.push({ id: "stage:tool_call", type: "stage", position: { x: agentX - 20, y: 200 }, data: { stage: call, selected: isSel("stage", "tool_call") } }); edge("et", "agent", "stage:tool_call", { sourceHandle: "down", targetHandle: "top" }); @@ -256,7 +282,12 @@ function layout(map: AgentMap, d: Drawn, hidden: number, selected: Selected): { return id; }; - if (!d.groups) { + if (d.collapsed) { + nodes.push({ id: "tools:all", type: "tools", position: grid(1, 380, 0), data: { tools: visible, selected: selected?.kind === "tools" } }); + edge("etc-all", "stage:tool_call", "tools:all", { animated: visible.some((t) => t.stats.total) }); + lastIds.push("tools:all"); + bottom = 380 + GROUP_ROW; + } else if (!d.groups) { d.tools.forEach((tl, i) => lastIds.push(toolNode(tl, grid(d.tools.length, 380, i), "stage:tool_call", String(i)))); bottom = 380 + rows(d.tools.length) * ROW; } else { @@ -271,7 +302,7 @@ function layout(map: AgentMap, d: Drawn, hidden: number, selected: Selected): { lastIds.push(id); }); if (extra) { - nodes.push({ id: "more:groups", type: "more", position: grid(cells, 380, shown.length, GROUPS_PER_ROW, GROUP_ROW), data: { label: `${plural(extra, "more group")} in the list` } }); + nodes.push({ id: "more:groups", type: "more", position: grid(cells, 380, shown.length, GROUPS_PER_ROW, GROUP_ROW), data: { label: `+${plural(extra, "more group")}` } }); edge("eg-more", "stage:tool_call", "more:groups", { style: { strokeDasharray: "4 4" } }); } bottom = 380 + rows(cells, GROUPS_PER_ROW) * GROUP_ROW; @@ -284,7 +315,7 @@ function layout(map: AgentMap, d: Drawn, hidden: number, selected: Selected): { // The opened group's tools hang off it; its own node carries the edge onward. ts.forEach((tl, i) => toolNode(tl, grid(n, top, i), from, `o${i}`)); if (rest) { - nodes.push({ id: "more:tools", type: "more", position: grid(n, top, ts.length), data: { label: `${rest} more in the list` } }); + nodes.push({ id: "more:tools", type: "more", position: grid(n, top, ts.length), data: { label: `+${rest} more in ${d.open.label.toLowerCase()}` } }); edge("eo-more", from, "more:tools", { style: { strokeDasharray: "4 4" } }); } bottom = top + rows(n) * ROW; @@ -297,10 +328,25 @@ function layout(map: AgentMap, d: Drawn, hidden: number, selected: Selected): { } } - map.subagents.forEach((s, i) => { + // Sub-agents to the right, below the reply. One the code hands work to but traffic + // has not used yet is drawn fainter, with a still wire. + const subs = drawnSubagents(map.subagents, DRAW_SUBAGENTS); + subs.shown.forEach((s, i) => { const id = `sub:${s.slug}`; - nodes.push({ id, type: "subagent", position: { x: agentX + 2 * COL + 40, y: 200 + i * 120 }, data: s }); - edge(`es-${i}`, "agent", id, { sourceHandle: "down", style: { strokeDasharray: "6 4" } }); + nodes.push({ id, type: "subagent", position: { x: agentX + 2 * COL + 40, y: 200 + i * 120 }, data: { link: s, framework: map.agent.framework } }); + edge(`es-${i}`, "agent", id, { sourceHandle: "down", animated: Boolean(s.seen), style: { strokeDasharray: "6 4", opacity: s.seen ? 1 : 0.6 } }); + }); + if (subs.rest) { + const id = "more:subagents"; + nodes.push({ id, type: "more", position: { x: agentX + 2 * COL + 40, y: 200 + subs.shown.length * 120 }, data: { label: `${plural(subs.rest, "more sub-agent")}`, from: "left" } }); + edge("es-more", "agent", id, { sourceHandle: "down", style: { strokeDasharray: "4 4" } }); + } + + // Whoever hands this agent work, to the left of the user, so the map links back up. + (map.parents || []).slice(0, 3).forEach((p, i) => { + const id = `parent:${p.slug}`; + nodes.push({ id, type: "parent", position: { x: -COL - 10, y: 25 + i * 120 }, data: { link: p } }); + edge(`ep-${i}`, id, "agent", { targetHandle: "in", animated: Boolean(p.seen), style: { strokeDasharray: "6 4", opacity: p.seen ? 1 : 0.6 } }); }); return { nodes, edges }; } @@ -464,15 +510,45 @@ function StatLine({ days, s, noun }: { days: number; s: Stats; noun: string }) { ); } -function Panel({ map, selected, groups, onSelect }: { map: AgentMap; selected: Selected; groups: ToolGroup[]; onSelect: (s: Selected) => void }) { +function Panel({ + map, + selected, + groups, + onSelect, + summary, + toolList, +}: { + map: AgentMap; + selected: Selected; + groups: ToolGroup[]; + onSelect: (s: Selected) => void; + summary?: ReactNode; + toolList: ReactNode; +}) { const slug = map.agent.slug; + // Figma's rule: with nothing selected, the inspector is about the whole file. + const back = ( + + ); + if (selected?.kind === "tools") + return ( +
+ {back} + {toolList} +
+ ); if (selected?.kind === "stage") { const s = map.stages.find((x) => x.key === selected.key); if (s) return (
+ {back} {s.title} +

Guardrails here

+

Checks that run here

{s.detectors.length ? (
@@ -488,8 +564,6 @@ function Panel({ map, selected, groups, onSelect }: { map: AgentMap; selected: S

Choose checks

-

Rules for this agent

-
); } @@ -499,6 +573,7 @@ function Panel({ map, selected, groups, onSelect }: { map: AgentMap; selected: S const s = groupSummary(g.tools); return (
+ {back} {g.label}

{plural(g.tools.length, "tool")} · {s.allowed} allowed · {s.ask} ask · {s.denied} not allowed @@ -515,22 +590,39 @@ function Panel({ map, selected, groups, onSelect }: { map: AgentMap; selected: S } } const tl = selected?.kind === "tool" ? map.tools.find((t) => t.key === selected.key) : null; - if (!tl) + if (!tl) { + const t = groupSummary(map.tools); return (

- Click a step or a tool -

See and change what applies there.

+ {summary} + {map.tools.length > 0 && ( + <> +

Tools

+ + + )} {map.everywhere.length > 0 && ( <>

At every step

)} +

Click a step or a tool on the canvas to see and change its guardrails.

); + } const limits = Object.entries(tl.limits || {}); return (
+ {back} {tl.key}
{IMPACT[tl.impact || ""] || "Risk not set"} @@ -566,7 +658,7 @@ function Panel({ map, selected, groups, onSelect }: { map: AgentMap; selected: S )}

Every call also passes the Tool calls step.

- + Limits and access
@@ -691,13 +783,15 @@ function Legend() { } /** - * One agent's guardrails, drawn where they act: on the user's message, on each tool - * call, on what tools return, and on the reply. Many tools are drawn as groups, with - * every tool in the searchable list beside the canvas. Click a step or tool to see - * its rules and change them, or the tool's access, for this agent. + * One project's guardrails, drawn where they act: on the user's message, on each + * tool call, on what tools return, and on the reply. The canvas shows the shape + * first: many tools are one node until expanded, then groups, then a group's + * tools, each with "+N more" into the inspector's list. The inspector beside it + * shows what is selected, and the project itself (`summary`) when nothing is. */ -export function GuardrailMap({ map }: { map: AgentMap }) { +export function GuardrailMap({ map, summary }: { map: AgentMap; summary?: ReactNode }) { const [selected, setSelected] = useState(null); + const [expanded, setExpanded] = useState(false); const [query, setQuery] = useState(""); const [by, setBy] = useState("impact"); const [filter, setFilter] = useState("all"); @@ -711,31 +805,68 @@ export function GuardrailMap({ map }: { map: AgentMap }) { : selected?.kind === "tool" ? groups.find((g) => g.tools.some((t) => t.key === selected.key))?.key || null : null; - const d = useMemo(() => drawn(visible, by, openKey), [visible, by, openKey]); - const { nodes, edges } = useMemo(() => layout(map, d, map.tools.length - visible.length, selected), [map, d, visible.length, selected]); + // Choosing a tool from the list draws it, which needs the tools expanded. + const showAll = expanded || selected?.kind === "tool" || selected?.kind === "group"; + const d = useMemo(() => drawTools(visible, by, showAll, openKey), [visible, by, showAll, openKey]); + const { nodes, edges } = useMemo(() => layout(map, d, visible, map.tools.length - visible.length, selected), [map, d, visible, selected]); const many = map.tools.length > DRAW_EACH; - // With many tools the side column holds the tool list and the details, one at a time, - // so the canvas keeps its width. - const [tab, setTab] = useState<"tools" | "details">(many ? "tools" : "details"); - const choose = (next: Selected) => { - setSelected(next); - if (next) setTab("details"); - }; + const toolList = ( + + ); return (
- +
+ + {many && ( +
+ {showAll && !d.collapsed && ( + + )} + +
+ )} +
{ const [kind, ...rest] = node.id.split(":"); const key = rest.join(":"); - if (kind === "stage" || kind === "tool") choose({ kind, key }); - else if (kind === "group") choose(openKey === key && selected?.kind === "group" ? null : { kind: "group", key }); - else if (kind !== "more") setSelected(null); + if (kind === "stage" || kind === "tool") setSelected({ kind, key }); + else if (kind === "tools") { + setExpanded(true); + setSelected(null); + } else if (kind === "group") setSelected(openKey === key && selected?.kind === "group" ? null : { kind: "group", key }); + else if (node.id === "more:groups") setSelected({ kind: "tools", key: "" }); + else if (node.id === "more:tools" && d.open) setSelected({ kind: "group", key: d.open.key }); + else setSelected(null); }} onPaneClick={() => setSelected(null)} > @@ -756,32 +892,7 @@ export function GuardrailMap({ map }: { map: AgentMap }) {
- {many && ( -
- - -
- )} - {many && tab === "tools" ? ( - - ) : ( - - )} +
); diff --git a/dashboard/components/product/agent/GuardrailMapLazy.tsx b/dashboard/components/product/agent/GuardrailMapLazy.tsx index 01104df5..f493b814 100644 --- a/dashboard/components/product/agent/GuardrailMapLazy.tsx +++ b/dashboard/components/product/agent/GuardrailMapLazy.tsx @@ -1,11 +1,12 @@ "use client"; import dynamic from "next/dynamic"; +import type { ReactNode } from "react"; import type { AgentMap } from "./GuardrailMap"; /** * The map's graph library is the heaviest thing on the agent page; load it only when - * the Map tab is open, not with every agent view. + * the canvas is open, not with every project view. */ const Map = dynamic(() => import("./GuardrailMap").then((m) => m.GuardrailMap), { ssr: false, @@ -18,6 +19,6 @@ const Map = dynamic(() => import("./GuardrailMap").then((m) => m.GuardrailMap), ), }); -export function GuardrailMapLazy({ map }: { map: AgentMap }) { - return ; +export function GuardrailMapLazy({ map, summary }: { map: AgentMap; summary?: ReactNode }) { + return ; } diff --git a/dashboard/components/product/features/FeaturesEditor.tsx b/dashboard/components/product/features/FeaturesEditor.tsx new file mode 100644 index 00000000..7f64498f --- /dev/null +++ b/dashboard/components/product/features/FeaturesEditor.tsx @@ -0,0 +1,244 @@ +"use client"; + +import Link from "next/link"; +import { useState } from "react"; +import { detectorName } from "@/lib/product/vocab"; +import { + LEVEL_LABEL, + changedCount, + checkGroups, + levelsFor, + lockedReason, + type CheckFeature, + type Features, + type Level, + type ProtectionFeature, +} from "@/lib/product/features"; + +const WHERE: Record = { input: "messages", output: "replies", tools: "tool calls", context: "documents" }; + +async function send(url: string, method: string, body?: unknown): Promise<{ ok: boolean; data: any }> { + const res = await fetch(url, { + method, + headers: { "Content-Type": "application/json" }, + body: body === undefined ? undefined : JSON.stringify(body), + }); + return { ok: res.ok, data: await res.json().catch(() => ({})) }; +} + +function errorOf(data: any, fallback: string): string { + const d = data?.detail; + if (typeof d === "string") return d; + if (d && typeof d.message === "string") return d.message; + return fallback; +} + +/** + * The workspace's switches, or one project's. With a project, each row says whether + * it inherits the workspace's value or changed it, and a change can be undone with + * Reset; a lower level the workspace has not opened to projects is shown but locked, + * with the reason, rather than offered and refused. + */ +export function FeaturesEditor({ initial, canOpen = true }: { initial: Features; canOpen?: boolean }) { + const [f, setF] = useState(initial); + const [busy, setBusy] = useState(null); + const [error, setError] = useState<{ key: string; text: string } | null>(null); + const project = f.agent; + const slug = project ? encodeURIComponent(project) : ""; + + const run = async (key: string, url: string, method: string, body?: unknown) => { + setBusy(key); + setError(null); + const r = await send(url, method, body); + setBusy(null); + if (!r.ok) return setError({ key, text: errorOf(r.data, "Could not save this.") }); + setF(r.data as Features); + }; + + const setProtection = (p: ProtectionFeature, level: Level) => + run(`p:${p.key}`, `/api/features/protections/${encodeURIComponent(p.key)}`, "POST", { level, agent: project || "" }); + const openProtection = (p: ProtectionFeature, open: boolean) => + run(`p:${p.key}`, `/api/features/protections/${encodeURIComponent(p.key)}`, "POST", { open_to_projects: open }); + const resetProtection = (p: ProtectionFeature) => + run(`p:${p.key}`, `/api/agents/${slug}/features/protections/${encodeURIComponent(p.key)}`, "DELETE"); + const setCheck = (c: CheckFeature, enabled: boolean) => + run(`c:${c.key}`, `/api/features/checks/${encodeURIComponent(c.key)}`, "POST", { enabled, agent: project || "" }); + const resetCheck = (c: CheckFeature) => run(`c:${c.key}`, `/api/agents/${slug}/features/checks/${encodeURIComponent(c.key)}`, "DELETE"); + + const changed = project ? changedCount(f) : 0; + const groups = checkGroups(f.checks); + + return ( +
+ {project && ( +

+ Everything here starts as the workspace sets it.{" "} + {changed ? {changed} changed for this project. : "Nothing changed for this project yet."}{" "} + Workspace features +

+ )} + +
+
+

Protections

+ + {project ? "Stricter is always allowed. Lower only where the workspace allows it." : "What every project gets unless it changes it."} + +
+
    + {f.protections.map((p) => { + const value = p.project ?? p.workspace; + const inherited = project && p.project === null; + const mode = project && p.project !== null ? p.project_mode : p.workspace_mode; + return ( +
  • +
    + {p.title} + + {!p.available ? ( + `Needs the ${p.pack} pack` + ) : project ? ( + inherited ? ( + <>Inherited from the workspace + ) : ( + <> + Changed here · workspace is {LEVEL_LABEL[p.workspace].toLowerCase()} + {p.effective !== p.project && " (the workspace's still applies)"} + + ) + ) : ( + <>{p.pack} + )} + {mode && value !== "off" && {mode}} + {project && p.available && levelsFor(p).some((l) => lockedReason(p, l)) && ( + + lower levels locked by the workspace + + )} + +
    +
    +
    + {levelsFor(p).map((l) => { + const locked = project ? lockedReason(p, l) : null; + return ( + + ); + })} +
    + {project && p.project !== null && ( + + )} + {!project && !p.protected && p.installed && canOpen && ( + + )} + {!project && p.protected && Locked} +
    + {error?.key === `p:${p.key}` &&

    {error.text}

    } +
  • + ); + })} +
+
+ +
+
+

Checks

+ + {project ? "Switch a check on or off for this project only, either way." : "The detectors that run on every project's traffic."} + +
+ {groups.map((g) => { + const installed = g.checks.filter((c) => c.installed); + const missing = g.checks.filter((c) => !c.installed); + return ( +
+

{g.title}

+
    + {installed.map((c) => ( + + ))} +
+ {missing.length > 0 && ( +
+ {missing.length} not installed +
    + {missing.map((c) => ( + + ))} +
+ How to install checks +
+ )} +
+ ); + })} +
+
+ ); +} + +function CheckRow({ + c, + project, + busy, + error, + onSet, + onReset, +}: { + c: CheckFeature; + project: string | null; + busy: boolean; + error: string | null; + onSet: (c: CheckFeature, on: boolean) => void; + onReset: (c: CheckFeature) => void; +}) { + const value = project ? c.effective : c.workspace; + const inherited = project && c.project === null; + const name = c.label || detectorName(c.key); + return ( +
  • +
    + {name} + + {c.where.map((w) => WHERE[w] || w).join(", ")} + {c.always_on ? " · always on" : ""} + {project && !c.always_on && c.installed ? (inherited ? " · inherited" : ` · changed here, workspace is ${c.workspace ? "on" : "off"}`) : ""} + +
    +
    +
    + {[true, false].map((on) => { + const disabled = busy || (!c.installed && on) || (c.always_on && !on); + return ( + + ); + })} +
    + {project && c.project !== null && ( + + )} +
    + {error &&

    {error}

    } +
  • + ); +} diff --git a/dashboard/components/product/policies/advanced.tsx b/dashboard/components/product/policies/advanced.tsx index d97282b1..40756bfd 100644 --- a/dashboard/components/product/policies/advanced.tsx +++ b/dashboard/components/product/policies/advanced.tsx @@ -328,7 +328,7 @@ export async function AdvancedTuning({ agent }: { agent?: string }) { {s.detector_key} - {s.agent === "*" ? All agents : {s.agent}} + {s.agent === "*" ? All agents : {s.agent}} {s.entity_type && {s.entity_type}} {s.reason || "-"} diff --git a/dashboard/components/product/start/FirstRequestWatcher.tsx b/dashboard/components/product/start/FirstRequestWatcher.tsx index 0738e1ff..f0b60019 100644 --- a/dashboard/components/product/start/FirstRequestWatcher.tsx +++ b/dashboard/components/product/start/FirstRequestWatcher.tsx @@ -75,7 +75,7 @@ export function FirstRequestWatcher({
    See activity → - Review agents → + Review agents →
    ); diff --git a/dashboard/components/product/welcome/WelcomeFlow.tsx b/dashboard/components/product/welcome/WelcomeFlow.tsx index 647e09e2..7caabbdf 100644 --- a/dashboard/components/product/welcome/WelcomeFlow.tsx +++ b/dashboard/components/product/welcome/WelcomeFlow.tsx @@ -173,7 +173,7 @@ export function WelcomeFlow({ - + Review one by one instead
    @@ -194,7 +194,7 @@ export function WelcomeFlow({ {plural(accepted.agents.length, "agent")} registered, {plural(accepted.policies.length, "policy", "policies")} live in observe. - Agents + Projects Issues
    ) diff --git a/dashboard/components/ui/index.tsx b/dashboard/components/ui/index.tsx index a6717f49..f9478513 100644 --- a/dashboard/components/ui/index.tsx +++ b/dashboard/components/ui/index.tsx @@ -38,7 +38,7 @@ export function AgentLink({ }) { const name = agents.find((a) => a.slug === slug)?.name; return ( - + {name || slug} ); diff --git a/dashboard/lib/docs/pages.ts b/dashboard/lib/docs/pages.ts index 22cdf62c..a907525d 100644 --- a/dashboard/lib/docs/pages.ts +++ b/dashboard/lib/docs/pages.ts @@ -52,7 +52,7 @@ export const DOC_NAV: DocSection[] = [ items: [ { href: "/docs/app", label: "Tour of the web app", description: "Sign in, workspaces, and where everything is." }, { href: "/docs/app/start", label: "Start here and connect", description: "The checklist, GitHub repos, API tokens." }, - { href: "/docs/app/agents", label: "Agents", description: "Registry, risk, knowledge boundary, kill switch." }, + { href: "/docs/app/projects", label: "Projects", description: "Canvas, features per project, setup banner, kill switch." }, { href: "/docs/app/findings", label: "Findings", description: "What needs a person, and why." }, { href: "/docs/app/traces", label: "Traces", description: "One request, every check, and why it was blocked." }, { href: "/docs/app/policies", label: "Policies and tuning", description: "Rules, modes, simulation, canary, detector tuning." }, diff --git a/dashboard/lib/generated/reference/api.json b/dashboard/lib/generated/reference/api.json index 6775ee3c..a6b9921b 100644 --- a/dashboard/lib/generated/reference/api.json +++ b/dashboard/lib/generated/reference/api.json @@ -1,6 +1,6 @@ { "generated_by": "scripts/gen/docs_reference.py", - "count": 277, + "count": 285, "routes": [ { "method": "GET", @@ -888,6 +888,35 @@ ], "body": null }, + { + "method": "POST", + "path": "/api/agents/{agent_id}/approve-system", + "section": "agents", + "audience": "operator", + "summary": "Approve a proposed agent and every proposed agent it hands work to, in one step.", + "description": "Approve a proposed agent and every proposed agent it hands work to, in one step.\n\nA scan proposes a multi-agent system as one draft per agent, joined by the\ndelegations its code declares. Adding the orchestrator without its sub-agents\nleaves a live agent handing work to drafts nothing governs, so the Proposed list\noffers this beside each system. Each agent gets the same state change and ledger\nentry as approving it on its own (`approve_agent`); one already added is skipped.", + "params": [ + { + "name": "agent_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "type": null + }, + { + "name": "x-agentfox-user", + "in": "header", + "required": false, + "type": null + } + ], + "body": null + }, { "method": "POST", "path": "/api/agents/{agent_id}/reject", @@ -1337,6 +1366,105 @@ ], "body": null }, + { + "method": "GET", + "path": "/api/agents/{slug}/features", + "section": "agents", + "audience": "operator", + "summary": "Get Agent Features", + "description": "", + "params": [ + { + "name": "slug", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "type": null + }, + { + "name": "x-agentfox-user", + "in": "header", + "required": false, + "type": null + } + ], + "body": null + }, + { + "method": "DELETE", + "path": "/api/agents/{slug}/features/checks/{key}", + "section": "agents", + "audience": "operator", + "summary": "Inherit Check", + "description": "", + "params": [ + { + "name": "slug", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "key", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "type": null + }, + { + "name": "x-agentfox-user", + "in": "header", + "required": false, + "type": null + } + ], + "body": null + }, + { + "method": "DELETE", + "path": "/api/agents/{slug}/features/protections/{key}", + "section": "agents", + "audience": "operator", + "summary": "Inherit Protection", + "description": "", + "params": [ + { + "name": "slug", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "key", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "type": null + }, + { + "name": "x-agentfox-user", + "in": "header", + "required": false, + "type": null + } + ], + "body": null + }, { "method": "POST", "path": "/api/agents/{slug}/kill", @@ -1702,6 +1830,35 @@ ] } }, + { + "method": "GET", + "path": "/api/agents/{slug}/setup", + "section": "agents", + "audience": "operator", + "summary": "What this project still needs, for the banner a project's pages show.", + "description": "What this project still needs, for the banner a project's pages show.\n\nComputed live, like the workspace checklist (`/api/onboarding`). The steps are in\nthe order they unblock each other: a proposed project is added first, nothing can\nbe judged before traffic arrives, tools decide what it may do, and blocking comes\nlast because it is only safe once watching looks right. ``next`` is the first\none not done; None when the project is fully set up.", + "params": [ + { + "name": "slug", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "type": null + }, + { + "name": "x-agentfox-user", + "in": "header", + "required": false, + "type": null + } + ], + "body": null + }, { "method": "GET", "path": "/api/agents/{slug}/signing-key", @@ -4023,6 +4180,18 @@ "type": "array", "required": false, "description": "" + }, + { + "name": "agents", + "type": "array", + "required": false, + "description": "" + }, + { + "name": "agent_edges", + "type": "array", + "required": false, + "description": "" } ] } @@ -5466,6 +5635,137 @@ ], "body": null }, + { + "method": "GET", + "path": "/api/features", + "section": "features", + "audience": "operator", + "summary": "Get Features", + "description": "", + "params": [ + { + "name": "authorization", + "in": "header", + "required": false, + "type": null + }, + { + "name": "x-agentfox-user", + "in": "header", + "required": false, + "type": null + } + ], + "body": null + }, + { + "method": "POST", + "path": "/api/features/checks/{key}", + "section": "features", + "audience": "operator", + "summary": "Switch a check on or off for every project, or for one. Switching on one that is not installed is refused: it would look on and check nothing.", + "description": "Switch a check on or off for every project, or for one. Switching on one that\nis not installed is refused: it would look on and check nothing.", + "params": [ + { + "name": "key", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "type": null + }, + { + "name": "x-agentfox-user", + "in": "header", + "required": false, + "type": null + } + ], + "body": { + "model": "agentfox__apps__gateway__routes__features__CheckIn", + "fields": [ + { + "name": "enabled", + "type": "boolean", + "required": true, + "description": "" + }, + { + "name": "agent", + "type": "string", + "required": false, + "description": "" + }, + { + "name": "reason", + "type": "string", + "required": false, + "description": "" + } + ] + } + }, + { + "method": "POST", + "path": "/api/features/protections/{key}", + "section": "features", + "audience": "operator", + "summary": "Set Protection", + "description": "", + "params": [ + { + "name": "key", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "authorization", + "in": "header", + "required": false, + "type": null + }, + { + "name": "x-agentfox-user", + "in": "header", + "required": false, + "type": null + } + ], + "body": { + "model": "ProtectionIn", + "fields": [ + { + "name": "level", + "type": "string", + "required": false, + "description": "" + }, + { + "name": "agent", + "type": "string", + "required": false, + "description": "" + }, + { + "name": "open_to_projects", + "type": "boolean", + "required": false, + "description": "" + }, + { + "name": "reason", + "type": "string", + "required": false, + "description": "" + } + ] + } + }, { "method": "GET", "path": "/api/findings", diff --git a/dashboard/lib/product/features.test.ts b/dashboard/lib/product/features.test.ts new file mode 100644 index 00000000..71bf0c24 --- /dev/null +++ b/dashboard/lib/product/features.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from "vitest"; +import { changedCount, checkGroups, levelsFor, lockedReason, type CheckFeature, type Features, type ProtectionFeature } from "./features"; + +const p = (extra: Partial = {}): ProtectionFeature => ({ + key: "harmful", + title: "Harmful content", + graded: true, + pack: "baseline", + installed: true, + available: true, + workspace: "medium", + workspace_mode: "watching", + open_to_projects: false, + protected: false, + project: null, + project_mode: null, + effective: "medium", + needs: null, + ...extra, +}); + +const c = (key: string, extra: Partial = {}): CheckFeature => ({ + key, + label: null, + group: "content", + group_title: "Harmful content", + source: "builtin", + where: ["input"], + installed: true, + always_on: false, + workspace: true, + project: null, + effective: true, + ...extra, +}); + +describe("features", () => { + it("offers levels only to graded protections", () => { + expect(levelsFor(p())).toEqual(["off", "low", "medium", "high"]); + expect(levelsFor(p({ graded: false, workspace: "on" }))).toEqual(["off", "on"]); + }); + + it("lets a project be stricter, and lower only where the workspace allows", () => { + expect(lockedReason(p(), "high")).toBeNull(); + expect(lockedReason(p(), "medium")).toBeNull(); + expect(lockedReason(p(), "off")).toMatch(/does not let projects/); + expect(lockedReason(p({ open_to_projects: true }), "off")).toBeNull(); + expect(lockedReason(p({ open_to_projects: true, protected: true }), "low")).toMatch(/cannot be lowered/); + }); + + it("counts what a project changed", () => { + const f: Features = { agent: "a", levels: [], protections: [p(), p({ key: "x", project: "high" })], checks: [c("a"), c("b", { project: false })] }; + expect(changedCount(f)).toBe(2); + }); + + it("groups checks with installed ones first", () => { + const groups = checkGroups([c("z"), c("a", { installed: false }), c("m", { group: "data", group_title: "Data" })]); + expect(groups.map((g) => g.key)).toEqual(["content", "data"]); + expect(groups[0].checks.map((x) => x.key)).toEqual(["z", "a"]); + }); +}); diff --git a/dashboard/lib/product/features.ts b/dashboard/lib/product/features.ts new file mode 100644 index 00000000..3937cacc --- /dev/null +++ b/dashboard/lib/product/features.ts @@ -0,0 +1,82 @@ +/** + * Features (`/api/features`, `/api/agents/{slug}/features`): what the workspace turns + * on for every project, and what one project changed. Types and the small decisions + * the editor makes, kept apart from it so they can be tested. + */ + +export type Level = "off" | "on" | "low" | "medium" | "high"; + +export type ProtectionFeature = { + key: string; + title: string; + graded: boolean; + pack: string; + installed: boolean; + available: boolean; + workspace: Level; + workspace_mode: "blocking" | "watching" | null; + open_to_projects: boolean; + protected: boolean; + /** This project's own value; null: it inherits the workspace's. */ + project: Level | null; + project_mode: "blocking" | "watching" | null; + effective: Level; + needs: string | null; +}; + +export type CheckFeature = { + key: string; + label: string | null; + group: string; + group_title: string; + source: string; + where: string[]; + installed: boolean; + always_on: boolean; + workspace: boolean; + project: boolean | null; + effective: boolean; +}; + +export type Features = { + agent: string | null; + levels: Level[]; + protections: ProtectionFeature[]; + checks: CheckFeature[]; +}; + +const RANK: Record = { off: 0, low: 1, on: 2, medium: 2, high: 3 }; + +/** The choices a protection offers. */ +export function levelsFor(p: ProtectionFeature): Level[] { + return p.graded ? ["off", "low", "medium", "high"] : ["off", "on"]; +} + +export const LEVEL_LABEL: Record = { off: "Off", on: "On", low: "Low", medium: "Medium", high: "High" }; + +/** + * Why a project may not pick this level, or null when it may. Stricter than the + * workspace is always allowed; lower only where the workspace opened it to projects. + */ +export function lockedReason(p: ProtectionFeature, level: Level): string | null { + if (RANK[level] >= RANK[p.workspace]) return null; + if (p.protected) return "This protection cannot be lowered for a single project."; + if (!p.open_to_projects) return "The workspace does not let projects lower this. Change it in workspace Features."; + return null; +} + +/** How many switches this project changed from the workspace. */ +export function changedCount(f: Features): number { + return f.protections.filter((p) => p.project !== null).length + f.checks.filter((c) => c.project !== null).length; +} + +/** Checks by group, installed ones first within each. */ +export function checkGroups(checks: CheckFeature[]): { key: string; title: string; checks: CheckFeature[] }[] { + const groups = new Map(); + for (const c of checks) { + if (!groups.has(c.group)) groups.set(c.group, { key: c.group, title: c.group_title, checks: [] }); + groups.get(c.group)!.checks.push(c); + } + for (const g of groups.values()) g.checks.sort((a, b) => Number(b.installed) - Number(a.installed) || a.key.localeCompare(b.key)); + return [...groups.values()]; +} diff --git a/dashboard/lib/product/guardrailMap.test.ts b/dashboard/lib/product/guardrailMap.test.ts index 3833b625..f9da13d9 100644 --- a/dashboard/lib/product/guardrailMap.test.ts +++ b/dashboard/lib/product/guardrailMap.test.ts @@ -1,5 +1,7 @@ import { describe, expect, it } from "vitest"; import { + DRAW_EACH, + drawTools, filterTools, groupSummary, groupTools, @@ -108,3 +110,25 @@ describe("changes", () => { expect(permissionCall("a", viaPattern, "allowed")?.body).toMatchObject({ tool_key: "crm.lookup", requires_approval: false, constraints: {} }); }); }); + +describe("what the canvas draws", () => { + const many = Array.from({ length: 40 }, (_, i) => tool(i)); + + it("draws a few tools one by one", () => { + const few = many.slice(0, DRAW_EACH); + expect(drawTools(few, "impact", false, null)).toMatchObject({ collapsed: false, tools: few, groups: null }); + }); + + it("collapses many tools into one node until expanded", () => { + expect(drawTools(many, "impact", false, null)).toMatchObject({ collapsed: true, tools: [], groups: null }); + const open = drawTools(many, "impact", true, "impact:write"); + expect(open.collapsed).toBe(false); + expect(open.groups?.length).toBe(5); + expect(open.open?.label).toBe("Writes"); + }); + + it("opens the only group there is", () => { + const same = many.map((t) => ({ ...t, impact: "read" })); + expect(drawTools(same, "impact", true, null).open?.label).toBe("Read only"); + }); +}); diff --git a/dashboard/lib/product/guardrailMap.ts b/dashboard/lib/product/guardrailMap.ts index d27d2074..7950e982 100644 --- a/dashboard/lib/product/guardrailMap.ts +++ b/dashboard/lib/product/guardrailMap.ts @@ -38,15 +38,76 @@ export type MapTool = { sources?: ("code" | "granted" | "seen")[]; grant?: { id: string; key: string; max_taint: string; actions: string[] } | null; }; +/** How a parent hands work to a sub-agent, when its code says (a repo scan read it). */ +export type Relation = "handoff" | "as_tool" | "delegates" | "member"; +/** One end of a delegation: a sub-agent this agent hands work to, or a parent that hands it work. */ +export type MapLink = { + slug: string; + name?: string; + calls: number; + /** Read from the code by a repo scan. */ + declared?: boolean; + /** Seen in traffic. */ + seen?: boolean; + relation?: Relation | string | null; +}; export type AgentMap = { - agent: { slug: string; name: string }; + agent: { slug: string; name: string; framework?: string | null }; window_days: number; stages: Stage[]; everywhere: MapRule[]; tools: MapTool[]; - subagents: { slug: string; calls: number }[]; + subagents: MapLink[]; + /** The agents that hand this one work. */ + parents?: MapLink[]; }; +// --- sub-agents -------------------------------------------------------------------- + +/** What the arrow to a sub-agent means, in words. A crew's members are a crew; other teams are a team. */ +export function relationLabel(relation: MapLink["relation"], framework?: string | null): string { + switch (relation) { + case "handoff": + return "Hands off to"; + case "as_tool": + return "Used as a tool"; + case "member": + return framework === "CrewAI" ? "Crew member" : "Team member"; + default: + return "Delegates to"; + } +} + +/** The same relation seen from the sub-agent's side. */ +export function parentLabel(relation: MapLink["relation"]): string { + switch (relation) { + case "as_tool": + return "Used as a tool by"; + case "member": + return "Member of"; + case "handoff": + return "Handed off from"; + default: + return "Called by"; + } +} + +/** "3 calls", or for a delegation only the code knows about yet, "From code · no calls yet". */ +export function linkActivity(s: MapLink): string { + if (s.calls > 0) return `${s.calls} ${s.calls === 1 ? "call" : "calls"}`; + return s.declared ? "From code · no calls yet" : "No calls yet"; +} + +/** At most this many sub-agents are drawn; the rest are summed up in one node. */ +export const DRAW_SUBAGENTS = 6; + +/** The sub-agents drawn, busiest first, and how many are left out. */ +export function drawnSubagents(subs: MapLink[], max = DRAW_SUBAGENTS): { shown: MapLink[]; rest: number } { + const ranked = [...subs].sort((a, b) => b.calls - a.calls || a.slug.localeCompare(b.slug)); + const shown = ranked.length > max ? ranked.slice(0, max - 1) : ranked; + return { shown, rest: ranked.length - shown.length }; +} + // --- tone ------------------------------------------------------------------------ export type Tone = "on" | "watch" | "off"; @@ -231,12 +292,27 @@ export function filterTools(tools: MapTool[], query: string, filter: ToolFilter) }); } -/** Up to this many tools are drawn one by one; more are drawn as groups. */ -export const DRAW_EACH = 12; -/** At most this many tools of an opened group are drawn; the rest are in the list. */ -export const DRAW_OPEN = 12; -/** At most this many groups are drawn; the rest are in the list. */ -export const DRAW_GROUPS = 12; +/** + * The canvas shows a project's shape before its detail. Up to this many tools are + * drawn one by one; with more, they are one "Tools" node until it is expanded, and + * then one node per group. + */ +export const DRAW_EACH = 6; +/** At most this many tools of an opened group are drawn; "+N more" opens the rest in the inspector. */ +export const DRAW_OPEN = 6; +/** At most this many groups are drawn; "+N more" opens the whole list in the inspector. */ +export const DRAW_GROUPS = 6; + +export type Drawn = { collapsed: boolean; tools: MapTool[]; groups: ToolGroup[] | null; open: ToolGroup | null }; + +/** What the canvas draws for these tools. A single group is drawn open: there is nothing to choose. */ +export function drawTools(visible: MapTool[], by: GroupBy, expanded: boolean, openKey: string | null): Drawn { + if (visible.length <= DRAW_EACH) return { collapsed: false, tools: visible, groups: null, open: null }; + if (!expanded) return { collapsed: true, tools: [], groups: null, open: null }; + const groups = groupTools(visible, by); + const open = groups.length === 1 ? groups[0] : groups.find((g) => g.key === openKey) || null; + return { collapsed: false, tools: [], groups, open }; +} export function groupSummary(tools: MapTool[]) { const by = (p: Permission) => tools.filter((t) => t.permission === p).length; diff --git a/dashboard/lib/product/nav.ts b/dashboard/lib/product/nav.ts new file mode 100644 index 00000000..c6b3b6fa --- /dev/null +++ b/dashboard/lib/product/nav.ts @@ -0,0 +1,71 @@ +/** + * The sidebar, in each of its two scopes (see lib/product/project.ts). + * + * All projects is the workspace: is anything wrong (Home), what is happening + * (Observe), what is broken (Issues), which projects (Projects), what is allowed + * (Policies), which switches are on (Features), what needs me (Approvals), does it hold up (Test), show others + * (Reports). One project is that project's pages only: its canvas (the map of + * its guardrails, and everything about it when nothing is selected), its traffic, + * what it may do, which features it changed, its issues and approvals, its settings. The workspace pages + * stay one click away, under All projects in the switcher. + * + * A row lists the other pages it stands for, so it stays lit on all of them, and + * may name the icon it shows when its href is not an icon's key. + * + * Plain data and functions: the server layout reads it for search, the client + * sidebar for its rows. + */ +import { projectHref } from "@/lib/product/project"; + +/** Label, href, other pages (path, or path?key=value) the row stands for, icon. */ +export type NavItem = [label: string, href: string, also?: string[], icon?: string]; +export type NavGroup = { group: string; items: NavItem[] }; + +const SETUP: NavGroup = { + group: "Setup", + items: [ + ["Get started", "/app/start"], + [ + "Settings", + "/app/settings", + ["/app/start?tab=connect", "/app/start?tab=tokens", "/app/sources", "/app/entitlement"], + ], + ], +}; + +export const WORKSPACE_NAV: NavGroup[] = [ + { + group: "", + items: [ + ["Home", "/app"], + ["Projects", "/app/projects"], + ["Observe", "/app/observe", ["/app/traces"]], + ["Issues", "/app/findings"], + ["Policies", "/app/policies"], + ["Features", "/app/features"], + ["Approvals", "/app/approvals"], + ["Test", "/app/test", ["/app/evals"]], + ["Reports", "/app/reports", ["/app/compliance", "/app/coverage"]], + ], + }, + SETUP, +]; + +export function projectNav(slug: string): NavGroup[] { + const q = `agent=${encodeURIComponent(slug)}`; + return [ + { + group: "", + items: [ + ["Canvas", projectHref(slug), [`/app/projects/${encodeURIComponent(slug)}?view=canvas`], "canvas"], + ["Activity", projectHref(slug, "activity"), [`/app/observe?${q}`, `/app/traces?${q}`], "/app/observe"], + ["Access", projectHref(slug, "access"), [], "/app/entitlement"], + ["Features", projectHref(slug, "features"), [], "/app/features"], + ["Issues", `/app/findings?${q}`], + ["Approvals", `/app/approvals?${q}`], + ["Settings", projectHref(slug, "settings"), [], "/app/settings"], + ], + }, + { group: "Workspace", items: [["Get started", "/app/start"], ["Workspace settings", "/app/settings", SETUP.items[1][2]]] }, + ]; +} diff --git a/dashboard/lib/product/project.test.ts b/dashboard/lib/product/project.test.ts new file mode 100644 index 00000000..d521e24c --- /dev/null +++ b/dashboard/lib/product/project.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from "vitest"; +import { projectHref, projectInPath, projectView } from "./project"; +import { WORKSPACE_NAV, projectNav } from "./nav"; + +describe("project views", () => { + it("opens the canvas by default and keeps old agent tabs working", () => { + expect(projectView({})).toBe("canvas"); + expect(projectView({ view: "access" })).toBe("access"); + expect(projectView({ view: "nope" })).toBe("canvas"); + expect(projectView({ tab: "map" })).toBe("canvas"); + expect(projectView({ tab: "overview" })).toBe("canvas"); + expect(projectView({ tab: "rules" })).toBe("activity"); + expect(projectView({ tab: "quality" })).toBe("activity"); + expect(projectView({ tab: "registration" })).toBe("settings"); + // Inside Activity, `tab` picks its part and the view stays. + expect(projectView({ view: "activity", tab: "rules" })).toBe("activity"); + }); + + it("links to a view", () => { + expect(projectHref("support bot")).toBe("/app/projects/support%20bot"); + expect(projectHref("a", "activity", { tab: "rules", range: undefined })).toBe("/app/projects/a?view=activity&tab=rules"); + }); + + it("reads the project from a path", () => { + expect(projectInPath("/app/projects/billing")).toBe("billing"); + expect(projectInPath("/app/projects/billing/protect")).toBe("billing"); + expect(projectInPath("/app/projects")).toBeNull(); + expect(projectInPath("/app/findings")).toBeNull(); + }); +}); + +describe("sidebar scopes", () => { + it("keeps workspace pages out of a project's sidebar", () => { + const hrefs = projectNav("billing").flatMap((g) => g.items.map((i) => i[1])); + expect(hrefs).toContain("/app/findings?agent=billing"); + expect(hrefs).toContain("/app/approvals?agent=billing"); + expect(hrefs.some((h) => h.startsWith("/app/policies"))).toBe(false); + }); + + it("has no map page left to link to", () => { + const hrefs = WORKSPACE_NAV.flatMap((g) => g.items.map((i) => i[1])); + expect(hrefs).not.toContain("/app/map"); + expect(hrefs).toContain("/app/projects"); + }); +}); diff --git a/dashboard/lib/product/project.ts b/dashboard/lib/product/project.ts new file mode 100644 index 00000000..f8ae1f7b --- /dev/null +++ b/dashboard/lib/product/project.ts @@ -0,0 +1,71 @@ +/** + * Projects: what the API calls an agent, as the dashboard presents it. + * + * The dashboard works in one of two scopes. A project scope is one project and + * everything about it: its canvas, its activity, its issues and approvals. The + * all-projects scope is the workspace: every project's issues, the policies they + * share, the reports. The scope a person last chose is kept in a cookie, so the + * sidebar can show the matching set of pages on any page they open; the URL of a + * project page sets it too (see middleware.ts), so a link to a project lands in + * that project's scope. + * + * Kept free of server-only imports: middleware, server components and the + * switcher in the sidebar all read it. + */ + +/** The cookie holding the selected project's slug. Absent or empty: all projects. */ +export const PROJECT_COOKIE = "agentfox_project"; + +/** The views of one project, in the order the page offers them. */ +export const PROJECT_VIEWS = [ + { key: "canvas", label: "Canvas" }, + { key: "activity", label: "Activity" }, + { key: "access", label: "Access" }, + { key: "features", label: "Features" }, + { key: "settings", label: "Settings" }, +] as const; + +export type ProjectView = (typeof PROJECT_VIEWS)[number]["key"]; + +/** + * The agent page's seven tabs, and where each one's content lives now. The map, + * its rules and the overview are the canvas; everything about traffic is activity. + * Kept so a bookmarked `?tab=` still opens the right view. + */ +const VIEW_FOR_TAB: Record = { + overview: "canvas", + map: "canvas", + rules: "activity", + permissions: "activity", + activity: "activity", + quality: "activity", + access: "access", + features: "features", + settings: "settings", + registration: "settings", +}; + +/** The view a project URL asks for: `?view=`, else an old `?tab=`, else the canvas. */ +export function projectView(sp: { view?: string; tab?: string }): ProjectView { + if (sp.view && PROJECT_VIEWS.some((v) => v.key === sp.view)) return sp.view as ProjectView; + if (sp.tab && VIEW_FOR_TAB[sp.tab]) return VIEW_FOR_TAB[sp.tab]; + return "canvas"; +} + +/** A project's page, at a view. The canvas is the default and has no `?view=`. */ +export function projectHref(slug: string, view: ProjectView = "canvas", extra: Record = {}): string { + const q = new URLSearchParams(); + if (view !== "canvas") q.set("view", view); + for (const [k, v] of Object.entries(extra)) if (v) q.set(k, v); + const qs = q.toString(); + return `/app/projects/${encodeURIComponent(slug)}${qs ? `?${qs}` : ""}`; +} + +/** The project slug a `/app/projects/…` path names, if any. */ +export function projectInPath(pathname: string): string | null { + const m = pathname.match(/^\/app\/projects\/([^/?#]+)/); + return m ? decodeURIComponent(m[1]) : null; +} + +/** How long the chosen scope is remembered: a year, like any other preference. */ +export const PROJECT_COOKIE_AGE = 60 * 60 * 24 * 365; diff --git a/dashboard/lib/product/proposedSystems.test.ts b/dashboard/lib/product/proposedSystems.test.ts new file mode 100644 index 00000000..83e3fd15 --- /dev/null +++ b/dashboard/lib/product/proposedSystems.test.ts @@ -0,0 +1,83 @@ +import { describe, expect, it } from "vitest"; +import { drawnSubagents, linkActivity, parentLabel, relationLabel, type MapLink } from "./guardrailMap"; +import { groupProposed, memberRelation, type ProposedAgent } from "./proposedSystems"; + +function agent(slug: string, children: [string, string][] = []): ProposedAgent { + return { id: `id-${slug}`, slug, children: children.map(([s, relation]) => ({ slug: s, relation })) }; +} + +describe("groupProposed", () => { + it("puts a system's agents under the one that hands them work", () => { + const groups = groupProposed([ + agent("billing"), + agent("triage", [ + ["billing", "handoff"], + ["refunds", "as_tool"], + ]), + agent("refunds", [["ledger", "delegates"]]), + agent("ledger"), + agent("lonely"), + ]); + expect(groups.map((g) => g.root.slug)).toEqual(["triage", "lonely"]); + expect(groups[0].members.map((m) => [m.agent.slug, m.depth, m.relation])).toEqual([ + ["triage", 0, null], + ["billing", 1, "handoff"], + ["refunds", 1, "as_tool"], + ["ledger", 2, "delegates"], + ]); + expect(groups[1].members).toHaveLength(1); + }); + + it("lists an agent with two parents once, and cuts a cycle", () => { + const groups = groupProposed([ + agent("a", [["b", "handoff"]]), + agent("b", [["a", "handoff"]]), + agent("crew", [ + ["x", "member"], + ["y", "member"], + ]), + agent("x", [["y", "delegates"]]), + agent("y"), + ]); + expect(groups.map((g) => g.members.map((m) => m.agent.slug))).toEqual([["crew", "x", "y"], ["a", "b"]]); + }); + + it("ignores children that are not proposals any more", () => { + const groups = groupProposed([agent("triage", [["already-added", "handoff"]])]); + expect(groups).toHaveLength(1); + expect(groups[0].members).toHaveLength(1); + }); + + it("says how a member is reached", () => { + expect(memberRelation("handoff")).toBe("Handed off to"); + expect(memberRelation(null)).toBe(""); + expect(memberRelation("delegates")).toBe("Delegated to"); + }); +}); + +describe("sub-agents on the map", () => { + const link = (slug: string, calls: number, extra: Partial = {}): MapLink => ({ slug, calls, ...extra }); + + it("labels each relation in plain words", () => { + expect(relationLabel("handoff")).toBe("Hands off to"); + expect(relationLabel("as_tool")).toBe("Used as a tool"); + expect(relationLabel("member", "CrewAI")).toBe("Crew member"); + expect(relationLabel("member", "AutoGen")).toBe("Team member"); + expect(relationLabel(null)).toBe("Delegates to"); + expect(parentLabel(null)).toBe("Called by"); + }); + + it("tells a sub-agent known from code apart from one seen in traffic", () => { + expect(linkActivity(link("a", 0, { declared: true }))).toBe("From code · no calls yet"); + expect(linkActivity(link("a", 1, { declared: true, seen: true }))).toBe("1 call"); + expect(linkActivity(link("a", 4, { seen: true }))).toBe("4 calls"); + }); + + it("draws the busiest few and sums up the rest", () => { + const subs = Array.from({ length: 9 }, (_, i) => link(`s${i}`, i)); + const { shown, rest } = drawnSubagents(subs, 6); + expect(shown.map((s) => s.slug)).toEqual(["s8", "s7", "s6", "s5", "s4"]); + expect(rest).toBe(4); + expect(drawnSubagents(subs.slice(0, 6), 6)).toEqual({ shown: [...subs.slice(0, 6)].reverse(), rest: 0 }); + }); +}); diff --git a/dashboard/lib/product/proposedSystems.ts b/dashboard/lib/product/proposedSystems.ts new file mode 100644 index 00000000..6e0d4804 --- /dev/null +++ b/dashboard/lib/product/proposedSystems.ts @@ -0,0 +1,83 @@ +/** + * The Proposed list, grouped into the systems a repo scan found. A multi-agent + * repository is proposed as one agent per agent, joined by the handoffs its code + * declares; listing those flat hides which ones belong together, and adding the + * orchestrator without its sub-agents leaves it handing work to agents nothing governs. + */ + +export type ProposedAgent = { + id: string; + slug: string; + name?: string; + framework?: string | null; + children?: { slug: string; relation?: string | null }[]; +}; + +export type SystemMember
    = { + agent: A; + /** 0 for the system's root, 1 for what it hands work to, and so on. */ + depth: number; + /** How the parent above hands work to this one; null for the root. */ + relation: string | null; +}; + +export type ProposedGroup = { + root: A; + /** The root first, then each agent under the one that hands it work. */ + members: SystemMember[]; +}; + +/** + * Group proposed agents into systems. An agent no other proposal hands work to is a + * root; everything it reaches is listed under it, once, under the first parent met. A + * cycle (agents that hand back to each other) is cut where it closes. A group with one + * member is an ordinary single proposal. + */ +export function groupProposed(proposed: A[]): ProposedGroup[] { + const bySlug = new Map(proposed.map((a) => [a.slug, a])); + const kids = new Map(); + const hasParent = new Set(); + for (const a of proposed) { + const list = (a.children || []).filter((c) => bySlug.has(c.slug) && c.slug !== a.slug); + kids.set( + a.slug, + list.map((c) => ({ slug: c.slug, relation: c.relation ?? null })), + ); + for (const c of list) hasParent.add(c.slug); + } + + const placed = new Set(); + const groups: ProposedGroup[] = []; + const walk = (slug: string, depth: number, relation: string | null, into: SystemMember[]) => { + if (placed.has(slug)) return; + placed.add(slug); + into.push({ agent: bySlug.get(slug)!, depth, relation }); + for (const c of kids.get(slug) || []) walk(c.slug, depth + 1, c.relation, into); + }; + const start = (a: A) => { + if (placed.has(a.slug)) return; + const members: SystemMember[] = []; + walk(a.slug, 0, null, members); + groups.push({ root: a, members }); + }; + for (const a of proposed) if (!hasParent.has(a.slug)) start(a); + // Agents reachable only through a cycle: start from the first one met. + for (const a of proposed) start(a); + return groups; +} + +/** "Hands off", "Used as a tool", ... — how an indented member is reached, in words. */ +export function memberRelation(relation: string | null): string { + switch (relation) { + case "handoff": + return "Handed off to"; + case "as_tool": + return "Used as a tool"; + case "member": + return "Member"; + case null: + return ""; + default: + return "Delegated to"; + } +} diff --git a/dashboard/lib/product/proxy.test.ts b/dashboard/lib/product/proxy.test.ts index 2ca1a3f5..2d01f227 100644 --- a/dashboard/lib/product/proxy.test.ts +++ b/dashboard/lib/product/proxy.test.ts @@ -22,7 +22,7 @@ function withToken(token: string | undefined) { cookiesGetMock.mockReturnValue(token === undefined ? undefined : { value: token }); } -function req(path = "/app/agents/support-triage") { +function req(path = "/app/projects/support-triage") { return new NextRequest(`http://localhost:3000${path}`); } @@ -43,12 +43,12 @@ describe("proxy auth/mutation boundary", () => { it("an unauthenticated caller is redirected with an error and the gateway is never called", async () => { withToken(undefined); - const res = await proxyReviewAction(req(), "/api/agents/x/approve", "/app/agents"); + const res = await proxyReviewAction(req(), "/api/agents/x/approve", "/app/projects"); expect(fetchMock).not.toHaveBeenCalled(); expect(res.status).toBe(307); // NextResponse.redirect default const location = new URL(res.headers.get("location")!); - expect(location.pathname).toBe("/app/agents"); + expect(location.pathname).toBe("/app/projects"); expect(location.searchParams.get("review_error")).toBe("not signed in"); }); @@ -56,7 +56,7 @@ describe("proxy auth/mutation boundary", () => { withToken("real-session-token"); fetchMock.mockResolvedValue(new Response(JSON.stringify({ ok: true }), { status: 200 })); - const res = await proxyReviewAction(req(), "/api/agents/x/approve", "/app/agents", { + const res = await proxyReviewAction(req(), "/api/agents/x/approve", "/app/projects", { successNotice: "Agent approved.", }); @@ -76,7 +76,7 @@ describe("proxy auth/mutation boundary", () => { new Response(JSON.stringify({ detail: "agent already approved" }), { status: 409 }), ); - const res = await proxyReviewAction(req(), "/api/agents/x/approve", "/app/agents"); + const res = await proxyReviewAction(req(), "/api/agents/x/approve", "/app/projects"); const location = new URL(res.headers.get("location")!); expect(location.searchParams.get("review_error")).toBe("agent already approved"); @@ -86,7 +86,7 @@ describe("proxy auth/mutation boundary", () => { withToken("real-session-token"); fetchMock.mockRejectedValue(new Error("fetch failed: ECONNREFUSED")); - const res = await proxyReviewAction(req(), "/api/agents/x/approve", "/app/agents"); + const res = await proxyReviewAction(req(), "/api/agents/x/approve", "/app/projects"); const location = new URL(res.headers.get("location")!); expect(location.searchParams.get("review_error")).toContain("ECONNREFUSED"); diff --git a/dashboard/lib/product/setupBanner.test.ts b/dashboard/lib/product/setupBanner.test.ts new file mode 100644 index 00000000..ca7aee01 --- /dev/null +++ b/dashboard/lib/product/setupBanner.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from "vitest"; +import { bannerFor, firstRequestSnippet, type Onboarding, type ProjectSetup } from "./setupBanner"; + +const ws = (extra: Partial = {}): Onboarding => ({ + completed: 2, + total: 6, + connected: true, + next: { id: "enforce", title: "Turn enforcement on" }, + counts: { agents: 2, traces: 5 }, + ...extra, +}); + +const proj = (next: ProjectSetup["next"], extra: Partial = {}): ProjectSetup => ({ + agent: "billing", + name: "Billing", + id: "agt_1", + status: "active", + traces: 1, + tools: 3, + granted: 0, + watching: [], + next, + ...extra, +}); + +describe("setup banner", () => { + it("starts an empty workspace at connecting an agent", () => { + expect(bannerFor(null, ws({ counts: { agents: 0, traces: 0 }, connected: false }))?.kind).toBe("connect"); + }); + + it("waits for the first request before anything else", () => { + expect(bannerFor(null, ws({ connected: false }))?.kind).toBe("first-request"); + }); + + it("names the next step, and says nothing once setup is done", () => { + expect(bannerFor(null, ws())?.body).toBe("Next: Turn enforcement on"); + expect(bannerFor(null, ws({ next: null }))).toBeNull(); + }); + + it("is about the project when one is open", () => { + expect(bannerFor(proj("traffic"), ws())?.snippet).toBe(firstRequestSnippet("billing")); + expect(bannerFor(proj("tools"), ws())?.title).toBe("Billing may not use any of its 3 tools yet"); + expect(bannerFor(proj("enforce"), null)?.kind).toBe("enforce"); + expect(bannerFor(proj(null), ws())).toBeNull(); + }); + + it("puts the project's slug in the test request", () => { + expect(firstRequestSnippet("billing")).toContain('"agent": "billing"'); + }); +}); diff --git a/dashboard/lib/product/setupBanner.ts b/dashboard/lib/product/setupBanner.ts new file mode 100644 index 00000000..6da5f0fa --- /dev/null +++ b/dashboard/lib/product/setupBanner.ts @@ -0,0 +1,126 @@ +/** + * What the setup banner says (components/product/SetupBanner.tsx), decided from the + * workspace checklist (`/api/onboarding`) or one project's (`/api/agents/{slug}/setup`). + * Pure, so the order of the steps is tested rather than eyeballed. + */ +import { projectHref } from "@/lib/product/project"; + +export type Onboarding = { + completed: number; + total: number; + connected: boolean; + next: { id: string; title: string } | null; + counts: { agents: number; traces: number }; +}; + +export type ProjectSetup = { + agent: string; + name: string; + id: string; + status: string; + traces: number; + tools: number; + granted: number; + watching: { key: string; label: string }[]; + next: "add" | "traffic" | "tools" | "enforce" | null; +}; + +export type BannerKind = "connect" | "first-request" | "next-step" | "add" | "traffic" | "tools" | "enforce"; + +export type Banner = { + kind: BannerKind; + tone: "start" | "wait" | "act"; + title: string; + body: string; + snippet?: string; + actions: { label: string; href: string }[]; +}; + +/** The request that turns a project's "no traffic yet" into its first run. */ +export function firstRequestSnippet(slug: string): string { + return [ + "curl -X POST \"$AGENTFOX_URL/v1/guard/input\" \\", + ' -H "Authorization: Bearer $AGENTFOX_API_KEY" \\', + ' -H "Content-Type: application/json" \\', + ` -d '{"agent": "${slug}", "content": "Hello"}'`, + ].join("\n"); +} + +export function bannerFor(project: ProjectSetup | null, workspace: Onboarding | null): Banner | null { + if (project) { + const name = project.name; + switch (project.next) { + case "add": + return { + kind: "add", + tone: "start", + title: `${name} was found by a scan`, + body: "Add it as a project to record its traffic and protect it.", + actions: [{ label: "All proposed", href: "/app/projects?tab=proposed" }], + }; + case "traffic": + return { + kind: "traffic", + tone: "wait", + title: `No traffic from ${name} yet`, + body: "Send its requests through AgentFox with an API key. Here is a test request:", + snippet: firstRequestSnippet(project.agent), + actions: [ + { label: "Get an API key", href: "/app/start?tab=tokens" }, + { label: "Integration guide", href: "/app/start" }, + ], + }; + case "tools": + return { + kind: "tools", + tone: "act", + title: `${name} may not use any of its ${project.tools} tool${project.tools === 1 ? "" : "s"} yet`, + body: "A tool that is not allowed is refused. Allow the ones it should use.", + actions: [{ label: "Choose its tools", href: projectHref(project.agent, "access") }], + }; + case "enforce": + return { + kind: "enforce", + tone: "act", + title: `${name} is only watching`, + body: "Its guardrails record what they would stop, without stopping it. Turn blocking on when that looks right.", + actions: [], + }; + default: + return null; + } + } + if (!workspace) return null; + if (!workspace.counts.agents) + return { + kind: "connect", + tone: "start", + title: "Connect your first agent", + body: "Scan a repo to find the agents in it, or connect one by hand. Each agent becomes a project. From a terminal: agentfox up", + actions: [ + { label: "Scan a repo", href: "/app/start?tab=connect" }, + { label: "Connect by hand", href: "/app/start" }, + ], + }; + if (!workspace.connected) + return { + kind: "first-request", + tone: "wait", + title: "Nothing received yet", + body: "Run your agent with an API key, or send a test request from Get started.", + actions: [{ label: "Get started", href: "/app/start" }], + }; + if (workspace.next) + return { + kind: "next-step", + tone: "act", + title: `Setup ${workspace.completed} of ${workspace.total}`, + body: `Next: ${workspace.next.title}`, + actions: [{ label: "Continue", href: "/app/start" }], + }; + return null; +} + +export function dismissKey(scope: string | null, kind: BannerKind): string { + return `agentfox_banner:${scope || "*"}:${kind}`; +} diff --git a/dashboard/lib/product/useProjectScope.ts b/dashboard/lib/product/useProjectScope.ts new file mode 100644 index 00000000..2f09772f --- /dev/null +++ b/dashboard/lib/product/useProjectScope.ts @@ -0,0 +1,43 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { usePathname, useSearchParams } from "next/navigation"; +import { PROJECT_COOKIE, PROJECT_COOKIE_AGE, projectInPath } from "@/lib/product/project"; + +function remember(slug: string | null) { + document.cookie = slug + ? `${PROJECT_COOKIE}=${encodeURIComponent(slug)}; path=/; max-age=${PROJECT_COOKIE_AGE}; samesite=lax` + : `${PROJECT_COOKIE}=; path=/; max-age=0; samesite=lax`; +} + +/** + * The scope the sidebar is in: the project the page names, if it names one, + * otherwise the one last chosen. A project's own page names it in its path; a + * workspace page filtered to one project (`?agent=`) names it too, so following + * "Issues" from a project keeps you in it. The projects list is all projects. + * + * Returns the scope and a way to choose one, which the switcher uses. + */ +export function useProjectScope(initial: string | null): [string | null, (slug: string | null) => void] { + const pathname = usePathname(); + const agent = useSearchParams().get("agent"); + const [chosen, setChosen] = useState(initial); + + const named: string | null | undefined = + projectInPath(pathname) ?? (pathname === "/app/projects" ? null : agent || undefined); + const scope = named === undefined ? chosen : named; + + // Only when the page changes to name another project: choosing All projects + // from a project's page happens while that page still names the project. + useEffect(() => { + if (named === undefined) return; + setChosen(named); + remember(named); + }, [named]); + + const choose = (slug: string | null) => { + setChosen(slug); + remember(slug); + }; + return [scope, choose]; +} diff --git a/dashboard/middleware.ts b/dashboard/middleware.ts index a222cdf2..418270eb 100644 --- a/dashboard/middleware.ts +++ b/dashboard/middleware.ts @@ -11,6 +11,7 @@ import { NextRequest, NextResponse } from "next/server"; import { SESSION_COOKIE } from "@/lib/product/api"; +import { PROJECT_COOKIE, PROJECT_COOKIE_AGE, projectInPath } from "@/lib/product/project"; // /playground is the public, unauthenticated demo (dashboard/app/(marketing)/playground) — it // talks directly to the gateway's own unauthenticated `/api/playground/*` routes @@ -132,6 +133,21 @@ export function middleware(req: NextRequest) { const underPrefix = (p: string) => pathname === p || pathname.startsWith(`${p}/`); + // Agents became projects. Every agent URL has a project URL one word away, so an + // old link or bookmark keeps working; `?tab=` is read by the project page itself. + for (const old of ["/app/agents", "/docs/app/agents"]) { + if (!underPrefix(old)) continue; + const url = new URL(`${old.replace(/agents$/, "projects")}${pathname.slice(old.length)}${req.nextUrl.search}${req.nextUrl.hash}`, req.url); + return NextResponse.redirect(url, 308); + } + // The workspace map was every project's canvas behind a picker; each project has + // its own canvas now. The one named, else the one last chosen, else the list. + if (pathname === "/app/map") { + const slug = req.nextUrl.searchParams.get("agent") || req.cookies.get(PROJECT_COOKIE)?.value; + const url = new URL(slug ? `/app/projects/${encodeURIComponent(slug)}` : "/app/projects", req.url); + return NextResponse.redirect(url, 307); + } + const moved = MOVED_TO_APP.find(underPrefix); if (moved) { const url = new URL(`/app${pathname}${req.nextUrl.search}`, req.url); @@ -146,7 +162,13 @@ export function middleware(req: NextRequest) { const url = new URL("/login", req.url); return NextResponse.redirect(url); } - return NextResponse.next({ request: { headers } }); + const res = NextResponse.next({ request: { headers } }); + // Opening a project is choosing it: the sidebar then shows that project's pages. + const project = projectInPath(pathname); + if (project && signedIn && req.cookies.get(PROJECT_COOKIE)?.value !== project) { + res.cookies.set(PROJECT_COOKIE, project, { path: "/", sameSite: "lax", maxAge: PROJECT_COOKIE_AGE }); + } + return res; } // Static files under public/ are excluded by extension as well as by prefix. Without diff --git a/dashboard/tsconfig.tsbuildinfo b/dashboard/tsconfig.tsbuildinfo index 1dca446e..3c8f4982 100644 --- a/dashboard/tsconfig.tsbuildinfo +++ b/dashboard/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"fileNames":["../../../../dashboard/node_modules/typescript/lib/lib.es5.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2015.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2016.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2017.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2018.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2019.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2020.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2021.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2022.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2023.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2024.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.esnext.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.dom.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.dom.iterable.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2015.core.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2016.intl.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2017.date.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2017.object.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2017.string.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2019.array.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2019.object.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2019.string.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2020.date.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2020.string.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2020.number.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2021.promise.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2021.string.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2021.intl.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2022.array.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2022.error.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2022.intl.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2022.object.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2022.string.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2023.array.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2023.collection.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2023.intl.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2024.collection.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2024.object.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2024.promise.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.es2024.string.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.esnext.array.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.esnext.collection.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.esnext.intl.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.esnext.decorators.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.esnext.iterator.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.decorators.d.ts","../../../../dashboard/node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../../../dashboard/node_modules/next/dist/server/after/builtin-request-context.d.ts","../../../../dashboard/node_modules/next/dist/compiled/webpack/webpack.d.ts","../../../../dashboard/node_modules/next/dist/server/config.d.ts","../../../../dashboard/node_modules/next/dist/lib/load-custom-routes.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/image-config.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","../../../../dashboard/node_modules/next/dist/server/get-page-files.d.ts","../../../../dashboard/node_modules/@types/node/compatibility/disposable.d.ts","../../../../dashboard/node_modules/@types/node/compatibility/indexable.d.ts","../../../../dashboard/node_modules/@types/node/compatibility/iterators.d.ts","../../../../dashboard/node_modules/@types/node/compatibility/index.d.ts","../../../../dashboard/node_modules/@types/node/globals.typedarray.d.ts","../../../../dashboard/node_modules/@types/node/buffer.buffer.d.ts","../../../../dashboard/node_modules/undici-types/header.d.ts","../../../../dashboard/node_modules/undici-types/readable.d.ts","../../../../dashboard/node_modules/undici-types/file.d.ts","../../../../dashboard/node_modules/undici-types/fetch.d.ts","../../../../dashboard/node_modules/undici-types/formdata.d.ts","../../../../dashboard/node_modules/undici-types/connector.d.ts","../../../../dashboard/node_modules/undici-types/client.d.ts","../../../../dashboard/node_modules/undici-types/errors.d.ts","../../../../dashboard/node_modules/undici-types/dispatcher.d.ts","../../../../dashboard/node_modules/undici-types/global-dispatcher.d.ts","../../../../dashboard/node_modules/undici-types/global-origin.d.ts","../../../../dashboard/node_modules/undici-types/pool-stats.d.ts","../../../../dashboard/node_modules/undici-types/pool.d.ts","../../../../dashboard/node_modules/undici-types/handlers.d.ts","../../../../dashboard/node_modules/undici-types/balanced-pool.d.ts","../../../../dashboard/node_modules/undici-types/agent.d.ts","../../../../dashboard/node_modules/undici-types/mock-interceptor.d.ts","../../../../dashboard/node_modules/undici-types/mock-agent.d.ts","../../../../dashboard/node_modules/undici-types/mock-client.d.ts","../../../../dashboard/node_modules/undici-types/mock-pool.d.ts","../../../../dashboard/node_modules/undici-types/mock-errors.d.ts","../../../../dashboard/node_modules/undici-types/proxy-agent.d.ts","../../../../dashboard/node_modules/undici-types/env-http-proxy-agent.d.ts","../../../../dashboard/node_modules/undici-types/retry-handler.d.ts","../../../../dashboard/node_modules/undici-types/retry-agent.d.ts","../../../../dashboard/node_modules/undici-types/api.d.ts","../../../../dashboard/node_modules/undici-types/interceptors.d.ts","../../../../dashboard/node_modules/undici-types/util.d.ts","../../../../dashboard/node_modules/undici-types/cookies.d.ts","../../../../dashboard/node_modules/undici-types/patch.d.ts","../../../../dashboard/node_modules/undici-types/websocket.d.ts","../../../../dashboard/node_modules/undici-types/eventsource.d.ts","../../../../dashboard/node_modules/undici-types/filereader.d.ts","../../../../dashboard/node_modules/undici-types/diagnostics-channel.d.ts","../../../../dashboard/node_modules/undici-types/content-type.d.ts","../../../../dashboard/node_modules/undici-types/cache.d.ts","../../../../dashboard/node_modules/undici-types/index.d.ts","../../../../dashboard/node_modules/@types/node/globals.d.ts","../../../../dashboard/node_modules/@types/node/assert.d.ts","../../../../dashboard/node_modules/@types/node/assert/strict.d.ts","../../../../dashboard/node_modules/@types/node/async_hooks.d.ts","../../../../dashboard/node_modules/@types/node/buffer.d.ts","../../../../dashboard/node_modules/@types/node/child_process.d.ts","../../../../dashboard/node_modules/@types/node/cluster.d.ts","../../../../dashboard/node_modules/@types/node/console.d.ts","../../../../dashboard/node_modules/@types/node/constants.d.ts","../../../../dashboard/node_modules/@types/node/crypto.d.ts","../../../../dashboard/node_modules/@types/node/dgram.d.ts","../../../../dashboard/node_modules/@types/node/diagnostics_channel.d.ts","../../../../dashboard/node_modules/@types/node/dns.d.ts","../../../../dashboard/node_modules/@types/node/dns/promises.d.ts","../../../../dashboard/node_modules/@types/node/domain.d.ts","../../../../dashboard/node_modules/@types/node/dom-events.d.ts","../../../../dashboard/node_modules/@types/node/events.d.ts","../../../../dashboard/node_modules/@types/node/fs.d.ts","../../../../dashboard/node_modules/@types/node/fs/promises.d.ts","../../../../dashboard/node_modules/@types/node/http.d.ts","../../../../dashboard/node_modules/@types/node/http2.d.ts","../../../../dashboard/node_modules/@types/node/https.d.ts","../../../../dashboard/node_modules/@types/node/inspector.d.ts","../../../../dashboard/node_modules/@types/node/module.d.ts","../../../../dashboard/node_modules/@types/node/net.d.ts","../../../../dashboard/node_modules/@types/node/os.d.ts","../../../../dashboard/node_modules/@types/node/path.d.ts","../../../../dashboard/node_modules/@types/node/perf_hooks.d.ts","../../../../dashboard/node_modules/@types/node/process.d.ts","../../../../dashboard/node_modules/@types/node/punycode.d.ts","../../../../dashboard/node_modules/@types/node/querystring.d.ts","../../../../dashboard/node_modules/@types/node/readline.d.ts","../../../../dashboard/node_modules/@types/node/readline/promises.d.ts","../../../../dashboard/node_modules/@types/node/repl.d.ts","../../../../dashboard/node_modules/@types/node/sea.d.ts","../../../../dashboard/node_modules/@types/node/sqlite.d.ts","../../../../dashboard/node_modules/@types/node/stream.d.ts","../../../../dashboard/node_modules/@types/node/stream/promises.d.ts","../../../../dashboard/node_modules/@types/node/stream/consumers.d.ts","../../../../dashboard/node_modules/@types/node/stream/web.d.ts","../../../../dashboard/node_modules/@types/node/string_decoder.d.ts","../../../../dashboard/node_modules/@types/node/test.d.ts","../../../../dashboard/node_modules/@types/node/timers.d.ts","../../../../dashboard/node_modules/@types/node/timers/promises.d.ts","../../../../dashboard/node_modules/@types/node/tls.d.ts","../../../../dashboard/node_modules/@types/node/trace_events.d.ts","../../../../dashboard/node_modules/@types/node/tty.d.ts","../../../../dashboard/node_modules/@types/node/url.d.ts","../../../../dashboard/node_modules/@types/node/util.d.ts","../../../../dashboard/node_modules/@types/node/v8.d.ts","../../../../dashboard/node_modules/@types/node/vm.d.ts","../../../../dashboard/node_modules/@types/node/wasi.d.ts","../../../../dashboard/node_modules/@types/node/worker_threads.d.ts","../../../../dashboard/node_modules/@types/node/zlib.d.ts","../../../../dashboard/node_modules/@types/node/index.d.ts","../../../../dashboard/node_modules/@types/react/global.d.ts","../../../../dashboard/node_modules/csstype/index.d.ts","../../../../dashboard/node_modules/@types/react/index.d.ts","../../../../dashboard/node_modules/@types/react/canary.d.ts","../../../../dashboard/node_modules/@types/react/experimental.d.ts","../../../../dashboard/node_modules/next/dist/lib/fallback.d.ts","../../../../dashboard/node_modules/next/dist/server/base-http/index.d.ts","../../../../dashboard/node_modules/next/dist/server/api-utils/index.d.ts","../../../../dashboard/node_modules/next/dist/server/node-environment-baseline.d.ts","../../../../dashboard/node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","../../../../dashboard/node_modules/next/dist/server/node-environment-extensions/random.d.ts","../../../../dashboard/node_modules/next/dist/server/node-environment-extensions/date.d.ts","../../../../dashboard/node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","../../../../dashboard/node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","../../../../dashboard/node_modules/next/dist/server/node-environment.d.ts","../../../../dashboard/node_modules/next/dist/server/require-hook.d.ts","../../../../dashboard/node_modules/next/dist/server/node-polyfill-crypto.d.ts","../../../../dashboard/node_modules/next/dist/lib/page-types.d.ts","../../../../dashboard/node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","../../../../dashboard/node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","../../../../dashboard/node_modules/next/dist/build/analysis/get-page-static-info.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/cache-control.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/cache-handlers/types.d.ts","../../../../dashboard/node_modules/next/dist/server/route-kind.d.ts","../../../../dashboard/node_modules/next/dist/server/response-cache/types.d.ts","../../../../dashboard/node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","../../../../dashboard/node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","../../../../dashboard/node_modules/next/dist/lib/constants.d.ts","../../../../dashboard/node_modules/next/dist/client/components/app-router-headers.d.ts","../../../../dashboard/node_modules/next/dist/server/render-result.d.ts","../../../../dashboard/node_modules/next/dist/server/body-streams.d.ts","../../../../dashboard/node_modules/next/dist/lib/setup-exception-listeners.d.ts","../../../../dashboard/node_modules/next/dist/lib/worker.d.ts","../../../../dashboard/node_modules/next/dist/build/rendering-mode.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/experimental/ppr.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack/plugins/app-build-manifest-plugin.d.ts","../../../../dashboard/node_modules/next/dist/build/page-extensions-type.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","../../../../dashboard/node_modules/next/dist/server/instrumentation/types.d.ts","../../../../dashboard/node_modules/next/dist/lib/coalesced-function.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/router-utils/types.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/constants.d.ts","../../../../dashboard/node_modules/next/dist/trace/types.d.ts","../../../../dashboard/node_modules/next/dist/trace/trace.d.ts","../../../../dashboard/node_modules/next/dist/trace/shared.d.ts","../../../../dashboard/node_modules/next/dist/trace/index.d.ts","../../../../dashboard/node_modules/next/dist/build/load-jsconfig.d.ts","../../../../dashboard/node_modules/@next/env/dist/index.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","../../../../dashboard/node_modules/next/dist/telemetry/storage.d.ts","../../../../dashboard/node_modules/next/dist/build/build-context.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/bloom-filter.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack-config.d.ts","../../../../dashboard/node_modules/next/dist/server/route-definitions/route-definition.d.ts","../../../../dashboard/node_modules/next/dist/build/swc/generated-native.d.ts","../../../../dashboard/node_modules/next/dist/build/swc/types.d.ts","../../../../dashboard/node_modules/next/dist/server/dev/parse-version-info.d.ts","../../../../dashboard/node_modules/next/dist/next-devtools/shared/types.d.ts","../../../../dashboard/node_modules/next/dist/server/dev/dev-indicator-server-state.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/parse-stack.d.ts","../../../../dashboard/node_modules/next/dist/next-devtools/server/shared.d.ts","../../../../dashboard/node_modules/next/dist/next-devtools/shared/stack-frame.d.ts","../../../../dashboard/node_modules/next/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","../../../../dashboard/node_modules/@types/react/jsx-runtime.d.ts","../../../../dashboard/node_modules/next/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","../../../../dashboard/node_modules/next/dist/next-devtools/dev-overlay/shared.d.ts","../../../../dashboard/node_modules/next/dist/server/dev/hot-reloader-types.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/types.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/lru-cache.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/dev-bundler-service.d.ts","../../../../dashboard/node_modules/next/dist/server/route-matchers/route-matcher.d.ts","../../../../dashboard/node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","../../../../dashboard/node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","../../../../dashboard/node_modules/next/dist/server/base-http/node.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","../../../../dashboard/node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","../../../../dashboard/node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/mitt.d.ts","../../../../dashboard/node_modules/next/dist/client/with-router.d.ts","../../../../dashboard/node_modules/next/dist/client/router.d.ts","../../../../dashboard/node_modules/next/dist/client/route-loader.d.ts","../../../../dashboard/node_modules/next/dist/client/page-loader.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/router/router.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","../../../../dashboard/node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/app-dir-module.d.ts","../../../../dashboard/node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","../../../../dashboard/node_modules/next/dist/server/web/spec-extension/cookies.d.ts","../../../../dashboard/node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","../../../../dashboard/node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","../../../../dashboard/node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/cache-signal.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","../../../../dashboard/node_modules/next/dist/server/request/fallback-params.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","../../../../dashboard/node_modules/next/dist/server/response-cache/index.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/lazy-result.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/implicit-tags.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/deep-readonly.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/app-render.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/amp-context.shared-runtime.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","../../../../dashboard/node_modules/next/dist/client/components/error-boundary.d.ts","../../../../dashboard/node_modules/next/dist/client/components/layout-router.d.ts","../../../../dashboard/node_modules/next/dist/client/components/render-from-template-context.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","../../../../dashboard/node_modules/next/dist/client/components/client-page.d.ts","../../../../dashboard/node_modules/next/dist/client/components/client-segment.d.ts","../../../../dashboard/node_modules/next/dist/server/request/search-params.d.ts","../../../../dashboard/node_modules/next/dist/client/components/hooks-server-context.d.ts","../../../../dashboard/node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","../../../../dashboard/node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","../../../../dashboard/node_modules/next/dist/lib/metadata/types/extra-types.d.ts","../../../../dashboard/node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","../../../../dashboard/node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","../../../../dashboard/node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","../../../../dashboard/node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","../../../../dashboard/node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","../../../../dashboard/node_modules/next/dist/lib/metadata/types/resolvers.d.ts","../../../../dashboard/node_modules/next/dist/lib/metadata/types/icons.d.ts","../../../../dashboard/node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","../../../../dashboard/node_modules/next/dist/lib/metadata/metadata.d.ts","../../../../dashboard/node_modules/next/dist/lib/framework/boundary-components.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/rsc/preloads.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/rsc/postpone.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/rsc/taint.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/collect-segment-data.d.ts","../../../../dashboard/node_modules/next/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/entry-base.d.ts","../../../../dashboard/node_modules/next/dist/build/templates/app-page.d.ts","../../../../dashboard/node_modules/@types/react/jsx-dev-runtime.d.ts","../../../../dashboard/node_modules/@types/react/compiler-runtime.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/app-page/module.d.ts","../../../../dashboard/node_modules/next/dist/server/web/adapter.d.ts","../../../../dashboard/node_modules/next/dist/server/use-cache/cache-life.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/types.d.ts","../../../../dashboard/node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","../../../../dashboard/node_modules/next/dist/client/flight-data-helpers.d.ts","../../../../dashboard/node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","../../../../dashboard/node_modules/next/dist/build/templates/pages.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/pages/module.d.ts","../../../../dashboard/node_modules/next/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","../../../../dashboard/node_modules/next/dist/server/render.d.ts","../../../../dashboard/node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","../../../../dashboard/node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","../../../../dashboard/node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","../../../../dashboard/node_modules/next/dist/server/normalizers/normalizer.d.ts","../../../../dashboard/node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","../../../../dashboard/node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","../../../../dashboard/node_modules/next/dist/server/normalizers/request/suffix.d.ts","../../../../dashboard/node_modules/next/dist/server/normalizers/request/rsc.d.ts","../../../../dashboard/node_modules/next/dist/server/normalizers/request/prefetch-rsc.d.ts","../../../../dashboard/node_modules/next/dist/server/normalizers/request/next-data.d.ts","../../../../dashboard/node_modules/next/dist/server/normalizers/request/segment-prefix-rsc.d.ts","../../../../dashboard/node_modules/next/dist/build/static-paths/types.d.ts","../../../../dashboard/node_modules/next/dist/server/base-server.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/render-server.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/router-server.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/router-utils/router-server-context.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/route-module.d.ts","../../../../dashboard/node_modules/next/dist/server/load-components.d.ts","../../../../dashboard/node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","../../../../dashboard/node_modules/next/dist/server/async-storage/work-store.d.ts","../../../../dashboard/node_modules/next/dist/server/web/http.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","../../../../dashboard/node_modules/next/dist/client/components/redirect-status-code.d.ts","../../../../dashboard/node_modules/next/dist/client/components/redirect-error.d.ts","../../../../dashboard/node_modules/next/dist/build/templates/app-route.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/app-route/module.d.ts","../../../../dashboard/node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","../../../../dashboard/node_modules/next/dist/build/segment-config/app/app-segments.d.ts","../../../../dashboard/node_modules/next/dist/build/utils.d.ts","../../../../dashboard/node_modules/next/dist/build/turborepo-access-trace/types.d.ts","../../../../dashboard/node_modules/next/dist/build/turborepo-access-trace/result.d.ts","../../../../dashboard/node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","../../../../dashboard/node_modules/next/dist/build/turborepo-access-trace/index.d.ts","../../../../dashboard/node_modules/next/dist/export/routes/types.d.ts","../../../../dashboard/node_modules/next/dist/export/types.d.ts","../../../../dashboard/node_modules/next/dist/export/worker.d.ts","../../../../dashboard/node_modules/next/dist/build/worker.d.ts","../../../../dashboard/node_modules/next/dist/build/index.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/incremental-cache/index.d.ts","../../../../dashboard/node_modules/next/dist/server/after/after.d.ts","../../../../dashboard/node_modules/next/dist/server/after/after-context.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","../../../../dashboard/node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","../../../../dashboard/node_modules/next/dist/server/request/params.d.ts","../../../../dashboard/node_modules/next/dist/server/route-matches/route-match.d.ts","../../../../dashboard/node_modules/next/dist/server/request-meta.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/i18n-provider.d.ts","../../../../dashboard/node_modules/next/dist/server/web/next-url.d.ts","../../../../dashboard/node_modules/next/dist/server/web/spec-extension/response.d.ts","../../../../dashboard/node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","../../../../dashboard/node_modules/next/dist/server/web/types.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","../../../../dashboard/node_modules/next/dist/server/lib/async-callback-set.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","../../../../dashboard/node_modules/sharp/lib/index.d.ts","../../../../dashboard/node_modules/next/dist/server/image-optimizer.d.ts","../../../../dashboard/node_modules/next/dist/server/next-server.d.ts","../../../../dashboard/node_modules/next/dist/server/dev/static-paths-worker.d.ts","../../../../dashboard/node_modules/next/dist/server/dev/next-dev-server.d.ts","../../../../dashboard/node_modules/next/dist/server/next.d.ts","../../../../dashboard/node_modules/next/dist/types.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/utils.d.ts","../../../../dashboard/node_modules/next/dist/cli/next-test.d.ts","../../../../dashboard/node_modules/next/dist/server/config-shared.d.ts","../../../../dashboard/node_modules/next/dist/server/web/spec-extension/request.d.ts","../../../../dashboard/node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","../../../../dashboard/node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","../../../../dashboard/node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","../../../../dashboard/node_modules/next/dist/server/web/spec-extension/image-response.d.ts","../../../../dashboard/node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","../../../../dashboard/node_modules/next/dist/compiled/@vercel/og/emoji/index.d.ts","../../../../dashboard/node_modules/next/dist/compiled/@vercel/og/types.d.ts","../../../../dashboard/node_modules/next/dist/server/after/index.d.ts","../../../../dashboard/node_modules/next/dist/server/request/root-params.d.ts","../../../../dashboard/node_modules/next/dist/server/request/connection.d.ts","../../../../dashboard/node_modules/next/server.d.ts","../../../../dashboard/node_modules/next/dist/server/request/cookies.d.ts","../../../../dashboard/node_modules/next/dist/server/request/headers.d.ts","../../../../dashboard/node_modules/next/dist/server/request/draft-mode.d.ts","../../../../dashboard/node_modules/next/headers.d.ts","./lib/env.ts","./lib/product/api.ts","./middleware.ts","../../../../dashboard/node_modules/next/dist/styled-jsx/types/css.d.ts","../../../../dashboard/node_modules/next/dist/styled-jsx/types/macro.d.ts","../../../../dashboard/node_modules/next/dist/styled-jsx/types/style.d.ts","../../../../dashboard/node_modules/next/dist/styled-jsx/types/global.d.ts","../../../../dashboard/node_modules/next/dist/styled-jsx/types/index.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/amp.d.ts","../../../../dashboard/node_modules/next/amp.d.ts","../../../../dashboard/node_modules/next/dist/pages/_app.d.ts","../../../../dashboard/node_modules/next/app.d.ts","../../../../dashboard/node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","../../../../dashboard/node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","../../../../dashboard/node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","../../../../dashboard/node_modules/next/dist/server/use-cache/cache-tag.d.ts","../../../../dashboard/node_modules/next/cache.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/runtime-config.external.d.ts","../../../../dashboard/node_modules/next/config.d.ts","../../../../dashboard/node_modules/next/dist/pages/_document.d.ts","../../../../dashboard/node_modules/next/document.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/dynamic.d.ts","../../../../dashboard/node_modules/next/dynamic.d.ts","../../../../dashboard/node_modules/next/dist/pages/_error.d.ts","../../../../dashboard/node_modules/next/error.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/head.d.ts","../../../../dashboard/node_modules/next/head.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/get-img-props.d.ts","../../../../dashboard/node_modules/next/dist/client/image-component.d.ts","../../../../dashboard/node_modules/next/dist/shared/lib/image-external.d.ts","../../../../dashboard/node_modules/next/image.d.ts","../../../../dashboard/node_modules/next/dist/client/link.d.ts","../../../../dashboard/node_modules/next/link.d.ts","../../../../dashboard/node_modules/next/dist/client/components/redirect.d.ts","../../../../dashboard/node_modules/next/dist/client/components/not-found.d.ts","../../../../dashboard/node_modules/next/dist/client/components/forbidden.d.ts","../../../../dashboard/node_modules/next/dist/client/components/unauthorized.d.ts","../../../../dashboard/node_modules/next/dist/client/components/unstable-rethrow.server.d.ts","../../../../dashboard/node_modules/next/dist/client/components/unstable-rethrow.d.ts","../../../../dashboard/node_modules/next/dist/client/components/navigation.react-server.d.ts","../../../../dashboard/node_modules/next/dist/client/components/unrecognized-action-error.d.ts","../../../../dashboard/node_modules/next/dist/client/components/navigation.d.ts","../../../../dashboard/node_modules/next/navigation.d.ts","../../../../dashboard/node_modules/next/router.d.ts","../../../../dashboard/node_modules/next/dist/client/script.d.ts","../../../../dashboard/node_modules/next/script.d.ts","../../../../dashboard/node_modules/next/types/global.d.ts","../../../../dashboard/node_modules/next/types/compiled.d.ts","../../../../dashboard/node_modules/next/types.d.ts","../../../../dashboard/node_modules/next/index.d.ts","../../../../dashboard/node_modules/next/image-types/global.d.ts","./next-env.d.ts","../../../../dashboard/node_modules/@vitest/spy/optional-types.d.ts","../../../../dashboard/node_modules/@vitest/spy/dist/index.d.ts","../../../../dashboard/node_modules/tinyrainbow/dist/index.d.ts","../../../../dashboard/node_modules/@standard-schema/spec/dist/index.d.ts","../../../../dashboard/node_modules/@vitest/pretty-format/dist/index.d.ts","../../../../dashboard/node_modules/@vitest/utils/dist/types.d-bcelap-c.d.ts","../../../../dashboard/node_modules/@vitest/utils/dist/diff.d.ts","../../../../dashboard/node_modules/@vitest/utils/dist/display.d.ts","../../../../dashboard/node_modules/@types/deep-eql/index.d.ts","../../../../dashboard/node_modules/assertion-error/index.d.ts","../../../../dashboard/node_modules/@types/chai/index.d.ts","../../../../dashboard/node_modules/@vitest/expect/dist/index.d.ts","../../../../dashboard/node_modules/vitest/node_modules/vite/types/hmrpayload.d.ts","../../../../dashboard/node_modules/vitest/node_modules/vite/dist/node/chunks/modulerunnertransport.d.ts","../../../../dashboard/node_modules/vitest/node_modules/vite/types/customevent.d.ts","../../../../dashboard/node_modules/rolldown/dist/shared/logging-xuho4may.d.mts","../../../../dashboard/node_modules/@oxc-project/types/types.d.ts","../../../../dashboard/node_modules/rolldown/dist/shared/binding-dzunhvw4.d.mts","../../../../dashboard/node_modules/@rolldown/pluginutils/dist/filter/index.d.mts","../../../../dashboard/node_modules/@rolldown/pluginutils/dist/index.d.mts","../../../../dashboard/node_modules/rolldown/dist/shared/define-config-dnx0wekp.d.mts","../../../../dashboard/node_modules/rolldown/dist/index.d.mts","../../../../dashboard/node_modules/rolldown/dist/parse-ast-index.d.mts","../../../../dashboard/node_modules/vitest/node_modules/vite/types/internal/rolluptypecompat.d.ts","../../../../dashboard/node_modules/rolldown/dist/shared/constructors-bm20b11r.d.mts","../../../../dashboard/node_modules/rolldown/dist/plugins-index.d.mts","../../../../dashboard/node_modules/rolldown/dist/shared/transform-cvse9_mh.d.mts","../../../../dashboard/node_modules/rolldown/dist/utils-index.d.mts","../../../../dashboard/node_modules/vitest/node_modules/vite/types/hot.d.ts","../../../../dashboard/node_modules/vitest/node_modules/vite/dist/node/module-runner.d.ts","../../../../dashboard/node_modules/vitest/node_modules/vite/types/internal/esbuildoptions.d.ts","../../../../dashboard/node_modules/vitest/node_modules/vite/types/metadata.d.ts","../../../../dashboard/node_modules/vitest/node_modules/vite/types/internal/terseroptions.d.ts","../../../../dashboard/node_modules/source-map-js/source-map.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/previous-map.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/input.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/css-syntax-error.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/declaration.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/root.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/warning.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/lazy-result.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/no-work-result.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/processor.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/result.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/document.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/rule.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/node.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/comment.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/container.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/at-rule.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/list.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/postcss.d.ts","../../../../dashboard/node_modules/vitest/node_modules/postcss/lib/postcss.d.mts","../../../../dashboard/node_modules/lightningcss/node/ast.d.ts","../../../../dashboard/node_modules/lightningcss/node/targets.d.ts","../../../../dashboard/node_modules/lightningcss/node/index.d.ts","../../../../dashboard/node_modules/vitest/node_modules/vite/types/internal/lightningcssoptions.d.ts","../../../../dashboard/node_modules/vitest/node_modules/vite/types/internal/csspreprocessoroptions.d.ts","../../../../dashboard/node_modules/rolldown/dist/filter-index.d.mts","../../../../dashboard/node_modules/vitest/node_modules/vite/types/importglob.d.ts","../../../../dashboard/node_modules/vitest/node_modules/vite/dist/node/index.d.ts","../../../../dashboard/node_modules/@vitest/utils/dist/types.d.ts","../../../../dashboard/node_modules/@vitest/utils/dist/helpers.d.ts","../../../../dashboard/node_modules/@vitest/utils/dist/timers.d.ts","../../../../dashboard/node_modules/@vitest/utils/dist/index.d.ts","../../../../dashboard/node_modules/@vitest/runner/dist/tasks.d-deyaimiu.d.ts","../../../../dashboard/node_modules/@vitest/runner/dist/index.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/traces.d.d2t_r8rx.d.ts","../../../../dashboard/node_modules/@vitest/snapshot/dist/environment.d-dojxxzv9.d.ts","../../../../dashboard/node_modules/@vitest/snapshot/dist/rawsnapshot.d-d_x3-62x.d.ts","../../../../dashboard/node_modules/@vitest/snapshot/dist/index.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/config.d.a1h_y6jt.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/environment.d.crsxczp1.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/rpc.d.b_8spu0w.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/worker.d.zphpo4yb.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/browser.d.bcoexmfg.d.ts","../../../../dashboard/node_modules/vitest/optional-types.d.ts","../../../../dashboard/node_modules/@vitest/runner/dist/utils.d.ts","../../../../dashboard/node_modules/tinybench/dist/index.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/benchmark.d.daahlpsq.d.ts","../../../../dashboard/node_modules/vitest/node_modules/@vitest/mocker/dist/types.d-bji5eawu.d.ts","../../../../dashboard/node_modules/vitest/node_modules/@vitest/mocker/dist/index.d-b41z0auw.d.ts","../../../../dashboard/node_modules/vitest/node_modules/@vitest/mocker/dist/index.d.ts","../../../../dashboard/node_modules/@vitest/utils/dist/source-map.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/coverage.d.bztk59wp.d.ts","../../../../dashboard/node_modules/@vitest/utils/dist/serialize.d.ts","../../../../dashboard/node_modules/@vitest/utils/dist/error.d.ts","../../../../dashboard/node_modules/vitest/dist/browser.d.ts","../../../../dashboard/node_modules/vitest/browser/context.d.ts","../../../../dashboard/node_modules/@vitest/snapshot/dist/manager.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/reporters.d.dtokvv2s.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/plugin.d.dwfiij7i.d.ts","../../../../dashboard/node_modules/vitest/dist/config.d.ts","../../../../dashboard/node_modules/vitest/config.d.ts","./vitest.config.ts","../../../../dashboard/node_modules/vitest/dist/chunks/global.d.dvssrdq5.d.ts","../../../../dashboard/node_modules/vitest/optional-runtime-types.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/suite.d.udjtyagw.d.ts","../../../../dashboard/node_modules/vitest/dist/chunks/evaluatedmodules.d.bxj5omdx.d.ts","../../../../dashboard/node_modules/vitest/dist/runners.d.ts","../../../../dashboard/node_modules/expect-type/dist/utils.d.ts","../../../../dashboard/node_modules/expect-type/dist/overloads.d.ts","../../../../dashboard/node_modules/expect-type/dist/branding.d.ts","../../../../dashboard/node_modules/expect-type/dist/messages.d.ts","../../../../dashboard/node_modules/expect-type/dist/index.d.ts","../../../../dashboard/node_modules/vitest/dist/index.d.ts","./lib/site.ts","./app/agents.md/route.ts","./lib/marketing/blog.ts","./lib/marketing/nav.ts","./app/llms.txt/route.ts","./app/.well-known/security.txt/route.ts","./app/machine-readable.test.ts","./app/nav-complete.test.ts","./app/og-mark.ts","./app/robots.ts","./lib/docs/pages.ts","./app/sitemap.ts","./app/(product)/app/links.test.ts","./lib/product/proxy.ts","./app/api/agents/route.ts","./app/api/agents/[slug]/approve/route.ts","./app/api/agents/[slug]/boundary/route.ts","./app/api/agents/[slug]/control/route.ts","./app/api/agents/[slug]/owner/route.ts","./app/api/agents/[slug]/reject/route.ts","./app/api/approvals/[id]/approve/route.ts","./app/api/approvals/[id]/deny/route.ts","./app/api/audit/verify/route.ts","./app/api/auth/github/callback/route.ts","./app/api/auth/github/login/route.ts","./app/api/auth/logout/route.ts","./app/api/auth/logout/route.test.ts","./app/api/auth/token/route.ts","./app/api/auth/token/route.test.ts","./app/api/compliance/compute/route.ts","./app/api/compliance/sync/route.ts","./lib/product/entitlement.ts","./app/api/entitlement/grants/route.ts","./app/api/entitlement/principals/route.ts","./app/api/escalation/handoffs/acknowledge/route.ts","./app/api/escalation/policy/route.ts","./app/api/eval/online/route.ts","./app/api/eval/results/[id]/annotate/route.ts","./app/api/eval/runs/route.ts","./app/api/eval/slos/route.ts","./app/api/eval/suites/route.ts","./app/api/eval/suites/[key]/cases/route.ts","./app/api/eval/suites/[key]/promote/route.ts","./app/api/evidence/route.ts","./app/api/evidence/[id]/download/route.ts","./app/api/findings/[id]/route.ts","./app/api/frameworks/review/route.ts","./app/api/guardrails/feedback/route.ts","./app/api/guardrails/suppressions/route.ts","./app/api/guardrails/suppressions/revoke/route.ts","./app/api/integrations/github/scan/route.ts","./app/api/integrations/hosted-api/scan/route.ts","./app/api/judgment/posture/route.ts","./app/api/judgment/posture/route.test.ts","./app/api/legal-holds/route.ts","./app/api/policies/[key]/approve/route.ts","./app/api/policies/[key]/canary/route.ts","./app/api/policies/[key]/canary/advance/route.ts","./app/api/policies/[key]/canary/rollback/route.ts","./app/api/policies/[key]/canary/start/route.ts","./app/api/policies/[key]/mode/route.ts","./app/api/policies/[key]/reject/route.ts","./app/api/policies/[key]/save/route.ts","./app/api/policies/simulate/route.ts","./app/api/policies/validate/route.ts","./app/api/redteam/campaigns/route.ts","./app/api/risk/assessments/[slug]/route.ts","./app/api/sources/route.ts","./lib/product/sourceconnection.ts","./app/api/sources/add/route.ts","./app/api/sources/connections/route.ts","./app/api/sources/context-check/route.ts","./app/api/sources/delete/route.ts","./app/api/sources/deprecate/route.ts","./app/api/sources/validate/route.ts","./app/api/tokens/route.ts","./app/api/tokens/[id]/revoke/route.ts","./app/api/traces/[id]/route.ts","./app/api/waitlist/route.ts","./app/blog/feed.xml/route.ts","./components/marketing/shot.tsx","./components/marketing/blog/blocks.tsx","./components/marketing/blog/card.tsx","./components/marketing/brand.tsx","./components/ui/themetoggle.tsx","./components/marketing/motion.tsx","./components/marketing/menu.tsx","./components/marketing/nav.tsx","./components/marketing/sections.tsx","./components/marketing/blog/article.tsx","./components/docs/copy.tsx","./components/docs/blocks.tsx","./components/marketing/blog/posts/ai-agent-audit-trail.tsx","./components/marketing/blog/posts/claude-code-hooks-security.tsx","./lib/generated/coverage.json","./components/marketing/blog/posts/continuous-red-teaming-ai-agents.tsx","./components/marketing/blog/posts/hidden-indirect-prompt-injection.tsx","./components/marketing/blog/posts/mcp-rug-pull-tool-poisoning.tsx","./components/marketing/blog/posts/prompt-injection-containment-not-detection.tsx","./components/marketing/blog/posts/index.ts","./lib/env.test.ts","./lib/marketing/blog.test.ts","./lib/marketing/showcase.ts","./lib/marketing/showcase.test.ts","./lib/product/controls.ts","./lib/product/coverage.ts","./lib/product/coverage.test.ts","./lib/product/entitlement.test.ts","./lib/generated/reference/finding-types.json","./lib/product/findingtypes.test.ts","./lib/product/legacyredirect.ts","./lib/product/legacyredirect.test.ts","./lib/product/proxy.test.ts","./lib/product/sourceoptions.ts","./app/error.tsx","../../../../dashboard/node_modules/next/dist/compiled/@next/font/dist/types.d.ts","../../../../dashboard/node_modules/geist/font.d.ts","../../../../dashboard/node_modules/geist/dist/mono.d.ts","../../../../dashboard/node_modules/geist/dist/sans.d.ts","../../../../dashboard/node_modules/next/dist/compiled/@next/font/dist/google/index.d.ts","../../../../dashboard/node_modules/next/font/google/index.d.ts","./app/layout.tsx","./app/loading.tsx","./app/not-found.tsx","../../../../dashboard/node_modules/next/dist/compiled/@vercel/og/index.edge.d.ts","../../../../dashboard/node_modules/next/dist/compiled/@vercel/og/index.node.d.ts","../../../../dashboard/node_modules/next/dist/server/og/image-response.d.ts","../../../../dashboard/node_modules/next/og.d.ts","./app/og.tsx","./app/opengraph-image.tsx","./app/(marketing)/how-it-works/_public.tsx","./components/marketing/platform.tsx","./components/marketing/sequence.tsx","./components/marketing/home.tsx","./app/page.tsx","./app/twitter-image.tsx","./components/marketing/charts.tsx","./app/(marketing)/benchmark/page.tsx","./app/(marketing)/compare/page.tsx","./components/marketing/capability.tsx","./app/(marketing)/control-points/page.tsx","./app/(marketing)/coverage/page.tsx","./app/(marketing)/discovery/page.tsx","./app/(marketing)/evidence/page.tsx","./app/(marketing)/frameworks/page.tsx","./app/(marketing)/grants/page.tsx","./app/(marketing)/hooks/page.tsx","./components/marketing/path.tsx","./components/marketing/product.tsx","./components/marketing/follow.tsx","./app/(marketing)/how-it-works/page.tsx","./app/(marketing)/legal/page.tsx","./app/(marketing)/live/page.tsx","./app/(marketing)/mcp/page.tsx","./components/ui/infotip.tsx","./components/ui/index.tsx","./components/marketing/playground.tsx","./app/(marketing)/playground/page.tsx","./components/marketing/waitlist.tsx","./components/marketing/editions.tsx","./app/(marketing)/pricing/page.tsx","./app/(marketing)/privacy/page.tsx","./components/marketing/mocks.tsx","./components/marketing/grant.tsx","./components/marketing/visuals.tsx","./components/marketing/pillars.tsx","./components/marketing/index-grid.tsx","./app/(marketing)/product/page.tsx","./app/(marketing)/runtime/page.tsx","./app/(marketing)/security/page.tsx","./app/(marketing)/support/page.tsx","./app/(marketing)/terms/page.tsx","./components/product/logo.tsx","./components/product/accountmenu.tsx","./components/product/notificationsbell.tsx","./components/product/commandsearch.tsx","./components/product/navicons.tsx","./components/product/sidenav.tsx","./components/product/topbarstats.tsx","./app/(product)/app/layout.tsx","./components/product/pageheader.tsx","./components/product/coverage.tsx","./app/(product)/app/page.tsx","./components/product/modal.tsx","./components/product/explainer.tsx","./app/(product)/app/agents/page.tsx","./components/product/breadcrumbs.tsx","./components/product/agentmap.tsx","./app/(product)/app/agents/[slug]/page.tsx","./components/product/countdown.tsx","./app/(product)/app/approvals/page.tsx","./app/(product)/app/board/page.tsx","./components/product/printbutton.tsx","./app/(product)/app/compliance/page.tsx","./app/(product)/app/compliance/frameworks/[key]/page.tsx","./app/(product)/app/coverage/page.tsx","./app/(product)/app/entitlement/page.tsx","./app/(product)/app/escalation/page.tsx","./app/(product)/app/escalation/conversations/[sessionid]/page.tsx","./app/(product)/app/evals/page.tsx","./app/(product)/app/evals/[key]/page.tsx","./app/(product)/app/evals/[key]/runs/[runid]/page.tsx","./components/product/findingevidence.tsx","./components/product/expandablefindingrow.tsx","./app/(product)/app/findings/page.tsx","./app/(product)/app/findings/[id]/page.tsx","./components/product/glossaryfilter.tsx","./components/product/productmap.tsx","./app/(product)/app/glossary/page.tsx","./app/(product)/app/guardrails/page.tsx","./components/product/detectorcatalogue.tsx","./components/product/judgmentposture.tsx","./app/(product)/app/policies/page.tsx","./components/product/policyeditor.tsx","./components/product/canarypanel.tsx","./app/(product)/app/policies/[key]/page.tsx","./app/(product)/app/settings/integrations/page.tsx","./app/(product)/app/settings/tokens/page.tsx","./components/product/contextcheck.tsx","./components/product/connectionfields.tsx","./components/product/sourceaddflow.tsx","./components/product/sourcerowactions.tsx","./app/(product)/app/sources/page.tsx","./components/product/repotable.tsx","./components/product/tokenmanager.tsx","./app/(product)/app/start/page.tsx","./components/product/expandabletracerow.tsx","./app/(product)/app/traces/page.tsx","./components/product/whyblocked.tsx","./app/(product)/app/traces/[id]/page.tsx","./app/(product)/login/page.tsx","./app/blog/page.tsx","./app/blog/[slug]/opengraph-image.tsx","./app/blog/[slug]/page.tsx","./app/blog/[slug]/twitter-image.tsx","./components/docs/sidebar.tsx","./app/docs/layout.tsx","./app/docs/page.tsx","./app/docs/app/page.tsx","./app/docs/app/access-and-sources/page.tsx","./app/docs/app/agents/page.tsx","./app/docs/app/approvals/page.tsx","./app/docs/app/compliance/page.tsx","./app/docs/app/evals/page.tsx","./app/docs/app/findings/page.tsx","./app/docs/app/playground/page.tsx","./app/docs/app/policies/page.tsx","./app/docs/app/start/page.tsx","./app/docs/app/traces/page.tsx","./app/docs/benchmarks/page.tsx","./app/docs/concepts/page.tsx","./app/docs/guides/approvals/page.tsx","./app/docs/guides/audit-evidence/page.tsx","./app/docs/guides/business-rules/page.tsx","./app/docs/guides/coding-agents/page.tsx","./app/docs/guides/contain-tool-calls/page.tsx","./app/docs/guides/gateway/page.tsx","./app/docs/guides/langgraph/page.tsx","./app/docs/guides/live-probes/page.tsx","./app/docs/guides/mcp/page.tsx","./app/docs/guides/monitoring/page.tsx","./app/docs/guides/observability/page.tsx","./app/docs/guides/python-auto/page.tsx","./app/docs/guides/rag/page.tsx","./app/docs/guides/red-team-and-evals/page.tsx","./app/docs/guides/scan-a-repo/page.tsx","./app/docs/guides/tuning/page.tsx","./app/docs/install/page.tsx","./app/docs/limits/page.tsx","./app/docs/plugin/page.tsx","./app/docs/quickstart/page.tsx","./lib/generated/reference/api.json","./lib/generated/reference/cli.json","./components/docs/reference.tsx","./app/docs/reference/api/page.tsx","./app/docs/reference/cli/page.tsx","./app/docs/reference/config/page.tsx","./app/docs/reference/detectors/page.tsx","./app/docs/reference/policies/page.tsx","./app/docs/reference/python/page.tsx","./app/docs/self-host/page.tsx","./app/docs/support/page.tsx","./components/marketing/boundary.tsx","./components/marketing/decisions.tsx","./components/marketing/explain.tsx","./components/marketing/hero.tsx","../../../../dashboard/node_modules/@types/estree/index.d.ts","./.next/types/routes.d.ts"],"fileIdsList":[[91,133,468,485,594,681,682,730],[91,133,186,468,485,594,681,682],[91,133,468,485,594,733],[91,133,485,594,681,682,688],[91,133,485,594,733],[91,133],[91,133,468,485,594,681,682,741,742,743],[91,133,468,485,594,681,682],[91,133,186,468,485,594,681,682,696],[91,133,436,485,594,681,682,750],[91,133,468,485,594,681,682,753],[91,133,468,485,594,681,682,759,760],[91,133,437,468,485,594,749,780,781],[91,133,437,468,485,594,749,774,777,778],[91,133,437,468,485,594,749,774,783],[91,133,485,594,704],[91,133,437,485,594,749,780],[91,133,437,468,485,594,749,774,786],[91,133,437,468,485,594,749,774],[91,133,437,468,485,594,749,774,777],[91,133,437,468,485,594,749,780],[91,133,437,468,485,594,749,777,780],[91,133,437,468,485,594,749,774,778],[91,133,437,485,594,698,749,780,796],[91,133,437,468,485,594,698,749,774,797],[91,133,485,594,774,800,801],[91,133,435,437,468,678,766,767,768,769,771,772],[91,133,146,155,593],[91,133,437,468,485,594,749,774,775],[91,133,437,485,594,749,780,807,808],[91,133,437,468,485,594,749,774,783,804,805],[91,133,437,468,485,594,707,749,774,777,812,814,815],[91,133,437,468,478,485,594,749,774,817,818],[91,133,437,468,485,594,698,749,780,822],[91,133,437,468,485,594,749,774,820],[91,133,435,437,468,478,485,594,766],[91,133,594],[91,133,431,607],[91,133,431,436,437],[91,133,431],[91,133,431,593,619],[91,133,431,593,621],[91,133,431,607,625],[91,133,431,435,436,437],[91,133,431,593,646],[91,133,431,435,436,437,662],[91,133,431,607,662],[91,133,607],[91,133,596,722],[91,133,478,485,594,596,683,693],[91,133,594,596],[91,133,468,485,594,596,676,681,682],[91,133,468,485,594,685],[91,133,186,468,485,594,685],[91,133,186,681,682,829],[91,133,468,485,594,604,685],[91,133,468,485,594,685,867],[91,133,186,468],[91,133,485,594,710,711,712,714],[91,133,594,596,597],[91,133,478],[91,133,593,594,595,596,597,598,599],[91,133,146,155,593,597],[91,133,468],[91,133,602,721],[91,133,722],[91,133,485,594,681,682,724,725,727],[91,133,485,594],[91,133,485,594,596,597,604],[91,133,186,468,684],[91,133,186],[91,133,865,866],[91,133,468,478,604],[91,133,186,466,468,594,596,675,676,681,682],[91,133,186,468,674],[91,133,466,468,596],[91,133,468,675,683,685],[91,133,468,675,683,685,688],[91,133,186,596,675,683,686,687,689,690,691,692],[91,133,186,468,597,681,682],[91,133,186,468,752],[91,133,186,756],[91,133,468,681,877],[91,133,186,468,674,681,726],[91,133,468,597],[91,133,186,468,597],[91,133,435,437,468,597,677,678,679,680],[91,133,186,468,756,757,758],[91,133,186,749],[91,133,468,597,677],[91,133,466],[91,133,186,478],[91,133,707],[91,133,468,699,749],[91,133,186,468,749,796],[91,133,186,468,749],[91,133,186,777],[91,133,468,749],[91,133,468,478,770],[91,133,186,707,813],[91,133,186,783],[91,133,436,468,702,748],[91,133,436,593],[91,133,146,155,593,596,597,604,605],[91,133,593,696],[91,133,436],[91,133,435,436],[91,133,437],[91,133,593,699],[91,133,593,625],[91,133,593,702],[91,133,593,704],[91,133,431,593,607],[91,133,431,437],[91,133,485,486,881],[91,133,155,581],[91,133,506],[91,133,496,497],[91,130,133],[91,132,133],[133],[91,133,138,168],[91,133,134,139,145,146,153,165,176],[91,133,134,135,145,153],[86,87,88,91,133],[91,133,136,177],[91,133,137,138,146,154],[91,133,138,165,173],[91,133,139,141,145,153],[91,132,133,140],[91,133,141,142],[91,133,145],[91,133,143,145],[91,132,133,145],[91,133,145,146,147,165,176],[91,133,145,146,147,160,165,168],[91,128,133,181],[91,128,133,141,145,148,153,165,176],[91,133,145,146,148,149,153,165,173,176],[91,133,148,150,165,173,176],[89,90,91,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182],[91,133,145,151],[91,133,152,176,181],[91,133,141,145,153,165],[91,133,154],[91,133,155],[91,132,133,156],[91,130,131,132,133,134,135,136,137,138,139,140,141,142,143,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182],[91,133,158],[91,133,159],[91,133,145,160,161],[91,133,160,162,177,179],[91,133,145,165,166,167,168],[91,133,165,167],[91,133,165,166],[91,133,168],[91,133,169],[91,130,133,165],[91,133,145,171,172],[91,133,171,172],[91,133,138,153,165,173],[91,133,174],[91,133,153,175],[91,133,148,159,176],[91,133,138,177],[91,133,165,178],[91,133,152,179],[91,133,180],[91,133,138,145,147,156,165,176,179,181],[91,133,165,182],[91,133,186,188,415,427,442],[91,133,186,187,415,427,442],[91,133,184,185],[91,133,489,490,491,494,495,498],[91,133,494,552,553],[91,133,494,552],[91,133,552],[91,133,492,552,556,557],[91,133,492,552,556],[91,133,488],[91,133,492,493],[91,133,492],[91,133,492,493,549,573],[91,133,549],[91,133,492,495,549,550,551],[91,133,588,589],[91,133,588,589,590,591],[91,133,588,590],[91,133,588],[91,133,710],[91,133,709,711,712],[91,133,541,542],[91,133,444],[91,133,446],[91,133,448,449,450,451],[91,133,453],[82,91,133,201,202,203,205,419],[82,91,133,191,234,236,238,239,242,419],[82,91,133,191,207,213,214,218,219,220,390,419],[91,133,419],[91,133,202,302,371,380,397],[82,91,133],[91,133,189,397],[91,133,245],[91,133,191,244,419],[91,133,148,282,302,331,483],[91,133,148,295,311,380,396],[91,133,148,346],[91,133,384],[91,133,383,384,385],[91,133,383],[82,85,91,133,148,189,198,199,202,206,207,213,221,222,223,339,362,381,415,419],[82,91,133,204,230,234,235,240,241,419,483],[91,133,204,483],[91,133,223,230,280,419,483],[91,133,483],[82,91,133,204,205,483],[91,133,237,483],[91,133,199,382,389],[91,133,159,253,397],[91,133,253,397],[91,133,186,253],[91,133,186,303],[91,133,299,344,397,475,476],[91,133,377,469,470,471,472,474],[91,133,376],[91,133,376,377],[91,133,214,340,341,342],[91,133,340,343,344],[91,133,473],[91,133,340,344],[83,91,133,186,463],[91,133,176,186],[91,133,186,204,270],[91,133,186,204],[91,133,268,272],[91,133,186,269,417],[91,133,709],[91,133,186,427],[91,133,165,183,186,427,718],[91,133,148,183,186,187,188,415,425,426,442],[91,133,148],[91,133,148,190,207,212,340,350,363,371,386,387,419,483],[91,133,198,388],[91,133,415],[81,91,133],[91,133,186,282,298,310,320,322,396],[91,133,159,282,298,319,320,321,396,482],[91,133,313,314,315,316,317,318],[91,133,315],[91,133,319],[91,133,251,252,253,255],[91,133,186,246,247,248,254],[91,133,251,254],[91,133,249],[91,133,250],[91,133,186,253,269,417],[91,133,186,253,416,417],[91,133,186,253,417],[91,133,363,393],[91,133,393],[91,133,148,190,417],[91,133,307],[91,132,133,306],[91,133,190,215,224,290,292,294,295,296,297,337,340,396,399],[91,133,224,328,340,344],[91,133,295,396],[91,133,186,295,304,305,307,308,309,310,311,312,323,324,325,326,327,329,330,396,397,483],[91,133,289],[91,133,148,159,190,191,212,224,225,264,296,337,338,339,344,363,371,392,415,419,483],[91,133,396],[91,132,133,190,202,293,296,339,392,394,395],[91,133,295],[91,132,133,212,284,285,286,287,288,289,290,291,292,294,396,397],[91,133,148,190,191,284,285,420],[91,133,190,202,339,340,363,392,396],[91,133,148,191,419],[91,133,148,165,190,191,399],[79,91,133,148,159,176,189,190,191,204,207,215,224,225,227,262,264,292,296,340,350,351,353,355,358,359,360,361,362,371,391,392,397,399,400,419],[91,133,148,165],[82,83,84,91,133,204,207,219,221,230,391,399,415,417,418,483],[91,133,148,165,176,242,243,245,246,247,248,255,483],[91,133,159,176,189,234,243,259,262,263,292,340,351,362,363,371,392,397,399,404,405,411,412],[91,133,198,199,221,339,362,392,419],[83,91,133,148,176,207,292,399,409,419],[91,133,281],[91,133,148,257,258,368],[91,133,399,419],[91,133,290,293],[91,133,292,296,391,417],[91,133,148,159,228,234,259,262,263,363,399,411,414],[91,133,148,198,199,234,364],[82,91,133,227,366,391,419],[91,133,148,176,419],[91,133,148,204,226,227,228,239,256,365,367,391,419],[85,91,133,224,296,370,415,417],[79,91,133,148,159,176,198,199,206,207,215,225,263,264,292,340,351,353,363,371,391,392,397,398,399,404,405,406,408,410,417],[91,133,148,165,199,368,399,411,413],[91,133,193,194,195,196,197],[91,133,354,400],[91,133,356],[91,133,354],[91,133,356,357],[91,133,719],[91,133,148,190,207,212,213,214],[81,83,91,133,148,159,191,215,224,264,296,348,349,371,399,415,417],[91,133,148,159,176,190,214,216,292,349,398],[91,133,285],[91,133,286],[91,133,287],[91,133,397],[91,133,209,210],[91,133,148,207,209,215],[91,133,208,210],[91,133,211],[91,133,209,243],[91,133,209,265],[91,133,209],[91,133,261,398,400],[91,133,260],[91,133,243,397,398],[91,133,352,398],[91,133,243,397],[91,133,337],[91,133,190,207,215,219,279,282,290,292,296,298,301,332,335,336,340,370,391,399],[91,133,273,276,277,278,299,300,344],[91,133,186,253,333,334],[91,133,379],[91,133,202,295,296,307,311,340,370,372,373,374,375,377,378,381,391,396,419,420],[91,133,344],[91,133,348],[91,133,148,215,266,345,347,350,370,399,415,417],[91,133,273,274,275,276,277,278,299,300,344,416],[85,91,133,148,159,176,190,209,225,243,264,292,296,368,369,371,391,392,415,419],[91,133,392,404,420,421],[91,133,148,400,419],[91,133,284,295],[91,133,283],[79,91,133,420],[91,133,284,401,419],[91,133,148,190,216,402,403,419,420,421],[91,133,186,340,341,343],[91,133,229],[83,91,133,186],[91,133,186,397],[85,91,133,186,264,296,415,417],[83,91,133,463,464],[91,133,186,272],[81,91,133,159,176,186,241,267,269,271,417],[91,133,190,204,397],[91,133,397,407],[91,133,340],[81,91,133,146,148,159,186,230,236,272,415,416],[91,133,186,187,188,415,427],[91,133,186,439,440,441,442],[91,133,138],[91,133,231,232,233],[91,133,231],[81,91,133,148,150,159,183,186,187,188,189,191,225,319,414,417,419,427,442],[91,133,455],[91,133,457],[91,133,459],[91,133,713],[91,133,461],[91,133,432,433,434],[91,133,465],[91,133,431,435,443,445,447,452,454,456,458,460,462,466,468,478,479,481,482,483,484],[91,133,467],[91,133,477],[91,133,720],[91,133,269],[91,133,480],[91,132,133,310,397,402,404,420,421,422,423,424,427,428,429,430],[91,133,183],[91,133,508],[91,133,503,505,508],[91,133,504,505],[91,133,505,508,512],[91,133,504],[91,133,505,508],[91,133,503,504,505,507],[91,133,503,505],[91,133,504,505,514],[91,133,165,183],[91,100,104,133,176],[91,100,133,165,176],[91,95,133],[91,97,100,133,173,176],[91,133,153,173],[91,95,133,183],[91,97,100,133,153,176],[91,92,93,96,99,133,145,165,176],[91,100,107,133],[91,92,98,133],[91,100,121,122,133],[91,96,100,133,168,176,183],[91,121,133,183],[91,94,95,133,183],[91,100,133],[91,94,95,96,97,98,99,100,101,102,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,122,123,124,125,126,127,133],[91,100,115,133],[91,100,107,108,133],[91,98,100,108,109,133],[91,99,133],[91,92,95,100,133],[91,100,104,108,109,133],[91,104,133],[91,98,100,103,133,176],[91,92,97,100,107,133],[91,133,165],[91,95,100,121,133,181,183],[91,133,575],[91,133,499,580,583],[91,133,489,492,494,495,550,551,552,554,555,558,559,571,572,574,583],[91,133,554,565,566,583],[91,133,554,555,562,583],[91,133,492,494,554,555,558,583],[91,133,517],[91,133,492,499,554,555,558,567,583],[91,133,548,578,580],[91,133,136,146,165,492,494,499,548,552,554,555,558,559,562,563,564,567,570,571,572,576,577,580,583],[91,133,517,554,555,558,583],[91,133,554,565,566,567,583],[91,133,517,554,559,560,561,583],[91,133,136,146,165,492,494,499,517,548,552,554,555,558,559,560,561,562,563,564,565,566,567,570,571,572,576,577,578,579,580,583],[91,133,489,492,494,499,517,552,554,555,558,559,560,561,562,563,565,566,567,570,583,584,585,586,587,592],[91,133,492,494,554,555,558,559,565,566,567,583,585],[91,133,568],[91,133,568,569],[91,133,536],[91,133,534,536],[91,133,525,533,534,535,537,539],[91,133,523],[91,133,526,531,536,539],[91,133,522,539],[91,133,526,527,530,531,532,539],[91,133,526,527,528,530,531,539],[91,133,523,524,525,526,527,531,532,533,535,536,537,539],[91,133,539],[91,133,521,523,524,525,526,527,528,530,531,532,533,534,535,536,537,538],[91,133,521,539],[91,133,526,528,529,531,532,539],[91,133,530,539],[91,133,531,532,536,539],[91,133,524,534],[91,133,500],[91,133,145,146,148,149,150,153,165,173,176,182,183,500,501,502,509,510,511,513,515,517,518,519,520,540,544,545,546,547,548],[91,133,500,501,502,516],[91,133,502],[91,133,543],[91,133,509,519,548],[91,133,509,548]],"fileInfos":[{"version":"e41c290ef7dd7dab3493e6cbe5909e0148edf4a8dad0271be08edec368a0f7b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"e12a46ce14b817d4c9e6b2b478956452330bf00c9801b79de46f7a1815b5bd40","impliedFormat":1},{"version":"4fd3f3422b2d2a3dfd5cdd0f387b3a8ec45f006c6ea896a4cb41264c2100bb2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"69e65d976bf166ce4a9e6f6c18f94d2424bf116e90837ace179610dbccad9b42","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"62bb211266ee48b2d0edf0d8d1b191f0c24fc379a82bd4c1692a082c540bc6b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"936e80ad36a2ee83fc3caf008e7c4c5afe45b3cf3d5c24408f039c1d47bdc1df","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"fef8cfad2e2dc5f5b3d97a6f4f2e92848eb1b88e897bb7318cef0e2820bceaab","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"f1e2a172204962276504466a6393426d2ca9c54894b1ad0a6c9dad867a65f876","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"b5ce7a470bc3628408429040c4e3a53a27755022a32fd05e2cb694e7015386c7","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"bab26767638ab3557de12c900f0b91f710c7dc40ee9793d5a27d32c04f0bf646","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"61d6a2092f48af66dbfb220e31eea8b10bc02b6932d6e529005fd2d7b3281290","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"829b9e6028b29e6a8b1c01ddb713efe59da04d857089298fa79acbdb3cfcfdef","impliedFormat":1},{"version":"24f8562308dd8ba6013120557fa7b44950b619610b2c6cb8784c79f11e3c4f90","impliedFormat":1},{"version":"5f90b8c733a1bda63e42160b15a2301051e83a6f9d5332a59d16eb12f463270d","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"631eff75b0e35d1b1b31081d55209abc43e16b49426546ab5a9b40bdd40b1f60","impliedFormat":1},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"030e350db2525514580ed054f712ffb22d273e6bc7eddc1bb7eda1e0ba5d395e","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"a79e62f1e20467e11a904399b8b18b18c0c6eea6b50c1168bf215356d5bebfaf","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fd06258805d26c72f5997e07a23155d322d5f05387adb3744a791fe6a0b042d","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"24bd580b5743dc56402c440dc7f9a4f5d592ad7a419f25414d37a7bfe11e342b","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"6bdc71028db658243775263e93a7db2fd2abfce3ca569c3cca5aee6ed5eb186d","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"ba97261afafe85aafe76d988e65e531da8e8cf791c49caf0531f5dd1689bd91b","affectsGlobalScope":true,"impliedFormat":1},{"version":"f52e8dacc97d71dcc96af29e49584353f9c54cb916d132e3e768d8b8129c928d","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"76103716ba397bbb61f9fa9c9090dca59f39f9047cb1352b2179c5d8e7f4e8d0","impliedFormat":1},{"version":"53eac70430b30089a3a1959d8306b0f9cfaf0de75224b68ef25243e0b5ad1ca3","affectsGlobalScope":true,"impliedFormat":1},{"version":"4314c7a11517e221f7296b46547dbc4df047115b182f544d072bdccffa57fc72","impliedFormat":1},{"version":"115971d64632ea4742b5b115fb64ed04bcaae2c3c342f13d9ba7e3f9ee39c4e7","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb","impliedFormat":1},{"version":"86956cc2eb9dd371d6fab493d326a574afedebf76eef3fa7833b8e0d9b52d6f1","affectsGlobalScope":true,"impliedFormat":1},{"version":"24642567d3729bcc545bacb65ee7c0db423400c7f1ef757cab25d05650064f98","impliedFormat":1},{"version":"e6f5a38687bebe43a4cef426b69d34373ef68be9a6b1538ec0a371e69f309354","impliedFormat":1},{"version":"a6bf63d17324010ca1fbf0389cab83f93389bb0b9a01dc8a346d092f65b3605f","impliedFormat":1},{"version":"e009777bef4b023a999b2e5b9a136ff2cde37dc3f77c744a02840f05b18be8ff","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"ee1ee365d88c4c6c0c0a5a5701d66ebc27ccd0bcfcfaa482c6e2e7fe7b98edf7","affectsGlobalScope":true,"impliedFormat":1},{"version":"88bc59b32d0d5b4e5d9632ac38edea23454057e643684c3c0b94511296f2998c","affectsGlobalScope":true,"impliedFormat":1},{"version":"e9ad08a376ac84948fcca0013d6f1d4ae4f9522e26b91f87945b97c99d7cc30b","impliedFormat":1},{"version":"eaf9ee1d90a35d56264f0bf39842282c58b9219e112ac7d0c1bce98c6c5da672","impliedFormat":1},{"version":"c15c4427ae7fd1dcd7f312a8a447ac93581b0d4664ddf151ecd07de4bf2bb9d7","impliedFormat":1},{"version":"5135bdd72cc05a8192bd2e92f0914d7fc43ee077d1293dc622a049b7035a0afb","impliedFormat":1},{"version":"4f80de3a11c0d2f1329a72e92c7416b2f7eab14f67e92cac63bb4e8d01c6edc8","impliedFormat":1},{"version":"6d386bc0d7f3afa1d401afc3e00ed6b09205a354a9795196caed937494a713e6","impliedFormat":1},{"version":"aeac7c51bde4658c192bc45819344eb20fc64743264b0465be6025201220a6b7","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4047ed87e765bd3bcc316a0c4c4c8b0061628460d8a5412d1c4b53a4658665a","impliedFormat":1},{"version":"b1b6ee0d012aeebe11d776a155d8979730440082797695fc8e2a5c326285678f","impliedFormat":1},{"version":"45875bcae57270aeb3ebc73a5e3fb4c7b9d91d6b045f107c1d8513c28ece71c0","impliedFormat":1},{"version":"3eb62baae4df08c9173e6903d3ca45942ccec8c3659b0565684a75f3292cffbb","affectsGlobalScope":true,"impliedFormat":1},{"version":"42aaa94addeed66a04b61e433c14e829c43d1efd653cf2fda480c5fb3d722ed8","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"c6b4e0a02545304935ecbf7de7a8e056a31bb50939b5b321c9d50a405b5a0bba","impliedFormat":1},{"version":"fab29e6d649aa074a6b91e3bdf2bff484934a46067f6ee97a30fcd9762ae2213","impliedFormat":1},{"version":"8145e07aad6da5f23f2fcd8c8e4c5c13fb26ee986a79d03b0829b8fce152d8b2","impliedFormat":1},{"version":"e1120271ebbc9952fdc7b2dd3e145560e52e06956345e6fdf91d70ca4886464f","impliedFormat":1},{"version":"15c5e91b5f08be34a78e3d976179bf5b7a9cc28dc0ef1ffebffeb3c7812a2dca","impliedFormat":1},{"version":"58832ded29e0094047596544ac391d68c799d7bd7d35936f47221857141628f1","impliedFormat":1},{"version":"553870e516f8c772b89f3820576152ebc70181d7994d96917bb943e37da7f8a7","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"93452d394fdd1dc551ec62f5042366f011a00d342d36d50793b3529bfc9bd633","impliedFormat":1},{"version":"745c4240220559bd340c8aeb6e3c5270a709d3565e934dc22a69c304703956bc","affectsGlobalScope":true,"impliedFormat":1},{"version":"2754d8221d77c7b382096651925eb476f1066b3348da4b73fe71ced7801edada","impliedFormat":1},{"version":"9212c6e9d80cb45441a3614e95afd7235a55a18584c2ed32d6c1aca5a0c53d93","affectsGlobalScope":true,"impliedFormat":1},{"version":"bef91efa0baea5d0e0f0f27b574a8bc100ce62a6d7e70220a0d58af6acab5e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"282fd2a1268a25345b830497b4b7bf5037a5e04f6a9c44c840cb605e19fea841","impliedFormat":1},{"version":"5360a27d3ebca11b224d7d3e38e3e2c63f8290cb1fcf6c3610401898f8e68bc3","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"7d6ff413e198d25639f9f01f16673e7df4e4bd2875a42455afd4ecc02ef156da","affectsGlobalScope":true,"impliedFormat":1},{"version":"6bd91a2a356600dee28eb0438082d0799a18a974a6537c4410a796bab749813c","affectsGlobalScope":true,"impliedFormat":1},{"version":"f689c4237b70ae6be5f0e4180e8833f34ace40529d1acc0676ab8fb8f70457d7","impliedFormat":1},{"version":"ae25afbbf1ed5df63a177d67b9048bf7481067f1b8dc9c39212e59db94fc9fc6","impliedFormat":1},{"version":"ac5ed35e649cdd8143131964336ab9076937fa91802ec760b3ea63b59175c10a","impliedFormat":1},{"version":"52a8e7e8a1454b6d1b5ad428efae3870ffc56f2c02d923467f2940c454aa9aec","affectsGlobalScope":true,"impliedFormat":1},{"version":"78dc0513cc4f1642906b74dda42146bcbd9df7401717d6e89ea6d72d12ecb539","impliedFormat":1},{"version":"ad90122e1cb599b3bc06a11710eb5489101be678f2920f2322b0ac3e195af78d","impliedFormat":1},{"version":"36a2e4c9a67439aca5f91bb304611d5ae6e20d420503e96c230cf8fcdc948d94","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"da172a28c35d5f298bf5f0361725cc80f45e89e803a353c56e1aa347e10f572d","impliedFormat":1},{"version":"865a2612f5ec073dd48d454307ccabb04c48f8b96fda9940c5ebfe6b4b451f51","impliedFormat":1},{"version":"4558ac151f289d39f651a630cc358111ef72c1148e06627ef7edaeb01eb26c82","impliedFormat":1},{"version":"413df52d4ea14472c2fa5bee62f7a40abd1eb49be0b9722ee01ee4e52e63beb2","impliedFormat":1},{"version":"35e6379c3f7cb27b111ad4c1aa69538fd8e788ab737b8ff7596a1b40e96f4f90","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"acf5a2ac47b59ca07afa9abbd2b31d001bf7448b041927befae2ea5b1951d9f9","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d71291eff1e19d8762a908ba947e891af44749f3a2cbc5bd2ec4b72f72ea795f","impliedFormat":1},{"version":"c0480e03db4b816dff2682b347c95f2177699525c54e7e6f6aa8ded890b76be7","impliedFormat":1},{"version":"27ab780875bcbb65e09da7496f2ca36288b0c541abaa75c311450a077d54ec15","impliedFormat":1},{"version":"ffa495b17a5ef1d0399586b590bd281056cee6ce3583e34f39926f8dcc6ecdb5","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"b6db56e4903e9c32e533b78ac85522de734b3d3a8541bf24d256058d464bf04b","impliedFormat":1},{"version":"24daa0366f837d22c94a5c0bad5bf1fd0f6b29e1fae92dc47c3072c3fdb2fbd5","impliedFormat":1},{"version":"570bb5a00836ffad3e4127f6adf581bfc4535737d8ff763a4d6f4cc877e60d98","impliedFormat":1},{"version":"889c00f3d32091841268f0b994beba4dceaa5df7573be12c2c829d7c5fbc232c","impliedFormat":1},{"version":"65f43099ded6073336e697512d9b80f2d4fec3182b7b2316abf712e84104db00","impliedFormat":1},{"version":"ce75b1aebb33d510ff28af960a9221410a3eaf7f18fc5f21f9404075fba77256","impliedFormat":1},{"version":"d6e73f8010935b7b4c7487b6fb13ea197cc610f0965b759bec03a561ccf8423a","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"174f3864e398f3f33f9a446a4f403d55a892aa55328cf6686135dfaf9e171657","impliedFormat":1},{"version":"824c76aec8d8c7e65769688cbee102238c0ef421ed6686f41b2a7d8e7e78a931","impliedFormat":1},{"version":"75b868be3463d5a8cfc0d9396f0a3d973b8c297401d00bfb008a42ab16643f13","impliedFormat":1},{"version":"5178eb4415a172c287c711dc60a619e110c3fd0b7de01ed0627e51a5336aa09c","impliedFormat":1},{"version":"ca6e5264278b53345bc1ce95f42fb0a8b733a09e3d6479c6ccfca55cdc45038c","impliedFormat":1},{"version":"15a234e5031b19c48a69ccc1607522d6e4b50f57d308ecb7fe863d44cd9f9eb3","impliedFormat":1},{"version":"ad0d1d75d129b1c80f911be438d6b61bfa8703930a8ff2be2f0e1f8a91841c64","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"496bbf339f3838c41f164238543e9fe5f1f10659cb30b68903851618464b98ba","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"fb1d8e814a3eeb5101ca13515e0548e112bd1ff3fb358ece535b93e94adf5a3a","impliedFormat":1},{"version":"98b18458acb46072947aabeeeab1e410f047e0cacc972943059ca5500b0a5e95","impliedFormat":1},{"version":"361e2b13c6765d7f85bb7600b48fde782b90c7c41105b7dab1f6e7871071ba20","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"380647d8f3b7f852cca6d154a376dbf8ac620a2f12b936594504a8a852e71d2f","impliedFormat":1},{"version":"208c9af9429dd3c76f5927b971263174aaa4bc7621ddec63f163640cbd3c473c","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"a23185bc5ef590c287c28a91baf280367b50ae4ea40327366ad01f6f4a8edbc5","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"002eae065e6960458bda3cf695e578b0d1e2785523476f8a9170b103c709cd4f","impliedFormat":1},{"version":"c83bb0c9c5645a46c68356c2f73fdc9de339ce77f7f45a954f560c7e0b8d5ebb","impliedFormat":1},{"version":"05c97cddbaf99978f83d96de2d8af86aded9332592f08ce4a284d72d0952c391","impliedFormat":1},{"version":"72179f9dd22a86deaad4cc3490eb0fe69ee084d503b686985965654013f1391b","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"7b6ff760c8a240b40dab6e4419b989f06a5b782f4710d2967e67c695ef3e93c4","impliedFormat":1},{"version":"c8f004e6036aa1c764ad4ec543cf89a5c1893a9535c80ef3f2b653e370de45e6","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"b064c36f35de7387d71c599bfcf28875849a1dbc733e82bd26cae3d1cd060521","impliedFormat":1},{"version":"6a148329edecbda07c21098639ef4254ef7869fb25a69f58e5d6a8b7b69d4236","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"f63ab283a1c8f5c79fabe7ca4ef85f9633339c4f0e822fce6a767f9d59282af2","impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a54c996c8870ef1728a2c1fa9b8eaec0bf4a8001cd2583c02dd5869289465b10","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"3754982006a3b32c502cff0867ca83584f7a43b1035989ca73603f400de13c96","impliedFormat":1},{"version":"a30ae9bb8a8fa7b90f24b8a0496702063ae4fe75deb27da731ed4a03b2eb6631","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"7d73b24e7bf31dfb8a931ca6c4245f6bb0814dfae17e4b60c9e194a631fe5f7b","impliedFormat":1},{"version":"413586add0cfe7369b64979d4ec2ed56c3f771c0667fbde1bf1f10063ede0b08","impliedFormat":1},{"version":"06472528e998d152375ad3bd8ebcb69ff4694fd8d2effaf60a9d9f25a37a097a","impliedFormat":1},{"version":"50b5bc34ce6b12eccb76214b51aadfa56572aa6cc79c2b9455cdbb3d6c76af1d","impliedFormat":1},{"version":"b7e16ef7f646a50991119b205794ebfd3a4d8f8e0f314981ebbe991639023d0e","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"a401617604fa1f6ce437b81689563dfdc377069e4c58465dbd8d16069aede0a5","impliedFormat":1},{"version":"e9dd71cf12123419c60dab867d44fbee5c358169f99529121eaef277f5c83531","impliedFormat":1},{"version":"5b6a189ba3a0befa1f5d9cb028eb9eec2af2089c32f04ff50e2411f63d70f25d","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"e91f7b1344577a02f051b9b471f33044fef8334a76dc9e1de003d17595a5219b","impliedFormat":1},{"version":"3af3584f79c57853028ef9421ec172539e1fe01853296dc05a9d615ade4ffaf6","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"0c7c947ff881c4274c0800deaa0086971e0bfe51f89a33bd3048eaa3792d4876","affectsGlobalScope":true,"impliedFormat":1},{"version":"db01d18853469bcb5601b9fc9826931cc84cc1a1944b33cad76fd6f1e3d8c544","affectsGlobalScope":true,"impliedFormat":1},{"version":"a8f8e6ab2fa07b45251f403548b78eaf2022f3c2254df3dc186cb2671fe4996d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"15b36126e0089bfef173ab61329e8286ce74af5e809d8a72edcafd0cc049057f","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"ad10d4f0517599cdeca7755b930f148804e3e0e5b5a3847adce0f1f71bbccd74","impliedFormat":1},{"version":"1042064ece5bb47d6aba91648fbe0635c17c600ebdf567588b4ca715602f0a9d","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"c49469a5349b3cc1965710b5b0f98ed6c028686aa8450bcb3796728873eb923e","impliedFormat":1},{"version":"4a889f2c763edb4d55cb624257272ac10d04a1cad2ed2948b10ed4a7fda2a428","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"72d63643a657c02d3e51cd99a08b47c9b020a565c55f246907050d3c8a5e77fb","impliedFormat":1},{"version":"1d415445ea58f8033ba199703e55ff7483c52ac6742075b803bd3e7bbe9f5d61","impliedFormat":1},{"version":"d6406c629bb3efc31aedb2de809bef471e475c86c7e67f3ef9b676b5d7e0d6b2","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"71d8ba39a9e024d9e4bb922464d18542ed8d2c25ee78efa7890c27213cc6e5d3","impliedFormat":1},{"version":"8c030e515014c10a2b98f9f48408e3ba18023dfd3f56e3312c6c2f3ae1f55a16","impliedFormat":1},{"version":"dafc31e9e8751f437122eb8582b93d477e002839864410ff782504a12f2a550c","impliedFormat":1},{"version":"754498c5208ce3c5134f6eabd49b25cf5e1a042373515718953581636491f3c3","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"f56bdc6884648806d34bc66d31cdb787c4718d04105ce2cd88535db214631f82","impliedFormat":1},{"version":"633d58a237f4bb25ec7d565e4ffa32cecdcee8660ac12189c4351c52557cee9e","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"13283350547389802aa35d9f2188effaeac805499169a06ef5cd77ce2a0bd63f","impliedFormat":1},{"version":"ce791f6ea807560f08065d1af6014581eeb54a05abd73294777a281b6dfd73c2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"49f95e989b4632c6c2a578cc0078ee19a5831832d79cc59abecf5160ea71abad","impliedFormat":1},{"version":"9666533332f26e8995e4d6fe472bdeec9f15d405693723e6497bf94120c566c8","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"e17cd049a1448de4944800399daa4a64c5db8657cc9be7ef46be66e2a2cd0e7c","impliedFormat":1},{"version":"43fa6ea8714e18adc312b30450b13562949ba2f205a1972a459180fa54471018","impliedFormat":1},{"version":"6e89c2c177347d90916bad67714d0fb473f7e37fb3ce912f4ed521fe2892cd0d","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"4d4927cbee21750904af7acf940c5e3c491b4d5ebc676530211e389dd375607a","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"8a97e578a9bc40eb4f1b0ca78f476f2e9154ecbbfd5567ee72943bab37fc156a","impliedFormat":1},{"version":"c857e0aae3f5f444abd791ec81206020fbcc1223e187316677e026d1c1d6fe08","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"2d7db1d73456e8c5075387d4240c29a2a900847f9c1bff106a2e490da8fbd457","impliedFormat":1},{"version":"2b15c805f48e4e970f8ec0b1915f22d13ca6212375e8987663e2ef5f0205e832","impliedFormat":1},{"version":"f22d05663d873ee7a600faf78abb67f3f719d32266803440cf11d5db7ac0cab2","impliedFormat":1},{"version":"d93c544ad20197b3976b0716c6d5cd5994e71165985d31dcab6e1f77feb4b8f2","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"a8b1c79a833ee148251e88a2553d02ce1641d71d2921cce28e79678f3d8b96aa","impliedFormat":1},{"version":"126d4f950d2bba0bd45b3a86c76554d4126c16339e257e6d2fabf8b6bf1ce00c","impliedFormat":1},{"version":"7e0b7f91c5ab6e33f511efc640d36e6f933510b11be24f98836a20a2dc914c2d","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"2d3cc2211f352f46ea6b7cf2c751c141ffcdf514d6e7ae7ee20b7b6742da313f","impliedFormat":1},{"version":"c75445151ff8b77d9923191efed7203985b1a9e09eccf4b054e7be864e27923d","impliedFormat":1},{"version":"0aedb02516baf3e66b2c1db9fef50666d6ed257edac0f866ea32f1aa05aa474f","impliedFormat":1},{"version":"fa8a8fbf91ee2a4779496225f0312aac6635b0f21aa09cdafa4283fe32d519c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"0e8aef93d79b000deb6ec336b5645c87de167168e184e84521886f9ecc69a4b5","impliedFormat":1},{"version":"56ccb49443bfb72e5952f7012f0de1a8679f9f75fc93a5c1ac0bafb28725fc5f","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"d90b9f1520366d713a73bd30c5a9eb0040d0fb6076aff370796bc776fd705943","impliedFormat":1},{"version":"bef86adb77316505c6b471da1d9b8c9e428867c2566270e8894d4d773a1c4dc2","impliedFormat":1},{"version":"de7052bfee2981443498239a90c04ea5cc07065d5b9bb61b12cb6c84313ad4ef","impliedFormat":1},{"version":"a3e7d932dc9c09daa99141a8e4800fc6c58c625af0d4bbb017773dc36da75426","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"4a2edd238d9104eac35b60d727f1123de5062f452b70ed8e0366cb36387dfdfd","impliedFormat":1},{"version":"ca921bf56756cb6fe957f6af693a35251b134fb932dc13f3dfff0bb7106f80b4","impliedFormat":1},{"version":"fee92c97f1aa59eb7098a0cc34ff4df7e6b11bae71526aca84359a2575f313d8","impliedFormat":1},{"version":"0bd0297484aacea217d0b76e55452862da3c5d9e33b24430e0719d1161657225","impliedFormat":1},{"version":"2ab6d334bcbf2aff3acfc4fd8c73ecd82b981d3c3aa47b3f3b89281772286904","impliedFormat":1},{"version":"d07cbc787a997d83f7bde3877fec5fb5b12ce8c1b7047eb792996ed9726b4dde","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"4805f6161c2c8cefb8d3b8bd96a080c0fe8dbc9315f6ad2e53238f9a79e528a6","impliedFormat":1},{"version":"b83cb14474fa60c5f3ec660146b97d122f0735627f80d82dd03e8caa39b4388c","impliedFormat":1},{"version":"f374cb24e93e7798c4d9e83ff872fa52d2cdb36306392b840a6ddf46cb925cb6","impliedFormat":1},{"version":"49179c6a23701c642bd99abe30d996919748014848b738d8e85181fc159685ff","impliedFormat":1},{"version":"7274fbffbd7c9589d8d0ffba68157237afd5cecff1e99881ea3399127e60572f","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"45490817629431853543adcb91c0673c25af52a456479588b6486daba34f68bb","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"8b4327413e5af38cd8cb97c59f48c3c866015d5d642f28518e3a891c469f240e","impliedFormat":1},{"version":"8514c62ce38e58457d967e9e73f128eedc1378115f712b9eef7127f7c88f82ae","impliedFormat":1},{"version":"f1289e05358c546a5b664fbb35a27738954ec2cc6eb4137350353099d154fc62","impliedFormat":1},{"version":"752513f35f6cff294ffe02d6027c41373adf7bfa35e593dbfd53d95c203635ee","impliedFormat":1},{"version":"6c800b281b9e89e69165fd11536195488de3ff53004e55905e6c0059a2d8591e","impliedFormat":1},{"version":"7d4254b4c6c67a29d5e7f65e67d72540480ac2cfb041ca484847f5ae70480b62","impliedFormat":1},{"version":"1a7e2ea171726446850ec72f4d1525d547ff7e86724cc9e7eec509725752a758","impliedFormat":1},{"version":"8c901126d73f09ecdea4785e9a187d1ac4e793e07da308009db04a7283ec2f37","impliedFormat":1},{"version":"c1de754ab5f3b0f4036d6893c74a0fc984c7fcb07936086f19bbe2974406775b","impliedFormat":1},{"version":"aab290b8e4b7c399f2c09b957666fc95335eb4522b2dd9ead1bf0cb64da6d6ee","impliedFormat":1},{"version":"94fe3281392e1015b22f39535878610b4fa6f1388dc8d78746be3bc4e4bb8950","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"06c25ddfc2242bd06c19f66c9eae4c46d937349a267810f89783680a1d7b5259","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"bd4131091b773973ca5d2326c60b789ab1f5e02d8843b3587effe6e1ea7c9d86","impliedFormat":1},{"version":"c7f6485931085bf010fbaf46880a9b9ec1a285ad9dc8c695a9e936f5a48f34b4","impliedFormat":1},{"version":"14f6b927888a1112d662877a5966b05ac1bf7ed25d6c84386db4c23c95a5363b","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"0427df5c06fafc5fe126d14b9becd24160a288deff40e838bfbd92a35f8d0d00","impliedFormat":1},{"version":"90c54a02432d04e4246c87736e53a6a83084357acfeeba7a489c5422b22f5c7a","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"83fe880c090afe485a5c02262c0b7cdd76a299a50c48d9bde02be8e908fb4ae6","impliedFormat":1},{"version":"0a372c2d12a259da78e21b25974d2878502f14d89c6d16b97bd9c5017ab1bc12","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"6511e4503cf74c469c60aafd6589e4d14d5eb0a25f9bf043dcbecdf65f261972","impliedFormat":1},{"version":"ec1ca97598eda26b7a5e6c8053623acbd88e43be7c4d29c77ccd57abc4c43999","impliedFormat":1},{"version":"efc0e6a64feef102d5edef7525de0c2ac5961821f12ae5038f0d3a8631457e10","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"a47e6d954d22dd9ebb802e7e431b560ed7c581e79fb885e44dc92ed4f60d4c07","impliedFormat":1},{"version":"f019e57d2491c159d47a107fd90219a1734bdd2e25cd8d1db3c8fae5c6b414c4","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"d1c9bf292a54312888a77bb19dba5e2503ad803f5393beafd45d78d2f4fe9b48","impliedFormat":1},{"version":"d682336018141807fb602709e2d95a192828fcb8d5ba06dda3833a8ea98f69e3","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"6dcf60530c25194a9ee0962230e874ff29d34c59605d8e069a49928759a17e0a","impliedFormat":1},{"version":"1a42d2ec31a1fe62fdc51591768695ed4a2dc64c01be113e7ff22890bebb5e3f","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"1d17ba45cfbe77a9c7e0df92f7d95f3eefd49ee23d1104d0548b215be56945ad","impliedFormat":1},{"version":"f7d628893c9fa52ba3ab01bcb5e79191636c4331ee5667ecc6373cbccff8ae12","impliedFormat":1},{"version":"1d879125d1ec570bf04bc1f362fdbe0cb538315c7ac4bcfcdf0c1e9670846aa6","impliedFormat":1},{"version":"a84d677a6172185d541b1b00ea8e4e04217b64351098f9e5c07403fdec305683","impliedFormat":1},{"version":"46273e8c29816125d0d0b56ce9a849cc77f60f9a5ba627447501d214466f0ff3","impliedFormat":1},{"version":"f82579d87701d639ff4e3930a9b24f4ee13ca74221a9a3a792feb47f01881a9c","impliedFormat":1},{"version":"d7e5d5245a8ba34a274717d085174b2c9827722778129b0081fefd341cca8f55","impliedFormat":1},{"version":"d9d32f94056181c31f553b32ce41d0ef75004912e27450738d57efcd2409c324","impliedFormat":1},{"version":"3be035da7bee86b4c3abf392e0edaa44fc6e45092995eefe36b39118c8a84068","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f828825d077c2fa0ea606649faeb122749273a353daab23924fe674e98ba44c","impliedFormat":1},{"version":"2896c2e673a5d3bd9b4246811f79486a073cbb03950c3d252fba10003c57411a","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"d9c46395b3980848a6c4b2f44e986d27d07faee3b3b4b1f214793255b8c64f2a","impliedFormat":1},{"version":"4fbd3116e00ed3a6410499924b6403cc9367fdca303e34838129b328058ede40","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"a68d4b3182e8d776cdede7ac9630c209a7bfbb59191f99a52479151816ef9f9e","impliedFormat":99},{"version":"39644b343e4e3d748344af8182111e3bbc594930fff0170256567e13bbdbebb0","impliedFormat":99},{"version":"ed7fd5160b47b0de3b1571c5c5578e8e7e3314e33ae0b8ea85a895774ee64749","impliedFormat":99},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"ecbaf0da125974be39c0aac869e403f72f033a4e7fd0d8cd821a8349b4159628","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"ceec3c81b2d81f5e3b855d9367c1d4c664ab5046dff8fd56552df015b7ccbe8f","affectsGlobalScope":true,"impliedFormat":1},{"version":"fec01479923e169fb52bd4f668dbeef1d7a7ea6e6d491e15617b46f2cacfa37d","impliedFormat":1},{"version":"8a8fb3097ba52f0ae6530ec6ab34e43e316506eb1d9aa29420a4b1e92a81442d","impliedFormat":1},{"version":"44e09c831fefb6fe59b8e65ad8f68a7ecc0e708d152cfcbe7ba6d6080c31c61e","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"0e619baf3b8fce9857731add3ed48095af35922b64c9e1af2bdc5354e56aff4b","signature":"a671bd207880b7da3d9354828b308aa795e15352be1894f4aa53f4b6b2ff2d9b"},"3c2427d2b1af5c390a352f9e38f0c64d486150c44572c57a59fc2196631927d4","ed947637acd101bfdc566f5e640c79c986f14de22987660aaf62b84247fe68ee",{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"0f6666b58e9276ac3a38fdc80993d19208442d6027ab885580d93aec76b4ef00","impliedFormat":1},{"version":"05fd364b8ef02fb1e174fbac8b825bdb1e5a36a016997c8e421f5fab0a6da0a0","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"407a06ba04eede4074eec470ecba2784cbb3bf4e7de56833b097dd90a2aa0651","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"5c96bad5f78466785cdad664c056e9e2802d5482ca5f862ed19ba34ffbb7b3a4","impliedFormat":1},{"version":"81d8603ac527e75cfec72bb9391228b58f161c2b33514a9d814c7f3ebd3ef466","impliedFormat":1},{"version":"5f3dc10ae646f375776b4e028d2bed039a93eebbba105694d8b910feebbe8b9c","impliedFormat":1},{"version":"bb0cd7862b72f5eba39909c9889d566e198fcaddf7207c16737d0c2246112678","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"320f4091e33548b554d2214ce5fc31c96631b513dffa806e2e3a60766c8c49d9","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"d90d5f524de38889d1e1dbc2aeef00060d779f8688c02766ddb9ca195e4a713d","impliedFormat":1},{"version":"a3f41ed1b4f2fc3049394b945a68ae4fdefd49fa1739c32f149d32c0545d67f5","impliedFormat":1},{"version":"bad68fd0401eb90fe7da408565c8aee9c7a7021c2577aec92fa1382e8876071a","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"4655709c9cb3fd6db2b866cab7c418c40ed9533ce8ea4b66b5f17ec2feea46a9","impliedFormat":1},{"version":"87affad8e2243635d3a191fa72ef896842748d812e973b7510a55c6200b3c2a4","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"3eecb25bb467a948c04874d70452b14ae7edb707660aac17dc053e42f2088b00","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"330896c1a2b9693edd617be24fbf9e5895d6e18c7955d6c08f028f272b37314d","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"30d8da250766efa99490fc02801047c2c6d72dd0da1bba6581c7e80d1d8842a4","impliedFormat":1},{"version":"03566202f5553bd2d9de22dfab0c61aa163cabb64f0223c08431fb3fc8f70280","impliedFormat":1},{"version":"5f0292a40df210ab94b9fb44c8b775c51e96777e14e073900e392b295ca1061b","impliedFormat":1},{"version":"bc9ee0192f056b3d5527bcd78dc3f9e527a9ba2bdc0a2c296fbc9027147df4b2","impliedFormat":1},{"version":"8627ad129bcf56e82adff0ab5951627c993937aa99f5949c33240d690088b803","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"8fac4a15690b27612d8474fb2fc7cc00388df52d169791b78d1a3645d60b4c8b","affectsGlobalScope":true,"impliedFormat":1},{"version":"064ac1c2ac4b2867c2ceaa74bbdce0cb6a4c16e7c31a6497097159c18f74aa7c","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"1d63055b690a582006435ddd3aa9c03aac16a696fac77ce2ed808f3e5a06efab","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},"85ae5aee75f011967cf2d25cbc342f62d69314e9d925f7f4aa3456fc2cffcca6",{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"4ead13a482c539b77394b2a97e3b877b809eac596390371cea490286f53b996a","impliedFormat":99},{"version":"12405c748be62bbefb6df7515956b0921f84d7b444d1f6c3e00af1980cc3af17","impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","impliedFormat":99},{"version":"3a582c6e8906f5b094ccf0de6cc6f4f8a54b05a34f52517aba5c9c7f704f6b28","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"49ab4f1d153a252779958fc87b700743d32b5ffa42addd70ae23ad3f429daa5c","impliedFormat":99},{"version":"0528f6d21f7a02d4092895090d2dd86104bd5a3e79eced96d5a1a7dd90943d17","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"5c935b7fc4ddc1410ea1cd7cd4e35ed106a6e4920dd27a9480a40fd224359dc3","affectsGlobalScope":true,"impliedFormat":99},{"version":"64ccfe7dcbcad9b8a661a042ecb2309e41b9ab957f7b8cd070b05583a7821a13","impliedFormat":99},{"version":"c911b3bd083dade8fdb7775261aa25feae695d562a3b11209650b47be90043fc","impliedFormat":99},{"version":"98580d6ed68c9cee7f956685abbb6502d70bd92f48175ac807ae6c5e254fadc6","impliedFormat":99},{"version":"fc5ad2ec8b130a59612c5cfcc2cdca9bcdd80851adb8acdb004aaeff00cc978d","impliedFormat":99},{"version":"f76d2226b20ec0d3e884114cfbbb933cb4d25d26cc306e8315e21a0e3afafc39","impliedFormat":99},{"version":"709b4ace1214759ca558d2b85c1ac20011c384077769ed8e08d6b2e6d68ef6d9","impliedFormat":99},{"version":"638a22f8e16b31c37297483fa38cfc760ed309b16d4c606a7b4532f4394c9c0c","impliedFormat":99},{"version":"9394183f4c37b8591156f32e41223c9e0dd211eefe7499155d4cdf15268eaea8","impliedFormat":99},{"version":"3ad031a1ffc57462a2c14b2f2e1e6eaa22787748fbcc32dd6a8a534a8e5e18b9","impliedFormat":99},{"version":"4273ef5d82e0f998bf38e777967753cde645668daf16dc8c3a72162c9cb4ff71","impliedFormat":99},{"version":"0824e917b53180b0b433f4fa85a1cc1d689a6c5f118bfdbe2086aa3b328c12db","impliedFormat":99},{"version":"7d3e062a778b8f5ea4f0cac7e925e31f88e6739812ebc5f827474324a4048f14","impliedFormat":99},{"version":"ca8bf149019241a83e9ace4f614d0410c00bff1246dbd1bbe63f88737823723d","impliedFormat":99},{"version":"33338bdcc31a340ca3a62ca2fac6086c5ac899b98cc50246d8fd4333c138e501","impliedFormat":99},{"version":"e27361b231a041b6c4da9fae61463d39d99f64926e1cd732513fd413b77bf869","impliedFormat":99},{"version":"ae1da3dbcdf34006350c132fd8795b1fc9a86d8fa398cd00de50463bd1eba9e2","impliedFormat":99},{"version":"4e003c868b0d8f8ad200b96cbc653e18e513fa23e1c19c4fe3cc25d4394efc47","impliedFormat":99},{"version":"046271de66ae07ca543f79eba0de19f342b78e759b597929159b2392e0a7ac87","impliedFormat":99},{"version":"e0864480ea083087d705f9405bd6bf59b795e8474c3447f0d6413b2bce535a09","impliedFormat":99},{"version":"e67cbea16f1994af89efd700542dbf3828a46a52b29e4d67e801bd7869dc103c","impliedFormat":99},{"version":"f582b0fcbf1eea9b318ab92fb89ea9ab2ebb84f9b60af89328a91155e1afce72","impliedFormat":99},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"38cb107048cd8ba54a70014ef9a30cf57bee0d9f10a0ca4cefa974056e1ee460","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"3cc8a3d123b6b232d48d34b51b785f9da8d193f5b5817fa521fcd2f3b9315c55","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"9a195d8476f48523446ece812e5450b5b1ec8c3b6abf99efb8ee2479524f6f56","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"354a7f8e1287d9d6b7561bc97fdd8cbc2f7c1dd79e4cb37b942e8a5cfaff1085","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"f3fe8197217cb6fd6e07c0eaa0966e4ece77cd779cbfc55767a93704cc692e04","impliedFormat":1},{"version":"a18642ddf216f162052a16cba0944892c4c4c977d3306a87cb673d46abbb0cbf","impliedFormat":1},{"version":"617112682fcb55805f0a0e16450397edc016881962eee4298f4f4e3ef795aee8","impliedFormat":1},{"version":"4ec16d7a4e366c06a4573d299e15fe6207fc080f41beac5da06f4af33ea9761e","impliedFormat":99},{"version":"960bd764c62ac43edc24eaa2af958a4b4f1fa5d27df5237e176d0143b36a39c6","affectsGlobalScope":true,"impliedFormat":99},{"version":"4a2479741e1be22a0537a0a0b341ac0df26df60c62d3d0f1bcda536936066526","impliedFormat":99},{"version":"e50d04d261120f8f9d07e85698735d5b88f3a66bc395c08ce26c3d817d141a73","affectsGlobalScope":true,"impliedFormat":99},{"version":"b0d00966e18eb7d205f31e4766dba87631e14bc121c5bef41bc70dad3c39a902","impliedFormat":99},{"version":"b5ce343886d23392be9c8280e9f24a87f1d7d3667f6672c2fe4aa61fa4ece7d4","impliedFormat":99},{"version":"72ce5b734c05da85c85a6f6dc05823b051d6aa41acaedeeb1d17c72f3b4efa72","impliedFormat":99},{"version":"b0857bb28fd5236ace84280f79a25093f919fd0eff13e47cc26ea03de60a7294","impliedFormat":99},{"version":"5e43e0824f10cd8c48e7a8c5c673638488925a12c31f0f9e0957965c290eb14c","impliedFormat":99},{"version":"53cf4076f42b29b8d411259d168d51b3a0274c42c8814e5b44dfa8803a35d4fc","impliedFormat":99},{"version":"a39461ee1f27cf3e6cfd63d21045713d26d521da55ea4d8efccb705f689e6dbb","impliedFormat":99},{"version":"61bb64660ee150f3ab618340e15cca0a81664801bede7c966ca0eca3a952fe63","impliedFormat":99},{"version":"42a12f2faa483c9b48195ed794d22698162274e755f6e07219c2351c4f08d732","impliedFormat":99},{"version":"ec0c42bb0f465e4993f2bc68a6ce9df9a2dcbc7b83e21748f82f1b69561938e3","impliedFormat":99},{"version":"f50ff37a9cbbe74475f426474d9827083c7c2c138a954d28f1690df338f69291","impliedFormat":99},{"version":"61fd6c17235d530c40f543dd7c40afab091d91c1ef890baeed30db6d82b04b28","impliedFormat":99},{"version":"bcbd3becd08b4515225880abea0dbfbbf0d1181ce3af8f18f72f61edbe4febfb","impliedFormat":99},{"version":"091767bc841f937654ed597d49e023ed59850355e746ae1a6f20ab31076ee1fb","impliedFormat":99},{"version":"19c6d6135af59693698d384050b45a8a049493500add442f58e4bd7c8a255ab6","impliedFormat":99},{"version":"6a0dba12d55314638a8c51108b20fe2f68f1364a619d098918bda91c22dec154","impliedFormat":99},{"version":"8124828a11be7db984fcdab052fd4ff756b18edcfa8d71118b55388176210923","impliedFormat":99},{"version":"2b39c6cf59088713babbfc3e20ee85f1375d40e66953156fa658346b8346f24f","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"6987dfb4b0c4e02112cc4e548e7a77b3d9ddfeffa8c8a2db13ceac361a4567d9","impliedFormat":99},{"version":"5e2ba3d18d78aebbde1f34bde356e41e9c76eeaeaeee56a37036596a9eff4211","impliedFormat":99},{"version":"8280ae8ccc0493b32d1742d585357ab9f0a508ea050af25a5a20d64010d0a5cf","impliedFormat":99},{"version":"7adfd9f9056ecd4ae6c65fde2a98654960c662714c73f048478959d04c09e144","impliedFormat":99},{"version":"437b7613a30a2fcde463f7b707c6d5567a8823fbc51de50b8641bf5b1d126fad","impliedFormat":99},{"version":"63ea959e28c110923f495576e614fb8b36c09b6828b467b2c7cd7f03b03ccf9f","impliedFormat":99},{"version":"1601a95dbb33059fc3d12638ed2a9aecff899e339c5c0f3a0b28768866d385b4","impliedFormat":99},{"version":"56fc978580577d30f4c2cdb5b1eb9217b66ed66537dd27141256f426e4b8dd68","impliedFormat":99},{"version":"2c5413050a2580becf9d82dd7e3006b95623e96f145356bf73230cd635352f70","impliedFormat":99},{"version":"860bedc71ead192ea4a0ea5ef4686e65724d14b391ebd1a6671a7044e6bd8e15","impliedFormat":99},{"version":"7c0a845bee4a084cbb8654709f48e5f13e2f6d45e5e2dde7c57cadf79fd9e3d5","impliedFormat":99},{"version":"7fa6fdc6eeeb3850e6b3166836f8c9199594c4f4bed3cee02cac0ca33d71894d","impliedFormat":99},{"version":"e53757f3e6688e8be16b36ccbeedc85637e607353fc67ff7b6665d6005aeaa12","impliedFormat":99},{"version":"88ba2660f5b024c7afd02ea700bc0c677687e17e34f0a94c3256ddb55e0f1c5d","impliedFormat":99},{"version":"7bbff6783e96c691a41a7cf12dd5486b8166a01b0c57d071dbcfca55c9525ec4","impliedFormat":99},{"version":"bd239b4c5e0a4de626d2bd30febe7d4596de481248e9e7436b1029363b45660e","signature":"ab48dcbb58c478548b4a6150e58104a481db9d7cb101c2b670e28522d00cefc5"},{"version":"4ffba3c5848b4fe62ee59b754fd5f256ad9656a0db6d37b9a2a8cb40dfc7ac21","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"32b35cf0dc3a1b1a7118b61c34ce2ad1a29695851679f9ec34e0776f2ece2a69","impliedFormat":99},{"version":"b413fbc6658fe2774f8bf9a15cf4c53e586fc38a2d5256b3b9647da242c14389","impliedFormat":99},{"version":"59e5e964b84fdb2378e9455e4e59405030e4ed2b4c6f891ce395f17796af3cbb","impliedFormat":99},{"version":"dd51e53752b310bd20c9b1a87bbf12b1fe2be7fe40f505b43199496481096275","impliedFormat":1},{"version":"a87be4662442b3feeffc331ecafe6b36cafd08727e2d7f2425a5099577e7fd18","impliedFormat":1},{"version":"cd4cd9220a1ba793bc935e76d8e5481c110a90d9868ae7866a182ee71cdb6abb","impliedFormat":1},{"version":"0a7fb8619b10bc05fd933ca9ac1c8b2ab2220be7a57b57565c3ac158595494ef","impliedFormat":1},{"version":"c4a5f91feb9c5a6b2a91089d959c38391b79a961db3b9cc73b8877d57ad7dcdc","impliedFormat":1},{"version":"90ba95a763101bb61b8a799731a2ed60b5016b8135c1a2d5186862d4b534d4a1","impliedFormat":99},"b272e3a799aeea8cdaee7ec9c4a808f8ad389517f8bd86dd0f06773701d4ae6b","e03ea500dcefe6b2cb49aaca7293a978e65714638b7051ba42dd3d3cad167d3c",{"version":"cf4b891366fc80b363cea636efcafecc5ba8899817f8d6a1ffc15b42f6a6a8ce","signature":"40229b3b5d4fe2c57887134e413593dd966eda5165bb8f07b5f12830305a3941"},{"version":"3294cd20c885b9037a59bbbd99f3191312cd3d5ec5526fc6a87128c6076ede0e","signature":"8cafbdb0da6961dac0675263cd2bff5e208379e46e6bab52a7a28ded48b6bcf2"},"812f9799d736a8a570ca380048e3862b815eab0e545ac2512b5366bdcd57380c","8c4055dfa42680fd733cd22ae5f42f3cd7ba07bf55ac8298b3d7fe474e7ee984","c25475eb4f6a445194bf8c869b3b19b88da31a8320de2e7ff5056fa06cfc903e",{"version":"458fd4c21d68557326a0425a048ffed886dede7c1c4ebf9e5f6da504d7f5aa88","signature":"1683d0ed29923c3af0e10b5e79a4ec57fe78f03e7dbab1fb69310934fbd59aee"},{"version":"e5e1f95b4a85239946bd42e36205c3f2361074816772ada23b5a2fbd53f36b3a","signature":"f3b8bb7015e732039323fda73fb19ce95e9c1912a80022fba04d8dc2aba7cf48"},"cb201b98c3065d198799f7aebfdd1780eb0dfda090b02814fee0f0e9c0b206a7",{"version":"332671962ca0a8a51584bce79785bff40d37b58d666c107f6c43ceee7ce584c2","signature":"775ee4c1901811383e799e0f39f4b2937267d39f171b019fc0f0ee8bef979517"},"1b70e8278d2e05b92031bd26962f513aeea07e0a7d63eabf1d511b4be076f07f",{"version":"d5776f63cec3a73e311ebf99c290eeed0572bad2301f1b977cdccb68cf52e46d","signature":"0135847364b4501d9d090e495007653aa9a112447565c83006b6f80a4a9109e2"},"ccdf20d78f853ef71cc2db6d6bfff29e71695d5925ff6bbd43a0ae3dd5b7cd51","804fc33126136a3fa07a8c63ea86b78b007c16f45468550301e2ce8de0f79484","6df011d36a7c5ec8985e2d25064543eac053ac750afc3d9f15830052610b943e","718834bb09520d71c2784165c2e8bf2d9144a996d6c4f2e4e42018102238ce58","c80b22219eba22d07a60fb9f591ac296ccbee02f8735f6c97bbf5195b7b3266a","f3754863fab84024ca0dac9a0e4b7e4064c2a2cf3c4d2c822c1b2c042cc102d0","d71e66be066702d69814e388f7a6bcdd375cc6484bac8cc844b10e784e88f304","15bb738b082a76b7d04a8365d9a6844b377cc0c5efc25f6e56717f41a8742b04","e615ce2e516ee9add50da36c23b052f17bc616ede79f85494dddd7ec0cd6805c","dc28000206b6b7be252e10f650845ce96294bd6e63632de575a1c0d960099359","c86ff00df3cef4cf4afcda8e75d779876c0a677ab6d2102140a590c94023162d",{"version":"2018384efa86247beedc65e0d3725e88a40182922789bc6107091252fa347332","signature":"75d858a79b0fee3d078876fc73598d94930516a6750eccd670cdda097cc9a903"},"ab26736130252fb0811b5dfce3bf99605726dad97671f625a7e0aeedabe14c6b","70113bfef357df90b0f2b41832126ab69703008d462d8a2b57cdf40fa75c7812","e3bda27f09a0acf14596b250db1e44408ed4d5b91b4563ffd31f82f94c029470","9070e0a1e012d84015374a4b58a29e1b21d8b66e90faa66e90b70d149160df45","df56f1a082f98d1866832821383deb4061e7bf09dc3c5a3e6061295a1906cf0f","25d9febbc258e7c26ae84862377ff23df7e8b8b6f2fa5b413c4be223af893a0a",{"version":"392c02f9a8617cd6c3346885d164b3732bebae9c0d933e190915b62f25d7af8c","signature":"9798fbd8cd02331b5c2812dbed806a71357ac7ce1dcd32af7ae1b01f6527d547"},"c0c6586ee01734ae247fe280b7ce9456a87d667c003faab3b23d62181afe8916","a469403c6604688edfda3fffb4c0144c0778b2b3abc09691b84b3dd2c3beabc2","23daea964a114a289a51a5b6cd4ff95792f5e74d8b01fbf49b06eed20049da0b","bbc4289fbbc080f0d640858ffb2066468561a64f1f49c8ad268135ef08c1d307","de3229fff02f5ea6543a756883edc353fdbfe33e7d6d0a2faa62afe55fb2a78e","419888a4953da818c7bae3c73b04ef7d6ac2e6efbaf73cb9e9faea2df213d29d","06fe1e12e8417d2e18f39cbe1191383b15d0603146c7be2b09e0004947de22f6","53433d5311c37192e6865b188b6d3337cccc045e6b6da7768826dfcf129d0a28","6660690e3107eacfc3c480d58574ab8a35c14089d2c325ef79da1c0106cde468","3d55ec80f5ec2d43c6bda6e26d50a03a28526e94c44812e650c8cfbc61db3bc2","2c768e18356d2c4be1c5cd5a54a1983badf752dfe7746c7c6161935a3d7fdd03","51d23372bdddb45499c4c5568e71ac4ce33af279edf15877c8aab03af7ffb40a","0466d87ccb5e10975ec359300da6d7d0c05732af752c09323cdb2521720e62c5","8049e0c1041eac5a8f6c39badbef82174074d2400cb384bf43b1224a638ca9dc","ebef7d378faa8a7715220fd906d88996794bd48a4ea258bccb0a21ea55b40d8f","180fe9b4e684870831cb141738d5f9388d90e804df8d5c06daa580e7b291da1f","26fdc4b60439fcf84005bf69331f84d8ae87fba82c9057baebcc1ac1307dd95a","f32bd7e0f6ed0a18b9a50e67b597692776ca5265e71f1ebb61bbdf54e0464e57","040119b2efd8308034b33871572b308e0beac06ffdd8b41c7d722819219cbf3d","0be24e498506cfeaf8463f719277c8f5d52b0d4afd2e22f5d45fc0de43bd6022","f3a2ac82af9d8a14375ddd4e3225f8578b3195ad33e85681bd18865328df5b43","3a15a4731a96d9dacb9cf735f12fcdca09d9c577823ec32b278c45fc48b08b05","72eb6665bcc46c277e2d5568d9f3b883058fc62e19a28e4a227200b721310b68","5df54120520aa61969b28350c3e13e018d73253963f07d3e5b8480f6e5ee21ee","2b1a2590469ea2d5f44c8909a08a449a269d01ca1a7c0fabb367c5bc28baa52a","c1b32a30289157cc33dbb9b66ccb96df89ab6d8a59bea1a8ce2a5981231571a2","369be937e86002ff2532a401e2a4984f1f91c42a1effe2b97b6506237b820b8e","56fb3fad4d94088134313449798ff3f92c5289e65bd8f87a18838b000ac94618","edbdb6e286f67c9151d6b65816d30603f134683851432c83f728979a50904742","d4a84657e7b960484e8fb28aa9eb3ddc3c27199a14b6d21e72ee6e82b317ebb6","22b4f46fd2e8a1d5c3d466c80a1e0eaba1100f072c4a5dc5cc232ffeaa7d9779","9a77b64a695927b45bb880f0ea155d0081e768d4d7322a8d78f0ca86ba9a0c2a","250f08b35fb145bea8fe00cde3d712b643e7fef2d06210b6d55628b092544104","72e42a0de9369b59bfe42e68cd19c9ad07b765f570c676d21610b75473d9eed6","490950319536b3ea28024df0c91e59ed2bf20cb0cc8029ae1a42df48874ab00f","385b814c0666e0f401a37735e60ff34c1a1019b7074b34421701a590375170da",{"version":"4d28868258b7208be3f4501d503a48863bf3203ee75aed2be4a73a205fe3dea6","signature":"be74e7487272d0772891b0ea2b337cac457c7009c984398965027398c293022a"},"86fb262322739f7304d8b5bbebdc002a937fa981356347d12dce01fa264eecee","4f0c190d4d71dc49ec4ef696f6012d328f1edba0215180f7e84b1193c381f383","7c70468076711abcee49afe4e6050546db72705b31891b88a280f8481d40ccf5","9a8dd2ae94882c056f8d3d238ff51f7990d29a0289a7063cb617820b48037dba","15b5783ba8e9edec259fdc2d63199f1b64cb9bae124e8dc6f5b283aa7d217783","8daaebd183a2fca2a80d5c1e1b284f628c7210930798559f174eb2178e5c2f47","fe64813e84d2c7a8a7b732dcf73d676b75c11ec055cc2bea0a6eb7e7c72eedb0","99bfa019855a100dcf3d356624654b8c6fe2fbf053b2d5ebc8158e08d0dc079d","1027a91ba0123b3119a8f6915bfd669f4813a2a33775b1a99a2a2171b7b35a12","d2f14ea53465caea54d4d584cd3ef68aa53534688839e3c0741205f9bae3075f","33074f64be05338c7fb1ae89639ab9407d504c94d0d04f8f74f2f70c6678a997",{"version":"15ae1ab5a3a467251aa3f58ee75f2cddbee32c98e3b597643577f538c2278769","signature":"96f99143319c234b768df03636e2674b787f4e4cdbf9e7c56947662f8144d12f"},{"version":"65e527e36a23a080d7ddc1636336b7d950112ac730360e18a5f7cc5f26977a7f","signature":"a4c5ca81dd6d42310a36e8ad5ed90ccd2288fcbac6f859917f5edec2b7de5d5b"},{"version":"5190600f2dd216333ef3aabad92da2185875315911ab5a19df9f23b809a13f31","signature":"a22b2a342ea7f18daec3d8f5f001deab95554b88930a7eafdd778c515847d82b"},{"version":"f8031790fb56b71ef1b501ddf066279f87769e741795fcdcd2c9d229ee0548b7","signature":"dc6cb8983fd0f072b86d5f6d451ed47523a3d7eca8fb08d64be6b6ab665c4cdb"},{"version":"731440639cff59b0c13fa28235b2836f378e2071b7443bb8b8361c299b699efb","signature":"50f1ae1749aa5aff6e8f83bb68b4b7cd57b36162bc3b17f09f78cfbd9b25ea40"},{"version":"d92adc7b952212d5d29b499602af59502823954f00c7e94ce2597d264c5c7c39","signature":"bd9462d89069f1a2dd735a3cc856120ecfa20c41bf1a019248d046a87fcbb560"},{"version":"db270b9c29fb25ef1c64fb9c71f050a8178f156ab69782a3dc92eb3820248145","signature":"19a01b4243aca91ffd0119b172bef715962cd440b32158544776cc8f2516d863"},"03a6589b2ca2a81c8513864bc8fc75eb10cc20ab91230cf9b62cdc8275dd589d",{"version":"d8ac524371b44eb53682626abcb3524693b74dc0009c8013971eadbb1bc7d8f6","signature":"b4e3a98b622e0df12acbe0c5c67c9985ef290ca9a5b3877437f1717c6cecd8ce"},"33dd8a66151aae844148c1e4d90b3a993c776594bc19ee9e772360e4affb48af",{"version":"cd13fcb9c9fca230e5391b20312cb60c56b3fef1ef264e95b574133987250586","signature":"adb11d6fb416e5e7c1931812ac6bd4baca455b110fd8841b4a6905f9e299d08c"},{"version":"609bd5324d58c6d8a903abae530e83fa8f64d0816ff82e8eb8b2471d6d1a7373","signature":"b71520965bb4a5223a60f774275465e046c9e8b74532e8b408dce576cfd4e22a"},"2961e56145cdb9dbba7f8ad63e57a426a3aecad9c71922f98eafe47c3f1d0b82","692bc7a0e0aefc4921352890a79aeadc4a68f45c5171794fd0a0cea24d535b34",{"version":"1b78ae8c38c922360d4c70325cf83e594f76e391e0d6bc39b0697c65a30a3a1b","signature":"666273a11838a8e9ba821630e1ce06fba622390cf7fbfc362ced4d28b5b449c4"},"f4c24f58866aeb2dafbb1233d5213c323b66fb731f8f018c0a3440a9f255e75f","0787f87c2026088ab2a84da42544e24df8c1fa965cf6dfa6bee1e836a3493919","77bfe2af58b9dda5ee7b11b8424b6cad7e7745ba1aaff7bf58657b7a31fdf87d","2a1ceb639417a528170aea1d8924e61d8b5bf8d95de37dc7e4b431011d2a49c2","71ff0a7cfba4209a055cf9b8abd8cb0ee468e5f512ad8163c07766213aac64d7",{"version":"f916dac7891a73594d3e8579d923fc433b9a65e026fba9e5b7dd39a7f01f3b83","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"954078297f1913ccec8bc3f35957e2a791dc4189f93bbe49d9df0c7e85d8f72f","3db978b42a198aeac30a981735a516c70d2ac7670bb666b7a0297b64e8ea4c38","9f69c6f9b97ac89f0eb75173e552d79367b11c9e4fd298d5085f4eef99379b95","35414df28dac03ed6e220af5a212f9aba38afeba214fae9802fec81a8399e5f9",{"version":"efbe6f29c56234634cddf69a318308620c714519fa65f153205ed2ba4386e2ba","signature":"4cc01d45183a43fa9402ec7c2d5e9fd4733cbca1fb778a5132b313b08bd43f64"},{"version":"71438a74370b6c0568ea3eee86caced05d220d748b8f336cb7be512e961674a4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"b0f6e812446471de0e3fc28cdeb7ee1912a9be2018ac3db3a47d9eaad172e204","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"1be62b0ae46d1fcc7bde98beb709e92ec69c64b824d3bc94589031b55eacbc0d","signature":"96223fdac3aaddd6718243f127330e50898d12240336e53725209a1f6160db67"},{"version":"16d3b90751bcff1bbd53309e05a696267e5da68539ae7c829fef13312e8ce858","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6e813effc479e7c5da7f9f4fda2df90f44e091db140e912c24e865dc16e7f16a","signature":"48404e226478f761ac667cd55a81db0e53bb6c8ec85871965b9f953f99262193"},{"version":"6ac8190d670c15f82d051cace93a0c02ea2922c29ceea1e14105e29402a126ec","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"f7d6846e8af1042ffef67520fa719bc7a3c74fbaa6bdc29bd4cd624497a029dd",{"version":"a76f042d64f8dff3c5e1d557847dd12560cc8e7fee996878e52ec1c92389a3b3","signature":"03985eb6c5ad09ef5151cc758c2f1034edf94a92c5019f4ee37533abc36da13f"},{"version":"21ac4d7b2fe2a8fc098acd752db64bca82c5e86db9a0c6d77e95f09e0ee0c7fd","signature":"61e0696065b325ed71e05401bd01a88916690d1664db0fbbc576e6a6528d8822"},{"version":"fe93c474ab38ac02e30e3af073412b4f92b740152cf3a751fdaee8cbea982341","impliedFormat":1},{"version":"d377c38b3921c35350c9b50ba202d5452fc7a21a0ad734b9d3f886f021d9ce1b","impliedFormat":99},{"version":"54d9695325331b7549212c33c8cf84d040206fe7b58c8c849a798456cb45216d","impliedFormat":99},{"version":"54d9695325331b7549212c33c8cf84d040206fe7b58c8c849a798456cb45216d","impliedFormat":99},{"version":"476e83e2c9e398265eed2c38773ae9081932b08ea5597b579a7d2e0c690ead56","impliedFormat":1},{"version":"1e00b8bf9e3766c958218cd6144ffe08418286f89ff44ba5a2cc830c03dd22c7","impliedFormat":1},"e8cd54078749c737e2c39bbf5577d7bbcbed30cdf28e6f1cc24e550c0f7b92be",{"version":"1ac71842b15c610aed71e7376b1e8a19b65dbb7ffff51b646fe404ce1054673c","signature":"555a4eb7b23abcf5c4e7e06976191f275021081b8e95fbc361e0301138639a0f"},{"version":"2a30c38a4687c848f0fcb6ed9adc9bdf4d2103f3ea44047eceb1976c95cf0a8c","signature":"442fc65a2a0732a9c559e7689f2c25cfb6ce646a2b5f218cb9a45a26f398ed59"},{"version":"0c996be745af11ccf07b28eb3e007aa4277d01b16f53259cb355daafab167abf","impliedFormat":99},{"version":"27c1b57e01b13f914e9d3507202036d676222057d6fa1032332e2954296b9d45","impliedFormat":99},{"version":"7348c35ce10f8323a515ea6a6c237c0dc7c3fd5e491c4dacec2a7b93f0dfcd26","impliedFormat":1},{"version":"258e67b2638408b67fbf5fe6c953b8f2dd2a69f9171d31c9a976cd2329716e4c","impliedFormat":1},{"version":"74137333f57d632f001828cee60b2094ec332abcef1aeee18a0970037923dcd4","signature":"dcce64474115c89001de4c463109080539b325c44858306e722b15cd18d98219"},{"version":"802e7bb6cae1c72dada49e132b6b28eb3f7afbaa27b5ab266b92b662b5327e47","signature":"4a6858702ce17a29995b5e223489b3481cb9998754d7d409dd97d00963d76e05"},{"version":"e479cd25cfbd4bc91642f4b12f351d658c484d1a7b70d0ecfc4a52697906a045","signature":"ab02b9b1f41df13cd6eacd101dc4cccf32b677c5df88f9d6f9d58ec9fe648a67"},{"version":"34bc3cfeffd604398847bb96e61f637be37d999ca0c10d67368136dc1be463cc","signature":"2571a03d3ed5d860359c75c177e55747b7ccccb97e898a5a320fc05623beddd8"},{"version":"c3edac43c4a1d3e16edf15e258d82bf554250a017ef4e3576834c63457f4d6c3","signature":"0e5889f9a92cc3fe8fe1871bfba2f90c1e6848c6e407ce9562ef95c36231f002"},"347343db651932270ab093d5830ea54f419111eeae659b77a9e2cb13f328a9f7","3b5ec50e7b2d52f8cbc0070ed5556ef15c90833fcd899cc2ecc5d47c46c74973",{"version":"cf59492bcef122d8585008c4752000175c53abce051c882757b7f1bbd8a58961","signature":"870fe0dd8e82102dbadf0760b77925e9412c93d95da14cb998f138177d26f222"},{"version":"f6aefd3198da082563b438f5a5c1563480e772730c9c51366edb199ec937ea4b","signature":"659b8c23e1a6160444eae10dbc944dc8cf37b17c25b6a75b021660376ba49cb9"},"6f4dadc8756570c85f25fab29d5025a744d41f61ed1f617fb77db1eae40d9b79","19da3542472ab262424b57bec72fc4d400b67497ad03b42c6007a25f23f4993c","55c69554020aeee5a248a84206d957cdf889bd5c931b66986770edf909ae07cb","c0fbb82566e3c587cdef075991cdc0e83e544ff71ca6ad7fc0a8b4114210d401","345fab538a281744e1ae6a55feef5b467d5389e48eed730ec49c4f782e0cc496","64e6a475df2e8aa93aaf367c5df92364e0ec677411bd068405184f83e517c84e","77a094e4e0ab32fb00ddde5c78476d5fe0828c712c6c2efde60a17a226eed0ab","cb7f713163f0c90bc6db0370b8a87fb73c98b358f8bcf48aead28597f55b0f86","56f427e398577979ee99d890fef5d9dc53f3f2c557085104eb6890019787d916","56dd16a4d896aead6a4cd787098fc91eb1b3835dc4b015f6f17e770551e9fb18",{"version":"39532325082bde9873919451c6f1a8ad91f5bef582b3ea16547062bdb7143065","signature":"bf25c45519d3efd692b7bd7387b41720d23d5dbdcab032c9cb4891a4e10910a0"},{"version":"fdb783a322c675e4561918088c76fa4adf691afc7c1935300138e093c1285f83","signature":"0cbc40d93c13b47faa989bdbfca8eed5a48f277a850366c910af796afe035935"},{"version":"f247359cb12728389b7bb2d3225d2e2376aa5fa7b2d7f46572372e14b415c69f","signature":"91e9bcbb27abdc6de28aba65757ad7c0371120f96af6c16c62f339f1e3745b51"},"d65387fc3ba09cca1a3303e645d498b33bc70a59528ea21ec210b2e78b842974","43c417abe8e54d69d037f666583cba8bf222070cb005f5cbb5fcc1b02e0fc01a","5c8e29eb6fca80933c1fb4e7f4ddfaf1a14a3392e5277ba1d327258f8dc99783","495d68e9b25ae0c7407b6e8c8695cf3e7395eaacf7dfc2617c6ecfe6eba8dbf2",{"version":"d151733bcd9d22e12fe89b0a59bad214872b2d561afe7fc0a727901a79ccbdb1","signature":"5839809854919e435a02ab674580cfac5a4110e68d9012d333b6342a573e513c"},{"version":"e08475aaf2f22efbd09cd9de5d8ea576adb3be230aaa5e4e24f185609d70fa7e","signature":"214b97d6fc9a99cac29651af928b444edb053979a55a1da2d7cc4f63c6ca7ee0"},"52cf598a1b721f3329bfc2ae6103b23f76173e61adc77bfa64c01bbb6002504d","72992d605284c2a96ce10507cf3dab03536cb9bbec569ed3de7d07a55fd35c7c",{"version":"05b3581ba268fc0794a9299ce2fe953bdd7eceec017d53deb79228ac13555be5","signature":"a941cfd36c68ded25c50c689518976de4e6669d5a633eb0b3b31a43127e0b786"},{"version":"f1b3b1d73ecdc7df1e5968f4154fbeb1b24d975c13f696d049d96653141ddf98","signature":"bf2ff4f10ea73bd8971ecd08059845bdef23875e2a1e1d36ef5ece5c21c8b83b"},"31536346bbfe853cbaba990964d535b187008041b1db87c87835a11fe5644fec","dcddf087a7d961377633fb6e57ee48a53e81a817590bd192b8b66517f73d86e4",{"version":"67bdd738108cec09fa4bc092e0b4af9386fa438030436b8da50f9640b684a4e9","signature":"2efcb185be86df956227b0d7571bc212081c84571c2a635314c8b00a3329cd47"},{"version":"1a5c85ca837fb53dfbf9afc003398f497aee12d2bd30a2acb435429147cb69aa","signature":"efb8d94cb63aa478219a57de29d534592d82716c0475eb85f97e9575a104071f"},{"version":"2aedc24ce50ed0aac66393dc0d1faf8b8fa17d3b2fb37f9f8e1c66c85954fc57","signature":"86578fc5db1f524c0a36be6052944fdf74f2e49e7c9670e2c4d920d6ccc2b7f5"},{"version":"350316a01b505d1cb0431c50817402a6a82c0d5fab6b27b76af27c259b021042","signature":"21c0cad3492f090864c27389e857d076c6ae7f142e446901ed25a1733bfbe76c"},{"version":"3fb64d2de7de1d85c1ef2143edae8f17de0e6fe62de014161dcdb194d7997c65","signature":"7e785b143a0d0c6bb023778f83d6d34e0d4976726135add21d1b3d6c25a521e3"},"f56191ec2f9da0206eaceb9579dd62f2d5aa9fea1499782c1883908eefe33201","b979f72b6f41e87df2372bc4993a2d4ffc203dc56670ea867869bbe1670f46ce","0c38761192759ded08995fbe45f9ba1a521169204690db5e067b0ad58bc14c42","37329c4dc9437527f615f961867b65e638a7d61db8554590029dc87aa2cea0dc","002b5be581b9f660dcd8f1caed92e3798b0980dce3f3c92005b9f95f0610fae4",{"version":"1fccc097235e47b0021a6365176b6c1e6028a7309a55e7d5decab91328f0acc1","signature":"bf135f969b9fbb7aa9133ad0ba9e55e6c6f3b7fc976eaa04f140ff895c536b62"},{"version":"ad0b264403db93942ed42db950397c46f8ceed9baad376c0baebbbac658f3fb7","signature":"2a3d13784afae69e5d3d6327bda126555b9dac27dd6808e25e3b39f1b163ef56"},"0a30389ea8dbfb42061721ef22f16e93b8c7413fc1451eef5a50a70dc3da50f3",{"version":"1d8b191083eeab243548897b93b1943f0f434aeb693351238fad315c69ce8c52","signature":"62e652f61742da5e791dd7c1dcd646e0e5e622fc5fc01ace7ad93df4ea4198b9"},{"version":"c462e9026a9ae5069d3754977523a440b5b45d4e1ca589d5bd62c1d24a1811a9","signature":"8b16906966e5a39ae872a27d3499d63a596c145478b6185abc698127c1943cb6"},{"version":"a0f1fe5716c9c7896068b3e1409dc659bdc003d41e2c3e688774d9fbc9c78e95","signature":"7d5a89b2cf1256da54d4f549f686f18e983069785d4d6930466160e350634cb9"},{"version":"6e66544c428f62a49d86b1d1f27ac16aae5f9175f150eefd891b10b9657a061a","signature":"88e37c714bb35035379b8bd3c106e04760caa815a09fd716636fb0eeeba0b33c"},"8b27322859387282d37adb4019620d0b928729e1f26d941147751d8e2ec7f824",{"version":"1464db80838c1a8a6bf2cf7c0472b08c5106052e08de977d952724b09cb874e7","signature":"ce3dd68db8ebef58a9bfef61ffc9366ab64eaac7d7c31c3fd4e87cc0088ec7c4"},"88a13699dd3a849f2077cb67abfffb7a254c77ab90844687e6017bf7e11f6438","2b3bfab7606e22f305cbe85f084d14a659a173ae38c5c1cd77b621d3db570405",{"version":"f4272be1c13f9fcec3147b88f423de92e2329268628242fcfc7576607da531aa","signature":"4d6e46097f7d85d47313ec2b26803b6953b5eaa41f8a04ec900a94f1a81a56b5"},{"version":"fd43685bc19eaf18c8fbd0a7439cdd0779766a4797dfc1a988fc9a698e10df44","signature":"db9a15a6d09242668afe9439df3d925651e9807463567e431b6defc6d748f139"},"7062ea6656ec0dbfe76abff8d9d3c6b65509fc3fd764b98b06bcdd97f3d1b047",{"version":"40813dcb69eb435756d545c61b30bd30dac45932585c0e1766f9b3a80e2b4b80","signature":"cee33bfbf9b7d865fae5994d17a926d998905e111bca63fbf83608452f633185"},{"version":"95376ab283602759c99a544fbae38ab89b3d2743e62dd5bac3c825c53ef4c8a9","signature":"af610c52bdfed5643127a3bc6bb2dec268ae6d21c1e6a0b86f6ce4779dd853f8"},"62d5e2db438a2bb718fa7ab1a7f225a0cbe85bc0bf2625f9f0a4b3183b49c3d4",{"version":"116754030001b14d636c61a0755163a57fd932a63ea92910c7edcc5f24375c89","signature":"aee79945eba0f93f292eb0881d6261b0f89e9b13b65ed0b270f1a7b119a89997"},"a3f6ec7d2ac2933f611c24ec2e85ccb5c12ead0d6662fbdd9f3db38d4c7f410f","3a355fb38d746de8f01cee4b4fbb9e9fcd1f2466161bc071a9c8ad7727f5edf2",{"version":"b8d8a2ea561c97be39a42db5f9c4930c0279879445ebff01e8f9dbf66b33911b","signature":"51b0b8b336830b28f2e881935ae29fb78a0011eb5f069af26a74e0a2988fb761"},"45085643263bcc103870ebd0183676a0018aca515e9e69a475d34bc4e85b0830","0adf819530aa5d53600fc3fa2f4b8d205171c02c53cd7fd1ca9e4e3e9097d8dc","e7a66b173fe02de0edaf023e6910b1c69cb210940ae6c8994d565b109598dba0","a37f7a05cf74ab1b9762b78bea9cc8226437c836577362e3b283f582b288d48a","49f9472dde6e2e5db6aff9e7bf2ef161d1bf98cb62d7b530ca002b2c7bf35edc","3986e13bca52fddc77a2710cb6f78b96aba628020b13f9fce581ad7742d3bff2","4675eb65cc2e9edb59c85d9c9d9427aacb101b8c728751c55df601197a562fc7","3a193a4803d7c772398cde1def166242af3410c3c543726d70037d4cf43fa889","f536b2ec53d43fa82b72c6871a1f182c78609912b5b9fdedd8ec5ec3d31b52db","d9f476970a025878ed76c53812a20d964e8907641e6edbaa0388897bcfb7eadb","b5db3cbf28caa68d9e35e56fbcd8b93e0fe15f6d128231f9b6cc57f58ad1a0d0","9ee44bd7c13df5ec8f17deb559a4cbac1803f9d54c94dba0571c8054fc354a87","7f013a7a9e3534dd37b43071fa0cdefaf1f29e52ac8a94396a623d634deb7db6",{"version":"c90982186fda667918dab68e62312f1ea7effcc9b32b0494690f18aca8fa70d5","signature":"9208f72dc36272749c88077ab3b09059c0c458949dac7e4163004f39e95cdabc"},"c6640526c82d82742c142723fdc141313d7495b6ea7db83cf8c249eaf7700b4e","df4261834998525bc7bd5b3c124d38515f2f02f78775af112edf69774abc544a","1897984c35f745db144b86ca4cefd7836af5c580da8d146ae1405672359982b9",{"version":"03a221ae30cffde1c1efbb63a1a698ee336b818dd75df606f1fac0255b145254","signature":"8d4bd4dae53d134cc822bd2805ae99a6126e630ab1ad0292f083ab8e156d6866"},{"version":"3f57a6bb2e181847b62722122d7e61b926f3480aa23259410904a6bad88302dd","signature":"e994bcc187fb3fa3acbc4015d2f7aa7c20a343908a8a33fd19429311d5432843"},"487a24e77f445ad876f616f09a959b02599817d2f7a6c6baa853a2e5f62f664e","5df25aa8b5cbf897175e5d0c22a8f58e848d37b2942a6d7bf3113bec26bffd53","8b8f94228e22aa82d0726a9a7d33c6f36dcaa359e7097d49079f057329529155","caf70a825e3ad9d00085fe8f78d32d2a630efa499fce692f81ad296dae4fd0e6","e2036fa1915b00d184491aa61d351f8dc4cc25e13c5dcb61275a6bbd96580041","b29a9d2d00977c14ffb83fc566906397109d9e4b5d051d6533a09b960646ee6e",{"version":"051f69724c0bb225c78858b0850ab8d3859ff8bea4dc3995cc6c15c8dca9e840","signature":"d720e6c2795c495a7dd70ab38c49526b6ef44f022202e5ce13b848f59b6ebff7"},{"version":"b6425f96b8190443d723c7bba3a8e57da96935e7dc74e17b2903088b2d7ecbed","signature":"dfedd859ccede9d9e55b127b5569e31bd00d914b18c1d6bedaa2518c26044866"},{"version":"62220f1a63680b78115e853f912295c2896ebf8f2afc09ffa602cd475afdac82","signature":"b049c82118ce5cf715658009e5486340b6177766acf0fe3606c5403a09b6529e"},{"version":"e2a4976f8f423b6f664a261798a11cbf2839bc2de6576c0433250a5f9db76926","signature":"00103ae8cd80f3aab8de0ccbc0fd90f06a49749925d818d53da3da70c958911a"},"c3dcf8b2c991f7adb7344aa0159d4129b264fad1b79711272804ab7cda17d842","834fabe6543e26e32407548963267f31e9c1c947e94326bb6ecbc61fb954bb0f",{"version":"5980d0b5bec8956947a2d9935b08385d3bd8728aef5cfa8379d7b36a322af16c","signature":"f5b66913fff0b28849ecd8669745817f07bb83553758708faa4637901ea79dc4"},"4853903533d328de3c6bf3e6728facb467b92677e790387a13476efdfabaebf6","4ffa4c413123ab9fce450e3037ccf6e1bbcdced482f12fbd8eac68709bb9953b","69b3d3b23649412363602fcc21843cbc856d00583b452bf9b6e803bdc77a2b84",{"version":"9a89d3b28543307bd13ab56eba18d2a8292794c6e3ac090f98c670029bec7436","signature":"3a0d9ab24b5fe2813990d4a70586001aa727b729410d411f67207a6fa9163b17"},"e49d1215538f51bbce3b480dcf369d7c9527303d5d985059df3db9ae73aba633","77be08b18a6466fae98e18acea30ab01efc76afaf53fe77d8a8a747ee779337c","672d61a909ff896bade9595918927154955e3cbf50e676c4d4df30cb7e470f37",{"version":"6ee4b629b54461a336925f859d7bf1259f4ec86aa8baad2a5276efe69b36ecd2","signature":"6716576d95bb66268cd3481e0d06b8133a0d5acf3393c96ae9f8eb8a07aa86a6"},"465f969a9b1ab10676223c18dc7d506f6ef3be63bd7a4ba9b085aee530b6282c",{"version":"6ee4b629b54461a336925f859d7bf1259f4ec86aa8baad2a5276efe69b36ecd2","signature":"6716576d95bb66268cd3481e0d06b8133a0d5acf3393c96ae9f8eb8a07aa86a6"},{"version":"013d54dad4917622afd222ccf9530d63a5874a6385f482b5b4afe3607b0ae279","signature":"4a6bf65706143a327eebb9b778a07adf2626aa208852c6282d77ca063ec065cb"},"964558aa33ce14d4547e1a2714370b9838cc30d067cb5b9385caf4f5d9e7c5d1","8aaac73bac1415dc2938d9744c7a2541e697e3c729e44344e89e668288a989b6","32bf6c45b59557ed08eedb85bcbb9217acce007d1b88eb5a4c5f9d159984db65","e42234af5867cac924e13459b3dc53937840e08f7e63764b1dbe7bed14f55814","c6c142e1a33065d2307d4a1d084011197dbf6c2898055255459d7daef0a6cb32","ea628a2736e081dc1a9a4528038f5ae402b147a490130d90d458d0783a5221be","285c96089ae4b6bf60f938bbd0f2f268199674df65698b0f815e785f8872bab3","d11dbad1c86581c6b7ada6500ce13a823b85aaf28802c86aaf9709ce46af37e4","a2c0ba66de4c7cca6f49996132a7d9ccd0436125ab445c8931da1e382848d71c","7d864d2d4021606ac0b424f67ff6ed1e24d5cfd0c0446a2b93b212a3bca93397","de3697b8e5255cd04449a376c6389450f949ccdf3133e78f3f2865e0e138ccf2","88eaffcbccaccfe5e0ca28e804eccf899e3167e55b4bd7d9610400c2ef9345cd","6701896deab6e4a552157fac599e28a32a39b2ffb1f37f87f421d44264589a80","934da826699a6806b2a593a0cc16b4ce23620e909aa22c7a5cd9acaf94de3896","4f60e363c73ec021f84f6d88eab43047ec544cec39911e4c3a1b0f423676e482","9c9b6c65d5fc2d52f73e87c3046d1355e3f05e07659b75edc0992aa95e71fe71","64e262b7b085152e898929aa7a803450cd16703660a9c5cd01a43c6ee042546e","4fabf42272e06465badb5f4ffa3d2067a8b2f7dc33b78fb0b3e7f42f4695988f","6cec859e5adf537ab411c26e21e5383a77a2379c024ef36ac14e7d2a6a32ea78","d22b63f3529238106eae787d97abd7e55f991722d60806cc5bdcc93b5749a217","93bc02569e2ff6f236d7c82dc44672fbeb7c6d990983992ba72a5ff80a0f3099","8fcd40b95e20807e4c0750cf794c42d10dbad9638304361f8eb26812541dccca","f76ae8ce733c0b1ac6b4db3fc601eabf27d6a740f06542acb0975f5b645198c4","9741a306db696db40ae0c6ad233162decef1a85d22ec343848755f63b241c48c","0791aee4086e23064d1c75f88ecc99d90048794cacfcb6995ab4a8ffb03229f0","e183fb51594836275e8209dbf3103f41d752c67c351cc648e8aeb31ffd52ced9","ad93099330dd539219c3b6aa2442722b84b8d1906a8b152de7fc9cd762f4b63d","9cd1fbeb0b5019e08cd7cfa1186d5c7aa387bb8eadb69e6edc6006964f8a23dd","58b6f534d52b5abab5a7dcb0c1897716d4dfdfc22b1b39e6921f29b3b7f38e03","8c5ad1fa9d70b55df375c004574ba9177bd7dae9313a96641beb762ad4272f34","52f01900b2a37d2416baca07ff10d7297b05cf753ecef22f9d4880a1646786cf",{"version":"04178fc0cf6b49e486741a69d556dad59a150e35f821e660b896abdec1e38944","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},"cd583cd6119b73e6e55a02a0d3ad60d2ac9ecb477a6df9fd223dedb34a9e1c9e","07bb227570c6c0045b2685d9bfe9ba78f0cdef1a486c284b78f5be0deef8f677","e41f5c7900600af274a728032ea2c15202b29871bc18b9c666a367baa95bfd23",{"version":"6be19bccd03455ea619ebab3019c348b8330b61238821234dd22a5f4a9ecede7","signature":"852dffdac07851cfb5c0854108f7c1feee238ef171cd0960ae26faccb8cbda04"},{"version":"e1eb1c7d0de10bbaf38adf7102b8f974ab5fafed3e41bd371ac15f3e24f9bd9a","signature":"d043ed0f85a192384e54867e9be4bd73b4751f7481418d2c098d3d9063a227da"},{"version":"5a37a6c6a71c401aeeacea536d264220e4977d9cb8fbeb1123094320c9ac7ca7","signature":"1fa11e6b5bb71a46d5ced0d267425ed7a31eddcce86b9d02f26542b50c4953af"},{"version":"47686d106e68bc27ee0d3dc7e073a44fc8792eade22f31752b8db56c0ea4d55d","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},"5bf6e294c29ee115db7bd3e110d750548d547172a303124b41b55fc5d4cfdc1c",{"version":"715056447818abea4c683e48602c88afd2564c72de74f68356dad89d8ef39c0b","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},"ce50b9c053d8820e28ab8945064042a6c8a08b39bf34b684cbdc6177ca7193c1","fbfc5271da0c7b350f386043c7f8533d4dac9a4d6bf3d033f4f2e26ec2b967ed","e8b772201097b27600185987db45e1eed7c7c291bb902a6cae76c87e5ebf5b9e",{"version":"e68c4bc736e1bab235f253cf5f6ca304f5982fa839d1d768014c3d29c4f6db9e","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},"49763cafa161100bed0f4c74e822517242979f52d50fbc05b7f1758a0ced0dd4",{"version":"b166ca5e6afe702a8b12b8ca853e3d89bfe5117394724f8c16511468ff7f694f","signature":"6b07eeaa1d9309d5156a9bde616b416db3624106e92856ac878b827e4dcb2e7b"},{"version":"ecba61d66cc848853b13af94a1304d5ad72b6b69994dd51ab7d27b77a631e321","signature":"f6b7ed6e72beeaee38d802539bc36942b285e75e62b4bff480c0df3fcb5936ba"},{"version":"89c419c1db7452834c7e23f48a50d72449c9863f9d201d425a9eb6db2f4875ec","signature":"efdc985497f0b6a99f9d69348cacb9fa36524801504f5b859d90d5569b35565d"},"a1e6f1bffdbd92e141063f827f4241d89ecce9238ad17a2f9a0f6d7afabf0175",{"version":"751764bb94219b4ce8f5475dc35d3de2e432fea01a0c9610cd7f69ad05e398c6","impliedFormat":1}],"root":[[436,438],487,582,[594,598],[600,687],[689,701],[703,708],[715,717],[722,864],[867,879]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":1,"module":99,"skipLibCheck":true,"strict":true,"target":9},"referencedMap":[[731,1],[732,2],[734,3],[735,4],[736,5],[737,5],[738,5],[739,5],[740,5],[724,6],[744,7],[745,8],[746,9],[747,5],[751,10],[754,11],[755,8],[761,12],[762,5],[763,8],[764,2],[765,8],[782,13],[779,14],[784,15],[785,16],[788,17],[787,18],[789,19],[790,20],[792,21],[791,16],[794,21],[795,22],[793,23],[799,24],[798,25],[802,26],[803,16],[773,27],[606,28],[776,29],[809,30],[806,31],[810,16],[811,16],[816,32],[819,33],[823,34],[821,35],[824,36],[599,37],[595,37],[609,38],[610,38],[611,38],[612,38],[613,38],[608,38],[614,38],[615,38],[616,38],[617,39],[618,40],[620,41],[619,39],[622,42],[621,39],[623,38],[624,38],[626,43],[627,38],[628,38],[629,38],[630,38],[631,38],[632,38],[633,38],[635,38],[636,38],[634,38],[638,44],[637,38],[639,38],[640,38],[641,38],[643,38],[642,38],[644,38],[645,38],[647,45],[646,38],[648,38],[649,38],[651,38],[652,38],[650,38],[653,38],[654,38],[655,38],[656,38],[657,38],[658,38],[659,38],[660,38],[663,46],[664,47],[665,38],[666,38],[667,38],[661,38],[668,38],[670,48],[669,38],[671,48],[672,38],[826,49],[827,50],[828,49],[673,51],[825,52],[833,53],[834,53],[835,53],[836,53],[837,53],[838,53],[832,53],[839,53],[840,53],[841,53],[842,53],[843,53],[844,54],[845,53],[846,53],[847,53],[848,53],[849,53],[850,53],[851,53],[852,53],[853,53],[854,53],[855,53],[856,53],[857,53],[858,53],[859,53],[860,53],[861,53],[830,55],[862,53],[831,56],[863,53],[864,53],[868,57],[869,57],[870,53],[871,53],[872,53],[873,53],[874,53],[875,53],[708,58],[715,59],[598,60],[716,61],[600,62],[601,63],[717,64],[602,6],[722,65],[723,66],[728,67],[603,68],[605,69],[729,66],[685,70],[684,71],[867,72],[829,73],[683,74],[675,75],[676,76],[686,77],[687,77],[689,78],[690,77],[693,79],[691,77],[692,77],[876,6],[677,6],[733,80],[730,71],[877,58],[753,81],[878,82],[743,71],[757,6],[879,83],[727,84],[760,85],[680,86],[756,71],[679,71],[681,87],[741,71],[759,88],[725,58],[750,89],[742,71],[682,90],[726,6],[674,91],[758,71],[752,6],[767,64],[781,6],[780,64],[808,89],[769,92],[813,93],[812,71],[783,71],[775,94],[804,64],[797,95],[820,96],[778,97],[796,98],[800,71],[805,64],[766,6],[777,71],[770,6],[768,98],[774,71],[807,89],[786,6],[801,98],[817,89],[771,99],[814,100],[815,100],[818,101],[772,64],[822,64],[749,102],[748,71],[678,71],[604,6],[694,103],[436,6],[688,6],[865,6],[866,6],[702,6],[695,104],[596,6],[597,6],[697,105],[696,106],[437,107],[698,108],[700,109],[699,6],[701,110],[625,6],[703,111],[705,112],[704,61],[706,113],[607,44],[662,6],[707,6],[594,106],[438,114],[487,115],[582,116],[236,6],[504,6],[506,6],[507,117],[491,6],[498,118],[496,6],[880,6],[130,119],[131,119],[132,120],[91,121],[133,122],[134,123],[135,124],[86,6],[89,125],[87,6],[88,6],[136,126],[137,127],[138,128],[139,129],[140,130],[141,131],[142,131],[144,132],[143,133],[145,134],[146,135],[147,136],[129,137],[90,6],[148,138],[149,139],[150,140],[183,141],[151,142],[152,143],[153,144],[154,145],[155,146],[156,147],[157,148],[158,149],[159,150],[160,151],[161,151],[162,152],[163,6],[164,6],[165,153],[167,154],[166,155],[168,156],[169,157],[170,158],[171,159],[172,160],[173,161],[174,162],[175,163],[176,164],[177,165],[178,166],[179,167],[180,168],[181,169],[182,170],[187,171],[334,6],[188,172],[184,6],[186,173],[333,71],[253,71],[499,174],[492,6],[554,175],[553,176],[565,175],[556,177],[558,178],[577,178],[557,179],[489,180],[488,6],[494,181],[495,182],[574,183],[550,184],[552,185],[573,6],[571,184],[551,6],[493,182],[549,6],[497,6],[185,6],[590,186],[592,187],[591,188],[589,189],[588,6],[711,190],[712,190],[710,191],[541,6],[543,192],[542,6],[445,193],[447,194],[452,195],[454,196],[204,197],[240,198],[391,199],[235,200],[223,6],[219,6],[202,6],[381,201],[403,202],[203,6],[362,203],[244,204],[245,205],[332,206],[378,207],[347,208],[385,209],[386,210],[384,211],[383,6],[382,212],[242,213],[205,214],[280,6],[281,215],[222,6],[224,216],[206,217],[264,216],[351,216],[84,216],[238,218],[237,6],[390,219],[418,6],[214,6],[308,220],[309,221],[303,71],[471,6],[311,6],[312,222],[304,223],[477,224],[475,225],[470,6],[377,226],[376,6],[469,227],[305,71],[343,228],[341,229],[472,6],[476,6],[474,230],[473,6],[342,231],[464,232],[467,233],[271,234],[270,235],[269,236],[480,71],[268,237],[283,6],[423,6],[713,238],[709,6],[426,6],[718,239],[719,240],[425,71],[427,241],[80,6],[387,242],[388,243],[389,244],[226,6],[213,245],[189,6],[324,71],[82,246],[323,247],[322,248],[313,6],[314,6],[321,6],[316,6],[319,249],[315,6],[317,250],[320,251],[318,250],[201,6],[217,6],[218,216],[254,252],[255,253],[252,254],[250,255],[251,256],[247,6],[330,222],[349,222],[446,257],[455,258],[459,259],[394,260],[393,6],[79,6],[428,261],[191,262],[306,263],[307,264],[298,265],[288,6],[329,266],[289,267],[331,268],[326,269],[325,6],[327,6],[340,270],[395,271],[396,272],[291,273],[295,274],[286,275],[373,276],[190,277],[263,278],[363,279],[216,280],[419,281],[81,200],[248,6],[256,282],[413,283],[246,6],[412,284],[85,6],[410,285],[225,6],[282,286],[406,6],[207,6],[208,6],[259,287],[221,6],[400,288],[294,289],[392,290],[293,6],[258,6],[249,6],[364,291],[365,292],[220,6],[367,293],[369,294],[368,295],[228,6],[257,280],[371,296],[411,297],[414,298],[192,6],[195,6],[193,6],[197,6],[194,6],[196,6],[198,299],[200,6],[355,300],[354,6],[360,301],[356,302],[359,303],[358,303],[361,301],[357,302],[720,304],[215,305],[350,306],[399,307],[430,6],[432,308],[434,309],[290,6],[433,310],[397,271],[429,311],[310,271],[199,6],[292,312],[210,313],[211,314],[212,315],[279,316],[372,316],[265,316],[352,317],[266,317],[243,318],[209,6],[262,319],[261,320],[260,321],[353,322],[398,323],[302,324],[337,325],[301,326],[335,327],[336,327],[380,328],[379,329],[375,330],[346,331],[348,332],[345,333],[370,334],[339,6],[451,6],[338,335],[374,6],[401,336],[287,242],[285,337],[284,338],[421,339],[424,6],[420,340],[402,340],[449,6],[448,6],[450,6],[422,6],[404,341],[300,71],[444,6],[344,342],[241,6],[230,343],[296,6],[457,71],[463,344],[278,71],[461,222],[277,345],[416,346],[276,344],[83,6],[465,347],[274,71],[275,71],[267,6],[229,6],[273,348],[272,349],[227,350],[297,150],[405,150],[366,6],[408,351],[407,6],[453,6],[328,352],[299,71],[417,353],[439,71],[442,354],[443,355],[440,71],[441,6],[239,356],[234,357],[233,6],[232,358],[231,6],[415,359],[456,360],[458,361],[460,362],[714,363],[462,364],[435,365],[486,366],[466,366],[485,367],[468,368],[478,369],[721,370],[479,371],[481,372],[431,373],[484,245],[483,6],[482,374],[546,375],[509,376],[510,377],[513,378],[505,379],[512,380],[508,381],[503,6],[514,382],[515,383],[409,384],[521,6],[566,6],[490,6],[77,6],[78,6],[13,6],[14,6],[16,6],[15,6],[2,6],[17,6],[18,6],[19,6],[20,6],[21,6],[22,6],[23,6],[24,6],[3,6],[25,6],[26,6],[4,6],[27,6],[31,6],[28,6],[29,6],[30,6],[32,6],[33,6],[34,6],[5,6],[35,6],[36,6],[37,6],[38,6],[6,6],[42,6],[39,6],[40,6],[41,6],[43,6],[7,6],[44,6],[49,6],[50,6],[45,6],[46,6],[47,6],[48,6],[8,6],[54,6],[51,6],[52,6],[53,6],[55,6],[9,6],[56,6],[57,6],[58,6],[60,6],[59,6],[61,6],[62,6],[10,6],[63,6],[64,6],[65,6],[11,6],[66,6],[67,6],[68,6],[69,6],[70,6],[1,6],[71,6],[72,6],[12,6],[75,6],[74,6],[73,6],[76,6],[107,385],[117,386],[106,385],[127,387],[98,388],[97,389],[126,374],[120,390],[125,391],[100,392],[114,393],[99,394],[123,395],[95,396],[94,374],[124,397],[96,398],[101,399],[102,6],[105,399],[92,6],[128,400],[118,401],[109,402],[110,403],[112,404],[108,405],[111,406],[121,374],[103,407],[104,408],[113,409],[93,410],[116,401],[115,399],[119,6],[122,411],[576,412],[581,413],[575,414],[567,415],[563,416],[559,417],[572,6],[560,177],[586,418],[583,419],[579,420],[578,421],[561,422],[585,423],[555,6],[562,424],[580,425],[593,426],[587,427],[569,428],[570,429],[568,6],[537,430],[535,431],[536,432],[524,433],[525,431],[532,434],[523,435],[528,436],[538,6],[529,437],[534,438],[540,439],[539,440],[522,441],[530,442],[531,443],[526,444],[533,430],[527,445],[501,446],[548,447],[517,448],[502,446],[500,6],[516,449],[547,6],[545,6],[518,6],[544,450],[511,451],[520,6],[519,452],[584,6],[564,6]],"affectedFilesPendingEmit":[731,732,734,735,736,737,738,739,740,724,744,745,746,747,751,754,755,761,762,763,764,765,782,779,784,785,788,787,789,790,792,791,794,795,793,799,798,802,803,773,606,776,809,806,810,811,816,819,823,821,824,599,595,609,610,611,612,613,608,614,615,616,617,618,620,619,622,621,623,624,626,627,628,629,630,631,632,633,635,636,634,638,637,639,640,641,643,642,644,645,647,646,648,649,651,652,650,653,654,655,656,657,658,659,660,663,664,665,666,667,661,668,670,669,671,672,826,827,828,673,825,833,834,835,836,837,838,832,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,830,862,831,863,864,868,869,870,871,872,873,874,875,708,715,598,716,600,601,717,602,722,723,728,603,605,729,685,684,867,829,683,675,676,686,687,689,690,693,691,692,876,677,733,730,877,753,878,743,757,879,727,760,680,756,679,681,741,759,725,750,742,682,726,674,758,752,767,781,780,808,769,813,812,783,775,804,797,820,778,796,800,805,766,777,770,768,774,807,786,801,817,771,814,815,818,772,822,749,748,678,604,694,436,695,596,597,697,696,437,698,700,699,701,625,703,705,704,706,607,662,707,594,438,582],"version":"5.7.3"} \ No newline at end of file +{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.iterator.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/dom-events.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/@types/react/canary.d.ts","./node_modules/@types/react/experimental.d.ts","./node_modules/next/dist/lib/fallback.d.ts","./node_modules/next/dist/server/base-http/index.d.ts","./node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/next/dist/server/node-environment.d.ts","./node_modules/next/dist/server/require-hook.d.ts","./node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/next/dist/lib/page-types.d.ts","./node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/server/lib/cache-control.d.ts","./node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/next/dist/server/route-kind.d.ts","./node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/next/dist/lib/constants.d.ts","./node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/next/dist/server/render-result.d.ts","./node_modules/next/dist/server/body-streams.d.ts","./node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/next/dist/lib/worker.d.ts","./node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/next/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","./node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/next/dist/build/webpack/plugins/app-build-manifest-plugin.d.ts","./node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/next/dist/trace/types.d.ts","./node_modules/next/dist/trace/trace.d.ts","./node_modules/next/dist/trace/shared.d.ts","./node_modules/next/dist/trace/index.d.ts","./node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/@next/env/dist/index.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","./node_modules/next/dist/telemetry/storage.d.ts","./node_modules/next/dist/build/build-context.d.ts","./node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/next/dist/build/webpack-config.d.ts","./node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/next/dist/build/swc/types.d.ts","./node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/next/dist/next-devtools/shared/types.d.ts","./node_modules/next/dist/server/dev/dev-indicator-server-state.d.ts","./node_modules/next/dist/server/lib/parse-stack.d.ts","./node_modules/next/dist/next-devtools/server/shared.d.ts","./node_modules/next/dist/next-devtools/shared/stack-frame.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","./node_modules/@types/react/jsx-runtime.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/shared.d.ts","./node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/next/dist/server/lib/types.d.ts","./node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/next/dist/server/base-http/node.d.ts","./node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/next/dist/server/lib/lazy-result.d.ts","./node_modules/next/dist/server/lib/implicit-tags.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/amp-context.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/next/dist/client/components/client-page.d.ts","./node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/next/dist/server/request/search-params.d.ts","./node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/next/dist/lib/framework/boundary-components.d.ts","./node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","./node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/next/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","./node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/@types/react/jsx-dev-runtime.d.ts","./node_modules/@types/react/compiler-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/next/dist/server/web/adapter.d.ts","./node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/next/dist/server/app-render/types.d.ts","./node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/next/dist/build/templates/pages.d.ts","./node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/next/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","./node_modules/next/dist/server/render.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/next/dist/server/normalizers/request/prefetch-rsc.d.ts","./node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/next/dist/server/normalizers/request/segment-prefix-rsc.d.ts","./node_modules/next/dist/build/static-paths/types.d.ts","./node_modules/next/dist/server/base-server.d.ts","./node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/next/dist/server/lib/router-utils/router-server-context.d.ts","./node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/next/dist/server/web/http.d.ts","./node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/next/dist/build/utils.d.ts","./node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/next/dist/export/routes/types.d.ts","./node_modules/next/dist/export/types.d.ts","./node_modules/next/dist/export/worker.d.ts","./node_modules/next/dist/build/worker.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/next/dist/server/after/after.d.ts","./node_modules/next/dist/server/after/after-context.d.ts","./node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/next/dist/server/request/params.d.ts","./node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/next/dist/server/web/next-url.d.ts","./node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/sharp/lib/index.d.ts","./node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/dist/types.d.ts","./node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/cli/next-test.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/emoji/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/next/dist/server/after/index.d.ts","./node_modules/next/dist/server/request/root-params.d.ts","./node_modules/next/dist/server/request/connection.d.ts","./node_modules/next/server.d.ts","./node_modules/next/dist/server/request/cookies.d.ts","./node_modules/next/dist/server/request/headers.d.ts","./node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/next/headers.d.ts","./node_modules/next/dist/client/components/redirect.d.ts","./node_modules/next/dist/client/components/not-found.d.ts","./node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.server.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/next/dist/client/components/unrecognized-action-error.d.ts","./node_modules/next/dist/client/components/navigation.d.ts","./node_modules/next/navigation.d.ts","./lib/env.ts","./lib/product/api.ts","./lib/product/project.ts","./middleware.ts","./.next/types/routes.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/shared/lib/amp.d.ts","./node_modules/next/amp.d.ts","./node_modules/next/dist/pages/_app.d.ts","./node_modules/next/app.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/next/cache.d.ts","./node_modules/next/dist/shared/lib/runtime-config.external.d.ts","./node_modules/next/config.d.ts","./node_modules/next/dist/pages/_document.d.ts","./node_modules/next/document.d.ts","./node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/next/dynamic.d.ts","./node_modules/next/dist/pages/_error.d.ts","./node_modules/next/error.d.ts","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/next/dist/client/image-component.d.ts","./node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/next/image.d.ts","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./node_modules/next/router.d.ts","./node_modules/next/dist/client/script.d.ts","./node_modules/next/script.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/types/compiled.d.ts","./node_modules/next/types.d.ts","./node_modules/next/index.d.ts","./node_modules/next/image-types/global.d.ts","./next-env.d.ts","./node_modules/@vitest/spy/optional-types.d.ts","./node_modules/@vitest/spy/dist/index.d.ts","./node_modules/tinyrainbow/dist/index.d.ts","./node_modules/@standard-schema/spec/dist/index.d.ts","./node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/@vitest/utils/dist/types.d-BCElaP-c.d.ts","./node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/@vitest/utils/dist/display.d.ts","./node_modules/@types/deep-eql/index.d.ts","./node_modules/assertion-error/index.d.ts","./node_modules/@types/chai/index.d.ts","./node_modules/@vitest/expect/dist/index.d.ts","./node_modules/vitest/node_modules/vite/types/hmrPayload.d.ts","./node_modules/vitest/node_modules/vite/dist/node/chunks/moduleRunnerTransport.d.ts","./node_modules/vitest/node_modules/vite/types/customEvent.d.ts","./node_modules/rolldown/dist/shared/logging-xuHO4mAy.d.mts","./node_modules/@oxc-project/types/types.d.ts","./node_modules/rolldown/dist/shared/binding-DZuNHVw4.d.mts","./node_modules/@rolldown/pluginutils/dist/filter/index.d.mts","./node_modules/@rolldown/pluginutils/dist/index.d.mts","./node_modules/rolldown/dist/shared/define-config-DNX0WekP.d.mts","./node_modules/rolldown/dist/index.d.mts","./node_modules/rolldown/dist/parse-ast-index.d.mts","./node_modules/vitest/node_modules/vite/types/internal/rollupTypeCompat.d.ts","./node_modules/rolldown/dist/shared/constructors-BM20b11r.d.mts","./node_modules/rolldown/dist/plugins-index.d.mts","./node_modules/rolldown/dist/shared/transform-CVse9_mH.d.mts","./node_modules/rolldown/dist/utils-index.d.mts","./node_modules/vitest/node_modules/vite/types/hot.d.ts","./node_modules/vitest/node_modules/vite/dist/node/module-runner.d.ts","./node_modules/vitest/node_modules/vite/types/internal/esbuildOptions.d.ts","./node_modules/vitest/node_modules/vite/types/metadata.d.ts","./node_modules/vitest/node_modules/vite/types/internal/terserOptions.d.ts","./node_modules/source-map-js/source-map.d.ts","./node_modules/vitest/node_modules/postcss/lib/previous-map.d.ts","./node_modules/vitest/node_modules/postcss/lib/input.d.ts","./node_modules/vitest/node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/vitest/node_modules/postcss/lib/declaration.d.ts","./node_modules/vitest/node_modules/postcss/lib/root.d.ts","./node_modules/vitest/node_modules/postcss/lib/warning.d.ts","./node_modules/vitest/node_modules/postcss/lib/lazy-result.d.ts","./node_modules/vitest/node_modules/postcss/lib/no-work-result.d.ts","./node_modules/vitest/node_modules/postcss/lib/processor.d.ts","./node_modules/vitest/node_modules/postcss/lib/result.d.ts","./node_modules/vitest/node_modules/postcss/lib/document.d.ts","./node_modules/vitest/node_modules/postcss/lib/rule.d.ts","./node_modules/vitest/node_modules/postcss/lib/node.d.ts","./node_modules/vitest/node_modules/postcss/lib/comment.d.ts","./node_modules/vitest/node_modules/postcss/lib/container.d.ts","./node_modules/vitest/node_modules/postcss/lib/at-rule.d.ts","./node_modules/vitest/node_modules/postcss/lib/list.d.ts","./node_modules/vitest/node_modules/postcss/lib/postcss.d.ts","./node_modules/vitest/node_modules/postcss/lib/postcss.d.mts","./node_modules/lightningcss/node/ast.d.ts","./node_modules/lightningcss/node/targets.d.ts","./node_modules/lightningcss/node/index.d.ts","./node_modules/vitest/node_modules/vite/types/internal/lightningcssOptions.d.ts","./node_modules/vitest/node_modules/vite/types/internal/cssPreprocessorOptions.d.ts","./node_modules/rolldown/dist/filter-index.d.mts","./node_modules/vitest/node_modules/vite/types/importGlob.d.ts","./node_modules/vitest/node_modules/vite/dist/node/index.d.ts","./node_modules/@vitest/utils/dist/types.d.ts","./node_modules/@vitest/utils/dist/helpers.d.ts","./node_modules/@vitest/utils/dist/timers.d.ts","./node_modules/@vitest/utils/dist/index.d.ts","./node_modules/@vitest/runner/dist/tasks.d-DEYaIMIu.d.ts","./node_modules/@vitest/runner/dist/index.d.ts","./node_modules/vitest/dist/chunks/traces.d.D2T_R8rx.d.ts","./node_modules/@vitest/snapshot/dist/environment.d-DOJxxZV9.d.ts","./node_modules/@vitest/snapshot/dist/rawSnapshot.d-D_X3-62x.d.ts","./node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/vitest/dist/chunks/config.d.A1h_Y6Jt.d.ts","./node_modules/vitest/dist/chunks/environment.d.CrsxCzP1.d.ts","./node_modules/vitest/dist/chunks/rpc.d.B_8sPU0w.d.ts","./node_modules/vitest/dist/chunks/worker.d.ZpHpO4yb.d.ts","./node_modules/vitest/dist/chunks/browser.d.BcoexmFG.d.ts","./node_modules/vitest/optional-types.d.ts","./node_modules/@vitest/runner/dist/utils.d.ts","./node_modules/tinybench/dist/index.d.ts","./node_modules/vitest/dist/chunks/benchmark.d.DAaHLpsq.d.ts","./node_modules/vitest/node_modules/@vitest/mocker/dist/types.d-BjI5eAwu.d.ts","./node_modules/vitest/node_modules/@vitest/mocker/dist/index.d-B41z0AuW.d.ts","./node_modules/vitest/node_modules/@vitest/mocker/dist/index.d.ts","./node_modules/@vitest/utils/dist/source-map.d.ts","./node_modules/vitest/dist/chunks/coverage.d.BZtK59WP.d.ts","./node_modules/@vitest/utils/dist/serialize.d.ts","./node_modules/@vitest/utils/dist/error.d.ts","./node_modules/vitest/dist/browser.d.ts","./node_modules/vitest/browser/context.d.ts","./node_modules/@vitest/snapshot/dist/manager.d.ts","./node_modules/vitest/dist/chunks/reporters.d.DtoKVV2s.d.ts","./node_modules/vitest/dist/chunks/plugin.d.DwFIiJ7i.d.ts","./node_modules/vitest/dist/config.d.ts","./node_modules/vitest/config.d.ts","./vitest.config.ts","./node_modules/vitest/dist/chunks/global.d.DVsSRdQ5.d.ts","./node_modules/vitest/optional-runtime-types.d.ts","./node_modules/vitest/dist/chunks/suite.d.udJtyAgw.d.ts","./node_modules/vitest/dist/chunks/evaluatedModules.d.BxJ5omdx.d.ts","./node_modules/vitest/dist/runners.d.ts","./node_modules/expect-type/dist/utils.d.ts","./node_modules/expect-type/dist/overloads.d.ts","./node_modules/expect-type/dist/branding.d.ts","./node_modules/expect-type/dist/messages.d.ts","./node_modules/expect-type/dist/index.d.ts","./node_modules/vitest/dist/index.d.ts","./lib/site.ts","./app/AGENTS.md/route.ts","./lib/marketing/blog.ts","./lib/marketing/nav.ts","./app/llms.txt/route.ts","./app/.well-known/security.txt/route.ts","./app/machine-readable.test.ts","./app/nav-complete.test.ts","./app/og-mark.ts","./app/robots.ts","./lib/docs/pages.ts","./app/sitemap.ts","./app/(product)/app/links.test.ts","./lib/product/origin.ts","./lib/product/proxy.ts","./app/api/agents/route.ts","./app/api/agents/[slug]/access/route.ts","./app/api/agents/[slug]/access/[id]/route.ts","./app/api/agents/[slug]/approve/route.ts","./app/api/agents/[slug]/boundary/route.ts","./app/api/agents/[slug]/breaker/route.ts","./app/api/agents/[slug]/control/route.ts","./app/api/agents/[slug]/enforcement/route.ts","./app/api/agents/[slug]/enforcement/preview/route.ts","./app/api/agents/[slug]/owner/route.ts","./lib/product/protection.ts","./app/api/agents/[slug]/protection/route.ts","./app/api/agents/[slug]/protection/preview/route.ts","./app/api/agents/[slug]/reject/route.ts","./app/api/agents/[slug]/rules/[ruleId]/route.ts","./app/api/agents/[slug]/tool-paths/tests/route.ts","./app/api/agents/[slug]/tools/route.ts","./app/api/alerts/slack/route.ts","./app/api/alerts/slack/test/route.ts","./app/api/approvals/[id]/approve/route.ts","./app/api/approvals/[id]/deny/route.ts","./app/api/audit/verify/route.ts","./app/api/auth/github/callback/route.ts","./app/api/auth/github/login/route.ts","./app/api/auth/logout/route.ts","./app/api/auth/logout/route.test.ts","./app/api/auth/token/route.ts","./app/api/auth/token/route.test.ts","./app/api/authorizers/route.ts","./app/api/authorizers/[key]/route.ts","./app/api/authorizers/[key]/enabled/route.ts","./app/api/authorizers/[key]/test/route.ts","./app/api/business/compile/route.ts","./app/api/business/rules/route.ts","./app/api/business/rules/[key]/route.ts","./app/api/business/rules/[key]/mode/route.ts","./app/api/compliance/compute/route.ts","./app/api/compliance/sync/route.ts","./app/api/controls/[key]/reviews/route.ts","./app/api/custom-models/route.ts","./app/api/custom-models/[key]/route.ts","./app/api/custom-models/[key]/enabled/route.ts","./app/api/custom-models/[key]/test/route.ts","./app/api/custom-rules/route.ts","./app/api/custom-rules/[key]/route.ts","./app/api/custom-rules/fields/route.ts","./app/api/custom-rules/try/route.ts","./app/api/detectors/[key]/route.ts","./app/api/detectors/[key]/pull/route.ts","./lib/product/entitlement.ts","./app/api/entitlement/grants/route.ts","./app/api/entitlement/principals/route.ts","./app/api/escalation/handoffs/acknowledge/route.ts","./app/api/escalation/policy/route.ts","./app/api/eval/online/route.ts","./app/api/eval/results/[id]/annotate/route.ts","./app/api/eval/runs/route.ts","./app/api/eval/slos/route.ts","./app/api/eval/suites/route.ts","./app/api/eval/suites/[key]/cases/route.ts","./app/api/eval/suites/[key]/promote/route.ts","./app/api/evidence/route.ts","./app/api/evidence/[id]/download/route.ts","./app/api/findings/[id]/route.ts","./app/api/findings/[id]/remedies/[key]/route.ts","./app/api/guardrails/feedback/route.ts","./app/api/guardrails/suppressions/route.ts","./app/api/guardrails/suppressions/revoke/route.ts","./app/api/import/[tool]/files.ts","./app/api/import/[tool]/files.test.ts","./app/api/import/[tool]/route.ts","./app/api/import/[tool]/plan/route.ts","./app/api/integrations/github/scan/route.ts","./app/api/integrations/hosted-api/scan/route.ts","./app/api/judgment/posture/route.ts","./app/api/judgment/posture/route.test.ts","./app/api/legal-holds/route.ts","./app/api/library/packs/[...id]/route.ts","./app/api/monitors/[id]/[action]/route.ts","./app/api/onboarding/route.ts","./app/api/policies/[key]/approve/route.ts","./app/api/policies/[key]/canary/route.ts","./app/api/policies/[key]/canary/advance/route.ts","./app/api/policies/[key]/canary/rollback/route.ts","./app/api/policies/[key]/canary/start/route.ts","./app/api/policies/[key]/mode/route.ts","./app/api/policies/[key]/reject/route.ts","./app/api/policies/[key]/rules/[ruleId]/route.ts","./app/api/policies/[key]/rules/[ruleId]/scope/route.ts","./app/api/policies/[key]/save/route.ts","./app/api/policies/simulate/route.ts","./app/api/policies/simulate-persist/route.ts","./app/api/policies/validate/route.ts","./app/api/proposals/[id]/[action]/route.ts","./app/api/redteam/campaigns/route.ts","./app/api/retention/[dataClass]/route.ts","./app/api/retention/purge/route.ts","./app/api/risk/assessments/[slug]/route.ts","./app/api/rules/[ruleId]/examples/route.ts","./app/api/rules/[ruleId]/examples/[id]/route.ts","./app/api/sources/route.ts","./lib/product/sourceConnection.ts","./app/api/sources/add/route.ts","./app/api/sources/connections/route.ts","./app/api/sources/context-check/route.ts","./app/api/sources/delete/route.ts","./app/api/sources/deprecate/route.ts","./app/api/sources/validate/route.ts","./app/api/start/agent/route.ts","./app/api/start/test/route.ts","./app/api/test/try/route.ts","./app/api/tokens/route.ts","./app/api/tokens/[id]/revoke/route.ts","./app/api/traces/[id]/route.ts","./app/api/validators/route.ts","./app/api/waitlist/route.ts","./app/api/welcome/accept/[id]/route.ts","./app/api/welcome/scan/route.ts","./app/api/workspace/apply/route.ts","./app/api/workspace/plan/route.ts","./app/blog/feed.xml/route.ts","./components/marketing/shot.tsx","./components/marketing/blog/blocks.tsx","./components/marketing/blog/card.tsx","./components/marketing/brand.tsx","./components/ui/ThemeToggle.tsx","./components/marketing/motion.tsx","./components/marketing/menu.tsx","./components/marketing/nav.tsx","./components/marketing/sections.tsx","./components/marketing/blog/article.tsx","./components/docs/copy.tsx","./components/docs/blocks.tsx","./components/marketing/blog/posts/ai-agent-audit-trail.tsx","./components/marketing/blog/posts/claude-code-hooks-security.tsx","./lib/generated/coverage.json","./components/marketing/blog/posts/continuous-red-teaming-ai-agents.tsx","./components/marketing/blog/posts/hidden-indirect-prompt-injection.tsx","./components/marketing/blog/posts/mcp-rug-pull-tool-poisoning.tsx","./components/marketing/blog/posts/prompt-injection-containment-not-detection.tsx","./components/marketing/blog/posts/index.ts","./components/product/start/snippets.ts","./lib/env.test.ts","./lib/marketing/blog.test.ts","./lib/marketing/showcase.ts","./lib/marketing/showcase.test.ts","./lib/product/api.test.ts","./lib/product/approvals.ts","./lib/product/approvals.test.ts","./lib/product/controls.ts","./lib/product/countdown.ts","./lib/product/countdown.test.ts","./lib/product/coverage.ts","./lib/product/coverage.test.ts","./lib/product/edition.ts","./lib/product/entitlement.test.ts","./lib/generated/reference/finding-types.json","./lib/product/findingTypes.test.ts","./lib/product/guardrailMap.ts","./lib/product/guardrailMap.test.ts","./lib/product/legacyRedirect.ts","./lib/product/legacyRedirect.test.ts","./lib/product/nav.ts","./components/ui/InfoTip.tsx","./lib/product/vocab.ts","./components/kit/index.tsx","./lib/product/observe.ts","./lib/product/origin.test.ts","./lib/product/project.test.ts","./lib/product/proxy.test.ts","./lib/product/range.ts","./lib/product/rules.ts","./lib/product/sourceOptions.ts","./lib/product/useProjectScope.ts","./lib/product/vocab.test.ts","./lib/product/welcome.ts","./lib/product/welcome.test.ts","./app/error.tsx","./node_modules/next/dist/compiled/@next/font/dist/types.d.ts","./node_modules/geist/font.d.ts","./node_modules/geist/dist/mono.d.ts","./node_modules/geist/dist/sans.d.ts","./node_modules/next/dist/compiled/@next/font/dist/google/index.d.ts","./node_modules/next/font/google/index.d.ts","./app/layout.tsx","./app/loading.tsx","./app/not-found.tsx","./node_modules/next/dist/compiled/@vercel/og/index.edge.d.ts","./node_modules/next/dist/compiled/@vercel/og/index.node.d.ts","./node_modules/next/dist/server/og/image-response.d.ts","./node_modules/next/og.d.ts","./app/og.tsx","./app/opengraph-image.tsx","./app/(marketing)/how-it-works/_public.tsx","./components/marketing/platform.tsx","./components/marketing/sequence.tsx","./components/marketing/home.tsx","./app/page.tsx","./app/twitter-image.tsx","./components/marketing/charts.tsx","./app/(marketing)/benchmark/page.tsx","./app/(marketing)/compare/page.tsx","./components/marketing/capability.tsx","./app/(marketing)/control-points/page.tsx","./app/(marketing)/coverage/page.tsx","./app/(marketing)/discovery/page.tsx","./app/(marketing)/evidence/page.tsx","./app/(marketing)/frameworks/page.tsx","./app/(marketing)/grants/page.tsx","./app/(marketing)/hooks/page.tsx","./components/marketing/path.tsx","./components/marketing/product.tsx","./components/marketing/follow.tsx","./app/(marketing)/how-it-works/page.tsx","./app/(marketing)/legal/page.tsx","./app/(marketing)/live/page.tsx","./app/(marketing)/mcp/page.tsx","./components/ui/index.tsx","./components/marketing/Playground.tsx","./app/(marketing)/playground/page.tsx","./components/marketing/waitlist.tsx","./components/marketing/editions.tsx","./app/(marketing)/pricing/page.tsx","./app/(marketing)/privacy/page.tsx","./components/marketing/mocks.tsx","./components/marketing/grant.tsx","./components/marketing/visuals.tsx","./components/marketing/pillars.tsx","./components/marketing/index-grid.tsx","./app/(marketing)/product/page.tsx","./app/(marketing)/runtime/page.tsx","./app/(marketing)/security/page.tsx","./app/(marketing)/support/page.tsx","./app/(marketing)/terms/page.tsx","./components/product/Logo.tsx","./components/product/AccountMenu.tsx","./components/product/NotificationsBell.tsx","./components/product/CommandSearch.tsx","./components/product/NavIcons.tsx","./components/product/ProjectSwitcher.tsx","./components/product/SideNav.tsx","./components/product/TopbarStats.tsx","./app/(product)/app/layout.tsx","./app/(product)/app/loading.tsx","./components/product/Countdown.tsx","./components/product/home/SetupProgress.tsx","./app/(product)/app/page.tsx","./components/product/PageHeader.tsx","./components/product/approvals/escalation.tsx","./app/(product)/app/approvals/page.tsx","./app/(product)/app/approvals/[id]/page.tsx","./app/(product)/app/board/page.tsx","./components/product/AreaTabs.tsx","./components/product/Retention.tsx","./ee/compliance.tsx","./components/product/EnterpriseOnly.tsx","./app/(product)/app/compliance/page.tsx","./app/(product)/app/compliance/controls/[key]/page.tsx","./app/(product)/app/compliance/frameworks/[key]/page.tsx","./app/(product)/app/coverage/page.tsx","./components/product/Modal.tsx","./app/(product)/app/entitlement/page.tsx","./app/(product)/app/escalation/page.tsx","./app/(product)/app/escalation/conversations/[sessionId]/page.tsx","./components/product/Explainer.tsx","./app/(product)/app/evals/page.tsx","./app/(product)/app/evals/[key]/page.tsx","./app/(product)/app/evals/[key]/runs/[runId]/page.tsx","./components/kit/RangeContext.tsx","./components/kit/FilterBar.tsx","./app/(product)/app/findings/page.tsx","./components/product/FindingEvidence.tsx","./components/kit/Act.tsx","./components/product/FindingActions.tsx","./app/(product)/app/findings/[id]/page.tsx","./components/product/GlossaryFilter.tsx","./components/product/ProductMap.tsx","./app/(product)/app/glossary/page.tsx","./app/(product)/app/guardrails/page.tsx","./app/(product)/app/observe/page.tsx","./components/product/policies/ChecksControls.tsx","./ee/AccessChecks.tsx","./ee/AccessChecksCard.tsx","./components/product/policies/Checks.tsx","./components/product/policies/ImportPolicyFormat.tsx","./components/product/policies/ImportGuard.tsx","./components/product/policies/LibraryPacks.tsx","./components/product/policies/PackMode.tsx","./components/product/policies/WorkspaceCode.tsx","./components/product/DetectorCatalogue.tsx","./components/product/policies/advanced.tsx","./app/(product)/app/policies/page.tsx","./components/product/PolicyEditor.tsx","./components/product/CanaryPanel.tsx","./app/(product)/app/policies/[key]/page.tsx","./components/product/policies/LadderEditor.tsx","./app/(product)/app/policies/business/[key]/page.tsx","./components/product/policies/ConditionFields.tsx","./components/product/policies/ValidatorFields.tsx","./components/product/policies/CustomRule.tsx","./components/product/policies/DescribeRule.tsx","./app/(product)/app/policies/new/page.tsx","./components/kit/RunsTable.tsx","./components/product/policies/RuleTests.tsx","./components/product/policies/RuleTuner.tsx","./app/(product)/app/policies/rules/[ruleId]/page.tsx","./app/(product)/app/projects/page.tsx","./components/product/agent/AccessEditor.tsx","./node_modules/@xyflow/system/dist/esm/types/changes.d.ts","./node_modules/@types/d3-selection/index.d.ts","./node_modules/@types/d3-drag/index.d.ts","./node_modules/@types/d3-color/index.d.ts","./node_modules/@types/d3-interpolate/index.d.ts","./node_modules/@types/d3-zoom/index.d.ts","./node_modules/@xyflow/system/dist/esm/types/utils.d.ts","./node_modules/@xyflow/system/dist/esm/utils/types.d.ts","./node_modules/@xyflow/system/dist/esm/types/nodes.d.ts","./node_modules/@xyflow/system/dist/esm/types/handles.d.ts","./node_modules/@xyflow/system/dist/esm/types/panzoom.d.ts","./node_modules/@xyflow/system/dist/esm/types/general.d.ts","./node_modules/@xyflow/system/dist/esm/types/edges.d.ts","./node_modules/@xyflow/system/dist/esm/types/index.d.ts","./node_modules/@xyflow/system/dist/esm/constants.d.ts","./node_modules/@xyflow/system/dist/esm/utils/attribution.d.ts","./node_modules/@xyflow/system/dist/esm/utils/connections.d.ts","./node_modules/@xyflow/system/dist/esm/utils/dom.d.ts","./node_modules/@xyflow/system/dist/esm/utils/edges/bezier-edge.d.ts","./node_modules/@xyflow/system/dist/esm/utils/edges/straight-edge.d.ts","./node_modules/@xyflow/system/dist/esm/utils/edges/smoothstep-edge.d.ts","./node_modules/@xyflow/system/dist/esm/utils/edges/general.d.ts","./node_modules/@xyflow/system/dist/esm/utils/edges/positions.d.ts","./node_modules/@xyflow/system/dist/esm/utils/edges/index.d.ts","./node_modules/@xyflow/system/dist/esm/utils/graph.d.ts","./node_modules/@xyflow/system/dist/esm/utils/general.d.ts","./node_modules/@xyflow/system/dist/esm/utils/marker.d.ts","./node_modules/@xyflow/system/dist/esm/utils/node-toolbar.d.ts","./node_modules/@xyflow/system/dist/esm/utils/edge-toolbar.d.ts","./node_modules/@xyflow/system/dist/esm/utils/store.d.ts","./node_modules/@xyflow/system/dist/esm/utils/shallow-node-data.d.ts","./node_modules/@xyflow/system/dist/esm/utils/index.d.ts","./node_modules/@xyflow/system/dist/esm/xydrag/XYDrag.d.ts","./node_modules/@xyflow/system/dist/esm/xydrag/index.d.ts","./node_modules/@xyflow/system/dist/esm/xyhandle/types.d.ts","./node_modules/@xyflow/system/dist/esm/xyhandle/XYHandle.d.ts","./node_modules/@xyflow/system/dist/esm/xyhandle/index.d.ts","./node_modules/@xyflow/system/dist/esm/xyminimap/index.d.ts","./node_modules/@xyflow/system/dist/esm/xypanzoom/XYPanZoom.d.ts","./node_modules/@xyflow/system/dist/esm/xypanzoom/index.d.ts","./node_modules/@xyflow/system/dist/esm/xyresizer/types.d.ts","./node_modules/@xyflow/system/dist/esm/xyresizer/XYResizer.d.ts","./node_modules/@xyflow/system/dist/esm/xyresizer/index.d.ts","./node_modules/@xyflow/system/dist/esm/index.d.ts","./node_modules/@xyflow/react/dist/esm/types/general.d.ts","./node_modules/@xyflow/react/dist/esm/types/nodes.d.ts","./node_modules/@xyflow/react/dist/esm/types/edges.d.ts","./node_modules/@xyflow/react/dist/esm/types/component-props.d.ts","./node_modules/@xyflow/react/dist/esm/types/store.d.ts","./node_modules/@xyflow/react/dist/esm/types/instance.d.ts","./node_modules/@xyflow/react/dist/esm/types/index.d.ts","./node_modules/@xyflow/react/dist/esm/container/ReactFlow/index.d.ts","./node_modules/@xyflow/react/dist/esm/components/Handle/index.d.ts","./node_modules/@xyflow/react/dist/esm/components/Edges/EdgeText.d.ts","./node_modules/@xyflow/react/dist/esm/components/Edges/StraightEdge.d.ts","./node_modules/@xyflow/react/dist/esm/components/Edges/StepEdge.d.ts","./node_modules/@xyflow/react/dist/esm/components/Edges/BezierEdge.d.ts","./node_modules/@xyflow/react/dist/esm/components/Edges/SimpleBezierEdge.d.ts","./node_modules/@xyflow/react/dist/esm/components/Edges/SmoothStepEdge.d.ts","./node_modules/@xyflow/react/dist/esm/components/Edges/BaseEdge.d.ts","./node_modules/@xyflow/react/dist/esm/components/ReactFlowProvider/index.d.ts","./node_modules/@xyflow/react/dist/esm/components/Panel/index.d.ts","./node_modules/@xyflow/react/dist/esm/components/EdgeLabelRenderer/index.d.ts","./node_modules/@xyflow/react/dist/esm/components/ViewportPortal/index.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useReactFlow.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useUpdateNodeInternals.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useNodes.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useEdges.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useViewport.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useKeyPress.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useNodesEdgesState.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useStore.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useOnViewportChange.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useOnSelectionChange.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useNodesInitialized.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useHandleConnections.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useNodeConnections.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useNodesData.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useConnection.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useInternalNode.d.ts","./node_modules/@xyflow/react/dist/esm/contexts/NodeIdContext.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useOnNodesChangeMiddleware.d.ts","./node_modules/@xyflow/react/dist/esm/hooks/useOnEdgesChangeMiddleware.d.ts","./node_modules/@xyflow/react/dist/esm/utils/changes.d.ts","./node_modules/@xyflow/react/dist/esm/utils/general.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/Background/types.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/Background/Background.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/Background/index.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/Controls/types.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/Controls/Controls.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/Controls/ControlButton.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/Controls/index.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/MiniMap/types.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/MiniMap/MiniMap.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/MiniMap/MiniMapNode.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/MiniMap/index.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/NodeResizer/types.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/NodeResizer/NodeResizer.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/NodeResizer/NodeResizeControl.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/NodeResizer/index.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/NodeToolbar/types.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/NodeToolbar/NodeToolbar.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/NodeToolbar/index.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/EdgeToolbar/types.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/EdgeToolbar/EdgeToolbar.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/EdgeToolbar/index.d.ts","./node_modules/@xyflow/react/dist/esm/additional-components/index.d.ts","./node_modules/@xyflow/react/dist/esm/utils/edges.d.ts","./node_modules/@xyflow/react/dist/esm/index.d.ts","./components/product/agent/GuardrailMap.tsx","./components/product/agent/GuardrailMapLazy.tsx","./app/(product)/app/projects/[slug]/page.tsx","./components/product/test/TryIt.tsx","./components/product/agent/ProtectWizard.tsx","./app/(product)/app/projects/[slug]/protect/page.tsx","./components/product/PrintButton.tsx","./app/(product)/app/reports/page.tsx","./ee/SlackForm.tsx","./components/product/start/CodeSnippet.tsx","./app/(product)/app/settings/page.tsx","./app/(product)/app/settings/integrations/page.tsx","./app/(product)/app/settings/tokens/page.tsx","./components/product/ContextCheck.tsx","./components/product/ConnectionFields.tsx","./components/product/SourceAddFlow.tsx","./components/product/SourceRowActions.tsx","./app/(product)/app/sources/page.tsx","./components/product/RepoTable.tsx","./components/product/start/EnforceAgent.tsx","./components/product/TokenManager.tsx","./components/product/start/ConnCard.tsx","./components/product/start/FirstRequestWatcher.tsx","./components/product/start/StartFlow.tsx","./app/(product)/app/start/page.tsx","./components/product/test/AddPathToTests.tsx","./app/(product)/app/test/page.tsx","./app/(product)/app/traces/page.tsx","./app/(product)/app/traces/[id]/page.tsx","./components/product/welcome/WelcomeFlow.tsx","./app/(product)/app/welcome/page.tsx","./app/(product)/login/page.tsx","./app/blog/page.tsx","./app/blog/[slug]/opengraph-image.tsx","./app/blog/[slug]/page.tsx","./app/blog/[slug]/twitter-image.tsx","./components/docs/sidebar.tsx","./app/docs/layout.tsx","./app/docs/page.tsx","./app/docs/app/page.tsx","./app/docs/app/access-and-sources/page.tsx","./app/docs/app/approvals/page.tsx","./app/docs/app/compliance/page.tsx","./app/docs/app/evals/page.tsx","./app/docs/app/findings/page.tsx","./app/docs/app/playground/page.tsx","./app/docs/app/policies/page.tsx","./app/docs/app/projects/page.tsx","./app/docs/app/start/page.tsx","./app/docs/app/traces/page.tsx","./app/docs/benchmarks/page.tsx","./app/docs/concepts/page.tsx","./app/docs/guides/approvals/page.tsx","./app/docs/guides/audit-evidence/page.tsx","./app/docs/guides/business-rules/page.tsx","./app/docs/guides/codex/page.tsx","./app/docs/guides/coding-agents/page.tsx","./app/docs/guides/contain-tool-calls/page.tsx","./app/docs/guides/gateway/page.tsx","./app/docs/guides/import-policies/page.tsx","./app/docs/guides/langgraph/page.tsx","./app/docs/guides/live-probes/page.tsx","./app/docs/guides/mcp/page.tsx","./app/docs/guides/migrate-from-llm-guard/page.tsx","./app/docs/guides/monitoring/page.tsx","./app/docs/guides/observability/page.tsx","./app/docs/guides/owasp-agentic-top-10/page.tsx","./app/docs/guides/python-auto/page.tsx","./app/docs/guides/rag/page.tsx","./app/docs/guides/red-team-and-evals/page.tsx","./app/docs/guides/scan-a-repo/page.tsx","./app/docs/guides/tuning/page.tsx","./app/docs/install/page.tsx","./app/docs/limits/page.tsx","./app/docs/plugin/page.tsx","./app/docs/quickstart/page.tsx","./lib/generated/reference/api.json","./lib/generated/reference/cli.json","./components/docs/reference.tsx","./app/docs/reference/api/page.tsx","./app/docs/reference/cli/page.tsx","./app/docs/reference/config/page.tsx","./app/docs/reference/detectors/page.tsx","./app/docs/reference/policies/page.tsx","./app/docs/reference/python/page.tsx","./app/docs/self-host/page.tsx","./app/docs/support/page.tsx","./components/marketing/boundary.tsx","./components/marketing/decisions.tsx","./components/marketing/explain.tsx","./components/marketing/hero.tsx","./components/product/AgentMap.tsx","./components/product/Coverage.tsx","./components/product/WhyBlocked.tsx","./.next/types/cache-life.d.ts","./.next/types/validator.ts","./.next/types/app/layout.ts","./.next/types/app/page.ts","./.next/types/app/(product)/app/layout.ts","./.next/types/app/(product)/app/page.ts","./.next/types/app/(product)/app/approvals/page.ts","./.next/types/app/(product)/app/findings/page.ts","./.next/types/app/(product)/app/policies/page.ts","./.next/types/app/(product)/app/projects/page.ts","./.next/types/app/(product)/app/projects/[slug]/page.ts","./.next/types/app/(product)/login/page.ts","./node_modules/@types/d3-transition/index.d.ts","./node_modules/@types/estree/index.d.ts"],"fileIdsList":[[91,133,319,860],[91,133,319,881],[91,133,319,853],[91,133,319,857],[91,133,319,902],[91,133,319,1030],[91,133,319,917],[91,133,319,1059],[91,133,319,795],[91,133,319,808],[91,133,460,461,462,463],[91,133],[91,133,431,450,486,597,600,601,611,612,613,614,615,616,617,618,619,620,622,623,624,625,626,627,628,629,630,631,632,633,634,635,637,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,681,682,683,684,685,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,795,808,811,812,814,815,816,817,818,819,820,824,825,826,827,830,833,834,840,841,842,843,844,853,857,860,861,862,867,868,869,870,872,873,874,876,877,878,881,885,888,889,890,902,905,907,912,916,917,1030,1033,1035,1038,1039,1040,1045,1052,1054,1055,1056,1058,1059,1060,1062,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1107,1108,1109,1110,1111,1112,1113,1114],[91,133,480,487,596,739,740,810],[91,133,186,480,487,596,739,740],[91,133,480,487,596,813],[91,133,487,596,739,740,746],[91,133,487,596,813],[91,133,480,487,596,739,740,821,822,823],[91,133,480,487,596,739,740],[91,133,186,480,487,596,739,740,755],[91,133,446,487,596,739,740,829],[91,133,480,487,596,739,740,832],[91,133,480,487,596,739,740,838,839],[91,133,447,480,487,596,758,776,828,855],[91,133,447,480,487,596,758,775,776,855,859],[91,133,487,596,771],[91,133,447,480,487,596,765,775,776,828,865,866],[91,133,447,480,487,596,765,776,828,865,866],[91,133,445,447,480,487,596,765,776,828,863,865,866],[91,133,447,480,487,596,776,828,863],[91,133,447,480,487,596,776,828,863,871],[91,133,447,480,487,596,776,828],[91,133,447,480,487,596,776,828,871],[91,133,447,480,487,596,775,776,828,875],[91,133,447,480,487,596,775,776,828,882,884],[91,133,447,480,487,596,775,776,828,880],[91,133,487,596,776,886,887],[91,133,186,435,447,448,480,736,765,773,845,846,847,848,851,852],[91,133,146,155,595],[91,133,447,480,487,596,775,776,777,781,879,880],[91,133,447,480,487,596,758,775,776,777,781,855,856],[91,133,447,487,596,776,828,903,904],[91,133,447,487,596,776,883,906],[91,133,447,480,487,596,775,776,896,897,910,911],[91,133,447,480,487,596,775,776,777,781,782,879,880,883,894,896,897,898,899,901],[91,133,447,480,487,596,775,776,777,781,782,879,880,910,913,914,915],[91,133,447,448,480,487,596,775,776,777,781,828,879,880,883,913,918,1029],[91,133,447,487,596,776,1032],[91,133,447,480,487,596,775,776,781,828,871],[91,133,447,480,487,596,765,776,863,866,1034],[91,133,445,446,447,487,596,765,776,828,863,864,866,883,1036,1037],[91,133,447,480,487,596,776,783,828,863,871,1041,1043,1044],[91,133,186,445,446,447,480,487,596,776,828,863,1037,1046,1047,1048,1049,1050,1051],[91,133,446,447,480,487,596,775,776,1031,1037,1053],[91,133,447,480,487,596,775,776,828],[91,133,447,480,487,596,775,776,777,781,828,879,880,913],[91,133,435,445,446,447,480,487,596,776,786,828,1037,1057],[91,133,435,445,447,480,487,596,845],[91,133,596],[91,133,610],[91,133,431,610],[91,133,431,609,610],[91,133,431,610,621],[91,133,431,446,447],[91,133,431],[91,133,431,595,635],[91,133,431,446,447,609],[91,133,431,595,637],[91,133,431,610,660],[91,133,431,435,446,447],[91,133,595,679],[91,133,431,610,679],[91,133,431,595,685],[91,133,431,435,446,447,609,712],[91,133,431,610,712],[91,133,598,802],[91,133,445,487,596,598,741,751],[91,133,596,598],[91,133,480,487,596,598,734,739,740],[91,133,480,487,596,743],[91,133,186,480,487,596,743],[91,133,186,739,740,1064],[91,133,480,487,596,606,743],[91,133,480,487,596,743,1106],[91,133,186,480],[91,133,487,596,790,791,792,794],[91,133,596,598,599],[91,133,445],[91,133,595,596,597,598,599,600,601],[91,133,146,155,595,599],[91,133,480],[91,133,604,801],[91,133,802],[91,133,487,596,739,740,804,805,807],[91,133,487,596],[91,133,487,596,598,599,606],[91,133,186,480,742],[91,133,186],[91,133,1104,1105],[91,133,445,480,606],[91,133,186,445],[91,133,186,445,775,879],[91,133,480,775,776],[91,133,186,480,774,775],[91,133,186,828],[91,133,186,478,480,596,598,733,734,739,740],[91,133,186,480,732],[91,133,478,480,598],[91,133,480,733,741,743],[91,133,480,733,741,743,746],[91,133,186,598,733,741,744,745,747,748,749,750],[91,133,186,480,599,739,740],[91,133,186,480,831],[91,133,186,835],[91,133,480,739,1116],[91,133,186,480,732,739,806],[91,133,480,599],[91,133,186,480,599],[91,133,435,447,480,599,735,736,737,738],[91,133,186,480,835,836,837],[91,133,480,599,735],[91,133,478],[91,133,765,776],[91,133,783],[91,133,186,761],[91,133,480,763,828],[91,133,480,765],[91,133,186,871],[91,133,186,445,480,883],[91,133,480,828],[91,133,186,445,448,480],[91,133,776,828],[91,133,445,480,773,784,849,850],[91,133,186,783,1042],[91,133,186,855],[91,133,186,445,775,883],[91,133,186,445,480,769,775,776,1027],[91,133,186,470,1028],[91,133,186,445,480,883,1031],[91,133,447,480,776,828,855,858],[91,133,447,480,765,775,776,866,883,891,893],[91,133,186,445,883],[91,133,186,883],[91,133,186,445,480,883,908,909],[91,133,186,445,775,883,895],[91,133,447,776,883],[91,133,186,445,775,782,883],[91,133,186,447,480,776,828,855,900],[91,133,186,445,480],[91,133,186,445,480,752,883,1037],[91,133,186,480,775,883],[91,133,186,480,786,883,1037,1050],[91,133,446,480,767,774],[91,133,776,883,892],[91,133,776,864],[91,133,446,595],[91,133,146,155,595,598,599,606,607],[91,133,595,755],[91,133,446],[91,133,447,595],[91,133,435,445,446],[91,133,595,758],[91,133,447],[91,133,595,761],[91,133,595,763],[91,133,186,447],[91,133,595,660],[91,133,595,767],[91,133,595,769],[91,133,595,771],[91,133,448],[91,133,775,776],[91,133,431,595,609],[91,133,448,595,773],[91,133,431,595,610],[91,133,431,435,446,447,609],[91,133,447,775],[91,133,186,445,448],[91,133,595,775],[91,133,595,786],[91,133,431,447,448],[91,133,450,487,488],[91,133,508],[91,133,498,499],[91,133,920,1134],[91,133,922],[91,133,920,923,1134],[91,130,133],[91,132,133],[133],[91,133,138,168],[91,133,134,139,145,146,153,165,176],[91,133,134,135,145,153],[86,87,88,91,133],[91,133,136,177],[91,133,137,138,146,154],[91,133,138,165,173],[91,133,139,141,145,153],[91,132,133,140],[91,133,141,142],[91,133,145],[91,133,143,145],[91,132,133,145],[91,133,145,146,147,165,176],[91,133,145,146,147,160,165,168],[91,128,133,181],[91,128,133,141,145,148,153,165,176],[91,133,145,146,148,149,153,165,173,176],[91,133,148,150,165,173,176],[89,90,91,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182],[91,133,145,151],[91,133,152,176,181],[91,133,141,145,153,165],[91,133,154],[91,133,155],[91,132,133,156],[91,130,131,132,133,134,135,136,137,138,139,140,141,142,143,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182],[91,133,158],[91,133,159],[91,133,145,160,161],[91,133,160,162,177,179],[91,133,145,165,166,167,168],[91,133,165,167],[91,133,165,166],[91,133,168],[91,133,169],[91,130,133,165],[91,133,145,171,172],[91,133,171,172],[91,133,138,153,165,173],[91,133,174],[91,133,153,175],[91,133,148,159,176],[91,133,138,177],[91,133,165,178],[91,133,152,179],[91,133,180],[91,133,138,145,147,156,165,176,179,181],[91,133,165,182],[91,133,186,188,415,427,454],[91,133,186,187,415,427,454],[91,133,184,185],[91,133,491,492,493,496,497,500],[91,133,496,554,555],[91,133,496,554],[91,133,554],[91,133,494,554,558,559],[91,133,494,554,558],[91,133,490],[91,133,494,495],[91,133,494],[91,133,494,495,551,575],[91,133,551],[91,133,494,497,551,552,553],[91,133,186,253,1004],[91,133,1004,1005],[91,133,253,1007],[91,133,186,253,1007],[91,133,1007,1008,1009],[91,133,186,962,969],[91,133,253,1022],[91,133,1022,1023],[91,133,186,962],[91,133,253,969,1011],[91,133,186,253,1011],[91,133,1011,1012,1013],[91,133,186,253,1015],[91,133,253,1015],[91,133,1015,1016,1017],[91,133,253,1019],[91,133,1019,1020],[91,133,1006,1010,1014,1018,1021,1024],[91,133,253,969],[91,133,186,253,969],[91,133,186,253,962,969],[91,133,186,969],[91,133,962,969],[91,133,969],[91,133,962],[91,133,962,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001,1002,1003,1025,1026],[91,133,963,964,965,966,967,968],[91,133,186,962,963],[91,133,932],[91,133,932,933,950,952,955,956,958,961],[91,133,925],[91,133,920,921,924,925,927,928,929,962,1134],[91,133,919,925,927,928,929,930,931],[91,133,926,932],[91,133,924,932],[91,133,930,962],[91,133,937,938,939,940,941],[91,133,925,927,930,931,932],[91,133,932,933],[91,133,926,934,935,936,942,943,944,945,946,947,948,949],[91,133,926,932,962],[91,133,951],[91,133,953],[91,133,954],[91,133,920,932,1134],[91,133,957],[91,133,932,959],[91,133,959,960],[91,133,921],[91,133,590,591],[91,133,590,591,592,593],[91,133,590,592],[91,133,590],[91,133,790],[91,133,789,791,792],[91,133,543,544],[91,133,456],[91,133,458],[91,133,465],[82,91,133,201,202,203,205,419],[82,91,133,191,234,236,238,239,242,419],[82,91,133,191,207,213,214,218,219,220,390,419],[91,133,419],[91,133,202,302,371,380,397],[82,91,133],[91,133,189,397],[91,133,245],[91,133,191,244,419],[91,133,148,282,302,331,485],[91,133,148,295,311,380,396],[91,133,148,346],[91,133,384],[91,133,383,384,385],[91,133,383],[82,85,91,133,148,189,198,199,202,206,207,213,221,222,223,339,362,381,415,419],[82,91,133,204,230,234,235,240,241,419,485],[91,133,204,485],[91,133,223,230,280,419,485],[91,133,485],[82,91,133,204,205,485],[91,133,237,485],[91,133,199,382,389],[91,133,159,253,397],[91,133,253,397],[91,133,186,253],[91,133,186,303],[91,133,299,344,397,442,443],[91,133,377,436,437,438,439,441],[91,133,376],[91,133,376,377],[91,133,214,340,341,342],[91,133,340,343,344],[91,133,440],[91,133,340,344],[83,91,133,186,475],[91,133,176,186],[91,133,186,204,270],[91,133,186,204],[91,133,268,272],[91,133,186,269,417],[91,133,789],[91,133,186,427],[91,133,165,183,186,427,798],[91,133,148,183,186,187,188,415,425,426,454],[91,133,148],[91,133,148,190,207,212,340,350,363,371,386,387,419,485],[91,133,198,388],[91,133,415],[81,91,133],[91,133,186,282,298,310,320,322,396],[91,133,159,282,298,319,320,321,396,484],[91,133,313,314,315,316,317,318],[91,133,315],[91,133,319],[91,133,251,252,253,255],[91,133,186,246,247,248,254],[91,133,251,254],[91,133,249],[91,133,250],[91,133,186,253,269,417],[91,133,186,253,416,417],[91,133,186,253,417],[91,133,363,393],[91,133,393],[91,133,148,190,417],[91,133,307],[91,132,133,306],[91,133,190,215,224,290,292,294,295,296,297,337,340,396,399],[91,133,224,328,340,344],[91,133,295,396],[91,133,186,295,304,305,307,308,309,310,311,312,323,324,325,326,327,329,330,396,397,485],[91,133,289],[91,133,148,159,190,191,212,224,225,264,296,337,338,339,344,363,371,392,415,419,485],[91,133,396],[91,132,133,190,202,293,296,339,392,394,395],[91,133,295],[91,132,133,212,284,285,286,287,288,289,290,291,292,294,396,397],[91,133,148,190,191,284,285,420],[91,133,190,202,339,340,363,392,396],[91,133,148,191,419],[91,133,148,165,190,191,399],[79,91,133,148,159,176,189,190,191,204,207,215,224,225,227,262,264,292,296,340,350,351,353,355,358,359,360,361,362,371,391,392,397,399,400,419],[91,133,148,165],[82,83,84,91,133,204,207,219,221,230,391,399,415,417,418,485],[91,133,148,165,176,242,243,245,246,247,248,255,485],[91,133,159,176,189,234,243,259,262,263,292,340,351,362,363,371,392,397,399,404,405,411,412],[91,133,198,199,221,339,362,392,419],[83,91,133,148,176,207,292,399,409,419],[91,133,281],[91,133,148,257,258,368],[91,133,399,419],[91,133,290,293],[91,133,292,296,391,417],[91,133,148,159,228,234,259,262,263,363,399,411,414],[91,133,148,198,199,234,364],[82,91,133,227,366,391,419],[91,133,148,176,419],[91,133,148,204,226,227,228,239,256,365,367,391,419],[85,91,133,224,296,370,415,417],[79,91,133,148,159,176,198,199,206,207,215,225,263,264,292,340,351,353,363,371,391,392,397,398,399,404,405,406,408,410,417],[91,133,148,165,199,368,399,411,413],[91,133,193,194,195,196,197],[91,133,354,400],[91,133,356],[91,133,354],[91,133,356,357],[91,133,799],[91,133,148,190,207,212,213,214],[81,83,91,133,148,159,191,215,224,264,296,348,349,371,399,415,417],[91,133,148,159,176,190,214,216,292,349,398],[91,133,285],[91,133,286],[91,133,287],[91,133,397],[91,133,209,210],[91,133,148,207,209,215],[91,133,208,210],[91,133,211],[91,133,209,243],[91,133,209,265],[91,133,209],[91,133,261,398,400],[91,133,260],[91,133,243,397,398],[91,133,352,398],[91,133,243,397],[91,133,337],[91,133,190,207,215,219,279,282,290,292,296,298,301,332,335,336,340,370,391,399],[91,133,273,276,277,278,299,300,344],[91,133,186,253,333,334],[91,133,379],[91,133,202,295,296,307,311,340,370,372,373,374,375,377,378,381,391,396,419,420],[91,133,344],[91,133,348],[91,133,148,215,266,345,347,350,370,399,415,417],[91,133,273,274,275,276,277,278,299,300,344,416],[85,91,133,148,159,176,190,209,225,243,264,292,296,368,369,371,391,392,415,419],[91,133,392,404,420,421],[91,133,148,400,419],[91,133,284,295],[91,133,283],[79,91,133,420],[91,133,284,401,419],[91,133,148,190,216,402,403,419,420,421],[91,133,186,340,341,343],[91,133,229],[83,91,133,186],[91,133,186,397],[85,91,133,186,264,296,415,417],[83,91,133,475,476],[91,133,186,272],[81,91,133,159,176,186,241,267,269,271,417],[91,133,190,204,397],[91,133,397,407],[91,133,340],[81,91,133,146,148,159,186,230,236,272,415,416],[91,133,186,187,188,415,427],[91,133,186,451,452,453,454],[91,133,138],[91,133,231,232,233],[91,133,231],[81,91,133,148,150,159,183,186,187,188,189,191,225,319,414,417,419,427,454],[91,133,467],[91,133,469],[91,133,471],[91,133,793],[91,133,473],[91,133,432,433,434],[91,133,477],[91,133,431,435,445,455,457,459,464,466,468,470,472,474,478,480,481,483,484,485,486],[91,133,479],[91,133,444],[91,133,800],[91,133,269],[91,133,482],[91,132,133,310,397,402,404,420,421,422,423,424,427,428,429,430],[91,133,183],[91,133,510],[91,133,505,507,510],[91,133,506,507],[91,133,507,510,514],[91,133,506],[91,133,507,510],[91,133,505,506,507,509],[91,133,505,507],[91,133,506,507,516],[91,133,165,183],[91,100,104,133,176],[91,100,133,165,176],[91,95,133],[91,97,100,133,173,176],[91,133,153,173],[91,95,133,183],[91,97,100,133,153,176],[91,92,93,96,99,133,145,165,176],[91,100,107,133],[91,92,98,133],[91,100,121,122,133],[91,96,100,133,168,176,183],[91,121,133,183],[91,94,95,133,183],[91,100,133],[91,94,95,96,97,98,99,100,101,102,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,122,123,124,125,126,127,133],[91,100,115,133],[91,100,107,108,133],[91,98,100,108,109,133],[91,99,133],[91,92,95,100,133],[91,100,104,108,109,133],[91,104,133],[91,98,100,103,133,176],[91,92,97,100,107,133],[91,133,165],[91,95,100,121,133,181,183],[91,133,577],[91,133,501,582,585],[91,133,491,494,496,497,552,553,554,556,557,560,561,573,574,576,585],[91,133,556,567,568,585],[91,133,556,557,564,585],[91,133,494,496,556,557,560,585],[91,133,519],[91,133,494,501,556,557,560,569,585],[91,133,550,580,582],[91,133,136,146,165,494,496,501,550,554,556,557,560,561,564,565,566,569,572,573,574,578,579,582,585],[91,133,519,556,557,560,585],[91,133,556,567,568,569,585],[91,133,519,556,561,562,563,585],[91,133,136,146,165,494,496,501,519,550,554,556,557,560,561,562,563,564,565,566,567,568,569,572,573,574,578,579,580,581,582,585],[91,133,491,494,496,501,519,554,556,557,560,561,562,563,564,565,567,568,569,572,585,586,587,588,589,594],[91,133,494,496,556,557,560,561,567,568,569,585,587],[91,133,570],[91,133,570,571],[91,133,538],[91,133,536,538],[91,133,527,535,536,537,539,541],[91,133,525],[91,133,528,533,538,541],[91,133,524,541],[91,133,528,529,532,533,534,541],[91,133,528,529,530,532,533,541],[91,133,525,526,527,528,529,533,534,535,537,538,539,541],[91,133,541],[91,133,523,525,526,527,528,529,530,532,533,534,535,536,537,538,539,540],[91,133,523,541],[91,133,528,530,531,533,534,541],[91,133,532,541],[91,133,533,534,538,541],[91,133,526,536],[91,133,502],[91,133,145,146,148,149,150,153,165,173,176,182,183,502,503,504,511,512,513,515,517,519,520,521,522,542,546,547,548,549,550],[91,133,502,503,504,518],[91,133,504],[91,133,545],[91,133,511,521,550],[91,133,511,550],[91,133,155,583]],"fileInfos":[{"version":"e41c290ef7dd7dab3493e6cbe5909e0148edf4a8dad0271be08edec368a0f7b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"e12a46ce14b817d4c9e6b2b478956452330bf00c9801b79de46f7a1815b5bd40","impliedFormat":1},{"version":"4fd3f3422b2d2a3dfd5cdd0f387b3a8ec45f006c6ea896a4cb41264c2100bb2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"69e65d976bf166ce4a9e6f6c18f94d2424bf116e90837ace179610dbccad9b42","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"62bb211266ee48b2d0edf0d8d1b191f0c24fc379a82bd4c1692a082c540bc6b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"936e80ad36a2ee83fc3caf008e7c4c5afe45b3cf3d5c24408f039c1d47bdc1df","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"fef8cfad2e2dc5f5b3d97a6f4f2e92848eb1b88e897bb7318cef0e2820bceaab","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"f1e2a172204962276504466a6393426d2ca9c54894b1ad0a6c9dad867a65f876","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"b5ce7a470bc3628408429040c4e3a53a27755022a32fd05e2cb694e7015386c7","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"bab26767638ab3557de12c900f0b91f710c7dc40ee9793d5a27d32c04f0bf646","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"61d6a2092f48af66dbfb220e31eea8b10bc02b6932d6e529005fd2d7b3281290","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"829b9e6028b29e6a8b1c01ddb713efe59da04d857089298fa79acbdb3cfcfdef","impliedFormat":1},{"version":"24f8562308dd8ba6013120557fa7b44950b619610b2c6cb8784c79f11e3c4f90","impliedFormat":1},{"version":"5f90b8c733a1bda63e42160b15a2301051e83a6f9d5332a59d16eb12f463270d","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"631eff75b0e35d1b1b31081d55209abc43e16b49426546ab5a9b40bdd40b1f60","impliedFormat":1},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"030e350db2525514580ed054f712ffb22d273e6bc7eddc1bb7eda1e0ba5d395e","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"a79e62f1e20467e11a904399b8b18b18c0c6eea6b50c1168bf215356d5bebfaf","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fd06258805d26c72f5997e07a23155d322d5f05387adb3744a791fe6a0b042d","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"24bd580b5743dc56402c440dc7f9a4f5d592ad7a419f25414d37a7bfe11e342b","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"6bdc71028db658243775263e93a7db2fd2abfce3ca569c3cca5aee6ed5eb186d","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"ba97261afafe85aafe76d988e65e531da8e8cf791c49caf0531f5dd1689bd91b","affectsGlobalScope":true,"impliedFormat":1},{"version":"f52e8dacc97d71dcc96af29e49584353f9c54cb916d132e3e768d8b8129c928d","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"76103716ba397bbb61f9fa9c9090dca59f39f9047cb1352b2179c5d8e7f4e8d0","impliedFormat":1},{"version":"53eac70430b30089a3a1959d8306b0f9cfaf0de75224b68ef25243e0b5ad1ca3","affectsGlobalScope":true,"impliedFormat":1},{"version":"4314c7a11517e221f7296b46547dbc4df047115b182f544d072bdccffa57fc72","impliedFormat":1},{"version":"115971d64632ea4742b5b115fb64ed04bcaae2c3c342f13d9ba7e3f9ee39c4e7","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb","impliedFormat":1},{"version":"86956cc2eb9dd371d6fab493d326a574afedebf76eef3fa7833b8e0d9b52d6f1","affectsGlobalScope":true,"impliedFormat":1},{"version":"24642567d3729bcc545bacb65ee7c0db423400c7f1ef757cab25d05650064f98","impliedFormat":1},{"version":"e6f5a38687bebe43a4cef426b69d34373ef68be9a6b1538ec0a371e69f309354","impliedFormat":1},{"version":"a6bf63d17324010ca1fbf0389cab83f93389bb0b9a01dc8a346d092f65b3605f","impliedFormat":1},{"version":"e009777bef4b023a999b2e5b9a136ff2cde37dc3f77c744a02840f05b18be8ff","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"ee1ee365d88c4c6c0c0a5a5701d66ebc27ccd0bcfcfaa482c6e2e7fe7b98edf7","affectsGlobalScope":true,"impliedFormat":1},{"version":"88bc59b32d0d5b4e5d9632ac38edea23454057e643684c3c0b94511296f2998c","affectsGlobalScope":true,"impliedFormat":1},{"version":"e9ad08a376ac84948fcca0013d6f1d4ae4f9522e26b91f87945b97c99d7cc30b","impliedFormat":1},{"version":"eaf9ee1d90a35d56264f0bf39842282c58b9219e112ac7d0c1bce98c6c5da672","impliedFormat":1},{"version":"c15c4427ae7fd1dcd7f312a8a447ac93581b0d4664ddf151ecd07de4bf2bb9d7","impliedFormat":1},{"version":"5135bdd72cc05a8192bd2e92f0914d7fc43ee077d1293dc622a049b7035a0afb","impliedFormat":1},{"version":"4f80de3a11c0d2f1329a72e92c7416b2f7eab14f67e92cac63bb4e8d01c6edc8","impliedFormat":1},{"version":"6d386bc0d7f3afa1d401afc3e00ed6b09205a354a9795196caed937494a713e6","impliedFormat":1},{"version":"aeac7c51bde4658c192bc45819344eb20fc64743264b0465be6025201220a6b7","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4047ed87e765bd3bcc316a0c4c4c8b0061628460d8a5412d1c4b53a4658665a","impliedFormat":1},{"version":"b1b6ee0d012aeebe11d776a155d8979730440082797695fc8e2a5c326285678f","impliedFormat":1},{"version":"45875bcae57270aeb3ebc73a5e3fb4c7b9d91d6b045f107c1d8513c28ece71c0","impliedFormat":1},{"version":"3eb62baae4df08c9173e6903d3ca45942ccec8c3659b0565684a75f3292cffbb","affectsGlobalScope":true,"impliedFormat":1},{"version":"42aaa94addeed66a04b61e433c14e829c43d1efd653cf2fda480c5fb3d722ed8","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"c6b4e0a02545304935ecbf7de7a8e056a31bb50939b5b321c9d50a405b5a0bba","impliedFormat":1},{"version":"fab29e6d649aa074a6b91e3bdf2bff484934a46067f6ee97a30fcd9762ae2213","impliedFormat":1},{"version":"8145e07aad6da5f23f2fcd8c8e4c5c13fb26ee986a79d03b0829b8fce152d8b2","impliedFormat":1},{"version":"e1120271ebbc9952fdc7b2dd3e145560e52e06956345e6fdf91d70ca4886464f","impliedFormat":1},{"version":"15c5e91b5f08be34a78e3d976179bf5b7a9cc28dc0ef1ffebffeb3c7812a2dca","impliedFormat":1},{"version":"58832ded29e0094047596544ac391d68c799d7bd7d35936f47221857141628f1","impliedFormat":1},{"version":"553870e516f8c772b89f3820576152ebc70181d7994d96917bb943e37da7f8a7","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"93452d394fdd1dc551ec62f5042366f011a00d342d36d50793b3529bfc9bd633","impliedFormat":1},{"version":"745c4240220559bd340c8aeb6e3c5270a709d3565e934dc22a69c304703956bc","affectsGlobalScope":true,"impliedFormat":1},{"version":"2754d8221d77c7b382096651925eb476f1066b3348da4b73fe71ced7801edada","impliedFormat":1},{"version":"9212c6e9d80cb45441a3614e95afd7235a55a18584c2ed32d6c1aca5a0c53d93","affectsGlobalScope":true,"impliedFormat":1},{"version":"bef91efa0baea5d0e0f0f27b574a8bc100ce62a6d7e70220a0d58af6acab5e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"282fd2a1268a25345b830497b4b7bf5037a5e04f6a9c44c840cb605e19fea841","impliedFormat":1},{"version":"5360a27d3ebca11b224d7d3e38e3e2c63f8290cb1fcf6c3610401898f8e68bc3","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"7d6ff413e198d25639f9f01f16673e7df4e4bd2875a42455afd4ecc02ef156da","affectsGlobalScope":true,"impliedFormat":1},{"version":"6bd91a2a356600dee28eb0438082d0799a18a974a6537c4410a796bab749813c","affectsGlobalScope":true,"impliedFormat":1},{"version":"f689c4237b70ae6be5f0e4180e8833f34ace40529d1acc0676ab8fb8f70457d7","impliedFormat":1},{"version":"ae25afbbf1ed5df63a177d67b9048bf7481067f1b8dc9c39212e59db94fc9fc6","impliedFormat":1},{"version":"ac5ed35e649cdd8143131964336ab9076937fa91802ec760b3ea63b59175c10a","impliedFormat":1},{"version":"52a8e7e8a1454b6d1b5ad428efae3870ffc56f2c02d923467f2940c454aa9aec","affectsGlobalScope":true,"impliedFormat":1},{"version":"78dc0513cc4f1642906b74dda42146bcbd9df7401717d6e89ea6d72d12ecb539","impliedFormat":1},{"version":"ad90122e1cb599b3bc06a11710eb5489101be678f2920f2322b0ac3e195af78d","impliedFormat":1},{"version":"36a2e4c9a67439aca5f91bb304611d5ae6e20d420503e96c230cf8fcdc948d94","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"da172a28c35d5f298bf5f0361725cc80f45e89e803a353c56e1aa347e10f572d","impliedFormat":1},{"version":"865a2612f5ec073dd48d454307ccabb04c48f8b96fda9940c5ebfe6b4b451f51","impliedFormat":1},{"version":"4558ac151f289d39f651a630cc358111ef72c1148e06627ef7edaeb01eb26c82","impliedFormat":1},{"version":"413df52d4ea14472c2fa5bee62f7a40abd1eb49be0b9722ee01ee4e52e63beb2","impliedFormat":1},{"version":"35e6379c3f7cb27b111ad4c1aa69538fd8e788ab737b8ff7596a1b40e96f4f90","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"acf5a2ac47b59ca07afa9abbd2b31d001bf7448b041927befae2ea5b1951d9f9","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d71291eff1e19d8762a908ba947e891af44749f3a2cbc5bd2ec4b72f72ea795f","impliedFormat":1},{"version":"c0480e03db4b816dff2682b347c95f2177699525c54e7e6f6aa8ded890b76be7","impliedFormat":1},{"version":"27ab780875bcbb65e09da7496f2ca36288b0c541abaa75c311450a077d54ec15","impliedFormat":1},{"version":"ffa495b17a5ef1d0399586b590bd281056cee6ce3583e34f39926f8dcc6ecdb5","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"b6db56e4903e9c32e533b78ac85522de734b3d3a8541bf24d256058d464bf04b","impliedFormat":1},{"version":"24daa0366f837d22c94a5c0bad5bf1fd0f6b29e1fae92dc47c3072c3fdb2fbd5","impliedFormat":1},{"version":"570bb5a00836ffad3e4127f6adf581bfc4535737d8ff763a4d6f4cc877e60d98","impliedFormat":1},{"version":"889c00f3d32091841268f0b994beba4dceaa5df7573be12c2c829d7c5fbc232c","impliedFormat":1},{"version":"65f43099ded6073336e697512d9b80f2d4fec3182b7b2316abf712e84104db00","impliedFormat":1},{"version":"ce75b1aebb33d510ff28af960a9221410a3eaf7f18fc5f21f9404075fba77256","impliedFormat":1},{"version":"d6e73f8010935b7b4c7487b6fb13ea197cc610f0965b759bec03a561ccf8423a","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"174f3864e398f3f33f9a446a4f403d55a892aa55328cf6686135dfaf9e171657","impliedFormat":1},{"version":"824c76aec8d8c7e65769688cbee102238c0ef421ed6686f41b2a7d8e7e78a931","impliedFormat":1},{"version":"75b868be3463d5a8cfc0d9396f0a3d973b8c297401d00bfb008a42ab16643f13","impliedFormat":1},{"version":"5178eb4415a172c287c711dc60a619e110c3fd0b7de01ed0627e51a5336aa09c","impliedFormat":1},{"version":"ca6e5264278b53345bc1ce95f42fb0a8b733a09e3d6479c6ccfca55cdc45038c","impliedFormat":1},{"version":"15a234e5031b19c48a69ccc1607522d6e4b50f57d308ecb7fe863d44cd9f9eb3","impliedFormat":1},{"version":"ad0d1d75d129b1c80f911be438d6b61bfa8703930a8ff2be2f0e1f8a91841c64","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"496bbf339f3838c41f164238543e9fe5f1f10659cb30b68903851618464b98ba","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"fb1d8e814a3eeb5101ca13515e0548e112bd1ff3fb358ece535b93e94adf5a3a","impliedFormat":1},{"version":"98b18458acb46072947aabeeeab1e410f047e0cacc972943059ca5500b0a5e95","impliedFormat":1},{"version":"361e2b13c6765d7f85bb7600b48fde782b90c7c41105b7dab1f6e7871071ba20","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"380647d8f3b7f852cca6d154a376dbf8ac620a2f12b936594504a8a852e71d2f","impliedFormat":1},{"version":"208c9af9429dd3c76f5927b971263174aaa4bc7621ddec63f163640cbd3c473c","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"a23185bc5ef590c287c28a91baf280367b50ae4ea40327366ad01f6f4a8edbc5","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"002eae065e6960458bda3cf695e578b0d1e2785523476f8a9170b103c709cd4f","impliedFormat":1},{"version":"c83bb0c9c5645a46c68356c2f73fdc9de339ce77f7f45a954f560c7e0b8d5ebb","impliedFormat":1},{"version":"05c97cddbaf99978f83d96de2d8af86aded9332592f08ce4a284d72d0952c391","impliedFormat":1},{"version":"72179f9dd22a86deaad4cc3490eb0fe69ee084d503b686985965654013f1391b","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"7b6ff760c8a240b40dab6e4419b989f06a5b782f4710d2967e67c695ef3e93c4","impliedFormat":1},{"version":"c8f004e6036aa1c764ad4ec543cf89a5c1893a9535c80ef3f2b653e370de45e6","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"b064c36f35de7387d71c599bfcf28875849a1dbc733e82bd26cae3d1cd060521","impliedFormat":1},{"version":"6a148329edecbda07c21098639ef4254ef7869fb25a69f58e5d6a8b7b69d4236","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"f63ab283a1c8f5c79fabe7ca4ef85f9633339c4f0e822fce6a767f9d59282af2","impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a54c996c8870ef1728a2c1fa9b8eaec0bf4a8001cd2583c02dd5869289465b10","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"3754982006a3b32c502cff0867ca83584f7a43b1035989ca73603f400de13c96","impliedFormat":1},{"version":"a30ae9bb8a8fa7b90f24b8a0496702063ae4fe75deb27da731ed4a03b2eb6631","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"7d73b24e7bf31dfb8a931ca6c4245f6bb0814dfae17e4b60c9e194a631fe5f7b","impliedFormat":1},{"version":"413586add0cfe7369b64979d4ec2ed56c3f771c0667fbde1bf1f10063ede0b08","impliedFormat":1},{"version":"06472528e998d152375ad3bd8ebcb69ff4694fd8d2effaf60a9d9f25a37a097a","impliedFormat":1},{"version":"50b5bc34ce6b12eccb76214b51aadfa56572aa6cc79c2b9455cdbb3d6c76af1d","impliedFormat":1},{"version":"b7e16ef7f646a50991119b205794ebfd3a4d8f8e0f314981ebbe991639023d0e","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"a401617604fa1f6ce437b81689563dfdc377069e4c58465dbd8d16069aede0a5","impliedFormat":1},{"version":"e9dd71cf12123419c60dab867d44fbee5c358169f99529121eaef277f5c83531","impliedFormat":1},{"version":"5b6a189ba3a0befa1f5d9cb028eb9eec2af2089c32f04ff50e2411f63d70f25d","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"e91f7b1344577a02f051b9b471f33044fef8334a76dc9e1de003d17595a5219b","impliedFormat":1},{"version":"3af3584f79c57853028ef9421ec172539e1fe01853296dc05a9d615ade4ffaf6","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"0c7c947ff881c4274c0800deaa0086971e0bfe51f89a33bd3048eaa3792d4876","affectsGlobalScope":true,"impliedFormat":1},{"version":"db01d18853469bcb5601b9fc9826931cc84cc1a1944b33cad76fd6f1e3d8c544","affectsGlobalScope":true,"impliedFormat":1},{"version":"a8f8e6ab2fa07b45251f403548b78eaf2022f3c2254df3dc186cb2671fe4996d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"15b36126e0089bfef173ab61329e8286ce74af5e809d8a72edcafd0cc049057f","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"ad10d4f0517599cdeca7755b930f148804e3e0e5b5a3847adce0f1f71bbccd74","impliedFormat":1},{"version":"1042064ece5bb47d6aba91648fbe0635c17c600ebdf567588b4ca715602f0a9d","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"c49469a5349b3cc1965710b5b0f98ed6c028686aa8450bcb3796728873eb923e","impliedFormat":1},{"version":"4a889f2c763edb4d55cb624257272ac10d04a1cad2ed2948b10ed4a7fda2a428","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"72d63643a657c02d3e51cd99a08b47c9b020a565c55f246907050d3c8a5e77fb","impliedFormat":1},{"version":"1d415445ea58f8033ba199703e55ff7483c52ac6742075b803bd3e7bbe9f5d61","impliedFormat":1},{"version":"d6406c629bb3efc31aedb2de809bef471e475c86c7e67f3ef9b676b5d7e0d6b2","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"71d8ba39a9e024d9e4bb922464d18542ed8d2c25ee78efa7890c27213cc6e5d3","impliedFormat":1},{"version":"8c030e515014c10a2b98f9f48408e3ba18023dfd3f56e3312c6c2f3ae1f55a16","impliedFormat":1},{"version":"dafc31e9e8751f437122eb8582b93d477e002839864410ff782504a12f2a550c","impliedFormat":1},{"version":"754498c5208ce3c5134f6eabd49b25cf5e1a042373515718953581636491f3c3","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"f56bdc6884648806d34bc66d31cdb787c4718d04105ce2cd88535db214631f82","impliedFormat":1},{"version":"633d58a237f4bb25ec7d565e4ffa32cecdcee8660ac12189c4351c52557cee9e","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"13283350547389802aa35d9f2188effaeac805499169a06ef5cd77ce2a0bd63f","impliedFormat":1},{"version":"ce791f6ea807560f08065d1af6014581eeb54a05abd73294777a281b6dfd73c2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"49f95e989b4632c6c2a578cc0078ee19a5831832d79cc59abecf5160ea71abad","impliedFormat":1},{"version":"9666533332f26e8995e4d6fe472bdeec9f15d405693723e6497bf94120c566c8","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"e17cd049a1448de4944800399daa4a64c5db8657cc9be7ef46be66e2a2cd0e7c","impliedFormat":1},{"version":"43fa6ea8714e18adc312b30450b13562949ba2f205a1972a459180fa54471018","impliedFormat":1},{"version":"6e89c2c177347d90916bad67714d0fb473f7e37fb3ce912f4ed521fe2892cd0d","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"4d4927cbee21750904af7acf940c5e3c491b4d5ebc676530211e389dd375607a","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"8a97e578a9bc40eb4f1b0ca78f476f2e9154ecbbfd5567ee72943bab37fc156a","impliedFormat":1},{"version":"c857e0aae3f5f444abd791ec81206020fbcc1223e187316677e026d1c1d6fe08","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"2d7db1d73456e8c5075387d4240c29a2a900847f9c1bff106a2e490da8fbd457","impliedFormat":1},{"version":"2b15c805f48e4e970f8ec0b1915f22d13ca6212375e8987663e2ef5f0205e832","impliedFormat":1},{"version":"f22d05663d873ee7a600faf78abb67f3f719d32266803440cf11d5db7ac0cab2","impliedFormat":1},{"version":"d93c544ad20197b3976b0716c6d5cd5994e71165985d31dcab6e1f77feb4b8f2","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"a8b1c79a833ee148251e88a2553d02ce1641d71d2921cce28e79678f3d8b96aa","impliedFormat":1},{"version":"126d4f950d2bba0bd45b3a86c76554d4126c16339e257e6d2fabf8b6bf1ce00c","impliedFormat":1},{"version":"7e0b7f91c5ab6e33f511efc640d36e6f933510b11be24f98836a20a2dc914c2d","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"2d3cc2211f352f46ea6b7cf2c751c141ffcdf514d6e7ae7ee20b7b6742da313f","impliedFormat":1},{"version":"c75445151ff8b77d9923191efed7203985b1a9e09eccf4b054e7be864e27923d","impliedFormat":1},{"version":"0aedb02516baf3e66b2c1db9fef50666d6ed257edac0f866ea32f1aa05aa474f","impliedFormat":1},{"version":"fa8a8fbf91ee2a4779496225f0312aac6635b0f21aa09cdafa4283fe32d519c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"0e8aef93d79b000deb6ec336b5645c87de167168e184e84521886f9ecc69a4b5","impliedFormat":1},{"version":"56ccb49443bfb72e5952f7012f0de1a8679f9f75fc93a5c1ac0bafb28725fc5f","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"d90b9f1520366d713a73bd30c5a9eb0040d0fb6076aff370796bc776fd705943","impliedFormat":1},{"version":"bef86adb77316505c6b471da1d9b8c9e428867c2566270e8894d4d773a1c4dc2","impliedFormat":1},{"version":"de7052bfee2981443498239a90c04ea5cc07065d5b9bb61b12cb6c84313ad4ef","impliedFormat":1},{"version":"a3e7d932dc9c09daa99141a8e4800fc6c58c625af0d4bbb017773dc36da75426","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"4a2edd238d9104eac35b60d727f1123de5062f452b70ed8e0366cb36387dfdfd","impliedFormat":1},{"version":"ca921bf56756cb6fe957f6af693a35251b134fb932dc13f3dfff0bb7106f80b4","impliedFormat":1},{"version":"fee92c97f1aa59eb7098a0cc34ff4df7e6b11bae71526aca84359a2575f313d8","impliedFormat":1},{"version":"0bd0297484aacea217d0b76e55452862da3c5d9e33b24430e0719d1161657225","impliedFormat":1},{"version":"2ab6d334bcbf2aff3acfc4fd8c73ecd82b981d3c3aa47b3f3b89281772286904","impliedFormat":1},{"version":"d07cbc787a997d83f7bde3877fec5fb5b12ce8c1b7047eb792996ed9726b4dde","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"4805f6161c2c8cefb8d3b8bd96a080c0fe8dbc9315f6ad2e53238f9a79e528a6","impliedFormat":1},{"version":"b83cb14474fa60c5f3ec660146b97d122f0735627f80d82dd03e8caa39b4388c","impliedFormat":1},{"version":"f374cb24e93e7798c4d9e83ff872fa52d2cdb36306392b840a6ddf46cb925cb6","impliedFormat":1},{"version":"49179c6a23701c642bd99abe30d996919748014848b738d8e85181fc159685ff","impliedFormat":1},{"version":"7274fbffbd7c9589d8d0ffba68157237afd5cecff1e99881ea3399127e60572f","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"45490817629431853543adcb91c0673c25af52a456479588b6486daba34f68bb","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"8b4327413e5af38cd8cb97c59f48c3c866015d5d642f28518e3a891c469f240e","impliedFormat":1},{"version":"8514c62ce38e58457d967e9e73f128eedc1378115f712b9eef7127f7c88f82ae","impliedFormat":1},{"version":"f1289e05358c546a5b664fbb35a27738954ec2cc6eb4137350353099d154fc62","impliedFormat":1},{"version":"752513f35f6cff294ffe02d6027c41373adf7bfa35e593dbfd53d95c203635ee","impliedFormat":1},{"version":"6c800b281b9e89e69165fd11536195488de3ff53004e55905e6c0059a2d8591e","impliedFormat":1},{"version":"7d4254b4c6c67a29d5e7f65e67d72540480ac2cfb041ca484847f5ae70480b62","impliedFormat":1},{"version":"1a7e2ea171726446850ec72f4d1525d547ff7e86724cc9e7eec509725752a758","impliedFormat":1},{"version":"8c901126d73f09ecdea4785e9a187d1ac4e793e07da308009db04a7283ec2f37","impliedFormat":1},{"version":"c1de754ab5f3b0f4036d6893c74a0fc984c7fcb07936086f19bbe2974406775b","impliedFormat":1},{"version":"aab290b8e4b7c399f2c09b957666fc95335eb4522b2dd9ead1bf0cb64da6d6ee","impliedFormat":1},{"version":"94fe3281392e1015b22f39535878610b4fa6f1388dc8d78746be3bc4e4bb8950","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"06c25ddfc2242bd06c19f66c9eae4c46d937349a267810f89783680a1d7b5259","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"bd4131091b773973ca5d2326c60b789ab1f5e02d8843b3587effe6e1ea7c9d86","impliedFormat":1},{"version":"c7f6485931085bf010fbaf46880a9b9ec1a285ad9dc8c695a9e936f5a48f34b4","impliedFormat":1},{"version":"14f6b927888a1112d662877a5966b05ac1bf7ed25d6c84386db4c23c95a5363b","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"0427df5c06fafc5fe126d14b9becd24160a288deff40e838bfbd92a35f8d0d00","impliedFormat":1},{"version":"90c54a02432d04e4246c87736e53a6a83084357acfeeba7a489c5422b22f5c7a","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"83fe880c090afe485a5c02262c0b7cdd76a299a50c48d9bde02be8e908fb4ae6","impliedFormat":1},{"version":"0a372c2d12a259da78e21b25974d2878502f14d89c6d16b97bd9c5017ab1bc12","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"6511e4503cf74c469c60aafd6589e4d14d5eb0a25f9bf043dcbecdf65f261972","impliedFormat":1},{"version":"ec1ca97598eda26b7a5e6c8053623acbd88e43be7c4d29c77ccd57abc4c43999","impliedFormat":1},{"version":"efc0e6a64feef102d5edef7525de0c2ac5961821f12ae5038f0d3a8631457e10","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"a47e6d954d22dd9ebb802e7e431b560ed7c581e79fb885e44dc92ed4f60d4c07","impliedFormat":1},{"version":"f019e57d2491c159d47a107fd90219a1734bdd2e25cd8d1db3c8fae5c6b414c4","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"d1c9bf292a54312888a77bb19dba5e2503ad803f5393beafd45d78d2f4fe9b48","impliedFormat":1},{"version":"d682336018141807fb602709e2d95a192828fcb8d5ba06dda3833a8ea98f69e3","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"6dcf60530c25194a9ee0962230e874ff29d34c59605d8e069a49928759a17e0a","impliedFormat":1},{"version":"1a42d2ec31a1fe62fdc51591768695ed4a2dc64c01be113e7ff22890bebb5e3f","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"1d17ba45cfbe77a9c7e0df92f7d95f3eefd49ee23d1104d0548b215be56945ad","impliedFormat":1},{"version":"f7d628893c9fa52ba3ab01bcb5e79191636c4331ee5667ecc6373cbccff8ae12","impliedFormat":1},{"version":"1d879125d1ec570bf04bc1f362fdbe0cb538315c7ac4bcfcdf0c1e9670846aa6","impliedFormat":1},{"version":"a84d677a6172185d541b1b00ea8e4e04217b64351098f9e5c07403fdec305683","impliedFormat":1},{"version":"46273e8c29816125d0d0b56ce9a849cc77f60f9a5ba627447501d214466f0ff3","impliedFormat":1},{"version":"f82579d87701d639ff4e3930a9b24f4ee13ca74221a9a3a792feb47f01881a9c","impliedFormat":1},{"version":"d7e5d5245a8ba34a274717d085174b2c9827722778129b0081fefd341cca8f55","impliedFormat":1},{"version":"d9d32f94056181c31f553b32ce41d0ef75004912e27450738d57efcd2409c324","impliedFormat":1},{"version":"3be035da7bee86b4c3abf392e0edaa44fc6e45092995eefe36b39118c8a84068","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f828825d077c2fa0ea606649faeb122749273a353daab23924fe674e98ba44c","impliedFormat":1},{"version":"2896c2e673a5d3bd9b4246811f79486a073cbb03950c3d252fba10003c57411a","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"d9c46395b3980848a6c4b2f44e986d27d07faee3b3b4b1f214793255b8c64f2a","impliedFormat":1},{"version":"4fbd3116e00ed3a6410499924b6403cc9367fdca303e34838129b328058ede40","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"a68d4b3182e8d776cdede7ac9630c209a7bfbb59191f99a52479151816ef9f9e","impliedFormat":99},{"version":"39644b343e4e3d748344af8182111e3bbc594930fff0170256567e13bbdbebb0","impliedFormat":99},{"version":"ed7fd5160b47b0de3b1571c5c5578e8e7e3314e33ae0b8ea85a895774ee64749","impliedFormat":99},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"ecbaf0da125974be39c0aac869e403f72f033a4e7fd0d8cd821a8349b4159628","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"ceec3c81b2d81f5e3b855d9367c1d4c664ab5046dff8fd56552df015b7ccbe8f","affectsGlobalScope":true,"impliedFormat":1},{"version":"fec01479923e169fb52bd4f668dbeef1d7a7ea6e6d491e15617b46f2cacfa37d","impliedFormat":1},{"version":"8a8fb3097ba52f0ae6530ec6ab34e43e316506eb1d9aa29420a4b1e92a81442d","impliedFormat":1},{"version":"44e09c831fefb6fe59b8e65ad8f68a7ecc0e708d152cfcbe7ba6d6080c31c61e","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"330896c1a2b9693edd617be24fbf9e5895d6e18c7955d6c08f028f272b37314d","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"30d8da250766efa99490fc02801047c2c6d72dd0da1bba6581c7e80d1d8842a4","impliedFormat":1},{"version":"03566202f5553bd2d9de22dfab0c61aa163cabb64f0223c08431fb3fc8f70280","impliedFormat":1},{"version":"5f0292a40df210ab94b9fb44c8b775c51e96777e14e073900e392b295ca1061b","impliedFormat":1},{"version":"bc9ee0192f056b3d5527bcd78dc3f9e527a9ba2bdc0a2c296fbc9027147df4b2","impliedFormat":1},{"version":"8627ad129bcf56e82adff0ab5951627c993937aa99f5949c33240d690088b803","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"7a9d8081e53ceb3b0700c04cc6a4326710e07d8ae5bd355fe838d8fbb576dbd3","signature":"fe6e4a89498f8e911129972c3ea7f3fa702ad3237c9fce5ff054796500419fa1"},{"version":"ea8451b009f99c2bf47e1902913652b3b9614b895e3d1131519a1bea5206ace6","signature":"3d90fcd2ccbafc7fd8723c9cddd66b915828ad4467750e397843ecd4777e9f9c"},{"version":"cc09f85ed3b0a514c16448147441909697ab2dffea3f63c41af571decadf07ef","signature":"7cb7c5b6a6a2e993366308734e33d17baad50280e1d59b4764f0a70615383e0e"},{"version":"327c99b7ca817e17d532f27eb3afc8dfa9c26b1fa09306964b9b6b5a8ee415cf","signature":"1d4eab0c5dd0e1ea56f82736ca04fcbe7c46e281d5143f9f5e38ceee213fb3c2"},{"version":"e25a593d67d92244115853b13766fbb982cc6682f31f80fcf70fabb23f604a2a","affectsGlobalScope":true},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"0f6666b58e9276ac3a38fdc80993d19208442d6027ab885580d93aec76b4ef00","impliedFormat":1},{"version":"05fd364b8ef02fb1e174fbac8b825bdb1e5a36a016997c8e421f5fab0a6da0a0","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"407a06ba04eede4074eec470ecba2784cbb3bf4e7de56833b097dd90a2aa0651","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"5c96bad5f78466785cdad664c056e9e2802d5482ca5f862ed19ba34ffbb7b3a4","impliedFormat":1},{"version":"81d8603ac527e75cfec72bb9391228b58f161c2b33514a9d814c7f3ebd3ef466","impliedFormat":1},{"version":"5f3dc10ae646f375776b4e028d2bed039a93eebbba105694d8b910feebbe8b9c","impliedFormat":1},{"version":"bb0cd7862b72f5eba39909c9889d566e198fcaddf7207c16737d0c2246112678","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"320f4091e33548b554d2214ce5fc31c96631b513dffa806e2e3a60766c8c49d9","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"d90d5f524de38889d1e1dbc2aeef00060d779f8688c02766ddb9ca195e4a713d","impliedFormat":1},{"version":"a3f41ed1b4f2fc3049394b945a68ae4fdefd49fa1739c32f149d32c0545d67f5","impliedFormat":1},{"version":"bad68fd0401eb90fe7da408565c8aee9c7a7021c2577aec92fa1382e8876071a","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"4655709c9cb3fd6db2b866cab7c418c40ed9533ce8ea4b66b5f17ec2feea46a9","impliedFormat":1},{"version":"87affad8e2243635d3a191fa72ef896842748d812e973b7510a55c6200b3c2a4","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"3eecb25bb467a948c04874d70452b14ae7edb707660aac17dc053e42f2088b00","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"8fac4a15690b27612d8474fb2fc7cc00388df52d169791b78d1a3645d60b4c8b","affectsGlobalScope":true,"impliedFormat":1},{"version":"064ac1c2ac4b2867c2ceaa74bbdce0cb6a4c16e7c31a6497097159c18f74aa7c","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"1d63055b690a582006435ddd3aa9c03aac16a696fac77ce2ed808f3e5a06efab","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},"85ae5aee75f011967cf2d25cbc342f62d69314e9d925f7f4aa3456fc2cffcca6",{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"4ead13a482c539b77394b2a97e3b877b809eac596390371cea490286f53b996a","impliedFormat":99},{"version":"12405c748be62bbefb6df7515956b0921f84d7b444d1f6c3e00af1980cc3af17","impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","impliedFormat":99},{"version":"3a582c6e8906f5b094ccf0de6cc6f4f8a54b05a34f52517aba5c9c7f704f6b28","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"49ab4f1d153a252779958fc87b700743d32b5ffa42addd70ae23ad3f429daa5c","impliedFormat":99},{"version":"0528f6d21f7a02d4092895090d2dd86104bd5a3e79eced96d5a1a7dd90943d17","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"5c935b7fc4ddc1410ea1cd7cd4e35ed106a6e4920dd27a9480a40fd224359dc3","affectsGlobalScope":true,"impliedFormat":99},{"version":"64ccfe7dcbcad9b8a661a042ecb2309e41b9ab957f7b8cd070b05583a7821a13","impliedFormat":99},{"version":"c911b3bd083dade8fdb7775261aa25feae695d562a3b11209650b47be90043fc","impliedFormat":99},{"version":"98580d6ed68c9cee7f956685abbb6502d70bd92f48175ac807ae6c5e254fadc6","impliedFormat":99},{"version":"fc5ad2ec8b130a59612c5cfcc2cdca9bcdd80851adb8acdb004aaeff00cc978d","impliedFormat":99},{"version":"f76d2226b20ec0d3e884114cfbbb933cb4d25d26cc306e8315e21a0e3afafc39","impliedFormat":99},{"version":"709b4ace1214759ca558d2b85c1ac20011c384077769ed8e08d6b2e6d68ef6d9","impliedFormat":99},{"version":"638a22f8e16b31c37297483fa38cfc760ed309b16d4c606a7b4532f4394c9c0c","impliedFormat":99},{"version":"9394183f4c37b8591156f32e41223c9e0dd211eefe7499155d4cdf15268eaea8","impliedFormat":99},{"version":"3ad031a1ffc57462a2c14b2f2e1e6eaa22787748fbcc32dd6a8a534a8e5e18b9","impliedFormat":99},{"version":"4273ef5d82e0f998bf38e777967753cde645668daf16dc8c3a72162c9cb4ff71","impliedFormat":99},{"version":"0824e917b53180b0b433f4fa85a1cc1d689a6c5f118bfdbe2086aa3b328c12db","impliedFormat":99},{"version":"7d3e062a778b8f5ea4f0cac7e925e31f88e6739812ebc5f827474324a4048f14","impliedFormat":99},{"version":"ca8bf149019241a83e9ace4f614d0410c00bff1246dbd1bbe63f88737823723d","impliedFormat":99},{"version":"33338bdcc31a340ca3a62ca2fac6086c5ac899b98cc50246d8fd4333c138e501","impliedFormat":99},{"version":"e27361b231a041b6c4da9fae61463d39d99f64926e1cd732513fd413b77bf869","impliedFormat":99},{"version":"ae1da3dbcdf34006350c132fd8795b1fc9a86d8fa398cd00de50463bd1eba9e2","impliedFormat":99},{"version":"4e003c868b0d8f8ad200b96cbc653e18e513fa23e1c19c4fe3cc25d4394efc47","impliedFormat":99},{"version":"046271de66ae07ca543f79eba0de19f342b78e759b597929159b2392e0a7ac87","impliedFormat":99},{"version":"e0864480ea083087d705f9405bd6bf59b795e8474c3447f0d6413b2bce535a09","impliedFormat":99},{"version":"e67cbea16f1994af89efd700542dbf3828a46a52b29e4d67e801bd7869dc103c","impliedFormat":99},{"version":"f582b0fcbf1eea9b318ab92fb89ea9ab2ebb84f9b60af89328a91155e1afce72","impliedFormat":99},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"38cb107048cd8ba54a70014ef9a30cf57bee0d9f10a0ca4cefa974056e1ee460","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"3cc8a3d123b6b232d48d34b51b785f9da8d193f5b5817fa521fcd2f3b9315c55","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"9a195d8476f48523446ece812e5450b5b1ec8c3b6abf99efb8ee2479524f6f56","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"354a7f8e1287d9d6b7561bc97fdd8cbc2f7c1dd79e4cb37b942e8a5cfaff1085","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"f3fe8197217cb6fd6e07c0eaa0966e4ece77cd779cbfc55767a93704cc692e04","impliedFormat":1},{"version":"a18642ddf216f162052a16cba0944892c4c4c977d3306a87cb673d46abbb0cbf","impliedFormat":1},{"version":"617112682fcb55805f0a0e16450397edc016881962eee4298f4f4e3ef795aee8","impliedFormat":1},{"version":"4ec16d7a4e366c06a4573d299e15fe6207fc080f41beac5da06f4af33ea9761e","impliedFormat":99},{"version":"960bd764c62ac43edc24eaa2af958a4b4f1fa5d27df5237e176d0143b36a39c6","affectsGlobalScope":true,"impliedFormat":99},{"version":"4a2479741e1be22a0537a0a0b341ac0df26df60c62d3d0f1bcda536936066526","impliedFormat":99},{"version":"e50d04d261120f8f9d07e85698735d5b88f3a66bc395c08ce26c3d817d141a73","affectsGlobalScope":true,"impliedFormat":99},{"version":"b0d00966e18eb7d205f31e4766dba87631e14bc121c5bef41bc70dad3c39a902","impliedFormat":99},{"version":"b5ce343886d23392be9c8280e9f24a87f1d7d3667f6672c2fe4aa61fa4ece7d4","impliedFormat":99},{"version":"72ce5b734c05da85c85a6f6dc05823b051d6aa41acaedeeb1d17c72f3b4efa72","impliedFormat":99},{"version":"b0857bb28fd5236ace84280f79a25093f919fd0eff13e47cc26ea03de60a7294","impliedFormat":99},{"version":"5e43e0824f10cd8c48e7a8c5c673638488925a12c31f0f9e0957965c290eb14c","impliedFormat":99},{"version":"53cf4076f42b29b8d411259d168d51b3a0274c42c8814e5b44dfa8803a35d4fc","impliedFormat":99},{"version":"a39461ee1f27cf3e6cfd63d21045713d26d521da55ea4d8efccb705f689e6dbb","impliedFormat":99},{"version":"61bb64660ee150f3ab618340e15cca0a81664801bede7c966ca0eca3a952fe63","impliedFormat":99},{"version":"42a12f2faa483c9b48195ed794d22698162274e755f6e07219c2351c4f08d732","impliedFormat":99},{"version":"ec0c42bb0f465e4993f2bc68a6ce9df9a2dcbc7b83e21748f82f1b69561938e3","impliedFormat":99},{"version":"f50ff37a9cbbe74475f426474d9827083c7c2c138a954d28f1690df338f69291","impliedFormat":99},{"version":"61fd6c17235d530c40f543dd7c40afab091d91c1ef890baeed30db6d82b04b28","impliedFormat":99},{"version":"bcbd3becd08b4515225880abea0dbfbbf0d1181ce3af8f18f72f61edbe4febfb","impliedFormat":99},{"version":"091767bc841f937654ed597d49e023ed59850355e746ae1a6f20ab31076ee1fb","impliedFormat":99},{"version":"19c6d6135af59693698d384050b45a8a049493500add442f58e4bd7c8a255ab6","impliedFormat":99},{"version":"6a0dba12d55314638a8c51108b20fe2f68f1364a619d098918bda91c22dec154","impliedFormat":99},{"version":"8124828a11be7db984fcdab052fd4ff756b18edcfa8d71118b55388176210923","impliedFormat":99},{"version":"2b39c6cf59088713babbfc3e20ee85f1375d40e66953156fa658346b8346f24f","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"6987dfb4b0c4e02112cc4e548e7a77b3d9ddfeffa8c8a2db13ceac361a4567d9","impliedFormat":99},{"version":"5e2ba3d18d78aebbde1f34bde356e41e9c76eeaeaeee56a37036596a9eff4211","impliedFormat":99},{"version":"8280ae8ccc0493b32d1742d585357ab9f0a508ea050af25a5a20d64010d0a5cf","impliedFormat":99},{"version":"7adfd9f9056ecd4ae6c65fde2a98654960c662714c73f048478959d04c09e144","impliedFormat":99},{"version":"437b7613a30a2fcde463f7b707c6d5567a8823fbc51de50b8641bf5b1d126fad","impliedFormat":99},{"version":"63ea959e28c110923f495576e614fb8b36c09b6828b467b2c7cd7f03b03ccf9f","impliedFormat":99},{"version":"1601a95dbb33059fc3d12638ed2a9aecff899e339c5c0f3a0b28768866d385b4","impliedFormat":99},{"version":"56fc978580577d30f4c2cdb5b1eb9217b66ed66537dd27141256f426e4b8dd68","impliedFormat":99},{"version":"2c5413050a2580becf9d82dd7e3006b95623e96f145356bf73230cd635352f70","impliedFormat":99},{"version":"860bedc71ead192ea4a0ea5ef4686e65724d14b391ebd1a6671a7044e6bd8e15","impliedFormat":99},{"version":"7c0a845bee4a084cbb8654709f48e5f13e2f6d45e5e2dde7c57cadf79fd9e3d5","impliedFormat":99},{"version":"7fa6fdc6eeeb3850e6b3166836f8c9199594c4f4bed3cee02cac0ca33d71894d","impliedFormat":99},{"version":"e53757f3e6688e8be16b36ccbeedc85637e607353fc67ff7b6665d6005aeaa12","impliedFormat":99},{"version":"88ba2660f5b024c7afd02ea700bc0c677687e17e34f0a94c3256ddb55e0f1c5d","impliedFormat":99},{"version":"7bbff6783e96c691a41a7cf12dd5486b8166a01b0c57d071dbcfca55c9525ec4","impliedFormat":99},{"version":"bd239b4c5e0a4de626d2bd30febe7d4596de481248e9e7436b1029363b45660e","signature":"ab48dcbb58c478548b4a6150e58104a481db9d7cb101c2b670e28522d00cefc5"},{"version":"4ffba3c5848b4fe62ee59b754fd5f256ad9656a0db6d37b9a2a8cb40dfc7ac21","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"32b35cf0dc3a1b1a7118b61c34ce2ad1a29695851679f9ec34e0776f2ece2a69","impliedFormat":99},{"version":"b413fbc6658fe2774f8bf9a15cf4c53e586fc38a2d5256b3b9647da242c14389","impliedFormat":99},{"version":"59e5e964b84fdb2378e9455e4e59405030e4ed2b4c6f891ce395f17796af3cbb","impliedFormat":99},{"version":"dd51e53752b310bd20c9b1a87bbf12b1fe2be7fe40f505b43199496481096275","impliedFormat":1},{"version":"a87be4662442b3feeffc331ecafe6b36cafd08727e2d7f2425a5099577e7fd18","impliedFormat":1},{"version":"cd4cd9220a1ba793bc935e76d8e5481c110a90d9868ae7866a182ee71cdb6abb","impliedFormat":1},{"version":"0a7fb8619b10bc05fd933ca9ac1c8b2ab2220be7a57b57565c3ac158595494ef","impliedFormat":1},{"version":"c4a5f91feb9c5a6b2a91089d959c38391b79a961db3b9cc73b8877d57ad7dcdc","impliedFormat":1},{"version":"90ba95a763101bb61b8a799731a2ed60b5016b8135c1a2d5186862d4b534d4a1","impliedFormat":99},{"version":"b272e3a799aeea8cdaee7ec9c4a808f8ad389517f8bd86dd0f06773701d4ae6b","signature":"9743b09c1b5e0e8385c6bdfc1917efbf55cab712c569a6f70f188e1d83ae4107"},{"version":"e03ea500dcefe6b2cb49aaca7293a978e65714638b7051ba42dd3d3cad167d3c","signature":"f86225e9f566efc6be1a6babfbd3901609b9701e2eb23f8e744cad36d8987a93"},{"version":"cf4b891366fc80b363cea636efcafecc5ba8899817f8d6a1ffc15b42f6a6a8ce","signature":"40229b3b5d4fe2c57887134e413593dd966eda5165bb8f07b5f12830305a3941"},{"version":"3294cd20c885b9037a59bbbd99f3191312cd3d5ec5526fc6a87128c6076ede0e","signature":"8cafbdb0da6961dac0675263cd2bff5e208379e46e6bab52a7a28ded48b6bcf2"},{"version":"812f9799d736a8a570ca380048e3862b815eab0e545ac2512b5366bdcd57380c","signature":"3fb1c16dff3882f5f23a249aa9cb9e81f84a970ca38167f003ee836f7f64a468"},{"version":"8c4055dfa42680fd733cd22ae5f42f3cd7ba07bf55ac8298b3d7fe474e7ee984","signature":"8e767ba14beaa655b913cfddf2196ea151f8dea8a9f92faf04fa5c893ba6aecf"},{"version":"c25475eb4f6a445194bf8c869b3b19b88da31a8320de2e7ff5056fa06cfc903e","signature":"eeba8050498df8c2a01e8c1b07e0d53909b4361f6c53efb35634241c37489d76"},{"version":"458fd4c21d68557326a0425a048ffed886dede7c1c4ebf9e5f6da504d7f5aa88","signature":"1683d0ed29923c3af0e10b5e79a4ec57fe78f03e7dbab1fb69310934fbd59aee"},{"version":"e5e1f95b4a85239946bd42e36205c3f2361074816772ada23b5a2fbd53f36b3a","signature":"f3b8bb7015e732039323fda73fb19ce95e9c1912a80022fba04d8dc2aba7cf48"},{"version":"cb201b98c3065d198799f7aebfdd1780eb0dfda090b02814fee0f0e9c0b206a7","signature":"efa061ceef75231b2409f13c065ab22e38cc1ac1510fd1f24780d7e4e58c5366"},{"version":"1dbed82f8eca30657773685de9401b16a31a9ac428ca062a18f17a64ccca7c30","signature":"775ee4c1901811383e799e0f39f4b2937267d39f171b019fc0f0ee8bef979517"},{"version":"1b70e8278d2e05b92031bd26962f513aeea07e0a7d63eabf1d511b4be076f07f","signature":"afb67696aef51bb54e53c6878d54e4793e9f890c2c48b203478ce89f595ee9d8"},{"version":"d5776f63cec3a73e311ebf99c290eeed0572bad2301f1b977cdccb68cf52e46d","signature":"0135847364b4501d9d090e495007653aa9a112447565c83006b6f80a4a9109e2"},{"version":"c9cc6771bc6a12e71df2b96a8e4fd03977b83c6cee4271a75b130980cfc7adf6","signature":"077b2b6f86556f3ff37ed2a15f154584f97dfc1742d7f203c518fb2f434d478a"},{"version":"1f3f52fb5912b57ba0e0e34440b2b722a6dc3c8acad4b3c2f1cd5dda079cc76c","signature":"d01cf50ebb344bad354f629717b0df05b70b1ceb7c1495dc843d6517c285b938"},{"version":"9692d1cf795afab61e816ce22e3e5a761639f77b3049dbc6cb08d60eeff7f0aa","signature":"565713ad1c2b46ecc073bf2b5851aacfd0ae5f8ce2dcc55b282afe243cdde770"},{"version":"0cfe4288d0b91a0d8bc91a455f823d05f567fa12da4212e5f1406675355b54eb","signature":"42b19d4490e7e8b574de575f7c5bc94b1ae7fcd078a11dcf08bfd4179528fbe6"},{"version":"d6b19489b6021ae2c994725cf982621f6d46640c1586b99aadb1321117619ae2","signature":"dd7ca710c6b3e738f8670f204a10407df7dbf804fede28700b97c476980b8328"},{"version":"c1ffecff2064c7b1b456cb62f85ddfa7928920793a959fdf7f71de99aec0dd4d","signature":"205c25acdf2ba4adb587fae5600804a1d11f2d47e09e78097c66faf0304967e8"},{"version":"6bf1bc92d8ed9c59b8dbd1b77dc680f413f516bf9d5f1d025d635aad177e4143","signature":"7fc7e69470635304be38af9f2007f76aa6011acd79559efa38abb967773b891d"},{"version":"94ebba8928b35d1a353fbdeb74025dd4c407d17a73faac0592e8538878ab0b78","signature":"dae8cb979de93b2c2bf1bca46fef3b1bf9365cd9760ca544577982ee88cd0c81"},{"version":"abca831cf871b2b2ed3b4bb3ed4b47864c01bd970578b78d00e9dbf3d9114226","signature":"3a0500fdf90fdb8391f8dd4ff940cace58f9a0acd79218800acd26b19c592602"},{"version":"bab3d1587718de09a1e93bcc12c9e38dc7b86dcaac5a6d7f570e60ca21f216b8","signature":"fa71eb4c7141d405f9a96edab5dee32ba812c411a258c0f1c10d80d88c260b81"},{"version":"4e70a91451f2d64bfe3c1747ea685d532eeb22400bb6f3b8c81dcdcf07dc5289","signature":"095d613da43e41b95e69d59e961aaee031af28cbc34ef629f4757cb99ed4b656"},{"version":"66de7c3ce2ba3cabd67978995ba2129f5ed9a9420206e03783b477af4781cb79","signature":"205c25acdf2ba4adb587fae5600804a1d11f2d47e09e78097c66faf0304967e8"},{"version":"c8bac70d2d5103104c9a7a3a9a955fd9b3df2fd4c3fcdf19c678b61e68d95ab7","signature":"b87a13dcc4da422f5a27a531684200bf1496221c9368ff750d2fc18110fa6b03"},{"version":"e38ceb5a88442e2cd0b88368ca9b1c79cb695aac4608133dece5595560db4804","signature":"262086a5505f004046c97b11dc9b6a4be7d86a454f31b07704c45f2bd20dd4f4"},{"version":"6a87b56e2d4b8b84f795e027373710a75870f8ad442603e929f9e14695cee317","signature":"13310c770662933330afcf0ee4d7e5c6a445411d288b32c5a868f88147f9f67a"},{"version":"96cfd6ef083f4938581e5610d67e63d844e0af84a16bce03e1ae62911ec08f32","signature":"205c25acdf2ba4adb587fae5600804a1d11f2d47e09e78097c66faf0304967e8"},{"version":"10d45b6411be140a6d4974ebdf78edc36706a7d6edaeec83b81f29b2eef3347d","signature":"0898f280ac5e349557ff3122244565598b7c15db3c7edbaa29b0e7779e4a1ca5"},{"version":"d222e58369c32b801e13a0b07f306e2e631f9fe9180c4db98feefca66dbb1ac2","signature":"fb25775b3b984a230d05f73e40e3f82473564613297ba15c53628564b577f297"},{"version":"cbd96a2def959b9f11a0a2fa649999da0e9206ff01876e5c146005d99422749a","signature":"020fa99160bb90c72c0b7819af914276f7571acee5bbb16c2360470326dc4a10"},{"version":"6cefbdc7dd11ab4acdfffb3db985c4c6be9e4a3beb36c8c340a1e0377582fea9","signature":"4ea96a8d072dc8eca5a2070073c5437c551aa6592200869aab3fd78514011695"},{"version":"899ddf4143cdf47d7ebee651245c45e5e55fbb88b158530199cc56876f070171","signature":"f94f157ac1f1410d2f8b852463a465dd25ae40d5d6f04fdd40362f51dc30f6e4"},{"version":"9ab9ecc1d60f52844aadbcc81c668ce4ffc2b92597b41174cf72c21736a56a32","signature":"64de59a0ac6bf2a8969811475378f6d66219433e2b70fda84e482e65b52cbdc3"},{"version":"b28d5a3f2c08ccc5cfcee154b5d0588751c88af0fb9bb2715bbd2afd4abad477","signature":"64de59a0ac6bf2a8969811475378f6d66219433e2b70fda84e482e65b52cbdc3"},{"version":"6f4fa8cbef0e122a86b76b69f34d68fd9bc31f4df5ddb04d6914b13ff462d603","signature":"9523d732998ffd7854f4f49c08fe9b376a326d087486eb367b28d9e08167e66f"},{"version":"c1d259cb6d24eec7c37892378ee9b3598f2b01c2bed572fad8cd0d4bb9a87d7b","signature":"bcbecde939aa17ff1ad4f768a1aedae3130b8efdee2276c1a32b7825722ef8f5"},{"version":"2018384efa86247beedc65e0d3725e88a40182922789bc6107091252fa347332","signature":"75d858a79b0fee3d078876fc73598d94930516a6750eccd670cdda097cc9a903"},{"version":"04099b3dcdbb56aca78430232bd01a142d8ccd1d0d5e30b1d33d7e56123ff0c6","signature":"260d086373dd742fafff87b6176c58c9804174780ee6e2a9aa4644fe5eeb8152"},{"version":"c6d96919a95560f356d69163ed364417aba29eca36bdfbe641c64ae010a2fea8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"53ac889dae56779f28fc4abbd9f4ad6f6650a00de4fdab9373752aa19f3a83d7","signature":"148ebec4be798bc5d3d98118ca2c019b5e72025959eb7ec675aca562ff898ef9"},{"version":"2004a66dd216857f34572c9dd12284f232a0ee5dc7de362c223503a0bd0508b1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"355c0a146f28d0652c412b6ff956f4df39039d53249cafa903a522e392911e0b","signature":"9aa3fe4cc9292e78332bbb169aebd989aba235cb662ac9fb1293a2036eacdb0c"},{"version":"ba369457666c166c3a16e9b2ed9e3bf65d6f477f75cd6f7d65fbd204e112ca80","signature":"bc34816b820137abb9de8dc7b9aaba13cdbd734eda3325add0b9786a6c5f5ccb"},{"version":"935b69b2feb087557c51b46fe03b4ee4cc3fc564fcfdc4ae7bba707c476c26c7","signature":"4e76afb5136973cd8e9c754befdd30b7c96ed969b211715b623d70b5cbccc956"},{"version":"730bfb5a5fb466a75cf0ccc8c6e43dcc60228703551d364373c3172460277e0e","signature":"5cf542b959b9b35bf145253c7c4f41173aa8d53f09db72f92a8e7f2034b8b35d"},{"version":"8b2aed6508cc5ca9007fd3cde2d89f00d320cfe8eabdb8bdb8c123f49709fd1c","signature":"9b7b627bf904866dd113be75bbf5803034dcbf33f8a96b167c7c0a9929f61e2b"},{"version":"8102299b88e15f61194c06ae8809f21aa27255883429b8cbfa6f84fcc12060f8","signature":"f8a964b5cf0e1ca241d36817036555dbe9e57cfca0bfa738be6b0c4f7a0cbcfd"},{"version":"295aed2b2b97353627b22550b9c9d0b06a9e4b3a96ad428982c7c22dc8ea52a9","signature":"41ab1f2a2778a537fe39c5f2b1ab5b53666fcc71592c3af8b363313f40bf1bd7"},{"version":"ea9dfdeb40b0072df42d8fd80ba590f18edbfacb19f2ab05f33acb51b2a06cf5","signature":"4e76afb5136973cd8e9c754befdd30b7c96ed969b211715b623d70b5cbccc956"},{"version":"df56f1a082f98d1866832821383deb4061e7bf09dc3c5a3e6061295a1906cf0f","signature":"ed09dafdbc7ba5b5b4b82c95bed45443149fdc510cfe6b30a80057641fda7682"},{"version":"25d9febbc258e7c26ae84862377ff23df7e8b8b6f2fa5b413c4be223af893a0a","signature":"ed09dafdbc7ba5b5b4b82c95bed45443149fdc510cfe6b30a80057641fda7682"},{"version":"4f7f63c16c734450893215eb924743b3f9c8bcea1117a805f2a75bac80c5fc1c","signature":"0bdd54716d032784b95ae26685acd82e3e34a8734913ae46cb1d75b0071d6a05"},{"version":"a933f158c2f98c537d2e71960c209f3e2c515a28f1060d1ae420d075da9f9511","signature":"a945e6934c261e7167243cc28a215e034285ce6a895248258ec7996e95f3407e"},{"version":"11b39fa38957ff6f663dc87b624734828581b96d6c1c504bb31e7c0cfa29b41c","signature":"bc34816b820137abb9de8dc7b9aaba13cdbd734eda3325add0b9786a6c5f5ccb"},{"version":"c1a3274217d9aa5fbe87143c3d39e091334a0bfa1a5838e07ef35184358908d6","signature":"4e76afb5136973cd8e9c754befdd30b7c96ed969b211715b623d70b5cbccc956"},{"version":"9e65ed81b3be9031ddbeea238ce8c24454fadbc9f461eee557ad76692d41152c","signature":"534a436dc68044c75d5d066e127d162d7226f4101480d7e83953989fb8c0efe2"},{"version":"969977cdbaee08ec192bd1e1bcb9718e88ab9c678071d1f95bddf58d34a8fed0","signature":"2e8c88ad9f67ad39e58a3925c6c953e87b7c32e860869bd7d8edd154fbcc88e6"},{"version":"599315a4ad465389616b86ce7cc5f546fdec6b8efc9deddc9154276812622d10","signature":"f7a9369227ca316a380700740ebe99f7d4c1b5d405e0a127a4440ac5644d28ed"},{"version":"f659763594de3a4e83ad522c09df767a7c253da9355253bee3e4ac6d41e7c377","signature":"e1b11bef4267ba38aa0f555c825741764678e377b63be43f973478221bf3e185"},{"version":"87fb31e702d20d1c34a1af8828771b0e7fb7b5ba8d6859870c9d6f0367475d75","signature":"1b3cc3332552772d3bc509c5559b19d896d4fc628e643523d57a7bc30ee6b045"},{"version":"19c9bd5320aacf8697ee1386450f1ccee7eadfec809db01fdf1f7d76a9d1b864","signature":"55220e35ad7534ced10e0348efa72f8d8f421b31b463523a4d4f2ca004196112"},{"version":"e1a3b69a5aa8ac6c231c3f80d5d7fed327b734351cff7be4a2ccaf9b4c9d9526","signature":"53898de07b15b44831b6def13f9864f7dc6f30e0e6e4820ba4cc02a7af753988"},{"version":"392c02f9a8617cd6c3346885d164b3732bebae9c0d933e190915b62f25d7af8c","signature":"9798fbd8cd02331b5c2812dbed806a71357ac7ce1dcd32af7ae1b01f6527d547"},{"version":"c0c6586ee01734ae247fe280b7ce9456a87d667c003faab3b23d62181afe8916","signature":"57356f6a018e4d0cc688cbf82ba9d4af28f3357ba98f86b7ce95f76cfe80e7b7"},{"version":"a469403c6604688edfda3fffb4c0144c0778b2b3abc09691b84b3dd2c3beabc2","signature":"7b1a45d3d160c62e5dbb548c024b4c65fbb8053d0eb158805290385e5eb89bc3"},{"version":"6ab102a4996a8be7c592696f0859cb6ad8f4b5d2de826f259505bedfebf1d21e","signature":"a87dbf641869c9e1ea39d817fa229a89efbbe9a9702d5096332863968274461a"},{"version":"98b0c6bb58580b8c0ff1efe485ee15b577dc8d911686a09cbf8d66fe82560129","signature":"786e7f0024b87371748fa4d509831ea62cd5ba37f605ce5c99092446717a4e89"},{"version":"d2147430a68bcb31ca2938d2e215c817a6c7a10175f66e9fa9151ba73646ff66","signature":"8f06ba0178d88d96af1cffc66ec281e0a1ec8a316172fa58ff9f39e7320de4c2"},{"version":"419888a4953da818c7bae3c73b04ef7d6ac2e6efbaf73cb9e9faea2df213d29d","signature":"8a58ff096f6ea1119d5b93e472b0acec40a8b55231da8f8aa98cf6fc9ef42a90"},{"version":"06fe1e12e8417d2e18f39cbe1191383b15d0603146c7be2b09e0004947de22f6","signature":"f16d990d89aaa952a1ac95e7e50af24c94435a7d6268f7b0e41e2523161c05f1"},{"version":"a71c2aaa370186f7677bc2b0d2f58bd0cc82236646fdd10d1d9be9b44262c112","signature":"ed09dafdbc7ba5b5b4b82c95bed45443149fdc510cfe6b30a80057641fda7682"},{"version":"7b74e0a3390239c17f9492c692090a722fbb89e775c2595f531b15c90e051844","signature":"ed09dafdbc7ba5b5b4b82c95bed45443149fdc510cfe6b30a80057641fda7682"},{"version":"3d55ec80f5ec2d43c6bda6e26d50a03a28526e94c44812e650c8cfbc61db3bc2","signature":"f2a520df9e3a19f25fcee3dba47faa79d40abe6031d990639abfa0ec08fabf5b"},{"version":"9431b43408d7828cd504cdd7060e6811c16eb95d62c66fdc2f570aa8aec109de","signature":"cb5c321a6b8db73ea27cd92a8cd6e31e8541144787246f6810e6640b25c5f735"},{"version":"b90aa8c263a933c49bde4e13eb1b9f600e81780b11bb118cb92434fcffe6b871","signature":"9197a64fd63675117c7da2482b0b305208277d87adc9f03fdca66393b3bed6fd"},{"version":"0466d87ccb5e10975ec359300da6d7d0c05732af752c09323cdb2521720e62c5","signature":"08875cceb85ca123216d7caf655c1343c6a21d553c9113c9f2d7e5b742e3a1c2"},{"version":"8049e0c1041eac5a8f6c39badbef82174074d2400cb384bf43b1224a638ca9dc","signature":"e4def3aa6de8b83815c6b843319a70823e13715478bdc149ae037f3f535c310c"},{"version":"dc11316646140b005fa943b24c17cc3211e154412a46e88709c617127f0bc115","signature":"5f720a1ac3fb17c07e2af678c2d41a832fdf9c911ecdf9f01af698960b1b508b"},{"version":"869b4b7def0bfdb395d882217b89d46fa7c969ea63e77539e91716b4140adaea","signature":"e4f9208fb378663fb93eec036456f0bd0e46c5236ce5198983008d21766db5c2"},{"version":"079701c7c19e774853dd61341a8d6058fcda51bcc5dab62634c469535ffe2f7c","signature":"4706a28c7039a784600878f3ece50eb5f26a3c0c364639ff08a648a93c389055"},{"version":"2252c7faec6ecdeaf63d469f1bbcf4c41a9491d26d88042f2e55a79e2a4a4230","signature":"ffe0ac73457ebca82bce457532777b466962bc0eb2c7cf865e2fb418783fec71"},{"version":"347ba04c4ad8e28cab7b4a2c524738ec3f468b33a8dc61f0bd204e3eaccc4157","signature":"63e479054333dbaa2132f0f2a5183d73f583452abde66b611b4db6df1768aafb"},{"version":"0f6d94d11ca6352ee795b5ffcf40169f9d6949a3d0b89ea614b5b907f52a75ed","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"af4833294b8c3ccfd5f2f5b38294d442739508b2dfe4aad3fea60846c5b1690f","signature":"c2304065e85d2d3b3536abc26ae51a5de83050d41df32d5efadb2717f3ce445d"},{"version":"3e27c7e87ee43b2fe8c3ffc12d2bf3a155f811d971d6388855c3ac04f45d2b81","signature":"dcbc54188548941418f465ac57a17d4a04c1f7614225805b467a98389b14efcd"},{"version":"2ce65ef4a159eb4c369372109e4244683e31bd4ff26be1268985a694f6190ced","signature":"450d7b6084c1ca2b5b34aa4cd29fc1734dcdbf1c16055df4299400eca47b0873"},{"version":"b66427864dff365d4acb72e472cf6fec54f1495ddaa97e3ec6653eab75f34d50","signature":"da3b4b6318c69ad102718e8adc70e8c766067b69a82d1caa5d12fb65d03e26ca"},{"version":"e9a3a708a6d32e72024ad6442b1ae6444537ac5602379bdb971af93184f2c8e4","signature":"29b5ceab4dbd9ba22bb10122bae1e28e9f899fbf1f4fa86f0dba766fe0bb75b7"},{"version":"37861565f8b2c418727d9658998824c93d6a13dee1b7a8f4e40dc321e525a068","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ceaa1ba2ce9042a8f9cb83130383905ff16da22afba9adce777b6bd8a42fb721","signature":"e3ce352dac45731bbbffd2777f8c241025f704ff7c9c41aef5548216e626ce74"},{"version":"03e8746fe42e459fe3a37c5f8a5e3de30afbc5ca0ae4c7ac32eaec1ac824831e","signature":"3223e52cb57685be80ed2b56790d8fe895473b6feb340b00edc73b3cb9384d9d"},{"version":"26130e382582796984304f2fdbb260f85334bda00220a9bace4f0714b05628b0","signature":"1fbd2a3b7820aff007e8433a0412bae8653982032e5b0c8c9783d7e9539990d6"},{"version":"e78aa5054e49172cca1837b7465d9fea4e02b53ec9e99d630e651ee6a7fa091d","signature":"4358f1ee288224a3c4f989226bc75f831aec4149dc646c6b0a1a1ec79e19ae08"},{"version":"5df54120520aa61969b28350c3e13e018d73253963f07d3e5b8480f6e5ee21ee","signature":"4e76afb5136973cd8e9c754befdd30b7c96ed969b211715b623d70b5cbccc956"},{"version":"2b1a2590469ea2d5f44c8909a08a449a269d01ca1a7c0fabb367c5bc28baa52a","signature":"944d09b5f167e7b016cfa9cc66f574553674a1f643bb7000752c9d75480f9622"},{"version":"c1b32a30289157cc33dbb9b66ccb96df89ab6d8a59bea1a8ce2a5981231571a2","signature":"6a5e7eac890b886cc3f44825a99ef4403903dd83ff5351d491acbe3d0a81248c"},{"version":"369be937e86002ff2532a401e2a4984f1f91c42a1effe2b97b6506237b820b8e","signature":"6a5e7eac890b886cc3f44825a99ef4403903dd83ff5351d491acbe3d0a81248c"},{"version":"56fb3fad4d94088134313449798ff3f92c5289e65bd8f87a18838b000ac94618","signature":"4e76afb5136973cd8e9c754befdd30b7c96ed969b211715b623d70b5cbccc956"},{"version":"edbdb6e286f67c9151d6b65816d30603f134683851432c83f728979a50904742","signature":"4e76afb5136973cd8e9c754befdd30b7c96ed969b211715b623d70b5cbccc956"},{"version":"d4a84657e7b960484e8fb28aa9eb3ddc3c27199a14b6d21e72ee6e82b317ebb6","signature":"4e76afb5136973cd8e9c754befdd30b7c96ed969b211715b623d70b5cbccc956"},{"version":"d706bdc5114f4af519547893d5f45ab84547ae27812ddf1b6c51e9e2fbb94422","signature":"fc8379fad55a5b18797da7f18adbe1c88c9110ba52c00b82e517237d2504cbdf"},{"version":"cae7010adc0355c66004bcd88bdd4cb6245d0c67086d52e43ef18479e8126971","signature":"6e26a0555f6d16c4fc509597b7aaa0d580dbc460f72bc6c390b5c9ee30122b10"},{"version":"22b4f46fd2e8a1d5c3d466c80a1e0eaba1100f072c4a5dc5cc232ffeaa7d9779","signature":"ed09dafdbc7ba5b5b4b82c95bed45443149fdc510cfe6b30a80057641fda7682"},{"version":"9a77b64a695927b45bb880f0ea155d0081e768d4d7322a8d78f0ca86ba9a0c2a","signature":"ed09dafdbc7ba5b5b4b82c95bed45443149fdc510cfe6b30a80057641fda7682"},{"version":"da7ca321f57f9fa51656088f95d0540087b58c37b224e2c02df274c9734d1b9c","signature":"77ec69dc11bf278733756b7952aac2e9827e7537e49ef2f6a404560455c5cd68"},{"version":"250f08b35fb145bea8fe00cde3d712b643e7fef2d06210b6d55628b092544104","signature":"ed09dafdbc7ba5b5b4b82c95bed45443149fdc510cfe6b30a80057641fda7682"},{"version":"e0a931847222293fc54d5462751f14a86612b1fd52db4ac5504011b7064342a2","signature":"98d76f8e5fc4e977a068482f67f8eda9b058b07bc2e1702d064a8b870a886d99"},{"version":"a77158331aef3f7337985eca3d93de65f570d5f5b742e64a6cc9aed042ec0868","signature":"3ed58601b803cb17cfbc86f1899b93cd012631856b3296e1fe35bba90d45c052"},{"version":"e65aef24492f9cc4e1c48fbf2d4855e7f15e635b9ecf396d2864c5e68b82d824","signature":"47d45688c2fc14a365f14ee9a520c403994ca09e691035269d44a12020e087b7"},{"version":"a5432af4a8ea62acb0b14ccf519c43a0894479d88c5567965aacb82c970cbf1b","signature":"2a4aa1b1ac81111c9a6578e6aca276e2b3e2dc6665a45c1f12790e43a4da44d4"},{"version":"490950319536b3ea28024df0c91e59ed2bf20cb0cc8029ae1a42df48874ab00f","signature":"7e763eec61ddf51f6683cc0f4fb42059d93de2d966f334d70eceff9cf41718d6"},{"version":"bbe06402b618583126f38f890106ce4ac34580f1887e45cac0b4a4634811b067","signature":"9630a0c19cca6193171e92b78c2dec9155ea34462bafd7cf5330ecefef3984f2"},{"version":"9b5e48dcca66f3c780a81a760d7720114d6b1f6b77084c06a11aedfb0558401f","signature":"04f731fe5f70bd049b1c7682ffbe46ea9b0d5845e4244e0639beec4dfc89ed7f"},{"version":"385b814c0666e0f401a37735e60ff34c1a1019b7074b34421701a590375170da","signature":"ed09dafdbc7ba5b5b4b82c95bed45443149fdc510cfe6b30a80057641fda7682"},{"version":"4d28868258b7208be3f4501d503a48863bf3203ee75aed2be4a73a205fe3dea6","signature":"be74e7487272d0772891b0ea2b337cac457c7009c984398965027398c293022a"},{"version":"e452758dc83f5d09d9d3b71b8c2f130ce9b71f7e687d8a1271b00bfc11b222e3","signature":"1357984c5669c5f78a38aa61aa4990a623ff8dfb05640b557ab1e25bce0ff4b7"},{"version":"4f0c190d4d71dc49ec4ef696f6012d328f1edba0215180f7e84b1193c381f383","signature":"7e3e0f27f1d33424de9694e9f598f897ea24be0569fda90d64fe26ff02fae4b1"},{"version":"7c70468076711abcee49afe4e6050546db72705b31891b88a280f8481d40ccf5","signature":"ed09dafdbc7ba5b5b4b82c95bed45443149fdc510cfe6b30a80057641fda7682"},{"version":"dee63b3f8f474fbde39f5292ab35eb2ffc7c3081f019cbc5ec2187ce3986da53","signature":"3dc1c94fb475f3a6b317921bffd54558f6e25f10220cff2db9e42b0086271ef4"},{"version":"f267fe19d061d8fa86b74575702124c0dd2c10b84aaf35c21739e794cb185e47","signature":"7208bc51a067f2e14347812e036ab7bb948949808eea7a2c25d7f49cd33e7c7f"},{"version":"de261b8a4a72077f712671d730f534d6aafab8a2d4a0ff2056f92dca9a2793a4","signature":"00d47ad8341fbfdf10ae30d294b17a55501168006a0b6d1aa745279db7ad4a75"},{"version":"55f60cc38dd7beff43f5d3d5c38ae4584731fa22f2f1f597fa2abf08fe701719","signature":"1c811eece933a18376a680764b027b5e436250f861682717065fa36156922e8d"},{"version":"1f5eddf848cf33e746449ea7d694b7af9bb1c7042e8c906b2f20cfff9ed7c09c","signature":"f74b06a18f37efc12fcec1fbf3afb33c01575f00ae23b6f5c23a7b6831fe0601"},{"version":"51b4fac026e0022c770ceebf8708b1d1d3035ea6951f1653521606b6e1bbd7da","signature":"1a3509c1d3e8649c9b8762925be770683c4652c299873709c3db397aaee932e8"},{"version":"fe64813e84d2c7a8a7b732dcf73d676b75c11ec055cc2bea0a6eb7e7c72eedb0","signature":"3e6a7cca0b67698df362aa0b0feda4f2e423d5b46cfb9cbe153ba3ab93c613f4"},{"version":"99bfa019855a100dcf3d356624654b8c6fe2fbf053b2d5ebc8158e08d0dc079d","signature":"7230fd5614f4e2e620d7e5534922dccc87ac1e999fe672fa51d7ec0ec269bed4"},{"version":"1027a91ba0123b3119a8f6915bfd669f4813a2a33775b1a99a2a2171b7b35a12","signature":"68cd1270d1b44578cb1d18773b3ac4b24818b5ead641855aeaee6d9bb930eee6"},{"version":"04f5f8440d34c408b470ad922625f5dbe88f3a256a04e1543f0c9ca4f9532b59","signature":"454b6958f42e585d606342665db36c97a34fd03ef4e12962df93c101260b0f48"},{"version":"d2f14ea53465caea54d4d584cd3ef68aa53534688839e3c0741205f9bae3075f","signature":"552859b9bd3660f4d64c130b94c76c8213b197b6f1d68105626cfab2853c40bf"},{"version":"9482ba9bbc2a3246f2dbe54d86e66b8629636257c5c1e2b870c81864c22ccfd8","signature":"73acca63f9d127dc2eada9fe1e34bf16d2956aeccc0272f3e4c8b057d8a96631"},{"version":"c88824902b56b56687497515714d42f1ff7a915baa20f25d89e226fcba6521a3","signature":"89bcc85e42dcac0037cc487bcef532d61141235d03f31f6d34b15f5b53a760da"},{"version":"cd2f0a27eae993220125b549baa6a047216852316d9625e87ab7e1c9b3dfb2f9","signature":"cf0d1977eabbd78aa13c40d0bbbceab2f5a16ce53486857ef94792d5b44821eb"},{"version":"2943c2249abfe4edd6a8c1731339c2737fa50fde22d4c710f2cea3e917a8860c","signature":"5e1187a7a825a77c37c327ae5a7de71102937c31175e633e618d31094a3ecf5b"},{"version":"33074f64be05338c7fb1ae89639ab9407d504c94d0d04f8f74f2f70c6678a997","signature":"e1414c452da3ebc0a70ff10a48fd9821cb06cca69aa030e109b806d4d106ff9c"},{"version":"15ae1ab5a3a467251aa3f58ee75f2cddbee32c98e3b597643577f538c2278769","signature":"96f99143319c234b768df03636e2674b787f4e4cdbf9e7c56947662f8144d12f"},{"version":"65e527e36a23a080d7ddc1636336b7d950112ac730360e18a5f7cc5f26977a7f","signature":"a4c5ca81dd6d42310a36e8ad5ed90ccd2288fcbac6f859917f5edec2b7de5d5b"},{"version":"5190600f2dd216333ef3aabad92da2185875315911ab5a19df9f23b809a13f31","signature":"a22b2a342ea7f18daec3d8f5f001deab95554b88930a7eafdd778c515847d82b"},{"version":"f8031790fb56b71ef1b501ddf066279f87769e741795fcdcd2c9d229ee0548b7","signature":"dc6cb8983fd0f072b86d5f6d451ed47523a3d7eca8fb08d64be6b6ab665c4cdb"},{"version":"731440639cff59b0c13fa28235b2836f378e2071b7443bb8b8361c299b699efb","signature":"50f1ae1749aa5aff6e8f83bb68b4b7cd57b36162bc3b17f09f78cfbd9b25ea40"},{"version":"d92adc7b952212d5d29b499602af59502823954f00c7e94ce2597d264c5c7c39","signature":"bd9462d89069f1a2dd735a3cc856120ecfa20c41bf1a019248d046a87fcbb560"},{"version":"db270b9c29fb25ef1c64fb9c71f050a8178f156ab69782a3dc92eb3820248145","signature":"19a01b4243aca91ffd0119b172bef715962cd440b32158544776cc8f2516d863"},{"version":"03a6589b2ca2a81c8513864bc8fc75eb10cc20ab91230cf9b62cdc8275dd589d","signature":"8ca649ea98705580d2ede60f727bfb9d0e84aca5de7a0ed8c1e532ef587be790"},{"version":"3e31fdc229346fdcaf9c18de949e531ab48ddb7680c8966913f1c0018f18fc12","signature":"b4e3a98b622e0df12acbe0c5c67c9985ef290ca9a5b3877437f1717c6cecd8ce"},{"version":"33dd8a66151aae844148c1e4d90b3a993c776594bc19ee9e772360e4affb48af","signature":"b917933d8465f25e06e9777f35f1df80047c39ed250aeeb164c07c8d42184f19"},{"version":"cd13fcb9c9fca230e5391b20312cb60c56b3fef1ef264e95b574133987250586","signature":"adb11d6fb416e5e7c1931812ac6bd4baca455b110fd8841b4a6905f9e299d08c"},{"version":"609bd5324d58c6d8a903abae530e83fa8f64d0816ff82e8eb8b2471d6d1a7373","signature":"b71520965bb4a5223a60f774275465e046c9e8b74532e8b408dce576cfd4e22a"},{"version":"2961e56145cdb9dbba7f8ad63e57a426a3aecad9c71922f98eafe47c3f1d0b82","signature":"d6b2d87eab2f22563b8c07a2a4d61b9d0d8f43f70e70058fb19140f1ced5a74c"},{"version":"692bc7a0e0aefc4921352890a79aeadc4a68f45c5171794fd0a0cea24d535b34","signature":"d6b2d87eab2f22563b8c07a2a4d61b9d0d8f43f70e70058fb19140f1ced5a74c"},{"version":"1b78ae8c38c922360d4c70325cf83e594f76e391e0d6bc39b0697c65a30a3a1b","signature":"666273a11838a8e9ba821630e1ce06fba622390cf7fbfc362ced4d28b5b449c4"},{"version":"f4c24f58866aeb2dafbb1233d5213c323b66fb731f8f018c0a3440a9f255e75f","signature":"d6b2d87eab2f22563b8c07a2a4d61b9d0d8f43f70e70058fb19140f1ced5a74c"},{"version":"9b1ea7c38717882a4faf4d7f799b94b92232bb5ce85125796e40f92d00108e67","signature":"d6b2d87eab2f22563b8c07a2a4d61b9d0d8f43f70e70058fb19140f1ced5a74c"},{"version":"77bfe2af58b9dda5ee7b11b8424b6cad7e7745ba1aaff7bf58657b7a31fdf87d","signature":"d6b2d87eab2f22563b8c07a2a4d61b9d0d8f43f70e70058fb19140f1ced5a74c"},{"version":"7d388add520ac301d30cc97e1abc2d7beba18081f000dfaf86ad98bafe6a3b8b","signature":"d6b2d87eab2f22563b8c07a2a4d61b9d0d8f43f70e70058fb19140f1ced5a74c"},{"version":"71ff0a7cfba4209a055cf9b8abd8cb0ee468e5f512ad8163c07766213aac64d7","signature":"8c4ba97747b48c2827af6d81bd627f5aa0e4d893c679e35ccb5288bf248a6352"},{"version":"bdbf2547eca0e2036cba7ce613c3dc74015c6c6f9b54ea13021db0b7ad7866a2","signature":"58b1b9e2d815c3fe1fe1c21eab8970baf001555915af13311132e6fd863862e1"},{"version":"a311b1b2658fe549b2c47ca398267994bedadd4dd3181d7e7a3cffb270ba3d24","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"954078297f1913ccec8bc3f35957e2a791dc4189f93bbe49d9df0c7e85d8f72f","signature":"779cef8804fece12ff351c25026638ccf404bfe69427fa0dc1395d2c21365d5f"},{"version":"3db978b42a198aeac30a981735a516c70d2ac7670bb666b7a0297b64e8ea4c38","signature":"c53eb0fc2e6c3e0a588b039bdb23874a3171293355c528c8891f33000d37b344"},{"version":"9f69c6f9b97ac89f0eb75173e552d79367b11c9e4fd298d5085f4eef99379b95","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f323348a5afdd00be5315058c09006fe38ec3cbe691c8075f9f9e08b297038c4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ddbe676b19c8a6d771375d75fd9de2cf29784bccf6af23642018a760cbbc66f2","signature":"bfdadc790561f0934b95ab9837d8949369dba8b55dc3f03b85e6d7e7519c8134"},{"version":"81f3844936e2e0d5e3c13843b0dfad3672a6796ad07cf6b992d314c6c8925d55","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"4a3d30d0e26621083af87a9089459c20ad50b2c5c352c8df443ef7062104a97f","signature":"3ac80ff1fc95461cffcbfe3eef686ebeeb0d3127e9b73ce3f46eaa5e73bff9fa"},{"version":"316ca7f124aea8cbce26ac9d9948b4ee6cef0d409206d698053b62da687c2b6e","signature":"66ded0caabc44fc1f04181f912fe4a918ec971ecd021bb8790ba119cf095f54d"},{"version":"673ef52bb7b54b8e20eafe7b7765922a023e39754e21ed762f0ce55b19268d21","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"efbe6f29c56234634cddf69a318308620c714519fa65f153205ed2ba4386e2ba","signature":"4cc01d45183a43fa9402ec7c2d5e9fd4733cbca1fb778a5132b313b08bd43f64"},{"version":"71438a74370b6c0568ea3eee86caced05d220d748b8f336cb7be512e961674a4","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"7f964a93d2baa1e582399fab033e6746c0c61b78a97354e0cc4b3273f23c3955","signature":"e26951d48379ee5486459588f41346c665351a57d7465f28b0a753a00ea1ef04"},{"version":"b0f6e812446471de0e3fc28cdeb7ee1912a9be2018ac3db3a47d9eaad172e204","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"eff044f3046372415bfb2b9c83e7bee5bc73a9a2ebaa7632de2573f1006b0215","signature":"96223fdac3aaddd6718243f127330e50898d12240336e53725209a1f6160db67"},{"version":"16d3b90751bcff1bbd53309e05a696267e5da68539ae7c829fef13312e8ce858","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"dd8c1009b6da82bfa48d4ac37355eb64ca0597e8d90989f586198370324f891e","signature":"8cb36b3c119ee6aac7ccde6bb4846f65ceec0d0ffcd7015d698a27be88e7dc13"},{"version":"4f4935b9d88ca02b5c329f5060714873f10695a2fc934a43f8ca134c1d8329e6","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"6e813effc479e7c5da7f9f4fda2df90f44e091db140e912c24e865dc16e7f16a","signature":"48404e226478f761ac667cd55a81db0e53bb6c8ec85871965b9f953f99262193"},{"version":"6ac8190d670c15f82d051cace93a0c02ea2922c29ceea1e14105e29402a126ec","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ffe99eeac5ec93ab900388ec991e6f1d23050a82c8cb13c41029ac9a6f56f0fa","signature":"2441bce11801807f01148eb125f06eeebdfdc27fe82c59927bef155a404217b6"},{"version":"d151733bcd9d22e12fe89b0a59bad214872b2d561afe7fc0a727901a79ccbdb1","signature":"5839809854919e435a02ab674580cfac5a4110e68d9012d333b6342a573e513c"},{"version":"ce0f53f2507c6fc7e80f909d9ce6fc472e02051e7bf68e4a2153920f7383c54e","signature":"a6c5e1aaa9ba5bf7d96ebc35efadf2037365513647c85a36191c45ee240f0ffb"},{"version":"10b887855abe1e953fcebd4b00ebeec6215ff647ce21cc2170289ed3fd7d3178","signature":"81e6270de560c94670153420d8fe8138c262d2ca59166a6abde4877d38707892"},{"version":"e018954df237ca8b8967a5dea0c84af86a19466e19ff3f0778bec19eccad032e","signature":"2d32e1a48972c582964d21ad5bd38476d26ef7c6c617a97d80ddec325eb2e4ee"},{"version":"d63dbab0bb94c2369a965f7e51dbb7293e4c3a5c742cc9d8841f67752ef16aeb","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"c1c50b3e60aada5b77daff7bbaa62810e30e70945a56522c0bd8827fa847f38f","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"479bcf8c3722520ac008b0fe8f09f1f0b88c14a3102fc2af253792a2fd6f2fe8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"670660f49dc3d32dc91008561f2267ed4f33da85285a1fdf87233f7b4f4e67e4","signature":"c91ffd6a07045de69143ec9cab72da2c192860ed0f12af402a414607ec0fb8be"},{"version":"e7e722f3cd11fc9713d2a76cbb9972b478ac3da86e26b44fdbb2891beabdf274","signature":"eddd46f41795ca97be1bac692ac037df492d4f6afa5a8824bbf00887071ebce5"},{"version":"a76f042d64f8dff3c5e1d557847dd12560cc8e7fee996878e52ec1c92389a3b3","signature":"03985eb6c5ad09ef5151cc758c2f1034edf94a92c5019f4ee37533abc36da13f"},{"version":"1bb91661903136ccbdf4d901894b13a46c4658e731cf7a9bce428927914e7929","signature":"91033635321668186ec611aecaa9293fc79fe386cb723509a65eccfa4cb215cc"},{"version":"f06787f3b88ee9888789cc9642c9da59add04f3f68459a9a30b52593fbb2fc5a","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"450ac1044caf88bf11998dc6e730cefe928e6795f9d03a8f1e4296812e530dc1","signature":"6255be78f5b1013936aa33219685728f58361fbe89f4eb9815559aa672bb0d01"},{"version":"eddeda337c8743eecf8f9272a367eb23549d6bb4fb106f6d00a4511499f8f51b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"21ac4d7b2fe2a8fc098acd752db64bca82c5e86db9a0c6d77e95f09e0ee0c7fd","signature":"61e0696065b325ed71e05401bd01a88916690d1664db0fbbc576e6a6528d8822"},{"version":"fe93c474ab38ac02e30e3af073412b4f92b740152cf3a751fdaee8cbea982341","impliedFormat":1},{"version":"d377c38b3921c35350c9b50ba202d5452fc7a21a0ad734b9d3f886f021d9ce1b","impliedFormat":99},{"version":"54d9695325331b7549212c33c8cf84d040206fe7b58c8c849a798456cb45216d","impliedFormat":99},{"version":"54d9695325331b7549212c33c8cf84d040206fe7b58c8c849a798456cb45216d","impliedFormat":99},{"version":"476e83e2c9e398265eed2c38773ae9081932b08ea5597b579a7d2e0c690ead56","impliedFormat":1},{"version":"1e00b8bf9e3766c958218cd6144ffe08418286f89ff44ba5a2cc830c03dd22c7","impliedFormat":1},{"version":"e8cd54078749c737e2c39bbf5577d7bbcbed30cdf28e6f1cc24e550c0f7b92be","signature":"aeb923c4b9000d637a8d83ad84efcd4b297453d95368833e7713963c675dd460"},{"version":"1ac71842b15c610aed71e7376b1e8a19b65dbb7ffff51b646fe404ce1054673c","signature":"555a4eb7b23abcf5c4e7e06976191f275021081b8e95fbc361e0301138639a0f"},{"version":"2a30c38a4687c848f0fcb6ed9adc9bdf4d2103f3ea44047eceb1976c95cf0a8c","signature":"442fc65a2a0732a9c559e7689f2c25cfb6ce646a2b5f218cb9a45a26f398ed59"},{"version":"0c996be745af11ccf07b28eb3e007aa4277d01b16f53259cb355daafab167abf","impliedFormat":99},{"version":"27c1b57e01b13f914e9d3507202036d676222057d6fa1032332e2954296b9d45","impliedFormat":99},{"version":"7348c35ce10f8323a515ea6a6c237c0dc7c3fd5e491c4dacec2a7b93f0dfcd26","impliedFormat":1},{"version":"258e67b2638408b67fbf5fe6c953b8f2dd2a69f9171d31c9a976cd2329716e4c","impliedFormat":1},{"version":"74137333f57d632f001828cee60b2094ec332abcef1aeee18a0970037923dcd4","signature":"dcce64474115c89001de4c463109080539b325c44858306e722b15cd18d98219"},{"version":"802e7bb6cae1c72dada49e132b6b28eb3f7afbaa27b5ab266b92b662b5327e47","signature":"4a6858702ce17a29995b5e223489b3481cb9998754d7d409dd97d00963d76e05"},{"version":"e479cd25cfbd4bc91642f4b12f351d658c484d1a7b70d0ecfc4a52697906a045","signature":"ab02b9b1f41df13cd6eacd101dc4cccf32b677c5df88f9d6f9d58ec9fe648a67"},{"version":"34bc3cfeffd604398847bb96e61f637be37d999ca0c10d67368136dc1be463cc","signature":"2571a03d3ed5d860359c75c177e55747b7ccccb97e898a5a320fc05623beddd8"},{"version":"c3edac43c4a1d3e16edf15e258d82bf554250a017ef4e3576834c63457f4d6c3","signature":"0e5889f9a92cc3fe8fe1871bfba2f90c1e6848c6e407ce9562ef95c36231f002"},{"version":"347343db651932270ab093d5830ea54f419111eeae659b77a9e2cb13f328a9f7","signature":"080361816aabfc62fef2389f69ada0fb0d0d0b14caa5abdc2b0da2c1e67dd27d"},{"version":"3b5ec50e7b2d52f8cbc0070ed5556ef15c90833fcd899cc2ecc5d47c46c74973","signature":"bfd36eb40e4feedcbb2b1569a62fe03bcfc254876ac7b9ddf67d557212f4ed17"},{"version":"cf59492bcef122d8585008c4752000175c53abce051c882757b7f1bbd8a58961","signature":"870fe0dd8e82102dbadf0760b77925e9412c93d95da14cb998f138177d26f222"},{"version":"f6aefd3198da082563b438f5a5c1563480e772730c9c51366edb199ec937ea4b","signature":"659b8c23e1a6160444eae10dbc944dc8cf37b17c25b6a75b021660376ba49cb9"},{"version":"e1739e45848d98cc6c0d917732006ff4034446cbd98bd3e8ee8da404df8a5bf4","signature":"276075e8fdefbb6e5a265304d12971616bb734fdc37f9f651d6bbae2659abd7e"},{"version":"19da3542472ab262424b57bec72fc4d400b67497ad03b42c6007a25f23f4993c","signature":"8f85de6d7af972550454e0b30b716f0ec91b67657a52c610e28f26e491664319"},{"version":"55c69554020aeee5a248a84206d957cdf889bd5c931b66986770edf909ae07cb","signature":"f233cb1e16368c11db6b6ad86e4c617e36eba9dd67656387f5fa66da2dd916cc"},{"version":"c0fbb82566e3c587cdef075991cdc0e83e544ff71ca6ad7fc0a8b4114210d401","signature":"9dc2555b0e38d3bd51c5f9e4bd00b9c4c3ec1a958578102e8bc4ba861eaa9d31"},{"version":"345fab538a281744e1ae6a55feef5b467d5389e48eed730ec49c4f782e0cc496","signature":"9fc9bd7c5196f960796b6fb6124087d8246745f4e3ff55839c9654280c3465e7"},{"version":"64e6a475df2e8aa93aaf367c5df92364e0ec677411bd068405184f83e517c84e","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"77a094e4e0ab32fb00ddde5c78476d5fe0828c712c6c2efde60a17a226eed0ab","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"cb7f713163f0c90bc6db0370b8a87fb73c98b358f8bcf48aead28597f55b0f86","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"56f427e398577979ee99d890fef5d9dc53f3f2c557085104eb6890019787d916","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"56dd16a4d896aead6a4cd787098fc91eb1b3835dc4b015f6f17e770551e9fb18","signature":"8e75987e889ce34bf56367d53e70335b895b4262cc767e233a8fa975a6897c78"},{"version":"39532325082bde9873919451c6f1a8ad91f5bef582b3ea16547062bdb7143065","signature":"bf25c45519d3efd692b7bd7387b41720d23d5dbdcab032c9cb4891a4e10910a0"},{"version":"fdb783a322c675e4561918088c76fa4adf691afc7c1935300138e093c1285f83","signature":"0cbc40d93c13b47faa989bdbfca8eed5a48f277a850366c910af796afe035935"},{"version":"f247359cb12728389b7bb2d3225d2e2376aa5fa7b2d7f46572372e14b415c69f","signature":"91e9bcbb27abdc6de28aba65757ad7c0371120f96af6c16c62f339f1e3745b51"},{"version":"d65387fc3ba09cca1a3303e645d498b33bc70a59528ea21ec210b2e78b842974","signature":"4b149e9fa0a6ae676a7307a768790b180ca83457e63c54b4299d44bbf19a1d62"},{"version":"43c417abe8e54d69d037f666583cba8bf222070cb005f5cbb5fcc1b02e0fc01a","signature":"903fea19f3268eae6bb181957b559c581ee202af35822e6d77a41bc5d89e879a"},{"version":"5c8e29eb6fca80933c1fb4e7f4ddfaf1a14a3392e5277ba1d327258f8dc99783","signature":"1f25afa953f8c28d8bc0a8602617cd0df27af4eca8a8bbc66228162cd11bbb66"},{"version":"495d68e9b25ae0c7407b6e8c8695cf3e7395eaacf7dfc2617c6ecfe6eba8dbf2","signature":"99d74df147976e2cf01e6ebf287fdb34a7a8ae676421d4c91d875f6a9b0cd2bd"},{"version":"56a45c6a6e1f5771247343b3cf6d2d9f2489a3c4e6b68e0c074313eb1844dae8","signature":"214b97d6fc9a99cac29651af928b444edb053979a55a1da2d7cc4f63c6ca7ee0"},{"version":"defa83d3d7da54c02eaf0ae29ea9ceccb870735d61b36093306569eebec5f952","signature":"aeaa3ed647162c2e6433cadde95e35b75be2b92bad104cbc965108dde9e5b8b3"},{"version":"72992d605284c2a96ce10507cf3dab03536cb9bbec569ed3de7d07a55fd35c7c","signature":"9c40d1c6298bdb2274e7c038e7e66d638429009f00a917472b26fec7c39c4ae9"},{"version":"05b3581ba268fc0794a9299ce2fe953bdd7eceec017d53deb79228ac13555be5","signature":"a941cfd36c68ded25c50c689518976de4e6669d5a633eb0b3b31a43127e0b786"},{"version":"7da97364768c4c40a74c26dd586b7823172eea352f09988dd1199dce419611dc","signature":"bf2ff4f10ea73bd8971ecd08059845bdef23875e2a1e1d36ef5ece5c21c8b83b"},{"version":"31536346bbfe853cbaba990964d535b187008041b1db87c87835a11fe5644fec","signature":"771b5ebb297a856cacc5d04e9d7746c3d1dc3d21e22931dc6bc01375bbab49a3"},{"version":"439649f4e15fced40239f2d9af18e365201fcdaffc1f91f02148f4e395effd94","signature":"264fb54f1382a8566f0451e413fae8b7919eb8aef3717f62d24a31de07a61a4e"},{"version":"67bdd738108cec09fa4bc092e0b4af9386fa438030436b8da50f9640b684a4e9","signature":"2efcb185be86df956227b0d7571bc212081c84571c2a635314c8b00a3329cd47"},{"version":"1a5c85ca837fb53dfbf9afc003398f497aee12d2bd30a2acb435429147cb69aa","signature":"efb8d94cb63aa478219a57de29d534592d82716c0475eb85f97e9575a104071f"},{"version":"5cca78422a613e26951029599f943d558b2abc35f98cfa8c69e67881e3cb16c0","signature":"86578fc5db1f524c0a36be6052944fdf74f2e49e7c9670e2c4d920d6ccc2b7f5"},{"version":"40caa7b771a6eaf17946edd28370ba4dade72fbde1369fe620ae78ce5b020470","signature":"21c0cad3492f090864c27389e857d076c6ae7f142e446901ed25a1733bfbe76c"},{"version":"3fb64d2de7de1d85c1ef2143edae8f17de0e6fe62de014161dcdb194d7997c65","signature":"7e785b143a0d0c6bb023778f83d6d34e0d4976726135add21d1b3d6c25a521e3"},{"version":"f56191ec2f9da0206eaceb9579dd62f2d5aa9fea1499782c1883908eefe33201","signature":"a3b7a41492647474fd6ebd2a43f9ef48c1dde2237a1682b37880ebcff18366ad"},{"version":"b979f72b6f41e87df2372bc4993a2d4ffc203dc56670ea867869bbe1670f46ce","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"0c38761192759ded08995fbe45f9ba1a521169204690db5e067b0ad58bc14c42","signature":"0c59e87a0efb243e5b9462a45a72d5343a76895d37f7d1689cb4de9d154ce83c"},{"version":"37329c4dc9437527f615f961867b65e638a7d61db8554590029dc87aa2cea0dc","signature":"099dfd4d00412b54958be092a16a645cf1f05bba092878eba1b1f53a5a959660"},{"version":"002b5be581b9f660dcd8f1caed92e3798b0980dce3f3c92005b9f95f0610fae4","signature":"09537bc74d55f2cc2d9fd7954d977287bd7eeeaa9689eef1ef697c355feb053b"},{"version":"1fccc097235e47b0021a6365176b6c1e6028a7309a55e7d5decab91328f0acc1","signature":"bf135f969b9fbb7aa9133ad0ba9e55e6c6f3b7fc976eaa04f140ff895c536b62"},{"version":"ad0b264403db93942ed42db950397c46f8ceed9baad376c0baebbbac658f3fb7","signature":"2a3d13784afae69e5d3d6327bda126555b9dac27dd6808e25e3b39f1b163ef56"},{"version":"0a30389ea8dbfb42061721ef22f16e93b8c7413fc1451eef5a50a70dc3da50f3","signature":"beefb3fe588e5df85e53433a46468db6df18619ea973f782a8f3af3f906238b3"},{"version":"1d8b191083eeab243548897b93b1943f0f434aeb693351238fad315c69ce8c52","signature":"62e652f61742da5e791dd7c1dcd646e0e5e622fc5fc01ace7ad93df4ea4198b9"},{"version":"833f817a1e86053e4e8c751f43e8e712844bab4d8b242d3a79b3b84cc4586178","signature":"8b16906966e5a39ae872a27d3499d63a596c145478b6185abc698127c1943cb6"},{"version":"474f1073cfcdf19f03cc17e9030f4efff0688e56e5f6d20b73ba406cf074e69a","signature":"80b8b9e4cba1d02795358f8475fe3f961bfbd5350db692aa9963e690ce97d0a6"},{"version":"a66259e675aa070c60a10093dc7fa3fe07edaa6f99f0c29a82abfe2d6fbe547f","signature":"f6acfe020380e86024ceaf739ba82a6842f2b5a34e615646108e187a87b42420"},{"version":"6e66544c428f62a49d86b1d1f27ac16aae5f9175f150eefd891b10b9657a061a","signature":"88e37c714bb35035379b8bd3c106e04760caa815a09fd716636fb0eeeba0b33c"},{"version":"6f78f3640cc9e69218b1ae103c9cad15c162b9993a8085b9645246b262dd03d2","signature":"59fbae6015c2b1e8d266c5daa8d4c270fb99e79f50c8b3f19acaeb50ef3e81d6"},{"version":"c9b442c154bb5c7a742437fcf5342950c158dcb793ebb8af6dfdb526bb6464b4","signature":"13d6b583c3a99cb3683bde8d66e1ff76bd43337be477173329a3db454fca2d0e"},{"version":"549b79c447b40c4324fe58cbe323b13bf56eed14f590da7eff45ce4cc50c4cb8","signature":"aee79945eba0f93f292eb0881d6261b0f89e9b13b65ed0b270f1a7b119a89997"},{"version":"8dd789401b130f6166118a809d389fe696e5051c1f03ded09a498723aaa25559","signature":"7b128eb922a3cd3279ec9e9892992bc5ed2a9c7c95a968d4decb18f95f45082a"},{"version":"92a2bf52fbba299dcbafe161c4272ae14461a9736d9b7830dc33842750f8576b","signature":"299c896e41cef63e56382d0a4b77c04755510a745be66d7e96cc7e3f0814a1ad"},{"version":"f18f2264185ec5926495b947acf5e550fed06ae987b3611b8ecb146d93987d69","signature":"adf9620e7b412955d26d34614aed00f0792eea0e0596bb7e6cf1c4d580adfccb"},{"version":"9708b6f4d793c94bcc939d964e51570a3fd915e76269fb01d697752b4393691e","signature":"74aa1b12be473505d180a9a7c0be6a054584cae1a71ff1a304d8314eacb5833c"},{"version":"2e5c121dc9dea0c42260d817e59feed11d4efb5ed3a16472d6e0ef66ca89555a","signature":"99beb3d79916b35dc363063ba9a6becc36535594629e287545276e5c75add9eb"},{"version":"b007b82d642f391033fe034c38825f0e40b44257a0c62ea661837eb0c462c679","signature":"024f3059b89f67341e646b537658005d47c3667be183173801d47a4aab61e8aa"},{"version":"3a355fb38d746de8f01cee4b4fbb9e9fcd1f2466161bc071a9c8ad7727f5edf2","signature":"e46d2c1820d4fa7ee985fe214651b627840f4c748664dc51fb8d10f10db876e4"},{"version":"784dcf85ab9c1211d1538520a14bec960eb4d0acf229741e0b7614fe63dfec32","signature":"65248f52541aa0182330d7151c87cc392186622c124b2b594a67adc0ef96bcaa"},{"version":"02e67849e3a994a169d06aceb58d0af59efde7a190f3037b1be3ae94d1034885","signature":"3d8875f891ad64347872eccb56fc41820e96dde61f73449699f667cb3c632a1d"},{"version":"c20a1892866517b88681b09c6c359a7f1661a3aa28eb897e22399cee204f0e4e","signature":"ce3676cb20c5f36cebebc3d96f9662d0bc786694260e8de5b0b91823ed20f24e"},{"version":"2e963ca4e0964156595898b4662e712fa74ad847777050a8fdc59bb328263eee","signature":"868ddd0c6fad7f6b6b2430c0d16c1a4ae6129c4acd0ac2db2ab4063683db92dc"},{"version":"5272ba54a8ccb25ec0aa55bd21c258af1555dcf4abc5737a6686e0d2653e61a5","signature":"86aa442c87657b91259879b1b9c2f5c1a95cdfc4cbcec086c43713cad8636577"},{"version":"7b5c4d79eedd064e9612f63b9b08ff71c29eee3a9dcd55c16043a685525f972e","signature":"029f7be0b91dcd872cd15e279ead045473abd4fde6d354531124954ca31453d0"},{"version":"7117d64502c3b99e2886608f5059296f4dd9bd937f51fe9ad09434049f16ee6d","signature":"3d741c7ed16772279b97c3465c5ce4ad5dc3b141781c4f31fec418fae5361193"},{"version":"e1d7166a9616f47a7a1881974140bdfaeb251a65c44d996b4ff8d4bc3c75d400","signature":"aa68721735557b02766dcabb4e94f3f40217bec420b22ca2bdfdf7e5b2a607cc"},{"version":"f4272be1c13f9fcec3147b88f423de92e2329268628242fcfc7576607da531aa","signature":"4d6e46097f7d85d47313ec2b26803b6953b5eaa41f8a04ec900a94f1a81a56b5"},{"version":"f4d6df0393fa10a2aee7009a64c30d9be65ef7bb356cdfc3a64950932aa62a3f","signature":"1c415ed5a42f3cc39f4317b7a9ec20e47c14febbc373b38ea16ac64f7da514b6"},{"version":"49f9472dde6e2e5db6aff9e7bf2ef161d1bf98cb62d7b530ca002b2c7bf35edc","signature":"b5c639556595473f15b5d6c7e99ae606d2dcdba7faa3e85433e0bf90b797c2b2"},{"version":"383bb589050b947b402a6a4731933e8d73604b5150695f5116c620db0010f5df","signature":"b9858477881ac33715a6429935e93c299b68a7533a7a7add6177b5e0e55bcbb8"},{"version":"fd43685bc19eaf18c8fbd0a7439cdd0779766a4797dfc1a988fc9a698e10df44","signature":"db9a15a6d09242668afe9439df3d925651e9807463567e431b6defc6d748f139"},{"version":"c902c7e27c710d5194fe5bd33594af871d910fceea1f184fba7fe8448fb7fd5a","signature":"f8decb4ec3a92ce7fb168ab21d09a06d19058f27729318e6cb5c7ff0ebcea9c7"},{"version":"ecfac4deb69e65b479b6630887073fc34b87e7b98f6d545c6bca309dad55bac6","signature":"53c7a922b2395584f623c3c0c6ae0ec0cbca93b1562416061b31b91339bf427a"},{"version":"897864de7059fb531c80855a96df794b101306e993a7bb9241ec006e2205d9bc","signature":"b4014d82dafbd70651d0249a0bbd9d80c57c6bd6ab26ad8a5beba22b550c77dc"},{"version":"e811a95ceffb9543edc3fbc95b95818b4a4d7a869e5d8ecf24106cc817c0e1db","signature":"6b8b9b5689efb14acadda73e2fd64cd93012382d106c2478fb951175a6000036"},{"version":"4d263e6dd9b925bdf29a75916246acf89895ce883895918771c1a5411bf84c87","signature":"9165387bde08c9159261da6367e3229f2ccf38e132df49f9738d11a4b0627607"},{"version":"ce84334cad60bcdd4ab204c6215ededcef648e45a3e6b15648930b142723b910","signature":"cac1458561769ad0f439dc24df9a7b93eb47f5b3ec7bee843757e6dc20cd1690"},{"version":"d9f476970a025878ed76c53812a20d964e8907641e6edbaa0388897bcfb7eadb","signature":"3400ee3f71604d8970acefd1250b595b8a9d3a21a9d645fd7724583bd0f05415"},{"version":"0ddb2d2590c9238b75439b1c66bb607b609d157da10654465d5b9dccc64eddd4","signature":"227412e7c8eac8cdc04757c21cab8a44706375ef04bf299bbea22a2f9742922b"},{"version":"5af530153b247852736f86c46e5cab154543c16b88bebedb18b44fd5b64f52ed","signature":"9a722e72396459c5f8775572a1ca1cbec71ebdc9e3a29b5c2683b714453018aa"},{"version":"76430028b9b4b3b081c143da45da170d96f78d7d7a1b4156c42dcf1eb19c29f9","signature":"085d7d88386294be7edd6e78f29d47576013dd6a2bca6b037efcecf9ce97204b"},{"version":"c90982186fda667918dab68e62312f1ea7effcc9b32b0494690f18aca8fa70d5","signature":"9208f72dc36272749c88077ab3b09059c0c458949dac7e4163004f39e95cdabc"},{"version":"d220d0129a23e805338c1732504b47206016dfd37a6816710cbe596ac330ffd7","signature":"e36a6dc827fc0af1b66b354112f3cdaffad20639a7747153f0ade868810cc443"},{"version":"cd01d6aaa62daa9bc38249830f0a1b74dfb7be1b90432d8104f1777b56132fd6","signature":"adab5620caf5b6c670c6a154ed604ca08859e9e9a4275f7e8db292ba856ad911"},{"version":"1897984c35f745db144b86ca4cefd7836af5c580da8d146ae1405672359982b9","signature":"b228f1bc0b0c9530ee4f6fc782cec842713840ab67c9e98f7c7fe1b8e408fe3e"},{"version":"3a27b67126c5840b42df4da0921ea7e98a4862a160d7baa758daff93609d22a2","signature":"5a875301b762a470412d4cf996a03bce92518d59a0313c02a6dcc517029033f4"},{"version":"1079e4517c736241cc014aa7dde48b9d074023de0ab407c7e817a3245fdd2b14","signature":"b02800ef52fb80793aa39f49d7e88a9684c24e759ddb7a4438bf6168dfa74c3a"},{"version":"bdb598a808084b5c69a1506e950d2bf5f890902fd0a9f2652ff33824bfa7bf05","signature":"b732f9a2887cec72e150f1cadb31fa16e2a366896cd8925c0084e5ed102f7d14"},{"version":"c5749ef8d3921cdb3367f2734c18521232f4d9116a04b1279edef8b72d805657","signature":"96e98f0e71d79b22865299db0024e1d2422de66271a2af8722c6eb9a9e3fa8d3"},{"version":"92dd46ae08b9914c364a318f6f71b84ccf7645c7a5939b0245147beb5db33449","signature":"c02a4ce40f9629bb6ec5790433828a39a8588db42732a6517be715a7705ffad0"},{"version":"2c37dce3f0c73c38f5661b486c9744a03edaef8d4c46da05268b7793f9a89d2f","signature":"f5ece32ce3e98730cc0e71aa7da948897a76ac55372cfc9fdf2d059a612dbfe2"},{"version":"fe1e84b3149e49d98a784cbae755ac20bc1b94153ad3422c8b1d881cae15beb2","signature":"0010df937f093d72443bc5a702bf2c69792b3b137790c22e0fe36e6891abf3b8"},{"version":"9b90eee23ee3ab96379996bc8d729282a890b8bba1cee14822d83247f28886ed","signature":"cb5fde38799738faa79ae57056792619c486741bbaa89da6969ef5e10620e9cc"},{"version":"bd354209537b43f3243aed09f2f388b0a242366395c34ce678288657640455b3","signature":"ae7fdb68cbca46d19f345e8dab793d97dc543160c57b8fb5df5b03c98f629172"},{"version":"525647eb88e65389a30e7798adf43248f314d1e574b475ef2d8a4da83799f15e","signature":"754779f5beacf63eb457fc4b39debac390cdb6221dc19b5725e3d91c83c3f6c0"},{"version":"a9fe0526ac227eb7c5a38558a81f691065521c30c1c5316a9b1ff884653323c9","signature":"d922a577f3f997a1914182a44f8fa6705592738ca462b7d9eb42c2c0f12ac9e0"},{"version":"58fca6f591d63aa13eabdcf110b695bcbdca881cb367eb561acb4c2d37231461","signature":"68f2a9e61af5f18d565acabe63e1e520fd4f3f4ca9ef1d7befddad7cac9057dc"},{"version":"82ac029dbeffe36970c834fa469148b3cf8eeadb695efe97bb591ac108e1ee67","signature":"0930755a9c485ad938d626bbfc3ef503b91bbb26740fb592417e9c1d3138d8c1"},{"version":"5df25aa8b5cbf897175e5d0c22a8f58e848d37b2942a6d7bf3113bec26bffd53","signature":"434b10591392199e5f955b3c029d6612af5c08038c4e243b1f54cf4f7ec7309b"},{"version":"8b8f94228e22aa82d0726a9a7d33c6f36dcaa359e7097d49079f057329529155","signature":"da291e93961c84c03bc67842c9c59c160c48495c22c3df2af5ec17f4697e0b87"},{"version":"2d60eaf9a86b0c4e0b893b000427198fa1604c60106e79b880ada041f20f49ad","signature":"68e664d1c7208b594f3aa7d9673db7872ee2aa6a4bfed5fd1db11c86bb196f71"},{"version":"4d87ebda47a210b5d3f93d2c0494897a9e30a024890cd1012c2e69042f7418eb","signature":"646897ed3cc3022560a9ddbf2d64f5dd661a02594f00524ddcdda4b621d5d476"},{"version":"e52ece823a6df87544f499e3c0a751c116880f5696588dbf8f71a774e15d1c5f","signature":"55a97ce42c796bfb118713b25d0cafa0e91a5aad5c2e05e9f8975d5ad72a962e"},{"version":"81f0550f6b49b30da7c7aba5dfec1d2b7ccd158ac150731128c1268165910a2c","signature":"3ca313dce6205e62a04a72704d12a5a47011a0ea43103c37dc353575904cfcd8"},{"version":"0d4d0d2402d175b03e3b60425bed77615614c10410cd4eca2c30a2b07f6c654c","signature":"5008e11dc3b08ed55c34cb990dfe0a56c6cc61e63be23b077c4688ef34a7e3b3"},{"version":"ccb11ca6dd6efb01f404c5bccacd9af344a1e543625c380632f8f2567869d9c1","signature":"ba61a093c1f086fadcc41c28d8553a6c20d5fb03fefff8b1ad61268521fe8743"},{"version":"4cca8510b26276661817daa40373d6cf625ba319587a215a7be3395c3aeaceb9","signature":"a7eaea56849c75db578d890fa3cb6456f0d82259807aa987647fe8dafc20ffb2"},{"version":"280eb1494c0cf22385bb0b372564b20a46a87d0ba9d29d56db8017c86677365a","signature":"cee86d0e7c1a11a319f043b86b0ace52538456bde72a3d6c14a008a45b8f0d39"},{"version":"eb3a0591390a130a382a127dc6608debcdc8475fa2c75cbd1bea4a2fd5907ecd","signature":"8d518f32eec2945c293eeb5a08fd94c45307ca37e42a537c7e089e21b3b830ee"},{"version":"23bfccd30e0691cbfce350666e53efbb4eda31d767e56ee93516f752fb61bb04","signature":"88459abf75f7b41911b3f0fe83a1a39282ab81dbdbec024b4b920393aaeabdb7"},{"version":"728736ab9acf9c581cdfc87cc6f80266ae8842cc7465f71f1dff1a007e0b8d7a","signature":"9a023db6d7f99322f2331f0ab632b16846f55736b14d8d6fca65c7c4299d58f9"},{"version":"81c425533471e318c02eeb71ea67be00a2baba77e80da1bd6b4265371b707474","signature":"51d2ee88fd537fcf38870cbfefa457e3959540106aba196dd2f67156169ddc38"},{"version":"ea347208a796e36f5f7a14d80b11fe49547f38028f0e37e3db8f69eccdbecc83","signature":"1bb0fdf782bb23f612568f1d93c125eecfb5c87709d13086ebe66052e4acd51c"},{"version":"dd07240eb5807e6420a09275b03c8b5c8828425ea6f24e5c654fe2af336871db","signature":"b5e5958542071baa1c0faca8e0dc6fe6e7b93f5ab9798273d81b32d07c8d77ac"},{"version":"713571db67fa81007d8267a5c35bd74662f8da3482f2e0117e142ffd5c0937a7","impliedFormat":1},{"version":"3cfc4e1beefbea6742f826658749fd6add1755a4d41f498242f55f2827adfb13","impliedFormat":1},{"version":"54e79224429e911b5d6aeb3cf9097ec9fd0f140d5a1461bbdece3066b17c232c","impliedFormat":1},{"version":"6fc1a4f64372593767a9b7b774e9b3b92bf04e8785c3f9ea98973aa9f4bbe490","impliedFormat":1},{"version":"d5895252efa27a50f134a9b580aa61f7def5ab73d0a8071f9b5bf9a317c01c2d","impliedFormat":1},{"version":"607a8979ba48fd54c7fc37033545beb3231408abc5da4542459ad9423aa8b29c","impliedFormat":1},{"version":"e9458a859b5277166d30117ae324e2e283c02467e59070f621855ac47ffe3e72","impliedFormat":1},{"version":"3fd17251af6b700a417a6333f6df0d45955ee926d0fc87d1535f070ae7715d81","impliedFormat":1},{"version":"df9e48304083ef3b9230064c19b8fba228a83d82d8a8a80d3a1d76fdc36728c7","impliedFormat":1},{"version":"a04338d8191ebc59875ebe52eb335eacf8c663adb786ee420ba553a808566dc0","impliedFormat":1},{"version":"3eeb5791a25114e40457eea09eac6e7bea6944fa1e55c4be6d80b5dd95f0cdf3","impliedFormat":1},{"version":"8483fdff64d02b5a7253fe7b90b3b3ff930a2aa36708b82f57f312554272297f","impliedFormat":1},{"version":"7fe31f933471075abbc4e7529805ad31251a7019cb9658df154663337e9bab60","impliedFormat":1},{"version":"aeb8e8e06b280225adcb57b5f9037c20f436e2cbbed2baf663f98dd8c079fc02","impliedFormat":1},{"version":"8301312903d99c319863dc7f788dc0e1a56915d8959d3e2ac78b5d12b366d571","impliedFormat":1},{"version":"620fbc862577b3401a9901625fcaa39036ea9b760df7a1302967c3f5343b763c","impliedFormat":1},{"version":"f32c9af2ceaa89fa11c0e1393e443cd536c59f94a1f835b28459188a791d0d24","impliedFormat":1},{"version":"0f8d5493a0123ebb6b6ca48a28ff23952db6d385d0505a2ba99d89d634f55502","impliedFormat":1},{"version":"5396ccd4007e9fea23eda8c4dca1f5ccfad239ec7e13f2a0d5fd2c535d12e821","impliedFormat":1},{"version":"9c44e80d832d0bca70527a603fd05b0e4b8d1a7d08921eecc47669b16f0d0094","impliedFormat":1},{"version":"8f6786732b48efa9dcf54e3cb5db9b37e93406ab387d0180062b0b3d1e88003f","impliedFormat":1},{"version":"313c53dbf07386f95b55a1eecc825d7d268437c634e9d42aa9a30417f71fc993","impliedFormat":1},{"version":"53dc4527a3ed51f201376ea3a11152afe0ab643477719234f69122f3e19fb7f8","impliedFormat":1},{"version":"3f9a50b3bd5d05ce64a1eaa5b6d9e4557b09f052cdf770f6960729230865811b","impliedFormat":1},{"version":"28e4024d110000ebd31e4446083d8491e38a8c4f64e8b710b758399aa317ab3e","impliedFormat":1},{"version":"a5d642b6ad2b55831f72a64a53d769bb90e4ffe8bf18a679223a062cc0911a4a","impliedFormat":1},{"version":"d88e0b5b07e7da500c1fcc6b4b1ffeacd8c4494148ee05657c076560ef23c318","impliedFormat":1},{"version":"7a9aaa2da69a99ddc1af90adc264f4c46d9b5bd5445827fdd10b5eb6b041f856","impliedFormat":1},{"version":"086caf9537c9e76607d11e605f2b1892b7f4e061a3d85de46c6b2718deb54a95","impliedFormat":1},{"version":"9be97e55fdb734ff04137191bef5ea38ede15c7ec1ca4d51ab0897c023f38610","impliedFormat":1},{"version":"4d1b4a4e6e4cec22d76f7a5bb6d909a3c42f2a99bb0102c159f2ebbdf9fefe09","impliedFormat":1},{"version":"d95575edc7d901793a6d7aadffa80eaa0d6f1a064c343abed968550d3472f064","impliedFormat":1},{"version":"a98ab1b88f65636695b52e05575b13bda171a758cee50bad0e6749fa98d56e2f","impliedFormat":1},{"version":"957e2258cd6c97d582673e83239141e810a42caf4862514a7db6806b35414c25","impliedFormat":1},{"version":"679652fac17b05a2fc6ec2ed7dfd96c01d29528ee2ef976b5a486c82ebcacd89","impliedFormat":1},{"version":"b6b12d7fc9caf24f95581113ceac63c12a674c82040b60e1f35fdc972f36d24e","impliedFormat":1},{"version":"066f0ab8c0d0100b9db417204defa31a9aa9d8c6194ba7aebf71375701afcf21","impliedFormat":1},{"version":"1d500b087e784c8fd25f81974ff5ab21fe9d54f2b997abc97ff7e75f851b94c1","impliedFormat":1},{"version":"c947497552a6d04a37575cec61860d12265b189af87d8ff8c0d5f6c20dd53e53","impliedFormat":1},{"version":"b2b9e2d66040fdada60701a2c6a44de785b4635fded7c5abdf333db98b14b986","impliedFormat":1},{"version":"61804c55cfa5ae7c421f1768bc8c59df448955842264a92f3d330d1222ca3781","impliedFormat":1},{"version":"77a903b2d44ced0a996826e9ba57a357c514c4a707b27f8978988166586da9e0","impliedFormat":1},{"version":"3e46c022f080be631daf4d4945ce934d01576f9d40546fd46842acaa045f1d24","impliedFormat":1},{"version":"1ed754d6574b3d08d9bcc143507a1dacf006bd91cbc2bd9a5d3d40b61b77cd88","impliedFormat":1},{"version":"8573c8633d64bf28f1f11815e28a66e089debd93470e2a0bfc420752fbccef64","impliedFormat":1},{"version":"aad61798065cd6be03e53ba862d6fe28a5e0347c8b54a26f7af264abb2228d81","impliedFormat":1},{"version":"d03447d1f0c153f4ea2b00135d73d19569b80191fba23fc78dfcbea62f3f3ab6","impliedFormat":1},{"version":"0a465040b8eabd13f8771eb6e8daafcb18a9b9e0f7d05fe9c415cc7cbc1ac4d5","impliedFormat":1},{"version":"a3a375f6602e67bd21790b870301bf64d5895a8158277af305c2e9c77beb4c34","impliedFormat":1},{"version":"97fbcbc2dbba4da759d703ec478404ff6838c9d51f420dd08a193f4dbfff0a73","impliedFormat":1},{"version":"8f433a52637174cf6394e731c14636e1fa187823c0322bbf94c955f14faa93b9","impliedFormat":1},{"version":"f3c2bd65d2b1ebe29b9672a06ac7cdd57c810f32f0733e7a718723c2dddd37c6","impliedFormat":1},{"version":"a693fdcc130eeb9ca6dd841f7d628d018194b6fd13e86d7203088f940d0a6f20","impliedFormat":1},{"version":"a4aaa063e4bb4935367f466f60bbc719ea7baccc4ed240621a0586b669b71674","impliedFormat":1},{"version":"ad52353cb2d395083e91a486e4a352cd8fab6f595b8001e1061ff8922e074506","impliedFormat":1},{"version":"0e6ee18a9299d14f74470171533d059c1b6e23238ce8c6e6cb470d4857f6974a","impliedFormat":1},{"version":"f0b297519bf8d9bb9e051aad6a4b733c631837d9963906cf55a87f0d6244243f","impliedFormat":1},{"version":"35132905bd4cdc718580e7d7893d2c2069d9e8e4ac7d617e1d04838fb951c51a","impliedFormat":1},{"version":"6c50f85b63e41ead945f0f61d546447fa2fabfd8e6854518675ddc2400504234","impliedFormat":1},{"version":"e67aa44222d0cfc33180f747fbf61d92357a33c89daa8ddd4edba5f587eaf868","impliedFormat":1},{"version":"31fea62febf974f1a499099bd47a2d18655f988ff2924bc6ab443b86ee912a21","impliedFormat":1},{"version":"4021b53cc689a2c4bd2e1e6ae1afcf411837c607e41c9690ce9c98d33b4bce4f","impliedFormat":1},{"version":"1ac4796de6906ad7f92042d4843e3ba28f4eed7aff51724ae2aec0cc237c4871","impliedFormat":1},{"version":"94a34050268481c1e27d0ad77a8698d896d71c7358e9d53ae42c2093267ffd53","impliedFormat":1},{"version":"f43f76675b1af949a8ed127b8d8991bb0307c3b85d34f53137fe30e496cb272a","impliedFormat":1},{"version":"f23302eb32a96f3ab5082d4b425dc4a227d14f725d4e6682d9b650586a80a3e7","impliedFormat":1},{"version":"ee7cc650232e8d921addfdea819290b05b4d22f7f914e57cd7ca1aa5582f5b29","impliedFormat":1},{"version":"2ad055a4363036e32cebb36afcceaa6e3966faada01c43a31cc14762217ee84e","impliedFormat":1},{"version":"fba569f1487287c59d8483c248a65a99bd6871c0b8308c81d33f2b45c1f446e7","impliedFormat":1},{"version":"75d774b9ccb1e202709ffbcadba1d8578bad1d6915d86633ac056574879269b0","impliedFormat":1},{"version":"08559fafddfa692a02cce2d3ef9fa77cf4481edd041c4da2b6154a8994dec70e","impliedFormat":1},{"version":"2e422973e645e6ee77190fe7867192094fa5451db96eb34bf6bf0419cef10e85","impliedFormat":1},{"version":"349f0616eb0bfbcaa8e0bf53fee657bff044bff6ccaf2b8295be42d2c8b8a3f3","impliedFormat":1},{"version":"25b0285ec91d78fcc1c0800022dd15f948df01b35d1775dafbae3cce5a79b162","impliedFormat":1},{"version":"8a6414c6d70225e89602733cfa2af2c02a03b2af48c865763932c3892df782d2","impliedFormat":1},{"version":"b37402e79f4cc5103b12b86dbdcbd98124a4431fb72684a911ef6ecf588cc0ef","impliedFormat":1},{"version":"6e72c4d0e83ca2d44207fa1de293b5ff260401ed706adad9dfbc0df7a3e53abe","impliedFormat":1},{"version":"b569745230c9e5cdb79ec7f1458d59d5e0dc04bf06fb8d398ca9d285f07c2147","impliedFormat":1},{"version":"9ddbd249d514938f9fc8be64bda78275b4c8c9df826ec33c7290672724119322","impliedFormat":1},{"version":"242012330179475ac6ffca9208827e165c796d0d69e53f957d631eaaea655047","impliedFormat":1},{"version":"320c53fc659467b10c05aad2e7730ba67d2eb703b0b3b6279894d67da153bee2","impliedFormat":1},{"version":"e2efe528ec3276c71f32154f0f458d7b387f0183827859cf0ce845773c7ff52d","impliedFormat":1},{"version":"176c7a1c47b5136de3683fbeac007b727905ca693dbd8cc340fa1fb9f26b365c","impliedFormat":1},{"version":"ebc07908e1834dca2f7dcea1ea841e1a22bc1c58832262ffa9b422ade7cbeb8a","impliedFormat":1},{"version":"6111a75d1c907033065b0d64efed162ae8027300edfcc6d231be5aba02f92497","impliedFormat":1},{"version":"22e92cabd62c19a7e43e76fba0865b33536b6434e50a97e0b0220c34c74831cb","impliedFormat":1},{"version":"d1f5f6ec7cafb6de252ce831d41e8d059bf7c44bd03bb4f8327b28b82c4d2700","impliedFormat":1},{"version":"96fba29a099df9b0c7d79ca051d7528ae546a625f9a16371b077e09f4f518e2d","impliedFormat":1},{"version":"79dd276b87e761fb23979c0d270974c19f1b3fd51575bab4691abf7701fe8154","impliedFormat":1},{"version":"764df94196883c293e3c7bc0d45eb365a9082c91a18d01f341675186f2fe8225","impliedFormat":1},{"version":"7654616453f4b4aabb6302828f884d41adddea7cfaec40d65ed507e637ae190d","impliedFormat":1},{"version":"b310eb6555fd2c6df7a1258d034b890d7bddd7a76048a8a9a8a600dd68a550f3","impliedFormat":1},{"version":"93d5a78ff448731738a42b22bd78fc52a92931097702218b90fcba5a4676a433","impliedFormat":1},{"version":"dcad64cbca4b8db52101c61f6771ef1ccca14aed432b923d86c0c7def3073b42","impliedFormat":1},{"version":"2ea7aba09d12e4e8f550206fc8dbf13d0bb2cc8bb7469fb9ccef39391dfa443c","impliedFormat":1},{"version":"d7f91db766561a83655b535c2f06163647bd780d9bbb2c19e50dec97c0e391ea","impliedFormat":1},{"version":"1c7951a2784c2fef0ed6218bf18cd3d3b895667881ba4d586b2bc15fffd0ab29","impliedFormat":1},{"version":"3d82db9fba4a59ef5bcc45f6a2172b6b262fd02331fe55ec60b08900f5df69f8","impliedFormat":1},{"version":"2594a354021468bb014f4e7cad72af89cd421b44f5ac3305a6b904d5513f1bd4","impliedFormat":1},{"version":"cbbd8d2ceb58f0c618e561d6a8d74c028dcbe36ce8e7a290b666c561824c39de","impliedFormat":1},{"version":"8c70aefeaa2989a0d36bb0c15d157132ad14bd1df1ce490ad850443ac899ba82","impliedFormat":1},{"version":"6961f2279f3ad848347154ea492c1971784705bc001aea20526b1c1d694ea0c0","impliedFormat":1},{"version":"2ae0c35c2bffb3ad231d40170402436a4b323fe9ef1dfcb9a20248090f600f36","impliedFormat":1},{"version":"9c1bce25595a518eaa5644c0af484a3794319ef22525bc63085a8137106d3ed9","impliedFormat":1},{"version":"a33ee8bd8beb3b14c3ab393b85717d7c1e5aca451ebcef09237675fa9a207389","impliedFormat":1},{"version":"6c5d50dca19d6fb862c9eac0db1b4882add3dd47a38ba5ed74b117b3860d078f","impliedFormat":1},{"version":"1f5679d1cd7b9909c1470f14350f409df0ee45c3a55d34c53f7869bf6d93b572","impliedFormat":1},{"version":"cc025898e42b57965a047d4b1d70dc6b6643bb810ee851f553fe1d25e7799b46","impliedFormat":1},{"version":"60f9540860c2d9e8f3db5dc2e84a2427e08d7d1d6024d5a1ad8ad65f2bf38548","impliedFormat":1},{"version":"5ebf34edd9ebdc3b86881a642f7f55b9d55fcd8ba8c8c518f40ac07af3dcfd65","signature":"3f10f74f88565da0caa16b822b82af2e5133479aa15d686327afc6b0b79e300c"},{"version":"c3a7f9b1d888ed2cffa734c13c578e82e88112fbd46b888aa59d1a83ddd87be7","signature":"c1617ad6c3d5b95309559587082a5eac6acbf325b752885a7e75e0a365dfde45"},{"version":"2eda764910fd6785448b72f743a76eeccf5177d748eefffcd2b8d868da2074ba","signature":"2cde6e183259fe45ce584ea7745fa693256fe8ad8d107f009dab45d1bda1f60e"},{"version":"4eb7daa1208dfbd6e18de99b604a85e1d7a08075493ae1345f10f11ae6ca3f2d","signature":"3dec2b69c402611efc627c7e24014c97d68559dd1146bef90916558c00d38609"},{"version":"a8b65e52c4cc46951462634fb162d33046cf07169322caed4d372052f8881678","signature":"fdc11746ee384183abff1c2d35012d10c00ef4cc6eb9807906e8a67ddf5434aa"},{"version":"258c6ae589642c931fad7147cd882e5b68355bf45fa7d23d77a13a04dedb070f","signature":"5d0a690327dcaac8acd803167ef86b16a01972aaba1da9a464eb4148719883b0"},{"version":"b8d8a2ea561c97be39a42db5f9c4930c0279879445ebff01e8f9dbf66b33911b","signature":"51b0b8b336830b28f2e881935ae29fb78a0011eb5f069af26a74e0a2988fb761"},{"version":"4de461353451081cb5417d625de12084061a2b38b297a1f7a230065d34f7c875","signature":"ee9d6e02d0bdeaac4b27169090d4ab179b369a3d782a74a4a426f8ef2b6672ae"},{"version":"0b9007628fa0154b36d57ad20d8a4b46e5f5bc995e87b28bba5dc8abf520f40b","signature":"05f4a98ec637e44c6cd087bcd416473d314c0e2b3b4dd4f81efaf655dc30157b"},{"version":"64821c8a45f7bdd2a03314e0b69d8e1e3623c7429cc3d58bd0cdd5afc37bed35","signature":"609b236ab6360c8864161a14213b64c4046f7321de72521b0ec3dcd5d61042e7"},{"version":"5a6e4346a4c21e8db684199c868ed7c3472e98068b0f63d88b28ec066135d081","signature":"bbd688a81cd34093141bb3550df5a33a55777dc58ff937b08e221825c1aecd49"},{"version":"e2036fa1915b00d184491aa61d351f8dc4cc25e13c5dcb61275a6bbd96580041","signature":"e61f0cfe68d4117ff6e8c8a9ea195efaee16251134986a56700dc979c46619eb"},{"version":"b29a9d2d00977c14ffb83fc566906397109d9e4b5d051d6533a09b960646ee6e","signature":"f868face382a9fbb4827fa90b96fc83840c8df770ea2e28dc474003eea777170"},{"version":"051f69724c0bb225c78858b0850ab8d3859ff8bea4dc3995cc6c15c8dca9e840","signature":"d720e6c2795c495a7dd70ab38c49526b6ef44f022202e5ce13b848f59b6ebff7"},{"version":"b6425f96b8190443d723c7bba3a8e57da96935e7dc74e17b2903088b2d7ecbed","signature":"dfedd859ccede9d9e55b127b5569e31bd00d914b18c1d6bedaa2518c26044866"},{"version":"62220f1a63680b78115e853f912295c2896ebf8f2afc09ffa602cd475afdac82","signature":"b049c82118ce5cf715658009e5486340b6177766acf0fe3606c5403a09b6529e"},{"version":"e2a4976f8f423b6f664a261798a11cbf2839bc2de6576c0433250a5f9db76926","signature":"00103ae8cd80f3aab8de0ccbc0fd90f06a49749925d818d53da3da70c958911a"},{"version":"9279f12d5b5a2f8e3f1e96621d778a2ec0c5c6aeddac785fbc95114ceea03e77","signature":"38ec9a5a9e2803d6e3c82957fa6ebb46859679344cc4c41183dd7eac3c49d510"},{"version":"834fabe6543e26e32407548963267f31e9c1c947e94326bb6ecbc61fb954bb0f","signature":"60a0d1effb60fe6bf2c1dc5fcedb04b3a1271f0c3cede1440067f4dd6ba773fd"},{"version":"da33d74a6b669effb9315cc3b4869756092db4d20f85aa00991994be294cfa7d","signature":"1cf1d54c1b1af12a4387ef383be5aa0b2198d06a38d0459a6baaecb40511b212"},{"version":"5980d0b5bec8956947a2d9935b08385d3bd8728aef5cfa8379d7b36a322af16c","signature":"f5b66913fff0b28849ecd8669745817f07bb83553758708faa4637901ea79dc4"},{"version":"263371647fc3a18a9c0a642b6e5a90ebf33691cf4beeaca739563f258fec2e5e","signature":"6781711c66a96ebbf577612bb6bea49ccec96f85846df82715d9591788d0dc99"},{"version":"eaa56b48d511283d4c3c82269c09858f6c92437c607ce7f73c1f1add6b1efd36","signature":"c84bc1375c74898c233bd3eac9e2ccd33ebfbf77979c44672e07966227523bb6"},{"version":"87fe318f4f144b0813aa01ef0b25f39be3861000f69e549d06df1901f5e0e43e","signature":"740f43342c74651336a8d5dc6fef47bb604984f4f1673eabcdda7ff24d6bb19c"},{"version":"b57ddb08ed81dfd536f4c0ae1e6e9808810a511f1deeb0f589c05300cb1eb882","signature":"d7f3d2fa4986ad6938efd2f2a04fb94177064d2b2e45b4ca5b611941ad71e625"},{"version":"bf3a1730765dbee2a2617d21dec8e617d7cab828b02572fd18370bdecf4c1994","signature":"64db83021f6309fff6c30bd5dbc2491c195b3dd37925ea33f54b1694521b191a"},{"version":"c5bc57c9e840ae049951c4a8746f95c033ddbb5d9fd6a237e171b156e713f716","signature":"eda672ebe206cb852195afcd83dc9506adeae9c54c71a76703be6cd4d1c6d9f0"},{"version":"73fb2e3411d982fe1399fa74427b0d052176779b2d553f270a145010d44e97cd","signature":"c1b646c1e1a2ec09a3ccde60cfd855f7c377edd18ab0bd53e3d39b706c56e668"},{"version":"23f0c7dc1296a1b419f3b6680cca1c061014c888b07d12a0eeecb003d62d2704","signature":"1fee75eb5ee379afd456d52434e55d81338c31e233c2689cffbe1b11bcabe1d5"},{"version":"4bc343eee8135ccb807aed2850baa2e04fcf1bf713b03a3e21c07147cb037d7e","signature":"de4ceadd913118011a57302e7121d8220e05b64c86f5d0202612b53547445a70"},{"version":"dfe7a640a85d71a1f4075140b2c65e8e78d35354a477f63fdf5c1c64b590e949","signature":"0083818293701eee235457398259e9d73682e986c94c8e829405b67e8e92e3f6"},{"version":"524c96ed4170e115ef230fd8fd531e99a7257a8a4ecd99389f2af96be6fd2c88","signature":"0f0613189f304105137f8489a5912b46f0b97394f3302431744d5dc6577d366b"},{"version":"672d61a909ff896bade9595918927154955e3cbf50e676c4d4df30cb7e470f37","signature":"39b74dd8651a88249d0aa4f3fa4bb9ae32825728ace42a2c370c3299a2f717a5"},{"version":"6ee4b629b54461a336925f859d7bf1259f4ec86aa8baad2a5276efe69b36ecd2","signature":"6716576d95bb66268cd3481e0d06b8133a0d5acf3393c96ae9f8eb8a07aa86a6"},{"version":"465f969a9b1ab10676223c18dc7d506f6ef3be63bd7a4ba9b085aee530b6282c","signature":"3834159e9beb9d89d47724d5f27ea13dbeb9b63475e7a98df15d396b3e8ffda3"},{"version":"6ee4b629b54461a336925f859d7bf1259f4ec86aa8baad2a5276efe69b36ecd2","signature":"6716576d95bb66268cd3481e0d06b8133a0d5acf3393c96ae9f8eb8a07aa86a6"},{"version":"013d54dad4917622afd222ccf9530d63a5874a6385f482b5b4afe3607b0ae279","signature":"4a6bf65706143a327eebb9b778a07adf2626aa208852c6282d77ca063ec065cb"},{"version":"964558aa33ce14d4547e1a2714370b9838cc30d067cb5b9385caf4f5d9e7c5d1","signature":"e6321d05756be5911cba0af5ab24661fe155127d6416820524f349fa7a3107e4"},{"version":"2bf0c656ae3499512d7729872de626ad9180a9370a4f69885f19d56fce4da9b8","signature":"d6091771c5bb1704dff9e886204c311d99672d8401332b731bfa7ad8501d542d"},{"version":"9bafc134417e7209fa17e1ed3ec246bcc2f924d5836869f671a16d6fc0ad2715","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"107b79f020003b72e827daf50b193a3d8feebe8135d564d7f6c476a3e222be73","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"72bfcc03197ff4936ea0071128860d4aa4568619ce3340e117d3cdc85478cc9d","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"70767e127b4c068efda49bbb084a46ccea4e140577d63ccdfbdeb94a82c6e872","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"d11dbad1c86581c6b7ada6500ce13a823b85aaf28802c86aaf9709ce46af37e4","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"a2c0ba66de4c7cca6f49996132a7d9ccd0436125ab445c8931da1e382848d71c","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"7d864d2d4021606ac0b424f67ff6ed1e24d5cfd0c0446a2b93b212a3bca93397","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"8c61ec2c3fe4aa072056dbe53b4e153e06fc9156a162827b89ad2c2742d48790","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"5d76d83b2e120ac0d333be06955449d9013cf1ac6e2adbfc5156352bc9a726ed","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"ea4aa3cc91fe87a9b3621006fd4246eb86985c79de1e8c9dec015320215c3ce3","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"6701896deab6e4a552157fac599e28a32a39b2ffb1f37f87f421d44264589a80","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"0df63a5d2fbee5d62ccdc99d242b3946a63405cd1b5868657026afdb6bd0ca50","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"2c824f29ccf772274d11d08562f345a6f7f6d20d0e68b043ae90504d2793a554","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"7acc3497425f5e8d13a9a0510704947e7be78b2100b112f412ee355442085c44","signature":"4242a68e8b52051bd39c0117621f5a228b18db15ef1f84283815cb2f577dfa67"},{"version":"4d0c0ca3c982c4d2206de9937da7e2b11db761dc61900f834f6ae1ce958891f5","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"4fabf42272e06465badb5f4ffa3d2067a8b2f7dc33b78fb0b3e7f42f4695988f","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"3ac8ff9872488b6201ff7e491f29b04df9679e69aeada70189275f17bced0029","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"13bc090b32dc0502f0a74c09cd14ff3c0c80aac45bcbc7d56f2ece0c834ba7e7","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"d22b63f3529238106eae787d97abd7e55f991722d60806cc5bdcc93b5749a217","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"43a4b188cffe143b932f07be413da10fbda9d5045028051fc044e10e83b8d951","signature":"4052355e1f53a8f1d81003ff4382f8e7a8c2b112644a68868a4d945d87d9c84e"},{"version":"3139778ec772acb88ee1bc2c079016fd6539981da7350703677cdf01f32f4b69","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"65bae9eb2d43f9aefda0e266b5244660850f82af6d9cb0cb8ea2234e5152eb97","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"5c1ba63915ce6c1897540d212526ab99af2f8526185c8dfb8e17e3df9f5bc3dd","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"91102be35a4840b04d855094fda24e1c6e67aef12412e7f6d031d0b4bae20874","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"e953359484e95b6f61ea53d887038d38f52e1f74ab3949c9825bd5ef18540561","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"f8b2908e88131f19081413c68de357a3f135f69dd392051fe3358673dc0e115d","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"b67d267231f93e446a173c48357d516cfb1a1a032776771e139f811a92a04d1c","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"fbcd6dab0943df65c478fb2c1f7a274d616627cf66609c2e501ad7cf7e3de75a","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"640ed92d28d0864d10f59bd06c136309f8540a09101ee1e53fd28cedcb11795d","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"d7195a58017d47f0da56d1f589e1258a3b9dbe6d3ec4b9bc362233b3d7e9fb49","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"58b6f534d52b5abab5a7dcb0c1897716d4dfdfc22b1b39e6921f29b3b7f38e03","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"7d1946a0e1623d1e01833d2a53b11447aab8fc8dbcef03597a43a8f0dfdd5b7d","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"f532ae482ca437c1b6921849557f15aa5db95409e1cc46ab1b1d6a48aa554fac","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"2e0726d28146febe7d18957b4866aefc5b600ce755395c007ab90a49edc0f730","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"cd583cd6119b73e6e55a02a0d3ad60d2ac9ecb477a6df9fd223dedb34a9e1c9e","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"c8f6e52f577b85a5bf701b97ab5fc8716dfd61762f7d1d7ca4ccfb1560af7f73","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"e41f5c7900600af274a728032ea2c15202b29871bc18b9c666a367baa95bfd23","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"2969bd056b3e6a0f02b3785d0709285ae30af3704d82141873e59f92f2614274","signature":"852dffdac07851cfb5c0854108f7c1feee238ef171cd0960ae26faccb8cbda04"},{"version":"f7edca37307205ecae793117856c34f7c37968283d7970c45887f398cfa338d8","signature":"f158f696582e7d1490c29860abe2ac520ddc9c7fd50e5cf478deefb6e5e83cb8"},{"version":"5a37a6c6a71c401aeeacea536d264220e4977d9cb8fbeb1123094320c9ac7ca7","signature":"1fa11e6b5bb71a46d5ced0d267425ed7a31eddcce86b9d02f26542b50c4953af"},{"version":"cdf2e570207a864a2b8d936b0dfe205ec6be58aba5d04891a6af634434ef227f","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"5bf6e294c29ee115db7bd3e110d750548d547172a303124b41b55fc5d4cfdc1c","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"7ab619fdd59b5fc00f3daa1681954ea8814aecccb28241cc23a69855cd69f021","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"b2ccbf746d368f3e0e50e69b6283a8c45bd61c875e507ccf8bda60d389c795da","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"447b54e24a6fc887b3e9cd8d9e7b915dd47bb75df28211749abf8390d2533bc4","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"4026c018f699b01a1e800f57e35f886a2f3b0e49cbfc18f47c308270a924ead9","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"36c351b105581bb0ff55970e774dd9b358af3478c784f14a32c7ff9151e16d8c","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"b143b016b03b781c642dd591b642cc15eb3b249d3dc61b025f01ca9f3f79da0c","signature":"a283527be6c5badef4c05b14b51ed2dd01fb6b1ca5e9711dddd1cf79f56727f6"},{"version":"b166ca5e6afe702a8b12b8ca853e3d89bfe5117394724f8c16511468ff7f694f","signature":"6b07eeaa1d9309d5156a9bde616b416db3624106e92856ac878b827e4dcb2e7b"},{"version":"ecba61d66cc848853b13af94a1304d5ad72b6b69994dd51ab7d27b77a631e321","signature":"f6b7ed6e72beeaee38d802539bc36942b285e75e62b4bff480c0df3fcb5936ba"},{"version":"f7a9935618d95ee3c1d4ef47775e770a57b9adebf5d0941e374f689f0ecf0c2c","signature":"efdc985497f0b6a99f9d69348cacb9fa36524801504f5b859d90d5569b35565d"},{"version":"a1e6f1bffdbd92e141063f827f4241d89ecce9238ad17a2f9a0f6d7afabf0175","signature":"ce04893ab3c81a3e4bcf2e5e941a21fffe1d817f957bc613e01d5077cb2cc0c3"},{"version":"95376ab283602759c99a544fbae38ab89b3d2743e62dd5bac3c825c53ef4c8a9","signature":"af610c52bdfed5643127a3bc6bb2dec268ae6d21c1e6a0b86f6ce4779dd853f8"},{"version":"ef10a13d74df34688ab737f9168a1d55f8198f54ea4f145c2f0a26ed63b98873","signature":"98208a770c3c215a85f7f6521fbeb6e5dbf41853bb532ef6fdb2711c99445909"},{"version":"b47af9f84ccb1ed124ea84c9066acb526fd565b2dd52fd1838f0feecb395c66e","signature":"3a0d9ab24b5fe2813990d4a70586001aa727b729410d411f67207a6fa9163b17"},"2552a31fad45a9ed1bde87e51b038dc0e786cd364b597162263abbf57018949b",{"version":"f3030bcbcca2b3e24a796f4557e51ecda6d6c5cb8cb77c6156e025889e1afcdc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"583e21c2751ee98c2bd5008b54500c005ad3374406a9735d62a1d656420a9893","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"3655119a13fbd4da1dce7ad028de9dd101aa7933e02377270541c27314140c87","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"ec21a2d78967bccbd586bd4951a4a2b95b075f4283c0e8772a0bb36394cd28b5","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"cdef56db9b2b4320e187a0a7553df60ed8f27c67f66f5eeb5dd7cfd6729747df","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"71e5f9eeebb8ec5f86791b1cdc163d1ef1042f59e821f6959c8247c20f8f006e","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"e1daa2eb447a3f06e1cecf5924764f6e94170d801ff386c27e7fec938c680e7b","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"9164b5ba6d1dcce048384b4249cfac0d51a33ced28bff3f469aed9f6211af784","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"5d12b6d84b8661cb85dc0648eccc34f5fd46b437a6e36e73935093eaa62d2952","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"106ec605800a1ea8d737cad563e5a9ad45b5a72c24fd49003c7f9acb0881913d","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"55b486bf20b4a80ff8b108ba92849167cb468e173c578c94e20c5e3d20602acb","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"421c3f008f6ef4a5db2194d58a7b960ef6f33e94b033415649cd557be09ef619","impliedFormat":1},{"version":"751764bb94219b4ce8f5475dc35d3de2e432fea01a0c9610cd7f69ad05e398c6","impliedFormat":1}],"root":[[446,450],489,584,[596,600],[602,745],[747,766],[768,788],[795,797],[802,918],[1028,1103],[1106,1133]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":1,"module":99,"skipLibCheck":true,"strict":true,"target":9},"referencedMap":[[1128,1],[1129,2],[1126,3],[1127,4],[1130,5],[1132,6],[1131,7],[1133,8],[1124,9],[1125,10],[1122,11],[450,12],[1123,13],[811,14],[812,15],[814,16],[815,17],[816,18],[817,18],[818,18],[819,18],[820,18],[804,12],[824,19],[825,20],[826,21],[827,18],[830,22],[833,23],[834,20],[840,24],[841,18],[842,20],[843,15],[844,20],[861,25],[860,26],[862,27],[868,28],[869,29],[867,30],[870,31],[872,32],[874,33],[873,27],[877,33],[878,34],[876,35],[885,36],[881,37],[888,38],[889,27],[853,39],[608,40],[854,12],[890,41],[857,42],[905,43],[907,44],[912,45],[902,46],[916,47],[1030,48],[1033,49],[917,50],[1035,51],[1039,27],[1038,52],[1040,27],[1045,53],[1052,54],[1054,55],[1056,56],[1055,57],[1058,58],[1059,59],[601,60],[597,60],[613,61],[612,62],[614,62],[615,62],[616,62],[617,63],[619,61],[618,61],[620,62],[623,64],[622,64],[624,62],[625,62],[626,62],[627,61],[611,62],[628,62],[629,61],[630,62],[631,62],[632,62],[633,65],[634,66],[636,67],[635,68],[638,69],[637,68],[641,62],[640,61],[642,62],[639,62],[643,62],[646,62],[645,62],[644,62],[647,62],[648,62],[649,62],[652,62],[651,61],[653,62],[650,62],[655,61],[656,62],[654,62],[657,62],[659,61],[658,62],[661,70],[662,62],[663,63],[664,63],[665,62],[666,62],[667,62],[668,62],[670,62],[671,63],[669,62],[673,71],[672,62],[675,62],[674,62],[676,62],[678,63],[677,62],[680,72],[679,12],[682,73],[681,73],[683,63],[684,63],[686,74],[685,62],[687,62],[688,61],[689,62],[690,61],[691,62],[693,62],[694,62],[692,62],[695,62],[696,62],[697,62],[698,62],[699,62],[700,62],[702,62],[701,62],[703,62],[704,62],[705,62],[706,62],[707,62],[708,62],[710,61],[709,62],[713,75],[714,76],[715,62],[716,63],[717,63],[711,62],[718,63],[719,62],[720,71],[721,62],[723,61],[722,62],[724,61],[725,61],[726,62],[727,61],[728,62],[729,62],[730,62],[1061,77],[1062,78],[1063,77],[731,79],[1060,80],[1068,81],[1069,81],[1070,81],[1071,81],[1072,81],[1067,81],[1073,81],[1074,81],[1075,81],[1076,81],[1077,81],[1078,81],[1079,82],[1080,81],[1081,81],[1082,81],[1083,81],[1084,81],[1085,81],[1086,81],[1087,81],[1088,81],[1089,81],[1090,81],[1091,82],[1092,81],[1093,81],[1094,81],[1095,81],[1096,81],[1097,81],[1098,81],[1099,81],[1100,81],[1065,83],[1101,81],[1066,84],[1102,81],[1103,81],[1107,85],[1108,85],[1109,81],[1110,81],[1111,81],[1112,81],[1113,81],[1114,81],[788,86],[795,87],[600,88],[796,89],[602,90],[603,91],[797,92],[604,12],[802,93],[803,94],[808,95],[605,96],[607,97],[809,94],[743,98],[742,99],[1106,100],[1064,101],[883,102],[880,103],[879,99],[913,104],[776,105],[829,106],[741,107],[733,108],[734,109],[744,110],[745,110],[747,111],[748,110],[751,112],[749,110],[750,110],[1115,12],[735,12],[813,113],[810,99],[1116,86],[832,114],[1117,115],[823,99],[836,12],[1118,116],[807,117],[839,118],[738,119],[835,99],[737,99],[739,120],[821,99],[838,121],[805,86],[822,99],[740,122],[806,12],[732,123],[837,99],[831,12],[846,92],[1119,12],[863,124],[904,106],[848,102],[1042,125],[1041,99],[855,126],[1120,127],[900,92],[866,128],[875,129],[884,130],[882,131],[886,99],[845,12],[871,99],[849,12],[847,131],[858,99],[903,106],[1034,12],[887,131],[850,132],[1046,106],[864,133],[851,134],[1043,135],[1044,135],[1048,136],[852,92],[1121,92],[918,137],[1028,138],[1029,139],[1032,140],[859,141],[856,92],[894,142],[891,143],[908,144],[910,145],[911,143],[896,146],[895,143],[906,143],[897,147],[898,143],[914,130],[915,148],[909,144],[899,143],[901,149],[1037,99],[1049,99],[1047,143],[1050,150],[1051,151],[752,12],[1053,143],[1031,152],[1057,153],[774,99],[736,99],[828,154],[892,143],[893,155],[1036,143],[865,156],[606,12],[753,157],[446,12],[746,12],[1104,12],[1105,12],[767,12],[754,158],[598,12],[599,12],[756,159],[755,160],[757,161],[447,162],[759,163],[758,12],[760,164],[762,165],[761,12],[764,166],[763,12],[765,167],[766,168],[660,12],[768,169],[770,170],[769,12],[772,171],[771,89],[773,172],[777,173],[778,174],[609,66],[779,175],[448,12],[621,12],[780,176],[610,177],[781,178],[782,164],[712,12],[783,12],[784,179],[785,180],[775,12],[787,181],[786,12],[596,160],[449,182],[489,183],[236,12],[506,12],[508,12],[509,184],[493,12],[500,185],[922,12],[921,186],[923,187],[920,12],[1134,186],[924,188],[498,12],[1135,12],[130,189],[131,189],[132,190],[91,191],[133,192],[134,193],[135,194],[86,12],[89,195],[87,12],[88,12],[136,196],[137,197],[138,198],[139,199],[140,200],[141,201],[142,201],[144,202],[143,203],[145,204],[146,205],[147,206],[129,207],[90,12],[148,208],[149,209],[150,210],[183,211],[151,212],[152,213],[153,214],[154,215],[155,216],[156,217],[157,218],[158,219],[159,220],[160,221],[161,221],[162,222],[163,12],[164,12],[165,223],[167,224],[166,225],[168,226],[169,227],[170,228],[171,229],[172,230],[173,231],[174,232],[175,233],[176,234],[177,235],[178,236],[179,237],[180,238],[181,239],[182,240],[187,241],[334,12],[188,242],[184,12],[186,243],[333,99],[253,99],[501,244],[494,12],[556,245],[555,246],[567,245],[558,247],[560,248],[579,248],[559,249],[491,250],[490,12],[496,251],[497,252],[576,253],[552,254],[554,255],[575,12],[573,254],[553,12],[495,252],[551,12],[1005,256],[1006,257],[1004,99],[1009,258],[1008,259],[1010,260],[1007,261],[1023,262],[1024,263],[1022,264],[1012,265],[1013,266],[1014,267],[1011,261],[1017,268],[1016,269],[1018,270],[1015,264],[1020,271],[1021,272],[1019,264],[1025,273],[981,99],[978,274],[975,275],[972,275],[976,276],[977,275],[974,275],[973,275],[971,264],[980,264],[979,276],[982,99],[970,277],[999,99],[997,278],[986,279],[994,280],[998,279],[988,280],[995,280],[985,279],[996,278],[989,277],[993,12],[1001,278],[1000,278],[992,279],[991,280],[983,279],[990,279],[984,280],[987,280],[1027,281],[966,261],[965,261],[963,261],[969,282],[968,278],[964,283],[967,278],[1002,278],[1026,280],[1003,277],[933,284],[962,285],[919,284],[931,286],[930,287],[928,284],[932,288],[927,289],[929,290],[925,12],[934,12],[935,284],[936,284],[947,12],[937,284],[940,291],[942,292],[941,293],[939,284],[938,12],[944,294],[943,284],[950,295],[945,284],[946,280],[949,284],[948,296],[926,284],[951,284],[952,297],[954,298],[955,299],[953,284],[956,300],[957,284],[958,301],[960,302],[961,303],[959,304],[499,12],[185,12],[592,305],[594,306],[593,307],[591,308],[590,12],[791,309],[792,309],[790,310],[543,12],[545,311],[544,12],[457,312],[459,313],[464,11],[466,314],[204,315],[240,316],[391,317],[235,318],[223,12],[219,12],[202,12],[381,319],[403,320],[203,12],[362,321],[244,322],[245,323],[332,324],[378,325],[347,326],[385,327],[386,328],[384,329],[383,12],[382,330],[242,331],[205,332],[280,12],[281,333],[222,12],[224,334],[206,335],[264,334],[351,334],[84,334],[238,336],[237,12],[390,337],[418,12],[214,12],[308,338],[309,339],[303,99],[438,12],[311,12],[312,340],[304,341],[444,342],[442,343],[437,12],[377,344],[376,12],[436,345],[305,99],[343,346],[341,347],[439,12],[443,12],[441,348],[440,12],[342,349],[476,350],[479,351],[271,352],[270,353],[269,354],[482,99],[268,355],[283,12],[423,12],[793,356],[789,12],[426,12],[798,357],[799,358],[425,99],[427,359],[80,12],[387,360],[388,361],[389,362],[226,12],[213,363],[189,12],[324,99],[82,364],[323,365],[322,366],[313,12],[314,12],[321,12],[316,12],[319,367],[315,12],[317,368],[320,369],[318,368],[201,12],[217,12],[218,334],[254,370],[255,371],[252,372],[250,373],[251,374],[247,12],[330,340],[349,340],[458,375],[467,376],[471,377],[394,378],[393,12],[79,12],[428,379],[191,380],[306,381],[307,382],[298,383],[288,12],[329,384],[289,385],[331,386],[326,387],[325,12],[327,12],[340,388],[395,389],[396,390],[291,391],[295,392],[286,393],[373,394],[190,395],[263,396],[363,397],[216,398],[419,399],[81,318],[248,12],[256,400],[413,401],[246,12],[412,402],[85,12],[410,403],[225,12],[282,404],[406,12],[207,12],[208,12],[259,405],[221,12],[400,406],[294,407],[392,408],[293,12],[258,12],[249,12],[364,409],[365,410],[220,12],[367,411],[369,412],[368,413],[228,12],[257,398],[371,414],[411,415],[414,416],[192,12],[195,12],[193,12],[197,12],[194,12],[196,12],[198,417],[200,12],[355,418],[354,12],[360,419],[356,420],[359,421],[358,421],[361,419],[357,420],[800,422],[215,423],[350,424],[399,425],[430,12],[432,426],[434,427],[290,12],[433,428],[397,389],[429,429],[310,389],[199,12],[292,430],[210,431],[211,432],[212,433],[279,434],[372,434],[265,434],[352,435],[266,435],[243,436],[209,12],[262,437],[261,438],[260,439],[353,440],[398,441],[302,442],[337,443],[301,444],[335,445],[336,445],[380,446],[379,447],[375,448],[346,449],[348,450],[345,451],[370,452],[339,12],[463,12],[338,453],[374,12],[401,454],[287,360],[285,455],[284,456],[421,457],[424,12],[420,458],[402,458],[461,12],[460,12],[462,12],[422,12],[404,459],[300,99],[456,12],[344,460],[241,12],[230,461],[296,12],[469,99],[475,462],[278,99],[473,340],[277,463],[416,464],[276,462],[83,12],[477,465],[274,99],[275,99],[267,12],[229,12],[273,466],[272,467],[227,468],[297,220],[405,220],[366,12],[408,469],[407,12],[465,12],[328,470],[299,99],[417,471],[451,99],[454,472],[455,473],[452,99],[453,12],[239,474],[234,475],[233,12],[232,476],[231,12],[415,477],[468,478],[470,479],[472,480],[794,481],[474,482],[435,483],[488,484],[478,484],[487,485],[480,486],[445,487],[801,488],[481,489],[483,490],[431,491],[486,363],[485,12],[484,492],[548,493],[511,494],[512,495],[515,496],[507,497],[514,498],[510,499],[505,12],[516,500],[517,501],[409,502],[523,12],[568,12],[492,12],[77,12],[78,12],[13,12],[14,12],[16,12],[15,12],[2,12],[17,12],[18,12],[19,12],[20,12],[21,12],[22,12],[23,12],[24,12],[3,12],[25,12],[26,12],[4,12],[27,12],[31,12],[28,12],[29,12],[30,12],[32,12],[33,12],[34,12],[5,12],[35,12],[36,12],[37,12],[38,12],[6,12],[42,12],[39,12],[40,12],[41,12],[43,12],[7,12],[44,12],[49,12],[50,12],[45,12],[46,12],[47,12],[48,12],[8,12],[54,12],[51,12],[52,12],[53,12],[55,12],[9,12],[56,12],[57,12],[58,12],[60,12],[59,12],[61,12],[62,12],[10,12],[63,12],[64,12],[65,12],[11,12],[66,12],[67,12],[68,12],[69,12],[70,12],[1,12],[71,12],[72,12],[12,12],[75,12],[74,12],[73,12],[76,12],[107,503],[117,504],[106,503],[127,505],[98,506],[97,507],[126,492],[120,508],[125,509],[100,510],[114,511],[99,512],[123,513],[95,514],[94,492],[124,515],[96,516],[101,517],[102,12],[105,517],[92,12],[128,518],[118,519],[109,520],[110,521],[112,522],[108,523],[111,524],[121,492],[103,525],[104,526],[113,527],[93,528],[116,519],[115,517],[119,12],[122,529],[578,530],[583,531],[577,532],[569,533],[565,534],[561,535],[574,12],[562,247],[588,536],[585,537],[581,538],[580,539],[563,540],[587,541],[557,12],[564,542],[582,543],[595,544],[589,545],[571,546],[572,547],[570,12],[539,548],[537,549],[538,550],[526,551],[527,549],[534,552],[525,553],[530,554],[540,12],[531,555],[536,556],[542,557],[541,558],[524,559],[532,560],[533,561],[528,562],[535,548],[529,563],[503,564],[550,565],[519,566],[504,564],[502,12],[518,567],[549,12],[547,12],[520,12],[546,568],[513,569],[522,12],[521,570],[586,12],[566,12],[584,571]],"affectedFilesPendingEmit":[1128,1129,1126,1127,1130,1132,1131,1133,1124,1125,1123,811,812,814,815,816,817,818,819,820,804,824,825,826,827,830,833,834,840,841,842,843,844,861,860,862,868,869,867,870,872,874,873,877,878,876,885,881,888,889,853,608,854,890,857,905,907,912,902,916,1030,1033,917,1035,1039,1038,1040,1045,1052,1054,1056,1055,1058,1059,601,597,613,612,614,615,616,617,619,618,620,623,622,624,625,626,627,611,628,629,630,631,632,633,634,636,635,638,637,641,640,642,639,643,646,645,644,647,648,649,652,651,653,650,655,656,654,657,659,658,661,662,663,664,665,666,667,668,670,671,669,673,672,675,674,676,678,677,680,679,682,681,683,684,686,685,687,688,689,690,691,693,694,692,695,696,697,698,699,700,702,701,703,704,705,706,707,708,710,709,713,714,715,716,717,711,718,719,720,721,723,722,724,725,726,727,728,729,730,1061,1062,1063,731,1060,1068,1069,1070,1071,1072,1067,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1065,1101,1066,1102,1103,1107,1108,1109,1110,1111,1112,1113,1114,788,795,600,796,602,603,797,604,802,803,808,605,607,809,743,742,1106,1064,883,880,879,913,776,829,741,733,734,744,745,747,748,751,749,750,1115,735,813,810,1116,832,1117,823,836,1118,807,839,738,835,737,739,821,838,805,822,740,806,732,837,831,846,1119,863,904,848,1042,1041,855,1120,900,866,875,884,882,886,845,871,849,847,858,903,1034,887,850,1046,864,851,1043,1044,1048,852,1121,918,1028,1029,1032,859,856,894,891,908,910,911,896,895,906,897,898,914,915,909,899,901,1037,1049,1047,1050,1051,752,1053,1031,1057,774,736,828,892,893,1036,865,606,753,446,754,598,599,756,755,757,447,759,758,760,762,761,764,763,765,766,660,768,770,769,772,771,773,777,778,609,779,448,621,780,610,781,782,712,783,784,785,775,787,786,596,449,584],"version":"5.7.3"} \ No newline at end of file diff --git a/demo/redteam-live-lang/vendor/agentfox-0.4.0-py3-none-any.whl b/demo/redteam-live-lang/vendor/agentfox-0.4.0-py3-none-any.whl index 92210f4b..b7695077 100644 Binary files a/demo/redteam-live-lang/vendor/agentfox-0.4.0-py3-none-any.whl and b/demo/redteam-live-lang/vendor/agentfox-0.4.0-py3-none-any.whl differ diff --git a/docs/architecture/api-spec.md b/docs/architecture/api-spec.md index bcba293f..e2b0c43d 100644 --- a/docs/architecture/api-spec.md +++ b/docs/architecture/api-spec.md @@ -108,7 +108,7 @@ from the code. Regenerate after changing any route: -277 operations, generated from the running app's OpenAPI document. Request and response schemas: `GET /openapi.json` or the interactive `/docs`. +285 operations, generated from the running app's OpenAPI document. Request and response schemas: `GET /openapi.json` or the interactive `/docs`. ### Inline enforcement (`/v1`) @@ -156,6 +156,7 @@ from the code. Regenerate after changing any route: | `GET` | `/api/agent-controls` | Kill-switch/quarantine state per agent. | | `GET` | `/api/agents` | List Agents | | `POST` | `/api/agents` | Create Agent | +| `POST` | `/api/agents/{agent_id}/approve-system` | Approve a proposed agent and every proposed agent it hands work to, in one step. | | `GET` | `/api/agents/{slug}` | Get Agent | | `PATCH` | `/api/agents/{slug}` | Update Agent | | `GET` | `/api/agents/{slug}/access` | Get Access | @@ -166,6 +167,9 @@ from the code. Regenerate after changing any route: | `POST` | `/api/agents/{slug}/breaker/reset` | Close the breaker now and let the agent's calls through. | | `POST` | `/api/agents/{slug}/enforcement` | Switch everything that protects this agent from watching to enforcing. | | `POST` | `/api/agents/{slug}/enforcement/preview` | What turning enforcement on for this agent switches, and what it would stop. | +| `GET` | `/api/agents/{slug}/features` | Get Agent Features | +| `DELETE` | `/api/agents/{slug}/features/checks/{key}` | Inherit Check | +| `DELETE` | `/api/agents/{slug}/features/protections/{key}` | Inherit Protection | | `POST` | `/api/agents/{slug}/kill` | Stop an agent now. Requires the stronger role — this is an incident action. | | `GET` | `/api/agents/{slug}/lineage` | Agent Lineage | | `GET` | `/api/agents/{slug}/map` | Which guardrails apply at each step of this agent's requests, with recent traffic. | @@ -175,6 +179,7 @@ from the code. Regenerate after changing any route: | `POST` | `/api/agents/{slug}/protection/preview` | Replay this agent's last week against the protections, as if enforcing. | | `POST` | `/api/agents/{slug}/quarantine` | Stop an agent while you investigate. Reversible and audited. | | `POST` | `/api/agents/{slug}/resume` | Restart a stopped agent. Deliberately the same role as `kill` — restarting | +| `GET` | `/api/agents/{slug}/setup` | What this project still needs, for the banner a project's pages show. | | `GET` | `/api/agents/{slug}/tool-paths` | The tool-call paths an agent took, which are new, and which no test expects. | | `POST` | `/api/agents/{slug}/tool-paths/tests` | A regression case expecting this path, so the suite checks the agent keeps it. | | `GET` | `/api/agents/{slug}/tools` | The tools this agent can be tested with: granted, in its code, or seen called, | @@ -438,6 +443,9 @@ from the code. Regenerate after changing any route: | `GET` | `/api/custom-rules/fields` | The arguments a tool is known to take, to suggest as a condition's field. | | `POST` | `/api/custom-rules/try` | Match one text against a rule that has not been saved. Records nothing. | | `DELETE` | `/api/custom-rules/{key}` | Remove Custom Rule | +| `GET` | `/api/features` | Get Features | +| `POST` | `/api/features/checks/{key}` | Switch a check on or off for every project, or for one. Switching on one that | +| `POST` | `/api/features/protections/{key}` | Set Protection | | `POST` | `/api/import/{tool}` | Apply Import | | `POST` | `/api/import/{tool}/plan` | Plan Import | | `GET` | `/api/judgment/posture` | What is in force, what may be changed, and what the deployment forbids. | diff --git a/migrations/versions/20261016_a4d2f8c6e317_agent_detector_settings.py b/migrations/versions/20261016_a4d2f8c6e317_agent_detector_settings.py new file mode 100644 index 00000000..57d3d833 --- /dev/null +++ b/migrations/versions/20261016_a4d2f8c6e317_agent_detector_settings.py @@ -0,0 +1,58 @@ +"""a project switching a detector on or off over the workspace's choice + +Adds the tenant-scoped `agent_detector_settings` table: one row per project and +detector it changed. No row means the project runs what the workspace runs, so +every existing project behaves exactly as before. + +Re-runnable: the table is created only if it is missing, because `init_db()`'s +create_all can leave a database holding this revision's schema while its recorded +revision is still the previous one. + +Revision ID: a4d2f8c6e317 +Revises: f7c3d9a2e815 +Create Date: 2026-10-16 09:00:00.000000 +""" + +from __future__ import annotations + +from collections.abc import Sequence + +import sqlalchemy as sa +from alembic import op + +revision: str = "a4d2f8c6e317" +down_revision: str | None = "f7c3d9a2e815" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def _has_table(table: str) -> bool: + return table in sa.inspect(op.get_bind()).get_table_names() + + +def upgrade() -> None: + if _has_table("agent_detector_settings"): + return + op.create_table( + "agent_detector_settings", + sa.Column("id", sa.String(length=40), nullable=False), + sa.Column("agent", sa.String(length=200), nullable=False), + sa.Column("key", sa.String(length=64), nullable=False), + sa.Column("enabled", sa.Boolean(), nullable=False), + sa.Column("updated_by", sa.String(length=200), nullable=False), + sa.Column("org_id", sa.String(length=64), nullable=False), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint( + "org_id", "agent", "key", name="ux_agent_detector_settings_org_agent_key" + ), + ) + op.create_index("ix_agent_detector_settings_agent", "agent_detector_settings", ["agent"]) + op.create_index("ix_agent_detector_settings_key", "agent_detector_settings", ["key"]) + op.create_index("ix_agent_detector_settings_org_id", "agent_detector_settings", ["org_id"]) + + +def downgrade() -> None: + if _has_table("agent_detector_settings"): + op.drop_table("agent_detector_settings") diff --git a/migrations/versions/20261016_a7d3e9c2f518_lineage_edge_kind.py b/migrations/versions/20261016_a7d3e9c2f518_lineage_edge_kind.py new file mode 100644 index 00000000..d599a558 --- /dev/null +++ b/migrations/versions/20261016_a7d3e9c2f518_lineage_edge_kind.py @@ -0,0 +1,42 @@ +"""how a delegation edge read from code hands work over + +Adds `lineage_edges.kind`: for a `delegates_to` edge a repository scan read from code, +how the parent hands work to the child (handoff, as_tool, delegates or member, see +`discovery.agents.RELATIONS`). NULL for an edge only ever seen in traffic. + +Re-runnable: `init_db()` adds this column at startup on an existing database (see +`_ADDITIVE_COLUMNS`), so the column may already be there. + +Revision ID: a7d3e9c2f518 +Revises: a4d2f8c6e317 +Create Date: 2026-10-16 10:00:00.000000 +""" + +from __future__ import annotations + +from collections.abc import Sequence + +import sqlalchemy as sa +from alembic import op + +revision: str = "a7d3e9c2f518" +down_revision: str | None = "a4d2f8c6e317" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def _has_column() -> bool: + columns = sa.inspect(op.get_bind()).get_columns("lineage_edges") + return any(c["name"] == "kind" for c in columns) + + +def upgrade() -> None: + if not _has_column(): + with op.batch_alter_table("lineage_edges") as batch_op: + batch_op.add_column(sa.Column("kind", sa.String(length=32), nullable=True)) + + +def downgrade() -> None: + if _has_column(): + with op.batch_alter_table("lineage_edges") as batch_op: + batch_op.drop_column("kind") diff --git a/src/agentfox/apps/cli/onboarding.py b/src/agentfox/apps/cli/onboarding.py index 12aea928..380624f0 100644 --- a/src/agentfox/apps/cli/onboarding.py +++ b/src/agentfox/apps/cli/onboarding.py @@ -335,6 +335,12 @@ def check( console.print(f"[bold]Scanned[/] {report.files_scanned} files in [dim]{report.root}[/]") if report.frameworks: console.print(f" [dim]built on:[/] {', '.join(report.frameworks)}") + # A multi-agent system is proposed as one agent per agent; say so here, with who + # hands work to whom, so the proposals it produces are not a surprise. + from rich.markup import escape + + for i, line in enumerate(report.agent_summary()): + console.print(f" [dim]{'agents:' if i == 0 else ' '}[/] {escape(line)}") calls = len(report.model_calls) ungoverned = len(report.ungoverned) diff --git a/src/agentfox/apps/cli/quickscan.py b/src/agentfox/apps/cli/quickscan.py index ddf3a6a4..32ce08fb 100644 --- a/src/agentfox/apps/cli/quickscan.py +++ b/src/agentfox/apps/cli/quickscan.py @@ -141,6 +141,10 @@ def quickscan( console.print(f" Scanned {repo_report.files_scanned} files in [dim]{repo_report.root}[/]") if repo_report.frameworks: console.print(f" [dim]built on:[/] {', '.join(repo_report.frameworks)}") + from rich.markup import escape + + for i, line in enumerate(repo_report.agent_summary()): + console.print(f" [dim]{'agents:' if i == 0 else ' '}[/] {escape(line)}") calls = len(repo_report.model_calls) ungoverned = len(repo_report.ungoverned) if calls: diff --git a/src/agentfox/apps/cli/wizard/backend.py b/src/agentfox/apps/cli/wizard/backend.py index ae6253e4..0c794304 100644 --- a/src/agentfox/apps/cli/wizard/backend.py +++ b/src/agentfox/apps/cli/wizard/backend.py @@ -244,6 +244,8 @@ def submit_scan(self, payload: dict[str, Any]) -> dict[str, Any]: frameworks=payload.get("frameworks", []), sites=payload.get("sites", []), author="agentfox up", + agents=payload.get("agents", []), + agent_edges=payload.get("agent_edges", []), ) run.summary_json = { "cli_command": "up", diff --git a/src/agentfox/apps/cli/wizard/components.py b/src/agentfox/apps/cli/wizard/components.py index 4af7fc1e..769dcdcd 100644 --- a/src/agentfox/apps/cli/wizard/components.py +++ b/src/agentfox/apps/cli/wizard/components.py @@ -220,7 +220,10 @@ def _scan_note(run: Run) -> str: def _apply_scan(run: Run) -> None: - payload = run.ctx.report.to_submission_payload(source="check") + # A local database is this machine: each agent's tool names may go there, so its + # project shows its tools before any traffic. Never to a server or Cloud. + local = isinstance(run.target.backend, Local) + payload = run.ctx.report.to_submission_payload(source="check", tool_names=local) payload["label"] = run.ctx.github_repo or run.ctx.project # Each recorded scan proposes its own draft agents and policies, so the same scan # sent again would pile up duplicates for someone to review. Per destination: a diff --git a/src/agentfox/apps/gateway/app.py b/src/agentfox/apps/gateway/app.py index 3c2d5408..d360e5c4 100644 --- a/src/agentfox/apps/gateway/app.py +++ b/src/agentfox/apps/gateway/app.py @@ -46,6 +46,7 @@ ) from agentfox.apps.gateway.routes import checks as check_routes from agentfox.apps.gateway.routes import custom_rules as custom_rule_routes +from agentfox.apps.gateway.routes import features as feature_routes from agentfox.apps.gateway.routes import imports as import_routes from agentfox.apps.gateway.routes import library as library_routes from agentfox.apps.gateway.routes import metrics as metrics_routes @@ -406,6 +407,7 @@ async def admission_gate(request: Request, call_next): app.include_router(access_routes.router) app.include_router(library_routes.router) app.include_router(custom_rule_routes.router) + app.include_router(feature_routes.router) app.include_router(check_routes.router) app.include_router(import_routes.router) app.include_router(protection_routes.router) diff --git a/src/agentfox/apps/gateway/routes/discovery.py b/src/agentfox/apps/gateway/routes/discovery.py index b63ad2dc..0663f202 100644 --- a/src/agentfox/apps/gateway/routes/discovery.py +++ b/src/agentfox/apps/gateway/routes/discovery.py @@ -40,6 +40,22 @@ class SubmittedSite(BaseModel): provider: str | None = None +class SubmittedAgent(BaseModel): + """One agent of a multi-agent system. No tools, file or line: those stay local.""" + + key: str + name: str + framework: str | None = None + kind: str = "agent" + top_dir: str = "root" + + +class SubmittedAgentEdge(BaseModel): + parent: str + child: str + relation: str = "delegates" + + class ScanSubmission(BaseModel): source: str # "quickscan" | "check" label: str = "repo" @@ -48,6 +64,8 @@ class ScanSubmission(BaseModel): coverage: float | None = None counts: dict[str, int] = {} sites: list[SubmittedSite] = [] + agents: list[SubmittedAgent] = [] + agent_edges: list[SubmittedAgentEdge] = [] @router.post("/api/discovery/submit", summary="Submit a redacted local scan for review") @@ -73,6 +91,8 @@ def submit_scan( frameworks=payload.frameworks, sites=[s.model_dump() for s in payload.sites], author=user.email or user.id, + agents=[a.model_dump() for a in payload.agents], + agent_edges=[e.model_dump() for e in payload.agent_edges], ) run.summary_json = { diff --git a/src/agentfox/apps/gateway/routes/features.py b/src/agentfox/apps/gateway/routes/features.py new file mode 100644 index 00000000..92a40ebc --- /dev/null +++ b/src/agentfox/apps/gateway/routes/features.py @@ -0,0 +1,151 @@ +"""Features: the workspace's switches, and each project's changes to them. + +* ``GET /api/features`` — the workspace's protections and checks +* ``GET /api/agents/{slug}/features`` — one project's, each with what it inherits +* ``POST /api/features/protections/{key}`` — set a protection for every project, or one +* ``DELETE /api/agents/{slug}/features/protections/{key}`` — back to the workspace's +* ``POST /api/features/checks/{key}`` — switch a check for every project, or one +* ``DELETE /api/agents/{slug}/features/checks/{key}`` — back to the workspace's + +The rules are in `capabilities/features.py`: a project may always be stricter, and +looser only where the workspace lets projects change that protection. +""" + +from __future__ import annotations + +from typing import Any + +from fastapi import APIRouter, Depends, HTTPException +from pydantic import BaseModel, Field +from sqlalchemy.orm import Session + +from agentfox.apps.gateway.deps import current_user, db, get_agent_or_404, require +from agentfox.apps.gateway.routes.policy import _GRANT_ROLES +from agentfox.capabilities import features +from agentfox.capabilities.detection import all_detectors +from agentfox.capabilities.detection.detector_settings import set_enabled, set_enabled_for_agent +from agentfox.core.models import User + +router = APIRouter(prefix="/api", tags=["features"]) + + +@router.get("/features") +def get_features( + session: Session = Depends(db), _user: User = Depends(current_user) +) -> dict[str, Any]: + return features.features(session) + + +@router.get("/agents/{slug}/features") +def get_agent_features( + slug: str, session: Session = Depends(db), _user: User = Depends(current_user) +) -> dict[str, Any]: + get_agent_or_404(session, slug) + return features.features(session, slug) + + +class ProtectionIn(BaseModel): + #: off | on | low | medium | high. Omit to change only `open_to_projects`. + level: str | None = None + #: Only this project; empty: every project. + agent: str = "" + #: Every project only: let projects lower or switch off this protection. + open_to_projects: bool | None = None + reason: str = Field("", max_length=500) + + +@router.post("/features/protections/{key}") +def set_protection( + key: str, + payload: ProtectionIn, + session: Session = Depends(db), + user: User = Depends(require("policy")), +) -> dict[str, Any]: + actor = user.email or user.id + try: + if payload.agent: + get_agent_or_404(session, payload.agent) + if payload.level is None: + raise HTTPException(400, "a project change needs a level") + features.set_project_protection( + session, payload.agent, key, payload.level, actor=actor, reason=payload.reason + ) + return features.features(session, payload.agent) + if payload.open_to_projects and user.role not in _GRANT_ROLES: + raise HTTPException( + 403, f"role '{user.role}' may not let projects lower a protection. Ask an admin." + ) + features.set_workspace_protection( + session, + key, + level=payload.level, + open_to_projects=payload.open_to_projects, + actor=actor, + reason=payload.reason, + ) + return features.features(session) + except features.FeatureLocked as exc: + raise HTTPException(409, str(exc)) from exc + except ValueError as exc: + raise HTTPException(400, str(exc)) from exc + + +@router.delete("/agents/{slug}/features/protections/{key}") +def inherit_protection( + slug: str, + key: str, + session: Session = Depends(db), + user: User = Depends(require("policy")), +) -> dict[str, Any]: + get_agent_or_404(session, slug) + try: + features.set_project_protection(session, slug, key, None, actor=user.email or user.id) + except ValueError as exc: + raise HTTPException(400, str(exc)) from exc + return features.features(session, slug) + + +class CheckIn(BaseModel): + enabled: bool + agent: str = "" + reason: str = Field("", max_length=500) + + +@router.post("/features/checks/{key}") +def set_check( + key: str, + payload: CheckIn, + session: Session = Depends(db), + user: User = Depends(require("suppressions")), +) -> dict[str, Any]: + """Switch a check on or off for every project, or for one. Switching on one that + is not installed is refused: it would look on and check nothing.""" + detector = all_detectors().get(key) + if detector is None: + raise HTTPException(404, f"unknown check '{key}'") + if payload.enabled and not detector.available(): + raise HTTPException(409, f"'{key}' is not installed in this deployment") + actor = user.email or user.id + try: + if payload.agent: + get_agent_or_404(session, payload.agent) + set_enabled_for_agent( + session, payload.agent, key, payload.enabled, actor=actor, reason=payload.reason + ) + return features.features(session, payload.agent) + set_enabled(session, key, payload.enabled, actor=actor, reason=payload.reason) + except ValueError as exc: + raise HTTPException(400, str(exc)) from exc + return features.features(session) + + +@router.delete("/agents/{slug}/features/checks/{key}") +def inherit_check( + slug: str, + key: str, + session: Session = Depends(db), + user: User = Depends(require("suppressions")), +) -> dict[str, Any]: + get_agent_or_404(session, slug) + set_enabled_for_agent(session, slug, key, None, actor=user.email or user.id, reason="") + return features.features(session, slug) diff --git a/src/agentfox/apps/gateway/routes/integrations.py b/src/agentfox/apps/gateway/routes/integrations.py index 8b4c50d5..64142bd6 100644 --- a/src/agentfox/apps/gateway/routes/integrations.py +++ b/src/agentfox/apps/gateway/routes/integrations.py @@ -589,6 +589,10 @@ def _top_dir(rel_path: str) -> str: if s.kind in ("agent_definition", "tool", "model_call", "lethal_trifecta") ], author=user.email or user.id, + # Read server-side from the downloaded code, so each agent carries its own + # tools as well (a laptop's `--submit` does not send them). + agents=[{**a.to_json(), "top_dir": a.top_dir} for a in report.agents], + agent_edges=[e.to_json() for e in report.agent_edges], ) raise_scan_findings(session, payload.repo_full_name, report, all_skill_issues) @@ -1068,6 +1072,48 @@ def approve_agent( return {"id": agent.id, "status": agent.status} +@router.post("/api/agents/{agent_id}/approve-system") +def approve_agent_system( + agent_id: str, session: Session = Depends(db), user: User = Depends(require("registry")) +) -> dict[str, Any]: + """Approve a proposed agent and every proposed agent it hands work to, in one step. + + A scan proposes a multi-agent system as one draft per agent, joined by the + delegations its code declares. Adding the orchestrator without its sub-agents + leaves a live agent handing work to drafts nothing governs, so the Proposed list + offers this beside each system. Each agent gets the same state change and ledger + entry as approving it on its own (`approve_agent`); one already added is skipped. + """ + from agentfox.capabilities.protection.scope import delegates + from agentfox.core.models import Agent + + root = session.get(Agent, agent_id) + if root is None: + raise HTTPException(404, "no agent with that id") + slugs = [root.slug, *delegates(session, root.slug)] + drafts = list( + session.scalars(select(Agent).where(Agent.slug.in_(slugs), Agent.status == "draft")) + ) + if not drafts: + raise HTTPException(404, "nothing in this system is waiting to be added") + actor = user.email or user.id + drafts.sort(key=lambda a: slugs.index(a.slug)) + for agent in drafts: + agent.status = "active" + agent.registered = True + session.flush() + chain.append( + session, + "agent.draft.approved", + actor_type="user", + actor_id=actor, + subject_type="agent", + subject_id=agent.id, + ) + session.commit() + return {"id": root.id, "approved": [a.slug for a in drafts]} + + @router.post("/api/agents/{agent_id}/reject") def reject_agent( agent_id: str, session: Session = Depends(db), user: User = Depends(require("registry")) diff --git a/src/agentfox/apps/gateway/routes/protection.py b/src/agentfox/apps/gateway/routes/protection.py index b0c0dea5..92e1393b 100644 --- a/src/agentfox/apps/gateway/routes/protection.py +++ b/src/agentfox/apps/gateway/routes/protection.py @@ -86,7 +86,7 @@ def get_protection( ) -> dict[str, Any]: get_agent_or_404(session, slug) detectors = all_detectors() - on = enabled_for(session) + on = enabled_for(session, slug) body = protection.state(session, slug).to_json() for p in body["protections"]: for field in ("needs", "better"): @@ -299,3 +299,47 @@ def enforce_agent( ) switched.append({"key": pack["key"], "label": pack["label"]}) return {"agent": slug, "enforcing": switched} + + +@router.get("/{slug}/setup") +def project_setup( + slug: str, session: Session = Depends(db), _user: User = Depends(current_user) +) -> dict[str, Any]: + """What this project still needs, for the banner a project's pages show. + + Computed live, like the workspace checklist (`/api/onboarding`). The steps are in + the order they unblock each other: a proposed project is added first, nothing can + be judged before traffic arrives, tools decide what it may do, and blocking comes + last because it is only safe once watching looks right. ``next`` is the first + one not done; None when the project is fully set up. + """ + from sqlalchemy import func, select + + from agentfox.capabilities.protection.map import agent_tools + from agentfox.core.models import Trace + + agent = get_agent_or_404(session, slug) + traces = int( + session.scalar(select(func.count()).select_from(Trace).where(Trace.agent_slug == slug)) or 0 + ) + tools = agent_tools(session, slug).get("tools", []) + granted = sum(1 for t in tools if "granted" in (t.get("sources") or [])) + watching = [{"key": p["key"], "label": p["label"]} for p in _watching_packs(session, slug)] + steps = [ + {"id": "add", "done": agent.status != "draft"}, + {"id": "traffic", "done": traces > 0}, + {"id": "tools", "done": granted > 0 or not tools}, + {"id": "enforce", "done": not watching}, + ] + return { + "agent": slug, + "name": agent.name or slug, + "id": agent.id, + "status": agent.status, + "traces": traces, + "tools": len(tools), + "granted": granted, + "watching": watching, + "steps": steps, + "next": next((s["id"] for s in steps if not s["done"]), None), + } diff --git a/src/agentfox/apps/gateway/routes/registry.py b/src/agentfox/apps/gateway/routes/registry.py index baa560b0..9aea61f2 100644 --- a/src/agentfox/apps/gateway/routes/registry.py +++ b/src/agentfox/apps/gateway/routes/registry.py @@ -28,6 +28,7 @@ Finding, Handoff, Identity, + LineageEdge, McpServer, Tool, User, @@ -209,8 +210,23 @@ def list_agents( query = query.where(Agent.registered.is_(registered)) if environment: query = query.where(Agent.environment == environment) + # Who each agent hands work to, from code or traffic: what lets the Proposed list + # show a scanned multi-agent system as one system with its members under it. + children: dict[str, list[dict[str, Any]]] = {} + for edge in session.scalars( + select(LineageEdge) + .where(LineageEdge.relation == "delegates_to", LineageEdge.dst_type == "agent") + .order_by(LineageEdge.src_id, LineageEdge.dst_id) + ): + if edge.src_id != edge.dst_id: + children.setdefault(edge.src_id, []).append( + {"slug": edge.dst_id, "relation": edge.kind, "declared": bool(edge.declared)} + ) return { - "agents": [_agent_json(a, session) for a in session.scalars(query)], + "agents": [ + {**_agent_json(a, session), "children": children.get(a.slug, [])} + for a in session.scalars(query) + ], "inventory": inventory(session), } diff --git a/src/agentfox/capabilities/detection/detector_settings.py b/src/agentfox/capabilities/detection/detector_settings.py index 29ee0dfc..76e82166 100644 --- a/src/agentfox/capabilities/detection/detector_settings.py +++ b/src/agentfox/capabilities/detection/detector_settings.py @@ -5,8 +5,14 @@ model-backed detector they installed, or switch a noisy one off, from the dashboard instead of an environment variable and a restart. -Switching a detector off silences it for the whole workspace, which makes it a -privileged operator action recorded like a suppression (`operator_log.PRIVILEGED`). +A project can change the workspace's choice for itself (`AgentDetectorSetting`), in +either direction: switch on a detector only it needs, or switch off one that is +noise for it. No row means it runs what the workspace runs, so a change for the +workspace reaches every project that has not changed that detector. + +Switching a detector off silences it for the whole workspace, or for one project, +which makes both privileged operator actions recorded like a suppression +(`operator_log.PRIVILEGED`). """ from __future__ import annotations @@ -15,7 +21,7 @@ from sqlalchemy.orm import Session from agentfox.core.config import get_settings -from agentfox.core.models import DetectorSetting +from agentfox.core.models import AgentDetectorSetting, DetectorSetting from agentfox.platform.ledger import operator_log #: Never switchable off per workspace: the always-on safety net, and the detector @@ -36,8 +42,19 @@ def overrides(session: Session) -> dict[str, bool]: WORKSPACE_VALIDATORS = "custom.validators" -def enabled_for(session: Session) -> frozenset[str]: - """The detectors this workspace runs: the deployment's list, plus its overrides. +def agent_overrides(session: Session, agent: str) -> dict[str, bool]: + """What one project changed, by detector key.""" + return { + row.key: row.enabled + for row in session.scalars( + select(AgentDetectorSetting).where(AgentDetectorSetting.agent == agent) + ) + } + + +def enabled_for(session: Session, agent: str | None = None) -> frozenset[str]: + """The detectors this workspace runs (or one project in it): the deployment's + list, plus the workspace's overrides, plus the project's. `ALWAYS_ON` limits what a workspace may switch off; it does not add to the deployment's list. A deployment that runs no detectors at all (a bypass, a @@ -53,11 +70,15 @@ def enabled_for(session: Session) -> frozenset[str]: if any_enabled(session): enabled.add(DETECTOR_KEY) - for key, on in overrides(session).items(): - if on: - enabled.add(key) - elif key not in ALWAYS_ON: - enabled.discard(key) + layers = [overrides(session)] + if agent: + layers.append(agent_overrides(session, agent)) + for layer in layers: + for key, on in layer.items(): + if on: + enabled.add(key) + elif key not in ALWAYS_ON: + enabled.discard(key) return frozenset(enabled) @@ -76,6 +97,10 @@ def enabled_anywhere(session: Session) -> frozenset[str]: with system_scope("warming the detectors any workspace switched on", routine=True): switched_on = set( session.scalars(select(DetectorSetting.key).where(DetectorSetting.enabled.is_(True))) + ) | set( + session.scalars( + select(AgentDetectorSetting.key).where(AgentDetectorSetting.enabled.is_(True)) + ) ) return frozenset(deployment) | switched_on @@ -104,3 +129,40 @@ def set_enabled( after={"enabled": enabled}, ) return row + + +def set_enabled_for_agent( + session: Session, agent: str, key: str, enabled: bool | None, *, actor: str, reason: str +) -> AgentDetectorSetting | None: + """Switch a detector on or off for one project; ``None`` goes back to the workspace's.""" + if enabled is False and key in ALWAYS_ON: + raise ValueError(f"'{key}' cannot be switched off") + row = session.scalar( + select(AgentDetectorSetting).where( + AgentDetectorSetting.agent == agent, AgentDetectorSetting.key == key + ) + ) + before = row.enabled if row else None + if enabled is None: + if row is not None: + session.delete(row) + row = None + else: + if row is None: + row = AgentDetectorSetting(agent=agent, key=key) + session.add(row) + row.enabled = enabled + row.updated_by = actor + session.flush() + word = {True: "on", False: "off", None: "back to the workspace setting"}[enabled] + operator_log.record( + session, + "operator.agent_detector.toggled", + actor=actor, + reason=reason or f"detector '{key}' {word} for agent '{agent}'", + subject_type="agent", + subject_id=agent, + before={"detector": key, "enabled": before}, + after={"detector": key, "enabled": enabled}, + ) + return row diff --git a/src/agentfox/capabilities/discovery/agents.py b/src/agentfox/capabilities/discovery/agents.py new file mode 100644 index 00000000..c8fe2408 --- /dev/null +++ b/src/agentfox/capabilities/discovery/agents.py @@ -0,0 +1,1205 @@ +"""Multi-agent structure, read statically: which agents a repository defines, which +tools each one is handed, and which agent hands work to which. + +The repo scanner (:mod:`agentfox.capabilities.discovery.repo`) answers "where does this +code talk to a model". For a multi-agent system that answer is not enough to govern +it: a triage agent that can hand off to a refunds agent is two things with two tool +sets, and a guardrail meant for "the refunds agent and everything it delegates to" has +nothing to attach to until the parts have names. Before this, the only source of that +structure was runtime traffic (``derive_lineage`` turning ``subagent`` spans into +``delegates_to`` edges), so a freshly scanned repository came out as one blob per +directory and the hierarchy appeared only after the system had already run ungoverned. + +Same rules as the rest of discovery: **no import, no execution**. Python is read with +``ast``; TypeScript and JavaScript with a small brace-aware reader over the source, +for the one shape that is unambiguous there (OpenAI Agents SDK's ``new Agent({...})``). +A constructor counts only when the file imported it from the framework it belongs to, +because ``Agent`` and ``Crew`` are names any codebase may define for itself. + +What is recognised, per framework: + +* **OpenAI Agents SDK** — ``Agent(name=..., tools=[...], handoffs=[...])``; + ``handoff(agent)`` inside ``handoffs``; ``other.as_tool(...)`` inside ``tools``. +* **LangGraph** — ``create_react_agent`` / ``create_agent``; ``create_supervisor`` + (supervisor delegates to each worker); ``create_swarm`` (members, plus + ``create_handoff_tool(agent_name=...)`` handoffs); ``StateGraph`` nodes, where a node + is an agent when it *is* one (a react agent, a compiled subgraph), calls one, or binds + tools to a model, and a node that routes to agent nodes (conditional edges or + ``Command(goto=...)``) is their supervisor. +* **CrewAI** — ``Agent(role=..., tools=[...])``, ``Crew(agents=[...])`` (members, or + delegation under a hierarchical process or a ``manager_agent``), ``allow_delegation``, + and the ``@CrewBase`` class layout (``@agent`` methods, ``agents=self.agents``). +* **AutoGen** — AgentChat's ``AssistantAgent`` and friends with ``tools`` and Swarm + ``handoffs``; ``RoundRobinGroupChat`` / ``SelectorGroupChat`` / ``Swarm`` / + ``MagenticOneGroupChat`` teams; AG2's ``GroupChat`` and the ``GroupChatManager`` + that runs it. +* **Google ADK** — ``Agent`` / ``LlmAgent`` ``sub_agents`` and ``AgentTool``, and the + ``Sequential``/``Parallel``/``Loop`` workflow agents. + +A variable is resolved to the agent it holds inside its own file first, then by a +repository-wide lookup of that name when exactly one file defines it, which covers the +common ``from .billing import billing_agent`` layout without following imports. +Anything that cannot be resolved is left out rather than guessed: a wrong edge in a +delegation graph widens what an "apply to sub-agents" policy reaches. +""" + +from __future__ import annotations + +import ast +import re +from collections import defaultdict +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + +#: How a parent hands work to a child, strongest claim first: when the same pair is +#: related twice (a crew member that is also the manager's delegate), the first wins. +#: +#: * ``handoff`` — the parent passes the conversation over (OpenAI handoffs, Swarm). +#: * ``as_tool`` — the parent calls the child like a tool and keeps the conversation. +#: * ``delegates`` — the parent decides which child works next (a supervisor, a +#: hierarchical crew's manager, ADK ``sub_agents``). +#: * ``member`` — the child is one of a team the parent runs (a crew, a group chat, a +#: graph's agent nodes). +RELATIONS = ("handoff", "as_tool", "delegates", "member") + + +@dataclass +class AgentSpec: + """One agent (or the crew, team or graph that runs several) found in code.""" + + #: Unique within one scan and stable across scans while the name is unchanged: + #: the proposed agent's slug is built from it. + key: str + name: str + framework: str + #: agent | supervisor | crew | team | graph + kind: str + file: str + line: int + tools: list[str] = field(default_factory=list) + + @property + def top_dir(self) -> str: + parts = Path(self.file).parts + return parts[0] if len(parts) > 1 else "root" + + def to_json(self) -> dict[str, Any]: + return { + "key": self.key, + "name": self.name, + "framework": self.framework, + "kind": self.kind, + "file": self.file, + "line": self.line, + "tools": self.tools, + } + + +@dataclass +class AgentEdge: + """``parent`` hands work to ``child`` (both :attr:`AgentSpec.key`).""" + + parent: str + child: str + relation: str + + def to_json(self) -> dict[str, str]: + return {"parent": self.parent, "child": self.child, "relation": self.relation} + + +# --------------------------------------------------------------------------- +# Raw extraction — one file at a time, references left unresolved +# --------------------------------------------------------------------------- + +#: A reference to an agent, resolved once every file has been read: +#: ("idx", n) a raw definition; ("var", name) a variable or factory function; +#: ("label", text) an agent's or graph node's own name; ("class", name) every +#: ``@agent`` method of a CrewBase class. +Ref = tuple[str, Any] + + +@dataclass +class _Raw: + idx: int + framework: str + kind: str + file: str + line: int + name: str | None = None + var: str | None = None + cls: str | None = None + tools: list[tuple[str, str]] = field(default_factory=list) # ("name"|"var", text) + children: list[tuple[Ref, str]] = field(default_factory=list) + #: CrewAI: this agent may ask the crew's other members for help. + allow_delegation: bool = False + #: AG2: the GroupChat a GroupChatManager runs; its members become the manager's. + absorbs: Ref | None = None + #: CrewAI: a crew's ``manager_agent``, who delegates to the crew's ``members``. + manager: Ref | None = None + members: list[Ref] = field(default_factory=list) + #: Set when a raw turns out to be nothing worth reporting (an AG2 GroupChat folded + #: into its manager, a graph with no agent nodes). + dropped: bool = False + + +@dataclass +class _Node: + name: str + #: What the node runs, as an agent reference when it can be one. + ref: Ref | None + func: ast.FunctionDef | ast.AsyncFunctionDef | None + #: Names the node function reads: an agent among them is the agent it runs. + loads: set[str] = field(default_factory=set) + #: Tools the node function binds to its model, or None when it binds none. + bound_tools: list[tuple[str, str]] | None = None + + +@dataclass +class _Graph: + raw: int + file: str + nodes: dict[str, _Node] = field(default_factory=dict) + #: (source, target, how) with how in "edge" | "route" + routes: list[tuple[str, str, str]] = field(default_factory=list) + + +@dataclass +class _FileResult: + file: str + raws: list[_Raw] = field(default_factory=list) + graphs: list[_Graph] = field(default_factory=list) + #: variable -> raw index, for this file + vars: dict[str, int] = field(default_factory=dict) + #: function or variable name -> tool name, for this file + tool_defs: dict[str, str] = field(default_factory=dict) + #: module-level variable names this file defines (for cross-file lookup) + exported: set[str] = field(default_factory=set) + #: `triage = build_triage()` — a variable holding what a factory returns + aliases: dict[str, str] = field(default_factory=dict) + + +#: Constructor -> (framework, role). Matched on the import-resolved dotted path's root +#: package and final name, so `from crewai import Agent` and `crewai.Agent` both match +#: and a local `Agent` class does not. +_AUTOGEN_CHAT_AGENTS = { + "AssistantAgent", + "UserProxyAgent", + "CodeExecutorAgent", + "SocietyOfMindAgent", +} +_AG2_AGENTS = {"AssistantAgent", "UserProxyAgent", "ConversableAgent"} + + +def _constructor(qual: str) -> tuple[str, str] | None: + root = qual.split(".")[0] + last = qual.rsplit(".", 1)[-1] + if root == "agents" and last == "Agent": + return "OpenAI Agents SDK", "openai_agent" + if root == "crewai": + if last == "Agent": + return "CrewAI", "crew_agent" + if last == "Crew": + return "CrewAI", "crew" + if root == "autogen_agentchat": + if last in _AUTOGEN_CHAT_AGENTS: + return "AutoGen", "autogen_agent" + if last in ("RoundRobinGroupChat", "Swarm"): + return "AutoGen", "team_member" + if last in ("SelectorGroupChat", "MagenticOneGroupChat"): + return "AutoGen", "team_delegates" + if root in ("autogen", "ag2"): + if last in _AG2_AGENTS: + return "AutoGen", "autogen_agent" + if last == "GroupChat": + return "AutoGen", "groupchat" + if last == "GroupChatManager": + return "AutoGen", "gc_manager" + if qual.startswith("google.adk"): + if last in ("Agent", "LlmAgent"): + return "Google ADK", "adk_agent" + if last in ("SequentialAgent", "ParallelAgent", "LoopAgent"): + return "Google ADK", "adk_workflow" + if root == "langgraph": + if last == "create_react_agent": + return "LangGraph", "react_agent" + if last == "StateGraph": + return "LangGraph", "graph" + if root == "langchain" and last == "create_agent": + return "LangGraph", "react_agent" + if root == "langgraph_supervisor" and last == "create_supervisor": + return "LangGraph", "supervisor" + if root == "langgraph_swarm" and last == "create_swarm": + return "LangGraph", "swarm" + return None + + +_TOOL_DECORATORS = ("tool", "function_tool", "mcp.tool", "agent.tool", "register_tool") +_TOOL_CONSTRUCTORS = ("Tool", "StructuredTool", "StructuredTool.from_function", "FunctionTool") + +#: Cheap pre-filter, so most of a repository is not parsed a second time: a file has +#: to import one of the frameworks above (every constructor is import-gated anyway) or +#: define a decorated tool an agent in another file may name. +_HINT = re.compile( + r"(?:from|import)\s+(?:agents|crewai|autogen\w*|ag2|google\.adk|langgraph\w*|langchain)\b" + r"|@openai/agents|@(?:\w+\.)?(?:function_)?tool\b" +) + + +def _path(node: ast.AST) -> str: + parts: list[str] = [] + while isinstance(node, ast.Attribute): + parts.append(node.attr) + node = node.value + if isinstance(node, ast.Name): + parts.append(node.id) + else: + return "" + return ".".join(reversed(parts)) + + +def _str(node: ast.AST | None) -> str | None: + if isinstance(node, ast.Constant) and isinstance(node.value, str): + return node.value + return None + + +def _strings(node: ast.AST | None) -> list[str]: + """Every string literal a node is or lists (``"a"``, ``["a", "b"]``).""" + if node is None: + return [] + if (s := _str(node)) is not None: + return [s] + if isinstance(node, ast.List | ast.Tuple | ast.Set): + return [s for e in node.elts if (s := _str(e)) is not None] + return [] + + +def _kw(call: ast.Call, *names: str) -> ast.AST | None: + for kw in call.keywords: + if kw.arg in names: + return kw.value + return None + + +def _decorator_name(node: ast.AST) -> str: + if isinstance(node, ast.Call): + node = node.func + return _path(node) + + +class _Extractor: + """Reads one Python file into raw definitions with unresolved references.""" + + def __init__(self, tree: ast.Module, rel: str, start: int) -> None: + self.tree = tree + self.rel = rel + self.out = _FileResult(file=rel) + self.next_idx = start + self.imports: dict[str, str] = {} + #: id(call) -> (var, enclosing class) for `x = Call(...)` and `return Call(...)` + self.call_target: dict[int, tuple[str | None, str | None]] = {} + #: id(call) -> raw index + self.call_raw: dict[int, int] = {} + #: variable -> list literal it was assigned (tools = [a, b]) + self.lists: dict[str, ast.AST] = {} + self.functions: dict[str, ast.FunctionDef | ast.AsyncFunctionDef] = {} + #: id(call) of every `return Crew(...)` inside a CrewBase `@crew` method + self.crew_methods: set[int] = set() + #: raw index -> (its constructor call, its role) + self.made: dict[int, tuple[ast.Call, str]] = {} + + # -- pass A: imports, assignments, functions, tools ---------------------- + + def collect(self) -> None: + for node in ast.walk(self.tree): + if isinstance(node, ast.Import): + for alias in node.names: + self.imports[alias.asname or alias.name.split(".")[0]] = ( + alias.name if alias.asname else alias.name.split(".")[0] + ) + elif isinstance(node, ast.ImportFrom) and node.module and not node.level: + for alias in node.names: + self.imports[alias.asname or alias.name] = f"{node.module}.{alias.name}" + for stmt in self.tree.body: + for target in _assigned_names(stmt): + self.out.exported.add(target) + if isinstance(stmt, ast.FunctionDef | ast.AsyncFunctionDef): + self.out.exported.add(stmt.name) + self._walk_scope(self.tree.body, cls=None) + + def _walk_scope(self, body: list[ast.stmt], cls: str | None) -> None: + for stmt in body: + if isinstance(stmt, ast.ClassDef): + self._walk_scope(stmt.body, cls=stmt.name) + continue + if isinstance(stmt, ast.FunctionDef | ast.AsyncFunctionDef): + self.functions.setdefault(stmt.name, stmt) + self._check_tool(stmt) + decorators = {_decorator_name(d).rsplit(".", 1)[-1] for d in stmt.decorator_list} + for sub in ast.walk(stmt): + if isinstance(sub, ast.Return) and isinstance(sub.value, ast.Call): + # `def researcher(self): return Agent(...)` — the agent is named + # by its factory, which is how CrewBase and most builders work. + self.call_target.setdefault(id(sub.value), (stmt.name, cls)) + if "crew" in decorators: + self.crew_methods.add(id(sub.value)) + self._walk_scope(stmt.body, cls=cls) + continue + self._note_assign(stmt, cls) + for child_body in _child_bodies(stmt): + self._walk_scope(child_body, cls=cls) + + def _note_assign(self, stmt: ast.stmt, cls: str | None) -> None: + value: ast.AST | None = None + targets: list[str] = [] + if isinstance(stmt, ast.Assign): + value = stmt.value + targets = [_target_name(t) for t in stmt.targets] + elif isinstance(stmt, ast.AnnAssign) and stmt.value is not None: + value = stmt.value + targets = [_target_name(stmt.target)] + targets = [t for t in targets if t] + if value is None or not targets: + return + var = targets[0] + if isinstance(value, ast.Call): + self.call_target[id(value)] = (var, cls) + if isinstance(value.func, ast.Name): + self.out.aliases.setdefault(var, value.func.id) + path = _path(value.func) + if path in _TOOL_CONSTRUCTORS or any( + path.endswith(f".{c}") for c in _TOOL_CONSTRUCTORS + ): + name = _str(_kw(value, "name")) + if name: + self.out.tool_defs[var] = name + elif isinstance(value, ast.List | ast.Tuple): + self.lists[var] = value + + def _check_tool(self, fn: ast.FunctionDef | ast.AsyncFunctionDef) -> None: + for decorator in fn.decorator_list: + name = _decorator_name(decorator) + if any(name.endswith(marker) for marker in _TOOL_DECORATORS): + explicit = None + if isinstance(decorator, ast.Call) and decorator.args: + explicit = _str(decorator.args[0]) + if isinstance(decorator, ast.Call): + explicit = explicit or _str(_kw(decorator, "name_override", "name")) + self.out.tool_defs[fn.name] = explicit or fn.name + return + + # -- pass B: one raw per recognised constructor ------------------------- + + def qual(self, func: ast.AST) -> str: + path = _path(func) + if not path: + return "" + head, _, rest = path.partition(".") + base = self.imports.get(head) + if base is None: + return "" + return f"{base}.{rest}" if rest else base + + def create(self) -> None: + for node in ast.walk(self.tree): + if not isinstance(node, ast.Call): + continue + match = _constructor(self.qual(node.func)) + if match is None: + continue + framework, role = match + kwargs = {kw.arg for kw in node.keywords if kw.arg} + # `agents` is also the name of plenty of local packages; the SDK's Agent is + # always given a name, and an instruction, tools or handoffs besides. + if role == "openai_agent" and not ( + "name" in kwargs and kwargs & {"instructions", "tools", "handoffs", "model"} + ): + continue + var, cls = self.call_target.get(id(node), (None, None)) + raw = _Raw( + idx=self.next_idx, + framework=framework, + kind=_KIND[role], + file=self.rel, + line=node.lineno, + var=var, + cls=cls, + ) + raw.name = self._name(node, role, var, cls) + self.next_idx += 1 + self.call_raw[id(node)] = raw.idx + self.out.raws.append(raw) + if var: + self.out.vars.setdefault(var, raw.idx) + self.made[raw.idx] = (node, role) + + def _name(self, call: ast.Call, role: str, var: str | None, cls: str | None) -> str | None: + if role == "crew_agent": + return _str(_kw(call, "role")) or var + if role == "crew": + if id(call) in self.crew_methods and cls: + return cls + return _str(_kw(call, "name")) or var + if role == "autogen_agent": + first = call.args[0] if call.args else None + return _str(_kw(call, "name")) or _str(first) or var + if role == "supervisor": + return _str(_kw(call, "supervisor_name")) or "supervisor" + if role == "graph": + return var + return _str(_kw(call, "name")) or var + + # -- pass C: tools and children ----------------------------------------- + + def link(self) -> None: + for raw in list(self.out.raws): + call, role = self.made[raw.idx] + if role in ("openai_agent", "crew_agent", "autogen_agent", "adk_agent", "react_agent"): + tools_expr = _kw(call, "tools") + if tools_expr is None and role == "react_agent" and len(call.args) > 1: + tools_expr = call.args[1] + self._tools(raw, tools_expr) + if role == "openai_agent": + for elt in self._elements(_kw(call, "handoffs")): + if ( + isinstance(elt, ast.Call) + and _path(elt.func).rsplit(".", 1)[-1] == "handoff" + ): + elt = elt.args[0] if elt.args else _kw(elt, "agent") + self._child(raw, elt, "handoff") + elif role == "crew_agent": + delegation = _kw(call, "allow_delegation") + raw.allow_delegation = isinstance(delegation, ast.Constant) and bool( + delegation.value + ) + elif role == "crew": + manager = _kw(call, "manager_agent") + process = _path(_kw(call, "process") or ast.Constant(None)) + members = [ + ("class", raw.cls) if ref[0] == "class" else ref + for elt in self._elements(_kw(call, "agents")) + if (ref := self._ref(elt)) is not None + ] + if manager is not None: + # crew -> its manager -> the members the manager assigns work to. + raw.manager = self._ref(manager) + raw.members = members + else: + # A hierarchical crew without a named manager gets one from CrewAI + # itself, so the crew decides who works next rather than just + # running them in order. + relation = "delegates" if process.endswith("hierarchical") else "member" + raw.children.extend((m, relation) for m in members) + elif role == "autogen_agent": + for label in _strings(_kw(call, "handoffs")): + raw.children.append((("label", label), "handoff")) + for elt in self._elements(_kw(call, "handoffs")): + if isinstance(elt, ast.Call): + target = _str(_kw(elt, "target")) or ( + _str(elt.args[0]) if elt.args else None + ) + if target: + raw.children.append((("label", target), "handoff")) + elif role in ("team_member", "team_delegates"): + members = _kw(call, "participants") or (call.args[0] if call.args else None) + for elt in self._elements(members): + self._child(raw, elt, "member" if role == "team_member" else "delegates") + elif role == "groupchat": + members = _kw(call, "agents") or (call.args[0] if call.args else None) + for elt in self._elements(members): + self._child(raw, elt, "delegates") + elif role == "gc_manager": + group = _kw(call, "groupchat") or (call.args[0] if call.args else None) + raw.absorbs = self._ref(group) if group is not None else None + elif role in ("adk_agent", "adk_workflow"): + relation = "delegates" if role == "adk_agent" else "member" + for elt in self._elements(_kw(call, "sub_agents")): + self._child(raw, elt, relation) + elif role in ("supervisor", "swarm"): + members = _kw(call, "agents") or (call.args[0] if call.args else None) + for elt in self._elements(members): + self._child(raw, elt, "delegates" if role == "supervisor" else "member") + if role == "supervisor": + self._tools(raw, _kw(call, "tools")) + elif role == "graph": + self._graph(raw) + + def _elements(self, expr: ast.AST | None) -> list[ast.AST]: + if expr is None: + return [] + if isinstance(expr, ast.List | ast.Tuple | ast.Set): + out: list[ast.AST] = [] + for elt in expr.elts: + if isinstance(elt, ast.Starred): + out.extend(self._elements(elt.value)) + else: + out.append(elt) + return out + if isinstance(expr, ast.BinOp) and isinstance(expr.op, ast.Add): + return self._elements(expr.left) + self._elements(expr.right) + if isinstance(expr, ast.Name) and expr.id in self.lists: + return self._elements(self.lists[expr.id]) + return [expr] + + def _ref(self, expr: ast.AST | None) -> Ref | None: + if expr is None: + return None + if isinstance(expr, ast.Call): + if id(expr) in self.call_raw: + return ("idx", self.call_raw[id(expr)]) + func = expr.func + if isinstance(func, ast.Name): + return ("var", func.id) + if isinstance(func, ast.Attribute) and _path(func.value) == "self": + return ("var", func.attr) + return None + if isinstance(expr, ast.Name): + return ("var", expr.id) + if isinstance(expr, ast.Attribute): + if _path(expr.value) == "self": + return ("class", None) if expr.attr == "agents" else ("var", expr.attr) + return ("var", expr.attr) + if (s := _str(expr)) is not None: + return ("label", s) + return None + + def _child(self, raw: _Raw, expr: ast.AST | None, relation: str) -> None: + ref = self._ref(expr) + if ref is None: + return + if ref[0] == "class": + ref = ("class", raw.cls) + raw.children.append((ref, relation)) + + def _tools(self, raw: _Raw, expr: ast.AST | None) -> None: + for elt in self._elements(expr): + if isinstance(elt, ast.Call): + path = _path(elt.func) + last = path.rsplit(".", 1)[-1] + if last in ("as_tool", "asTool") and isinstance(elt.func, ast.Attribute): + self._child(raw, elt.func.value, "as_tool") + continue + if last == "AgentTool": + self._child( + raw, _kw(elt, "agent") or (elt.args[0] if elt.args else None), "as_tool" + ) + continue + if last == "create_handoff_tool": + target = _str(_kw(elt, "agent_name")) or ( + _str(elt.args[0]) if elt.args else None + ) + if target: + raw.children.append((("label", target), "handoff")) + continue + if last in ("function_tool", "tool", "FunctionTool") and (elt.args or elt.keywords): + inner = elt.args[0] if elt.args else _kw(elt, "func") + name = _str(_kw(elt, "name", "name_override")) + if name: + raw.tools.append(("name", name)) + elif isinstance(inner, ast.Name): + raw.tools.append(("var", inner.id)) + continue + name = _str(_kw(elt, "name")) + if name and (path in _TOOL_CONSTRUCTORS or last in _TOOL_CONSTRUCTORS): + raw.tools.append(("name", name)) + elif last: + # `SerperDevTool()` — a library tool, named by its class. + raw.tools.append(("name", last)) + elif isinstance(elt, ast.Name): + raw.tools.append(("var", elt.id)) + elif isinstance(elt, ast.Attribute): + raw.tools.append(("var", elt.attr)) + elif (s := _str(elt)) is not None: + raw.tools.append(("name", s)) + + # -- LangGraph StateGraph ------------------------------------------------ + + def _graph(self, raw: _Raw) -> None: + graph = _Graph(raw=raw.idx, file=self.rel) + names = {raw.var} if raw.var else set() + if not names: + return + for node in ast.walk(self.tree): + if not isinstance(node, ast.Call) or not isinstance(node.func, ast.Attribute): + continue + receiver = _path(node.func.value) + method = node.func.attr + if receiver not in names: + continue + if method == "add_node": + self._add_node(graph, node) + elif method == "add_edge" and len(node.args) >= 2: + for src in _node_names(node.args[0]): + for dst in _node_names(node.args[1]): + graph.routes.append((src, dst, "edge")) + elif method == "add_conditional_edges" and node.args: + sources = _node_names(node.args[0]) + path_map = _kw(node, "path_map") or (node.args[2] if len(node.args) > 2 else None) + targets: list[str] = [] + if isinstance(path_map, ast.Dict): + targets = [s for v in path_map.values if (s := _str(v)) is not None] + else: + targets = _strings(path_map) + if not targets: + router = _kw(node, "path") or (node.args[1] if len(node.args) > 1 else None) + if isinstance(router, ast.Name) and router.id in self.functions: + targets = _returned_strings(self.functions[router.id]) + for src in sources: + for dst in targets: + graph.routes.append((src, dst, "route")) + elif method == "compile": + name = _str(_kw(node, "name")) + if name: + raw.name = name + var, _ = self.call_target.get(id(node), (None, None)) + if var: + self.out.vars.setdefault(var, raw.idx) + for gname, gnode in graph.nodes.items(): + if gnode.func is not None: + for dst in _goto_strings(gnode.func): + graph.routes.append((gname, dst, "route")) + gnode.bound_tools = self._bound_tools(gnode.func) + gnode.loads = _loaded_names(gnode.func) + self.out.graphs.append(graph) + + def _add_node(self, graph: _Graph, call: ast.Call) -> None: + if not call.args: + return + first = call.args[0] + name = _str(first) + value: ast.AST | None + if name is not None: + value = call.args[1] if len(call.args) > 1 else _kw(call, "action", "node") + elif isinstance(first, ast.Name): + name, value = first.id, first + else: + return + func = None + if isinstance(value, ast.Name) and value.id in self.functions: + func = self.functions[value.id] + graph.nodes[name] = _Node(name=name, ref=self._ref(value), func=func) + + def _bound_tools(self, fn: ast.AST) -> list[tuple[str, str]] | None: + """The tools a node function binds to its model, or None when it binds none.""" + for sub in ast.walk(fn): + if ( + isinstance(sub, ast.Call) + and isinstance(sub.func, ast.Attribute) + and sub.func.attr == "bind_tools" + ): + probe = _Raw(idx=-1, framework="", kind="", file="", line=0) + self._tools(probe, sub.args[0] if sub.args else _kw(sub, "tools")) + return probe.tools + return None + + +_KIND = { + "openai_agent": "agent", + "crew_agent": "agent", + "crew": "crew", + "autogen_agent": "agent", + "team_member": "team", + "team_delegates": "team", + "groupchat": "team", + "gc_manager": "supervisor", + "adk_agent": "agent", + "adk_workflow": "team", + "react_agent": "agent", + "graph": "graph", + "supervisor": "supervisor", + "swarm": "team", +} + + +def _target_name(target: ast.AST) -> str: + if isinstance(target, ast.Name): + return target.id + if isinstance(target, ast.Attribute) and _path(target.value) == "self": + return target.attr + return "" + + +def _assigned_names(stmt: ast.stmt) -> list[str]: + if isinstance(stmt, ast.Assign): + return [n for t in stmt.targets if (n := _target_name(t))] + if isinstance(stmt, ast.AnnAssign): + return [n] if (n := _target_name(stmt.target)) else [] + return [] + + +def _child_bodies(stmt: ast.stmt) -> list[list[ast.stmt]]: + """Nested statement lists (if/with/try/for bodies) that are still the same scope.""" + out: list[list[ast.stmt]] = [] + for attr in ("body", "orelse", "finalbody"): + value = getattr(stmt, attr, None) + if isinstance(value, list) and value and isinstance(value[0], ast.stmt): + out.append(value) + for handler in getattr(stmt, "handlers", []) or []: + out.append(handler.body) + return out + + +def _node_names(expr: ast.AST) -> list[str]: + """A graph endpoint: a node name, a list of them, or START/END (skipped).""" + if isinstance(expr, ast.List | ast.Tuple): + return [n for e in expr.elts for n in _node_names(e)] + if (s := _str(expr)) is not None: + return [s] + if _path(expr).rsplit(".", 1)[-1] == "START": + return ["__start__"] + return [] + + +def _returned_strings(fn: ast.AST) -> list[str]: + return [s for sub in ast.walk(fn) if isinstance(sub, ast.Return) for s in _strings(sub.value)] + + +def _goto_strings(fn: ast.FunctionDef | ast.AsyncFunctionDef) -> list[str]: + """Where a node function can send the graph next: ``Command(goto=...)`` literals + and a ``Command[Literal[...]]`` return annotation.""" + out: list[str] = [] + for sub in ast.walk(fn): + if isinstance(sub, ast.Call) and _path(sub.func).rsplit(".", 1)[-1] == "Command": + out.extend(_strings(_kw(sub, "goto"))) + ann = fn.returns + if isinstance(ann, ast.Subscript) and _path(ann.value).rsplit(".", 1)[-1] == "Command": + inner = ann.slice + if isinstance(inner, ast.Subscript) and _path(inner.value).rsplit(".", 1)[-1] == "Literal": + sl = inner.slice + out.extend(_strings(sl) if not isinstance(sl, ast.Constant) else [sl.value]) + return [s for s in out if isinstance(s, str)] + + +def _loaded_names(fn: ast.AST) -> set[str]: + return {n.id for n in ast.walk(fn) if isinstance(n, ast.Name) and isinstance(n.ctx, ast.Load)} + + +# --------------------------------------------------------------------------- +# TypeScript / JavaScript — OpenAI Agents SDK only +# --------------------------------------------------------------------------- + +_JS_AGENT_DECL = re.compile( + r"(?:const|let|var)\s+(\w+)\s*=\s*(?:new\s+Agent|Agent\.create)\s*\(\s*\{" +) +_JS_TOOL_DECL = re.compile(r"(?:const|let|var)\s+(\w+)\s*=\s*tool\s*\(\s*\{") +_JS_AGENTS_IMPORT = re.compile(r"""from\s+["']@openai/agents(?:[-/][\w/-]*)?["']""") + + +def _js_object(source: str, start: int) -> dict[str, str]: + """The top-level ``key: value`` pairs of the object literal opening at ``start``. + + A reader, not a parser: it tracks bracket depth and string literals, which is all + an object literal of names and arrays needs. + """ + out: dict[str, str] = {} + depth = 0 + i = start + n = len(source) + key_start = start + 1 + while i < n: + c = source[i] + if c in "'\"`": + j = i + 1 + while j < n and source[j] != c: + j += 2 if source[j] == "\\" else 1 + i = j + 1 + continue + if c in "{[(": + depth += 1 + elif c in "}])": + depth -= 1 + if depth == 0: + _js_pair(source[key_start:i], out) + return out + elif c == "," and depth == 1: + _js_pair(source[key_start:i], out) + key_start = i + 1 + i += 1 + return out + + +def _js_pair(text: str, out: dict[str, str]) -> None: + key, sep, value = text.partition(":") + key = key.strip() + if sep and re.fullmatch(r"\w+", key): + out[key] = value.strip() + elif re.fullmatch(r"\w+", key): + out[key] = key # shorthand `{ tools }` + + +def _js_elements(value: str) -> list[str]: + value = value.strip() + if not (value.startswith("[") and value.endswith("]")): + return [] + items, depth, cur = [], 0, "" + for c in value[1:-1]: + if c in "{[(": + depth += 1 + elif c in "}])": + depth -= 1 + if c == "," and depth == 0: + items.append(cur.strip()) + cur = "" + else: + cur += c + if cur.strip(): + items.append(cur.strip()) + return items + + +def _js_string(value: str) -> str | None: + m = re.fullmatch(r"""\s*(["'`])([^"'`]+)\1\s*""", value) + return m.group(2) if m else None + + +def _extract_js(source: str, rel: str, start: int) -> _FileResult: + out = _FileResult(file=rel) + if not _JS_AGENTS_IMPORT.search(source): + return out + for m in _JS_TOOL_DECL.finditer(source): + fields = _js_object(source, m.end() - 1) + out.tool_defs[m.group(1)] = _js_string(fields.get("name", "")) or m.group(1) + idx = start + for m in _JS_AGENT_DECL.finditer(source): + fields = _js_object(source, m.end() - 1) + var = m.group(1) + raw = _Raw( + idx=idx, + framework="OpenAI Agents SDK", + kind="agent", + file=rel, + line=source.count("\n", 0, m.start()) + 1, + name=_js_string(fields.get("name", "")) or var, + var=var, + ) + idx += 1 + for item in _js_elements(fields.get("tools", "")): + as_tool = re.match(r"(\w+)\.asTool\s*\(", item) + if as_tool: + raw.children.append((("var", as_tool.group(1)), "as_tool")) + elif re.fullmatch(r"\w+", item): + raw.tools.append(("var", item)) + for item in _js_elements(fields.get("handoffs", "")): + target = re.match(r"(?:handoff\s*\(\s*)?(\w+)", item) + if target: + raw.children.append((("var", target.group(1)), "handoff")) + out.raws.append(raw) + out.vars.setdefault(var, raw.idx) + out.exported.add(var) + return out + + +# --------------------------------------------------------------------------- +# Public entry points +# --------------------------------------------------------------------------- + + +class AgentCollector: + """Accumulates raw definitions file by file; :meth:`finish` resolves them.""" + + def __init__(self) -> None: + self.files: list[_FileResult] = [] + self._next = 0 + + def add(self, source: str, rel: str, suffix: str) -> None: + if not _HINT.search(source): + return + if suffix == ".py": + try: + tree = ast.parse(source, filename=rel) + except (SyntaxError, ValueError): + return # reported by the site pass already + ex = _Extractor(tree, rel, self._next) + ex.collect() + ex.create() + ex.link() + result = ex.out + else: + result = _extract_js(source, rel, self._next) + self._next += len(result.raws) + # A file of tools alone is kept too: an agent elsewhere names them. + if result.raws or result.tool_defs: + self.files.append(result) + + def finish(self) -> tuple[list[AgentSpec], list[AgentEdge]]: + return _Linker(self.files, self._next).run() + + +class _Linker: + def __init__(self, files: list[_FileResult], next_idx: int) -> None: + self.files = files + self.raws: dict[int, _Raw] = {r.idx: r for f in files for r in f.raws} + self.file_of: dict[int, _FileResult] = {r.idx: f for f in files for r in f.raws} + self.next_idx = next_idx + # Repository-wide lookups, used only when a name is defined in exactly one file. + self.global_vars: dict[str, set[int]] = defaultdict(set) + self.global_tools: dict[str, set[str]] = defaultdict(set) + for f in files: + for var, idx in f.vars.items(): + if var in f.exported: + self.global_vars[var].add(idx) + for var, name in f.tool_defs.items(): + self.global_tools[var].add(name) + self.labels: dict[str, set[int]] = defaultdict(set) + #: (graph raw, node name) -> raw, filled by the graph pass + self.node_agents: dict[tuple[int, str], int] = {} + + def _by_var(self, var: str, file: _FileResult | None, depth: int = 0) -> int | None: + if file is not None and var in file.vars: + return file.vars[var] + found = self.global_vars.get(var) or set() + if len(found) == 1: + return next(iter(found)) + if file is not None and var in file.aliases and depth < 3: + return self._by_var(file.aliases[var], file, depth + 1) + return None + + def _by_label(self, label: str, file: str) -> int | None: + hits = self.labels.get(_slug(label)) or set() + local = [i for i in hits if self.raws[i].file == file] + if len(local) == 1: + return local[0] + return next(iter(hits)) if len(hits) == 1 else None + + def resolve(self, ref: Ref, raw: _Raw) -> list[int]: + kind, value = ref + file = self.file_of.get(raw.idx) + if kind == "idx": + return [value] + if kind == "var": + hit = self._by_var(value, file) + return [hit] if hit is not None else [] + if kind == "label": + hit = self._by_label(value, raw.file) + return [hit] if hit is not None else [] + if kind == "class": + return [ + r.idx + for r in self.raws.values() + if r.file == raw.file and r.cls == value and r.kind == "agent" and r.idx != raw.idx + ] + return [] + + def _tool_name(self, ref: tuple[str, str], raw: _Raw) -> str: + kind, value = ref + if kind == "name": + return value + file = self.file_of.get(raw.idx) + if file is not None and value in file.tool_defs: + return file.tool_defs[value] + found = self.global_tools.get(value) or set() + return next(iter(found)) if len(found) == 1 else value + + def _new_raw(self, **kw: Any) -> _Raw: + raw = _Raw(idx=self.next_idx, **kw) + self.next_idx += 1 + self.raws[raw.idx] = raw + return raw + + def run(self) -> tuple[list[AgentSpec], list[AgentEdge]]: + for raw in self.raws.values(): + if raw.name: + self.labels[_slug(raw.name)].add(raw.idx) + edges: list[tuple[int, int, str]] = [] + for raw in list(self.raws.values()): + for ref, relation in raw.children: + for child in self.resolve(ref, raw): + edges.append((raw.idx, child, relation)) + edges.extend(self._graphs()) + edges.extend(self._crew_managers()) + edges = self._fold_groupchats(edges) + edges.extend(self._crew_delegation(edges)) + + # Orchestrators (crews, teams, graphs) are reported only when they run agents. + parents = {p for p, _, _ in edges} + for raw in self.raws.values(): + if raw.kind in ("crew", "team", "graph") and raw.idx not in parents: + raw.dropped = True + live = {i for i, r in self.raws.items() if not r.dropped} + + keys = self._keys([self.raws[i] for i in sorted(live, key=self._order)]) + specs = [ + AgentSpec( + key=keys[i], + name=self.raws[i].name or keys[i], + framework=self.raws[i].framework, + kind=self.raws[i].kind, + file=self.raws[i].file, + line=self.raws[i].line, + tools=list( + dict.fromkeys(self._tool_name(t, self.raws[i]) for t in self.raws[i].tools) + ), + ) + for i in sorted(live, key=self._order) + ] + seen: set[tuple[int, int]] = set() + out_edges: list[AgentEdge] = [] + rank = {r: n for n, r in enumerate(RELATIONS)} + for parent, child, relation in sorted(edges, key=lambda e: rank.get(e[2], 9)): + if parent == child or parent not in live or child not in live: + continue + if (parent, child) in seen: + continue + seen.add((parent, child)) + out_edges.append(AgentEdge(keys[parent], keys[child], relation)) + out_edges.sort(key=lambda e: (e.parent, e.child)) + return specs, out_edges + + def _order(self, idx: int) -> tuple[str, int, int]: + raw = self.raws[idx] + return (raw.file, raw.line, idx) + + def _keys(self, raws: list[_Raw]) -> dict[int, str]: + """A unique, stable key per agent: its name, then its file when two share one.""" + base = {r.idx: _slug(r.name or f"{Path(r.file).stem}-{r.kind}") for r in raws} + counts: dict[str, int] = defaultdict(int) + for key in base.values(): + counts[key] += 1 + out: dict[int, str] = {} + used: set[str] = set() + for raw in raws: + key = base[raw.idx] + if counts[key] > 1: + key = f"{key}-{_slug(Path(raw.file).stem)}" + candidate, n = key, 2 + while candidate in used: + candidate, n = f"{key}-{n}", n + 1 + used.add(candidate) + out[raw.idx] = candidate + return out + + def _graphs(self) -> list[tuple[int, int, str]]: + edges: list[tuple[int, int, str]] = [] + for f in self.files: + for graph in f.graphs: + graph_raw = self.raws[graph.raw] + agents: dict[str, int] = {} + for name, node in graph.nodes.items(): + hit: int | None = None + if node.ref is not None: + found = self.resolve(node.ref, graph_raw) + hit = found[0] if found and found[0] != graph.raw else None + if hit is None and node.func is not None: + # A node function that runs an agent stands for that agent. + found_ids = { + i + for n in node.loads + if (i := self._by_var(n, f)) is not None + and i != graph.raw + and self.raws[i].kind != "graph" + } + if len(found_ids) == 1: + hit = found_ids.pop() + if hit is None and node.bound_tools is not None: + # A node that binds tools to a model is an agent of its own. + hit = self._new_raw( + framework="LangGraph", + kind="agent", + file=graph_raw.file, + line=node.func.lineno if node.func else graph_raw.line, + name=name, + tools=node.bound_tools, + ).idx + self.file_of[hit] = f + if hit is not None: + agents[name] = hit + # A node that routes to agent nodes is their supervisor, whatever it is. + for src, dst, how in graph.routes: + if ( + how == "route" + and dst in agents + and src not in agents + and src in graph.nodes + ): + node = graph.nodes[src] + agents[src] = self._new_raw( + framework="LangGraph", + kind="supervisor", + file=graph_raw.file, + line=node.func.lineno if node.func else graph_raw.line, + name=src, + ).idx + self.file_of[agents[src]] = f + routed = { + (agents[s], agents[d]) + for s, d, how in graph.routes + if how == "route" and s in agents and d in agents and s != d + } + # A worker that hands control back (`Command(goto="supervisor")`) is + # returning, not delegating: of a pair routing both ways, the side that + # routes to more agents is the supervisor. A tie is a pair of peers. + fan_out: dict[int, int] = defaultdict(int) + for parent, _ in routed: + fan_out[parent] += 1 + routed = { + (a, b) for a, b in routed if not ((b, a) in routed and fan_out[b] > fan_out[a]) + } + has_parent: set[int] = set() + for parent, child in routed: + edges.append((parent, child, "delegates")) + has_parent.add(child) + for src, dst, how in graph.routes: + if how != "edge" or src not in agents or dst not in agents or src == dst: + continue + a, b = agents[src], agents[dst] + if (b, a) in routed or (a, b) in routed: + continue # a worker returning to its supervisor + edges.append((a, b, "handoff")) + has_parent.add(b) + for idx in dict.fromkeys(agents.values()): + if idx not in has_parent: + edges.append((graph.raw, idx, "member")) + return edges + + def _fold_groupchats(self, edges: list[tuple[int, int, str]]) -> list[tuple[int, int, str]]: + """AG2: a GroupChatManager runs a GroupChat; the chat's members are its own.""" + for raw in self.raws.values(): + if raw.absorbs is None: + continue + for group in self.resolve(raw.absorbs, raw): + members = [(p, c, r) for p, c, r in edges if p == group] + if not members: + continue + self.raws[group].dropped = True + edges = [e for e in edges if e[0] != group and e[1] != group] + edges.extend((raw.idx, c, "delegates") for _, c, _ in members) + return edges + + def _crew_members(self, crew: _Raw) -> list[int]: + return [i for ref in crew.members for i in self.resolve(ref, crew)] + + def _crew_managers(self) -> list[tuple[int, int, str]]: + """CrewAI: a crew runs its ``manager_agent``, who delegates to every member.""" + out: list[tuple[int, int, str]] = [] + for crew in list(self.raws.values()): + if crew.manager is None: + continue + members = self._crew_members(crew) + for manager in self.resolve(crew.manager, crew): + out.append((crew.idx, manager, "member")) + out.extend((manager, m, "delegates") for m in members if m != manager) + return out + + def _crew_delegation(self, edges: list[tuple[int, int, str]]) -> list[tuple[int, int, str]]: + """CrewAI: an agent with ``allow_delegation=True`` may hand work to any crewmate.""" + out: list[tuple[int, int, str]] = [] + crews: dict[int, list[int]] = defaultdict(list) + for parent, child, _ in edges: + if self.raws[parent].kind == "crew": + crews[parent].append(child) + for crew in self.raws.values(): + if crew.manager is not None: + crews[crew.idx] = self._crew_members(crew) + for members in crews.values(): + for member in members: + if self.raws[member].allow_delegation: + out.extend((member, other, "delegates") for other in members if other != member) + return out + + +_SLUG = re.compile(r"[^a-z0-9-]+") + + +def _slug(value: str) -> str: + return _SLUG.sub("-", (value or "").strip().lower()).strip("-") or "agent" diff --git a/src/agentfox/capabilities/discovery/repo.py b/src/agentfox/capabilities/discovery/repo.py index 72a1e003..5b1ccc53 100644 --- a/src/agentfox/capabilities/discovery/repo.py +++ b/src/agentfox/capabilities/discovery/repo.py @@ -41,6 +41,8 @@ from pathlib import Path from typing import Any +from agentfox.capabilities.discovery.agents import AgentCollector, AgentEdge, AgentSpec + log = logging.getLogger(__name__) #: Directories never worth walking. Skipping them is the difference between a scan @@ -104,6 +106,13 @@ "llama_index": "LlamaIndex", "crewai": "CrewAI", "autogen": "AutoGen", + # AutoGen 0.4+ ships as separate packages; AG2 is the community fork's name. + "autogen_agentchat": "AutoGen", + "autogen_core": "AutoGen", + "autogen_ext": "AutoGen", + "ag2": "AutoGen", + "langgraph_supervisor": "LangGraph", + "langgraph_swarm": "LangGraph", "openai": "OpenAI SDK", "anthropic": "Anthropic SDK", "litellm": "LiteLLM", @@ -228,6 +237,40 @@ class ScanReport: sites: list[Site] = field(default_factory=list) errors: list[str] = field(default_factory=list) governed_files: list[str] = field(default_factory=list) + #: Each agent the code defines, with its own tools, and which hands work to which + #: (:mod:`agentfox.capabilities.discovery.agents`). Empty for a repository whose + #: framework is not one that module reads, which is not a claim there are none. + agents: list[AgentSpec] = field(default_factory=list) + agent_edges: list[AgentEdge] = field(default_factory=list) + + @property + def multi_agent(self) -> bool: + """Two or more agents were found, so the scan proposes one per agent instead + of one per directory (``registry.service.propose_from_scan``).""" + return len(self.agents) >= 2 + + def agent_summary(self, limit: int = 8) -> list[str]: + """The multi-agent structure as short lines for a terminal: a count, then each + agent that hands work on and to whom. Empty for a single-agent repository.""" + if not self.multi_agent: + return [] + lines = [f"{len(self.agents)} agents, {len(self.agent_edges)} handoff(s) between them"] + # "{parent} hands off to {children}", or with a suffix: "triage uses kb as a tool". + words = { + "handoff": ("hands off to", ""), + "as_tool": ("uses", " as a tool"), + "delegates": ("delegates to", ""), + "member": ("runs", ""), + } + children: dict[tuple[str, str], list[str]] = {} + for edge in self.agent_edges: + children.setdefault((edge.parent, edge.relation), []).append(edge.child) + for (parent, relation), kids in list(children.items())[:limit]: + verb, after = words.get(relation, ("delegates to", "")) + lines.append(f"{parent} {verb} {', '.join(kids)}{after}") + if len(children) > limit: + lines.append(f"... and {len(children) - limit} more; `--json` lists them all") + return lines @property def inconclusive(self) -> bool: @@ -342,9 +385,13 @@ def to_json(self) -> dict[str, Any]: "counts": self.by_kind(), "sites": [s.to_json() for s in self.sites], "errors": self.errors, + # The multi-agent structure: one entry per agent (with its own tools) and + # one per "parent hands work to child" relation between them. + "agents": [a.to_json() for a in self.agents], + "agent_edges": [e.to_json() for e in self.agent_edges], } - def to_submission_payload(self, *, source: str) -> dict[str, Any]: + def to_submission_payload(self, *, source: str, tool_names: bool = False) -> dict[str, Any]: """The redacted subset of this report that `agentfox scan --submit` / `agentfox scan --sessions --submit` are allowed to send to a control plane. @@ -357,6 +404,18 @@ def to_submission_payload(self, *, source: str) -> dict[str, Any]: connected-repo scan (``routes/integrations.py``) already groups proposals by. No file path deeper than its first component, no line numbers, no detail text, no error messages. + + For a multi-agent system it also carries each agent's name, framework, kind and + top-level directory, and which agent hands work to which: the structure a + reviewer needs to approve the system as the separate agents it is. An agent's + name is the one it gives itself in code (``Agent(name="Billing")``), the same + kind of identifier as a directory name. Its tools' names are not sent, for the + same reason a ``tool`` site's are not: they stay on this machine, and only the + connected-repo scan, which reads the code server-side, records them. + + ``tool_names`` is for a payload that does not leave this machine: `agentfox up` + recording the scan in a local database (`wizard/backend.Local`). There each + agent's own tools are added, so its project shows them before any traffic. """ def top_dir(rel_path: str) -> str: @@ -378,6 +437,20 @@ def top_dir(rel_path: str) -> str: # about which tools or what they read. if s.kind in ("agent_definition", "tool", "model_call", "lethal_trifecta") ], + "agents": [ + { + "key": a.key, + "name": a.name, + "framework": a.framework, + "kind": a.kind, + "top_dir": a.top_dir, + **({"tools": list(a.tools)} if tool_names else {}), + } + for a in self.agents + ] + if self.multi_agent + else [], + "agent_edges": [e.to_json() for e in self.agent_edges] if self.multi_agent else [], } def next_step(self) -> str: @@ -485,6 +558,9 @@ def visit_ImportFrom(self, node: ast.ImportFrom) -> None: def _note_import(self, module: str) -> None: root = module.split(".")[0] label = _FRAMEWORK_IMPORTS.get(root) + if module == "google.adk" or module.startswith("google.adk."): + # `google` alone names a dozen unrelated SDKs, so ADK is matched by prefix. + label = "Google ADK" if label: self.frameworks.add(label) if root == "agentfox": @@ -676,6 +752,7 @@ def _add_tool(self, name: str, description: str, line: int, how: str) -> None: "llamaindex": "LlamaIndex", "@llamaindex": "LlamaIndex", "crewai-ts": "CrewAI", + "@openai/agents": "OpenAI Agents SDK", "@modelcontextprotocol/sdk": "MCP", "@google/generative-ai": "Google GenAI SDK", "@google/genai": "Google GenAI SDK", @@ -1034,6 +1111,7 @@ def scan(root: str | Path = ".", *, include_config: bool = True) -> ScanReport: root_path = Path(root).resolve() report = ScanReport(root=str(root_path)) frameworks: set[str] = set() + agents = AgentCollector() suffixes = set(CODE_SUFFIXES) if include_config: @@ -1060,6 +1138,10 @@ def scan(root: str | Path = ".", *, include_config: bool = True) -> ScanReport: report.errors.append(str(exc)) continue frameworks |= found + if path.suffix in CODE_SUFFIXES: + agents.add( + path.read_text(errors="ignore"), str(path.relative_to(root_path)), path.suffix + ) if governed: report.governed_files.append(str(path.relative_to(root_path))) for site in sites: @@ -1068,6 +1150,10 @@ def scan(root: str | Path = ".", *, include_config: bool = True) -> ScanReport: site.severity = "info" report.sites.append(site) + report.agents, report.agent_edges = agents.finish() + # The OpenAI Agents SDK imports as `agents`, a name too common among a repository's + # own packages to detect from the import alone; an agent built with it is proof. + frameworks |= {a.framework for a in report.agents} report.frameworks = sorted(frameworks) _detect_mcp(root_path, report) _detect_trifectas(report) diff --git a/src/agentfox/capabilities/features.py b/src/agentfox/capabilities/features.py new file mode 100644 index 00000000..700b14e9 --- /dev/null +++ b/src/agentfox/capabilities/features.py @@ -0,0 +1,314 @@ +"""Features: what the workspace turns on for every project, and what one project changes. + +A developer thinks "is personal-data masking on for this project?", not "which pack +holds `pii.outbound_redact` and is there an agent-level copy". This module answers in +their terms, for the two kinds of switch the product has: + +* **Protections** (`protection.CATALOGUE`): rules in shipped packs. The workspace + value is the rule as its pack has it. A project's value is its own copy, in its + layer (``agent.``), when it has one; without one it inherits. A project can + always be stricter. It can be looser (lower, or off) only where the workspace lets + projects change it (the rules' ``overridable`` flag, which this module calls + ``open_to_projects``), and never for a protected rule. That is the hierarchy's own + rule (`platform/policy/hierarchy.py`), applied by `protection/scope.py`. +* **Checks** (detectors): `detector_settings` holds the workspace's choice and each + project's, and a project may change it either way. + +Setting a project to the workspace's value removes its copy, so "the same as the +workspace" and "inherited" are one state and a later workspace change reaches it. +""" + +from __future__ import annotations + +from collections import defaultdict +from typing import Any + +from sqlalchemy import select +from sqlalchemy.orm import Session + +from agentfox.capabilities import protection +from agentfox.capabilities.detection import all_detectors +from agentfox.capabilities.detection import detector_settings as checks +from agentfox.capabilities.detection.catalog import GROUPS, describe +from agentfox.capabilities.protection import scope +from agentfox.core.models import Policy +from agentfox.platform.ledger import operator_log +from agentfox.platform.policy import ( + PolicyDocument, + Rule, + current_binding, + effective_for, + load_version_document, +) +from agentfox.platform.policy.model import PROTECTED_RULES +from agentfox.platform.policy.publish import current_mode, publish_in_current_mode + +OFF, ON = protection.OFF, protection.ON +_PROTECTED = {rid for ids in PROTECTED_RULES.values() for rid in ids} + + +class FeatureLocked(ValueError): + """A project change the workspace does not allow (it would loosen a closed rule).""" + + +# --- protections: reading ----------------------------------------------------------- + + +def _level(rule: Rule | None) -> str | None: + """off / on / low / medium / high for one rule copy; None when there is none.""" + if rule is None: + return None + if not rule.enabled: + return OFF + detection = rule.when.detection + return protection.level_of(detection.min_score) if detection else ON + + +def _workspace_rules(session: Session, slug: str | None) -> dict[str, tuple[Rule, str]]: + """Every rule in force above the project layer: rule id -> (rule, pack key). + + For a project, everything but its own layer (its team's rules included); for the + workspace, what applies with no project named. + """ + if slug: + view = scope._view(session, slug) + return {rid: (rule, key) for rid, (rule, key, _level) in view.default.items()} + effective = effective_for(session) + return {r.rule.id: (r.rule, r.layer.document.key if r.layer else "") for r in effective.rules} + + +def _mode_word(mode: str | None) -> str | None: + return {"enforce": "blocking", "observe": "watching"}.get(mode or "") + + +def protections(session: Session, slug: str | None = None) -> list[dict[str, Any]]: + """Each protection: the workspace's value, the project's own (or None), what applies.""" + workspace = _workspace_rules(session, slug) + own = {r.id: r for r in protection.own_layer(session, slug).rules} if slug else {} + own_mode = current_mode(session, protection.layer_key(slug)) if slug else None + templates = protection._template_rules() + out = [] + for p in protection.CATALOGUE: + first = p.rules[0] + base = workspace.get(first) + mine = own.get(first) + template = templates.get(first) + graded = bool((base and base[0].when.detection) or (template and template.when.detection)) + ws_level = _level(base[0]) if base else OFF + open_to_projects = bool(base and base[0].overridable) + protected = any(r in _PROTECTED for r in p.rules) + project = _level(mine) + applied = ws_level + if project is not None: + # A looser copy only counts where the workspace lets projects loosen it. + looser = _rank(project) < _rank(ws_level) + if not looser or (open_to_projects and not protected): + applied = project + out.append( + { + "key": p.key, + "title": p.title, + "graded": graded, + "pack": base[1] if base else p.pack, + "installed": base is not None, + "available": template is not None or base is not None, + "workspace": ws_level, + "workspace_mode": _mode_word(current_mode(session, base[1])) if base else None, + "open_to_projects": open_to_projects and not protected, + "protected": protected, + "project": project, + "project_mode": _mode_word(own_mode) if project is not None else None, + "effective": applied, + "needs": p.needs or None, + } + ) + return out + + +_ORDER = {OFF: 0, "low": 1, "medium": 2, ON: 2, "high": 3} + + +def _rank(level: str | None) -> int: + return _ORDER.get(level or OFF, 0) + + +def _change(level: str, graded: bool) -> dict[str, Any]: + if level == OFF: + return {"enabled": False} + if graded: + if level not in protection.LEVELS: + raise ValueError(f"this protection takes off, low, medium or high, not '{level}'") + return {"enabled": True, "min_score": protection.LEVELS[level]} + if level != ON: + raise ValueError(f"this protection is on or off, not '{level}'") + return {"enabled": True} + + +# --- protections: writing ----------------------------------------------------------- + + +def _live_doc(session: Session, key: str) -> PolicyDocument | None: + policy = session.scalar(select(Policy).where(Policy.key == key)) + if policy is None: + return None + _binding, live = current_binding(session, policy.id) + return load_version_document(live) if live else None + + +def set_workspace_protection( + session: Session, + key: str, + *, + level: str | None = None, + open_to_projects: bool | None = None, + actor: str, + reason: str = "", +) -> dict[str, Any]: + """Change a protection for every project: its rules, edited in the packs that hold + them, live in the mode each pack is already in (enforcing packs are replayed + first, as for any generated change).""" + p = protection.BY_KEY.get(key) + if p is None: + raise ValueError(f"unknown protection '{key}'") + if open_to_projects and any(r in _PROTECTED for r in p.rules): + raise FeatureLocked(f"'{p.title}' cannot be opened to projects: it is protected") + workspace = _workspace_rules(session, None) + by_pack: dict[str, list[str]] = defaultdict(list) + for rid in p.rules: + if rid in workspace: + by_pack[workspace[rid][1]].append(rid) + if not by_pack: + raise ValueError( + f"'{p.title}' needs the {p.pack} pack, which this workspace has not installed" + ) + before = {"level": _level(workspace[p.rules[0]][0]) if p.rules[0] in workspace else None} + published = [] + for pack, ids in by_pack.items(): + doc = _live_doc(session, pack) + if doc is None: + continue + data = doc.model_dump(exclude_none=True) + for r in data["rules"]: + if r["id"] not in ids: + continue + if level is not None: + change = _change(level, bool(r.get("when", {}).get("detection"))) + r["enabled"] = change["enabled"] + if "min_score" in change: + r["when"]["detection"]["min_score"] = change["min_score"] + if open_to_projects is not None: + r["overridable"] = open_to_projects + result = publish_in_current_mode( + session, + PolicyDocument.model_validate(data), + actor=actor, + notes=f"{p.title} for every project", + ) + published.append( + { + "policy": pack, + "version": result.version, + "mode": result.mode, + "simulation": result.simulation, + } + ) + operator_log.record( + session, + "operator.workspace_protection.changed", + actor=actor, + reason=reason or f"protection '{key}' changed for every project", + subject_type="protection", + subject_id=key, + before=before, + after={"level": level, "open_to_projects": open_to_projects}, + ) + return {"published": published} + + +def set_project_protection( + session: Session, slug: str, key: str, level: str | None, *, actor: str, reason: str = "" +) -> dict[str, Any]: + """Set a protection for one project, or with ``None`` go back to the workspace's.""" + p = protection.BY_KEY.get(key) + if p is None: + raise ValueError(f"unknown protection '{key}'") + workspace = _workspace_rules(session, slug) + if level is None: + for rid in p.rules: + scope.reset_for_agent(session, slug, rid, actor=actor, reason=reason or "inherit") + return {"project": None} + if not any(rid in workspace for rid in p.rules): + # Nothing to inherit from: the project's own copy is the whole protection. + states = {s.protection.key: s.level for s in protection.state(session, slug).protections} + states[key] = level + protection.save(session, slug, states, "", actor=actor) + return {"project": level} + applied = [] + for rid in p.rules: + if rid not in workspace: + continue + rule, pack = workspace[rid] + try: + planned = scope.plan( + session, pack, rid, [slug], _change(level, rule.when.detection is not None) + ) + except scope.LooseningNotAllowed as exc: + raise FeatureLocked( + f"The workspace does not let projects lower '{p.title}'. Change it for " + "every project, or let projects change it on the workspace's features." + if not exc.protected + else f"'{p.title}' cannot be lowered for a single project." + ) from exc + applied += scope.apply_for_agents( + session, + planned, + rule_id=rid, + actor=actor, + reason=reason or f"{p.title} {level} for {slug}", + ) + return {"project": level, "applied": applied} + + +# --- checks -------------------------------------------------------------------------- + + +def checks_for(session: Session, slug: str | None = None) -> list[dict[str, Any]]: + """Every detector: whether it is installed, the workspace's switch, the project's.""" + detectors = all_detectors() + workspace_on = checks.enabled_for(session) + project = checks.agent_overrides(session, slug) if slug else {} + group_names = dict(GROUPS) + out = [] + for key in sorted(detectors): + if key == checks.WORKSPACE_RULES or key == checks.WORKSPACE_VALIDATORS: + continue # on wherever a workspace rule exists; switched with the rule + d = detectors[key] + facts = describe(d) + mine = project.get(key) + ws = key in workspace_on + effective = ws if mine is None else (mine or key in checks.ALWAYS_ON) + out.append( + { + "key": key, + "label": facts.get("label"), + "group": facts["group"], + "group_title": group_names.get(facts["group"], facts["group"]), + "source": facts["source"], + "where": facts["where"], + "installed": d.available(), + "always_on": key in checks.ALWAYS_ON, + "workspace": ws, + "project": mine, + "effective": effective, + } + ) + return out + + +def features(session: Session, slug: str | None = None) -> dict[str, Any]: + return { + "agent": slug, + "levels": [OFF, *protection.LEVELS], + "protections": protections(session, slug), + "checks": checks_for(session, slug), + } diff --git a/src/agentfox/capabilities/protection/map.py b/src/agentfox/capabilities/protection/map.py index 25d6fd78..0a81be98 100644 --- a/src/agentfox/capabilities/protection/map.py +++ b/src/agentfox/capabilities/protection/map.py @@ -298,7 +298,7 @@ def agent_map(session: Session, slug: str) -> dict[str, Any]: # --- detectors ----------------------------------------------------------- detectors = all_detectors() - running = enabled_for(session) + running = enabled_for(session, slug) detectors_by_stage: dict[str, list[str]] = defaultdict(list) for key in sorted(running): detector = detectors.get(key) @@ -359,17 +359,8 @@ def agent_map(session: Session, slug: str) -> dict[str, Any]: } ) - # --- sub-agents ---------------------------------------------------------- - subagents = [] - if agent: - for edge in session.scalars( - select(LineageEdge).where( - LineageEdge.src_id == slug, - LineageEdge.dst_type == "agent", - LineageEdge.relation == "delegates_to", - ) - ): - subagents.append({"slug": edge.dst_id, "calls": edge.observed_count}) + # --- sub-agents and parents ------------------------------------------------ + subagents, parents = _hierarchy(session, slug) if agent else ([], []) stages = [ { @@ -388,10 +379,59 @@ def agent_map(session: Session, slug: str) -> dict[str, Any]: "slug": slug, "name": (agent.name if agent else "") or slug, "known": agent is not None, + "framework": agent.framework if agent else None, }, "window_days": WINDOW_DAYS, "stages": stages, "everywhere": by_stage.get("everywhere", []), "tools": tools, "subagents": subagents, + "parents": parents, } + + +def _edge_json(edge: LineageEdge, other: str, names: dict[str, str]) -> dict[str, Any]: + return { + "slug": other, + "name": names.get(other) or other, + "calls": edge.observed_count, + # Read from the code by a repo scan, and/or actually seen in traffic: a + # sub-agent the code hands work to shows before it has ever been called. + "declared": bool(edge.declared), + "seen": edge.observed_count > 0, + # How the work is handed over, when the code says (``discovery.agents``); + # None for a delegation only ever seen in a span. + "relation": edge.kind, + } + + +def _hierarchy(session: Session, slug: str) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + """``(subagents, parents)``: who this agent hands work to, and who hands it work. + + A proposal someone dismissed is left off both: it is not part of the system they + chose to govern, even though the code still names it. + """ + edges = list( + session.scalars( + select(LineageEdge).where( + LineageEdge.relation == "delegates_to", + LineageEdge.dst_type == "agent", + (LineageEdge.src_id == slug) | (LineageEdge.dst_id == slug), + ) + ) + ) + others = {e.dst_id if e.src_id == slug else e.src_id for e in edges} + rows = list(session.scalars(select(Agent).where(Agent.slug.in_(others)))) if others else [] + dismissed = {a.slug for a in rows if a.status == "rejected"} + names = {a.slug: a.name for a in rows} + subagents = [ + _edge_json(e, e.dst_id, names) + for e in sorted(edges, key=lambda e: (-e.observed_count, e.dst_id)) + if e.src_id == slug and e.dst_id != slug and e.dst_id not in dismissed + ] + parents = [ + _edge_json(e, e.src_id, names) + for e in sorted(edges, key=lambda e: e.src_id) + if e.dst_id == slug and e.src_id != slug and e.src_id not in dismissed + ] + return subagents, parents diff --git a/src/agentfox/capabilities/protection/scope.py b/src/agentfox/capabilities/protection/scope.py index e373ff7a..46627884 100644 --- a/src/agentfox/capabilities/protection/scope.py +++ b/src/agentfox/capabilities/protection/scope.py @@ -94,11 +94,13 @@ def agent_tree(session: Session) -> list[dict[str, Any]]: An agent nobody delegates to is a root. One delegated to by several is shown under the first; a cycle is cut where it closes. """ - agents = { - a.slug: a - for a in session.scalars(select(Agent).order_by(Agent.slug)) - if a.status != "draft" - } + agents: dict[str, Agent] = {} + proposals: set[str] = set() + for a in session.scalars(select(Agent).order_by(Agent.slug)): + if a.status in ("draft", "rejected"): + proposals.add(a.slug) + else: + agents[a.slug] = a children: dict[str, list[str]] = {} has_parent: set[str] = set() for edge in session.scalars( @@ -106,6 +108,10 @@ def agent_tree(session: Session) -> list[dict[str, Any]]: .where(LineageEdge.relation == "delegates_to", LineageEdge.dst_type == "agent") .order_by(LineageEdge.src_id, LineageEdge.dst_id) ): + # A scan's declared edges join proposals nobody has added yet; those are not + # agents to pick for a rule until someone does. + if edge.src_id in proposals or edge.dst_id in proposals: + continue children.setdefault(edge.src_id, []).append(edge.dst_id) if edge.src_id != edge.dst_id: has_parent.add(edge.dst_id) diff --git a/src/agentfox/core/db.py b/src/agentfox/core/db.py index 5eac6aa8..e8261706 100644 --- a/src/agentfox/core/db.py +++ b/src/agentfox/core/db.py @@ -202,6 +202,8 @@ def init_db(stamp: bool = True) -> None: ("playground_sandboxes", "spare", "BOOLEAN NOT NULL DEFAULT FALSE"), # e2a8c5f1b964 — an agent's circuit breaker settings and state. ("agent_controls", "breaker_json", "JSON"), + # a7d3e9c2f518 — a delegation edge read from code says how the parent hands over. + ("lineage_edges", "kind", "VARCHAR(32)"), ) diff --git a/src/agentfox/core/models/__init__.py b/src/agentfox/core/models/__init__.py index fb9271e9..2330de70 100644 --- a/src/agentfox/core/models/__init__.py +++ b/src/agentfox/core/models/__init__.py @@ -64,6 +64,7 @@ RedTeamFinding, ) from agentfox.core.models.guardrails import ( + AgentDetectorSetting, AgentMessageLog, AgentSigningKey, Budget, @@ -155,6 +156,7 @@ "Budget", "BusinessRule", "CustomRule", + "AgentDetectorSetting", "DetectorSetting", "CustomModel", "ExternalAuthorizer", diff --git a/src/agentfox/core/models/guardrails.py b/src/agentfox/core/models/guardrails.py index dc24d192..7f1987af 100644 --- a/src/agentfox/core/models/guardrails.py +++ b/src/agentfox/core/models/guardrails.py @@ -147,6 +147,26 @@ class DetectorSetting(Base, TimestampMixin): updated_by: Mapped[str] = mapped_column(String(200), default="") +class AgentDetectorSetting(Base, TimestampMixin): + """One project's choice to switch a detector on or off, over the workspace's. + + No row: the project runs what the workspace runs (`DetectorSetting`, then the + deployment). A row wins for that project only, in either direction, except that + `detector_settings.ALWAYS_ON` cannot be switched off anywhere. + """ + + __tablename__ = "agent_detector_settings" + __table_args__ = ( + UniqueConstraint("org_id", "agent", "key", name="ux_agent_detector_settings_org_agent_key"), + ) + + id: Mapped[str] = mapped_column(String(40), primary_key=True, default=lambda: ids.new_id("ads")) + agent: Mapped[str] = mapped_column(String(200), index=True) + key: Mapped[str] = mapped_column(String(64), index=True) + enabled: Mapped[bool] = mapped_column(Boolean, default=True) + updated_by: Mapped[str] = mapped_column(String(200), default="") + + class CustomModel(Base, TimestampMixin): """A classifier the workspace runs itself, called over HTTP as a detector. diff --git a/src/agentfox/core/models/registry.py b/src/agentfox/core/models/registry.py index ca4e3c16..cfc067c8 100644 --- a/src/agentfox/core/models/registry.py +++ b/src/agentfox/core/models/registry.py @@ -194,6 +194,10 @@ class LineageEdge(Base, TimestampMixin): relation: Mapped[str] = mapped_column(String(32)) observed_count: Mapped[int] = mapped_column(Integer, default=0) declared: Mapped[bool] = mapped_column(Boolean, default=False) + #: For a ``delegates_to`` edge read from code: how the parent hands work over + #: (``discovery.agents.RELATIONS``: handoff | as_tool | delegates | member). None + #: for an edge only ever seen in traffic, where a span does not say. + kind: Mapped[str | None] = mapped_column(String(32), nullable=True) first_observed_at: Mapped[dt.datetime] = mapped_column(DateTime(timezone=True), default=utcnow) last_observed_at: Mapped[dt.datetime] = mapped_column(DateTime(timezone=True), default=utcnow) diff --git a/src/agentfox/platform/ledger/operator_log.py b/src/agentfox/platform/ledger/operator_log.py index f90a62ef..22d7a3eb 100644 --- a/src/agentfox/platform/ledger/operator_log.py +++ b/src/agentfox/platform/ledger/operator_log.py @@ -160,6 +160,17 @@ class PrivilegedAction: "operator.detector.toggled", "switching a detector off silences it for the whole workspace, like a suppression", ), + PrivilegedAction( + "agentfox.capabilities.detection.detector_settings.set_enabled_for_agent", + "operator.agent_detector.toggled", + "switching a detector off for a project silences it there, like a suppression", + ), + PrivilegedAction( + "agentfox.capabilities.features.set_workspace_protection", + "operator.workspace_protection.changed", + "changes a protection for every project at once, and can switch it off or let " + "projects loosen it", + ), PrivilegedAction( "agentfox.capabilities.detection.custom_models.save_model", "operator.custom_model.saved", diff --git a/src/agentfox/platform/registry/service.py b/src/agentfox/platform/registry/service.py index 3860205c..d210db97 100644 --- a/src/agentfox/platform/registry/service.py +++ b/src/agentfox/platform/registry/service.py @@ -122,6 +122,88 @@ def register_agent( return agent +def _propose_draft(session: Session, *, slug: str, **fields: Any) -> Agent | None: + """Register a scan's proposal as a draft, unless a human already approved it. + + Re-scanning a repository proposes the same slugs again, which updates a draft in + place instead of duplicating it. An agent someone already added is left alone: a + scan turning a live agent back into a proposal would silently undo the approval. + Returns None for such an agent, since this scan did not propose it. + """ + existing = session.scalar(select(Agent).where(Agent.slug == slugify(slug))) + if existing is not None and existing.status not in ("draft", "rejected"): + return None + return register_agent(session, slug=slug, draft=True, **fields) + + +def _propose_agent_specs( + session: Session, + *, + run_id: str, + repo_short: str, + repo_display_name: str, + specs: list[dict[str, Any]], + edges: list[dict[str, Any]], + trifecta_dirs: set[str], +) -> list[str]: + """One draft per agent a multi-agent scan found, and its declared delegations. + + The slug is the repository plus the agent's own scan key (its name in code), so a + re-scan lands on the same rows. A delegation the code no longer declares is removed + from the edges, unless traffic has also seen it: a declared edge is a reading of + the code, and the code changed. + """ + slug_of: dict[str, str] = {} + created: list[str] = [] + for spec in specs: + key = slugify(str(spec.get("key") or spec.get("name") or "")) + slug = repo_short if key == repo_short else f"{repo_short}-{key}" + slug_of[str(spec.get("key"))] = slugify(slug) + tools = [str(t) for t in spec.get("tools") or [] if t] + agent = _propose_draft( + session, + slug=slug, + name=f"{repo_display_name}/{spec.get('name') or key}", + purpose="", + framework=spec.get("framework"), + risk_tier="high" if (spec.get("top_dir") or "root") in trifecta_dirs else "limited", + declared_tools=tools or None, + source_scan_run_id=run_id, + ) + if agent is not None: + created.append(agent.slug) + + wanted: set[tuple[str, str]] = set() + for edge in edges: + parent = slug_of.get(str(edge.get("parent"))) + child = slug_of.get(str(edge.get("child"))) + if not parent or not child or parent == child: + continue + wanted.add((parent, child)) + record_edge( + session, + "agent", + parent, + "agent", + child, + "delegates_to", + declared=True, + kind=str(edge.get("relation") or "delegates"), + ) + for stale in session.scalars( + select(LineageEdge).where( + LineageEdge.relation == "delegates_to", + LineageEdge.declared.is_(True), + LineageEdge.observed_count == 0, + LineageEdge.src_id.in_(list(slug_of.values())), + ) + ): + if (stale.src_id, stale.dst_id) not in wanted: + session.delete(stale) + session.flush() + return created + + def propose_from_scan( session: Session, *, @@ -131,10 +213,20 @@ def propose_from_scan( frameworks: list[str], sites: list[dict[str, Any]], author: str, + agents: list[dict[str, Any]] | None = None, + agent_edges: list[dict[str, Any]] | None = None, ) -> tuple[list[str], list[str]]: """Propose one draft agent per top-level directory and one observe-mode policy per detected framework, from a discovery scan's governable sites. + A multi-agent system (``agents`` holds two or more, see + ``discovery.ScanReport.multi_agent``) is proposed as the agents it is instead: one + draft per agent, holding that agent's own tools, with each "hands work to" relation + in ``agent_edges`` recorded as a declared ``delegates_to`` edge. Those edges are + what the guardrail map draws as sub-agents and what "apply to sub-agents" follows, + so both work before the system has run once. Directories holding no agent still + get the per-directory proposal, so a model call outside the system is not lost. + Shared by every scan entry point that ends up here — the GitHub-connected repo scan (``routes/integrations.py``) and a locally-run ``agentfox scan --submit`` / ``agentfox scan --sessions --submit`` (``routes/discovery.py``) — so a scan looks the @@ -160,6 +252,20 @@ def propose_from_scan( groups[site.get("top_dir") or "root"].append(site) created_agents: list[str] = [] + specs = agents or [] + if len(specs) >= 2: + created_agents += _propose_agent_specs( + session, + run_id=run_id, + repo_short=repo_short, + repo_display_name=repo_display_name, + specs=specs, + edges=agent_edges or [], + trifecta_dirs=trifecta_dirs, + ) + covered = {spec.get("top_dir") or "root" for spec in specs} + groups = {k: v for k, v in groups.items() if k not in covered} + for group_key, group_sites in groups.items(): providers = [s["provider"] for s in group_sites if s.get("provider")] framework = providers[0] if providers else (frameworks[0] if frameworks else None) @@ -172,7 +278,7 @@ def propose_from_scan( ) group_slug = slugify(group_key) slug = repo_short if group_slug == repo_short else f"{repo_short}-{group_slug}" - agent = register_agent( + agent = _propose_draft( session, slug=slug, name=f"{repo_display_name}/{group_key}", @@ -184,10 +290,10 @@ def propose_from_scan( # approval (`compliance.risk.classify` explains why it is a proposal). risk_tier="high" if group_key in trifecta_dirs else "limited", declared_tools=tools or None, - draft=True, source_scan_run_id=run_id, ) - created_agents.append(agent.slug) + if agent is not None: + created_agents.append(agent.slug) created_policies: list[str] = [] for framework in frameworks: @@ -621,9 +727,15 @@ def record_edge( dst_id: str, relation: str, declared: bool = False, + kind: str | None = None, ) -> LineageEdge: """Record an observed (or declared) lineage edge, idempotent under races. + ``declared=True`` is an edge read from code (``propose_from_scan``) rather than + seen in a span: it is recorded with no observation counted, so "from code, never + called" and "called N times" stay distinguishable. ``kind`` says how a declared + delegation hands work over (``discovery.agents.RELATIONS``). + The existence check below and the insert it guards are two separate round-trips, so two callers recording the same edge for the first time at nearly the same moment (two concurrent requests each building their own @@ -653,6 +765,7 @@ def record_edge( dst_id=dst_id, relation=relation, declared=declared, + kind=kind, # Set explicitly: the column default is applied at flush, # and this counter is incremented before the flush happens. observed_count=0, @@ -668,9 +781,12 @@ def record_edge( ) ) edge.observed_count += 0 if declared else 1 - edge.last_observed_at = utcnow() + if not declared: + # Only traffic counts as an observation; re-reading the code is not one. + edge.last_observed_at = utcnow() if declared: edge.declared = True + edge.kind = kind or edge.kind session.flush() return edge @@ -815,12 +931,21 @@ def assess_delegation(session: Session) -> list[Finding]: is not a repeated tool call — every call is to a different agent — so the loop is only visible in the shape of the delegation graph itself, built here from the "delegates_to" edges derive_lineage already records from subagent spans. + + Only edges traffic has actually used count. A delegation read from code (a scan's + declared edge) that has never been taken is a possibility, not a runaway chain, and + handoffs that point back at the agent they came from — a triage agent its + specialists can return to — are the ordinary shape of a multi-agent system. """ from agentfox.platform.registry.attribution import delegation_graph edges = [ (str(e.src_id), str(e.dst_id)) - for e in session.scalars(select(LineageEdge).where(LineageEdge.relation == "delegates_to")) + for e in session.scalars( + select(LineageEdge).where( + LineageEdge.relation == "delegates_to", LineageEdge.observed_count > 0 + ) + ) ] if not edges: return [] diff --git a/src/agentfox/runtime/enforcement/enforcer.py b/src/agentfox/runtime/enforcement/enforcer.py index f1e504f8..8d4aaef7 100644 --- a/src/agentfox/runtime/enforcement/enforcer.py +++ b/src/agentfox/runtime/enforcement/enforcer.py @@ -203,8 +203,8 @@ def __init__(self, session: Session, pipeline: DetectorPipeline | None = None) - # enforcer: an enforcer serves one request, and both change only by an # operator action. self._custom_rule_cache: dict[str | None, list[Any]] = {} - self._custom_model_cache: list[Any] | None = None - self._enabled_detector_cache: frozenset[str] | None = None + self._custom_model_cache: dict[str | None, list[Any]] = {} + self._enabled_detector_cache: dict[str | None, frozenset[str]] = {} #: Context assembly is the caller's step, not ours: it needs the ranked #: chunks and the real token budget, and it repairs as well as reports. Exposed @@ -419,7 +419,7 @@ def _run_detectors(self, call: _Evaluation) -> None: "tool": call.tool_key or self.evidence.get("tool"), # The workspace's own model endpoints, credentials decrypted # (custom_models.py); only read when its detector is selected. - "custom_models": self._custom_models(), + "custom_models": self._custom_models(call.agent_slug), # What the answer was built from, for detectors that check an # answer against it (grounding.nli). Supplied by the caller. "context": [ @@ -427,7 +427,7 @@ def _run_detectors(self, call: _Evaluation) -> None: for c in (self.evidence.get("chunks") or []) ], }, - enabled_detectors=self._enabled_detectors(), + enabled_detectors=self._enabled_detectors(call.agent_slug), ) ledger = self.ledger() pipeline_result = self.pipeline.run( @@ -466,20 +466,23 @@ def _custom_rules(self, agent_slug: str | None) -> list[Any]: self._custom_rule_cache[agent_slug] = compiled_custom_rules(self.session, agent_slug) return self._custom_rule_cache[agent_slug] - def _custom_models(self) -> list[Any]: - """Compiled model endpoints for this workspace, once per enforcer.""" - if self._custom_model_cache is None: - if DETECTOR_KEY in self._enabled_detectors(): - self._custom_model_cache = compiled_custom_models(self.session) - else: - self._custom_model_cache = [] - return self._custom_model_cache - - def _enabled_detectors(self) -> frozenset[str]: - """This workspace's detector choice.""" - if self._enabled_detector_cache is None: - self._enabled_detector_cache = detectors_enabled_for(self.session) - return self._enabled_detector_cache + def _custom_models(self, agent_slug: str | None) -> list[Any]: + """Compiled model endpoints for this workspace, when this call's project runs them.""" + if agent_slug not in self._custom_model_cache: + self._custom_model_cache[agent_slug] = ( + compiled_custom_models(self.session) + if DETECTOR_KEY in self._enabled_detectors(agent_slug) + else [] + ) + return self._custom_model_cache[agent_slug] + + def _enabled_detectors(self, agent_slug: str | None) -> frozenset[str]: + """The detectors this call's project runs: the workspace's choice, then its own.""" + if agent_slug not in self._enabled_detector_cache: + self._enabled_detector_cache[agent_slug] = detectors_enabled_for( + self.session, agent_slug + ) + return self._enabled_detector_cache[agent_slug] def _check_capability(self, call: _Evaluation) -> None: """Does the identity hold a grant for this tool, within its limits?""" diff --git a/tests/apps/gateway/test_features.py b/tests/apps/gateway/test_features.py new file mode 100644 index 00000000..00aa806e --- /dev/null +++ b/tests/apps/gateway/test_features.py @@ -0,0 +1,105 @@ +"""Features: the workspace sets each switch, a project inherits it until it changes it. + +A project can always be stricter than the workspace. It can be looser only where the +workspace lets projects change that protection, and setting it back to the +workspace's value is the same as inheriting. +""" + +from __future__ import annotations + +from agentfox.capabilities.detection.detector_settings import enabled_for +from tests.conftest import as_user + +ADMIN = as_user("admin@example.com") +AGENT = "support-triage" +OTHER = "payments-ops" + + +def _by_key(body, kind): + return {f["key"]: f for f in body[kind]} + + +def _agent(client, slug=AGENT): + return client.get(f"/api/agents/{slug}/features", headers=ADMIN) + + +def _set(client, key, **body): + return client.post(f"/api/features/protections/{key}", json=body, headers=ADMIN) + + +def test_a_project_inherits_the_workspace_until_it_changes_something(client): + workspace = _by_key(client.get("/api/features", headers=ADMIN).json(), "protections") + project = _by_key(_agent(client).json(), "protections") + for key, ws in workspace.items(): + assert project[key]["project"] is None + assert project[key]["workspace"] == ws["workspace"] + assert project[key]["effective"] == ws["workspace"] + + +def test_a_project_can_be_stricter_without_asking(client): + r = _set(client, "attacks", agent=AGENT, level="high") + assert r.status_code == 200, r.text + mine = _by_key(r.json(), "protections")["attacks"] + assert mine["project"] == "high" and mine["effective"] == "high" + # Nobody else changed. + assert _by_key(_agent(client, OTHER).json(), "protections")["attacks"]["project"] is None + + +def test_lowering_needs_the_workspace_to_allow_it(client): + ws = _by_key(client.get("/api/features", headers=ADMIN).json(), "protections")["harmful"] + assert ws["workspace"] != "off" and not ws["open_to_projects"] + refused = _set(client, "harmful", agent=AGENT, level="off") + assert refused.status_code == 409 + assert "does not let projects" in refused.json()["detail"] + + assert _set(client, "harmful", open_to_projects=True).status_code == 200 + r = _set(client, "harmful", agent=AGENT, level="off") + assert r.status_code == 200, r.text + mine = _by_key(r.json(), "protections")["harmful"] + assert mine["project"] == "off" and mine["effective"] == "off" + assert _by_key(_agent(client, OTHER).json(), "protections")["harmful"]["effective"] != "off" + + +def test_going_back_to_the_workspace_removes_the_projects_copy(client): + _set(client, "attacks", agent=AGENT, level="high") + r = client.delete(f"/api/agents/{AGENT}/features/protections/attacks", headers=ADMIN) + assert r.status_code == 200, r.text + assert _by_key(r.json(), "protections")["attacks"]["project"] is None + + +def test_a_workspace_change_reaches_every_project_that_inherits(client): + r = _set(client, "harmful", level="off") + assert r.status_code == 200, r.text + assert _by_key(r.json(), "protections")["harmful"]["workspace"] == "off" + for slug in (AGENT, OTHER): + assert _by_key(_agent(client, slug).json(), "protections")["harmful"]["effective"] == "off" + + +def test_a_check_can_be_switched_for_one_project_either_way(client, session): + checks = _by_key(_agent(client).json(), "checks") + assert checks["safety.lexicon"]["workspace"] and checks["safety.lexicon"]["project"] is None + + r = client.post( + "/api/features/checks/safety.lexicon", + json={"enabled": False, "agent": AGENT}, + headers=ADMIN, + ) + assert r.status_code == 200, r.text + mine = _by_key(r.json(), "checks")["safety.lexicon"] + assert mine["project"] is False and mine["effective"] is False + # The runtime reads the same choice: off for this project, on for the rest. + assert "safety.lexicon" not in enabled_for(session, AGENT) + assert "safety.lexicon" in enabled_for(session, OTHER) + + back = client.delete(f"/api/agents/{AGENT}/features/checks/safety.lexicon", headers=ADMIN) + assert _by_key(back.json(), "checks")["safety.lexicon"]["project"] is None + assert "safety.lexicon" in enabled_for(session, AGENT) + + +def test_the_safety_net_cannot_be_switched_off_for_a_project(client): + r = client.post( + "/api/features/checks/injection.heuristic", + json={"enabled": False, "agent": AGENT}, + headers=ADMIN, + ) + assert r.status_code == 400 diff --git a/tests/apps/gateway/test_multi_agent_proposals.py b/tests/apps/gateway/test_multi_agent_proposals.py new file mode 100644 index 00000000..a151af88 --- /dev/null +++ b/tests/apps/gateway/test_multi_agent_proposals.py @@ -0,0 +1,282 @@ +"""A scanned multi-agent system becomes one proposed agent per agent, joined by the +delegations its code declares, and the map shows that structure before any traffic. + +The failure this replaces: a repository with a triage agent and three specialists came +out of a scan as one proposal per directory, all four agents' tools pooled into one, +and the hierarchy appeared on the map only after the system had run ungoverned long +enough to produce subagent spans. +""" + +from __future__ import annotations + +import textwrap +from pathlib import Path + +from sqlalchemy import select + +from agentfox.capabilities.discovery.repo import scan +from agentfox.core.models import Agent, LineageEdge, ScanRun +from agentfox.platform.registry.service import ( + assess_delegation, + propose_from_scan, + record_edge, +) +from tests.conftest import as_user + +ADMIN = as_user("admin@example.com") + +SYSTEM = """ +from agents import Agent, function_tool + +@function_tool +def lookup_invoice(invoice_id: str) -> str: + return "paid" + +@function_tool +def issue_refund(invoice_id: str) -> str: + return "ok" + +billing = Agent(name="Billing", instructions="Bills.", tools=[lookup_invoice]) +refunds = Agent(name="Refunds", instructions="Refunds.", tools=[issue_refund]) +triage = Agent(name="Triage", instructions="Route.", handoffs=[billing, refunds]) +""" + + +def _system_report(tmp_path: Path): + (tmp_path / "support").mkdir() + (tmp_path / "support" / "bots.py").write_text(textwrap.dedent(SYSTEM)) + return scan(tmp_path) + + +def _run(session) -> str: + run = ScanRun(source_kind="cli", repo_full_name="acme/helpdesk", status="completed") + session.add(run) + session.flush() + return run.id + + +def _propose(session, report): + return propose_from_scan( + session, + run_id=_run(session), + repo_slug_base="helpdesk", + repo_display_name="acme/helpdesk", + frameworks=report.frameworks, + sites=[ + {"kind": s.kind, "top_dir": "support", "provider": s.provider} + for s in report.sites + if s.kind in ("agent_definition", "tool", "model_call") + ], + author="test", + agents=[{**a.to_json(), "top_dir": a.top_dir} for a in report.agents], + agent_edges=[e.to_json() for e in report.agent_edges], + ) + + +def _delegations(session) -> set[tuple[str, str, str | None, bool, int]]: + return { + (e.src_id, e.dst_id, e.kind, e.declared, e.observed_count) + for e in session.scalars(select(LineageEdge).where(LineageEdge.relation == "delegates_to")) + } + + +def test_one_draft_per_agent_with_its_own_tools_and_declared_edges(session, tmp_path): + agents, _ = _propose(session, _system_report(tmp_path)) + + assert sorted(agents) == ["helpdesk-billing", "helpdesk-refunds", "helpdesk-triage"] + rows = {a.slug: a for a in session.scalars(select(Agent))} + assert rows["helpdesk-billing"].declared_tools == ["lookup_invoice"] + assert rows["helpdesk-refunds"].declared_tools == ["issue_refund"] + assert rows["helpdesk-triage"].declared_tools == [] + assert all(rows[s].status == "draft" for s in agents) + assert rows["helpdesk-triage"].name == "acme/helpdesk/Triage" + # The directory the system lives in is not proposed a second time as a blob. + assert "helpdesk-support" not in rows + assert _delegations(session) == { + ("helpdesk-triage", "helpdesk-billing", "handoff", True, 0), + ("helpdesk-triage", "helpdesk-refunds", "handoff", True, 0), + } + + +def test_rescan_updates_in_place_and_drops_a_handoff_the_code_removed(session, tmp_path): + report = _system_report(tmp_path) + _propose(session, report) + # Traffic later confirms one of the two handoffs. + record_edge(session, "agent", "helpdesk-triage", "agent", "helpdesk-billing", "delegates_to") + + report.agent_edges = [e for e in report.agent_edges if e.child != "refunds"] + _propose(session, report) + _propose(session, report) + + assert session.scalar(select(Agent).where(Agent.slug == "helpdesk-triage")) is not None + assert len(list(session.scalars(select(Agent)))) == 3 + assert _delegations(session) == { + ("helpdesk-triage", "helpdesk-billing", "handoff", True, 1), + } + + +def test_rescan_never_turns_an_added_agent_back_into_a_proposal(session, tmp_path): + report = _system_report(tmp_path) + _propose(session, report) + triage = session.scalar(select(Agent).where(Agent.slug == "helpdesk-triage")) + triage.status, triage.registered = "active", True + session.flush() + + agents, _ = _propose(session, report) + + assert "helpdesk-triage" not in agents + assert triage.status == "active" and triage.registered is True + + +def test_a_single_agent_repo_keeps_the_per_directory_proposal(session, tmp_path): + (tmp_path / "bot").mkdir() + (tmp_path / "bot" / "main.py").write_text( + "from agents import Agent\nsolo = Agent(name='Solo', instructions='x')\n" + ) + report = scan(tmp_path) + agents, _ = propose_from_scan( + session, + run_id=_run(session), + repo_slug_base="solo", + repo_display_name="solo", + frameworks=report.frameworks, + sites=[{"kind": "model_call", "top_dir": "bot", "provider": "openai"}], + author="t", + agents=[{**a.to_json(), "top_dir": a.top_dir} for a in report.agents], + agent_edges=[], + ) + assert agents == ["solo-bot"] + + +def test_handoffs_back_to_triage_are_not_a_delegation_cycle(session): + record_edge(session, "agent", "a", "agent", "b", "delegates_to", declared=True, kind="handoff") + record_edge(session, "agent", "b", "agent", "a", "delegates_to", declared=True, kind="handoff") + assert assess_delegation(session) == [] + + +# --------------------------------------------------------------------------- +# Through the API: submission, map, lineage, list and "Add all" +# --------------------------------------------------------------------------- + + +def _submit(client) -> None: + response = client.post( + "/api/discovery/submit", + json={ + "source": "check", + "label": "helpdesk", + "frameworks": ["OpenAI Agents SDK"], + "sites": [{"kind": "tool", "top_dir": "support", "provider": None}], + "agents": [ + { + "key": "triage", + "name": "Triage", + "framework": "OpenAI Agents SDK", + "top_dir": "support", + }, + { + "key": "billing", + "name": "Billing", + "framework": "OpenAI Agents SDK", + "top_dir": "support", + }, + { + "key": "refunds", + "name": "Refunds", + "framework": "OpenAI Agents SDK", + "top_dir": "support", + }, + ], + "agent_edges": [ + {"parent": "triage", "child": "billing", "relation": "handoff"}, + {"parent": "triage", "child": "refunds", "relation": "as_tool"}, + ], + }, + headers=ADMIN, + ) + assert response.status_code == 200, response.text + summary = response.json()["summary"] + assert sorted(summary["agents_proposed"]) == [ + "helpdesk-billing", + "helpdesk-refunds", + "helpdesk-triage", + ] + + +def test_map_shows_declared_sub_agents_and_parents_before_traffic(client): + _submit(client) + + parent = client.get("/api/agents/helpdesk-triage/map", headers=ADMIN).json() + subs = {s["slug"]: s for s in parent["subagents"]} + assert subs["helpdesk-billing"] == { + "slug": "helpdesk-billing", + "name": "helpdesk/Billing", + "calls": 0, + "declared": True, + "seen": False, + "relation": "handoff", + } + assert subs["helpdesk-refunds"]["relation"] == "as_tool" + assert parent["parents"] == [] + + child = client.get("/api/agents/helpdesk-billing/map", headers=ADMIN).json() + assert [(p["slug"], p["relation"]) for p in child["parents"]] == [ + ("helpdesk-triage", "handoff") + ] + assert child["subagents"] == [] + + lineage = client.get("/api/agents/helpdesk-triage/lineage", headers=ADMIN).json() + assert {(link["target"], link["declared"]) for link in lineage["links"]} >= { + ("helpdesk-billing", True) + } + + +def test_a_seen_edge_still_counts_its_calls(client, session): + _submit(client) + record_edge(session, "agent", "helpdesk-triage", "agent", "helpdesk-billing", "delegates_to") + record_edge(session, "agent", "helpdesk-triage", "agent", "ad-hoc-helper", "delegates_to") + session.commit() + + subs = { + s["slug"]: s + for s in client.get("/api/agents/helpdesk-triage/map", headers=ADMIN).json()["subagents"] + } + assert subs["helpdesk-billing"]["calls"] == 1 + assert subs["helpdesk-billing"]["declared"] and subs["helpdesk-billing"]["seen"] + assert subs["ad-hoc-helper"] == { + "slug": "ad-hoc-helper", + "name": "ad-hoc-helper", + "calls": 1, + "declared": False, + "seen": True, + "relation": None, + } + + +def test_agent_list_carries_children_and_add_all_approves_the_system(client): + _submit(client) + agents = {a["slug"]: a for a in client.get("/api/agents", headers=ADMIN).json()["agents"]} + triage = agents["helpdesk-triage"] + assert [c["slug"] for c in triage["children"]] == ["helpdesk-billing", "helpdesk-refunds"] + + response = client.post(f"/api/agents/{triage['id']}/approve-system", headers=ADMIN) + assert response.status_code == 200, response.text + assert response.json()["approved"] == [ + "helpdesk-triage", + "helpdesk-billing", + "helpdesk-refunds", + ] + agents = {a["slug"]: a for a in client.get("/api/agents", headers=ADMIN).json()["agents"]} + assert all(agents[s]["status"] == "active" for s in agents if s.startswith("helpdesk-")) + + again = client.post(f"/api/agents/{triage['id']}/approve-system", headers=ADMIN) + assert again.status_code == 404 + + +def test_a_dismissed_sub_agent_leaves_the_map(client): + _submit(client) + agents = {a["slug"]: a for a in client.get("/api/agents", headers=ADMIN).json()["agents"]} + client.post(f"/api/agents/{agents['helpdesk-refunds']['id']}/reject", headers=ADMIN) + + subs = client.get("/api/agents/helpdesk-triage/map", headers=ADMIN).json()["subagents"] + assert [s["slug"] for s in subs] == ["helpdesk-billing"] diff --git a/tests/apps/gateway/test_project_setup.py b/tests/apps/gateway/test_project_setup.py new file mode 100644 index 00000000..c421d793 --- /dev/null +++ b/tests/apps/gateway/test_project_setup.py @@ -0,0 +1,37 @@ +"""A project's setup banner: what it still needs, computed from its data.""" + +from __future__ import annotations + +from agentfox.core.models import Agent +from tests.conftest import as_user + +ADMIN = as_user("admin@example.com") +AGENT = "support-triage" + + +def _setup(client, slug=AGENT): + r = client.get(f"/api/agents/{slug}/setup", headers=ADMIN) + assert r.status_code == 200, r.text + return r.json() + + +def test_a_quiet_project_waits_for_traffic_first(client): + body = _setup(client) + assert body["traces"] == 0 + assert body["next"] == "traffic" + + +def test_traffic_moves_it_on(client): + client.post("/v1/guard/input", json={"agent": AGENT, "content": "hello"}) + body = _setup(client) + assert body["traces"] >= 1 + assert body["next"] != "traffic" + # Watching packs are listed so the banner can say what turning blocking on switches. + if body["next"] == "enforce": + assert body["watching"] + + +def test_a_proposed_project_is_added_first(client, session): + session.add(Agent(slug="scan-found", name="Scan found", status="draft")) + session.commit() + assert _setup(client, "scan-found")["next"] == "add" diff --git a/tests/capabilities/discovery/test_multi_agent_scan.py b/tests/capabilities/discovery/test_multi_agent_scan.py new file mode 100644 index 00000000..2fc4ac8e --- /dev/null +++ b/tests/capabilities/discovery/test_multi_agent_scan.py @@ -0,0 +1,652 @@ +"""Multi-agent structure read from code: one entry per agent, its own tools, and which +agent hands work to which. + +Each fixture below is the shape the framework's own documentation and examples use, so +a passing test means the scanner reads code people actually write, not a toy dialect. +The negative tests matter as much: a local class called ``Agent`` is not an agent, and +an unresolvable reference is left out rather than guessed, because a wrong edge widens +what an "apply to sub-agents" policy reaches. +""" + +from __future__ import annotations + +import textwrap +from pathlib import Path + +from agentfox.capabilities.discovery.repo import ScanReport, scan + + +def _write(root: Path, rel: str, source: str) -> None: + path = root / rel + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(textwrap.dedent(source).lstrip()) + + +def _agents(report: ScanReport) -> dict[str, dict]: + return {a.key: a.to_json() for a in report.agents} + + +def _edges(report: ScanReport) -> set[tuple[str, str, str]]: + return {(e.parent, e.child, e.relation) for e in report.agent_edges} + + +# --------------------------------------------------------------------------- +# OpenAI Agents SDK +# --------------------------------------------------------------------------- + + +def test_openai_agents_handoffs_tools_and_as_tool(tmp_path): + _write( + tmp_path, + "support/tools.py", + """ + from agents import function_tool + + @function_tool + def lookup_invoice(invoice_id: str) -> str: + return "paid" + + @function_tool(name_override="issue_refund") + def refund(invoice_id: str) -> str: + return "ok" + """, + ) + _write( + tmp_path, + "support/agents.py", + """ + from agents import Agent, handoff + from .tools import lookup_invoice, refund + + billing_agent = Agent( + name="Billing agent", + instructions="Answer billing questions.", + tools=[lookup_invoice], + ) + refund_agent = Agent(name="Refund agent", instructions="Refunds.", tools=[refund]) + translator = Agent(name="Translator", instructions="Translate.") + + triage_agent = Agent( + name="Triage agent", + instructions="Route the customer.", + handoffs=[billing_agent, handoff(refund_agent)], + tools=[translator.as_tool(tool_name="translate", tool_description="Translate")], + ) + """, + ) + report = scan(tmp_path) + agents = _agents(report) + assert set(agents) == {"billing-agent", "refund-agent", "translator", "triage-agent"} + assert agents["billing-agent"]["tools"] == ["lookup_invoice"] + assert agents["refund-agent"]["tools"] == ["issue_refund"] + assert agents["triage-agent"]["tools"] == [] # an agent used as a tool is an edge + assert agents["triage-agent"]["framework"] == "OpenAI Agents SDK" + assert agents["triage-agent"]["file"] == "support/agents.py" + assert _edges(report) == { + ("triage-agent", "billing-agent", "handoff"), + ("triage-agent", "refund-agent", "handoff"), + ("triage-agent", "translator", "as_tool"), + } + assert "OpenAI Agents SDK" in report.frameworks + assert report.multi_agent + + +def test_cross_file_reference_resolves_by_module_level_name(tmp_path): + _write( + tmp_path, + "app/billing.py", + """ + from agents import Agent + billing_agent = Agent(name="Billing", instructions="Bill.") + """, + ) + _write( + tmp_path, + "app/triage.py", + """ + from agents import Agent + from app.billing import billing_agent + triage = Agent(name="Triage", instructions="Route.", handoffs=[billing_agent]) + """, + ) + assert _edges(scan(tmp_path)) == {("triage", "billing", "handoff")} + + +def test_a_local_agent_class_is_not_an_agent(tmp_path): + _write( + tmp_path, + "agents/__init__.py", + """ + class Agent: + def __init__(self, name): + self.name = name + """, + ) + _write( + tmp_path, + "main.py", + """ + from agents import Agent + from mylib import Agent as Other + a = Agent("worker") + b = Other(name="x", tools=[]) + """, + ) + assert scan(tmp_path).agents == [] + + +def test_unresolvable_handoff_is_left_out(tmp_path): + _write( + tmp_path, + "bot.py", + """ + from agents import Agent + a = Agent(name="A", instructions="x", handoffs=[make_agent_somehow()]) + b = Agent(name="B", instructions="y") + """, + ) + report = scan(tmp_path) + assert set(_agents(report)) == {"a", "b"} + assert report.agent_edges == [] + + +# --------------------------------------------------------------------------- +# LangGraph +# --------------------------------------------------------------------------- + + +def test_langgraph_supervisor_delegates_to_react_agents(tmp_path): + _write( + tmp_path, + "team/graph.py", + """ + from langchain_core.tools import tool + from langgraph.prebuilt import create_react_agent + from langgraph_supervisor import create_supervisor + + @tool + def web_search(query: str) -> str: + '''Search the web.''' + return "" + + def add(a: int, b: int) -> int: + return a + b + + research_agent = create_react_agent(model, tools=[web_search], name="research_expert") + math_agent = create_react_agent(model, [add], name="math_expert") + + workflow = create_supervisor([research_agent, math_agent], model=model) + app = workflow.compile() + """, + ) + report = scan(tmp_path) + agents = _agents(report) + assert agents["research-expert"]["tools"] == ["web_search"] + assert agents["math-expert"]["tools"] == ["add"] + assert agents["supervisor"]["kind"] == "supervisor" + assert _edges(report) == { + ("supervisor", "research-expert", "delegates"), + ("supervisor", "math-expert", "delegates"), + } + + +def test_langgraph_state_graph_supervisor_routes_to_worker_nodes(tmp_path): + _write( + tmp_path, + "graph.py", + """ + from typing import Literal + from langgraph.graph import StateGraph, START, END + from langgraph.prebuilt import create_react_agent + from langgraph.types import Command + + researcher = create_react_agent(llm, tools=[tavily], name="researcher") + + def research_node(state) -> Command[Literal["supervisor"]]: + result = researcher.invoke(state) + return Command(update={"messages": result["messages"]}, goto="supervisor") + + def coder_node(state): + response = llm.bind_tools([python_repl]).invoke(state["messages"]) + return {"messages": [response]} + + def supervisor_node(state) -> Command[Literal["researcher", "coder", "__end__"]]: + choice = llm.with_structured_output(Router).invoke(state["messages"]) + return Command(goto=choice["next"]) + + builder = StateGraph(State) + builder.add_node("supervisor", supervisor_node) + builder.add_node("researcher", research_node) + builder.add_node("coder", coder_node) + builder.add_edge(START, "supervisor") + builder.add_edge("coder", "supervisor") + graph = builder.compile(name="research_team") + """, + ) + report = scan(tmp_path) + agents = _agents(report) + assert agents["research-team"]["kind"] == "graph" + assert agents["supervisor"]["kind"] == "supervisor" + assert agents["coder"]["tools"] == ["python_repl"] + assert agents["researcher"]["tools"] == ["tavily"] + assert _edges(report) == { + ("research-team", "supervisor", "member"), + ("supervisor", "researcher", "delegates"), + ("supervisor", "coder", "delegates"), + } + + +def test_langgraph_conditional_edges_and_a_plain_pipeline(tmp_path): + _write( + tmp_path, + "pipeline.py", + """ + from langgraph.graph import StateGraph, END + from langgraph.prebuilt import create_react_agent + + planner = create_react_agent(llm, tools=[plan_tool], name="planner") + writer = create_react_agent(llm, tools=[write_tool], name="writer") + editor = create_react_agent(llm, tools=[], name="editor") + + def route(state): + if state["done"]: + return END + return "editor" + + g = StateGraph(State) + g.add_node("planner", planner) + g.add_node("writer", writer) + g.add_node("editor", editor) + g.add_node("format", format_output) + g.add_edge("planner", "writer") + g.add_conditional_edges("writer", route) + g.add_edge("editor", "format") + app = g.compile() + """, + ) + report = scan(tmp_path) + # `format` is a plain function: a step, not an agent. + assert "format" not in _agents(report) + assert _edges(report) == { + ("g", "planner", "member"), + ("planner", "writer", "handoff"), + ("writer", "editor", "delegates"), + } + + +def test_langgraph_swarm_handoff_tools(tmp_path): + _write( + tmp_path, + "swarm.py", + """ + from langgraph.prebuilt import create_react_agent + from langgraph_swarm import create_handoff_tool, create_swarm + + alice = create_react_agent( + model, [add, create_handoff_tool(agent_name="Bob")], name="Alice" + ) + bob = create_react_agent( + model, [create_handoff_tool(agent_name="Alice")], name="Bob" + ) + workflow = create_swarm([alice, bob], default_active_agent="Alice") + """, + ) + report = scan(tmp_path) + assert _agents(report)["alice"]["tools"] == ["add"] + assert _edges(report) >= {("alice", "bob", "handoff"), ("bob", "alice", "handoff")} + assert ("workflow", "alice", "member") in _edges(report) + + +# --------------------------------------------------------------------------- +# CrewAI +# --------------------------------------------------------------------------- + + +def test_crewai_crew_members_and_delegation(tmp_path): + _write( + tmp_path, + "crew/main.py", + """ + from crewai import Agent, Crew, Process, Task + from crewai_tools import SerperDevTool + + search_tool = SerperDevTool() + + researcher = Agent( + role="Senior Research Analyst", + goal="Find things", + backstory="...", + tools=[search_tool, SerperDevTool()], + allow_delegation=True, + ) + writer = Agent(role="Writer", goal="Write", backstory="...", allow_delegation=False) + + crew = Crew(agents=[researcher, writer], tasks=[t1, t2], process=Process.sequential) + crew.kickoff() + """, + ) + report = scan(tmp_path) + agents = _agents(report) + assert agents["senior-research-analyst"]["tools"] == ["search_tool", "SerperDevTool"] + assert agents["crew"]["kind"] == "crew" + assert _edges(report) == { + ("crew", "senior-research-analyst", "member"), + ("crew", "writer", "member"), + ("senior-research-analyst", "writer", "delegates"), + } + + +def test_crewai_manager_agent_delegates_to_members(tmp_path): + _write( + tmp_path, + "crew.py", + """ + from crewai import Agent, Crew, Process + + manager = Agent(role="Project Manager", goal="g", backstory="b", allow_delegation=True) + dev = Agent(role="Developer", goal="g", backstory="b") + qa = Agent(role="QA", goal="g", backstory="b") + team = Crew(agents=[dev, qa], tasks=[], manager_agent=manager, process=Process.hierarchical) + """, + ) + assert _edges(scan(tmp_path)) == { + ("team", "project-manager", "member"), + ("project-manager", "developer", "delegates"), + ("project-manager", "qa", "delegates"), + } + + +def test_crewai_crewbase_class_layout(tmp_path): + _write( + tmp_path, + "src/research_crew/crew.py", + """ + from crewai import Agent, Crew, Process, Task + from crewai.project import CrewBase, agent, crew, task + from crewai_tools import SerperDevTool + + @CrewBase + class ResearchCrew: + @agent + def researcher(self) -> Agent: + return Agent(config=self.agents_config["researcher"], tools=[SerperDevTool()]) + + @agent + def reporting_analyst(self) -> Agent: + return Agent(config=self.agents_config["reporting_analyst"]) + + @crew + def crew(self) -> Crew: + return Crew(agents=self.agents, tasks=self.tasks, process=Process.sequential) + """, + ) + report = scan(tmp_path) + agents = _agents(report) + assert agents["researcher"]["tools"] == ["SerperDevTool"] + assert agents["researchcrew"]["kind"] == "crew" + assert _edges(report) == { + ("researchcrew", "researcher", "member"), + ("researchcrew", "reporting-analyst", "member"), + } + + +# --------------------------------------------------------------------------- +# AutoGen +# --------------------------------------------------------------------------- + + +def test_autogen_agentchat_team_and_tools(tmp_path): + _write( + tmp_path, + "team.py", + """ + from autogen_agentchat.agents import AssistantAgent, UserProxyAgent + from autogen_agentchat.teams import RoundRobinGroupChat, SelectorGroupChat + + async def get_weather(city: str) -> str: + return "sunny" + + weather = AssistantAgent("weather_agent", model_client=client, tools=[get_weather]) + critic = AssistantAgent(name="critic", model_client=client) + user = UserProxyAgent("user") + + team = RoundRobinGroupChat([weather, critic], termination_condition=stop) + selector = SelectorGroupChat(participants=[weather, critic, user], model_client=client) + """, + ) + report = scan(tmp_path) + agents = _agents(report) + assert agents["weather-agent"]["tools"] == ["get_weather"] + assert "AutoGen" in report.frameworks + assert _edges(report) == { + ("team", "weather-agent", "member"), + ("team", "critic", "member"), + ("selector", "weather-agent", "delegates"), + ("selector", "critic", "delegates"), + ("selector", "user", "delegates"), + } + + +def test_autogen_swarm_handoffs_by_name(tmp_path): + _write( + tmp_path, + "swarm.py", + """ + from autogen_agentchat.agents import AssistantAgent + from autogen_agentchat.teams import Swarm + + travel = AssistantAgent( + "travel_agent", model_client=c, handoffs=["flights_refunder", "user"] + ) + flights = AssistantAgent( + "flights_refunder", model_client=c, handoffs=["travel_agent"], tools=[refund_flight] + ) + team = Swarm([travel, flights]) + """, + ) + edges = _edges(scan(tmp_path)) + assert ("travel-agent", "flights-refunder", "handoff") in edges + assert ("flights-refunder", "travel-agent", "handoff") in edges + + +def test_ag2_group_chat_manager_runs_the_chat(tmp_path): + _write( + tmp_path, + "chat.py", + """ + import autogen + + coder = autogen.AssistantAgent(name="Coder", llm_config=cfg) + pm = autogen.AssistantAgent(name="Product_manager", llm_config=cfg) + groupchat = autogen.GroupChat(agents=[coder, pm], messages=[], max_round=12) + manager = autogen.GroupChatManager(groupchat=groupchat, name="chat_manager", llm_config=cfg) + """, + ) + report = scan(tmp_path) + assert "groupchat" not in _agents(report) + assert _edges(report) == { + ("chat-manager", "coder", "delegates"), + ("chat-manager", "product-manager", "delegates"), + } + + +# --------------------------------------------------------------------------- +# Google ADK +# --------------------------------------------------------------------------- + + +def test_google_adk_sub_agents_and_agent_tool(tmp_path): + _write( + tmp_path, + "adk/agent.py", + """ + from google.adk.agents import Agent, SequentialAgent + from google.adk.tools.agent_tool import AgentTool + + greeter = Agent(name="greeter", model="gemini-2.0-flash", tools=[say_hello]) + searcher = Agent(name="searcher", model="gemini-2.0-flash") + root_agent = Agent( + name="coordinator", + model="gemini-2.0-flash", + sub_agents=[greeter], + tools=[AgentTool(agent=searcher), get_time], + ) + pipeline = SequentialAgent(name="pipeline", sub_agents=[root_agent]) + """, + ) + report = scan(tmp_path) + agents = _agents(report) + assert agents["coordinator"]["tools"] == ["get_time"] + assert "Google ADK" in report.frameworks + assert _edges(report) == { + ("coordinator", "greeter", "delegates"), + ("coordinator", "searcher", "as_tool"), + ("pipeline", "coordinator", "member"), + } + + +# --------------------------------------------------------------------------- +# TypeScript — OpenAI Agents SDK +# --------------------------------------------------------------------------- + + +def test_openai_agents_typescript(tmp_path): + _write( + tmp_path, + "src/agents.ts", + """ + import { Agent, tool, handoff } from '@openai/agents'; + import { z } from 'zod'; + + const getWeather = tool({ + name: 'get_weather', + description: 'Weather for a city', + parameters: z.object({ city: z.string() }), + execute: async ({ city }) => `sunny in ${city}`, + }); + + const weatherAgent = new Agent({ name: 'Weather', instructions: 'W', tools: [getWeather] }); + const summarizer = new Agent({ name: 'Summarizer', instructions: 'Summarize, short.' }); + export const triage = Agent.create({ + name: 'Triage', + instructions: 'Route, then answer.', + handoffs: [weatherAgent], + tools: [summarizer.asTool({ toolName: 'summarize' })], + }); + """, + ) + report = scan(tmp_path) + agents = _agents(report) + assert agents["weather"]["tools"] == ["get_weather"] + assert _edges(report) == { + ("triage", "weather", "handoff"), + ("triage", "summarizer", "as_tool"), + } + + +# --------------------------------------------------------------------------- +# Output shape +# --------------------------------------------------------------------------- + + +def test_json_and_submission_payload_carry_the_structure(tmp_path): + _write( + tmp_path, + "bots/team.py", + """ + from agents import Agent, function_tool + + @function_tool + def secret_internal_tool() -> str: + return "" + + b = Agent(name="B", instructions="b", tools=[secret_internal_tool]) + a = Agent(name="A", instructions="a", handoffs=[b]) + """, + ) + report = scan(tmp_path) + data = report.to_json() + assert {a["key"] for a in data["agents"]} == {"a", "b"} + assert data["agent_edges"] == [{"parent": "a", "child": "b", "relation": "handoff"}] + # Existing keys are untouched. + assert data["agent_definitions"] == 0 and "sites" in data + + payload = report.to_submission_payload(source="check") + assert payload["agents"] == [ + { + "key": "b", + "name": "B", + "framework": "OpenAI Agents SDK", + "kind": "agent", + "top_dir": "bots", + }, + { + "key": "a", + "name": "A", + "framework": "OpenAI Agents SDK", + "kind": "agent", + "top_dir": "bots", + }, + ] + assert payload["agent_edges"] == [{"parent": "a", "child": "b", "relation": "handoff"}] + # No file paths, lines or tool names leave the machine. + dumped = repr(payload["agents"]) + repr(payload["agent_edges"]) + assert "team.py" not in dumped and "secret_internal_tool" not in dumped + + +def test_single_agent_repo_sends_no_structure(tmp_path): + _write( + tmp_path, + "bot.py", + """ + from agents import Agent + solo = Agent(name="Solo", instructions="x") + """, + ) + report = scan(tmp_path) + assert len(report.agents) == 1 and not report.multi_agent + payload = report.to_submission_payload(source="check") + assert payload["agents"] == [] and payload["agent_edges"] == [] + + +def test_terminal_summary_names_who_hands_work_to_whom(tmp_path): + _write( + tmp_path, + "bots.py", + """ + from agents import Agent + b = Agent(name="Billing", instructions="b") + r = Agent(name="Refunds", instructions="r") + t = Agent(name="Triage", instructions="t", handoffs=[b, r]) + """, + ) + assert scan(tmp_path).agent_summary() == [ + "3 agents, 2 handoff(s) between them", + "triage hands off to billing, refunds", + ] + + +def test_tool_names_go_only_where_asked(tmp_path): + """A submission to a server never names tools; `up` recording into a local + database asks for them, so each project shows its own tools before traffic.""" + _write( + tmp_path, + "bots.py", + """ + from agents import Agent, function_tool + + @function_tool + def search_kb(q: str) -> str: + return "" + + kb = Agent(name="Knowledge", instructions="k", tools=[search_kb]) + t = Agent(name="Triage", instructions="t", tools=[kb.as_tool(tool_name="ask_kb")]) + """, + ) + report = scan(tmp_path) + assert all("tools" not in a for a in report.to_submission_payload(source="check")["agents"]) + local = { + a["key"]: a for a in report.to_submission_payload(source="check", tool_names=True)["agents"] + } + assert "search_kb" in local["knowledge"]["tools"] + assert report.agent_summary()[1] == "triage uses knowledge as a tool"