chore(repo): consolidate committed agent skills under .agents/skills and publish four local skills - #5558
Conversation
…and publish four local skills Move the five committed .claude-native skills to .agents/skills, publish write-issue, implement-feature, plan-feature, and style-editing, and make the .gitignore allowlist match the full tracked set. The .claude symlinks for these skills land in the follow-up commit. Claude-Session: https://claude.ai/code/session_01KFiHhzfjyGZ95fU7FGw5Mb
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR adds agent workflow skills, updates API and runner safeguards, introduces voice and attachment features, adds parked connection interactions, simplifies navigation, migrates table consumers to shared packages, and updates canonical tracing, evaluation, and deployment contracts. ChangesAgent skills and documentation
Platform and runner
Navigation and settings
Agent chat and shared UI
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 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 |
There was a problem hiding this comment.
Actionable comments posted: 17
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
web/oss/src/components/AgentChatSlice/components/clientTools/ConnectToolWidget.tsx (1)
63-108: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winShow manual-retry errors before the settled-result branch.
A manual retry happens after
meta.settledis already true, so Line 64 returns the generic “Connection not completed” chip before Line 99 can rendererrorText. Move thephase === "error"branch above the settled check.Proposed fix
+ if (phase === "error") { + return ( + <ChipRow icon={<Warning size={13} weight="fill" className="text-colorError" />}> + <Text type="danger" className="!text-xs truncate" title={errorText ?? undefined}> + {errorText ?? "Connection failed."} + </Text> + <RetryButton onClick={() => runConnect(false)} /> + </ChipRow> + ) + } + if (meta.settled || outcome) {web/oss/src/components/pages/observability/components/StatusRenderer.tsx (1)
5-18: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDo not render unset statuses as
success.When
statusis null or undefined, the renderer passes"STATUS_CODE_UNSET", butstatusMapperfalls through to its success mapping for every non-error value. Add an explicit neutral/unset mapping and reserve success for an actual success status.Also applies to: 28-34
🟡 Minor comments (9)
.agents/skills/update-api-docs/SKILL.md-68-68 (1)
68-68: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winRenumber the verification step.
The workflow jumps from Step 2 to Step 5. Rename this to Step 3 or restore the missing steps so the instructions are sequential.
.agents/skills/create-changelog-announcement/SKILL.md-311-317 (1)
311-317: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse a Discussion-specific close flow.
githubUrlcan point to a GitHub discussion, butgh issue closetargets issues/PRs. Add a check for the discussion path and direct CLI users to the GitHub Discussions interface or a GraphQLcloseDiscussioncall instead..agents/skills/write-social-announcement/SKILL.md-14-21 (1)
14-21: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winRemove the contradictory “key benefit” wording.
The skill forbids
keyas an adjective but later requires a “key benefit.” Use “main benefit” or “user benefit” instead.Also applies to: 137-143
web/oss/src/pages/w/[workspace_id]/p/[project_id]/settings/index.tsx-121-138 (1)
121-138: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winRaw
--ag-c-*CSS variable literal for the demo badge.
bg-[var(--ag-c-0517290F)]uses a raw--ag-c-*literal instead of a supported theme token. As per coding guidelines,web/**/*.{ts,tsx}files must "Consume theme colors through Ant Design semantic tokens, Tailwind color utilities, or supportedvar(--ag-color*)variables; do not use raw hex colors or--ag-c-*literals." This also risks not adapting correctly between light/dark themes.#!/bin/bash # Check whether --ag-c-0517290F (or similar --ag-c- literals) are used elsewhere / have a sanctioned semantic token equivalent. rg -n '\-\-ag-c-' --type=ts --type=tsx --type=css -g '!**/theme-variables.css' 2>/dev/nullSource: Coding guidelines
docs/design/simplify-nav-new-users/context.md-89-90 (1)
89-90: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winBroken list formatting for criteria 6–8.
Items 6, 7, 8 and the "Phase 2" heading are run together on two lines with no breaks, unlike items 1–5 which are each on their own line. This will render as a run-on paragraph instead of a continued numbered list.
📝 Proposed fix
-**Phase 2** 6. The Settings → Feature flags switch flips the effective mode and survives a -reload. 7. Toggling the switch updates the sidebar without a full page reload. 8. Phase 1's sidebar behavior is unchanged when no override is set. +**Phase 2** + +6. The Settings → Feature flags switch flips the effective mode and survives a reload. +7. Toggling the switch updates the sidebar without a full page reload. +8. Phase 1's sidebar behavior is unchanged when no override is set.web/oss/src/components/Drives/useDriveDrop.ts-58-87 (1)
58-87: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
dragleavedecrements for non-file drags and the spring timer outlives unmount.Two small asymmetries in this effect:
onEnteronly counts file drags, butonLeavedecrements on everydragleave, so a text drag crossing the window can zero the depth and cleardraggingwhile a file drag is still active.- The cleanup removes listeners but never calls
clearSpring(), so a pending 700ms spring-load firesonNavigateafter unmount.🛡️ Proposed fix
- const onLeave = () => { + const onLeave = (e: DragEvent) => { + if (!has(e)) return depth.current = Math.max(0, depth.current - 1) if (depth.current === 0) setDragging(false) } @@ return () => { window.removeEventListener("dragenter", onEnter) window.removeEventListener("dragleave", onLeave) window.removeEventListener("drop", onEnd) window.removeEventListener("dragend", onEnd) + clearSpring() }web/oss/src/components/AgentChatSlice/components/RecordingBar.tsx-66-88 (1)
66-88: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winLive region wraps a per-second clock, so AT re-announces every tick.
role="status" aria-live="polite"on the container includesmmss(seconds), which changes once a second. Keep the live region for state changes only and mark the timer text asaria-hidden(or movearia-liveto a dedicated node).web/oss/src/components/AgentChatSlice/hooks/useAudioRecorder.ts-138-146 (1)
138-146: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winRe-entrancy gap while permission is pending.
recRef.currentis stillnullduring"requesting", so a secondstart()before the prompt resolves issues anothergetUserMedia; the first resolved stream then leaks (only the last one is stored instreamRef). Callers currently disable the button onpending, but the hook shouldn't rely on that.🛡️ Proposed guard
const start = useCallback(() => { - if (!supported || recRef.current) return + if (!supported || recRef.current || startingRef.current) return + startingRef.current = true setError(null)Reset
startingRef.current = falsein both the.thenand.catchpaths (and inteardown).web/oss/src/components/Drives/driveFileSource.tsx-42-49 (1)
42-49: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winKey local preview state by the selected file. Switching between staged files can reuse state from the prior file: a prior decode error makes the next media file fail immediately, and prior text can render while the next file is loading.
web/oss/src/components/Drives/driveFileSource.tsx#L42-L49: store the failed object URL/path rather than a shared boolean, or reset failure state when the local source changes.web/oss/src/components/Drives/driveFileSource.tsx#L72-L85: clear text for a new local source and ignore completions from superseded reads.
🧹 Nitpick comments (11)
web/oss/src/lib/helpers/dynamicEnv.ts (1)
36-48: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueKeep these feature-flag comments to one line.
Collapse the UI behavior descriptions into concise constraints; place extended rollout details in documentation. 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.”
Source: Coding guidelines
web/packages/agenta-shared/package.json (1)
32-32: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win
peerDependencies.axiosfloor not bumped to match the sibling packages.
dependencies.axioswas bumped to1.18.0here, butpeerDependencies.axios(Line 49) is still>=1.13.5 <2.0.0— unlikeagenta-entitiesandagenta-entity-ui, whose peer floors were both raised to>=1.18.0in this same change. A consumer could satisfy this package's declared peer contract with a much older, vulnerable axios. The workspace-levelpnpm.overrides.axios: "1.18.0"masks the practical impact today, but the manifest is inconsistent with the rest of this bump.♻️ Proposed fix
"peerDependencies": { "`@tanstack/react-query`": ">=5.0.0", - "axios": ">=1.13.5 <2.0.0", + "axios": ">=1.18.0 <2.0.0", "jotai": ">=2.0.0",Also applies to: 47-53
web/oss/next.config.ts (1)
139-144: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd a production regression check for
refractor/all.The fix depends on webpack preserving
react-syntax-highlighter’s dynamicrefractor/allimport. Add a production build/render test for a PrismAsync code block so this IgnorePlugin rule cannot regress silently.web/oss/src/components/AgentChatSlice/AgentConversation.tsx (1)
1722-1749: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated attachment-key formula between
toUploadFileanduploads.enqueue.The uid string
${name}-${lastModified}-${size}is built twice; ifuseAttachmentUploadsever keys items differently, enqueue silently matches nothing. Extract one helper (attachmentUid(file)) used by both.♻️ Suggested extraction
+const attachmentUid = (file: File) => `${file.name}-${file.lastModified}-${file.size}` + const toUploadFile = (file: File): UploadFile => ({ - uid: `${file.name}-${file.lastModified}-${file.size}`, + uid: attachmentUid(file), name: file.name, status: "done", originFileObj: file as UploadFile["originFileObj"], }) @@ - uploads.enqueue(accepted.map((f) => `${f.name}-${f.lastModified}-${f.size}`)) + uploads.enqueue(accepted.map(attachmentUid))web/oss/src/components/Drives/DriveExplorer.tsx (2)
1866-1885: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winIndex-based staged ids reshuffle on removal.
id: ${i}-${f.name}changes for every item after a removal, so the synthetic tile keys (__staged__/${id}) all change and the surviving tiles remount (losing their fade/scale state).fileKey(f)is already imported and is stable per file.♻️ Proposed change
staged.map((f, i) => ({ - id: `${i}-${f.name}`, + id: fileKey(f), name: f.name,
1727-1733: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDouble cast hides the shape mismatch.
{path, size} as unknown as MountFilesilences the compiler for injected upload nodes. IfMountFilehas additional required fields, prefer a narrowed helper type (e.g.Pick<MountFile, "path" | "size">widened at thebuildDriveTreeboundary) or make those fields optional, so a future field addition surfaces here.web/oss/src/components/Drives/useDriveDrop.ts (1)
170-194: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
useStageDrophighlight flickers across child elements.Unlike
useDriveDrop, this has no depth counter:onDragLeavefires when the cursor crosses any child of the rail/aside, sodropActivetoggles off mid-drag. A depth ref (or checkinge.currentTarget.contains(e.relatedTarget as Node)) keeps the tint steady.web/oss/src/components/AgentChatSlice/components/AudioPlayer.tsx (1)
28-34: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueReset
duration/currenton src change.The src effect resets
probingRefbut leaves the previous clip'sduration/currentin state, so the timer and progress bar briefly show the old clip's values (and a wrong ratio) untilloadedmetadatafires.♻️ Proposed tweak
probingRef.current = false + setDuration(0) + setCurrent(0)web/oss/src/components/AgentChatSlice/components/RecordingBar.tsx (1)
38-48: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valuerAF loop for a once-per-second clock.
The frame loop runs ~60×/s to publish a value that changes 1×/s. An interval (e.g. 250 ms) derived from
startedAtRefkeeps the no-drift property at a fraction of the wakeups.web/oss/src/components/AgentChatSlice/components/RecordingWaveform.tsx (1)
25-29: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value
parseRgbassumes anrgb()/rgba()computed color.If the theme token resolves to a modern color space (
oklch(...),color(srgb …)), the digit scan yields nonsense channels or the[0,0,0]fallback — an invisible waveform in dark mode. Consider falling back tocurrentColorfill with per-stopglobalAlphainstead of reconstructing channels.Also applies to: 53-66
web/packages/agenta-ui/src/RichChatInput/ComposerSendButton.tsx (1)
34-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace raw hex colors with semantic theme tokens.
The
#191a0dand#b8cb3fvalues will not adapt with the supported theme system. Use Ant semantic tokens, Tailwind semantic utilities, or supported--ag-color*variables instead.As per coding guidelines, “Consume theme colors through Ant Design semantic tokens, Tailwind color utilities, or supported
var(--ag-color*)variables; do not use raw hex colors.”Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 03599dc8-8972-4f8a-a39e-60b25ef11bbc
⛔ Files ignored due to path filters (6)
api/uv.lockis excluded by!**/*.lockclients/python/uv.lockis excluded by!**/*.locksdks/python/uv.lockis excluded by!**/*.lockservices/runner/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlservices/uv.lockis excluded by!**/*.lockweb/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (246)
.agents/skills/add-announcement/SKILL.md.agents/skills/create-changelog-announcement/SKILL.md.agents/skills/implement-feature/SKILL.md.agents/skills/plan-feature/SKILL.md.agents/skills/style-editing/SKILL.md.agents/skills/update-api-docs/SKILL.md.agents/skills/update-llm-model-list/SKILL.md.agents/skills/write-issue/SKILL.md.agents/skills/write-social-announcement/SKILL.md.claude/skills/add-announcement.claude/skills/create-changelog-announcement.claude/skills/implement-feature.claude/skills/plan-feature.claude/skills/style-editing.claude/skills/sync-model-catalog.claude/skills/update-api-docs.claude/skills/update-llm-model-list.claude/skills/write-issue.claude/skills/write-social-announcement.gitignore.gitleaksignoreapi/entrypoints/routers.pyapi/oss/src/core/mounts/service.pyapi/oss/src/middlewares/prefix.pyapi/oss/tests/pytest/unit/middlewares/test_prefix.pyapi/oss/tests/pytest/unit/test_mounts_file_ops.pyapi/pyproject.tomlclients/python/pyproject.tomldocs/design/simplify-nav-new-users/README.mddocs/design/simplify-nav-new-users/context.mddocs/design/simplify-nav-new-users/plan.mddocs/design/simplify-nav-new-users/research.mddocs/design/simplify-nav-new-users/status.mdhosting/kubernetes/helm/Chart.yamlsdks/python/pyproject.tomlservices/pyproject.tomlservices/runner/package.jsonservices/runner/src/engines/sandbox_agent/run-limits.tsservices/runner/src/env.tsservices/runner/src/sessions/persist.tsservices/runner/src/sessions/records-query.tsservices/runner/tests/unit/env-num.test.tsservices/runner/tests/unit/run-limits.test.tsservices/runner/tests/unit/session-persist.test.tsservices/runner/tests/unit/session-records-query.test.tsweb/ee/css-modules.d.tsweb/ee/package.jsonweb/ee/tsconfig.jsonweb/oss/css-modules.d.tsweb/oss/next.config.tsweb/oss/package.jsonweb/oss/src/components/AgentChatSlice/AgentConversation.tsxweb/oss/src/components/AgentChatSlice/assets/attachments.tsweb/oss/src/components/AgentChatSlice/assets/constants.tsweb/oss/src/components/AgentChatSlice/assets/markdown.tsxweb/oss/src/components/AgentChatSlice/components/AgentMessage.tsxweb/oss/src/components/AgentChatSlice/components/AttachmentViewerDrawer.tsxweb/oss/src/components/AgentChatSlice/components/AudioPlayer.tsxweb/oss/src/components/AgentChatSlice/components/ComposerAttachments.tsxweb/oss/src/components/AgentChatSlice/components/ConnectModelBanner.tsxweb/oss/src/components/AgentChatSlice/components/Inspector/InspectSessionButton.tsxweb/oss/src/components/AgentChatSlice/components/InteractionDock.tsxweb/oss/src/components/AgentChatSlice/components/MicPermissionNotice.tsxweb/oss/src/components/AgentChatSlice/components/QueuedMessages.tsxweb/oss/src/components/AgentChatSlice/components/RecordingBar.tsxweb/oss/src/components/AgentChatSlice/components/RecordingWaveform.tsxweb/oss/src/components/AgentChatSlice/components/ToolActivity.tsxweb/oss/src/components/AgentChatSlice/components/VoiceInputButton.tsxweb/oss/src/components/AgentChatSlice/components/clientTools/ConnectToolWidget.tsxweb/oss/src/components/AgentChatSlice/components/clientTools/useConnectFlow.tsweb/oss/src/components/AgentChatSlice/hooks/useAttachmentUploads.tsweb/oss/src/components/AgentChatSlice/hooks/useAudioRecorder.tsweb/oss/src/components/AgentChatSlice/hooks/useVoiceInput.tsweb/oss/src/components/Drives/ContextRail.tsxweb/oss/src/components/Drives/DriveExplorer.tsxweb/oss/src/components/Drives/FilesDrawer.tsxweb/oss/src/components/Drives/SessionFilesDrawer.tsxweb/oss/src/components/Drives/StorageFilesHeader.tsxweb/oss/src/components/Drives/StorageSection.tsxweb/oss/src/components/Drives/VirtualTileGrid.tsxweb/oss/src/components/Drives/configDrive.tsweb/oss/src/components/Drives/driveFileSource.tsxweb/oss/src/components/Drives/driveMedia.tsweb/oss/src/components/Drives/renderers.tsxweb/oss/src/components/Drives/useDriveDrop.tsweb/oss/src/components/Drives/useImagePreviews.tsweb/oss/src/components/Drives/useMountUpload.tsweb/oss/src/components/Drives/useSessionDrive.tsweb/oss/src/components/EvalRunDetails/Table.tsxweb/oss/src/components/EvalRunDetails/atoms/table/run.tsweb/oss/src/components/EvalRunDetails/atoms/traces.tsweb/oss/src/components/EvalRunDetails/components/CompareRunsMenu.tsxweb/oss/src/components/EvalRunDetails/components/EvalTestcaseDrawerAdapter/index.tsxweb/oss/src/components/EvalRunDetails/components/FocusDrawerHeader.tsxweb/oss/src/components/EvalRunDetails/components/FocusDrawerSidePanel.tsxweb/oss/src/components/EvalRunDetails/components/columnVisibility/ColumnVisibilityPopoverContent.tsxweb/oss/src/components/EvalRunDetails/components/views/SingleScenarioViewerPOC/ScenarioNavigator.tsxweb/oss/src/components/EvalRunDetails/components/views/SingleScenarioViewerPOC/index.tsxweb/oss/src/components/EvalRunDetails/evaluationPreviewTableStore.tsweb/oss/src/components/EvalRunDetails/hooks/useCellVisibility.tsweb/oss/src/components/EvalRunDetails/hooks/usePreviewColumns.tsxweb/oss/src/components/EvalRunDetails/utils/buildPreviewColumns.tsxweb/oss/src/components/EvaluationRunsTablePOC/atoms/fetchAutoEvaluationRuns.tsweb/oss/src/components/EvaluationRunsTablePOC/atoms/tableStore.tsweb/oss/src/components/EvaluationRunsTablePOC/components/EvaluationRunsTable/index.tsxweb/oss/src/components/EvaluationRunsTablePOC/components/EvaluationRunsTable/types.tsweb/oss/src/components/EvaluationRunsTablePOC/components/columnVisibility/ColumnVisibilityPopoverContent.tsxweb/oss/src/components/EvaluationRunsTablePOC/components/filters/EvaluationRunsHeaderFilters.tsxweb/oss/src/components/EvaluationRunsTablePOC/hooks/useEvaluationRunsColumns/index.tsxweb/oss/src/components/EvaluationRunsTablePOC/hooks/useEvaluationRunsColumns/utils.tsxweb/oss/src/components/EvaluationRunsTablePOC/hooks/useEvaluatorHeaderReference.tsweb/oss/src/components/EvaluationRunsTablePOC/types.tsweb/oss/src/components/InfiniteVirtualTable/InfiniteVirtualTable.tsxweb/oss/src/components/InfiniteVirtualTable/atoms/columnHiddenKeys.tsweb/oss/src/components/InfiniteVirtualTable/atoms/columnVisibility.tsweb/oss/src/components/InfiniteVirtualTable/atoms/columnWidths.tsweb/oss/src/components/InfiniteVirtualTable/columns/cells.tsxweb/oss/src/components/InfiniteVirtualTable/columns/createStandardColumns.tsxweb/oss/src/components/InfiniteVirtualTable/columns/createTableColumns.tsweb/oss/src/components/InfiniteVirtualTable/columns/types.tsweb/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityHeader.tsxweb/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityTrigger.tsxweb/oss/src/components/InfiniteVirtualTable/components/InfiniteVirtualTableInner.tsxweb/oss/src/components/InfiniteVirtualTable/components/TableDescription.tsxweb/oss/src/components/InfiniteVirtualTable/components/TableShell.tsxweb/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityMenuTrigger.tsxweb/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityPopoverContent.tsxweb/oss/src/components/InfiniteVirtualTable/components/columnVisibility/TableSettingsDropdown.tsxweb/oss/src/components/InfiniteVirtualTable/components/filters/FiltersPopoverTrigger.tsxweb/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityContext.tsweb/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityFlagContext.tsxweb/oss/src/components/InfiniteVirtualTable/context/VirtualTableScrollContainerContext.tsweb/oss/src/components/InfiniteVirtualTable/createInfiniteDatasetStore.tsweb/oss/src/components/InfiniteVirtualTable/createInfiniteTableStore.tsweb/oss/src/components/InfiniteVirtualTable/features/InfiniteVirtualTableFeatureShell.tsxweb/oss/src/components/InfiniteVirtualTable/features/index.tsweb/oss/src/components/InfiniteVirtualTable/features/useInfiniteTableFeaturePagination.tsweb/oss/src/components/InfiniteVirtualTable/helpers/createSimpleTableStore.tsweb/oss/src/components/InfiniteVirtualTable/helpers/createTableRowHelpers.tsweb/oss/src/components/InfiniteVirtualTable/helpers/index.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useColumnDomRefs.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibility.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibilityControls.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useContainerResize.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useContainerSize.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useEditableTable.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useExpandableRows.tsxweb/oss/src/components/InfiniteVirtualTable/hooks/useHeaderViewportVisibility.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteScroll.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteTablePagination.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useResizableColumns.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useRowHeight.tsxweb/oss/src/components/InfiniteVirtualTable/hooks/useScopedColumnVisibility.tsxweb/oss/src/components/InfiniteVirtualTable/hooks/useScrollConfig.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useScrollContainer.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useSmartResizableColumns.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useTableActions.tsxweb/oss/src/components/InfiniteVirtualTable/hooks/useTableExport.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useTableHeaderHeight.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useTableKeyboardShortcuts.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useTableManager.tsxweb/oss/src/components/InfiniteVirtualTable/hooks/useTableRowSelection.tsweb/oss/src/components/InfiniteVirtualTable/index.tsweb/oss/src/components/InfiniteVirtualTable/providers/ColumnVisibilityProvider.tsxweb/oss/src/components/InfiniteVirtualTable/providers/InfiniteVirtualTableStoreProvider.tsxweb/oss/src/components/InfiniteVirtualTable/types.tsweb/oss/src/components/InfiniteVirtualTable/utils/columnUtils.tsweb/oss/src/components/Playground/Components/TestsetDropdown/TestsetPreviewPanelWrapper.tsxweb/oss/src/components/SessionInspector/PanelSessionInspectorButton.tsxweb/oss/src/components/SharedDrawers/AddToTestsetDrawer/components/PreviewSection.tsxweb/oss/src/components/SharedDrawers/SessionDrawer/components/SessionDrawerButton/index.tsxweb/oss/src/components/SharedDrawers/SessionDrawer/store/sessionDrawerStore.tsweb/oss/src/components/SharedDrawers/SessionDrawer/types.tsweb/oss/src/components/SharedDrawers/TraceDrawer/components/TraceContent/components/OverviewTabItem/index.tsxweb/oss/src/components/SharedDrawers/TraceDrawer/components/TraceTree/assets/spanVisibility.tsweb/oss/src/components/SharedDrawers/TraceDrawer/components/TraceTree/index.tsxweb/oss/src/components/SharedDrawers/TraceDrawer/store/traceDrawerStore.tsweb/oss/src/components/Sidebar/dynamic/registry.tsweb/oss/src/components/Sidebar/hooks/useSidebarConfig/index.tsxweb/oss/src/components/Sidebar/scopes/settingsScope.tsxweb/oss/src/components/TestcasesTableNew/components/TestcaseHeader.tsxweb/oss/src/components/TestcasesTableNew/components/TestcasesTableShell.tsxweb/oss/src/components/TestcasesTableNew/index.tsxweb/oss/src/components/TestcasesTableNew/state/rowHeight.tsweb/oss/src/components/TestsetsTable/TestsetsTable.tsxweb/oss/src/components/TestsetsTable/assets/createTestsetsColumns.tsxweb/oss/src/components/TestsetsTable/atoms/fetchTestsets.tsweb/oss/src/components/TestsetsTable/components/TestsetsHeaderFilters.tsxweb/oss/src/components/pages/agent-home/OnboardingEntry.tsxweb/oss/src/components/pages/agent-home/hooks/useCreateAgent.tsweb/oss/src/components/pages/agents/AgentsPage.tsxweb/oss/src/components/pages/agents/store.tsweb/oss/src/components/pages/observability/assets/constants.tsweb/oss/src/components/pages/observability/assets/exportUtils.tsweb/oss/src/components/pages/observability/assets/getObservabilityColumns.tsxweb/oss/src/components/pages/observability/components/AvatarTreeContent.tsxweb/oss/src/components/pages/observability/components/NodeNameCell.tsxweb/oss/src/components/pages/observability/components/SessionsTable/assets/getSessionColumns.tsxweb/oss/src/components/pages/observability/components/StatusRenderer.tsxweb/oss/src/components/pages/observability/components/TimestampCell.tsxweb/oss/src/components/pages/prompts/PromptsPage.tsxweb/oss/src/components/pages/settings/FeatureFlags/FeatureFlags.tsxweb/oss/src/components/pages/settings/Organization/General.tsxweb/oss/src/components/pages/settings/assets/navigation.test.tsweb/oss/src/components/pages/settings/assets/navigation.tsweb/oss/src/hooks/usePostAuthRedirect.tsweb/oss/src/lib/helpers/dynamicEnv.tsweb/oss/src/lib/hooks/usePreviewEvaluations/index.tsweb/oss/src/lib/hooks/usePreviewEvaluations/types.tsweb/oss/src/lib/onboarding/atoms.tsweb/oss/src/lib/onboarding/index.tsweb/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/[app_id]/index.tsxweb/oss/src/pages/w/[workspace_id]/p/[project_id]/settings/index.tsxweb/oss/src/services/tracing/types/index.tsweb/oss/src/state/app/atoms/fetcher.tsweb/oss/src/state/entities/shared/README.mdweb/oss/src/state/entities/shared/createPaginatedEntityStore.tsweb/oss/src/state/entities/testcase/paginatedStore.tsweb/oss/src/state/entities/testset/paginatedStore.tsweb/oss/src/state/newObservability/atoms/queries.tsweb/oss/src/state/onboarding/selectors.tsweb/oss/src/state/settings/featureFlags.tsweb/oss/tsconfig.jsonweb/package.jsonweb/packages/agenta-api-client/package.jsonweb/packages/agenta-entities/package.jsonweb/packages/agenta-entities/src/evaluationRun/core/schema.tsweb/packages/agenta-entities/src/evaluationRun/etl/resolveMappings.tsweb/packages/agenta-entities/src/trace/etl/exportMatchingTraces.tsweb/packages/agenta-entities/src/workflow/index.tsweb/packages/agenta-entities/src/workflow/state/index.tsweb/packages/agenta-entities/src/workflow/state/inspectMeta.tsweb/packages/agenta-entities/tests/unit/export-matching-traces.test.tsweb/packages/agenta-entity-ui/package.jsonweb/packages/agenta-playground/src/index.tsweb/packages/agenta-playground/src/state/execution/index.tsweb/packages/agenta-playground/src/state/index.tsweb/packages/agenta-shared/package.jsonweb/packages/agenta-ui/src/InfiniteVirtualTable/index.tsweb/packages/agenta-ui/src/InfiniteVirtualTable/types.tsweb/packages/agenta-ui/src/RichChatInput/ComposerSendButton.tsxweb/packages/agenta-ui/src/RichChatInput/RichChatInput.tsxweb/packages/agenta-ui/src/RichChatInput/index.tsweb/packages/agenta-ui/src/RichChatInput/plugins/SendButton.tsxweb/packages/agenta-ui/src/RichChatInput/plugins/dictation.tsweb/tests/package.json
💤 Files with no reviewable changes (57)
- web/oss/src/components/InfiniteVirtualTable/hooks/useEditableTable.ts
- web/oss/src/components/SharedDrawers/SessionDrawer/types.ts
- web/oss/src/components/InfiniteVirtualTable/features/useInfiniteTableFeaturePagination.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useColumnDomRefs.ts
- web/oss/src/components/InfiniteVirtualTable/columns/cells.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useContainerSize.ts
- web/oss/src/components/InfiniteVirtualTable/helpers/index.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useScopedColumnVisibility.tsx
- web/oss/src/components/InfiniteVirtualTable/components/TableDescription.tsx
- web/oss/src/components/InfiniteVirtualTable/columns/types.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableRowSelection.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useContainerResize.ts
- web/oss/src/components/InfiniteVirtualTable/atoms/columnWidths.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteScroll.ts
- web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/TableSettingsDropdown.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useSmartResizableColumns.ts
- web/oss/src/components/InfiniteVirtualTable/providers/ColumnVisibilityProvider.tsx
- web/oss/src/components/InfiniteVirtualTable/helpers/createSimpleTableStore.ts
- web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityMenuTrigger.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableKeyboardShortcuts.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableHeaderHeight.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibilityControls.ts
- web/oss/src/components/InfiniteVirtualTable/helpers/createTableRowHelpers.ts
- web/oss/src/components/InfiniteVirtualTable/InfiniteVirtualTable.tsx
- web/oss/src/components/SharedDrawers/SessionDrawer/components/SessionDrawerButton/index.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableManager.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useScrollContainer.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useHeaderViewportVisibility.ts
- web/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityHeader.tsx
- web/oss/src/components/InfiniteVirtualTable/columns/createTableColumns.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useResizableColumns.ts
- web/oss/src/components/InfiniteVirtualTable/components/TableShell.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useScrollConfig.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableActions.tsx
- web/oss/src/components/InfiniteVirtualTable/createInfiniteDatasetStore.ts
- web/oss/src/components/InfiniteVirtualTable/context/VirtualTableScrollContainerContext.ts
- web/oss/src/components/InfiniteVirtualTable/atoms/columnHiddenKeys.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteTablePagination.ts
- web/oss/src/components/InfiniteVirtualTable/atoms/columnVisibility.ts
- web/oss/src/components/InfiniteVirtualTable/components/filters/FiltersPopoverTrigger.tsx
- web/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityTrigger.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibility.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useRowHeight.tsx
- web/oss/src/components/InfiniteVirtualTable/features/index.ts
- web/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityContext.ts
- web/oss/src/components/InfiniteVirtualTable/utils/columnUtils.ts
- web/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityFlagContext.tsx
- web/oss/src/components/InfiniteVirtualTable/components/InfiniteVirtualTableInner.tsx
- web/oss/src/components/InfiniteVirtualTable/createInfiniteTableStore.ts
- web/oss/src/components/InfiniteVirtualTable/columns/createStandardColumns.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useExpandableRows.tsx
- web/oss/src/components/InfiniteVirtualTable/index.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableExport.ts
- web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityPopoverContent.tsx
- web/oss/src/components/InfiniteVirtualTable/providers/InfiniteVirtualTableStoreProvider.tsx
- web/oss/src/components/InfiniteVirtualTable/types.ts
- web/oss/src/components/InfiniteVirtualTable/features/InfiniteVirtualTableFeatureShell.tsx
| ## Phase 6: Stacked branch (GitButler) | ||
|
|
||
| Put the work on its own lane. This repo runs GitButler, so use `but`, not raw git, and follow | ||
| the repo's gotchas. | ||
|
|
||
| 1. `but oplog snapshot -m "before <feature> branch"` first. The workspace is usually dirty | ||
| with other lanes' work, so protect it. | ||
| 2. `but status` to see the lanes and pick the parent to stack on. | ||
| 3. `but branch new <type>/<feature> --anchor <parent>` to stack on that parent. | ||
| 4. Stage only this feature's files to the new lane with `but rub <path> <branch>`, then | ||
| `but commit <branch> --only -m "..."`. Never run a bare `but commit`, which sweeps every | ||
| unassigned change in the workspace into the lane. | ||
| 5. Stop there unless the user asks to push. When they do, `but push <branch>` then | ||
| `gh pr create --head <branch> --base <parent-or-main>`. Use the PR body drafted in Phase 5 | ||
| (via the `write-pr-description` skill). Confirm it includes Context, Scope / risk, and the | ||
| full How to QA section (prerequisites, steps, expected result, test command, edge cases) | ||
| before creating the PR. End commit messages and PR bodies with the session footer the repo | ||
| conventions require. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Resolve the contradictory push policy and document verification.
Phase 6 says to stop unless the user explicitly asks to push, but the coordination section says to push a lane whenever a slice is done. Choose one policy, preferably explicit user approval, and add the required local-SHA versus git ls-remote verification after any push.
As per coding guidelines, GitButler pushes must be verified against the remote SHA; silent success is not sufficient.
Also applies to: 215-218
Source: Coding guidelines
| ```ts | ||
| import { atomWithStorage } from "jotai/utils"; | ||
|
|
||
| /** null = follow default, true = force simplified, false = force full. Per-user via LS. */ | ||
| export const navSimplifiedOverrideAtom = atomWithStorage<boolean | null>( | ||
| "agenta:onboarding:<userId>:nav-simplified-override", | ||
| null, | ||
| ); | ||
|
|
||
| // Phase 2: explicit choice wins; else fall back to the signup-era default. | ||
| export const advancedNavHiddenAtom = atom((get) => { | ||
| const override = get(navSimplifiedOverrideAtom); | ||
| return override ?? get(navSimplifiedDefaultAtom); | ||
| }); | ||
| ``` |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
navSimplifiedOverrideAtom snippet isn't actually per-user — contradicts its own comment and Slice 1's pattern.
The comment says "Per-user via LS," but the code uses a single atomWithStorage with a literal, non-interpolated key "agenta:onboarding:<userId>:nav-simplified-override". Unlike Slice 1's navSimplifiedDefaultAtomFamily (which wraps atomFamily(userId) + createScopedStorageKey(userId, ...)), this snippet has no per-user family/wrapper — if implemented as written, all users on the same browser would share one override value.
Please verify the actual implementation (in state/onboarding/selectors.ts, not included in this batch) uses proper per-user scoping matching navSimplifiedDefaultAtomFamily, and fix this doc snippet to match if it doesn't.
#!/bin/bash
# Verify whether navSimplifiedOverrideAtom is actually per-user-scoped in the real implementation.
fd -a selectors.ts web/oss/src/state/onboarding
rg -n -A8 'navSimplifiedOverrideAtom' web/oss/src/state/onboarding/selectors.ts web/oss/src/lib/onboarding/atoms.ts 2>/dev/null| const open = openUid !== null | ||
|
|
||
| // Build (and own the object URLs of) the node set only while open, revoking on close/change. | ||
| const [nodes, setNodes] = useState<BuiltNodes | null>(null) | ||
| useEffect(() => { | ||
| if (!open) { | ||
| setNodes(null) | ||
| return | ||
| } | ||
| const built = buildNodes(uploads) | ||
| setNodes(built) | ||
| return () => built.source.forEach((v) => URL.revokeObjectURL(v.objectUrl)) | ||
| }, [open, uploads]) |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win
Rebuilds every object URL on any uploads reference change.
buildNodes mints a fresh URL.createObjectURL for every attachment whenever this effect reruns, and it reruns on ANY reference change to uploads — including a progress-percent update on a single in-flight attachment if the composer drives progress through this same list. That thrashes object URLs (revoke+recreate) for every unrelated file while the drawer is open, wasting allocations and risking a preview flicker.
Consider keying URL creation per-file (memoized on file identity, like useImagePreviews) instead of rebuilding the whole node set whenever uploads changes identity.
#!/bin/bash
# Confirm whether the composer's uploads array is re-created on progress ticks
fd ComposerAttachments.tsx web/oss/src/components/AgentChatSlice/components --exec cat -n {}♻️ Direction for a per-file-memoized fix
- const [nodes, setNodes] = useState<BuiltNodes | null>(null)
- useEffect(() => {
- if (!open) {
- setNodes(null)
- return
- }
- const built = buildNodes(uploads)
- setNodes(built)
- return () => built.source.forEach((v) => URL.revokeObjectURL(v.objectUrl))
- }, [open, uploads])
+ const urlsRef = useRef(new Map<File, string>())
+ const [nodes, setNodes] = useState<BuiltNodes | null>(null)
+ useEffect(() => {
+ if (!open) {
+ setNodes(null)
+ return
+ }
+ // buildNodes reuses urlsRef entries for files already seen, only minting
+ // URLs for newly-added files and revoking ones no longer present.
+ setNodes(buildNodes(uploads, urlsRef.current))
+ }, [open, uploads])
+ useEffect(() => {
+ return () => {
+ urlsRef.current.forEach((url) => URL.revokeObjectURL(url))
+ urlsRef.current.clear()
+ }
+ }, [])| const runConnect = useCallback( | ||
| async (settleParkedCall: boolean) => { | ||
| if (phase === "connecting") return | ||
| if (settleParkedCall && (!activeRef.current || settledRef.current || meta.settled)) | ||
| return | ||
| setErrorText(null) | ||
| setPhase("connecting") | ||
| try { | ||
| const result = await handleCreate({slug, name: slug, mode}) | ||
| if (settleParkedCall && !activeRef.current) return | ||
| const redirectUrl = | ||
| typeof result.connection?.data?.redirect_url === "string" | ||
| ? result.connection.data.redirect_url | ||
| : undefined | ||
|
|
||
| const onSuccess = () => { | ||
| if (settleParkedCall && !activeRef.current) return | ||
| invalidate() | ||
| if (settleParkedCall) finish({connected: true, integration, slug}) | ||
| else { | ||
| setManuallyConnected(true) | ||
| setPhase("idle") | ||
| } | ||
| } | ||
|
|
||
| if (!redirectUrl) { | ||
| // No OAuth step (e.g. api_key created inline): the connection already exists. | ||
| onSuccess() | ||
| return | ||
| } | ||
|
|
||
| const popup = window.open( | ||
| redirectUrl, | ||
| oauthWindowName, | ||
| "width=600,height=700,popup=yes", | ||
| ) | ||
| if (!popup) { | ||
| setPhase("error") | ||
| setErrorText("Couldn’t open the connection window. Allow popups and retry.") | ||
| return | ||
| } | ||
| popupRef.current = popup | ||
|
|
||
| const apiOrigin = agentaApiOrigin() | ||
| let succeeded = false | ||
|
|
||
| const onMessage = (event: MessageEvent) => { | ||
| // HARD requirement: only trust the callback from the Agenta API origin. | ||
| if (!apiOrigin || event.origin !== apiOrigin) return | ||
| const data = event.data as { | ||
| type?: unknown | ||
| slug?: unknown | ||
| integration?: unknown | ||
| } | null | ||
| if (!data || data.type !== "tools:oauth:complete") return | ||
| // Several connect flows can be live at once (an agent may ask for | ||
| // multiple connections in one turn). The callback tags the completion with | ||
| // its connection identity, so a flow settles ONLY on its own completion — | ||
| // otherwise the first finished flow would mark every open one connected. | ||
| // A legacy callback without identity keeps the prior single-flow behavior. | ||
| if (typeof data.slug === "string" && data.slug !== slug) return | ||
| else if ( | ||
| typeof data.slug !== "string" && | ||
| typeof data.integration === "string" && | ||
| data.integration !== integration | ||
| ) | ||
| return | ||
| succeeded = true | ||
| teardown() | ||
| onSuccess() | ||
| } | ||
| window.addEventListener("message", onMessage) | ||
|
|
||
| const poll = window.setInterval(() => { | ||
| if (!popupRef.current?.closed || succeeded) return | ||
| // Abandon: closed without a success message. | ||
| teardown() | ||
| if (settleParkedCall && !activeRef.current) return | ||
| if (settleParkedCall) | ||
| finish({connected: false, integration, slug, reason: "cancelled"}) | ||
| else setPhase("idle") | ||
| }, POPUP_POLL_MS) | ||
|
|
||
| const timeout = window.setTimeout(() => { | ||
| if (succeeded) return | ||
| teardown() | ||
| if (settleParkedCall && !activeRef.current) return | ||
| if (settleParkedCall) | ||
| finish({connected: false, integration, slug, reason: "timeout"}) | ||
| else setPhase("idle") | ||
| }, CONNECT_TIMEOUT_MS) | ||
|
|
||
| cleanupRef.current = () => { | ||
| window.removeEventListener("message", onMessage) | ||
| window.clearInterval(poll) | ||
| window.clearTimeout(timeout) | ||
| try { | ||
| popupRef.current?.close() | ||
| } catch { | ||
| // best effort | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Unmount during handleCreate leaks the listener/poll/timeout (and updates state after unmount).
cleanupRef is only assigned after the awaited handleCreate resolves and the popup opens. If the card unmounts while that request is in flight, the unmount effect runs teardown() with cleanupRef still null; the continuation then registers the message listener, setInterval and setTimeout, which are never torn down — the poll keeps running and its finish/setPhase calls hit an unmounted component. activeRef only guards the settleParkedCall branches, so the retry path (settleParkedCall === false) leaks unconditionally.
Track mount state and bail (plus close the popup) before wiring the watchers.
🛡️ Proposed fix
const popupRef = useRef<Window | null>(null)
const cleanupRef = useRef<(() => void) | null>(null)
+ const mountedRef = useRef(true)
+ useEffect(() => () => {
+ mountedRef.current = false
+ }, [])
@@
const result = await handleCreate({slug, name: slug, mode})
+ if (!mountedRef.current) return
if (settleParkedCall && !activeRef.current) return
@@
popupRef.current = popup
+ if (!mountedRef.current) {
+ try {
+ popup.close()
+ } catch {
+ // best effort
+ }
+ return
+ }📝 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 runConnect = useCallback( | |
| async (settleParkedCall: boolean) => { | |
| if (phase === "connecting") return | |
| if (settleParkedCall && (!activeRef.current || settledRef.current || meta.settled)) | |
| return | |
| setErrorText(null) | |
| setPhase("connecting") | |
| try { | |
| const result = await handleCreate({slug, name: slug, mode}) | |
| if (settleParkedCall && !activeRef.current) return | |
| const redirectUrl = | |
| typeof result.connection?.data?.redirect_url === "string" | |
| ? result.connection.data.redirect_url | |
| : undefined | |
| const onSuccess = () => { | |
| if (settleParkedCall && !activeRef.current) return | |
| invalidate() | |
| if (settleParkedCall) finish({connected: true, integration, slug}) | |
| else { | |
| setManuallyConnected(true) | |
| setPhase("idle") | |
| } | |
| } | |
| if (!redirectUrl) { | |
| // No OAuth step (e.g. api_key created inline): the connection already exists. | |
| onSuccess() | |
| return | |
| } | |
| const popup = window.open( | |
| redirectUrl, | |
| oauthWindowName, | |
| "width=600,height=700,popup=yes", | |
| ) | |
| if (!popup) { | |
| setPhase("error") | |
| setErrorText("Couldn’t open the connection window. Allow popups and retry.") | |
| return | |
| } | |
| popupRef.current = popup | |
| const apiOrigin = agentaApiOrigin() | |
| let succeeded = false | |
| const onMessage = (event: MessageEvent) => { | |
| // HARD requirement: only trust the callback from the Agenta API origin. | |
| if (!apiOrigin || event.origin !== apiOrigin) return | |
| const data = event.data as { | |
| type?: unknown | |
| slug?: unknown | |
| integration?: unknown | |
| } | null | |
| if (!data || data.type !== "tools:oauth:complete") return | |
| // Several connect flows can be live at once (an agent may ask for | |
| // multiple connections in one turn). The callback tags the completion with | |
| // its connection identity, so a flow settles ONLY on its own completion — | |
| // otherwise the first finished flow would mark every open one connected. | |
| // A legacy callback without identity keeps the prior single-flow behavior. | |
| if (typeof data.slug === "string" && data.slug !== slug) return | |
| else if ( | |
| typeof data.slug !== "string" && | |
| typeof data.integration === "string" && | |
| data.integration !== integration | |
| ) | |
| return | |
| succeeded = true | |
| teardown() | |
| onSuccess() | |
| } | |
| window.addEventListener("message", onMessage) | |
| const poll = window.setInterval(() => { | |
| if (!popupRef.current?.closed || succeeded) return | |
| // Abandon: closed without a success message. | |
| teardown() | |
| if (settleParkedCall && !activeRef.current) return | |
| if (settleParkedCall) | |
| finish({connected: false, integration, slug, reason: "cancelled"}) | |
| else setPhase("idle") | |
| }, POPUP_POLL_MS) | |
| const timeout = window.setTimeout(() => { | |
| if (succeeded) return | |
| teardown() | |
| if (settleParkedCall && !activeRef.current) return | |
| if (settleParkedCall) | |
| finish({connected: false, integration, slug, reason: "timeout"}) | |
| else setPhase("idle") | |
| }, CONNECT_TIMEOUT_MS) | |
| cleanupRef.current = () => { | |
| window.removeEventListener("message", onMessage) | |
| window.clearInterval(poll) | |
| window.clearTimeout(timeout) | |
| try { | |
| popupRef.current?.close() | |
| } catch { | |
| // best effort | |
| } | |
| } | |
| const popupRef = useRef<Window | null>(null) | |
| const cleanupRef = useRef<(() => void) | null>(null) | |
| const mountedRef = useRef(true) | |
| useEffect( | |
| () => () => { | |
| mountedRef.current = false | |
| }, | |
| [], | |
| ) | |
| const runConnect = useCallback( | |
| async (settleParkedCall: boolean) => { | |
| if (phase === "connecting") return | |
| if (settleParkedCall && (!activeRef.current || settledRef.current || meta.settled)) | |
| return | |
| setErrorText(null) | |
| setPhase("connecting") | |
| try { | |
| const result = await handleCreate({slug, name: slug, mode}) | |
| if (!mountedRef.current) return | |
| if (settleParkedCall && !activeRef.current) return | |
| const redirectUrl = | |
| typeof result.connection?.data?.redirect_url === "string" | |
| ? result.connection.data.redirect_url | |
| : undefined | |
| const onSuccess = () => { | |
| if (settleParkedCall && !activeRef.current) return | |
| invalidate() | |
| if (settleParkedCall) finish({connected: true, integration, slug}) | |
| else { | |
| setManuallyConnected(true) | |
| setPhase("idle") | |
| } | |
| } | |
| if (!redirectUrl) { | |
| // No OAuth step (e.g. api_key created inline): the connection already exists. | |
| onSuccess() | |
| return | |
| } | |
| const popup = window.open( | |
| redirectUrl, | |
| oauthWindowName, | |
| "width=600,height=700,popup=yes", | |
| ) | |
| if (!popup) { | |
| setPhase("error") | |
| setErrorText("Couldn’t open the connection window. Allow popups and retry.") | |
| return | |
| } | |
| popupRef.current = popup | |
| if (!mountedRef.current) { | |
| try { | |
| popup.close() | |
| } catch { | |
| // best effort | |
| } | |
| return | |
| } | |
| const apiOrigin = agentaApiOrigin() | |
| let succeeded = false | |
| const onMessage = (event: MessageEvent) => { | |
| // HARD requirement: only trust the callback from the Agenta API origin. | |
| if (!apiOrigin || event.origin !== apiOrigin) return | |
| const data = event.data as { | |
| type?: unknown | |
| slug?: unknown | |
| integration?: unknown | |
| } | null | |
| if (!data || data.type !== "tools:oauth:complete") return | |
| // Several connect flows can be live at once (an agent may ask for | |
| // multiple connections in one turn). The callback tags the completion with | |
| // its connection identity, so a flow settles ONLY on its own completion — | |
| // otherwise the first finished flow would mark every open one connected. | |
| // A legacy callback without identity keeps the prior single-flow behavior. | |
| if (typeof data.slug === "string" && data.slug !== slug) return | |
| else if ( | |
| typeof data.slug !== "string" && | |
| typeof data.integration === "string" && | |
| data.integration !== integration | |
| ) | |
| return | |
| succeeded = true | |
| teardown() | |
| onSuccess() | |
| } | |
| window.addEventListener("message", onMessage) | |
| const poll = window.setInterval(() => { | |
| if (!popupRef.current?.closed || succeeded) return | |
| // Abandon: closed without a success message. | |
| teardown() | |
| if (settleParkedCall && !activeRef.current) return | |
| if (settleParkedCall) | |
| finish({connected: false, integration, slug, reason: "cancelled"}) | |
| else setPhase("idle") | |
| }, POPUP_POLL_MS) | |
| const timeout = window.setTimeout(() => { | |
| if (succeeded) return | |
| teardown() | |
| if (settleParkedCall && !activeRef.current) return | |
| if (settleParkedCall) | |
| finish({connected: false, integration, slug, reason: "timeout"}) | |
| else setPhase("idle") | |
| }, CONNECT_TIMEOUT_MS) | |
| cleanupRef.current = () => { | |
| window.removeEventListener("message", onMessage) | |
| window.clearInterval(poll) | |
| window.clearTimeout(timeout) | |
| try { | |
| popupRef.current?.close() | |
| } catch { | |
| // best effort | |
| } | |
| } |
| if (file.status === "error") { | ||
| return ( | ||
| <Tooltip title={typeof file.error === "string" ? file.error : "Upload failed"}> | ||
| <div className="absolute inset-0 flex items-center justify-center rounded-lg bg-[var(--ant-color-error-bg)] ring-1 ring-inset ring-colorError"> | ||
| {onRetry && ( | ||
| <button | ||
| type="button" | ||
| aria-label={`Retry ${file.name}`} | ||
| onClick={(e) => { | ||
| e.stopPropagation() | ||
| onRetry(file.uid) | ||
| }} | ||
| className="flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border-0 bg-colorError text-white" | ||
| > | ||
| <ArrowClockwise size={14} weight="bold" /> | ||
| </button> | ||
| )} | ||
| </div> | ||
| </Tooltip> | ||
| ) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Error overlay swallows clicks on the remove button.
StatusOverlay renders after the tile with absolute inset-0 and, unlike the uploading branch, no pointer-events-none, so a failed attachment can't be removed — only retried (and not even that when onRetry is absent). Scope pointer events to the retry control.
🐛 Proposed fix
- <div className="absolute inset-0 flex items-center justify-center rounded-lg bg-[var(--ant-color-error-bg)] ring-1 ring-inset ring-colorError">
+ <div className="pointer-events-none absolute inset-0 flex items-center justify-center rounded-lg bg-[var(--ant-color-error-bg)] ring-1 ring-inset ring-colorError">
{onRetry && (
<button
type="button"
+ className="pointer-events-auto ..."📝 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 (file.status === "error") { | |
| return ( | |
| <Tooltip title={typeof file.error === "string" ? file.error : "Upload failed"}> | |
| <div className="absolute inset-0 flex items-center justify-center rounded-lg bg-[var(--ant-color-error-bg)] ring-1 ring-inset ring-colorError"> | |
| {onRetry && ( | |
| <button | |
| type="button" | |
| aria-label={`Retry ${file.name}`} | |
| onClick={(e) => { | |
| e.stopPropagation() | |
| onRetry(file.uid) | |
| }} | |
| className="flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border-0 bg-colorError text-white" | |
| > | |
| <ArrowClockwise size={14} weight="bold" /> | |
| </button> | |
| )} | |
| </div> | |
| </Tooltip> | |
| ) | |
| } | |
| if (file.status === "error") { | |
| return ( | |
| <Tooltip title={typeof file.error === "string" ? file.error : "Upload failed"}> | |
| <div className="pointer-events-none absolute inset-0 flex items-center justify-center rounded-lg bg-[var(--ant-color-error-bg)] ring-1 ring-inset ring-colorError"> | |
| {onRetry && ( | |
| <button | |
| type="button" | |
| aria-label={`Retry ${file.name}`} | |
| onClick={(e) => { | |
| e.stopPropagation() | |
| onRetry(file.uid) | |
| }} | |
| className="pointer-events-auto flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border-0 bg-colorError text-white" | |
| > | |
| <ArrowClockwise size={14} weight="bold" /> | |
| </button> | |
| )} | |
| </div> | |
| </Tooltip> | |
| ) | |
| } |
| // Abort in-flight uploads on unmount (session switch / pane close). | ||
| useEffect(() => { | ||
| const map = controllers.current | ||
| return () => map.forEach((c) => c.abort()) | ||
| }, []) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Abort uploads when their file leaves the tray.
Cleanup only runs on unmount. If a caller removes a UID from files, its request can still complete and transmit an attachment the user withdrew. Abort and remove controllers absent from the current list.
Suggested fix
+ useEffect(() => {
+ const activeUids = new Set(files.map((file) => file.uid))
+ for (const [uid, controller] of controllers.current) {
+ if (!activeUids.has(uid)) {
+ controller.abort()
+ controllers.current.delete(uid)
+ }
+ }
+ }, [files])
+
// Abort in-flight uploads on unmount (session switch / pane close).📝 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.
| // Abort in-flight uploads on unmount (session switch / pane close). | |
| useEffect(() => { | |
| const map = controllers.current | |
| return () => map.forEach((c) => c.abort()) | |
| }, []) | |
| useEffect(() => { | |
| const activeUids = new Set(files.map((file) => file.uid)) | |
| for (const [uid, controller] of controllers.current) { | |
| if (!activeUids.has(uid)) { | |
| controller.abort() | |
| controllers.current.delete(uid) | |
| } | |
| } | |
| }, [files]) | |
| // Abort in-flight uploads on unmount (session switch / pane close). | |
| useEffect(() => { | |
| const map = controllers.current | |
| return () => map.forEach((c) => c.abort()) | |
| }, []) |
| export async function uploadMountFile({ | ||
| mountId, | ||
| destFolder, | ||
| file, | ||
| projectId, | ||
| onProgress, | ||
| signal, | ||
| }: { | ||
| mountId: string | ||
| destFolder: string | ||
| file: File | ||
| projectId?: string | null | ||
| onProgress?: (percent: number) => void | ||
| signal?: AbortSignal | ||
| }): Promise<void> { | ||
| const form = new FormData() | ||
| form.append("file", file, file.name) | ||
| const path = destFolder ? `${destFolder.replace(/\/$/, "")}/${file.name}` : file.name | ||
| await axios.post(`${getAgentaApiUrl()}/mounts/${mountId}/files/upload`, form, { | ||
| params: {path, project_id: projectId}, | ||
| signal, | ||
| onUploadProgress: (e) => { | ||
| if (onProgress && e.total) onProgress(Math.round((e.loaded / e.total) * 100)) | ||
| }, | ||
| }) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Use the mount Fern resource accessor for uploads.
Line 185 directly uses Axios, and Line 186 uses Axios-style params. Add or use the per-resource SDK upload accessor and pass query values with {queryParams: {...}}.
As per coding guidelines, “Frontend API code must use per-resource Fern client accessors from @agenta/sdk/resources, never raw axios,” and Fern query parameters must use {queryParams: {...}}.
#!/bin/bash
set -euo pipefail
ast-grep outline web/packages/agenta-sdk/src/resources.ts --items all
rg -n -i -C3 'mount.*upload|upload.*mount' web/packages/agenta-sdk/src web/oss/src| if (!firstRun || !projectURL) return | ||
| void router.replace(`${projectURL}/playground`) | ||
| }, [loading, firstRun, projectURL, router]) | ||
| }, [firstRun, projectURL, router]) | ||
|
|
||
| // Loading the agents query, or redirecting a first-run user — the shared onboarding loader, so the | ||
| // whole flow reads as one continuous "setting up" screen (never the wrong surface, never blank). | ||
| if (loading || firstRun) { | ||
| // The shared onboarding loader, so the whole flow reads as one continuous "setting up" screen. | ||
| if (resolving) { | ||
| return <OnboardingLoader /> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep first-run onboarding in the loader until the redirect target is ready.
When firstRun is true but projectURL is falsy, the effect returns and the component falls through to <AgentHome /> because only resolving controls the loader. This can leave new users on the wrong surface instead of redirecting them to the playground.
Suggested guard
- if (resolving) {
+ if (resolving || (firstRun && !projectURL)) {
return <OnboardingLoader />
}📝 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 (!firstRun || !projectURL) return | |
| void router.replace(`${projectURL}/playground`) | |
| }, [loading, firstRun, projectURL, router]) | |
| }, [firstRun, projectURL, router]) | |
| // Loading the agents query, or redirecting a first-run user — the shared onboarding loader, so the | |
| // whole flow reads as one continuous "setting up" screen (never the wrong surface, never blank). | |
| if (loading || firstRun) { | |
| // The shared onboarding loader, so the whole flow reads as one continuous "setting up" screen. | |
| if (resolving) { | |
| return <OnboardingLoader /> | |
| if (!firstRun || !projectURL) return | |
| void router.replace(`${projectURL}/playground`) | |
| }, [firstRun, projectURL, router]) | |
| // The shared onboarding loader, so the whole flow reads as one continuous "setting up" screen. | |
| if (resolving || (firstRun && !projectURL)) { | |
| return <OnboardingLoader /> |
| // Latent: GET /organizations list omits default_workspace (details-only field) — typed as-is. | ||
| const deletedWorkspaceId = | ||
| (selectedOrg as Partial<OrgDetails> | undefined)?.default_workspace?.id || null | ||
| clearWorkspaceOrgCache(deletedWorkspaceId) | ||
| clearLastUsedProjectId(deletedWorkspaceId) | ||
|
|
||
| const remainingOrgs = orgs.filter((org) => org.id !== organizationId) | ||
| if (remainingOrgs.length > 0) { | ||
| await changeSelectedOrg(remainingOrgs[0].id) | ||
| } | ||
|
|
||
| resetOrganizationData() | ||
| resetProjectData() | ||
| await refetch() |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Cache-clearing on org delete is a no-op — self-documented as "Latent."
deletedWorkspaceId is derived from selectedOrg?.default_workspace?.id, but the adjacent comment says the /organizations list response omits default_workspace (it's details-only). That means deletedWorkspaceId will always be null here, so clearWorkspaceOrgCache(null) and clearLastUsedProjectId(null) never actually target the deleted org's cached workspace — the invalidation this code was written to perform silently never happens, potentially leaving stale workspace/project cache entries after deletion.
Consider fetching org details (or otherwise sourcing the real default_workspace.id) before calling deleteOrganization, so the cache-clear call receives the actual workspace id instead of always null.
#!/bin/bash
# Check whether an org-details fetch (with default_workspace) is available/used elsewhere for this purpose.
rg -n 'default_workspace' -C3 web/oss/src/lib/Types.ts web/oss/src/services/organization/api.ts web/oss/src/state/org 2>/dev/null| @@ -108,7 +110,7 @@ export interface ColumnGroup { | |||
| /** Stable cache key for this group — useful for grouping in renderers. */ | |||
| key: string | |||
| /** The step.references that drove the grouping (preserved for downstream code). */ | |||
| refs: Record<string, {id?: string; slug?: string} | null | undefined> | null | |||
| refs: Record<string, EvaluationRunStepReference | null | undefined> | null | |||
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Keep ColumnGroup.refs compatible with slug-only references.
EvaluationRunStepReference requires id, but the previous ColumnGroup.refs element type allowed id?. This narrows the contract and can reject valid grouped references or force unsafe casts. Use a group-specific type with optional id, or normalize references before assigning them.
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 17
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
web/oss/src/components/AgentChatSlice/components/clientTools/ConnectToolWidget.tsx (1)
63-108: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winShow manual-retry errors before the settled-result branch.
A manual retry happens after
meta.settledis already true, so Line 64 returns the generic “Connection not completed” chip before Line 99 can rendererrorText. Move thephase === "error"branch above the settled check.Proposed fix
+ if (phase === "error") { + return ( + <ChipRow icon={<Warning size={13} weight="fill" className="text-colorError" />}> + <Text type="danger" className="!text-xs truncate" title={errorText ?? undefined}> + {errorText ?? "Connection failed."} + </Text> + <RetryButton onClick={() => runConnect(false)} /> + </ChipRow> + ) + } + if (meta.settled || outcome) {web/oss/src/components/pages/observability/components/StatusRenderer.tsx (1)
5-18: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDo not render unset statuses as
success.When
statusis null or undefined, the renderer passes"STATUS_CODE_UNSET", butstatusMapperfalls through to its success mapping for every non-error value. Add an explicit neutral/unset mapping and reserve success for an actual success status.Also applies to: 28-34
🟡 Minor comments (9)
.agents/skills/update-api-docs/SKILL.md-68-68 (1)
68-68: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winRenumber the verification step.
The workflow jumps from Step 2 to Step 5. Rename this to Step 3 or restore the missing steps so the instructions are sequential.
.agents/skills/create-changelog-announcement/SKILL.md-311-317 (1)
311-317: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse a Discussion-specific close flow.
githubUrlcan point to a GitHub discussion, butgh issue closetargets issues/PRs. Add a check for the discussion path and direct CLI users to the GitHub Discussions interface or a GraphQLcloseDiscussioncall instead..agents/skills/write-social-announcement/SKILL.md-14-21 (1)
14-21: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winRemove the contradictory “key benefit” wording.
The skill forbids
keyas an adjective but later requires a “key benefit.” Use “main benefit” or “user benefit” instead.Also applies to: 137-143
web/oss/src/pages/w/[workspace_id]/p/[project_id]/settings/index.tsx-121-138 (1)
121-138: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winRaw
--ag-c-*CSS variable literal for the demo badge.
bg-[var(--ag-c-0517290F)]uses a raw--ag-c-*literal instead of a supported theme token. As per coding guidelines,web/**/*.{ts,tsx}files must "Consume theme colors through Ant Design semantic tokens, Tailwind color utilities, or supportedvar(--ag-color*)variables; do not use raw hex colors or--ag-c-*literals." This also risks not adapting correctly between light/dark themes.#!/bin/bash # Check whether --ag-c-0517290F (or similar --ag-c- literals) are used elsewhere / have a sanctioned semantic token equivalent. rg -n '\-\-ag-c-' --type=ts --type=tsx --type=css -g '!**/theme-variables.css' 2>/dev/nullSource: Coding guidelines
docs/design/simplify-nav-new-users/context.md-89-90 (1)
89-90: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winBroken list formatting for criteria 6–8.
Items 6, 7, 8 and the "Phase 2" heading are run together on two lines with no breaks, unlike items 1–5 which are each on their own line. This will render as a run-on paragraph instead of a continued numbered list.
📝 Proposed fix
-**Phase 2** 6. The Settings → Feature flags switch flips the effective mode and survives a -reload. 7. Toggling the switch updates the sidebar without a full page reload. 8. Phase 1's sidebar behavior is unchanged when no override is set. +**Phase 2** + +6. The Settings → Feature flags switch flips the effective mode and survives a reload. +7. Toggling the switch updates the sidebar without a full page reload. +8. Phase 1's sidebar behavior is unchanged when no override is set.web/oss/src/components/Drives/useDriveDrop.ts-58-87 (1)
58-87: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
dragleavedecrements for non-file drags and the spring timer outlives unmount.Two small asymmetries in this effect:
onEnteronly counts file drags, butonLeavedecrements on everydragleave, so a text drag crossing the window can zero the depth and cleardraggingwhile a file drag is still active.- The cleanup removes listeners but never calls
clearSpring(), so a pending 700ms spring-load firesonNavigateafter unmount.🛡️ Proposed fix
- const onLeave = () => { + const onLeave = (e: DragEvent) => { + if (!has(e)) return depth.current = Math.max(0, depth.current - 1) if (depth.current === 0) setDragging(false) } @@ return () => { window.removeEventListener("dragenter", onEnter) window.removeEventListener("dragleave", onLeave) window.removeEventListener("drop", onEnd) window.removeEventListener("dragend", onEnd) + clearSpring() }web/oss/src/components/AgentChatSlice/components/RecordingBar.tsx-66-88 (1)
66-88: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winLive region wraps a per-second clock, so AT re-announces every tick.
role="status" aria-live="polite"on the container includesmmss(seconds), which changes once a second. Keep the live region for state changes only and mark the timer text asaria-hidden(or movearia-liveto a dedicated node).web/oss/src/components/AgentChatSlice/hooks/useAudioRecorder.ts-138-146 (1)
138-146: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winRe-entrancy gap while permission is pending.
recRef.currentis stillnullduring"requesting", so a secondstart()before the prompt resolves issues anothergetUserMedia; the first resolved stream then leaks (only the last one is stored instreamRef). Callers currently disable the button onpending, but the hook shouldn't rely on that.🛡️ Proposed guard
const start = useCallback(() => { - if (!supported || recRef.current) return + if (!supported || recRef.current || startingRef.current) return + startingRef.current = true setError(null)Reset
startingRef.current = falsein both the.thenand.catchpaths (and inteardown).web/oss/src/components/Drives/driveFileSource.tsx-42-49 (1)
42-49: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winKey local preview state by the selected file. Switching between staged files can reuse state from the prior file: a prior decode error makes the next media file fail immediately, and prior text can render while the next file is loading.
web/oss/src/components/Drives/driveFileSource.tsx#L42-L49: store the failed object URL/path rather than a shared boolean, or reset failure state when the local source changes.web/oss/src/components/Drives/driveFileSource.tsx#L72-L85: clear text for a new local source and ignore completions from superseded reads.
🧹 Nitpick comments (11)
web/oss/src/lib/helpers/dynamicEnv.ts (1)
36-48: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueKeep these feature-flag comments to one line.
Collapse the UI behavior descriptions into concise constraints; place extended rollout details in documentation. 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.”
Source: Coding guidelines
web/packages/agenta-shared/package.json (1)
32-32: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win
peerDependencies.axiosfloor not bumped to match the sibling packages.
dependencies.axioswas bumped to1.18.0here, butpeerDependencies.axios(Line 49) is still>=1.13.5 <2.0.0— unlikeagenta-entitiesandagenta-entity-ui, whose peer floors were both raised to>=1.18.0in this same change. A consumer could satisfy this package's declared peer contract with a much older, vulnerable axios. The workspace-levelpnpm.overrides.axios: "1.18.0"masks the practical impact today, but the manifest is inconsistent with the rest of this bump.♻️ Proposed fix
"peerDependencies": { "`@tanstack/react-query`": ">=5.0.0", - "axios": ">=1.13.5 <2.0.0", + "axios": ">=1.18.0 <2.0.0", "jotai": ">=2.0.0",Also applies to: 47-53
web/oss/next.config.ts (1)
139-144: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd a production regression check for
refractor/all.The fix depends on webpack preserving
react-syntax-highlighter’s dynamicrefractor/allimport. Add a production build/render test for a PrismAsync code block so this IgnorePlugin rule cannot regress silently.web/oss/src/components/AgentChatSlice/AgentConversation.tsx (1)
1722-1749: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated attachment-key formula between
toUploadFileanduploads.enqueue.The uid string
${name}-${lastModified}-${size}is built twice; ifuseAttachmentUploadsever keys items differently, enqueue silently matches nothing. Extract one helper (attachmentUid(file)) used by both.♻️ Suggested extraction
+const attachmentUid = (file: File) => `${file.name}-${file.lastModified}-${file.size}` + const toUploadFile = (file: File): UploadFile => ({ - uid: `${file.name}-${file.lastModified}-${file.size}`, + uid: attachmentUid(file), name: file.name, status: "done", originFileObj: file as UploadFile["originFileObj"], }) @@ - uploads.enqueue(accepted.map((f) => `${f.name}-${f.lastModified}-${f.size}`)) + uploads.enqueue(accepted.map(attachmentUid))web/oss/src/components/Drives/DriveExplorer.tsx (2)
1866-1885: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winIndex-based staged ids reshuffle on removal.
id: ${i}-${f.name}changes for every item after a removal, so the synthetic tile keys (__staged__/${id}) all change and the surviving tiles remount (losing their fade/scale state).fileKey(f)is already imported and is stable per file.♻️ Proposed change
staged.map((f, i) => ({ - id: `${i}-${f.name}`, + id: fileKey(f), name: f.name,
1727-1733: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDouble cast hides the shape mismatch.
{path, size} as unknown as MountFilesilences the compiler for injected upload nodes. IfMountFilehas additional required fields, prefer a narrowed helper type (e.g.Pick<MountFile, "path" | "size">widened at thebuildDriveTreeboundary) or make those fields optional, so a future field addition surfaces here.web/oss/src/components/Drives/useDriveDrop.ts (1)
170-194: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
useStageDrophighlight flickers across child elements.Unlike
useDriveDrop, this has no depth counter:onDragLeavefires when the cursor crosses any child of the rail/aside, sodropActivetoggles off mid-drag. A depth ref (or checkinge.currentTarget.contains(e.relatedTarget as Node)) keeps the tint steady.web/oss/src/components/AgentChatSlice/components/AudioPlayer.tsx (1)
28-34: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueReset
duration/currenton src change.The src effect resets
probingRefbut leaves the previous clip'sduration/currentin state, so the timer and progress bar briefly show the old clip's values (and a wrong ratio) untilloadedmetadatafires.♻️ Proposed tweak
probingRef.current = false + setDuration(0) + setCurrent(0)web/oss/src/components/AgentChatSlice/components/RecordingBar.tsx (1)
38-48: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valuerAF loop for a once-per-second clock.
The frame loop runs ~60×/s to publish a value that changes 1×/s. An interval (e.g. 250 ms) derived from
startedAtRefkeeps the no-drift property at a fraction of the wakeups.web/oss/src/components/AgentChatSlice/components/RecordingWaveform.tsx (1)
25-29: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value
parseRgbassumes anrgb()/rgba()computed color.If the theme token resolves to a modern color space (
oklch(...),color(srgb …)), the digit scan yields nonsense channels or the[0,0,0]fallback — an invisible waveform in dark mode. Consider falling back tocurrentColorfill with per-stopglobalAlphainstead of reconstructing channels.Also applies to: 53-66
web/packages/agenta-ui/src/RichChatInput/ComposerSendButton.tsx (1)
34-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace raw hex colors with semantic theme tokens.
The
#191a0dand#b8cb3fvalues will not adapt with the supported theme system. Use Ant semantic tokens, Tailwind semantic utilities, or supported--ag-color*variables instead.As per coding guidelines, “Consume theme colors through Ant Design semantic tokens, Tailwind color utilities, or supported
var(--ag-color*)variables; do not use raw hex colors.”Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 03599dc8-8972-4f8a-a39e-60b25ef11bbc
⛔ Files ignored due to path filters (6)
api/uv.lockis excluded by!**/*.lockclients/python/uv.lockis excluded by!**/*.locksdks/python/uv.lockis excluded by!**/*.lockservices/runner/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlservices/uv.lockis excluded by!**/*.lockweb/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (246)
.agents/skills/add-announcement/SKILL.md.agents/skills/create-changelog-announcement/SKILL.md.agents/skills/implement-feature/SKILL.md.agents/skills/plan-feature/SKILL.md.agents/skills/style-editing/SKILL.md.agents/skills/update-api-docs/SKILL.md.agents/skills/update-llm-model-list/SKILL.md.agents/skills/write-issue/SKILL.md.agents/skills/write-social-announcement/SKILL.md.claude/skills/add-announcement.claude/skills/create-changelog-announcement.claude/skills/implement-feature.claude/skills/plan-feature.claude/skills/style-editing.claude/skills/sync-model-catalog.claude/skills/update-api-docs.claude/skills/update-llm-model-list.claude/skills/write-issue.claude/skills/write-social-announcement.gitignore.gitleaksignoreapi/entrypoints/routers.pyapi/oss/src/core/mounts/service.pyapi/oss/src/middlewares/prefix.pyapi/oss/tests/pytest/unit/middlewares/test_prefix.pyapi/oss/tests/pytest/unit/test_mounts_file_ops.pyapi/pyproject.tomlclients/python/pyproject.tomldocs/design/simplify-nav-new-users/README.mddocs/design/simplify-nav-new-users/context.mddocs/design/simplify-nav-new-users/plan.mddocs/design/simplify-nav-new-users/research.mddocs/design/simplify-nav-new-users/status.mdhosting/kubernetes/helm/Chart.yamlsdks/python/pyproject.tomlservices/pyproject.tomlservices/runner/package.jsonservices/runner/src/engines/sandbox_agent/run-limits.tsservices/runner/src/env.tsservices/runner/src/sessions/persist.tsservices/runner/src/sessions/records-query.tsservices/runner/tests/unit/env-num.test.tsservices/runner/tests/unit/run-limits.test.tsservices/runner/tests/unit/session-persist.test.tsservices/runner/tests/unit/session-records-query.test.tsweb/ee/css-modules.d.tsweb/ee/package.jsonweb/ee/tsconfig.jsonweb/oss/css-modules.d.tsweb/oss/next.config.tsweb/oss/package.jsonweb/oss/src/components/AgentChatSlice/AgentConversation.tsxweb/oss/src/components/AgentChatSlice/assets/attachments.tsweb/oss/src/components/AgentChatSlice/assets/constants.tsweb/oss/src/components/AgentChatSlice/assets/markdown.tsxweb/oss/src/components/AgentChatSlice/components/AgentMessage.tsxweb/oss/src/components/AgentChatSlice/components/AttachmentViewerDrawer.tsxweb/oss/src/components/AgentChatSlice/components/AudioPlayer.tsxweb/oss/src/components/AgentChatSlice/components/ComposerAttachments.tsxweb/oss/src/components/AgentChatSlice/components/ConnectModelBanner.tsxweb/oss/src/components/AgentChatSlice/components/Inspector/InspectSessionButton.tsxweb/oss/src/components/AgentChatSlice/components/InteractionDock.tsxweb/oss/src/components/AgentChatSlice/components/MicPermissionNotice.tsxweb/oss/src/components/AgentChatSlice/components/QueuedMessages.tsxweb/oss/src/components/AgentChatSlice/components/RecordingBar.tsxweb/oss/src/components/AgentChatSlice/components/RecordingWaveform.tsxweb/oss/src/components/AgentChatSlice/components/ToolActivity.tsxweb/oss/src/components/AgentChatSlice/components/VoiceInputButton.tsxweb/oss/src/components/AgentChatSlice/components/clientTools/ConnectToolWidget.tsxweb/oss/src/components/AgentChatSlice/components/clientTools/useConnectFlow.tsweb/oss/src/components/AgentChatSlice/hooks/useAttachmentUploads.tsweb/oss/src/components/AgentChatSlice/hooks/useAudioRecorder.tsweb/oss/src/components/AgentChatSlice/hooks/useVoiceInput.tsweb/oss/src/components/Drives/ContextRail.tsxweb/oss/src/components/Drives/DriveExplorer.tsxweb/oss/src/components/Drives/FilesDrawer.tsxweb/oss/src/components/Drives/SessionFilesDrawer.tsxweb/oss/src/components/Drives/StorageFilesHeader.tsxweb/oss/src/components/Drives/StorageSection.tsxweb/oss/src/components/Drives/VirtualTileGrid.tsxweb/oss/src/components/Drives/configDrive.tsweb/oss/src/components/Drives/driveFileSource.tsxweb/oss/src/components/Drives/driveMedia.tsweb/oss/src/components/Drives/renderers.tsxweb/oss/src/components/Drives/useDriveDrop.tsweb/oss/src/components/Drives/useImagePreviews.tsweb/oss/src/components/Drives/useMountUpload.tsweb/oss/src/components/Drives/useSessionDrive.tsweb/oss/src/components/EvalRunDetails/Table.tsxweb/oss/src/components/EvalRunDetails/atoms/table/run.tsweb/oss/src/components/EvalRunDetails/atoms/traces.tsweb/oss/src/components/EvalRunDetails/components/CompareRunsMenu.tsxweb/oss/src/components/EvalRunDetails/components/EvalTestcaseDrawerAdapter/index.tsxweb/oss/src/components/EvalRunDetails/components/FocusDrawerHeader.tsxweb/oss/src/components/EvalRunDetails/components/FocusDrawerSidePanel.tsxweb/oss/src/components/EvalRunDetails/components/columnVisibility/ColumnVisibilityPopoverContent.tsxweb/oss/src/components/EvalRunDetails/components/views/SingleScenarioViewerPOC/ScenarioNavigator.tsxweb/oss/src/components/EvalRunDetails/components/views/SingleScenarioViewerPOC/index.tsxweb/oss/src/components/EvalRunDetails/evaluationPreviewTableStore.tsweb/oss/src/components/EvalRunDetails/hooks/useCellVisibility.tsweb/oss/src/components/EvalRunDetails/hooks/usePreviewColumns.tsxweb/oss/src/components/EvalRunDetails/utils/buildPreviewColumns.tsxweb/oss/src/components/EvaluationRunsTablePOC/atoms/fetchAutoEvaluationRuns.tsweb/oss/src/components/EvaluationRunsTablePOC/atoms/tableStore.tsweb/oss/src/components/EvaluationRunsTablePOC/components/EvaluationRunsTable/index.tsxweb/oss/src/components/EvaluationRunsTablePOC/components/EvaluationRunsTable/types.tsweb/oss/src/components/EvaluationRunsTablePOC/components/columnVisibility/ColumnVisibilityPopoverContent.tsxweb/oss/src/components/EvaluationRunsTablePOC/components/filters/EvaluationRunsHeaderFilters.tsxweb/oss/src/components/EvaluationRunsTablePOC/hooks/useEvaluationRunsColumns/index.tsxweb/oss/src/components/EvaluationRunsTablePOC/hooks/useEvaluationRunsColumns/utils.tsxweb/oss/src/components/EvaluationRunsTablePOC/hooks/useEvaluatorHeaderReference.tsweb/oss/src/components/EvaluationRunsTablePOC/types.tsweb/oss/src/components/InfiniteVirtualTable/InfiniteVirtualTable.tsxweb/oss/src/components/InfiniteVirtualTable/atoms/columnHiddenKeys.tsweb/oss/src/components/InfiniteVirtualTable/atoms/columnVisibility.tsweb/oss/src/components/InfiniteVirtualTable/atoms/columnWidths.tsweb/oss/src/components/InfiniteVirtualTable/columns/cells.tsxweb/oss/src/components/InfiniteVirtualTable/columns/createStandardColumns.tsxweb/oss/src/components/InfiniteVirtualTable/columns/createTableColumns.tsweb/oss/src/components/InfiniteVirtualTable/columns/types.tsweb/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityHeader.tsxweb/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityTrigger.tsxweb/oss/src/components/InfiniteVirtualTable/components/InfiniteVirtualTableInner.tsxweb/oss/src/components/InfiniteVirtualTable/components/TableDescription.tsxweb/oss/src/components/InfiniteVirtualTable/components/TableShell.tsxweb/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityMenuTrigger.tsxweb/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityPopoverContent.tsxweb/oss/src/components/InfiniteVirtualTable/components/columnVisibility/TableSettingsDropdown.tsxweb/oss/src/components/InfiniteVirtualTable/components/filters/FiltersPopoverTrigger.tsxweb/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityContext.tsweb/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityFlagContext.tsxweb/oss/src/components/InfiniteVirtualTable/context/VirtualTableScrollContainerContext.tsweb/oss/src/components/InfiniteVirtualTable/createInfiniteDatasetStore.tsweb/oss/src/components/InfiniteVirtualTable/createInfiniteTableStore.tsweb/oss/src/components/InfiniteVirtualTable/features/InfiniteVirtualTableFeatureShell.tsxweb/oss/src/components/InfiniteVirtualTable/features/index.tsweb/oss/src/components/InfiniteVirtualTable/features/useInfiniteTableFeaturePagination.tsweb/oss/src/components/InfiniteVirtualTable/helpers/createSimpleTableStore.tsweb/oss/src/components/InfiniteVirtualTable/helpers/createTableRowHelpers.tsweb/oss/src/components/InfiniteVirtualTable/helpers/index.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useColumnDomRefs.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibility.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibilityControls.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useContainerResize.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useContainerSize.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useEditableTable.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useExpandableRows.tsxweb/oss/src/components/InfiniteVirtualTable/hooks/useHeaderViewportVisibility.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteScroll.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteTablePagination.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useResizableColumns.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useRowHeight.tsxweb/oss/src/components/InfiniteVirtualTable/hooks/useScopedColumnVisibility.tsxweb/oss/src/components/InfiniteVirtualTable/hooks/useScrollConfig.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useScrollContainer.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useSmartResizableColumns.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useTableActions.tsxweb/oss/src/components/InfiniteVirtualTable/hooks/useTableExport.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useTableHeaderHeight.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useTableKeyboardShortcuts.tsweb/oss/src/components/InfiniteVirtualTable/hooks/useTableManager.tsxweb/oss/src/components/InfiniteVirtualTable/hooks/useTableRowSelection.tsweb/oss/src/components/InfiniteVirtualTable/index.tsweb/oss/src/components/InfiniteVirtualTable/providers/ColumnVisibilityProvider.tsxweb/oss/src/components/InfiniteVirtualTable/providers/InfiniteVirtualTableStoreProvider.tsxweb/oss/src/components/InfiniteVirtualTable/types.tsweb/oss/src/components/InfiniteVirtualTable/utils/columnUtils.tsweb/oss/src/components/Playground/Components/TestsetDropdown/TestsetPreviewPanelWrapper.tsxweb/oss/src/components/SessionInspector/PanelSessionInspectorButton.tsxweb/oss/src/components/SharedDrawers/AddToTestsetDrawer/components/PreviewSection.tsxweb/oss/src/components/SharedDrawers/SessionDrawer/components/SessionDrawerButton/index.tsxweb/oss/src/components/SharedDrawers/SessionDrawer/store/sessionDrawerStore.tsweb/oss/src/components/SharedDrawers/SessionDrawer/types.tsweb/oss/src/components/SharedDrawers/TraceDrawer/components/TraceContent/components/OverviewTabItem/index.tsxweb/oss/src/components/SharedDrawers/TraceDrawer/components/TraceTree/assets/spanVisibility.tsweb/oss/src/components/SharedDrawers/TraceDrawer/components/TraceTree/index.tsxweb/oss/src/components/SharedDrawers/TraceDrawer/store/traceDrawerStore.tsweb/oss/src/components/Sidebar/dynamic/registry.tsweb/oss/src/components/Sidebar/hooks/useSidebarConfig/index.tsxweb/oss/src/components/Sidebar/scopes/settingsScope.tsxweb/oss/src/components/TestcasesTableNew/components/TestcaseHeader.tsxweb/oss/src/components/TestcasesTableNew/components/TestcasesTableShell.tsxweb/oss/src/components/TestcasesTableNew/index.tsxweb/oss/src/components/TestcasesTableNew/state/rowHeight.tsweb/oss/src/components/TestsetsTable/TestsetsTable.tsxweb/oss/src/components/TestsetsTable/assets/createTestsetsColumns.tsxweb/oss/src/components/TestsetsTable/atoms/fetchTestsets.tsweb/oss/src/components/TestsetsTable/components/TestsetsHeaderFilters.tsxweb/oss/src/components/pages/agent-home/OnboardingEntry.tsxweb/oss/src/components/pages/agent-home/hooks/useCreateAgent.tsweb/oss/src/components/pages/agents/AgentsPage.tsxweb/oss/src/components/pages/agents/store.tsweb/oss/src/components/pages/observability/assets/constants.tsweb/oss/src/components/pages/observability/assets/exportUtils.tsweb/oss/src/components/pages/observability/assets/getObservabilityColumns.tsxweb/oss/src/components/pages/observability/components/AvatarTreeContent.tsxweb/oss/src/components/pages/observability/components/NodeNameCell.tsxweb/oss/src/components/pages/observability/components/SessionsTable/assets/getSessionColumns.tsxweb/oss/src/components/pages/observability/components/StatusRenderer.tsxweb/oss/src/components/pages/observability/components/TimestampCell.tsxweb/oss/src/components/pages/prompts/PromptsPage.tsxweb/oss/src/components/pages/settings/FeatureFlags/FeatureFlags.tsxweb/oss/src/components/pages/settings/Organization/General.tsxweb/oss/src/components/pages/settings/assets/navigation.test.tsweb/oss/src/components/pages/settings/assets/navigation.tsweb/oss/src/hooks/usePostAuthRedirect.tsweb/oss/src/lib/helpers/dynamicEnv.tsweb/oss/src/lib/hooks/usePreviewEvaluations/index.tsweb/oss/src/lib/hooks/usePreviewEvaluations/types.tsweb/oss/src/lib/onboarding/atoms.tsweb/oss/src/lib/onboarding/index.tsweb/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/[app_id]/index.tsxweb/oss/src/pages/w/[workspace_id]/p/[project_id]/settings/index.tsxweb/oss/src/services/tracing/types/index.tsweb/oss/src/state/app/atoms/fetcher.tsweb/oss/src/state/entities/shared/README.mdweb/oss/src/state/entities/shared/createPaginatedEntityStore.tsweb/oss/src/state/entities/testcase/paginatedStore.tsweb/oss/src/state/entities/testset/paginatedStore.tsweb/oss/src/state/newObservability/atoms/queries.tsweb/oss/src/state/onboarding/selectors.tsweb/oss/src/state/settings/featureFlags.tsweb/oss/tsconfig.jsonweb/package.jsonweb/packages/agenta-api-client/package.jsonweb/packages/agenta-entities/package.jsonweb/packages/agenta-entities/src/evaluationRun/core/schema.tsweb/packages/agenta-entities/src/evaluationRun/etl/resolveMappings.tsweb/packages/agenta-entities/src/trace/etl/exportMatchingTraces.tsweb/packages/agenta-entities/src/workflow/index.tsweb/packages/agenta-entities/src/workflow/state/index.tsweb/packages/agenta-entities/src/workflow/state/inspectMeta.tsweb/packages/agenta-entities/tests/unit/export-matching-traces.test.tsweb/packages/agenta-entity-ui/package.jsonweb/packages/agenta-playground/src/index.tsweb/packages/agenta-playground/src/state/execution/index.tsweb/packages/agenta-playground/src/state/index.tsweb/packages/agenta-shared/package.jsonweb/packages/agenta-ui/src/InfiniteVirtualTable/index.tsweb/packages/agenta-ui/src/InfiniteVirtualTable/types.tsweb/packages/agenta-ui/src/RichChatInput/ComposerSendButton.tsxweb/packages/agenta-ui/src/RichChatInput/RichChatInput.tsxweb/packages/agenta-ui/src/RichChatInput/index.tsweb/packages/agenta-ui/src/RichChatInput/plugins/SendButton.tsxweb/packages/agenta-ui/src/RichChatInput/plugins/dictation.tsweb/tests/package.json
💤 Files with no reviewable changes (57)
- web/oss/src/components/InfiniteVirtualTable/hooks/useEditableTable.ts
- web/oss/src/components/SharedDrawers/SessionDrawer/types.ts
- web/oss/src/components/InfiniteVirtualTable/features/useInfiniteTableFeaturePagination.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useColumnDomRefs.ts
- web/oss/src/components/InfiniteVirtualTable/columns/cells.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useContainerSize.ts
- web/oss/src/components/InfiniteVirtualTable/helpers/index.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useScopedColumnVisibility.tsx
- web/oss/src/components/InfiniteVirtualTable/components/TableDescription.tsx
- web/oss/src/components/InfiniteVirtualTable/columns/types.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableRowSelection.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useContainerResize.ts
- web/oss/src/components/InfiniteVirtualTable/atoms/columnWidths.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteScroll.ts
- web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/TableSettingsDropdown.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useSmartResizableColumns.ts
- web/oss/src/components/InfiniteVirtualTable/providers/ColumnVisibilityProvider.tsx
- web/oss/src/components/InfiniteVirtualTable/helpers/createSimpleTableStore.ts
- web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityMenuTrigger.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableKeyboardShortcuts.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableHeaderHeight.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibilityControls.ts
- web/oss/src/components/InfiniteVirtualTable/helpers/createTableRowHelpers.ts
- web/oss/src/components/InfiniteVirtualTable/InfiniteVirtualTable.tsx
- web/oss/src/components/SharedDrawers/SessionDrawer/components/SessionDrawerButton/index.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableManager.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useScrollContainer.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useHeaderViewportVisibility.ts
- web/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityHeader.tsx
- web/oss/src/components/InfiniteVirtualTable/columns/createTableColumns.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useResizableColumns.ts
- web/oss/src/components/InfiniteVirtualTable/components/TableShell.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useScrollConfig.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableActions.tsx
- web/oss/src/components/InfiniteVirtualTable/createInfiniteDatasetStore.ts
- web/oss/src/components/InfiniteVirtualTable/context/VirtualTableScrollContainerContext.ts
- web/oss/src/components/InfiniteVirtualTable/atoms/columnHiddenKeys.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteTablePagination.ts
- web/oss/src/components/InfiniteVirtualTable/atoms/columnVisibility.ts
- web/oss/src/components/InfiniteVirtualTable/components/filters/FiltersPopoverTrigger.tsx
- web/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityTrigger.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibility.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useRowHeight.tsx
- web/oss/src/components/InfiniteVirtualTable/features/index.ts
- web/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityContext.ts
- web/oss/src/components/InfiniteVirtualTable/utils/columnUtils.ts
- web/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityFlagContext.tsx
- web/oss/src/components/InfiniteVirtualTable/components/InfiniteVirtualTableInner.tsx
- web/oss/src/components/InfiniteVirtualTable/createInfiniteTableStore.ts
- web/oss/src/components/InfiniteVirtualTable/columns/createStandardColumns.tsx
- web/oss/src/components/InfiniteVirtualTable/hooks/useExpandableRows.tsx
- web/oss/src/components/InfiniteVirtualTable/index.ts
- web/oss/src/components/InfiniteVirtualTable/hooks/useTableExport.ts
- web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityPopoverContent.tsx
- web/oss/src/components/InfiniteVirtualTable/providers/InfiniteVirtualTableStoreProvider.tsx
- web/oss/src/components/InfiniteVirtualTable/types.ts
- web/oss/src/components/InfiniteVirtualTable/features/InfiniteVirtualTableFeatureShell.tsx
🛑 Comments failed to post (5)
.agents/skills/add-announcement/SKILL.md (1)
219-222: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Use GitButler commands in the example workflow.
Raw
git add/git commitcan commit unassigned changes from another lane. Replace them with lane-scopedbut rubandbut commit <lane> --onlycommands.As per coding guidelines, when GitButler workspace mode is active, use the
butCLI instead of rawgit commit.Source: Coding guidelines
.agents/skills/create-changelog-announcement/SKILL.md (2)
272-285: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep the sidebar example valid JSON.
The fenced block is labeled
jsonbut contains// ... existing entries, which makeschangelog.jsoninvalid. Remove the comment or use a valid JSON example.
370-386: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Include the required summary structure in the canonical example.
The detailed-entry example starts directly with
## Overviewand omits both<Summary>...</Summary>and{/* truncate */}, despite the earlier instructions making them required. Copying this example can produce an incomplete changelog index entry..agents/skills/update-llm-model-list/SKILL.md (2)
35-46: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Make the “no local install needed” checker executable.
uvx --with litellmprovides LiteLLM, but the script importsagenta.sdk.utils.assetsfrom/tmp. In a clean checkout, the Agenta package is neither installed nor onsys.path, so the command fails before checking any model. Install the local package explicitly, set the required source path and dependencies, or make the script consume a serialized model list.Also applies to: 70-70
50-53: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Restrict prefix stripping to the documented providers.
exists()strips any prefix containing/, so agemini/invalid-nameentry can pass ifinvalid-nameexists in LiteLLM. Only strip the Anthropic and Cohere prefixes described in the rules; otherwise require the exact provider-prefixed key.Suggested fix
def exists(m): if m in mc: return True - if "/" in m and m.split("/", 1)[1] in mc: return True + if m.startswith(("anthropic/", "cohere/")): + return m.split("/", 1)[1] in mc return False📝 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.def exists(m): if m in mc: return True if m.startswith(("anthropic/", "cohere/")): return m.split("/", 1)[1] in mc return False
Why
The committed agent skills were split across two conventions. Six lived in
.agents/skills(the tool-agnostic location that AGENTS.md documents), while five others (add-announcement,create-changelog-announcement,update-api-docs,update-llm-model-list,write-social-announcement) lived only in.claude/skills, so only Claude Code could discover them.sync-model-cataloghad no.claudesymlink at all. On top of that, the.gitignoreallowlist only named 2 of the 11 tracked skills; the other 9 stayed tracked by accident of history, which madegit addon them behave confusingly for anyone without the files already in their index.What changed
.agents/skills/<name>/with a symlink at.claude/skills/<name>. The five.claude-native skills moved (git tracks them as 100% renames, history preserved), andsync-model-cataloggot its missing symlink.write-issue,implement-feature,plan-feature, andstyle-editing. They were scanned for machine-specific details (IPs, hostnames, credentials) before publishing; each is a single self-containedSKILL.md..gitignoreallowlist now matches reality: all 15 committed skills are explicitly un-ignored in both directories, so the ignore rules describe exactly what is tracked. Everything else under both skill directories stays local-only by default, as before.Verification
git diff --name-status main..chore/skills-repo-synccontains exactly the 5 renames, 4 new skills, 10 new symlinks, and the.gitignoreedit; nothing else..claude/skills/<name>/SKILL.mdpaths resolve through their symlinks on disk.git check-ignoreconfirms no committed path is ignored and the local-only skills remain ignored.https://claude.ai/code/session_01KFiHhzfjyGZ95fU7FGw5Mb