diff --git a/apps/web/app/settings/agents/page.tsx b/apps/web/app/settings/agents/page.tsx
index 2b1c4722e1..44c2ced89c 100644
--- a/apps/web/app/settings/agents/page.tsx
+++ b/apps/web/app/settings/agents/page.tsx
@@ -77,27 +77,24 @@ function InstalledAgentsHeader({
-
-
- {profiles.length === 0
- ? t("agents:noProfilesYet")
- : t("agents:profileCount", { count: profiles.length })}
-
-
-
-
- {t("agents:newProfile")}
-
-
+
+ {savedAgent.profiles.map((profile) => (
+
+ ))}
- {/* Narrowed on `savedAgent` rather than on `profiles.length`: the rows
- need the agent itself, and only this check proves it is there. */}
- {savedAgent && savedAgent.profiles.length > 0 && (
-
- {savedAgent.profiles.map((profile) => (
-
- ))}
-
- )}
);
}
diff --git a/apps/web/components/settings/installed-agent-card.test.tsx b/apps/web/components/settings/installed-agent-card.test.tsx
new file mode 100644
index 0000000000..16ea726831
--- /dev/null
+++ b/apps/web/components/settings/installed-agent-card.test.tsx
@@ -0,0 +1,67 @@
+import { cleanup, render, screen } from "@testing-library/react";
+import type { ReactNode } from "react";
+import { afterEach, describe, expect, it, vi } from "vitest";
+
+import type { Agent, AgentDiscovery } from "@/lib/types/http";
+
+vi.mock("@/components/routing/app-link", () => ({
+ default: ({ children, ...props }: { children?: ReactNode }) =>
{children} ,
+}));
+
+vi.mock("@/components/agent-logo", () => ({
+ AgentLogo: () =>
,
+}));
+
+vi.mock("@/components/settings/host-shell-dialog", () => ({
+ HostShellDialog: () => null,
+}));
+
+vi.mock("@/components/settings/agent-login-dialog", () => ({
+ AgentLoginDialog: () => null,
+}));
+
+vi.mock("@/components/settings/agent-runtime-update-control", () => ({
+ AgentRuntimeUpdateControl: () => null,
+}));
+
+import { InstalledAgentCard } from "./installed-agent-card";
+
+const DISCOVERY = {
+ name: "mock-agent",
+ supports_mcp: false,
+ available: true,
+ matched_path: null,
+ login_command: undefined,
+} as unknown as AgentDiscovery;
+
+const SAVED_WITHOUT_PROFILES = {
+ id: "agent-1",
+ name: "mock-agent",
+ profiles: [],
+} as unknown as Agent;
+
+function renderCard(savedAgent?: Agent) {
+ return render(
+
,
+ );
+}
+
+afterEach(() => cleanup());
+
+describe("InstalledAgentCard setup links", () => {
+ it("uses the base setup route when no agent record exists", () => {
+ renderCard();
+
+ expect(screen.getByTestId("setup-profile-mock-agent").getAttribute("href")).toBe(
+ "/settings/agents/mock-agent",
+ );
+ });
+
+ it("uses create mode when a saved agent has no profiles", () => {
+ renderCard(SAVED_WITHOUT_PROFILES);
+
+ expect(screen.getByTestId("setup-profile-mock-agent").getAttribute("href")).toBe(
+ "/settings/agents/mock-agent?mode=create",
+ );
+ });
+});
diff --git a/apps/web/components/settings/installed-agent-card.tsx b/apps/web/components/settings/installed-agent-card.tsx
index d6468d316b..20f01be01a 100644
--- a/apps/web/components/settings/installed-agent-card.tsx
+++ b/apps/web/components/settings/installed-agent-card.tsx
@@ -4,7 +4,7 @@ import { useState } from "react";
import type { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import Link from "@/components/routing/app-link";
-import { IconLoader2, IconLock, IconSettings } from "@tabler/icons-react";
+import { IconLoader2, IconLock, IconPlus, IconSettings } from "@tabler/icons-react";
import { Badge } from "@kandev/ui/badge";
import { NotInstalledBadge } from "@/components/settings/record-badges";
import { Button } from "@kandev/ui/button";
@@ -136,6 +136,7 @@ export function InstalledAgentCard({
const { t } = useTranslation();
const configured = Boolean(savedAgent && savedAgent.profiles.length > 0);
const hasAgentRecord = Boolean(savedAgent);
+ const agentHref = `/settings/agents/${encodeURIComponent(agent.name)}`;
const [loginOpen, setLoginOpen] = useState(false);
const [shellOpen, setShellOpen] = useState(false);
const authRequired = capabilityStatus === "auth_required";
@@ -153,7 +154,10 @@ export function InstalledAgentCard({
return (
{/* Header section: identity + agent-level actions. */}
-
+
)}
- {!hasAgentRecord && (
- // Sized to match the runtime-update trigger it sits beside — a
- // coarse-pointer target on a phone, 28px from `sm` up. The default
- // `sm` size (24px) both mismatched that neighbour on desktop and
- // left a sub-target-size tap area on touch.
-
-
+ {configured ? (
+
+
+
+ {t("agents:newProfile")}
+
+
+ ) : (
+ // Keep the setup action for agents without a usable profile. A
+ // saved record needs create mode so its first profile is drafted.
+
+
{t("agents:setupProfile")}
diff --git a/apps/web/e2e/tests/settings/agent-profile-layout.spec.ts b/apps/web/e2e/tests/settings/agent-profile-layout.spec.ts
new file mode 100644
index 0000000000..4bcd3213ea
--- /dev/null
+++ b/apps/web/e2e/tests/settings/agent-profile-layout.spec.ts
@@ -0,0 +1,47 @@
+import { test, expect } from "../../fixtures/test-base";
+
+test.describe("Agent settings profile layout", () => {
+ test("keeps profile creation in the agent card header", async ({ testPage, apiClient }) => {
+ const { agents } = await apiClient.listAgents();
+ const agent = agents[0];
+ if (!agent || agent.profiles.length === 0) {
+ throw new Error("The E2E fixture must provide a configured agent profile");
+ }
+
+ await testPage.goto("/settings/agents");
+
+ const card = testPage.getByTestId(`agent-group-${agent.name}`);
+ const header = card.getByTestId(`agent-card-header-${agent.name}`);
+ const newProfile = header.getByTestId(`new-profile-${agent.name}`);
+ await expect(newProfile).toBeVisible({ timeout: 15_000 });
+ await expect(newProfile).toHaveAttribute(
+ "href",
+ `/settings/agents/${encodeURIComponent(agent.name)}?mode=create`,
+ );
+
+ // The action belongs to the card header, not the profile-list body.
+ await expect(
+ card.getByTestId(`agent-profiles-${agent.name}`).getByTestId(`new-profile-${agent.name}`),
+ ).toHaveCount(0);
+ await expect(card.getByText(/^\d+ profiles?$/)).toHaveCount(0);
+
+ await newProfile.click();
+ await expect(testPage).toHaveURL(
+ new RegExp(`/settings/agents/${encodeURIComponent(agent.name)}\\?mode=create$`),
+ );
+ });
+
+ test("orders installed-agent actions with refresh before agent creation", async ({
+ testPage,
+ }) => {
+ await testPage.goto("/settings/agents");
+
+ const actions = testPage.getByTestId("installed-agents-actions");
+ await expect(actions).toBeVisible({ timeout: 15_000 });
+ await expect(
+ actions
+ .locator("[data-testid]")
+ .evaluateAll((elements) => elements.map((element) => element.getAttribute("data-testid"))),
+ ).resolves.toEqual(["open-host-shell", "rescan-agents-button", "new-agent-button"]);
+ });
+});
diff --git a/apps/web/e2e/tests/settings/mobile-agent-profile-layout.spec.ts b/apps/web/e2e/tests/settings/mobile-agent-profile-layout.spec.ts
new file mode 100644
index 0000000000..7c4f76b4ac
--- /dev/null
+++ b/apps/web/e2e/tests/settings/mobile-agent-profile-layout.spec.ts
@@ -0,0 +1,56 @@
+import { test, expect } from "../../fixtures/test-base";
+
+test.describe("Agent settings profile layout on mobile", () => {
+ test("keeps creation reachable without horizontal overflow", async ({ testPage, apiClient }) => {
+ const { agents } = await apiClient.listAgents();
+ const agent = agents[0];
+ if (!agent || agent.profiles.length === 0) {
+ throw new Error("The E2E fixture must provide a configured agent profile");
+ }
+
+ await testPage.goto("/settings/agents");
+
+ await expect
+ .poll(
+ async () =>
+ testPage.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth),
+ { timeout: 15_000 },
+ )
+ .toBe(true);
+
+ const card = testPage.getByTestId(`agent-group-${agent.name}`);
+ const newProfile = card
+ .getByTestId(`agent-card-header-${agent.name}`)
+ .getByTestId(`new-profile-${agent.name}`);
+ await expect(newProfile).toBeVisible({ timeout: 15_000 });
+ await expect
+ .poll(
+ async () => {
+ const box = await newProfile.boundingBox();
+ return box ? Math.min(box.width, box.height) : null;
+ },
+ { timeout: 10_000 },
+ )
+ .toBeGreaterThanOrEqual(44);
+
+ const actions = testPage.getByTestId("installed-agents-actions");
+ for (const testId of ["open-host-shell", "rescan-agents-button", "new-agent-button"]) {
+ const control = actions.getByTestId(testId);
+ await expect(control).toBeVisible();
+ await expect
+ .poll(
+ async () => {
+ const box = await control.boundingBox();
+ return box ? Math.min(box.width, box.height) : null;
+ },
+ { timeout: 10_000 },
+ )
+ .toBeGreaterThanOrEqual(44);
+ }
+
+ await newProfile.tap();
+ await expect(testPage).toHaveURL(
+ new RegExp(`/settings/agents/${encodeURIComponent(agent.name)}\\?mode=create$`),
+ );
+ });
+});
diff --git a/docs/plans/agent-settings-profile-layout/plan.md b/docs/plans/agent-settings-profile-layout/plan.md
new file mode 100644
index 0000000000..d11e0ecec9
--- /dev/null
+++ b/docs/plans/agent-settings-profile-layout/plan.md
@@ -0,0 +1,128 @@
+---
+spec: docs/specs/agents/settings-profile-layout.md
+created: 2026-08-13
+status: building
+---
+
+# Implementation Plan: Simplify the agent settings profile layout
+
+## Overview
+
+Move profile creation into each installed-agent card header and remove the
+redundant profile-count/action row from the card body. Reorder the existing
+installed-agent toolbar so refresh/rescan is immediately before the rightmost
+agent-creation action. Preserve the current profile rows and agent-level
+controls, then prove the layout and navigation on desktop and mobile.
+
+The change is frontend-only. No API, store, persistence, or locale contract is
+needed because the existing translated labels and routes are reused.
+
+## Frontend
+
+### Agent profile sub-list
+
+- `apps/web/components/settings/agents/agent-profiles-section.tsx`
+ - Remove the profile-count/empty-state header row and its creation link.
+ - Keep the existing profile rows and their actions in the card body.
+ - Avoid rendering an empty profile body when no saved profiles exist; the
+ card header supplies the setup action.
+
+### Installed-agent card
+
+- `apps/web/components/settings/installed-agent-card.tsx`
+ - Add the existing `New profile` link to the configured agent's header,
+ alongside agent-level controls, using the existing `?mode=create` route.
+ - Keep `Setup profile` as the only header creation action for an agent with
+ no saved record.
+ - Preserve touch-sized header actions on mobile and the existing status,
+ authentication, and runtime-update controls.
+
+### Installed-agent toolbar
+
+- `apps/web/app/settings/agents/page.tsx`
+ - Reorder the current header controls so the refresh/rescan button is
+ immediately before the existing Add TUI Agent action and the latter is
+ rightmost.
+ - Add stable test IDs for the toolbar and the two ordered controls if the
+ existing markup does not provide a reliable scoped selector.
+ - Keep the terminal action available and keep the current agent-creation
+ behavior and label.
+
+## Tests
+
+- **What:** A configured agent shows profile creation in the card header,
+ profile rows have no count/action sub-header, and an unconfigured agent has
+ only its setup action.
+ **File:** `apps/web/components/settings/agents/agent-profiles-section.test.tsx`
+ and `apps/web/components/settings/installed-agent-card.test.tsx`.
+ **How:** React Testing Library assertions against the rendered card/list,
+ including the creation link target and absence of count/empty-state copy.
+- **What:** Existing profile-row duplicate/delete behavior remains intact.
+ **File:** `apps/web/components/settings/agents/agent-profiles-section.test.tsx`
+ **How:** Keep and rerun the current store read-after-write tests.
+- **What:** The installed-agent toolbar keeps terminal available and orders
+ refresh/rescan directly before the rightmost agent-creation action.
+ **File:** `apps/web/e2e/tests/settings/agent-profile-layout.spec.ts`
+ **How:** Navigate through the real settings page, scope the toolbar by its
+ test ID, assert the ordered controls, and verify the configured card's
+ **New profile** link route.
+
+## E2E Tests
+
+- **Scenario:** GIVEN a configured agent with one or more profiles, WHEN the
+ user opens `/settings/agents`, THEN the card header contains **New profile**,
+ profile rows are directly below it, and no profile-count/action row exists.
+ **File:** `apps/web/e2e/tests/settings/agent-profile-layout.spec.ts`.
+- **Scenario:** GIVEN a phone viewport below 768px, WHEN the user opens the
+ agents settings page, THEN the same creation action and ordered toolbar are
+ touch-reachable and the document has no horizontal overflow.
+ **File:** `apps/web/e2e/tests/settings/mobile-agent-profile-layout.spec.ts`.
+ **What to verify:** Use the configured `mobile-chrome` project, assert the
+ header action hitbox is at least 44px, use `.tap()` for the creation action,
+ and assert the settings document remains contained.
+
+## Mobile design contract
+
+- Desktop outcome: manage an agent's saved profiles and create another profile
+ from the same agent card.
+- Mobile entry point: the installed-agent card on `/settings/agents`; no new
+ drawer, route, or mobile-only control is needed because the action is a
+ direct link and the existing settings scroll container remains the sole
+ vertical scroll owner.
+- Nearest shipped exemplar: the managed runtime update controls on the same
+ agent card, whose mobile E2E coverage verifies 44px controls and no document
+ overflow. Reuse its touch-sized action treatment and existing card geometry.
+- Mobile hierarchy: agent identity/status first, creation and agent-level
+ controls in the header, then profile rows in one vertical list.
+- Presentation: inline card-header action; no drawer is justified because
+ profile creation is a direct destination rather than a temporary choice.
+- Shared state and logic: reuse the existing agent/profile store data and route
+ handlers; only the placement and responsive classes change.
+
+## Verification Results
+
+Passed.
+
+- Component regression coverage passed: `cd apps/web && pnpm test -- components/settings/agents/agent-profiles-section.test.tsx components/settings/installed-agent-card.test.tsx` (8 tests).
+- Static checks passed: `cd apps/web && pnpm run typecheck`, the focused ESLint command from task 01, and `cd apps/web && pnpm run i18n:check` (with the repository's existing advisory catalog warnings).
+- Desktop managed E2E passed: `cd apps/web && pnpm e2e:run --project chromium tests/settings/agent-profile-layout.spec.ts` (2 tests).
+- Mobile managed E2E passed: `cd apps/web && pnpm e2e:run --project mobile-chrome tests/settings/mobile-agent-profile-layout.spec.ts` (1 test).
+- Review follow-up coverage passed for both setup routes, the saved-empty profile body guard, and the toolbar ordering invariant.
+
+## Implementation Waves And Parallel Candidates
+
+Wave 1:
+
+- [x] [task-01-agent-settings-layout](task-01-agent-settings-layout.md)
+
+Wave 2:
+
+- [x] [task-02-agent-settings-e2e](task-02-agent-settings-e2e.md)
+
+The tasks are sequential because the E2E selectors and layout assertions
+depend on the component markup from task 01.
+
+## Open Questions
+
+- None. The existing **Add TUI Agent** control is treated as the requested
+ agent-creation action; its semantics and translated label remain unchanged.
diff --git a/docs/plans/agent-settings-profile-layout/task-01-agent-settings-layout.md b/docs/plans/agent-settings-profile-layout/task-01-agent-settings-layout.md
new file mode 100644
index 0000000000..47b6006723
--- /dev/null
+++ b/docs/plans/agent-settings-profile-layout/task-01-agent-settings-layout.md
@@ -0,0 +1,74 @@
+---
+id: "01-agent-settings-layout"
+title: "Agent settings layout"
+status: done
+wave: 1
+depends_on: []
+plan: "plan.md"
+spec: "../../specs/agents/settings-profile-layout.md"
+---
+
+# Task 01: Agent settings layout
+
+## Acceptance
+
+- Configured agent cards show the existing `New profile` link in their header;
+ the profile body has no count or separate creation/empty-state row.
+- Unconfigured agent cards show only the existing `Setup profile` header action,
+ while saved profile rows and their duplicate/delete behavior remain intact.
+- The installed-agent toolbar places refresh/rescan immediately before the
+ rightmost Add TUI Agent action, keeps terminal available, and preserves
+ touch-safe targets on mobile.
+
+## Verification
+
+```bash
+cd apps && pnpm install --frozen-lockfile
+cd apps/web && pnpm test -- components/settings/agents/agent-profiles-section.test.tsx components/settings/installed-agent-card.test.tsx
+cd apps/web && pnpm run typecheck
+cd apps/web && pnpm exec eslint app/settings/agents/page.tsx components/settings/agents/agent-profiles-section.tsx components/settings/installed-agent-card.tsx
+cd apps/web && pnpm run i18n:check
+```
+
+The focused card test covers both unconfigured setup-link branches, while the
+existing profile-section test keeps the profile-row regression coverage.
+
+## Files likely touched
+
+- `apps/web/app/settings/agents/page.tsx`
+- `apps/web/components/settings/agents/agent-profiles-section.tsx`
+- `apps/web/components/settings/agents/agent-profiles-section.test.tsx`
+- `apps/web/components/settings/installed-agent-card.tsx`
+- `apps/web/components/settings/installed-agent-card.test.tsx`
+
+## Dependencies
+
+None.
+
+## Parallelism
+
+Sequential. The component markup, selector IDs, and unit assertions are one
+small vertical slice.
+
+## Inputs
+
+- Spec: `docs/specs/agents/settings-profile-layout.md`, especially What and
+ the first four scenarios.
+- Plan: `plan.md`, Frontend and Mobile design contract sections.
+- Existing patterns: `InstalledAgentCard`, `AgentProfilesSubList`, and the
+ current `agent-profiles-section.test.tsx` store read-after-write tests.
+
+## Output contract
+
+Report the final changed files, exact unit/typecheck/lint/i18n commands and
+results, any selector or responsive decisions, blockers, risks, and synchronized
+task/plan status in the same conversation.
+
+## Results
+
+- `cd apps && pnpm install --frozen-lockfile` passed.
+- `cd apps/web && pnpm test -- components/settings/agents/agent-profiles-section.test.tsx components/settings/installed-agent-card.test.tsx` passed (2 files, 8 tests), including both setup-link routes and both empty-body guards.
+- `cd apps/web && pnpm run typecheck` passed.
+- `cd apps/web && pnpm exec eslint app/settings/agents/page.tsx components/settings/agents/agent-profiles-section.tsx components/settings/installed-agent-card.tsx` passed.
+- `cd apps/web && pnpm run i18n:check` passed. It reported the repository's existing advisory catalog parity/unreferenced-key warnings; this change adds no locale keys.
+- Review follow-up added setup-link coverage, the saved-empty profile-body guard, and removed a redundant toolbar-count assertion in the desktop E2E test.
diff --git a/docs/plans/agent-settings-profile-layout/task-02-agent-settings-e2e.md b/docs/plans/agent-settings-profile-layout/task-02-agent-settings-e2e.md
new file mode 100644
index 0000000000..7e0764e34f
--- /dev/null
+++ b/docs/plans/agent-settings-profile-layout/task-02-agent-settings-e2e.md
@@ -0,0 +1,65 @@
+---
+id: "02-agent-settings-e2e"
+title: "Agent settings E2E coverage"
+status: done
+wave: 2
+depends_on: ["01-agent-settings-layout"]
+plan: "plan.md"
+spec: "../../specs/agents/settings-profile-layout.md"
+---
+
+# Task 02: Agent settings E2E coverage
+
+## Acceptance
+
+- The desktop E2E test proves the configured card has the `New profile` action
+ in its header, no profile-count/action row, and the installed-agent toolbar
+ order is terminal, refresh/rescan, then rightmost Add TUI Agent.
+- The mobile E2E test proves the same user value with the `mobile-chrome`
+ project, uses touch for the creation action, verifies a 44px-or-larger hitbox,
+ and detects no document-level horizontal overflow.
+
+## Verification
+
+```bash
+cd apps && pnpm install --frozen-lockfile
+cd apps/web && pnpm e2e:run --project chromium tests/settings/agent-profile-layout.spec.ts
+cd apps/web && pnpm e2e:run --project mobile-chrome tests/settings/mobile-agent-profile-layout.spec.ts
+```
+
+The managed runner must rebuild the production Vite assets before the tests.
+Do not use headed mode. If a focused test needs the exact fixture path or a
+different project selector, record the final command actually run below.
+
+## Files likely touched
+
+- `apps/web/e2e/tests/settings/agent-profile-layout.spec.ts`
+- `apps/web/e2e/tests/settings/mobile-agent-profile-layout.spec.ts`
+
+## Dependencies
+
+Task 01 must land first so the tests target final markup and selectors.
+
+## Parallelism
+
+Sequential. E2E selectors and layout assertions depend on task 01.
+
+## Inputs
+
+- Spec: `docs/specs/agents/settings-profile-layout.md`, all Scenarios.
+- Plan: `plan.md`, E2E Tests and Mobile design contract sections.
+- Existing patterns: `agent-profile-duplicate.spec.ts`,
+ `mobile-agent-profile-duplicate.spec.ts`, and
+ `mobile-agent-runtime-update.spec.ts`.
+
+## Output contract
+
+Report changed test files, exact managed-runner commands and test counts,
+screenshots or failure artifacts if produced, cleanup evidence, blockers, and
+synchronized task/plan status in the same conversation.
+
+## Results
+
+- `cd apps/web && pnpm e2e:run --project chromium tests/settings/agent-profile-layout.spec.ts` passed (2 tests).
+- `cd apps/web && pnpm e2e:run --project mobile-chrome tests/settings/mobile-agent-profile-layout.spec.ts` passed (1 test).
+- Both managed runs rebuilt the backend and pseudo-locale Vite assets and started from the runner's cleaned E2E artifact directories. No failure artifacts remain.
diff --git a/docs/specs/INDEX.md b/docs/specs/INDEX.md
index 9e9584f56d..c0ca1bd573 100644
--- a/docs/specs/INDEX.md
+++ b/docs/specs/INDEX.md
@@ -116,6 +116,7 @@ Roles, governance gates, and granular permissions that apply across human users
|---|---|
| [runtime-updates](agents/runtime-updates.md) | approved |
| [profile-disable](agents/profile-disable.md) | draft |
+| [settings-profile-layout](agents/settings-profile-layout.md) | shipped |
| [dynamic-provider-options](agents/dynamic-provider-options.md) | shipped |
| [utility-agent-profiles](agents/utility-agent-profiles.md) | approved |
| [roles](agents/roles.md) | shipped |
diff --git a/docs/specs/agents/settings-profile-layout.md b/docs/specs/agents/settings-profile-layout.md
new file mode 100644
index 0000000000..5aff57203d
--- /dev/null
+++ b/docs/specs/agents/settings-profile-layout.md
@@ -0,0 +1,72 @@
+---
+status: shipped
+created: 2026-08-13
+owner: kandev
+---
+
+# Simplify the agent settings profile layout
+
+## Why
+
+The agent settings page repeats the profile count and profile creation action
+in a separate sub-header inside every agent card. This adds a second hierarchy
+between the agent identity and its profiles and makes the primary action harder
+to associate with the agent it changes.
+
+## What
+
+- On `/settings/agents`, each configured agent card exposes its existing **New
+ profile** action in the card header, on the right with the other agent-level
+ actions. The action continues to open that agent's profile creation flow at
+ `/settings/agents/?mode=create`.
+- An agent without a saved profile keeps one **Setup profile** action in its
+ card header. It does not show a second profile-creation action in an empty
+ profile area.
+- The profile list body no longer renders a profile-count or empty-state
+ header row. When profiles exist, the saved profile rows appear directly in
+ the card body. When no profiles exist, the card header remains the only
+ setup entry point.
+- The installed-agent section keeps its existing terminal and refresh/rescan
+ actions. The refresh/rescan action appears immediately before the existing
+ agent-creation action, and the agent-creation action is the rightmost action
+ in that toolbar. The current agent-creation behavior and translated label
+ remain unchanged.
+- Existing profile rows, profile links, duplicate/delete actions, agent status
+ badges, authentication controls, and runtime-update controls keep their
+ current behavior.
+- The desktop and mobile layouts expose the same actions and outcomes. On a
+ narrow touch viewport, card-header actions remain visible, reachable with a
+ touch target of at least 44px, and do not create document-level horizontal
+ overflow. The settings content keeps its existing scroll owner.
+
+## Scenarios
+
+- **GIVEN** a configured agent with one or more profiles, **WHEN** the user
+ opens `/settings/agents`, **THEN** the agent card header shows **New
+ profile** on the right, the profile rows appear directly below it, and no
+ profile-count text or separate count/action row is rendered.
+- **GIVEN** an agent with no saved profile, **WHEN** the user opens
+ `/settings/agents`, **THEN** the card header shows **Setup profile**, no
+ profile-count or empty-state action row is rendered, and clicking the action
+ opens that agent's profile creation flow.
+- **GIVEN** the installed-agent section is visible, **WHEN** the user reads
+ its right-side toolbar, **THEN** refresh/rescan is immediately followed by
+ the agent-creation action, which is the rightmost action, while the terminal
+ action remains available.
+- **GIVEN** a configured agent card, **WHEN** the user clicks **New profile**,
+ **THEN** the browser navigates to that agent's profile creation route and
+ the existing profile rows remain unchanged.
+- **GIVEN** a phone viewport below 768px, **WHEN** the user opens the agents
+ settings page, **THEN** the card-header creation action and the section
+ toolbar actions remain visible and touch-reachable, the profile rows remain
+ vertically usable, and `document.documentElement.scrollWidth` does not
+ exceed `window.innerWidth`.
+
+## Out of scope
+
+- Changing the profile editor, profile-row actions, agent discovery, agent
+ installation, or the backend/API contracts.
+- Renaming the existing translated **New profile**, **Setup profile**, refresh,
+ or agent-creation labels.
+- Changing the profile count text used by unrelated task or picker surfaces.
+- Changing the settings sidebar hierarchy or the `/office/agents` surface.