feat(frontend): session and agent surfaces move into the packages - #5869
feat(frontend): session and agent surfaces move into the packages#5869ardaerzin wants to merge 6 commits into
Conversation
|
@coderabbitai review |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✅ Action performedReview finished.
|
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe PR updates agent onboarding and template selection, adds shared agent and session UI components, and integrates them into OSS pages. It also centralizes session-open state, configuration summaries, list rendering, filtering, pagination, and related package exports. ChangesAgent onboarding and template flow
Shared agent surfaces
Shared session surfaces
Small maintenance updates
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant TemplateGallery
participant TemplateDetail
participant StripHome
participant StripComposer
TemplateGallery->>TemplateDetail: Navigate with selected template
TemplateDetail->>StripHome: Provide template selection
StripHome->>StripComposer: Seed composer through provenance
sequenceDiagram
participant SessionsPage
participant SessionFiltersPanel
participant SessionsListView
participant SessionCardList
SessionsPage->>SessionFiltersPanel: Provide filters and agent options
SessionsPage->>SessionsListView: Provide list scope and actions
SessionsListView->>SessionCardList: Render grouped session rows
SessionCardList-->>SessionsPage: Invoke row and menu callbacks
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 17
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/packages/agenta-sessions-ui/src/index.ts (1)
33-40: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winExport
SessionStatusListControlfrom the package entry point.The new reusable control is not available through
@agenta/sessions-ui. Export it with the other filter controls so consumers do not import an internal module path.Proposed fix
export { SessionSearchControl, SessionStatusControl, + SessionStatusListControl, SessionModeControl, SessionArchivedControl, } from "./controls/SessionFilterControls"
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 848c02a8-e65f-438f-ad30-e35fcf246392
📒 Files selected for processing (42)
web/oss/src/components/TemplateStrip/index.tsxweb/oss/src/components/pages/agent-home/PlaygroundOnboarding/useAgentOnboarding.tsweb/oss/src/components/pages/agent-home/StripHome.tsxweb/oss/src/components/pages/agent-home/assets/templates.tsweb/oss/src/components/pages/agent-home/components/HomeTaskComposer.tsxweb/oss/src/components/pages/agent-home/components/TemplateDetail/index.tsxweb/oss/src/components/pages/agent-home/components/TemplatesGallery/index.tsxweb/oss/src/components/pages/agent-home/components/TemplatesSection/index.tsxweb/oss/src/components/pages/agent-home/components/YourAgentsTable/AgentRow.tsxweb/oss/src/components/pages/agent-home/components/YourAgentsTable/useAgentActivity.tsweb/oss/src/components/pages/agents/AgentsGrid.tsxweb/oss/src/components/pages/overview/agent/AgentConfigurationCard.tsxweb/oss/src/components/pages/overview/agent/AgentOverview.tsxweb/oss/src/components/pages/sessions/SessionsPage.tsxweb/oss/src/components/pages/sessions/components/SessionListCard.tsxweb/oss/src/components/pages/settings/Triggers/components/GatewaySchedulesSection.tsxweb/packages/agenta-entity-ui/package.jsonweb/packages/agenta-entity-ui/src/agent/AgentCard.tsxweb/packages/agenta-entity-ui/src/agent/AgentCardGrid.tsxweb/packages/agenta-entity-ui/src/agent/AgentConfigSummaryCard.tsxweb/packages/agenta-entity-ui/src/agent/NextTriggersSection.tsxweb/packages/agenta-entity-ui/src/agent/agentConfigSummary.tsweb/packages/agenta-entity-ui/src/agent/index.tsweb/packages/agenta-entity-ui/src/agent/state.tsweb/packages/agenta-entity-ui/tests/unit/agentConfigSummary.test.tsweb/packages/agenta-sessions-ui/package.jsonweb/packages/agenta-sessions-ui/src/SessionCardList.tsxweb/packages/agenta-sessions-ui/src/SessionFiltersPanel.tsxweb/packages/agenta-sessions-ui/src/SessionRow.tsxweb/packages/agenta-sessions-ui/src/SessionsListView.tsxweb/packages/agenta-sessions-ui/src/assets/motion.tsweb/packages/agenta-sessions-ui/src/controls/SessionFilterControls.tsxweb/packages/agenta-sessions-ui/src/index.tsweb/packages/agenta-sessions/src/row/index.tsweb/packages/agenta-sessions/src/row/sessionOpenTarget.tsweb/packages/agenta-sessions/src/row/viewModel.tsweb/packages/agenta-sessions/src/state/index.tsweb/packages/agenta-sessions/src/state/pendingSessionOpen.tsweb/packages/agenta-sessions/src/state/useSessionCardList.tsweb/packages/agenta-sessions/src/state/useSessionList.tsweb/packages/agenta-sessions/test-results/junit.xmlweb/packages/agenta-sessions/tests/unit/sessionOpenTarget.test.ts
| // Default to the most recently touched agent — the one you're most likely to want next. | ||
| const effectiveAgentId = agentId ?? agents[0]?.workflowId ?? null |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Validate the selected agent against the current roster.
If the selected agent is deleted or becomes inaccessible, effectiveAgentId remains stale. The composer then enables send and passes an invalid appId to startSession.
Proposed fix
- const effectiveAgentId = agentId ?? agents[0]?.workflowId ?? null
+ const effectiveAgentId =
+ agents.some((agent) => agent.workflowId === agentId)
+ ? agentId
+ : agents[0]?.workflowId ?? null📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Default to the most recently touched agent — the one you're most likely to want next. | |
| const effectiveAgentId = agentId ?? agents[0]?.workflowId ?? null | |
| // Default to the most recently touched agent — the one you're most likely to want next. | |
| const effectiveAgentId = | |
| agents.some((agent) => agent.workflowId === agentId) | |
| ? agentId | |
| : agents[0]?.workflowId ?? null |
| const handleSelectTemplate = useCallback( | ||
| (template: AgentTemplate) => void createFromTemplate(template), | ||
| [createFromTemplate], | ||
| (template: AgentTemplate) => | ||
| void router.push(`${baseAppURL}/agent-templates/${template.key}`), | ||
| [router, baseAppURL], |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
Define baseAppURL before constructing the template route.
baseAppURL is not declared in this module. TypeScript cannot compile this callback.
Proposed fix
+import useURL from "`@/oss/hooks/useURL`"
+
const TemplatesGalleryPage = () => {
const router = useRouter()
+ const {baseAppURL} = useURL()
const {message} = App.useApp()📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleSelectTemplate = useCallback( | |
| (template: AgentTemplate) => void createFromTemplate(template), | |
| [createFromTemplate], | |
| (template: AgentTemplate) => | |
| void router.push(`${baseAppURL}/agent-templates/${template.key}`), | |
| [router, baseAppURL], | |
| import useURL from "`@/oss/hooks/useURL`" | |
| const TemplatesGalleryPage = () => { | |
| const router = useRouter() | |
| const {baseAppURL} = useURL() | |
| const {message} = App.useApp() | |
| const handleSelectTemplate = useCallback( | |
| (template: AgentTemplate) => | |
| void router.push(`${baseAppURL}/agent-templates/${template.key}`), | |
| [router, baseAppURL], |
| event.preventDefault() | ||
| actions.onOpenPlayground(record) | ||
| } | ||
| if (event.key === "Enter" || event.key === " ") actions.onOpenPlayground(record) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep keyboard activation on the row itself.
The onKeyDown handler also receives key events from the nested actions Button. Pressing Enter or Space while that button is focused calls actions.onOpenPlayground(record) before the button can perform its own action. The button's onClick propagation guard does not stop this keydown.
Handle the row shortcut only when event.target === event.currentTarget, then call event.preventDefault() for the row's Space activation.
Proposed fix
onKeyDown={(event) => {
- if (event.key === "Enter" || event.key === " ") actions.onOpenPlayground(record)
+ if (event.target !== event.currentTarget) return
+ if (event.key === "Enter" || event.key === " ") {
+ event.preventDefault()
+ actions.onOpenPlayground(record)
+ }
}}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (event.key === "Enter" || event.key === " ") actions.onOpenPlayground(record) | |
| if (event.target !== event.currentTarget) return | |
| if (event.key === "Enter" || event.key === " ") { | |
| event.preventDefault() | |
| actions.onOpenPlayground(record) | |
| } |
| const handleCodingAgentCopy = useCallback(async () => { | ||
| const text = composerRef.current?.getMarkdown().trim() ?? "" | ||
| try { | ||
| await navigator.clipboard.writeText(buildCodingAgentClipboard(text)) | ||
| setToastOpen(true) | ||
| } catch { | ||
| message.error("Couldn't copy — copy it manually") | ||
| return | ||
| } | ||
| captureFirstAgentIntent(posthog, { | ||
| source: "composer", | ||
| properties: {action: "coding_agent_copy", message: truncateForCapture(text)}, | ||
| }) | ||
| }, [message, posthog]) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Do not send composer text to analytics.
text can contain credentials, customer data, or other sensitive prompt content. truncateForCapture only limits length. It does not sanitize the content. Capture non-sensitive metadata, such as an action name and a length bucket, instead.
| const menuFor = useCallback( | ||
| (vm: SessionRowVm) => | ||
| actions.menuItems(actionTargetFor(vm), {onOpen: () => handleOpen(vm)}), | ||
| [actions, handleOpen], |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win
Convert Ant Design menu items before passing them to SessionCardList.
actions.menuItems() returns MenuProps["items"]. SessionCardList.menuFor requires SessionMenuEntry[]. These types are not compatible because Ant Design items can be null, groups, submenus, or items without a required string key and label.
Convert the result with toSessionMenuEntries, as web/oss/src/components/pages/sessions/SessionsPage.tsx already does.
Proposed fix
+import {toSessionMenuEntries} from "../assets/menuEntries"
+
const menuFor = useCallback(
(vm: SessionRowVm) =>
- actions.menuItems(actionTargetFor(vm), {onOpen: () => handleOpen(vm)}),
+ toSessionMenuEntries(
+ actions.menuItems(actionTargetFor(vm), {onOpen: () => handleOpen(vm)}),
+ ),
[actions, handleOpen],
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const menuFor = useCallback( | |
| (vm: SessionRowVm) => | |
| actions.menuItems(actionTargetFor(vm), {onOpen: () => handleOpen(vm)}), | |
| [actions, handleOpen], | |
| import {toSessionMenuEntries} from "../assets/menuEntries" | |
| const menuFor = useCallback( | |
| (vm: SessionRowVm) => | |
| toSessionMenuEntries( | |
| actions.menuItems(actionTargetFor(vm), {onOpen: () => handleOpen(vm)}), | |
| ), | |
| [actions, handleOpen], | |
| ) |
| <nav className="flex flex-col gap-0.5"> | ||
| {STATUSES.map((option) => ( | ||
| <button | ||
| key={option.value} | ||
| type="button" | ||
| onClick={() => setStatus(option.value)} | ||
| className={`box-border flex w-full cursor-pointer items-center gap-2 rounded-lg border-0 px-3 py-2 text-left text-sm transition-colors ${ | ||
| option.value === status | ||
| ? "bg-colorFillSecondary text-colorText" | ||
| : "bg-transparent text-colorTextSecondary hover:bg-colorFillQuaternary" | ||
| }`} | ||
| > | ||
| <span className="min-w-0 flex-1 truncate">{option.label}</span> | ||
| {option.value === "waiting" && waitingCount ? ( | ||
| <span className="shrink-0 rounded bg-colorWarningBg px-1.5 py-0.5 text-[11px] leading-none text-colorWarningText"> | ||
| {waitingCount} | ||
| </span> | ||
| ) : null} | ||
| </button> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Expose the active status to assistive technology.
The selected status is only visual. Add a labelled control group and expose each button state with aria-pressed.
Proposed fix
- <nav className="flex flex-col gap-0.5">
+ <div role="group" aria-label="Session status" className="flex flex-col gap-0.5">
{STATUSES.map((option) => (
<button
key={option.value}
type="button"
+ aria-pressed={option.value === status}
onClick={() => setStatus(option.value)}
@@
- </nav>
+ </div>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <nav className="flex flex-col gap-0.5"> | |
| {STATUSES.map((option) => ( | |
| <button | |
| key={option.value} | |
| type="button" | |
| onClick={() => setStatus(option.value)} | |
| className={`box-border flex w-full cursor-pointer items-center gap-2 rounded-lg border-0 px-3 py-2 text-left text-sm transition-colors ${ | |
| option.value === status | |
| ? "bg-colorFillSecondary text-colorText" | |
| : "bg-transparent text-colorTextSecondary hover:bg-colorFillQuaternary" | |
| }`} | |
| > | |
| <span className="min-w-0 flex-1 truncate">{option.label}</span> | |
| {option.value === "waiting" && waitingCount ? ( | |
| <span className="shrink-0 rounded bg-colorWarningBg px-1.5 py-0.5 text-[11px] leading-none text-colorWarningText"> | |
| {waitingCount} | |
| </span> | |
| ) : null} | |
| </button> | |
| <div role="group" aria-label="Session status" className="flex flex-col gap-0.5"> | |
| {STATUSES.map((option) => ( | |
| <button | |
| key={option.value} | |
| type="button" | |
| aria-pressed={option.value === status} | |
| onClick={() => setStatus(option.value)} | |
| className={`box-border flex w-full cursor-pointer items-center gap-2 rounded-lg border-0 px-3 py-2 text-left text-sm transition-colors ${ | |
| option.value === status | |
| ? "bg-colorFillSecondary text-colorText" | |
| : "bg-transparent text-colorTextSecondary hover:bg-colorFillQuaternary" | |
| }`} | |
| > | |
| <span className="min-w-0 flex-1 truncate">{option.label}</span> | |
| {option.value === "waiting" && waitingCount ? ( | |
| <span className="shrink-0 rounded bg-colorWarningBg px-1.5 py-0.5 text-[11px] leading-none text-colorWarningText"> | |
| {waitingCount} | |
| </span> | |
| ) : null} | |
| </button> | |
| ))} | |
| </div> |
| <button | ||
| type="button" | ||
| onClick={() => onOpenRow(vm)} | ||
| className="group box-border flex w-full cursor-pointer items-start gap-3 border-0 border-b border-solid border-colorBorderSecondary bg-transparent px-2 py-3 text-left hover:bg-colorFillQuaternary" | ||
| > | ||
| {/* A glyph for the KIND of row, with the status as a dot on its shoulder — the clock | ||
| and the chat bubble separate automation runs from conversations without a heading. */} | ||
| <SimpleTooltip title={vm.status.label}> | ||
| <span className="relative mt-0.5 flex shrink-0 text-colorTextTertiary"> | ||
| {origin ? <ClockIcon size={18} /> : <ChatCircleIcon size={18} />} | ||
| <span | ||
| className={`absolute -right-0.5 -top-0.5 h-2 w-2 rounded-full border border-solid border-colorBgContainer ${vm.status.dotClassName} ${ | ||
| vm.status.pulse ? "motion-safe:animate-pulse" : "" | ||
| }`} | ||
| /> | ||
| </span> | ||
| </SimpleTooltip> | ||
| <span className="flex min-w-0 flex-1 flex-col gap-1"> | ||
| <span className="flex w-full items-center gap-2"> | ||
| <span className="min-w-0 flex-1 truncate text-sm text-colorText"> | ||
| {vm.title} | ||
| </span> | ||
| {/* Quiet chip: the amber urgency lives on the dot; this states WHAT is asked. */} | ||
| {vm.status.chipLabel ? ( | ||
| <span className="shrink-0 rounded bg-colorFillQuaternary px-1.5 py-0.5 text-xs leading-none text-colorTextSecondary"> | ||
| {pendingGateLabel(vm.pending?.kinds)} | ||
| </span> | ||
| ) : null} | ||
| {showAgent ? ( | ||
| <span className="w-24 shrink-0 truncate text-right"> | ||
| <SessionAgentName agentId={vm.agentId} /> | ||
| </span> | ||
| ) : null} | ||
| <span className="w-16 shrink-0 text-right text-xs text-colorTextTertiary"> | ||
| {vm.activityAt ? timeAgo(Date.parse(vm.activityAt)) : "—"} | ||
| </span> | ||
| <SimpleTooltip title={vm.isPinned ? "Unpin" : "Pin"}> | ||
| <span | ||
| role="button" | ||
| tabIndex={-1} | ||
| aria-label={vm.isPinned ? "Unpin session" : "Pin session"} | ||
| onClick={(event) => { | ||
| event.stopPropagation() | ||
| onTogglePin(vm.id) | ||
| }} | ||
| className={`shrink-0 text-colorTextTertiary ${ | ||
| vm.isPinned || alwaysShowPin | ||
| ? "" | ||
| : "opacity-0 group-hover:opacity-100" | ||
| }`} | ||
| > | ||
| <PushPinIcon size={14} weight={vm.isPinned ? "fill" : "regular"} /> | ||
| </span> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Do not nest the pin action inside the row button.
The pin control is inside the row <button>. Nested interactive controls are invalid HTML. The pin control also has tabIndex={-1}, so keyboard users cannot toggle it.
Use a non-interactive row container. Make the title a button and make the pin an independent native button. SessionRow already uses this structure.
| /** | ||
| * The session filters — the whole panel, extracted from the desktop rail. The controls bind to | ||
| * the shared filter atoms (`useSessionFilters` / the control components), so desktop's 280px | ||
| * rail and mobile's stacked sheet render the same filter state. The agent picker runs on the | ||
| * kit Select and takes the host's agent list (each app resolves its roster differently). | ||
| */ |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Reduce the added explanatory comments.
Rewrite these ordinary comments as one short line or remove them. They do not document a bug, race, or ordering constraint.
web/packages/agenta-sessions-ui/src/SessionFiltersPanel.tsx#L1-L6: remove or reduce the component overview.web/packages/agenta-sessions-ui/src/SessionFiltersPanel.tsx#L85-L86: reduce the filter-heading explanation to one short line.web/packages/agenta-sessions-ui/src/controls/SessionFilterControls.tsx#L103-L104: reduce the control-description comment to one short line.web/packages/agenta-sessions/tests/unit/sessionOpenTarget.test.ts#L1-L5: remove the redundant test-file overview.
As per coding guidelines, “Keep in-code comments to at most one short line; use longer comments only for genuinely surprising constraints such as bugs, races, or ordering requirements.”
📍 Affects 3 files
web/packages/agenta-sessions-ui/src/SessionFiltersPanel.tsx#L1-L6(this comment)web/packages/agenta-sessions-ui/src/SessionFiltersPanel.tsx#L85-L86web/packages/agenta-sessions-ui/src/controls/SessionFilterControls.tsx#L103-L104web/packages/agenta-sessions/tests/unit/sessionOpenTarget.test.ts#L1-L5
Source: Coding guidelines
| const canShowMore = | ||
| !isEmpty && | ||
| (listRows.length > recentRows.length + pinnedRows.length + waitingRows.length || | ||
| Boolean(listQuery.hasNextPage)) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Correct canShowMore row accounting.
listRows excludes waiting rows and excludes pinned rows when withPinned is enabled. Lines 140-141 add waitingRows and pinnedRows to the rendered recent-row count. This can hide “Show more” while loaded recent rows are still not rendered.
For example, with a limit of 7 and two waiting rows, recentRows contains five rows from seven listRows. The current comparison evaluates 7 > 5 + 2 as false.
Proposed fix
- (listRows.length > recentRows.length + pinnedRows.length + waitingRows.length ||
+ (listRows.length > recentRows.length ||
Boolean(listQuery.hasNextPage))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const canShowMore = | |
| !isEmpty && | |
| (listRows.length > recentRows.length + pinnedRows.length + waitingRows.length || | |
| Boolean(listQuery.hasNextPage)) | |
| const canShowMore = | |
| !isEmpty && | |
| (listRows.length > recentRows.length || | |
| Boolean(listQuery.hasNextPage)) |
| @@ -0,0 +1,51 @@ | |||
| <?xml version="1.0" encoding="UTF-8" ?> | |||
| <testsuites name="vitest tests" tests="21" failures="0" errors="0" time="0.012510875"> | |||
| <testsuite name="tests/unit/sessionPreview.test.ts" timestamp="2026-08-06T05:27:01.475Z" hostname="Ardas-MacBook-Pro.local" tests="5" failures="0" errors="0" skipped="0" time="0.0045445"> | |||
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Remove the generated local test report.
Line 3 exposes Ardas-MacBook-Pro.local. The report also omits the new sessionOpenTarget suite. Do not commit local test output. Publish it only as a CI artifact.
- TemplatesGallery read `baseAppURL` without declaring it; take it from `urlAtom`. - SessionListCard handed antd `MenuProps["items"]` to `menuFor`, which wants `SessionMenuEntry[]` — route it through the existing `toSessionMenuEntries`. - @agenta/sessions-ui exported seven modules this lane never added, and SessionsListView imported one of them; bring SessionRowContextMenu over and drop the exports whose files land in a later lane.
…roken - "Show more" compared the recent-only population against a count of all three rendered groups, so loaded rows went unrendered with no way to reveal them; each population is now measured against its own slice. - A harness without a model rendered the model row as satisfied; the status follows `summary.model`, the composed string stays the summary. - Failed revision/schedule/subscription requests fell through to the empty state. One SectionLoadError with the query's own refetch covers both cards. - The next-run time never recomputed after the move. nowTickAtom moves out of the chat slice into @agenta/shared/state so both surfaces share the one interval. - AgentCard's initial came from the raw name, so a padded single word blanked it. - Home's composer kept a deleted agent selected and sent its id; a selection now has to still be in the roster.
- The card row's pin was a `role="button" tabIndex={-1}` span nested inside the row
button — invalid, and unreachable by keyboard. The row takes SessionRow's shape:
a plain container, a title button, and the shared SessionPinButton as a sibling.
- Enter/Space on the agent row and card fired the container's action even when the
kebab had focus, and Space scrolled the page.
- The status filter list was styling only; it is now a labelled group whose buttons
carry aria-pressed.
…g comments The two committed junit.xml files carry developer hostnames, and the narrow per-package ignore could not cover them once tracked. One `test-results/` pattern covers every workspace.
A deliberate behaviour change: `first_agent_intent` no longer carries the raw "describe your agent" text. `truncateForCapture` only capped length — a composer message can hold credentials, customer data or a pasted secret, and none of that belongs in an analytics property. The module already had the signal it says it exists to capture: `classifyAgentIntent` buckets the message into support/research/ops/content/ coding/data/other. Two of the three call sites already sent that bucket alongside the raw text, so dropping the text loses nothing there; the coding-agent-copy path on StripHome gains the classification it never had. `truncateForCapture` and `MESSAGE_CAPTURE_LIMIT` are gone with their last reference.
aa9863a to
d71bf16
Compare
617face to
5071cfc
Compare
The session and agent surfaces leave the app layer for the packages, so desktop and
/mrenderthe same components instead of two drifting copies.
Not run in a browser — static gates only (
pnpm lint-fix24/24,tsc --noEmitcleanfor
@agenta/shared,ui,entities,entity-ui,settings-ui,oss,ee,mobile).Stacked on
pkg/ui-primitives; review only this lane's diff.