Skip to content

Workspace: the rail's Info body renders a failed agent-page fetch as an empty agent #2597

Closed

Description

@AndriiPasternak31

Found during the /review pass on the ent#547 + #2580 branch. Pre-existing (it was true of the Agent-details panel too), but ent#547 widens the exposure, so it is filed rather than fixed in that PR.

What happens

src/frontend/src/components/portal/PortalAgentDetails.vue:207 destructures only what it renders:

const { header, capabilities } = usePortalAgentPage(toRef(props, 'agentName'), timeWindow)

usePortalAgentPage also returns error and loaded, and neither is read. The component's only failure surfaces (LoadFailed, InlineError) belong to the Reports section, which is a different fetch.

So when GET /api/enterprise/client-portal/agents/{name}/page fails, the Info body renders a plausible-looking agent instead of saying so:

  • identity falls back to the slug, description blank;
  • "This agent hasn't published anything it can do yet" — a positive statement the client has no evidence for;
  • "Your chats" still works (it is a slice of the shell's thread list, not this fetch), which makes the rest read as loaded.

Why ent#547 widens it

  1. It was one panel about the active agent, whose band sat right beside it and does render InlineError for the same failed fetch. The band is now a separate mount with its own load(), so it can succeed while a body fails.
  2. PortalRailInfo renders one body per participant in a room. Each issues its own /page call, so N independent chances to fail silently — including for a participant that is in the room but off the viewer's roster, where _require_roster returns a uniform 404 by design.

Suggested fix

Read error (and loaded) in PortalAgentDetails and render the existing LoadFailed / InlineError primitive with the composable's reload, per the ent#253 rule the band already follows: a failed refresh keeps the data it has and says so beside it; a failed first load says it failed rather than rendering an empty agent. Gate on the loaded verdict, not on loading (#2540/#1927 — and the loadingGateRatchet counts bare loading gates).

Not a security issue

The 404 discloses nothing: the participant name is already visible in the room header, and client_portal/router.py::_require_roster is access-first with a uniform 404. This is honest-status only.

Refs: ent#547, docs/memory/feature-flows/workspace-rail.md (Slice 3)

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions