Skip to content

fix(hosting): runner containers leak zombie processes; run them under an init - #5557

Merged
mmabrouk merged 1 commit into
release/v0.106.1from
fix/runner-container-init
Jul 29, 2026
Merged

fix(hosting): runner containers leak zombie processes; run them under an init#5557
mmabrouk merged 1 commit into
release/v0.106.1from
fix/runner-container-init

Conversation

@mmabrouk

Copy link
Copy Markdown
Member

Context

On a long-running dev deployment, one runner container had accumulated 3,010 zombie processes after 4 days of uptime; two other runner containers on the same box had 292 and 79. Zombies hold PID/task slots, so a busy runner eventually exhausts process limits.

The root cause is that the runner (a Node/tsx server) runs as PID 1 inside its container. Node reaps its own direct children, but harness runs spawn process trees, and when an intermediate process exits, its orphaned children re-parent to PID 1. Node never reaps those, so every orphaned grandchild stays a zombie forever. The compose files set init: true for other services (worker-streams, worker-queues, cron, the gh api), but the runner block never got it.

Changes

Adds init: true to the runner service in every compose file that defines it:

  • oss/docker-compose.dev.yml, ee/docker-compose.dev.yml
  • oss/docker-compose.gh.yml, ee/docker-compose.gh.yml
  • oss/docker-compose.gh.local.yml, ee/docker-compose.gh.local.yml
  • oss/docker-compose.gh.ssl.yml

With the flag, Docker starts docker-init (tini) as PID 1. It reaps orphaned children and forwards signals; the runner process becomes its child. Same pattern the other services in these files already use, placed in the same # === EXECUTION === # section.

This is a runtime flag only. No image rebuild is needed; it takes effect the next time the container is recreated.

Tests

  • docker compose -f <file> config for all 7 files: each renders the runner service with init: true.
  • Mechanism check with the existing runner image:
    • docker run --init --rm --entrypoint cat <runner-image> /proc/1/comm prints docker-init.
    • The same command without --init shows the container command itself as PID 1 (no reaper).
  • ee/docker-compose.dev.harness.local.yml (a local-only overlay that only patches runner env/volumes) is untouched; it inherits init from the base file.

What to QA

  • Recreate the runner (run.sh ... --recreate runner) and run a few agent executions. ps aux | grep defunct inside the runner container should stay near zero instead of growing with every run.
  • Regression: agent runs still stream and complete; stopping the container still shuts down cleanly (docker-init forwards SIGTERM).

https://claude.ai/code/session_01BhfwcD7KTfPE1FZpdCqJ19

@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Jul 29, 2026
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jul 29, 2026 6:58pm

Request Review

@dosubot dosubot Bot added the Bug Report Something isn't working label Jul 29, 2026
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request updates API routing and archive packaging, hardens runner timers and persistence, adds agent chat voice/attachment workflows, introduces drive uploads and local previews, simplifies navigation, migrates table and tracing contracts, and bumps release metadata and dependencies.

Changes

Platform and runtime

Layer / File(s) Summary
Routing and archive namespace handling
api/entrypoints/routers.py, api/oss/src/middlewares/*, api/oss/src/core/mounts/service.py, api/oss/tests/pytest/unit/*
Trailing-slash redirects are disabled, API path normalization becomes route-aware, and ZIP archive members are filtered for reserved names, collisions, and duplicates.
Runner timer and persistence safeguards
services/runner/src/*, services/runner/tests/unit/*
Environment values are bounded and warned once, timer delays are normalized, record queries receive abort timeouts, and durable drop counts are reported during flush.
Release and build support
hosting/*, web/*/package.json, */pyproject.toml, .gitleaksignore
Versions, dependencies, Docker init settings, CSS declarations, TypeScript scripts, and secret-scan suppressions are updated.

Product experience

Layer / File(s) Summary
Agent chat interaction and voice features
web/oss/src/components/AgentChatSlice/*, web/packages/agenta-ui/src/RichChatInput/*
Chat gains attachment uploads and previews, audio playback, voice dictation, recording controls, parked connect interactions, waiting states, and inspector feature gating.
Drive upload and local preview flow
web/oss/src/components/Drives/*
Drive browsing supports drag-and-drop uploads, staged files, optimistic progress/error tiles, local-file previews, downloads, and session-scoped staged state.
Navigation and settings state
web/oss/src/state/onboarding/*, web/oss/src/components/Sidebar/*, web/oss/src/pages/*/settings/*, web/oss/src/components/pages/settings/*
Per-user simplified navigation defaults and overrides are added, settings are grouped by scope, feature flags and organization settings pages are wired, and agent first-run detection is updated.

Shared contracts and package migration

Layer / File(s) Summary
Shared table and tracing contracts
web/oss/src/components/EvalRunDetails/*, web/oss/src/components/EvaluationRunsTablePOC/*, web/oss/src/components/InfiniteVirtualTable/*, web/oss/src/services/tracing/types/*, web/packages/agenta-ui/src/InfiniteVirtualTable/*
Table consumers use @agenta/ui/table, the local InfiniteVirtualTable implementation is removed, and tracing types are sourced from shared entities.
Entity contracts and rich editor primitives
web/packages/agenta-entities/*, web/packages/agenta-playground/*, web/packages/agenta-ui/src/RichChatInput/*
Evaluation-run references, trace export identity handling, model modality lookup, playground exports, and Lexical dictation sessions are extended.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 66.67% which is sufficient. The required threshold is 60.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: adding init to runner containers to prevent zombie processes.
Description check ✅ Passed The description matches the compose-file init flag changes and the stated zombie-process fix.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/runner-container-init

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

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 (1)
web/oss/src/components/AgentChatSlice/components/clientTools/ConnectToolWidget.tsx (1)

63-108: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The phase === "error" branch is unreachable, so manual-retry failures show no reason.

phase only becomes "error" via runConnect(false), which this widget invokes solely from the settled/error chips (Lines 93 and 105) — i.e. only when meta.settled is already true. The meta.settled || outcome branch at Line 64 therefore always wins, and Lines 98-108 never render: a blocked popup or a failed create surfaces as the generic "Connection not completed" with the actual errorText discarded.

Check the error phase before the settled branch:

🐛 Proposed reordering
+    // ── Error on a manual retry (create failed, popup blocked): show reason + Retry ──────────────
+    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>
+        )
+    }
+
     // ── Settled: the result chip (U1). `outcome` covers the render before `meta.settled` flips. ──
     if (meta.settled || outcome) {
@@
     }
 
-    // ── Error on a manual retry (create failed, popup blocked): show reason + Retry ──────────────
-    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>
-        )
-    }
-
     // ── Pending: passive marker — the InteractionDock (above the composer) owns the actions ──────
🟡 Minor comments (23)
web/packages/agenta-ui/src/RichChatInput/ComposerSendButton.tsx-35-37 (1)

35-37: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Replace raw hex colors with supported theme tokens.

Line 37 bypasses the light/dark theme contract with #191a0d and #b8cb3f; use semantic or supported --ag-color* tokens 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 or --ag-c-* literals.”

Source: Coding guidelines

web/packages/agenta-ui/src/RichChatInput/ComposerSendButton.tsx-15-22 (1)

15-22: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Condense the implementation commentary.

These multi-line style and rationale comments exceed the repository’s one-short-line comment convention without documenting a bug, race, or ordering constraint. 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.”

Also applies to: 32-33

Source: Coding guidelines

web/packages/agenta-ui/src/RichChatInput/RichChatInput.tsx-297-297 (1)

297-297: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Disable submission while dictation is active.

Line 297 makes the editor read-only, but the send control still receives only disabled. A click can submit a partial transcript while subsequent recognizer updates continue mutating the composer.

Proposed fix
-                                    disabled={disabled}
+                                    disabled={disabled || dictating}
web/oss/src/components/EvaluationRunsTablePOC/components/filters/EvaluationRunsHeaderFilters.tsx-3-3 (1)

3-3: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the active-filter button state when switching implementations.

EvaluationRunsHeaderFilters still passes buttonType={filtersButtonState.buttonType}, but FiltersPopoverTrigger in web/packages/agenta-ui/src/InfiniteVirtualTable/components/filters/FiltersPopoverTrigger.tsx:8-79 always renders type="default". Active filters will therefore lose their primary-button styling. Make the shared component use type={buttonType}, or retain the local implementation until the contracts match.

web/oss/src/components/Drives/useDriveDrop.ts-47-53 (1)

47-53: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Pending spring timer isn't cleared on unmount. If the drawer/pane unmounts mid-hover (a drop that closes the host, navigation away), the 700ms timeout still fires onNavigate(path) and setHoverPath(null) on a dead component.

🛡️ Clear the timer on unmount
     const clearSpring = useCallback(() => {
         window.clearTimeout(springTimer.current)
         springTimer.current = undefined
         springPath.current = null
     }, [])
+
+    useEffect(() => () => window.clearTimeout(springTimer.current), [])

Also applies to: 89-101

web/oss/src/components/Drives/useMountUpload.ts-88-95 (1)

88-95: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Empty mount.id produces a malformed request instead of a no-op. ?? "" builds /mounts//files/upload, which fails with an opaque server error and surfaces as a generic "Upload failed" item. Bail out and mark the item instead.

🛡️ Guard the missing mount id
             const controller = new AbortController()
             controllers.current.set(id, controller)
+            const mountId = src.target.mount.id
+            if (!mountId) {
+                patch(id, {error: "This drive isn't writable"})
+                return
+            }
             patch(id, {percent: 0, error: null})
             uploadMountFile({
-                mountId: src.target.mount.id ?? "",
+                mountId,
web/oss/src/components/Drives/driveMedia.ts-167-192 (1)

167-192: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Normalize destFolder before transmitting path. The endpoint rejects absolute/mount-escape paths, but this constructs it with only destFolder.replace(/\/$/, ""), so leading slashes and . / .. segments are still sent to the backend instead of being eliminated at the entry point.

Source: Coding guidelines

web/oss/src/components/AgentChatSlice/components/ComposerAttachments.tsx-124-140 (1)

124-140: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

role="button" tiles aren't keyboard operable.

Both the image tile and Chip take onClick with role="button" but no tabIndex or key handler, so viewing an attachment is mouse-only. Add tabIndex={0} + Enter/Space handling when clickable (or wrap in a real <button>).

Also applies to: 338-346

web/oss/src/components/AgentChatSlice/components/ComposerAttachments.tsx-99-121 (1)

99-121: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The error overlay covers the tile's remove button.

StatusOverlay's error state is absolute inset-0 and rendered after the tile, so a failed attachment can only be retried, not removed. Consider insetting the overlay or re-exposing the remove affordance above it.

Also applies to: 405-405

web/oss/src/components/AgentChatSlice/components/AudioPlayer.tsx-28-76 (1)

28-76: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Metadata may already be loaded when the listener attaches.

For a blob/cached src, loadedmetadata can fire before this effect runs (preload="metadata" starts loading at mount), leaving duration at 0 so the total time and progress bar never appear. Run the handler once if el.readyState >= HAVE_METADATA.

🐛 Proposed fix
         el.addEventListener("loadedmetadata", onLoadedMetadata)
+        // Metadata can already be there (blob / cached src loaded before this effect ran).
+        if (el.readyState >= 1) onLoadedMetadata()
         return () => {
web/oss/src/components/AgentChatSlice/components/ComposerAttachments.tsx-185-196 (1)

185-196: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Object URLs are recreated on every files change, not just on add/remove.

The effect depends on the whole files array, so an upload status/percent update revokes and re-creates every URL — audio playback restarts and thumbnails flicker mid-upload. Key the map by uid and only create URLs for uids that don't have one.

♻️ Sketch
-    useEffect(() => {
-        const next: Record<string, string> = {}
-        files.forEach((f) => {
-            const file = f.originFileObj as File | undefined
-            const type = file?.type || ""
-            if (file && (type.startsWith("image/") || type.startsWith("audio/"))) {
-                next[f.uid] = URL.createObjectURL(file)
-            }
-        })
-        setPreviews(next)
-        return () => Object.values(next).forEach((u) => URL.revokeObjectURL(u))
-    }, [files])
+    // Keyed by uid: only new attachments get a URL, and only removed ones are revoked, so an
+    // upload progress tick can't reset a playing clip.
+    const uidKey = files.map((f) => f.uid).join("|")
+    useEffect(() => {
+        setPreviews((prev) => {
+            const next: Record<string, string> = {}
+            files.forEach((f) => {
+                const file = f.originFileObj as File | undefined
+                const type = file?.type || ""
+                if (!file || !(type.startsWith("image/") || type.startsWith("audio/"))) return
+                next[f.uid] = prev[f.uid] ?? URL.createObjectURL(file)
+            })
+            Object.entries(prev).forEach(([uid, url]) => {
+                if (!next[uid]) URL.revokeObjectURL(url)
+            })
+            return next
+        })
+    }, [uidKey])

(plus an unmount-only cleanup that revokes whatever remains)

web/oss/src/components/AgentChatSlice/assets/markdown.tsx-195-201 (1)

195-201: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the anchor label for drive links.

driveFileResolver.renderCode currently feeds text into ChatFileCode, which resolves a candidate path and renders DriveFileInlineRef with the path basename as its visible label. For markdown links, pass children through, so [report](out/report.csv) displays as report while still resolving out/report.csv.

web/oss/src/components/AgentChatSlice/components/RecordingWaveform.tsx-24-29 (1)

24-29: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

parseRgb silently degrades to black on non-rgb() computed colors.

getComputedStyle().color is not guaranteed to serialize as rgb()/rgba() — Chrome returns oklch(...) / color(srgb 0.2 0.4 0.6) when the authored value uses a modern color space. The digit regex then reads 0.2/0.4/0.6 as 8-bit channels, so the waveform renders near-black in both themes. Using globalAlpha with the raw color string sidesteps the parse entirely:

🛡️ Proposed color-space-agnostic tint
-            const [r, g, b] = parseRgb(getComputedStyle(canvas).color)
-            gradient = ctx.createLinearGradient(0, 0, width, 0)
-            gradient.addColorStop(0, `rgba(${r}, ${g}, ${b}, 0.12)`)
-            gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0.95)`)
+            // Tint by alpha via globalAlpha so any computed color serialization works.
+            ctx.fillStyle = getComputedStyle(canvas).color
+            gradient = null

…and in draw, set ctx.globalAlpha per bar from 0.12 → 0.95 across i / (HISTORY - 1) instead of relying on the gradient.

Also applies to: 62-65

web/oss/src/components/AgentChatSlice/hooks/useAudioRecorder.ts-138-196 (1)

138-196: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Re-entrant start() calls can leak an open mic stream.

recRef.current (the guard at line 139) is only assigned inside the getUserMedia().then() callback (line 189), so it stays null for the whole duration of the pending permission prompt. A second start() call issued before the first promise resolves passes the guard, and the second call's streamRef.current/recRef.current/audioCtxRef.current assignments overwrite the first's — the first stream's tracks are then unreachable and never stopped by teardown() (mic stays open), and the orphaned MediaRecorder's cap timer can still fire a stray onComplete later.

🔒 Proposed fix: synchronous re-entrancy guard
+    const startingRef = useRef(false)
+
     const start = useCallback(() => {
-        if (!supported || recRef.current) return
+        if (!supported || recRef.current || startingRef.current) return
+        startingRef.current = true
         setError(null)
         cancelledRef.current = false
         erroredRef.current = false
         setStatus("requesting")
         navigator.mediaDevices
             .getUserMedia({audio: true})
             .then((stream) => {
+                startingRef.current = false
                 if (cancelledRef.current) {
                     stream.getTracks().forEach((t) => t.stop())
                     return
                 }
                 ...
             })
             .catch((e: unknown) => {
+                startingRef.current = false
                 teardown()
                 ...
             })
     }, [supported, teardown, meter])
docs/design/simplify-nav-new-users/plan.md-145-152 (1)

145-152: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Do not document a shared literal localStorage key.

"agenta:onboarding:<userId>:nav-simplified-override" is only a placeholder. If copied literally, every account would share one override key. Show the active-user-scoped atom-family implementation used by web/oss/src/lib/onboarding/atoms.ts.

docs/design/simplify-nav-new-users/plan.md-3-6 (1)

3-6: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align the Phase 2 scope statements across the design documents. Phase 2 adds the per-user override atom in web/oss/src/lib/onboarding/atoms.ts, so it does modify a Phase-1 file.

  • docs/design/simplify-nav-new-users/plan.md#L3-L6: revise the top-level “no Phase-1 file” claim.
  • docs/design/simplify-nav-new-users/README.md#L15-L16: revise the two-phase decision wording.
  • docs/design/simplify-nav-new-users/plan.md#L138-L143: revise the additive-phase description.
  • docs/design/simplify-nav-new-users/status.md#L14-L16: revise the locked decision.
docs/design/simplify-nav-new-users/research.md-7-24 (1)

7-24: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Refresh the sidebar line references in both documents. The supplied useSidebarConfig uses different line locations than those cited, so these references are no longer reliable.

  • docs/design/simplify-nav-new-users/research.md#L7-L24: update the ownership and target-item references.
  • docs/design/simplify-nav-new-users/plan.md#L98-L115: update the slice implementation references.
web/oss/src/components/pages/settings/Organization/General.tsx-229-259 (1)

229-259: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Raw gray utilities won't adapt to dark mode.

bg-gray-100, text-gray-400, and border-gray-100 are fixed light-mode values, so the email chip and option separators will look wrong in dark theme. Prefer semantic tokens (e.g. var(--ant-color-fill-tertiary), var(--ant-color-text-tertiary), var(--ant-color-border-secondary)).

As per coding guidelines: "Implement light and dark appearance and interaction states for every added or changed UI element, and verify both themes."

Source: Coding guidelines

web/oss/src/components/pages/settings/FeatureFlags/FeatureFlags.tsx-57-57 (1)

57-57: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Generate text-colorError from the Ant Design error token.

--ag-colorError is available, but this class does not resolve through the current Tailwind token/safelist setup; use a generated token class or a supported CSS variable such as text-[color:var(--ag-colorError)] so the icon doesn’t fall back to the parent text color.

Source: Coding guidelines

web/oss/src/components/pages/settings/Organization/General.tsx-186-195 (1)

186-195: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Whitespace-only name passes validation and submits an empty rename.

required accepts " ", and name.trim() then sends an empty string to updateOrganization.

🛠️ Proposed fix
-                    onFinish={({name}) => renameMutation.mutate({name: name.trim()})}
+                    onFinish={({name}) => renameMutation.mutate({name: name.trim()})}
@@
                     <Form.Item
                         name="name"
                         className="!mb-0 flex-1"
-                        rules={[{required: true, message: "Please enter an organization name"}]}
+                        rules={[
+                            {
+                                required: true,
+                                whitespace: true,
+                                message: "Please enter an organization name",
+                            },
+                        ]}
                     >
web/oss/src/hooks/usePostAuthRedirect.ts-142-142 (1)

142-142: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Persist the simplified-nav default for invited new users too.

An invited user with is_new_user returns at Lines 133-136 before either write, so they retain full navigation. Set this flag before the invited-user early return and remove the duplicate branch writes.

The atom contract identifies this as the persisted signup-era setting.

Also applies to: 154-154

web/oss/src/pages/w/[workspace_id]/p/[project_id]/settings/index.tsx-134-134 (1)

134-134: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Replace the legacy raw color variable.

bg-[var(--ag-c-0517290F)] bypasses the supported semantic theme-token path; use an Ant Design semantic token, Tailwind color utility, or supported var(--ag-color*) value 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 or --ag-c-* literals.”

Source: Coding guidelines

web/oss/src/pages/w/[workspace_id]/p/[project_id]/settings/index.tsx-125-132 (1)

125-132: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the organization-ID copy control keyboard accessible.

This Antd Tag without href is not keyboard-focusable, and onClick will not receive activation for keyboard users. Use a <button> for this copy action, or add equivalent keyboard semantics plus tabIndex={0}/key handling.

🧹 Nitpick comments (12)
web/oss/src/components/pages/observability/assets/constants.ts (1)

705-765: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use an approved semantic color token.

Replace var(--ag-zinc-2) with an Ant semantic token or supported var(--ag-color*) variable to keep theme colors centrally managed. As per coding guidelines, “Consume theme colors through Ant Design semantic tokens, Tailwind color utilities, or supported var(--ag-color*) variables.”

Source: Coding guidelines

web/oss/src/services/tracing/types/index.ts (1)

1-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Keep the new comments to one short line.

Lines 1 and 8 add multi-line commentary where a concise module-level note is sufficient.

As per coding guidelines, “Keep in-code comments to at most one short line.”

Proposed cleanup
-// Span types live in `@agenta/entities/trace`; this module adds only OSS extras.
+// Extends shared trace spans with OSS annotations.
-
 import type {TraceSpanNode as EntityTraceSpanNode} from "`@agenta/entities/trace`"
-
 import type {AnnotationDto} from "`@/oss/lib/hooks/useAnnotations/types`"
 
 export interface TraceSpanNode extends EntityTraceSpanNode {
-    /** Attached by the trace/session drawer stores when annotation data is loaded */
     annotations?: AnnotationDto[]
 }

Source: Coding guidelines

web/oss/src/components/Drives/DriveExplorer.tsx (1)

1727-1733: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Double cast hides the synthetic node's shape. as unknown as MountFile will silently keep compiling if MountFile gains required fields, and consumers (recentsByPath, size formatting) then read undefined. Consider building a real MountFile with explicit defaults, or narrowing the tree builder input to the subset it actually needs ({path, size}).

web/oss/src/components/Drives/useMountUpload.ts (1)

73-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the mount invalidation keys source these roots from the shared query-family constants. The current roots match the mount listings, but keeping queryKey prefixes next to useMountUpload’s invalidation array prevents future query-family renames from leaving uploads on a stale mount cache.

web/oss/src/components/AgentChatSlice/AgentConversation.tsx (1)

1722-1749: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the enqueue uids from the created UploadFiles instead of re-deriving the key.

Line 1743 re-implements the uid formula from toUploadFile (Line 1723). If either changes, uploads.enqueue silently targets uids that don't exist (currently masked because no uploader is wired).

♻️ Proposed fix
         if (accepted.length) {
-            setFiles((prev) => [...prev, ...accepted.map(toUploadFile)])
-            uploads.enqueue(accepted.map((f) => `${f.name}-${f.lastModified}-${f.size}`))
+            const staged = accepted.map(toUploadFile)
+            setFiles((prev) => [...prev, ...staged])
+            uploads.enqueue(staged.map((f) => f.uid))
         }
web/oss/src/components/AgentChatSlice/components/RecordingBar.tsx (1)

38-48: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

A frame-rate loop for a 1 Hz clock.

The setSeconds guard keeps React renders at ~1/s, but the rAF callback still runs every frame for the whole take. A ~250 ms interval gives the same visual accuracy at a fraction of the wake-ups:

♻️ Proposed simplification
-    useEffect(() => {
-        let raf = 0
-        const tick = () => {
-            const startedAt = startedAtRef.current
-            const next = startedAt ? Math.floor((Date.now() - startedAt) / 1000) : 0
-            setSeconds((prev) => (prev === next ? prev : next))
-            raf = requestAnimationFrame(tick)
-        }
-        raf = requestAnimationFrame(tick)
-        return () => cancelAnimationFrame(raf)
-    }, [startedAtRef])
+    useEffect(() => {
+        const tick = () => {
+            const startedAt = startedAtRef.current
+            const next = startedAt ? Math.floor((Date.now() - startedAt) / 1000) : 0
+            setSeconds((prev) => (prev === next ? prev : next))
+        }
+        tick()
+        const id = window.setInterval(tick, 250)
+        return () => window.clearInterval(id)
+    }, [startedAtRef])
web/oss/src/components/AgentChatSlice/components/VoiceInputButton.tsx (1)

96-104: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Dictation never settles if this button unmounts mid-dictation.

useVoiceInput aborts recognition on unmount, but recording never flips for an unmounting component, so endDictation()/onDictatingChange(false) are skipped and the parent's editor stays locked. A dedicated unmount cleanup closes the gap:

🛡️ Proposed cleanup
     }, [transcribe.recording, inputRef, onDictatingChange])
+
+    // Unmounting mid-dictation would otherwise leave the editor locked.
+    useEffect(
+        () => () => {
+            if (wasRecording.current) {
+                inputRef.current?.endDictation()
+                onDictatingChange(false)
+            }
+        },
+        [inputRef, onDictatingChange],
+    )
web/oss/src/components/AgentChatSlice/components/clientTools/useConnectFlow.ts (2)

36-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Leftover reviewer note in shipped source.

Line 39 carries an unresolved open question addressed to a named individual. Either resolve the bound or move the question to the PR/issue tracker before merge — happy to open an issue for the timeout bound if useful.


251-259: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

setPhase("error") is immediately overwritten on the live-parked path.

When settleParkedCall is true, finish() resets phase to "idle", so the "error" phase and errorText set just above are never observable. Scope them to the manual-retry path to make the intent explicit:

♻️ Proposed clarification
-                setPhase("error")
-                setErrorText(message)
-                if (settleParkedCall) finish({connected: false, integration, slug, reason: message})
+                if (settleParkedCall) {
+                    finish({connected: false, integration, slug, reason: message})
+                } else {
+                    setPhase("error")
+                    setErrorText(message)
+                }
web/oss/src/components/AgentChatSlice/hooks/useAttachmentUploads.ts (1)

44-85: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Removing an attachment leaves its upload running.

Nothing aborts a controller when a file disappears from files; the request keeps streaming (and its AbortController stays in the map) until unmount, and every patch for it is silently dropped. A reconciliation effect keyed on the uid set handles it:

♻️ Proposed cleanup on removal
     // Abort in-flight uploads on unmount (session switch / pane close).
     useEffect(() => {
         const map = controllers.current
         return () => map.forEach((c) => c.abort())
     }, [])
+
+    // A file removed from the tray should not keep uploading.
+    useEffect(() => {
+        const live = new Set(files.map((f) => f.uid))
+        controllers.current.forEach((controller, uid) => {
+            if (live.has(uid)) return
+            controller.abort()
+            controllers.current.delete(uid)
+        })
+    }, [files])
web/oss/src/components/AgentChatSlice/hooks/useVoiceInput.ts (1)

97-109: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Interim segments joined without a separator.

finalRef segments are joined with a space, but interim concatenates multiple non-final result[0].transcript values with no separator, which could glue words together if a browser ever emits more than one interim result in the same event.

♻️ Optional consistency fix
-                } else {
-                    interim += result[0].transcript
-                }
+                } else {
+                    interim += (interim ? " " : "") + result[0].transcript
+                }
docs/design/simplify-nav-new-users/status.md (1)

62-65: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Add executable regression coverage for sidebar visibility.

The status explicitly accepts manual QA only for the five hidden entries and their preserved non-targets. Add coverage in an existing CI-wired web test layer, or wire an OSS test harness, so dependency-array or isHidden regressions are caught automatically.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 161fe067-38c1-41b2-8990-ffa4a07c0cfc

📥 Commits

Reviewing files that changed from the base of the PR and between 56be600 and ac0ed54.

⛔ Files ignored due to path filters (6)
  • api/uv.lock is excluded by !**/*.lock
  • clients/python/uv.lock is excluded by !**/*.lock
  • sdks/python/uv.lock is excluded by !**/*.lock
  • services/runner/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • services/uv.lock is excluded by !**/*.lock
  • web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (233)
  • .gitleaksignore
  • api/entrypoints/routers.py
  • api/oss/src/core/mounts/service.py
  • api/oss/src/middlewares/prefix.py
  • api/oss/tests/pytest/unit/middlewares/test_prefix.py
  • api/oss/tests/pytest/unit/test_mounts_file_ops.py
  • api/pyproject.toml
  • clients/python/pyproject.toml
  • docs/design/simplify-nav-new-users/README.md
  • docs/design/simplify-nav-new-users/context.md
  • docs/design/simplify-nav-new-users/plan.md
  • docs/design/simplify-nav-new-users/research.md
  • docs/design/simplify-nav-new-users/status.md
  • hosting/docker-compose/ee/docker-compose.dev.yml
  • hosting/docker-compose/ee/docker-compose.gh.local.yml
  • hosting/docker-compose/ee/docker-compose.gh.yml
  • hosting/docker-compose/oss/docker-compose.dev.yml
  • hosting/docker-compose/oss/docker-compose.gh.local.yml
  • hosting/docker-compose/oss/docker-compose.gh.ssl.yml
  • hosting/docker-compose/oss/docker-compose.gh.yml
  • hosting/kubernetes/helm/Chart.yaml
  • sdks/python/pyproject.toml
  • services/pyproject.toml
  • services/runner/package.json
  • services/runner/src/engines/sandbox_agent/run-limits.ts
  • services/runner/src/env.ts
  • services/runner/src/sessions/persist.ts
  • services/runner/src/sessions/records-query.ts
  • services/runner/tests/unit/env-num.test.ts
  • services/runner/tests/unit/run-limits.test.ts
  • services/runner/tests/unit/session-persist.test.ts
  • services/runner/tests/unit/session-records-query.test.ts
  • web/ee/css-modules.d.ts
  • web/ee/package.json
  • web/ee/tsconfig.json
  • web/oss/css-modules.d.ts
  • web/oss/next.config.ts
  • web/oss/package.json
  • web/oss/src/components/AgentChatSlice/AgentConversation.tsx
  • web/oss/src/components/AgentChatSlice/assets/attachments.ts
  • web/oss/src/components/AgentChatSlice/assets/constants.ts
  • web/oss/src/components/AgentChatSlice/assets/markdown.tsx
  • web/oss/src/components/AgentChatSlice/components/AgentMessage.tsx
  • web/oss/src/components/AgentChatSlice/components/AttachmentViewerDrawer.tsx
  • web/oss/src/components/AgentChatSlice/components/AudioPlayer.tsx
  • web/oss/src/components/AgentChatSlice/components/ComposerAttachments.tsx
  • web/oss/src/components/AgentChatSlice/components/ConnectModelBanner.tsx
  • web/oss/src/components/AgentChatSlice/components/Inspector/InspectSessionButton.tsx
  • web/oss/src/components/AgentChatSlice/components/InteractionDock.tsx
  • web/oss/src/components/AgentChatSlice/components/MicPermissionNotice.tsx
  • web/oss/src/components/AgentChatSlice/components/QueuedMessages.tsx
  • web/oss/src/components/AgentChatSlice/components/RecordingBar.tsx
  • web/oss/src/components/AgentChatSlice/components/RecordingWaveform.tsx
  • web/oss/src/components/AgentChatSlice/components/ToolActivity.tsx
  • web/oss/src/components/AgentChatSlice/components/VoiceInputButton.tsx
  • web/oss/src/components/AgentChatSlice/components/clientTools/ConnectToolWidget.tsx
  • web/oss/src/components/AgentChatSlice/components/clientTools/useConnectFlow.ts
  • web/oss/src/components/AgentChatSlice/hooks/useAttachmentUploads.ts
  • web/oss/src/components/AgentChatSlice/hooks/useAudioRecorder.ts
  • web/oss/src/components/AgentChatSlice/hooks/useVoiceInput.ts
  • web/oss/src/components/Drives/ContextRail.tsx
  • web/oss/src/components/Drives/DriveExplorer.tsx
  • web/oss/src/components/Drives/FilesDrawer.tsx
  • web/oss/src/components/Drives/SessionFilesDrawer.tsx
  • web/oss/src/components/Drives/StorageFilesHeader.tsx
  • web/oss/src/components/Drives/StorageSection.tsx
  • web/oss/src/components/Drives/VirtualTileGrid.tsx
  • web/oss/src/components/Drives/configDrive.ts
  • web/oss/src/components/Drives/driveFileSource.tsx
  • web/oss/src/components/Drives/driveMedia.ts
  • web/oss/src/components/Drives/renderers.tsx
  • web/oss/src/components/Drives/useDriveDrop.ts
  • web/oss/src/components/Drives/useImagePreviews.ts
  • web/oss/src/components/Drives/useMountUpload.ts
  • web/oss/src/components/Drives/useSessionDrive.ts
  • web/oss/src/components/EvalRunDetails/Table.tsx
  • web/oss/src/components/EvalRunDetails/atoms/table/run.ts
  • web/oss/src/components/EvalRunDetails/atoms/traces.ts
  • web/oss/src/components/EvalRunDetails/components/CompareRunsMenu.tsx
  • web/oss/src/components/EvalRunDetails/components/EvalTestcaseDrawerAdapter/index.tsx
  • web/oss/src/components/EvalRunDetails/components/FocusDrawerHeader.tsx
  • web/oss/src/components/EvalRunDetails/components/FocusDrawerSidePanel.tsx
  • web/oss/src/components/EvalRunDetails/components/columnVisibility/ColumnVisibilityPopoverContent.tsx
  • web/oss/src/components/EvalRunDetails/components/views/SingleScenarioViewerPOC/ScenarioNavigator.tsx
  • web/oss/src/components/EvalRunDetails/components/views/SingleScenarioViewerPOC/index.tsx
  • web/oss/src/components/EvalRunDetails/evaluationPreviewTableStore.ts
  • web/oss/src/components/EvalRunDetails/hooks/useCellVisibility.ts
  • web/oss/src/components/EvalRunDetails/hooks/usePreviewColumns.tsx
  • web/oss/src/components/EvalRunDetails/utils/buildPreviewColumns.tsx
  • web/oss/src/components/EvaluationRunsTablePOC/atoms/fetchAutoEvaluationRuns.ts
  • web/oss/src/components/EvaluationRunsTablePOC/atoms/tableStore.ts
  • web/oss/src/components/EvaluationRunsTablePOC/components/EvaluationRunsTable/index.tsx
  • web/oss/src/components/EvaluationRunsTablePOC/components/EvaluationRunsTable/types.ts
  • web/oss/src/components/EvaluationRunsTablePOC/components/columnVisibility/ColumnVisibilityPopoverContent.tsx
  • web/oss/src/components/EvaluationRunsTablePOC/components/filters/EvaluationRunsHeaderFilters.tsx
  • web/oss/src/components/EvaluationRunsTablePOC/hooks/useEvaluationRunsColumns/index.tsx
  • web/oss/src/components/EvaluationRunsTablePOC/hooks/useEvaluationRunsColumns/utils.tsx
  • web/oss/src/components/EvaluationRunsTablePOC/hooks/useEvaluatorHeaderReference.ts
  • web/oss/src/components/EvaluationRunsTablePOC/types.ts
  • web/oss/src/components/InfiniteVirtualTable/InfiniteVirtualTable.tsx
  • web/oss/src/components/InfiniteVirtualTable/atoms/columnHiddenKeys.ts
  • web/oss/src/components/InfiniteVirtualTable/atoms/columnVisibility.ts
  • web/oss/src/components/InfiniteVirtualTable/atoms/columnWidths.ts
  • web/oss/src/components/InfiniteVirtualTable/columns/cells.tsx
  • web/oss/src/components/InfiniteVirtualTable/columns/createStandardColumns.tsx
  • web/oss/src/components/InfiniteVirtualTable/columns/createTableColumns.ts
  • web/oss/src/components/InfiniteVirtualTable/columns/types.ts
  • web/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityHeader.tsx
  • web/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityTrigger.tsx
  • web/oss/src/components/InfiniteVirtualTable/components/InfiniteVirtualTableInner.tsx
  • web/oss/src/components/InfiniteVirtualTable/components/TableDescription.tsx
  • web/oss/src/components/InfiniteVirtualTable/components/TableShell.tsx
  • web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityMenuTrigger.tsx
  • web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityPopoverContent.tsx
  • web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/TableSettingsDropdown.tsx
  • web/oss/src/components/InfiniteVirtualTable/components/filters/FiltersPopoverTrigger.tsx
  • web/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityContext.ts
  • web/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityFlagContext.tsx
  • web/oss/src/components/InfiniteVirtualTable/context/VirtualTableScrollContainerContext.ts
  • web/oss/src/components/InfiniteVirtualTable/createInfiniteDatasetStore.ts
  • web/oss/src/components/InfiniteVirtualTable/createInfiniteTableStore.ts
  • web/oss/src/components/InfiniteVirtualTable/features/InfiniteVirtualTableFeatureShell.tsx
  • web/oss/src/components/InfiniteVirtualTable/features/index.ts
  • web/oss/src/components/InfiniteVirtualTable/features/useInfiniteTableFeaturePagination.ts
  • web/oss/src/components/InfiniteVirtualTable/helpers/createSimpleTableStore.ts
  • web/oss/src/components/InfiniteVirtualTable/helpers/createTableRowHelpers.ts
  • web/oss/src/components/InfiniteVirtualTable/helpers/index.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useColumnDomRefs.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibility.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibilityControls.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useContainerResize.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useContainerSize.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useEditableTable.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useExpandableRows.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useHeaderViewportVisibility.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteScroll.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteTablePagination.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useResizableColumns.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useRowHeight.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useScopedColumnVisibility.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useScrollConfig.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useScrollContainer.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useSmartResizableColumns.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableActions.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableExport.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableHeaderHeight.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableKeyboardShortcuts.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableManager.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableRowSelection.ts
  • web/oss/src/components/InfiniteVirtualTable/index.ts
  • web/oss/src/components/InfiniteVirtualTable/providers/ColumnVisibilityProvider.tsx
  • web/oss/src/components/InfiniteVirtualTable/providers/InfiniteVirtualTableStoreProvider.tsx
  • web/oss/src/components/InfiniteVirtualTable/types.ts
  • web/oss/src/components/InfiniteVirtualTable/utils/columnUtils.ts
  • web/oss/src/components/Playground/Components/TestsetDropdown/TestsetPreviewPanelWrapper.tsx
  • web/oss/src/components/SessionInspector/PanelSessionInspectorButton.tsx
  • web/oss/src/components/SharedDrawers/AddToTestsetDrawer/components/PreviewSection.tsx
  • web/oss/src/components/SharedDrawers/SessionDrawer/components/SessionDrawerButton/index.tsx
  • web/oss/src/components/SharedDrawers/SessionDrawer/store/sessionDrawerStore.ts
  • web/oss/src/components/SharedDrawers/SessionDrawer/types.ts
  • web/oss/src/components/SharedDrawers/TraceDrawer/components/TraceContent/components/OverviewTabItem/index.tsx
  • web/oss/src/components/SharedDrawers/TraceDrawer/components/TraceTree/assets/spanVisibility.ts
  • web/oss/src/components/SharedDrawers/TraceDrawer/components/TraceTree/index.tsx
  • web/oss/src/components/SharedDrawers/TraceDrawer/store/traceDrawerStore.ts
  • web/oss/src/components/Sidebar/dynamic/registry.ts
  • web/oss/src/components/Sidebar/hooks/useSidebarConfig/index.tsx
  • web/oss/src/components/Sidebar/scopes/settingsScope.tsx
  • web/oss/src/components/TestcasesTableNew/components/TestcaseHeader.tsx
  • web/oss/src/components/TestcasesTableNew/components/TestcasesTableShell.tsx
  • web/oss/src/components/TestcasesTableNew/index.tsx
  • web/oss/src/components/TestcasesTableNew/state/rowHeight.ts
  • web/oss/src/components/TestsetsTable/TestsetsTable.tsx
  • web/oss/src/components/TestsetsTable/assets/createTestsetsColumns.tsx
  • web/oss/src/components/TestsetsTable/atoms/fetchTestsets.ts
  • web/oss/src/components/TestsetsTable/components/TestsetsHeaderFilters.tsx
  • web/oss/src/components/pages/agent-home/OnboardingEntry.tsx
  • web/oss/src/components/pages/agent-home/hooks/useCreateAgent.ts
  • web/oss/src/components/pages/agents/AgentsPage.tsx
  • web/oss/src/components/pages/agents/store.ts
  • web/oss/src/components/pages/observability/assets/constants.ts
  • web/oss/src/components/pages/observability/assets/exportUtils.ts
  • web/oss/src/components/pages/observability/assets/getObservabilityColumns.tsx
  • web/oss/src/components/pages/observability/components/AvatarTreeContent.tsx
  • web/oss/src/components/pages/observability/components/NodeNameCell.tsx
  • web/oss/src/components/pages/observability/components/SessionsTable/assets/getSessionColumns.tsx
  • web/oss/src/components/pages/observability/components/StatusRenderer.tsx
  • web/oss/src/components/pages/observability/components/TimestampCell.tsx
  • web/oss/src/components/pages/prompts/PromptsPage.tsx
  • web/oss/src/components/pages/settings/FeatureFlags/FeatureFlags.tsx
  • web/oss/src/components/pages/settings/Organization/General.tsx
  • web/oss/src/components/pages/settings/assets/navigation.test.ts
  • web/oss/src/components/pages/settings/assets/navigation.ts
  • web/oss/src/hooks/usePostAuthRedirect.ts
  • web/oss/src/lib/helpers/dynamicEnv.ts
  • web/oss/src/lib/hooks/usePreviewEvaluations/index.ts
  • web/oss/src/lib/hooks/usePreviewEvaluations/types.ts
  • web/oss/src/lib/onboarding/atoms.ts
  • web/oss/src/lib/onboarding/index.ts
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/[app_id]/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/settings/index.tsx
  • web/oss/src/services/tracing/types/index.ts
  • web/oss/src/state/app/atoms/fetcher.ts
  • web/oss/src/state/entities/shared/README.md
  • web/oss/src/state/entities/shared/createPaginatedEntityStore.ts
  • web/oss/src/state/entities/testcase/paginatedStore.ts
  • web/oss/src/state/entities/testset/paginatedStore.ts
  • web/oss/src/state/newObservability/atoms/queries.ts
  • web/oss/src/state/onboarding/selectors.ts
  • web/oss/src/state/settings/featureFlags.ts
  • web/oss/tsconfig.json
  • web/package.json
  • web/packages/agenta-api-client/package.json
  • web/packages/agenta-entities/package.json
  • web/packages/agenta-entities/src/evaluationRun/core/schema.ts
  • web/packages/agenta-entities/src/evaluationRun/etl/resolveMappings.ts
  • web/packages/agenta-entities/src/trace/etl/exportMatchingTraces.ts
  • web/packages/agenta-entities/src/workflow/index.ts
  • web/packages/agenta-entities/src/workflow/state/index.ts
  • web/packages/agenta-entities/src/workflow/state/inspectMeta.ts
  • web/packages/agenta-entities/tests/unit/export-matching-traces.test.ts
  • web/packages/agenta-entity-ui/package.json
  • web/packages/agenta-playground/src/index.ts
  • web/packages/agenta-playground/src/state/execution/index.ts
  • web/packages/agenta-playground/src/state/index.ts
  • web/packages/agenta-shared/package.json
  • web/packages/agenta-ui/src/InfiniteVirtualTable/index.ts
  • web/packages/agenta-ui/src/InfiniteVirtualTable/types.ts
  • web/packages/agenta-ui/src/RichChatInput/ComposerSendButton.tsx
  • web/packages/agenta-ui/src/RichChatInput/RichChatInput.tsx
  • web/packages/agenta-ui/src/RichChatInput/index.ts
  • web/packages/agenta-ui/src/RichChatInput/plugins/SendButton.tsx
  • web/packages/agenta-ui/src/RichChatInput/plugins/dictation.ts
  • web/tests/package.json
💤 Files with no reviewable changes (57)
  • web/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityHeader.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibilityControls.ts
  • web/oss/src/components/InfiniteVirtualTable/atoms/columnHiddenKeys.ts
  • web/oss/src/components/InfiniteVirtualTable/components/TableDescription.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useScrollContainer.ts
  • web/oss/src/components/InfiniteVirtualTable/features/useInfiniteTableFeaturePagination.ts
  • web/oss/src/components/InfiniteVirtualTable/providers/InfiniteVirtualTableStoreProvider.tsx
  • web/oss/src/components/SharedDrawers/SessionDrawer/types.ts
  • web/oss/src/components/InfiniteVirtualTable/atoms/columnWidths.ts
  • web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityMenuTrigger.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useColumnDomRefs.ts
  • web/oss/src/components/InfiniteVirtualTable/components/ColumnVisibilityTrigger.tsx
  • web/oss/src/components/InfiniteVirtualTable/columns/createTableColumns.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableActions.tsx
  • web/oss/src/components/InfiniteVirtualTable/features/index.ts
  • web/oss/src/components/SharedDrawers/SessionDrawer/components/SessionDrawerButton/index.tsx
  • web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/TableSettingsDropdown.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useContainerResize.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useContainerSize.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableKeyboardShortcuts.ts
  • web/oss/src/components/InfiniteVirtualTable/helpers/index.ts
  • web/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityContext.ts
  • web/oss/src/components/InfiniteVirtualTable/context/VirtualTableScrollContainerContext.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useRowHeight.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useScopedColumnVisibility.tsx
  • web/oss/src/components/InfiniteVirtualTable/columns/types.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableRowSelection.ts
  • web/oss/src/components/InfiniteVirtualTable/providers/ColumnVisibilityProvider.tsx
  • web/oss/src/components/InfiniteVirtualTable/createInfiniteTableStore.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableManager.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useScrollConfig.ts
  • web/oss/src/components/InfiniteVirtualTable/atoms/columnVisibility.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useColumnVisibility.ts
  • web/oss/src/components/InfiniteVirtualTable/columns/createStandardColumns.tsx
  • web/oss/src/components/InfiniteVirtualTable/InfiniteVirtualTable.tsx
  • web/oss/src/components/InfiniteVirtualTable/types.ts
  • web/oss/src/components/InfiniteVirtualTable/helpers/createSimpleTableStore.ts
  • web/oss/src/components/InfiniteVirtualTable/utils/columnUtils.ts
  • web/oss/src/components/InfiniteVirtualTable/components/filters/FiltersPopoverTrigger.tsx
  • web/oss/src/components/InfiniteVirtualTable/context/ColumnVisibilityFlagContext.tsx
  • web/oss/src/components/InfiniteVirtualTable/helpers/createTableRowHelpers.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteTablePagination.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useSmartResizableColumns.ts
  • web/oss/src/components/InfiniteVirtualTable/components/InfiniteVirtualTableInner.tsx
  • web/oss/src/components/InfiniteVirtualTable/createInfiniteDatasetStore.ts
  • web/oss/src/components/InfiniteVirtualTable/features/InfiniteVirtualTableFeatureShell.tsx
  • web/oss/src/components/InfiniteVirtualTable/columns/cells.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useHeaderViewportVisibility.ts
  • web/oss/src/components/InfiniteVirtualTable/components/TableShell.tsx
  • web/oss/src/components/InfiniteVirtualTable/components/columnVisibility/ColumnVisibilityPopoverContent.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useInfiniteScroll.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableHeaderHeight.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useResizableColumns.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useEditableTable.ts
  • web/oss/src/components/InfiniteVirtualTable/hooks/useExpandableRows.tsx
  • web/oss/src/components/InfiniteVirtualTable/hooks/useTableExport.ts
  • web/oss/src/components/InfiniteVirtualTable/index.ts

Comment on lines +38 to +52
const local = useLocalFile(path)
const mountRes = useMountFileMediaSrc(mount, path)
// A local blob can still fail to decode (corrupt / unsupported) — surface it like a mount error
// so the viewer shows its "couldn't load" card rather than a broken element.
const [localFailed, setLocalFailed] = useState(false)
if (local) {
return {
src: localFailed ? null : local.objectUrl,
isPending: false,
failed: localFailed,
onError: () => setLocalFailed(true),
}
}
return mountRes
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Per-path state isn't reset when path/local changes. These hooks are called from long-lived body components (ImageBody, TextBody, …) that keep their instance across path prop changes, so:

  • useDriveFileText: switching from local file A to B keeps text = A's content and reports isPending: false, so the viewer renders the wrong body until B resolves.
  • useDriveMediaSrc: a localFailed set for A sticks, so B renders the "couldn't load" card immediately.
🐛 Reset the per-file state when the source changes
-    const [localFailed, setLocalFailed] = useState(false)
+    const [failedUrl, setFailedUrl] = useState<string | null>(null)
+    const localFailed = local != null && failedUrl === local.objectUrl
     if (local) {
         return {
             src: localFailed ? null : local.objectUrl,
             isPending: false,
             failed: localFailed,
-            onError: () => setLocalFailed(true),
+            onError: () => setFailedUrl(local.objectUrl),
         }
     }
     useEffect(() => {
+        setText(undefined)
         if (!local) return
         let cancelled = false

Also applies to: 70-86

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Status Destroyed (PR closed)

Updated at 2026-07-29T20:08:36.244Z

@mmabrouk
mmabrouk changed the base branch from main to release/v0.106.1 July 29, 2026 20:07
@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels Jul 29, 2026
@mmabrouk
mmabrouk merged commit b849f78 into release/v0.106.1 Jul 29, 2026
44 of 45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Report Something isn't working size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant