thub.com
From e54de1c75176a28995ed9ffdaee477202ec36a14 Mon Sep 17 00:00:00 2001
From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com>
Date: Thu, 17 Jul 2025 14:32:59 +0000
Subject: [PATCH 01/12] initial implementation
---
.../LicensesSettingsPage.tsx | 1 +
.../LicensesSettingsPageView.tsx | 20 +-
.../ManagedAgentsConsumption.stories.tsx | 52 +++++
.../ManagedAgentsConsumption.tsx | 210 ++++++++++++++++++
4 files changed, 282 insertions(+), 1 deletion(-)
create mode 100644 site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx
create mode 100644 site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
diff --git a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/LicensesSettingsPage.tsx b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/LicensesSettingsPage.tsx
index 5f617412a0c04..535a08b8228c9 100644
--- a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/LicensesSettingsPage.tsx
+++ b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/LicensesSettingsPage.tsx
@@ -85,6 +85,7 @@ const LicensesSettingsPage: FC = () => {
isRemovingLicense={isRemovingLicense}
removeLicense={(licenseId: number) => removeLicenseApi(licenseId)}
activeUsers={userStatusCount?.active}
+ entitlements={entitlementsQuery.data}
refreshEntitlements={async () => {
try {
await refreshEntitlementsMutation.mutateAsync();
diff --git a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/LicensesSettingsPageView.tsx b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/LicensesSettingsPageView.tsx
index eb60361883b72..95655025f5a00 100644
--- a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/LicensesSettingsPageView.tsx
+++ b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/LicensesSettingsPageView.tsx
@@ -4,7 +4,7 @@ import MuiLink from "@mui/material/Link";
import Skeleton from "@mui/material/Skeleton";
import Tooltip from "@mui/material/Tooltip";
import type { GetLicensesResponse } from "api/api";
-import type { UserStatusChangeCount } from "api/typesGenerated";
+import type { Entitlements, UserStatusChangeCount } from "api/typesGenerated";
import { Button } from "components/Button/Button";
import {
SettingsHeader,
@@ -20,6 +20,7 @@ import Confetti from "react-confetti";
import { Link } from "react-router-dom";
import { LicenseCard } from "./LicenseCard";
import { LicenseSeatConsumptionChart } from "./LicenseSeatConsumptionChart";
+import { ManagedAgentsConsumption } from "./ManagedAgentsConsumption";
type Props = {
showConfetti: boolean;
@@ -32,6 +33,7 @@ type Props = {
removeLicense: (licenseId: number) => void;
refreshEntitlements: () => void;
activeUsers: UserStatusChangeCount[] | undefined;
+ entitlements?: Entitlements;
};
const LicensesSettingsPageView: FC = ({
@@ -45,9 +47,14 @@ const LicensesSettingsPageView: FC = ({
removeLicense,
refreshEntitlements,
activeUsers,
+ entitlements,
}) => {
const theme = useTheme();
const { width, height } = useWindowSize();
+ const managedAgentFeature = entitlements?.features?.managed_agent_limit;
+ const managedAgentLimitStarts = entitlements?.features?.managed_agent_limit?.usage_period?.start;
+ const managedAgentLimitExpires = entitlements?.features?.managed_agent_limit?.usage_period?.end;
+ const managedAgentFeatureEnabled = entitlements?.features?.managed_agent_limit?.enabled;
return (
<>
@@ -151,6 +158,17 @@ const LicensesSettingsPageView: FC = ({
}))}
/>
)}
+
+ {licenses && licenses.length > 0 && managedAgentFeature && (
+
+ )}
>
);
diff --git a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx
new file mode 100644
index 0000000000000..fbe9713d03e5d
--- /dev/null
+++ b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx
@@ -0,0 +1,52 @@
+import type { Meta, StoryObj } from "@storybook/react";
+import { ManagedAgentsConsumption } from "./ManagedAgentsConsumption";
+
+const meta: Meta = {
+ title:
+ "pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption",
+ component: ManagedAgentsConsumption,
+ args: {
+ usage: 50000,
+ included: 60000,
+ limit: 120000,
+ startDate: "February 27, 2025",
+ endDate: "February 27, 2026",
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {};
+
+export const NearLimit: Story = {
+ args: {
+ usage: 115000,
+ included: 60000,
+ limit: 120000,
+ },
+};
+
+export const OverIncluded: Story = {
+ args: {
+ usage: 80000,
+ included: 60000,
+ limit: 120000,
+ },
+};
+
+export const LowUsage: Story = {
+ args: {
+ usage: 25000,
+ included: 60000,
+ limit: 120000,
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ usage: NaN,
+ included: NaN,
+ limit: NaN,
+ },
+};
diff --git a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
new file mode 100644
index 0000000000000..ff76c029850df
--- /dev/null
+++ b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
@@ -0,0 +1,210 @@
+import { Button } from "components/Button/Button";
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "components/Collapsible/Collapsible";
+import { Link } from "components/Link/Link";
+import { Stack } from "components/Stack/Stack";
+import { ChevronRightIcon } from "lucide-react";
+import type { FC } from "react";
+import { Link as RouterLink } from "react-router-dom";
+import { docs } from "utils/docs";
+import MuiLink from "@mui/material/Link";
+import { type Interpolation, type Theme } from "@emotion/react";
+import dayjs from "dayjs";
+
+interface ManagedAgentsConsumptionProps {
+ usage: number;
+ included: number;
+ limit: number;
+ startDate: string;
+ endDate: string;
+ enabled?: boolean;
+}
+
+export const ManagedAgentsConsumption: FC = ({
+ usage,
+ included,
+ limit,
+ startDate,
+ endDate,
+ enabled = true,
+}) => {
+ // If feature is disabled, show disabled state
+ if (!enabled) {
+ return (
+
+
+
+
+ Managed AI Agent Feature Disabled
+
+
+ The managed AI agent feature is not included in your current license.
+ Contact{" "}
+ sales to
+ upgrade your license and unlock this feature.
+
+
+
+
+ Managed agents are counted based on active agent connections during the billing period.
+ Each unique agent that connects to your deployment consumes one managed agent seat.
+
+
+
+
+ Current usage represents active managed agents during this period.
+
+
+
+ Included allowance from your current license plan.
+
+
+
+
+
+ Total limit including any additional purchased capacity.
+
- Managed agents are counted based on active agent connections during the billing period.
- Each unique agent that connects to your deployment consumes one managed agent seat.
+ Managed agents are counted based on the amount of started workspaces with an AI agent.
@@ -97,7 +95,7 @@ export const ManagedAgentsConsumption: FC = ({
className="rounded-[2px] bg-highlight-green size-3 inline-block"
aria-label="Legend for current usage in the chart"
/>
- Current usage represents active managed agents during this period.
+ Amount of started workspaces with an AI agent.
= ({
>
- Total limit including any additional purchased capacity.
+ Total limit after which the feature will be disabled.
-
-
-
- Managed AI Agent Feature Disabled
-
-
- The managed AI agent feature is not included in your current license.
- Contact{" "}
- sales to
- upgrade your license and unlock this feature.
-
-
-
-
+
+ );
};
const styles = {
- disabledTitle: {
- fontSize: 16,
- },
-
- disabledRoot: (theme) => ({
- minHeight: 240,
- display: "flex",
- alignItems: "center",
- justifyContent: "center",
- borderRadius: 8,
- border: `1px solid ${theme.palette.divider}`,
- padding: 48,
- }),
-
- disabledDescription: (theme) => ({
- color: theme.palette.text.secondary,
- textAlign: "center",
- maxWidth: 464,
- marginTop: 8,
- }),
+ disabledTitle: {
+ fontSize: 16,
+ },
+
+ disabledRoot: (theme) => ({
+ minHeight: 240,
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+ borderRadius: 8,
+ border: `1px solid ${theme.palette.divider}`,
+ padding: 48,
+ }),
+
+ disabledDescription: (theme) => ({
+ color: theme.palette.text.secondary,
+ textAlign: "center",
+ maxWidth: 464,
+ marginTop: 8,
+ }),
} satisfies Record>;
From 9d58c44c74da48e0068fd9a1ffeb3ccb478c67dc Mon Sep 17 00:00:00 2001
From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com>
Date: Tue, 22 Jul 2025 13:54:03 +0000
Subject: [PATCH 05/12] layout fixes on small screen and PR review
---
.../ManagedAgentsConsumption.stories.tsx | 9 +++++
.../ManagedAgentsConsumption.tsx | 33 ++++++++++++++-----
2 files changed, 34 insertions(+), 8 deletions(-)
diff --git a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx
index 555e03a8e5b14..2621bfc48a6b6 100644
--- a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx
+++ b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx
@@ -43,8 +43,17 @@ export const LowUsage: Story = {
},
};
+export const IncludedAtLimit: Story = {
+ args: {
+ usage: 25000,
+ included: 30500,
+ limit: 30500,
+ },
+};
+
export const Disabled: Story = {
args: {
+ enabled: false,
usage: Number.NaN,
included: Number.NaN,
limit: Number.NaN,
diff --git a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
index de1346f9d6aab..e0bb6c5873b9a 100644
--- a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
+++ b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
@@ -34,11 +34,11 @@ export const ManagedAgentsConsumption: FC = ({
- Managed AI Agent Feature Disabled
+ Managed AI Agents Disabled
- The managed AI agent feature is not included in your current
- license. Contact{" "}
+ Managed AI agents are not included in your current license.
+ Contact{" "}
sales to upgrade
your license and unlock this feature.
@@ -70,7 +70,7 @@ export const ManagedAgentsConsumption: FC = ({
`}
>
- How we calculate managed agent consumption
+ How we calculate managed agents consumption
@@ -83,8 +83,8 @@ export const ManagedAgentsConsumption: FC = ({
`}
>
- Managed agents are counted based on the amount of started
- workspaces with an AI agent.
+ Managed agents are counted based on the amount of successfully
+ started workspaces with an AI agent.
- Total limit after which the feature will be disabled.
+ Total limit after which further AI workspace builds will be blocked.
@@ -138,7 +138,7 @@ export const ManagedAgentsConsumption: FC = ({
/>
-
- Managed AI Agents Disabled
-
+ Managed AI Agents Disabled
Managed AI agents are not included in your current license.
- Contact{" "}
- sales to upgrade
- your license and unlock this feature.
+ Contact sales to
+ upgrade your license and unlock this feature.
@@ -171,7 +164,8 @@ export const ManagedAgentsConsumption: FC = ({
>
- Total limit after which further AI workspace builds will be blocked.
+ Total limit after which further AI workspace builds will be
+ blocked.
From ba22be6486282b57eeb72e9b8fea5aeec977e4c5 Mon Sep 17 00:00:00 2001
From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com>
Date: Mon, 28 Jul 2025 11:41:15 +0000
Subject: [PATCH 09/12] reverted the orange/red based on usage
---
.../ManagedAgentsConsumption.stories.tsx | 65 +------------------
.../ManagedAgentsConsumption.tsx | 18 +----
2 files changed, 5 insertions(+), 78 deletions(-)
diff --git a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx
index 8b5839d66ea7a..8b526914edd50 100644
--- a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx
+++ b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.stories.tsx
@@ -24,70 +24,13 @@ const meta: Meta = {
export default meta;
type Story = StoryObj;
-export const Default: Story = {
- name: "Normal Usage (42% of limit)",
-};
-
-export const Warning: Story = {
- name: "Warning (80-99% of limit)",
- args: {
- managedAgentFeature: {
- enabled: true,
- actual: 96000, // 80% of limit - should show orange
- soft_limit: 60000,
- limit: 120000,
- usage_period: {
- start: "February 27, 2025",
- end: "February 27, 2026",
- issued_at: "February 27, 2025",
- },
- entitlement: "entitled",
- },
- },
-};
+export const Default: Story = {};
export const NearLimit: Story = {
- name: "Near Limit (95% of limit)",
- args: {
- managedAgentFeature: {
- enabled: true,
- actual: 114000, // 95% of limit - should show orange
- soft_limit: 60000,
- limit: 120000,
- usage_period: {
- start: "February 27, 2025",
- end: "February 27, 2026",
- issued_at: "February 27, 2025",
- },
- entitlement: "entitled",
- },
- },
-};
-
-export const AtLimit: Story = {
- name: "At Limit (100% of limit)",
- args: {
- managedAgentFeature: {
- enabled: true,
- actual: 120000, // 100% of limit - should show red
- soft_limit: 60000,
- limit: 120000,
- usage_period: {
- start: "February 27, 2025",
- end: "February 27, 2026",
- issued_at: "February 27, 2025",
- },
- entitlement: "entitled",
- },
- },
-};
-
-export const OverLimit: Story = {
- name: "Over Limit (120% of limit)",
args: {
managedAgentFeature: {
enabled: true,
- actual: 144000, // 120% of limit - should show red
+ actual: 115000,
soft_limit: 60000,
limit: 120000,
usage_period: {
@@ -101,11 +44,10 @@ export const OverLimit: Story = {
};
export const OverIncluded: Story = {
- name: "Over Included (67% of limit)",
args: {
managedAgentFeature: {
enabled: true,
- actual: 80000, // Over included but under 80% of total limit - should still be green
+ actual: 80000,
soft_limit: 60000,
limit: 120000,
usage_period: {
@@ -119,7 +61,6 @@ export const OverIncluded: Story = {
};
export const LowUsage: Story = {
- name: "Low Usage (21% of limit)",
args: {
managedAgentFeature: {
enabled: true,
diff --git a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
index 8150adaf5c2f8..c2f948f8dee75 100644
--- a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
+++ b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
@@ -94,20 +94,6 @@ export const ManagedAgentsConsumption: FC = ({
const includedPercentage = Math.min((included / limit) * 100, 100);
const remainingPercentage = Math.max(100 - includedPercentage, 0);
- // Determine usage bar color based on percentage
- const getUsageColor = () => {
- const actualUsagePercent = (usage / limit) * 100;
- if (actualUsagePercent >= 100) {
- return "bg-highlight-red"; // Critical: at or over limit
- }
- if (actualUsagePercent >= 80) {
- return "bg-surface-orange"; // Warning: approaching limit
- }
- return "bg-highlight-green"; // Normal: safe usage
- };
-
- const usageBarColor = getUsageColor();
-
return (
@@ -145,7 +131,7 @@ export const ManagedAgentsConsumption: FC = ({
Amount of started workspaces with an AI agent.
@@ -182,7 +168,7 @@ export const ManagedAgentsConsumption: FC = ({
From f33f393e3196fa097eb54784001962c77fd19d5b Mon Sep 17 00:00:00 2001
From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com>
Date: Mon, 28 Jul 2025 12:16:40 +0000
Subject: [PATCH 10/12] reworded docs
---
.../ManagedAgentsConsumption.tsx | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
index c2f948f8dee75..24e6684ece19c 100644
--- a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
+++ b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
@@ -9,8 +9,9 @@ import {
} from "components/Collapsible/Collapsible";
import { Stack } from "components/Stack/Stack";
import dayjs from "dayjs";
-import { ChevronRightIcon } from "lucide-react";
+import { ChevronRightIcon, Link } from "lucide-react";
import type { FC } from "react";
+import { docs } from "utils/docs";
interface ManagedAgentsConsumptionProps {
managedAgentFeature?: Feature;
@@ -100,7 +101,7 @@ export const ManagedAgentsConsumption: FC = ({
- Managed agents consumption
+ Managed AI Agents Usage
@@ -112,7 +113,7 @@ export const ManagedAgentsConsumption: FC = ({
`}
>
- How we calculate managed agents consumption
+ Learn more
@@ -125,8 +126,13 @@ export const ManagedAgentsConsumption: FC = ({
`}
>
- Managed agents are counted based on the amount of successfully
- started workspaces with an AI agent.
+
+ Coder Tasks
+ {" "} and upcoming managed AI features are included in Coder Premium
+ licenses during beta. Usage limits and pricing subject to change.
From d65392b1850062ae40ef81f986df4edad7cc7692 Mon Sep 17 00:00:00 2001
From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com>
Date: Mon, 28 Jul 2025 16:32:43 +0000
Subject: [PATCH 11/12] fmt lint
---
.../ManagedAgentsConsumption.tsx | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
index 24e6684ece19c..033af8cf93fc5 100644
--- a/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
+++ b/site/src/pages/DeploymentSettingsPage/LicensesSettingsPage/ManagedAgentsConsumption.tsx
@@ -9,7 +9,7 @@ import {
} from "components/Collapsible/Collapsible";
import { Stack } from "components/Stack/Stack";
import dayjs from "dayjs";
-import { ChevronRightIcon, Link } from "lucide-react";
+import { ChevronRightIcon } from "lucide-react";
import type { FC } from "react";
import { docs } from "utils/docs";
@@ -100,9 +100,7 @@ export const ManagedAgentsConsumption: FC = ({