diff --git a/ui/src/pages/Agents.test.tsx b/ui/src/pages/Agents.test.tsx
new file mode 100644
index 00000000000..ea8a02f03c9
--- /dev/null
+++ b/ui/src/pages/Agents.test.tsx
@@ -0,0 +1,153 @@
+// @vitest-environment jsdom
+
+import { act } from "react";
+import type { ReactNode } from "react";
+import { createRoot } from "react-dom/client";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import type { Agent } from "@paperclipai/shared";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { Agents } from "./Agents";
+
+const mockAgentsApi = vi.hoisted(() => ({
+ list: vi.fn(),
+ org: vi.fn(),
+}));
+
+const mockHeartbeatsApi = vi.hoisted(() => ({
+ liveRunsForCompany: vi.fn(),
+}));
+
+const mockOpenNewAgent = vi.hoisted(() => vi.fn());
+const mockSetBreadcrumbs = vi.hoisted(() => vi.fn());
+
+vi.mock("@/lib/router", () => ({
+ Link: ({ children, to, ...props }: { children: ReactNode; to: string }) => (
+ {children}
+ ),
+ useLocation: () => ({ pathname: "/agents/all", search: "", hash: "", state: null }),
+ useNavigate: () => vi.fn(),
+}));
+
+vi.mock("../context/CompanyContext", () => ({
+ useCompany: () => ({ selectedCompanyId: "company-1" }),
+}));
+
+vi.mock("../context/DialogContext", () => ({
+ useDialogActions: () => ({ openNewAgent: mockOpenNewAgent }),
+}));
+
+vi.mock("../context/BreadcrumbContext", () => ({
+ useBreadcrumbs: () => ({ setBreadcrumbs: mockSetBreadcrumbs }),
+}));
+
+vi.mock("../context/SidebarContext", () => ({
+ useSidebar: () => ({ isMobile: false }),
+}));
+
+vi.mock("../api/agents", () => ({
+ agentsApi: mockAgentsApi,
+}));
+
+vi.mock("../api/heartbeats", () => ({
+ heartbeatsApi: mockHeartbeatsApi,
+}));
+
+vi.mock("../adapters/adapter-display-registry", () => ({
+ getAdapterLabel: (type: string) => type,
+}));
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
+
+function makeAgent(overrides: Partial): Agent {
+ return {
+ id: "agent-1",
+ companyId: "company-1",
+ name: "Alpha",
+ urlKey: "alpha",
+ role: "engineer",
+ title: null,
+ icon: null,
+ status: "active",
+ reportsTo: null,
+ capabilities: null,
+ adapterType: "codex_local",
+ adapterConfig: {},
+ runtimeConfig: {},
+ budgetMonthlyCents: 0,
+ spentMonthlyCents: 0,
+ pauseReason: null,
+ pausedAt: null,
+ permissions: { canCreateAgents: false },
+ lastHeartbeatAt: null,
+ metadata: null,
+ createdAt: new Date("2026-01-01T00:00:00Z"),
+ updatedAt: new Date("2026-01-01T00:00:00Z"),
+ ...overrides,
+ };
+}
+
+async function flushReact() {
+ await act(async () => {
+ await Promise.resolve();
+ await new Promise((resolve) => window.setTimeout(resolve, 0));
+ });
+}
+
+describe("Agents", () => {
+ let container: HTMLDivElement;
+ let root: ReturnType | null;
+ let queryClient: QueryClient;
+
+ beforeEach(() => {
+ container = document.createElement("div");
+ document.body.appendChild(container);
+ root = null;
+ queryClient = new QueryClient({
+ defaultOptions: { queries: { retry: false } },
+ });
+
+ mockAgentsApi.list.mockResolvedValue([
+ makeAgent({ adapterConfig: { model: "gpt-5.4" } }),
+ ]);
+ mockAgentsApi.org.mockResolvedValue([
+ {
+ id: "agent-1",
+ name: "Alpha",
+ role: "engineer",
+ status: "active",
+ reports: [],
+ },
+ ]);
+ mockHeartbeatsApi.liveRunsForCompany.mockResolvedValue([]);
+ });
+
+ afterEach(async () => {
+ const currentRoot = root;
+ if (currentRoot) {
+ await act(async () => {
+ currentRoot.unmount();
+ });
+ }
+ queryClient.clear();
+ container.remove();
+ document.body.innerHTML = "";
+ vi.clearAllMocks();
+ });
+
+ it("shows the configured model beside the adapter on the all agents page", async () => {
+ root = createRoot(container);
+ await act(async () => {
+ root!.render(
+
+
+ ,
+ );
+ });
+ await flushReact();
+ await flushReact();
+
+ expect(container.textContent).toContain("codex_local");
+ expect(container.textContent).toContain("gpt-5.4");
+ });
+});
diff --git a/ui/src/pages/Agents.tsx b/ui/src/pages/Agents.tsx
index 179a7c065ea..6e055da915b 100644
--- a/ui/src/pages/Agents.tsx
+++ b/ui/src/pages/Agents.tsx
@@ -41,6 +41,13 @@ function filterAgents(agents: Agent[], tab: FilterTab, showTerminated: boolean):
.sort((a, b) => a.name.localeCompare(b.name));
}
+function getConfiguredModel(agent: Agent): string | null {
+ const value = agent.adapterConfig?.model;
+ if (typeof value !== "string") return null;
+ const model = value.trim();
+ return model.length > 0 ? model : null;
+}
+
function filterOrgTree(nodes: OrgNode[], tab: FilterTab, showTerminated: boolean): OrgNode[] {
return nodes
.reduce((acc, node) => {
@@ -253,9 +260,15 @@ export function Agents() {
liveCount={liveRunByAgent.get(agent.id)!.liveCount}
/>
)}
-
+
{getAdapterLabel(agent.adapterType)}
+
+ {getConfiguredModel(agent) ?? "—"}
+
{agent.lastHeartbeatAt ? relativeTime(agent.lastHeartbeatAt) : "—"}
@@ -356,9 +369,15 @@ function OrgTreeNode({
)}
{agent && (
<>
-
+
{getAdapterLabel(agent.adapterType)}
+
+ {getConfiguredModel(agent) ?? "—"}
+
{agent.lastHeartbeatAt ? relativeTime(agent.lastHeartbeatAt) : "—"}