From 07f3a16acb95a1badcae24a1a4b3c722aabc79e5 Mon Sep 17 00:00:00 2001 From: Logan Johnson Date: Mon, 31 Aug 2026 18:47:37 -0400 Subject: [PATCH] fix(desktop): guard stopped agent startup with authored presence Show exact-key Online/Away independently of local lifecycle. Replace card Start with presence and guard profile/member/callback startup without inventing Stop authority. Cover rendered controls, keyboard activation and presence transitions. Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> Signed-off-by: Logan Johnson --- desktop/src/features/agents/AGENTS.md | 5 +- .../agents/lib/useAgentAvailability.test.mjs | 28 ++- .../agents/lib/useAgentAvailability.ts | 13 ++ .../agents/ui/AgentRuntimeAvatarControl.tsx | 12 +- .../UnifiedAgentsSectionCardTarget.test.mjs | 86 ++++++++++ .../agents/ui/useManagedAgentActions.ts | 20 +++ .../channels/ui/MembersSidebarMemberCard.tsx | 19 ++- .../channels/ui/useMembersSidebarActions.ts | 28 +++ .../profile/ui/UserProfilePanelSections.tsx | 14 +- .../profile/ui/UserProfilePrimaryActions.tsx | 8 +- .../profile/ui/useAgentLifecycleActions.ts | 15 +- desktop/tests/e2e/agent-availability.spec.ts | 159 ++++++++++++++++++ docs/agent-availability.md | 21 ++- 13 files changed, 417 insertions(+), 11 deletions(-) diff --git a/desktop/src/features/agents/AGENTS.md b/desktop/src/features/agents/AGENTS.md index f803ec5113..832908bf7f 100644 --- a/desktop/src/features/agents/AGENTS.md +++ b/desktop/src/features/agents/AGENTS.md @@ -207,7 +207,10 @@ with a TypeScript lookup table or an id comparison in a component. [the identity contract](../../../../docs/agent-profile-identity.md). Availability dots read relay presence, never a saved deployment receipt or runtime status. Failed/disconnected reads are unknown; lifecycle - actions retain their separate routing. See + actions retain their separate routing. Current exact-key Online/Away presence + suppresses Start for an inactive local record without granting Stop authority; + list/profile/member startup guards must not interpret Offline as proof of safe + startup. See [the availability contract](../../../../docs/agent-availability.md). 14. **Thinking effort has two surfaces: a local-only WRITE control and a read-only two-facts DISPLAY.** The write control is `EffortPickerField` diff --git a/desktop/src/features/agents/lib/useAgentAvailability.test.mjs b/desktop/src/features/agents/lib/useAgentAvailability.test.mjs index 6bf1b25959..f9b2df050a 100644 --- a/desktop/src/features/agents/lib/useAgentAvailability.test.mjs +++ b/desktop/src/features/agents/lib/useAgentAvailability.test.mjs @@ -89,7 +89,31 @@ for (const lifecycle of ["running", "stopped"]) { onStart() {}, }), ); - assert.equal(html.includes('data-testid="start"'), !isActive); - assert.equal(html.includes('data-testid="active"'), isActive); + assert.equal(html.includes('data-testid="start"'), false); + assert.equal(html.includes('data-testid="active"'), true); + }); +} + +for (const availability of ["online", "away"]) { + test(`stale restart and runtime error cannot hide stopped ${availability} presence`, () => { + const html = renderToStaticMarkup( + createElement(AgentRuntimeAvatarControl, { + activeTestId: "active", + startTestId: "start", + errorTestId: "error", + isActive: false, + isStarting: false, + requiresRestart: true, + errorLabel: "Previous startup failed", + availability, + label: "Agent", + onStart() {}, + }), + ); + assert.match(html, /data-testid="active"/); + assert.doesNotMatch( + html, + /data-testid="start"|data-testid="error"|