feat(frontend): @agenta/home-ui — the home overview, templates and agent roster leave the app - #5880
feat(frontend): @agenta/home-ui — the home overview, templates and agent roster leave the app#5880ardaerzin wants to merge 6 commits into
Conversation
|
@coderabbitai review |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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 adds coding-agent clipboard support, centralizes starter-template contracts, introduces the shared ChangesCoding-agent handoff
Shared template catalog and agent roster
Shared home surface
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 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 |
|
f332816 to
7d387ac
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (15)
web/oss/src/components/TemplateStrip/components/CopiedToast.tsx (1)
8-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueShorten the implementation comment.
Lines 8-12 describe standard component behavior. Keep only a short comment if it documents an unusual constraint.
As per coding guidelines, keep in-code comments to one short line except for genuinely surprising constraints.
Source: Coding guidelines
web/packages/agenta-entities/src/workflow/agentTemplates.ts (1)
150-159: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
agentTemplateSeedat the template-create call sites.
OnboardingBrowseTemplates.tsxLine 34 andOnboardingConfigPanel.tsxLine 45 still constructtemplateBuilderMessage(template)andtemplate.nameseparately. Use this helper in both places so template name and builder seed stay under one shared contract.web/packages/agenta-entities/tests/unit/agentTemplates.test.ts (1)
3-3: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCover the new helper contracts.
Add tests for
agentTemplateSeedandagentTemplateByKey. Include a known key, an unknown key, an omitted key, and the builder-message fallback. These helpers drive template creation and URL resolution.web/oss/src/components/pages/agent-home/hooks/useTemplateSelect.ts (1)
42-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReduce this comment to one short line.
The repository limit is one short in-code comment. Move the longer rationale to documentation if it is required.
Source: Coding guidelines
web/packages/agenta-entity-ui/src/agent/AgentRosterGrid.tsx (1)
1-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReduce multi-line implementation comments.
Replace these comments with one short line, or move detailed rationale to documentation.
web/packages/agenta-entity-ui/src/agent/AgentRosterGrid.tsx#L1-L10: reduce the module description.web/packages/agenta-entity-ui/src/agent/AgentCard.tsx#L62-L63: reduce the optional-action description.web/packages/agenta-entity-ui/src/agent/AgentCard.tsx#L137-L138: reduce the open-action description.web/oss/src/components/pages/agents/AgentsGrid.tsx#L9-L11: reduce the roster mapping description.web/oss/src/components/pages/agents/AgentsPage.tsx#L91-L92: reduce the filter-rail description.web/oss/src/components/pages/agents/AgentsPage.tsx#L108-L109: reduce the archived-agent link description.web/oss/src/components/pages/agents/AgentsPage.tsx#L118-L119: reduce the grid-padding description.web/oss/src/components/pages/agents/store.ts#L16-L18: reduce the shared-search description.As per coding guidelines, keep in-code comments to at most one short line except for genuinely surprising constraints.
Source: Coding guidelines
web/oss/src/components/pages/agents/AgentsPage.tsx (1)
102-105: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winDebounce the agents roster search atom update.
SearchInputforwards every input change immediately, and agents WorkflowsQuery adds the search term to the TanStack query key, so typing changes the server query per keystroke. Apply a short delay before updatingagentsSearchTermAtom.Source: Coding guidelines
web/oss/src/components/pages/agent-home/StripHome.tsx (1)
3-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMerge the two imports from
@agenta/entities/workflow.Lines 3 and 4 both import from the same module specifier.
import/no-duplicatesflags this, sopnpm lintcan fail on the file.♻️ Proposed fix
-import {appTemplatesQueryAtom} from "`@agenta/entities/workflow`" -import {AGENT_TEMPLATES, type AgentStarterTemplate} from "`@agenta/entities/workflow`" +import { + AGENT_TEMPLATES, + appTemplatesQueryAtom, + type AgentStarterTemplate, +} from "`@agenta/entities/workflow`"As per coding guidelines: "Before committing frontend changes, run
pnpm lint-fixfrom thewebdirectory."Source: Coding guidelines
web/oss/src/components/pages/agent-home/components/TemplatesGallery/index.tsx (1)
67-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
TemplateSetupDraweris now unreachable on this page.
handleSelectTemplatenavigates to the detail route, and nothing else callssetSetupTemplate.setupTemplatetherefore staysnull,openis alwaysfalse, andhandleTemplateCreatenever runs. The drawer, its state, and themessage.infostub are dead code on this page.Either remove the drawer from this page until Phase B wires it, or restore an entry point that opens it. Do you want me to open an issue to track the Phase B wiring?
web/oss/src/components/pages/agent-home/hooks/useCreateAgent.ts (1)
47-47: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winStabilize the
onErrorcallback passed touseCreateAgentCore.Line 47 creates a new options object and a new arrow on every render. The core hook lists
onErrorin itsuseCallbackdependencies, so the returnedcreateAgentgets a new identity on every render. That identity then invalidates this hook's ownuseCallback(Line 91),useAgentHomeActions.onCreate, andStripHome.handleCreate. The whole create chain loses its memoization.♻️ Proposed fix
- const createAgent = useCreateAgentCore({onError: (text) => message.error(text)}) + const onError = useCallback((text: string) => message.error(text), [message]) + const createAgent = useCreateAgentCore({onError})As per coding guidelines: "Minimize React re-renders with
useMemo,useCallback, andReact.memowhere appropriate; avoid unstable inline functions and objects."Source: Coding guidelines
web/packages/agenta-home-ui/src/useCreateAgent.ts (1)
49-63: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winA busy call and a failed call are indistinguishable to the host.
Line 50 returns
nullwithout callingonErrorwhen a create is already in flight. Every other failure path also returnsnull, but withonError. The host cannot tell the two apart.
web/oss/src/components/pages/agent-home/hooks/useCreateAgent.tsmapsnulltofalse, andStripHome.handleCreatethen runssetLoading(false). A second click during an in-flight create therefore stops the spinner while the first create is still running, and the button reads as idle.Return a discriminated result (for example
{status: "busy" | "error" | "created"}), or expose anisCreatingflag the host can render from.web/packages/agenta-entities/src/session/core/freshSessions.ts (1)
14-18: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider keeping
freshSessionIdsmodule-private.The three helpers cover mark, check, and clear. Exporting the raw
Setlets any consumer mutate the tracker directly and bypass those helpers. If no consumer needs iteration, keep theSetlocal and drop it from the public re-export inweb/packages/agenta-entities/src/session/index.ts.♻️ Proposed change
-export const freshSessionIds = new Set<string>() +const freshSessionIds = new Set<string>()Then remove
freshSessionIdsfrom the export list inweb/packages/agenta-entities/src/session/index.ts.web/packages/agenta-home-ui/src/AgentsPanel.tsx (1)
66-76: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winMake the recency comparator total for entries without a timestamp.
Date.parse("")returnsNaN, so the comparator returnsNaNfor any entry that has neitheragent.updatedAtnorcreatedAt. The specification treats aNaNcomparator result as0, so such an entry compares equal to every other entry. The comparator then becomes non-transitive and the resulting order depends on the engine's sort implementation. That defeats the stated purpose of sorting here, which is one identical order on every surface.Map an unparseable date to
0so undated entries sort last deterministically.♻️ Proposed change
+const recency = (entry: AgentsPanelEntry) => { + const parsed = Date.parse(entry.agent.updatedAt ?? entry.createdAt ?? "") + return Number.isNaN(parsed) ? 0 : parsed +} + const DEFAULT_LIMIT = 5const shown = useMemo( - () => - [...entries] - .sort( - (a, b) => - Date.parse(b.agent.updatedAt ?? b.createdAt ?? "") - - Date.parse(a.agent.updatedAt ?? a.createdAt ?? ""), - ) - .slice(0, limit), + () => [...entries].sort((a, b) => recency(b) - recency(a)).slice(0, limit), [entries, limit], )web/packages/agenta-home-ui/src/TemplateDetail.tsx (2)
249-292: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoffBoth shells mount at every viewport.
identity,meta, andbodyare inserted into the phone shell (lines 268-270) and intoFilterRailLayout(lines 283-290). React renders two independent instances of each tree, and only CSS hides one. Two effects follow. First,renderMarkdown(template.instructions)runs twice on every render, at every screen size. Second, the document contains two<h1>elements for the same template.If the doubled markdown render becomes measurable, select one shell with a breakpoint value instead of
lg:hidden/hidden lg:flex.
1-18: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMulti-paragraph rationale comments exceed the comment-length guideline. Each new component in this package opens with a long narrative block that explains design history and rejected alternatives. The guideline allows one short line, and reserves longer comments for genuinely surprising constraints such as bugs, races, or ordering requirements. Keep the parts that record a real constraint, for example the sticky-child padding note in
HomeOverview.tsxline 61 and the import-cycle note inweb/packages/agenta-entities/src/session/core/freshSessions.ts. Move the design rationale to the PR description or an ADR.
web/packages/agenta-home-ui/src/TemplateDetail.tsx#L1-L18: reduce the 18-line header to one line stating what the component renders; keep the host-supplied markdown renderer note.web/packages/agenta-home-ui/src/AgentsPanel.tsx#L45-L53: reduce to one line; keep the creation-order constraint that justifies the local sort at lines 62-65.web/packages/agenta-home-ui/src/HomeOverview.tsx#L32-L47: reduce to one line; keep the padding-cancellation constraint for host frames.web/packages/agenta-home-ui/src/HomeTaskComposer.tsx#L24-L35: reduce to one line describing the composer's single action.web/packages/agenta-home-ui/src/TemplateGallery.tsx#L1-L13: reduce to one line; keep the note on which decisions stay with the host.web/packages/agenta-home-ui/src/UsageCard.tsx#L43-L51: reduce to one line; keep the reason cost and tokens sit behind Expand.web/packages/agenta-home-ui/src/NewAgentButton.tsx#L43-L49: reduce to one line.web/packages/agenta-home-ui/src/AgentActivity.tsx#L6-L12: reduce to one line.Based on the coding guideline "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."
Source: Coding guidelines
web/packages/agenta-home-ui/src/TemplateGallery.tsx (1)
159-177: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExpose the active category to assistive technology.
The active category is conveyed only by the background color on line 167. A screen reader announces every entry identically. Add
aria-pressedso the selected filter is programmatically determinable.♿ Proposed change
<button key={item.value} type="button" + aria-pressed={item.value === category} onClick={() => onCategoryChange(item.value)}
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: d9a1cef6-140d-4c09-abe9-aa3755cb4f3b
📒 Files selected for processing (65)
web/oss/src/components/AgentChatSlice/hooks/useOnboardingChat.tsweb/oss/src/components/NewAgentButton/index.tsxweb/oss/src/components/TemplateStrip/assets/codingAgentClipboard.test.tsweb/oss/src/components/TemplateStrip/assets/codingAgentClipboard.tsweb/oss/src/components/TemplateStrip/assets/constants.tsweb/oss/src/components/TemplateStrip/components/AgentIntentActions.tsxweb/oss/src/components/TemplateStrip/components/CopiedToast.tsxweb/oss/src/components/TemplateStrip/components/IntegrationBadges.tsxweb/oss/src/components/TemplateStrip/components/StripCard.tsxweb/oss/src/components/TemplateStrip/components/StripComposer.tsxweb/oss/src/components/TemplateStrip/components/StripRow.tsxweb/oss/src/components/TemplateStrip/components/TemplateChip.tsxweb/oss/src/components/TemplateStrip/components/TemplateChipDock.tsxweb/oss/src/components/TemplateStrip/hooks/useTemplateProvenance.tsxweb/oss/src/components/TemplateStrip/index.tsxweb/oss/src/components/pages/agent-home/PlaygroundOnboarding/OnboardingBrowseTemplates.tsxweb/oss/src/components/pages/agent-home/PlaygroundOnboarding/OnboardingConfigPanel.tsxweb/oss/src/components/pages/agent-home/StripHome.tsxweb/oss/src/components/pages/agent-home/components/HomeAutomationsSection.tsxweb/oss/src/components/pages/agent-home/components/HomeSessionsSection.tsxweb/oss/src/components/pages/agent-home/components/TemplateDetail/index.tsxweb/oss/src/components/pages/agent-home/components/TemplateSetupDrawer/IntegrationRow.tsxweb/oss/src/components/pages/agent-home/components/TemplateSetupDrawer/ToolsPreview.tsxweb/oss/src/components/pages/agent-home/components/TemplateSetupDrawer/index.tsxweb/oss/src/components/pages/agent-home/components/TemplatesGallery/TemplateSection.tsxweb/oss/src/components/pages/agent-home/components/TemplatesGallery/index.tsxweb/oss/src/components/pages/agent-home/components/TemplatesSection/ProviderMarks.tsxweb/oss/src/components/pages/agent-home/components/TemplatesSection/TemplateCard.tsxweb/oss/src/components/pages/agent-home/components/TemplatesSection/index.tsxweb/oss/src/components/pages/agent-home/components/YourAgentsTable/index.tsxweb/oss/src/components/pages/agent-home/components/YourAgentsTable/useAgentActivity.tsweb/oss/src/components/pages/agent-home/hooks/useCreateAgent.tsweb/oss/src/components/pages/agent-home/hooks/useCreateAgentFromTemplate.tsweb/oss/src/components/pages/agent-home/hooks/useTemplateSelect.tsweb/oss/src/components/pages/agent-home/index.tsxweb/oss/src/components/pages/agents/AgentsGrid.tsxweb/oss/src/components/pages/agents/AgentsPage.tsxweb/oss/src/components/pages/agents/store.tsweb/oss/src/state/url/template.test.tsweb/oss/src/state/url/template.tsweb/packages/agenta-entities/src/session/core/freshSessions.tsweb/packages/agenta-entities/src/session/index.tsweb/packages/agenta-entities/src/workflow/agentTemplates.tsweb/packages/agenta-entities/src/workflow/index.tsweb/packages/agenta-entities/src/workflow/state/agentRoster.tsweb/packages/agenta-entities/src/workflow/state/index.tsweb/packages/agenta-entities/tests/unit/agentTemplates.test.tsweb/packages/agenta-entity-ui/src/agent/AgentCard.tsxweb/packages/agenta-entity-ui/src/agent/AgentRosterGrid.tsxweb/packages/agenta-entity-ui/src/agent/index.tsweb/packages/agenta-home-ui/eslint.config.mjsweb/packages/agenta-home-ui/package.jsonweb/packages/agenta-home-ui/src/AgentActivity.tsxweb/packages/agenta-home-ui/src/AgentsPanel.tsxweb/packages/agenta-home-ui/src/AnalyticsRangePicker.tsxweb/packages/agenta-home-ui/src/HomeOverview.tsxweb/packages/agenta-home-ui/src/HomeTaskComposer.tsxweb/packages/agenta-home-ui/src/NewAgentButton.tsxweb/packages/agenta-home-ui/src/TemplateDetail.tsxweb/packages/agenta-home-ui/src/TemplateGallery.tsxweb/packages/agenta-home-ui/src/UsageCard.tsxweb/packages/agenta-home-ui/src/index.tsweb/packages/agenta-home-ui/src/useAgentActivity.tsweb/packages/agenta-home-ui/src/useCreateAgent.tsweb/packages/agenta-home-ui/tsconfig.json
💤 Files with no reviewable changes (6)
- web/oss/src/components/pages/agent-home/components/HomeAutomationsSection.tsx
- web/oss/src/components/pages/agent-home/components/HomeSessionsSection.tsx
- web/oss/src/components/pages/agent-home/components/TemplatesGallery/TemplateSection.tsx
- web/oss/src/components/pages/agent-home/components/TemplatesSection/ProviderMarks.tsx
- web/oss/src/components/pages/agent-home/components/TemplatesSection/TemplateCard.tsx
- web/oss/src/components/pages/agent-home/hooks/useCreateAgentFromTemplate.ts
| usagePanel={ | ||
| <UsageCard | ||
| expandedContent={ | ||
| <AnalyticsDashboard layout="stack" showTimeRangeSelector={false} /> | ||
| } | ||
| /> | ||
| } |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether HeightCollapse unmounts children when closed, and how UsageCard renders the slot.
fd -i 'HeightCollapse' --type f | head
fd -i 'HeightCollapse' --type f --extension tsx --exec cat -n {}
echo '--- UsageCard ---'
fd 'UsageCard.tsx' --type f --exec sed -n '80,125p' {}Repository: Agenta-AI/agenta
Length of output: 9203
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- StripHome relevant lines ---'
sed -n '1,80p' web/oss/src/components/pages/agent-home/StripHome.tsx
sed -n '180,225p' web/oss/src/components/pages/agent-home/StripHome.tsx
echo '--- UsageCard outline/lines ---'
wc -l web/packages/agenta-home-ui/src/UsageCard.tsx
sed -n '1,170p' web/packages/agenta-home-ui/src/UsageCard.tsx
echo '--- AnalyticsDashboard definitions/usages ---'
fd -i 'AnalyticsDashboard' --type f --exec sh -c 'echo "FILE $1"; sed -n "1,220p" "$1"' sh {}
echo '--- dynamic/import behavior scan ---'
rg -n "dynamic\\(|useObservabilityDashboard|AnalyticsDashboard|expandedContent|HeightCollapse" web/oss/src/components/pages/agent-home/StripHome.tsx web/packages/agenta-home-ui/src/UsageCard.tsx web/packages/agenta-sdk/src web/packages/agenta-ui/src -g '!**/node_modules/**'Repository: Agenta-AI/agenta
Length of output: 23224
Gate the lazy analytics dashboard by the expand state.
AnalyticsDashboard is created and passed to UsageCard during returning-user Home rendering, and UsageCard always renders that node inside HeightCollapse. HeightCollapse animates height to collapsedHeight rather than unmounting its children, so the lazy-loaded analytics chunk and useObservabilityDashboard fetch can run before the user clicks Expand. Return null from ExpansionCard when expandedContent is not expanded, or pass a renderable render prop so the slot is mounted only when expanded is true.
| /** Owner-specified key (matches the design prototype; intentionally not "agenta:"-prefixed). */ | ||
| export const STRIP_HIDDEN_STORAGE_KEY = "agenta-tpl-strip-hidden" |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use the required storage-key prefix.
STRIP_HIDDEN_STORAGE_KEY stores a persisted preference, but agenta-tpl-strip-hidden does not use the required agenta: prefix. Change it to a key such as agenta:tpl-strip-hidden.
As per coding guidelines, persisted preference storage keys must be prefixed with agenta:.
Source: Coding guidelines
| visible ? "opacity-100" : "opacity-0" | ||
| }`} | ||
| > | ||
| <Check size={15} strokeWidth={2.4} className="shrink-0 text-[var(--ant-lime-6)]" /> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use a semantic theme color for the icon.
--ant-lime-6 is a palette token. Replace it with an Ant Design semantic token or a supported var(--ag-color*) variable. Verify the result in light and dark themes.
As per coding guidelines, consume theme colors through semantic tokens or supported var(--ag-color*) variables. Based on learnings, validate both appearances when replacing a color token.
Sources: Coding guidelines, Learnings
| const [agentId, setAgentId] = useState<string | null>(null) | ||
|
|
||
| // Default to the most recently touched agent — the one you're most likely to want next. | ||
| const effectiveAgentId = agentId ?? agents[0]?.id ?? null | ||
| const selectedName = useMemo( | ||
| () => agents.find((agent) => agent.id === effectiveAgentId)?.name, | ||
| [agents, effectiveAgentId], | ||
| ) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Fall back to the default agent when the selected agent leaves the list.
effectiveAgentId short-circuits on any non-null agentId, so a stale selection survives changes to agents. If the selected agent is archived, renamed to a new id, or the project changes, two things follow: selectedName becomes undefined, so the trigger shows the robot icon with no label; and onStart receives an agentId that no longer exists. Validate the selection against the current list.
🐛 Proposed fix
- // Default to the most recently touched agent — the one you're most likely to want next.
- const effectiveAgentId = agentId ?? agents[0]?.id ?? null
- const selectedName = useMemo(
- () => agents.find((agent) => agent.id === effectiveAgentId)?.name,
- [agents, effectiveAgentId],
- )
+ // Default to the most recently touched agent; drop a selection that left the list.
+ const selected = useMemo(
+ () => agents.find((agent) => agent.id === agentId) ?? agents[0],
+ [agents, agentId],
+ )
+ const effectiveAgentId = selected?.id ?? null
+ const selectedName = selected?.name📝 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 [agentId, setAgentId] = useState<string | null>(null) | |
| // Default to the most recently touched agent — the one you're most likely to want next. | |
| const effectiveAgentId = agentId ?? agents[0]?.id ?? null | |
| const selectedName = useMemo( | |
| () => agents.find((agent) => agent.id === effectiveAgentId)?.name, | |
| [agents, effectiveAgentId], | |
| ) | |
| const [agentId, setAgentId] = useState<string | null>(null) | |
| // Default to the most recently touched agent; drop a selection that left the list. | |
| const selected = useMemo( | |
| () => agents.find((agent) => agent.id === agentId) ?? agents[0], | |
| [agents, agentId], | |
| ) | |
| const effectiveAgentId = selected?.id ?? null | |
| const selectedName = selected?.name |
| <ChatComposer | ||
| onSubmit={async (text) => { | ||
| if (!effectiveAgentId) return | ||
| await onStart({agentId: effectiveAgentId, text}) | ||
| }} | ||
| attachments={attachments} | ||
| placeholder="Describe the task, or start the conversation…" | ||
| disabled={!effectiveAgentId} |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how ChatComposer invokes and guards onSubmit.
fd -t f 'ChatComposer.tsx' web/packages --exec rg -n -C 15 'onSubmit' {} \;Repository: Agenta-AI/agenta
Length of output: 4045
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Locate rich-chat-input files"
fd -t f 'rich-chat-input' web/packages --max-depth 5
echo
echo "Find handle types and use/receive of onSubmit"
rg -n -C 8 'RichChatInputHandle|onSubmit|executeSubmit|submit|handleSubmit|handleKeyPress' web/packages -g '*.ts' -g '*.tsx' | head -n 240Repository: Agenta-AI/agenta
Length of output: 24450
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read focused composer implementation without scanning broad files.
composer=$(fd -t f 'ChatComposer.tsx' web/packages 2>/dev/null | head -n 1)
echo "composer=$composer"
if [ -n "${composer:-}" ]; then
wc -l "$composer"
sed -n '1,180p' "$composer" | cat -n
fi
echo
echo "Find RichChatInput definition files by likely package path"
fd -t f 'RichChatInput.tsx|RichChatInput.ts' web/packages | xargs -r -n1 sh -c 'echo "===== $0 ====="; wc -l "$0"; sed -n "1,240p" "$0" | cat -n'Repository: Agenta-AI/agenta
Length of output: 21923
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "SubmitPlugin implementation"
sed -n '1,260p' web/packages/agenta-ui/src/RichChatInput/plugins/SubmitPlugin.tsx | cat -n
echo
echo "Other onSubmit call sites in RichChatInput/RichChatInput.tsx"
rg -n -C 5 'onSubmit\(' web/packages/agenta-ui/src/RichChatInput/RichChatInput.tsx web/packages/agenta-ui/src/RichChatInput -g '*.tsx' -g '*.ts'Repository: Agenta-AI/agenta
Length of output: 4644
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "RichChatInput submit helper:"
sed -n '1,70p' web/packages/agenta-ui/src/RichChatInput/assets/submit.ts | cat -n
echo
echo "Submit path from SendButton plugin:"
sed -n '1,90p' web/packages/agenta-ui/src/RichChatInput/plugins/SendButton.tsx | cat -nRepository: Agenta-AI/agenta
Length of output: 6114
🏁 Script executed:
#!/bin/bash
set -euo pipefail
node - <<'JS'
function submitEditorAsMarkdown(trimmed, onSubmit, clearFn) {
if (!trimmed) return {submitted: false}
onSubmit(trimmed)
clearFn()
return {submitted: true}
}
function withComposerState(value, submit, clearFn) {
let current = value
try {
submit()
current = ''
clearFn()
return {current, submitted: true}
} catch (e) {
return {current, submitted: false, error: e.message}
}
}
{
const state = withComposerState('hello', () => {
throw new Error('network failure')
}, () => {})
console.log('rejected:', state)
}
function submitWithCatchValue(value, onSubmit, clearFn) {
try {
submitEditorAsMarkdown(value, onSubmit, clearFn)
} catch (e) {
// The catch should ensure the clear path is skipped or deferred.
}
return value
}
{
const state = submitWithCatchValue('hello', () => {
throw new Error('network failure')
}, () => {})
console.log('caught_preservesValue:', state)
}
JSRepository: Agenta-AI/agenta
Length of output: 258
Handle rejected onSubmit in ChatComposer.
onStart is awaited directly in HomeTaskComposer, so a rejection propagates through RichChatInput without restoration or failure reporting from the composer’s submit handler. Wrap the submission around the clear/update path or catch at the host boundary and keep showing the user's draft when onStart rejects.
| * A re-entry latch protects every caller (home button, composer, template cards) from a rapid | ||
| * double-click minting two agents; the UI-level disabled/loading guards don't cover every path. | ||
| */ | ||
| export const useCreateAgent = ({onError}: UseCreateAgentOptions = {}) => { | ||
| const commitFromEphemeral = useSetAtom(createWorkflowFromEphemeralAtom) | ||
| const inFlightRef = useRef(false) | ||
|
|
||
| return useCallback( | ||
| async ({name, entityId}: CreateAgentParams = {}): Promise<CreatedAgent | null> => { | ||
| if (inFlightRef.current) return null | ||
| inFlightRef.current = true |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find the create surfaces that call the shared hook, and check whether more than one can mount together.
rg -nP --type=ts --type=tsx -C3 '\buseCreateAgent\s*\(' | head -60Repository: Agenta-AI/agenta
Length of output: 186
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Files:"
git ls-files 'web/**/*.{ts,tsx}' | rg 'useCreateAgent|home|composer|template' || true
echo
echo "useCreateAgent usages:"
rg -n -C3 'useCreateAgent\s*\(' -g '*.ts' -g '*.tsx' web || true
echo
echo "useCreateAgent definition:"
file="$(git ls-files | rg '(^|/)useCreateAgent\.ts$' | head -1 || true)"
if [ -n "$file" ]; then
wc -l "$file"
sed -n '1,140p' "$file" | cat -n
fiRepository: Agenta-AI/agenta
Length of output: 9339
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Locate `@agenta/home-ui` useCreateAgent source:"
fd -a 'useCreateAgent\.ts$|resources\.ts$|WorkflowApi|createWorkflowFrom' web/packages | sed 's#^\./##' | head -80 || true
file="$(git ls-files | rg 'packages/agenta-home-ui/src/useCreateAgent\.ts$|src/.*useCreateAgent\.ts$' | head -1 || true)"
if [ -n "$file" ]; then
echo
echo "Definition at $file:"
wc -l "$file"
sed -n '1,140p' "$file" | cat -n
fi
echo
echo "Imports/usages of `@agenta/home-ui` useCreateAgent and createWorkflowFrom:"
rg -n -C2 '`@agenta/home-ui`|createWorkflowFromEphemeralAtom|useCreateAgent\(\{|createWorkflowFromEphemeral' -g '*.ts' -g '*.tsx' web || true
echo
echo "Agents table/home buttons context:"
sed -n '1,220p' web/oss/src/components/pages/agent-home/index.tsx 2>/dev/null | cat -n || true
sed -n '1,180p' web/oss/src/components/pages/agent-home/hooks/useAgentHomeActions.ts 2>/dev/null | cat -n || true
sed -n '1,180p' web/oss/src/components/pages/agent-home/hooks/useConsumePendingTemplate.ts 2>/dev/null | cat -n || true
sed -n '1,180p' web/oss/src/components/pages/agent-home/hooks/useTemplateSelect.ts 2>/dev/null | cat -n || trueRepository: Agenta-AI/agenta
Length of output: 37271
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "web/packages/agenta-home-ui/src/useCreateAgent.ts:"
wc -l web/packages/agenta-home-ui/src/useCreateAgent.ts
sed -n '1,180p' web/packages/agenta-home-ui/src/useCreateAgent.ts
echo
echo "web/packages/agenta-entities/src/workflow/state/commit.ts atom section:"
sed -n '530,635p' web/packages/agenta-entities/src/workflow/state/commit.ts
echo
echo "Shared component create entry points:"
sed -n '1,220p' web/oss/src/components/pages/agent-home/hooks/useAgentHomeActions.ts
sed -n '1,180p' web/oss/src/components/pages/agent-home/hooks/useConsumePendingTemplate.ts
sed -n '1,180p' web/oss/src/components/pages/agent-home/hooks/useTemplateSelect.ts
sed -n '1,140p' web/packages/agenta-home-ui/src/index.tsRepository: Agenta-AI/agenta
Length of output: 20129
Narrow or share the create-agent re-entry latch.
inFlightRef is local to each useCreateAgent() instance. Multiple create surfaces call separate hook instances, so the documented claim that this latch protects every caller across home button, composer, and template cards is too broad. Move it to module scope/Jotai if callers are expected to collide, or limit the comment to per-hook-instance repeats.
…ent roster leave the app
This lane reintroduces `first_agent_intent` call sites that the same fix on `pkg/session-surfaces` couldn't see, so they still send the raw "describe your agent" text under a `message` property. `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. `classifyAgentIntent` is the signal the module says it exists to capture: it buckets the message into support/research/ops/content/coding/data/other. The two free-text submit paths already sent that bucket alongside the raw text, so dropping the text loses nothing there; both coding-agent-copy paths (the home strip and the onboarding chat composer) gain the classification they never had. `truncateForCapture` and `MESSAGE_CAPTURE_LIMIT` go with their last reference.
Two things the app's own copy of this composer already gets right, and mobile's (the package one) did not: - The selection short-circuited on any non-null id, so an agent that was archived, re-created under a new id, or lost to a project switch stayed "selected": the trigger rendered a robot icon with no label, and Send handed `onStart` an id that no longer resolves. Honour the selection only while it is still in the roster, else fall back to the head, else nothing — the app copy's approach. - A rejected `onStart` was awaited with no catch. RichChatInput's submit is fire-and-forget, so the rejection went nowhere while the editor had already cleared: no message, no error, no idea the task never started. Put the draft back through the input handle and report it in the attachments strip, exactly as the mobile chat composer does for a send that can't carry its files.
`--ant-lime-6` is a raw antd palette variable that only exists where antd's CSS-var theme is injected. The mark means "it worked", so it takes the semantic role instead: `--ag-colorSuccess`, which the shared token layer defines for both themes and mobile imports.
…nded `HeightCollapse` clips its children rather than unmounting them, so whatever a host passes as `expandedContent` mounts on the first paint of Home — desktop's slot is a lazily-imported tremor dashboard with its own observability query, downloaded and fired before anyone clicks Expand, on the page whose whole point is first paint. Mount it on the first expand and keep it mounted after that. Keeping it beats unmounting on collapse: the collapse animates height to 0 with the content still in place, where dropping the node would animate an empty box. The prop stays a ReactNode — building the element is free, it is the mount that costs — so both callers (Home and the usage summary's strip variant) are fixed as they stand.
The comment claimed the latch protects every caller — the home button, the composer, the template cards — but it was a ref inside the hook, and each of those surfaces calls `useCreateAgent()` for itself (the classic home mounts three instances at once). It only ever stopped one button from being double-clicked; clicking a template card and then hitting send while the commit was in flight minted two agents. Move the latch to module scope so the documented guarantee is the real one.
7d387ac to
f7f967f
Compare
The home surface as a package: the overview, the template gallery and the agent roster.
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/sessions-tabs; review only this lane's diff.