diff --git a/ui/src/components/SidebarAgents.test.tsx b/ui/src/components/SidebarAgents.test.tsx
index ed4b28e783f..c75327fb767 100644
--- a/ui/src/components/SidebarAgents.test.tsx
+++ b/ui/src/components/SidebarAgents.test.tsx
@@ -238,4 +238,76 @@ describe("SidebarAgents", () => {
expect(mockAgentsApi.resume).toHaveBeenCalledWith("agent-1", "company-1");
expect(mockPushToast).toHaveBeenCalledWith(expect.objectContaining({ title: "Agent resumed" }));
});
+
+ it("only shows updating state for the agent currently being changed", async () => {
+ mockAgentsApi.list.mockResolvedValue([
+ makeAgent({ id: "agent-1", name: "Alpha", urlKey: "alpha" }),
+ makeAgent({ id: "agent-2", name: "Beta", urlKey: "beta" }),
+ ]);
+ mockAgentsApi.pause.mockImplementation(() => new Promise(() => {}));
+ const currentRoot = createRoot(container);
+ root = currentRoot;
+
+ await act(async () => {
+ currentRoot.render(
+
+
+ ,
+ );
+ });
+ await flushReact();
+ await openAgentMenu();
+
+ const pauseItem = Array.from(document.body.querySelectorAll('[data-slot="dropdown-menu-item"]'))
+ .find((element) => element.textContent?.includes("Pause agent"));
+ expect(pauseItem).toBeTruthy();
+
+ await act(async () => {
+ pauseItem?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ });
+ await flushReact();
+ await openAgentMenu("Open actions for Beta");
+
+ const betaPauseItem = Array.from(
+ document.body.querySelectorAll('[data-slot="dropdown-menu-item"]'),
+ )
+ .find((element) => element.textContent?.includes("Pause agent"));
+ expect(betaPauseItem).toBeTruthy();
+ expect(document.body.textContent).not.toContain("Updating...");
+ });
+
+ it("does not offer sidebar resume for budget-paused agents", async () => {
+ mockAgentsApi.list.mockResolvedValue([
+ makeAgent({
+ status: "paused",
+ pauseReason: "budget",
+ pausedAt: new Date("2026-01-02T00:00:00Z"),
+ }),
+ ]);
+ const currentRoot = createRoot(container);
+ root = currentRoot;
+
+ await act(async () => {
+ currentRoot.render(
+
+
+ ,
+ );
+ });
+ await flushReact();
+ await openAgentMenu();
+
+ const budgetPausedItem = Array.from(
+ document.body.querySelectorAll('[data-slot="dropdown-menu-item"]'),
+ )
+ .find((element) => element.textContent?.includes("Budget paused"));
+ expect(budgetPausedItem).toBeTruthy();
+
+ await act(async () => {
+ budgetPausedItem?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ });
+ await flushReact();
+
+ expect(mockAgentsApi.resume).not.toHaveBeenCalled();
+ });
});
diff --git a/ui/src/components/SidebarAgents.tsx b/ui/src/components/SidebarAgents.tsx
index e0aa98bb60b..e2efa56eb50 100644
--- a/ui/src/components/SidebarAgents.tsx
+++ b/ui/src/components/SidebarAgents.tsx
@@ -61,7 +61,14 @@ function SidebarAgentItem({
const editHref = `${agentUrl(agent)}/configuration`;
const isActive = activeAgentId === routeRef;
const isPaused = agent.status === "paused";
+ const isBudgetPaused = isPaused && agent.pauseReason === "budget";
const pauseResumeLabel = isPaused ? "Resume agent" : "Pause agent";
+ const pauseResumeDisabled = disabled || agent.status === "pending_approval" || isBudgetPaused;
+ const pauseResumeDisabledLabel = disabled
+ ? "Updating..."
+ : isBudgetPaused
+ ? "Budget paused"
+ : pauseResumeLabel;
return (
@@ -130,11 +137,15 @@ function SidebarAgentItem({
onPauseResume(agent, isPaused ? "resume" : "pause")}
- disabled={disabled || agent.status === "pending_approval"}
+ onClick={() => {
+ if (pauseResumeDisabled) return;
+ onPauseResume(agent, isPaused ? "resume" : "pause");
+ }}
+ disabled={pauseResumeDisabled}
+ title={isBudgetPaused ? "Agent was paused by budget limits" : undefined}
>
{isPaused ? : }
- {disabled ? "Updating..." : pauseResumeLabel}
+ {pauseResumeDisabledLabel}
@@ -144,6 +155,7 @@ function SidebarAgentItem({
export function SidebarAgents() {
const [open, setOpen] = useState(true);
+ const [pendingAgentIds, setPendingAgentIds] = useState
>(() => new Set());
const queryClient = useQueryClient();
const { selectedCompanyId } = useCompany();
const { openNewAgent } = useDialog();
@@ -198,6 +210,13 @@ export function SidebarAgents() {
action === "pause"
? agentsApi.pause(agent.id, selectedCompanyId ?? undefined)
: agentsApi.resume(agent.id, selectedCompanyId ?? undefined),
+ onMutate: ({ agent }) => {
+ setPendingAgentIds((current) => {
+ const next = new Set(current);
+ next.add(agent.id);
+ return next;
+ });
+ },
onSuccess: async (_agent, { agent, action }) => {
if (selectedCompanyId) {
await Promise.all([
@@ -223,6 +242,13 @@ export function SidebarAgents() {
tone: "error",
});
},
+ onSettled: (_data, _error, { agent }) => {
+ setPendingAgentIds((current) => {
+ const next = new Set(current);
+ next.delete(agent.id);
+ return next;
+ });
+ },
});
return (
@@ -263,7 +289,7 @@ export function SidebarAgents() {
activeAgentId={activeAgentId}
activeTab={activeTab}
agent={agent}
- disabled={pauseResumeAgent.isPending}
+ disabled={pendingAgentIds.has(agent.id)}
isMobile={isMobile}
onPauseResume={(targetAgent, action) => pauseResumeAgent.mutate({ agent: targetAgent, action })}
runCount={runCount}