feat(web): add Profiles tab to view agent profiles#222
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #222 +/- ##
=======================================
Coverage ? 92.77%
=======================================
Files ? 64
Lines ? 5188
Branches ? 0
=======================================
Hits ? 4813
Misses ? 375
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@patrick-muller Thanks for raising the PR. Clean, focused UI addition. Reuses the existing Blocking / should-fix1. No unit tests for the new component (P1)The web project already has vitest + Worth adding (following the existing pattern):
Low cost, high value — matches the bar the repo already holds. 2. Keyboard shortcut numbering shifts silently (P1)
The handler in Options:
Small call — pick one and make it deliberate. Polish3. Expanded row duplicates the name (P3)
4. Source-badge colors are binary (P3)
Could reuse the canonical Summary
No P0s. Happy to see the feature — (1) is the only one I'd consider a should-fix; the rest are polish. |
- Add vitest tests for ProfilesPanel in components.test.tsx - Remove redundant Name/Source from expanded row (keep Description only) - Use per-source badge colors via SOURCE_BADGE map - Align with project conventions: useStore for errors, reusable fetchProfiles
42f8fea to
20ac341
Compare
- Add vitest tests for ProfilesPanel in components.test.tsx - Remove redundant Name/Source from expanded row (keep Description only) - Use per-source badge colors via SOURCE_BADGE map - Align with project conventions: useStore for errors, reusable fetchProfiles
20ac341 to
c39644b
Compare
|
Thanks for the follow-up, @patrick-muller. Confirming what I see in Addressed ✅
Still open ❌(P1) Keyboard shortcut reshuffleProfiles is still inserted at position 3 ( Could you either:
Pick either — just want it deliberate. (P3) Binary badge colorsThe If you'd like to close this for real, keying the map by source and reusing Summary
2 of 4 resolved. Only No. 2 is a should-fix before merge; No. 4 is polish. |
|
@patrick-muller would you like to follow up with the fixes before we merge the PR ? many thanks. |
gutosantos82
left a comment
There was a problem hiding this comment.
PR Review: #222 — feat(web): add Profiles tab to view agent profiles
Summary
Adds a read-only Profiles tab to the Web UI (new ProfilesPanel.tsx + App.tsx nav wiring) that lists agent profiles from the existing GET /agents/profiles endpoint, with expandable rows and source badges. The change is frontend-only, follows sibling-panel idioms closely, and ships 6 vitest cases. One real defect keeps this from a clean approval: the AIM filter dereferences p.description without a null guard, so a single profile with an empty/null description throws and hides every profile. Fix that one line (plus a regression test) and this is a solid merge.
Blocking (must fix before merge)
None. (The null-description issue below is a genuine functional bug but is a 1-line frontend fix, not a security/data-integrity blocker — classified Important.)
Important (should fix)
- [correctness/tests/security] web/src/components/ProfilesPanel.tsx:20 —
all.filter(p => !p.description.includes('managed by AIM'))assumesp.descriptionis always a string. The backend (utils/agent_profiles.py,data.metadata.get("description", "")) only defaults to""when the frontmatter key is absent; a present-but-emptydescription:line parses to YAMLNone→ the API emits"description": null. Calling.includes()onnullthrowsTypeError, which the surroundingtry/catchswallows into the "Failed to load profiles" snackbar andsetProfiles([]). Net effect: one profile with an empty description hides the entire list. The expanded view already guards this exact case ({p.description || '—'}at line 79), so the filter is inconsistently unguarded. Fix:p => !(p.description ?? '').includes('managed by AIM'). All three of the security, correctness, and tests reviewers independently flagged this path. - [tests] web/src/test/components.test.tsx — The error/
catchbranch (ProfilesPanel.tsx:21-24:showSnackbar+setProfiles([])) has zero coverage — no test rejectslistProfiles. Add amockRejectedValuetest asserting the empty state still renders on failure. Also add a regression test for anull/empty-description profile (would currently crash per the finding above), and — optionally — an App-level test thatAlt+3selects the Profiles tab, since the tab wiring and shortcut are untested.
Nits (optional)
- [conventions] web/src/components/ProfilesPanel.tsx:20 — The AIM-hide rule is a magic substring baked into the UI. Extract to a named const (
AIM_MANAGED_MARKER) or, better, filter server-side. Note the drift risk:AgentPanel/FlowsPanellist profiles without this filter, so the Profiles tab now shows a different set than the spawn dropdowns. - [security] web/src/components/ProfilesPanel.tsx:20 — The AIM filter is display-only, not a security boundary:
GET /agents/profilesstill returns AIM profiles to any caller, and any AIM profile whose description lacks the exact"managed by AIM"substring will still render. Fine if AIM profiles aren't sensitive; otherwise filter server-side. - [correctness] web/src/App.tsx:62 — Stale comment: says "Keyboard shortcuts: Alt+1-4" but this PR makes
TABS.length5, so the handler now binds Alt+1–5. Logic is correct (data-driven); update the comment to Alt+1-5.
Prior feedback (already raised — not restating)
- ↩︎ Source-badge colors are binary (built-in blue, all else green); reuse the richer
SOURCE_LABELSmap fromAgentPanel.tsx:15for per-source colors — already raised by @haofeif; we concur.
Tests
The 6 added ProfilesPanel tests (loading, empty+CLI hint, render, single-open expand/collapse, badge colors, AIM filter) are well-formed, use the repo's vi.mock('../api') + waitFor idiom, and the mock shape {name, description, source} exactly matches AgentProfileInfo — they will pass. Coverage gaps on changed behavior: the error/catch branch and the null-description filter path (a latent crash) are untested, and there is no App-level tab/Alt+3 wiring test.
Verified correct (not findings)
Alt+3is wired up —App.tsxkeydown handler is data-driven offTABS.length, andAlt+3 → TABS[2] = 'profiles'. The PR-body claim holds./agents/profiles+AgentProfileInfogenuinely providename/description/source; no backend change needed as claimed.- Expand/collapse keyed on
p.nameis safe (backend dedupes profiles by name). - React auto-escapes all rendered profile strings; no
dangerouslySetInnerHTMLor injection sink. No security issues. - Inclusive-language clean; component/test/tab layout mirrors existing panels; no ESLint config exists so the empty-dep
useEffectmatches every sibling; CHANGELOG is git-cliff-generated so no manual entry needed.
Verdict
Request changes — fix the unguarded p.description null deref (1 line) and add the error-path/null-description regression tests. Everything else is nits. Once the filter is guarded this is an Approve.
Consistency review did not return before synthesis; its key checks (Alt+3 wiring, badge logic, PR-body claims, missing test-file entry in the "Changes" list) were independently covered by the correctness and conventions reviewers above.
Summary
Adds a new Profiles tab to the Web UI, allowing users to browse installed agent profiles directly from the dashboard.
Problem
The Web UI shows a profile count on the Home page but provides no way to view the actual list of profiles or their details. Users have to use the CLI (
cao install) to discover what profiles are available.Solution
Added a new Profiles tab between Agents and Flows with:
built-in(blue) andinstalled/kiro(green) profilesAlt+3to switch to the tabUses the existing
/agents/profilesAPI endpoint — no backend changes required.Changes
web/src/components/ProfilesPanel.tsx— New component for the Profiles tabweb/src/App.tsx— Added Profiles tab to navigation with Package iconTesting