Skip to content
45 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)1import { Outlet, data, type ShouldRevalidateFunctionArgs, useMatch, useRouteLoaderData } from "react-router";
2
3import { type WorkspaceAgent, hasCodeAccess } from "@g1t/contracts";
Chat and workspace agents: channels, DMs and named agents you talk to4
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)5import { AgentsSidebar } from "../../../components/agents-mode";
Chat and workspace agents: channels, DMs and named agents you talk to6
7import type { Route } from "./+types/layout";
8import { workspaceAgents } from "../../../lib/services.server";
9import { getViewer, roleIn } from "../../../lib/session.server";
10
11/** Agents mode: the workspace's agents, for its sidebar; null when the agents service does not answer. */
12export type AgentsLayoutData = { slug: string; agents: WorkspaceAgent[] | null };
13
14export async function loader({ params, context }: Route.LoaderArgs): Promise<AgentsLayoutData> {
15 const viewer = getViewer(context);
16 if (!roleIn(viewer, params.owner)) throw data(null, { status: 404 });
17 const listed = await workspaceAgents.list(params.owner.toLowerCase(), viewer!).catch(() => null);
18 return {
19 slug: params.owner.toLowerCase(),
20 agents: listed?.ok ? listed.value.filter((agent) => !agent.archived_at).sort((a, b) => a.display_name.localeCompare(b.display_name)) : null,
21 };
22}
23
24/** The list changes when an agent is made or changed, not when moving between them. */
25export function shouldRevalidate({ currentParams, nextParams, formMethod, defaultShouldRevalidate }: ShouldRevalidateFunctionArgs) {
26 if (formMethod && formMethod !== "GET") return defaultShouldRevalidate;
27 return currentParams.owner !== nextParams.owner;
28}
29
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)30export default function AgentsLayout({ loaderData }: Route.ComponentProps) {
31 const root = useRouteLoaderData("root") as { user?: { workspaces?: { slug: string; role: string; code_access?: boolean }[] } | null } | undefined;
32 const membership = root?.user?.workspaces?.find((m) => m.slug === loaderData.slug) ?? null;
33 // A phone: the agents first, as a list; the fleet's numbers under it.
34 const index = useMatch("/:owner/-/agents");
35 return (
36 <>
37 {index && (
38 <div className="-mx-4 -mt-6 mb-8 md:hidden">
39 <AgentsSidebar slug={loaderData.slug} shellAgents={null} code={hasCodeAccess(membership)} owner={membership?.role === "owner"} phone />
40 </div>
41 )}
42 <Outlet />
43 </>
44 );
Chat and workspace agents: channels, DMs and named agents you talk to45}

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