Skip to content

feat(agents): playground perf, chat UX, and connection-flow fixes - #5145

Merged
ardaerzin merged 58 commits into
big-agentsfrom
fe-chore/request-optimizations-onbig
Jul 10, 2026
Merged

feat(agents): playground perf, chat UX, and connection-flow fixes#5145
ardaerzin merged 58 commits into
big-agentsfrom
fe-chore/request-optimizations-onbig

Conversation

@ardaerzin

@ardaerzin ardaerzin commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Context

This branch started as agent-playground load and reload performance and grew to cover the agent chat experience and the multi-connection flow alongside it. Three areas: the playground was slow and flashed the wrong chrome on load; the agent chat panel and session UI had rough edges; and asking for more than one connection in a single step failed in confusing ways.

Base is big-agents, and the latest base is merged in.

Changes

Playground load and reload

No chrome flash on load. An early, app-id-keyed agent signal (playgroundEarlyAgentStateAtom), backed by a persisted {workflowId: type} map, lets the header, config-panel header, loading shell, and split layout commit to the agent layout up front instead of defaulting to prompt and flipping. The prompt playground is unchanged.

Instant reloads via disk-backed SWR. The static agent-template schema, harness catalog, and per-revision inspect persist to localStorage, paint from disk on reload, and revalidate once in the background at low priority. A catalog prefetcher warms the static catalogs in parallel with the revision and inspect waterfall. The inspect cache is bounded by a byte budget (not an entry count) with quota self-healing, so large payloads can't monopolize the shared localStorage origin and starve auth state.

Correct request priorities and fewer requests. Secondary playground requests (tool connections and catalog, triggers, deploy environments, build-kit overlay, sidebar and variant-picker queries) and the entitlement/billing bootstrap send the priority: "low" fetch hint; Fern clients gained low-priority variants. A degraded billing service fails fast instead of retrying a 502 three times at high priority. Two round-trips were deduped (current-app revision detail from the revisions list, selected-org detail seeded from the workspace fetch), and the entitlement/billing bootstrap is deferred to browser idle.

Lighter chunks and skeletons. The chat panel regions, prompt-generation UIs, comparison view, and drawers are code-split out of the playground chunk, and the agent-template control idle-warms. Layout-matched loading skeletons and low-priority background trace-summary hydration smooth the first paint. Drawer data (the Model and harness draft, the trigger provider groups) fetches only when its drawer or section is open.

Agent chat UX

The chat panel is frame-split: a thin synchronous shell paints the structure in the first frame, and the conversation body, session bar, and rail lazy-load with their own region skeletons. Chat session state persists across pane remounts, and the session rail is resizable. Session status dots hold the streaming colour on the active session and surface an awaiting-input state.

Agent self-commit reads clearly now: a bottom-pinned banner announces it, changed sections get indicators, the config header shows an agent-update notice, and the transition animates instead of snapping. Config sections stay mounted across revision switches so they don't tear down and refetch. Tool-step names in chat are humanized, and trace-pending turns show a graceful state instead of a broken metric row.

Connection flow (multiple connections in one step)

Asking for two connections in a single step was broken end to end. This bundles the full fix across frontend, api, and services/runner.

Before: the agent asks for GitHub and Slack in one step. Slack immediately shows "Connection not completed" with no user action, completing GitHub resumes the run early, and the agent's re-ask of Slack never produces a widget. The user is stuck.

After: GitHub parks and shows its widget, Slack shows "Connecting next" (not a failure), completing GitHub resumes and the agent re-asks Slack, and a fresh Slack widget parks for the user to connect.

The pieces:

  • OAuth completion is scoped to its connection. The signed callback state carries the connection slug/integration, and the widget settles only on its own completion, so several live connect widgets no longer settle each other (api + frontend).
  • The connect widget leaves its "Connecting…" spinner once the call settles; it previously stuck on the spinner after finishing.
  • A runner-deferred sibling renders as pending, not a failed retry with a Retry button that raced the agent's own re-ask.
  • The runner no longer lets a deferred sibling satisfy its own retry: a DEFERRED_NOT_EXECUTED result is kept out of the client-output store, so the model's re-request re-parks and emits a fresh widget (services/runner).

Reliability

  • Release the chat queue on an orphaned post-approval resume (AGE-3937): a resume-shaped tail restored from storage after a reload no longer freezes the composer with no dock and no stop.
  • Tolerate a non-numeric revision version in workflowSchema (degrade to null instead of failing the whole revision parse).
  • Re-enable the Crisp chat widget on cloud.
  • Keyboard-accessible evaluator rows.

Tests / notes

  • services/runner: full unit suite green (741 tests), tsc clean.
  • @agenta/entities and @agenta/playground: package unit tests green (workflow version schema, agent message queue), tsc clean.
  • eslint clean on touched files.
  • Latest origin/big-agents merged in. Conflicts resolved deliberately: kept the frame-split chat panel and ported big-agents' build-kit overlay gating into AgentConversation; unioned both version schema fixes (strip the vN static-catalog prefix and tolerate a non-numeric value).
  • The ~8s local backend response times seen in testing are server-side (cold workers, DB), not addressed here.

What to QA

  • Reload an agent playground. No flash of the "New Evaluation / Compare / Evaluator / Test set" header or the "Select variant / Form / Commit" config chrome. On a second reload, DevTools Network shows agent-template, harnesses/, and inspect at Low priority and the config paints from cache.
  • Ask an agent to connect two integrations in one message (for example GitHub and Slack). Only the first shows a Connect button; the second shows "Connecting next" with no Retry. Complete the first, the agent re-asks the second, a fresh widget appears, and completing it continues the run. No connection is ever marked done without you doing it.
  • In the chat, switch revisions while config sections are open. They stay mounted. Trigger a self-commit and confirm the banner, the changed-section indicators, and the config-header notice.
  • Reload mid-approval, then send a message. The composer is not frozen and the queued message goes through (AGE-3937).
  • Regression: open a prompt (non-agent) playground. It still shows the variant selector, the Form/JSON/YAML switch, and the eval/compare controls.

@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
agenta-documentation Error Error Jul 10, 2026 6:48pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 282f30f0-e8c1-4b18-abf3-305997b06220

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds deferred request loading and persistence, agent-aware playground initialization, a session-based chat UI, trace summary queries, OAuth completion identity propagation, and filtering for deferred client-tool results.

Changes

Platform data loading and persistence

Layer / File(s) Summary
Deferred request infrastructure
web/packages/agenta-sdk/..., web/packages/agenta-shared/..., web/oss/src/state/access/...
Adds low-priority clients, idle scheduling, and updated entitlement query retry and enablement rules.
Workflow and app state
web/packages/agenta-entities/src/workflow/..., web/oss/src/state/{app,workflow}/..., web/oss/src/components/Sidebar/...
Adds single-workflow resolution, persisted metadata and catalog seeds, narrower app subscriptions, and activation-gated catalog reads.
Evaluator and workflow-reference activation
web/packages/agenta-entity-ui/src/selection/..., web/oss/src/components/DrillInView/..., web/oss/src/components/Evaluators/...
Defers evaluator and workflow-reference catalog work until relevant controls activate it.

Agent playground and chat

Layer / File(s) Summary
Agent-aware configuration and loading
web/oss/src/components/Playground/..., web/packages/agenta-entity-ui/src/DrillInView/..., web/packages/agenta-ui/src/components/presentational/section/...
Uses early agent state, skeletons, lazy controls, commit indicators, and updated section loading behavior.
Session chat implementation
web/oss/src/components/AgentChatSlice/...
Splits the panel into a session shell and per-session conversations, with persistent queue/ephemeral state, transcript scrolling, tool display helpers, approvals, and loading placeholders.
Selection and layout state
web/oss/src/state/url/playground.ts, web/oss/src/components/Layout/Layout.tsx
Persists per-app selections, defers default application, and keeps the layout content tree stable across route and height changes.

Trace and integration flows

Layer / File(s) Summary
Trace summaries and freshness
web/packages/agenta-entities/src/trace/..., web/packages/agenta-entities/src/loadable/controller.ts, web/packages/agenta-playground/src/state/execution/...
Adds lightweight root/error span summaries, freshness-aware retries, and fresh-trace marking from execution results.
OAuth identity propagation
api/oss/src/core/gateway/connections/..., api/oss/src/apis/fastapi/tools/router.py
Carries connection slug and integration identity through signed OAuth state and completion postMessages.
Deferred tool-result filtering
services/runner/src/responder.ts, services/runner/src/tracing/otel.ts, services/runner/tests/unit/responder.test.ts
Excludes deferred-not-executed tool results from client-output storage and verifies pending retry behavior.

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main themes of the change: playground performance, chat UX, and connection-flow fixes.
Description check ✅ Passed The description is detailed and directly matches the changeset, covering playground performance, chat UX, and multi-connection fixes.
Docstring Coverage ✅ Passed Docstring coverage is 68.00% 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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fe-chore/request-optimizations-onbig

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.

ardaerzin added 2 commits July 7, 2026 21:10
plansQueryAtom and rolesQueryAtom were enabled as soon as the session
existed, so they fired before the profile resolved and the axios interceptor
had the user + project it needs — the request went out and was immediately
aborted. Gate `enabled` on user id + projectId as well so each query fires
once, only when it can actually succeed.
…gates

Stop firing project-wide list/catalog queries on plain page loads; defer
them behind gates that open only when a feature needs the data.

- Evaluator revision fan-out (POST /workflows/revisions/query): one-way
  evaluatorEnrichmentActivatedAtom gate; enrichment atoms return stable
  empties until a picker/switcher activates.
- Evaluator catalog (GET /evaluators/catalog/templates): dropdown body
  mounts only when popover opens; playground + revision drawer gate on the
  is_evaluator flag (builtin apps share the agenta:builtin: URI prefix).
- Current workflow/app by id: workflowDetailQueryAtomFamily + useCurrentAppLite
  resolve one artifact by id instead of listing the whole apps/evaluator
  catalog; dedupe duplicate /workflows/query calls.
- Sidebar: switcher lists read EMPTY_EVALUATORS_ATOM until opened; suppress
  recent-evaluator type-tag flash during workflow resolution.
- Browse-mode pickers read a stable empty list atom in scoped (app) mode.
- Annotation drawer reads empty evaluator refs while closed.
- Projects/webhooks: drop workspaceId/projectId params (backend scopes from
  session).
The playground drill-in provider (OSSdrillInUIProvider) is always mounted in
OSSPlaygroundShell and eagerly subscribed to the project-wide workflow list
(apps + evaluators) and the evaluator catalog via useWorkflowReferenceBridge —
a big-agents-only feature (workflow-as-tool references) that main never had. So
on big-agents those queries fired on every playground load regardless of the
list/catalog lazy-gates, since one eager subscriber mounts the shared atoms and
makes the other gates no-ops.

Gate the bridge behind a one-way activation latch: `workflows`/`workflowsLoading`
and the evaluator-catalog name map stay dormant until `activate()` is called —
on reference-picker open (AgentTemplateControl) or when an existing reference is
displayed (ReferenceToolFormView). A plain playground load with no workflow
references now pulls neither the workflow list nor the catalog.
ardaerzin added 15 commits July 8, 2026 03:01
Move the Model & harness draft useModelHarness into a ModelHarnessSectionDrawerBody
that only mounts while the section drawer is open (SectionDrawer destroyOnClose), and
extract the app-trigger provider groups into AppTriggerProviderGroups that fetches the
connections + ~90-app catalog only when there are app subscriptions to decorate. Both
previously fired on every agent-config render with the drawer/section collapsed.
Disk-backed SWR (localStorage seed + one background revalidate) for the static
agent-template schema and harness catalog, per-revision inspect (bounded LRU, the reload
long pole), and a {workflowId: type} map so playgroundEarlyAgentStateAtom knows agent-ness
synchronously on reload. Revalidations that serve from disk are demoted to low network
priority (fetch-adapter Fetch Priority hint) so they don't compete with the critical-path
queries. Versioned cache keys allow hard invalidation on response-shape changes.
Add playgroundEarlyAgentStateAtom (app-id keyed, backed by the persisted type map) so the
playground commits to the agent layout up front instead of defaulting to non-agent and
flipping once the revision's is_agent flag loads. Gate the page-header eval chrome, the
config-panel header (variant selector / view-type), the loading shell, and the MainLayout
split on this signal, staying neutral until agent-ness is confirmed (settled) so neither
the eval stack nor the prompt config header flashes on an agent reload.
…aterfall

Mount AgentCatalogPrefetcher on the playground when it's an agent (onboarding, or the early
signal), warming the agent-template schema + harness catalog alongside the revision/inspect
waterfall. On a cold first load the schema no longer fetches last (after inspect resolves
its ref); on a warm reload it just kicks the background revalidate a little earlier.
Hydrate per-run trace summaries in the background without competing with render-critical
traffic. Adds a low-priority traces client (withLowPriorityFetch / getLowPriorityTracesClient
sending the priority: "low" fetch hint) and a lighter traceSummaryQueryAtomFamily that the
loadable controller reads to derive a run's error/root-span from the summary instead of the
full trace. A just-finished run's trace may not be ingested yet, so markTraceAsFresh/isTraceFresh
give fresh traces an aggressive not-found retry grace (5x vs once), marked at run-finish in the
agent chat panel and execution runners. createBatchFetcher gains an idle flush mode
(requestIdleCallback) so background-hydration batches coalesce and yield to render.
…riority

The tool connections/catalog, trigger subscriptions/schedules, deploy environments, and
build-kit overlay all fire on agent-playground load but none are on the critical path to
first interactivity, so they shouldn't compete at High priority with the config/chat queries.
Add low-priority variants of the Fern tools/workflows clients (getLowPriority*Client, reusing
withLowPriorityFetch) and thread a lowPriority option through the axios fetchers via
lowPriorityWhenCached, then send all six with the priority: "low" fetch hint. Low priority
only bites under contention, so pages where this data is primary are unaffected.
…teway errors

The /billing/subscription, /billing/{catalog,pricing}, /access/{plans,roles} queries are
entitlement bootstrap that gate no render-critical UI, yet fired at High priority on every
load — and a degraded billing service 502-ing retried 3x at High (0.5-4s each), competing
with the config/chat critical path. Send them with the priority: "low" hint and share a
retry policy that fails fast on gateway errors (502/503/504) instead of hammering — the
entitlements fall back to hobby/free anyway.
…ound critical path

On an agent-playground load, only the by-id workflow detail, the current-app latest revision,
and the current-app revision detail are needed for first paint. The sidebar apps list
(queryWorkflows), the sidebar prompt/agent-split batch that fetches every app's latest revision
for the is_agent badge (fetchWorkflowsBatch), and the variant-picker label query
(queryWorkflowVariants) were all firing High and competing with that critical path. Thread a
lowPriority option through the shared functions and pass it only at those non-critical call
sites. The agent-flags batch still primes the per-app caches, so the critical current-app fetch
can share it. (Left the per-revision detail batch's comparison wave for a dedup follow-up.)
…isions list

On a cold playground first paint the displayed revision was fetched twice: the
revisions-by-workflow list fetches it (and primes the per-revision detail cache under the same
key), while workflowQueryAtomFamily races ahead and fires its own by-id round-trip. Before that
standalone fetch, await any in-flight revisions-by-workflow query and re-check the primed cache,
so the revision resolves from the list instead. Best-effort and wrapped in try/catch — any miss
or error falls through to the direct fetch, so the molecule's data path is never at risk.
…r idle

The access/plans, access/roles, billing/{subscription,catalog,pricing} queries are entitlement
chrome — never needed for first paint, and their permission/entitlement consumers already default
to not-ready until loaded. Add an idleReadyAtom (flips true on the first requestIdleCallback, ~2s
timeout fallback, then sticky) and gate these queries' enabled on it, so they fire once the browser
has a spare moment instead of joining the load burst. Complements the earlier priority demotion:
demote reorders, this keeps them out of the concurrent flood that saturates a capacity-limited backend.
On a cold load with no cached workspace->org mapping, selectedOrgQueryAtom first keys by the
workspace id, resolves the org via the project, and fetches the org; caching the workspace->org
pair then lets selectedOrgIdAtom resolve the org id, so the atom re-keys to ["selectedOrg", orgId]
and refetches the same org — two identical /organizations/{id} round trips on load. Seed the
org-id-keyed cache from the first fetch so the re-keyed query (refetchOnMount:false) and
resolveWorkspaceIdForOrg's getQueryData fast-path serve it from cache.
Hold the agent panes' real shape while they load instead of a blank canvas. AgentChatSkeleton
mirrors the chat layout (session tabs, transcript turns, composer) and fills the two gaps — the
revision still resolving the agent flag, and the lazy AgentChatPanel (AI SDK) chunk loading — as
the dynamic-import fallback and the pending agent-generation host. AgentConfigSkeleton mirrors the
config section-row list (Model & harness, Instructions, Tools, MCP, Skills, Triggers, Advanced),
surfaced via a new loadingFallback prop on PlaygroundConfigSection so the panel shows a
layout-matched skeleton instead of the generic pulse boxes.
A restored turn's first-seen timestamp is the reload moment, not the turn's send time — stamping
it made old turns read "just now" until (or forever if) their trace loaded. Track restored ids
and skip stamping them, so their timestamp slot holds a placeholder until the real trace time
arrives (settled-with-no-trace shows nothing, never a wrong time). Likewise the per-message metrics:
usage renders immediately while only the latency slot waits on the trace, held by a fixed-size
Skeleton placeholder so the row neither shifts nor blanks known data.
…t-optimizations-onbig

# Conflicts:
#	web/packages/agenta-shared/src/api/index.ts
@ardaerzin ardaerzin changed the title perf(frontend): gate access plans/roles queries on full auth context perf(frontend): cut agent playground load & reload time Jul 8, 2026
@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 7

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/AgentChatSkeleton.tsx (1)

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

Prettier format check is failing in CI.

The pipeline logs show pnpm run format (prettier --check "**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts,mdx}") failing. Run pnpm lint-fix in the web folder and commit the formatted output before merging.

As per coding guidelines: "Frontend changes require running pnpm lint-fix within the web folder before committing."

Sources: Coding guidelines, Pipeline failures

🧹 Nitpick comments (7)
web/packages/agenta-entities/src/workflow/state/store.ts (1)

1124-1147: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid relying on query.promise here — TanStack Query v5.90.21 exposes it, but only as an experimental/internal API, so this dedup path is brittle across upgrades. A supported await path would keep the optimization stable.

web/ee/src/components/Scripts/assets/CloudScripts.tsx (1)

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

Remove commented-out Crisp code and orphaned explanatory comments.

Commenting out code rather than removing it is a code smell. The explanatory comments at lines 23–32 now describe behavior that no longer exists in active code, which is confusing. Additionally, useAppTheme() at line 11 still runs but appTheme is only referenced in the commented-out blocks, causing unnecessary re-renders on theme changes.

If Crisp removal is permanent, remove the commented-out code, the orphaned comments, and the useAppTheme() call. If temporary, add a TODO explaining why and when it should be re-enabled.

♻️ Proposed cleanup
 const CloudScripts = () => {
-    const {appTheme} = useAppTheme()
-
-    // useEffect(() => {
-    //     const isCrispEnabled = !!getEnv("NEXT_PUBLIC_CRISP_WEBSITE_ID")
-
-    //     if (!isCrispEnabled) {
-    //         return
-    //     }
-
-    //     Crisp.configure(getEnv("NEXT_PUBLIC_CRISP_WEBSITE_ID"))
-    // }, [])
-
-    // The Crisp chatbox renders in its own cross-origin iframe, so we can't style
-    // its light/dark useCrispChat from our CSS, and crisp-sdk-web exposes no runtime
-    // light/dark toggle (only the accent color via setColorTheme). Darken the
-    // accent in dark mode so the launcher/accent reads less out-of-place; light
-    // restores the dashboard's "default" accent.
-    //
-    // TODO(dark-mode): for the chat WINDOW itself to render dark, enable dark mode
-    // in the Crisp dashboard (Settings → Chatbox → Appearance). That follows the
-    // visitor's *system* color scheme — the SDK has no API to bind it to our
-    // in-app theme toggle, so this accent tweak is the only code-side lever.
-    // useEffect(() => {
-    //     const isCrispEnabled = !!getEnv("NEXT_PUBLIC_CRISP_WEBSITE_ID")
-
-    //     if (!isCrispEnabled) {
-    //         return
-    //     }
-
-    //     Crisp.setColorTheme(
-    //         appTheme === ThemeMode.Dark ? ChatboxColors.Black : ChatboxColors.Default,
-    //     )
-    // }, [appTheme])

     return (
web/packages/agenta-entities/src/loadable/controller.ts (2)

224-245: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate trace-derived output logic across two atom families.

getDerivedOutputValues inside connectedRowsAtomFamily (lines 224-245) and the body of derivedOutputValuesAtomFamily (lines 1920-1945) contain the identical rootSpan-extraction + extractAgData + fallback block. This PR touches both copies in lockstep to swap to traceSummaryQueryAtomFamily, which is exactly the kind of duplication that's easy to let drift on the next change.

Consider extracting a shared helper, e.g. getDataSourceForOutputMapping(get, executionResult), and calling it from both atom families.

♻️ Proposed extraction
+const getOutputMappingDataSource = (
+    get: Getter,
+    executionResult: {traceId?: string; output?: unknown},
+): Record<string, unknown> | null => {
+    let dataSource: Record<string, unknown> | null = null
+    if (executionResult.traceId) {
+        const rootSpan = get(traceSummaryQueryAtomFamily(executionResult.traceId)).data?.rootSpan
+        if (rootSpan) {
+            const agData = extractAgData(rootSpan)
+            if (agData && Object.keys(agData).length > 0) {
+                dataSource = {data: agData}
+            }
+        }
+    }
+    if (!dataSource && executionResult.output && typeof executionResult.output === "object") {
+        dataSource = executionResult.output as Record<string, unknown>
+    }
+    return dataSource
+}

Then both call sites reduce to const dataSource = getOutputMappingDataSource(get, executionResult).

Also applies to: 1920-1945


1626-1660: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

getTraceErrorFromSpans duplicates the traversal intent of getTraceErrorFromResponse.

Both walk spans looking for the first non-tool run-failure span and extract its message; one recurses a tree, the other iterates a flat list. Since they already share isRunFailureSpanType/spanErrorMessage, this is a minor, low-risk duplication (different input shapes justify separate functions), but worth a one-line comment noting the relationship if kept as-is.

web/oss/src/components/Playground/Components/PlaygroundVariantConfig/assets/AgentConfigSkeleton.tsx (1)

27-32: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use clsx for conditional className instead of string concatenation.

Sibling files in this cohort (PlaygroundVariantConfig/index.tsx, PlaygroundConfigSection.tsx) use clsx for conditional classNames; this file mixes plain string concatenation.

♻️ Proposed refactor
+import clsx from "clsx"
+
 const AgentConfigSkeleton = () => (
     <div className="flex flex-col px-4" aria-busy aria-label="Loading agent configuration">
         {ROWS.map((row, i) => (
             <div
                 key={i}
-                className={
-                    "flex items-center gap-3 py-6" +
-                    (i < ROWS.length - 1
-                        ? " border-0 border-b border-solid border-[var(--ag-rgba-051729-06)]"
-                        : "")
-                }
+                className={clsx(
+                    "flex items-center gap-3 py-6",
+                    i < ROWS.length - 1 &&
+                        "border-0 border-b border-solid border-[var(--ag-rgba-051729-06)]",
+                )}
             >
web/oss/src/components/Playground/Components/PlaygroundVariantConfig/assets/PlaygroundVariantConfigHeader.tsx (1)

101-110: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate agent-header-mode formula.

isAgentEffective/showAgentHeader here is computed with the exact same three inputs (isAgentModeAtomFamily, playgroundEarlyAgentStateAtom, workflow query isPending) as isAgentHeaderMode in PlaygroundVariantConfig/index.tsx (lines 71-78). See the consolidated refactor suggestion there.

web/oss/src/components/Playground/Components/PlaygroundVariantConfig/index.tsx (1)

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

Extract shared useAgentHeaderMode/atom to avoid duplicated agent-loading logic.

isAgentHeaderMode here and showAgentHeader in PlaygroundVariantConfigHeader.tsx (lines 101-110) are computed with the exact same formula and inputs (isAgentModeAtomFamily(variantId), playgroundEarlyAgentStateAtom, and the workflow query's isPending). Since index.tsx uses this flag to decide what to pass as extraActions to the header, and the header independently recomputes the same flag to decide its own rendering, the two must stay manually in sync — a future edit to one without the other could desync the header's "Configuration" title from the extraActions it's given.

Consider extracting this into a single derived atom or hook (e.g. alongside playgroundEarlyAgentStateAtom in @/oss/state/workflow) and consuming it from both files.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a0d17c1a-7944-41dc-8e61-c45319c7bf61

📥 Commits

Reviewing files that changed from the base of the PR and between aeb6135 and fa8d2e2.

📒 Files selected for processing (74)
  • .gitignore
  • web/ee/src/components/Scripts/assets/CloudScripts.tsx
  • web/oss/src/components/AgentChatSlice/AgentChatPanel.tsx
  • web/oss/src/components/AgentChatSlice/components/AgentChatSkeleton.tsx
  • web/oss/src/components/AgentChatSlice/components/AgentMessage.tsx
  • web/oss/src/components/DrillInView/OSSdrillInUIProvider.tsx
  • web/oss/src/components/Evaluators/components/EvaluatorTemplateDropdown.tsx
  • web/oss/src/components/Filters/Filters.tsx
  • web/oss/src/components/Playground/Components/AgentCatalogPrefetcher.tsx
  • web/oss/src/components/Playground/Components/MainLayout/index.tsx
  • web/oss/src/components/Playground/Components/Menus/SelectVariant/index.tsx
  • web/oss/src/components/Playground/Components/PlaygroundHeader/index.tsx
  • web/oss/src/components/Playground/Components/PlaygroundVariantConfig/assets/AgentConfigSkeleton.tsx
  • web/oss/src/components/Playground/Components/PlaygroundVariantConfig/assets/PlaygroundVariantConfigHeader.tsx
  • web/oss/src/components/Playground/Components/PlaygroundVariantConfig/index.tsx
  • web/oss/src/components/Playground/Playground.tsx
  • web/oss/src/components/PlaygroundRouter/index.tsx
  • web/oss/src/components/SharedDrawers/AnnotateDrawer/index.tsx
  • web/oss/src/components/Sidebar/hooks/useSidebarConfig/index.tsx
  • web/oss/src/components/Sidebar/hooks/useWorkflowSwitcher.tsx
  • web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx
  • web/oss/src/components/pages/app-management/modals/CustomWorkflowModal/hooks/useCustomWorkflowConfig.tsx
  • web/oss/src/hooks/usePlaygroundNavigation.ts
  • web/oss/src/lib/atoms/breadcrumb/index.ts
  • web/oss/src/lib/helpers/auth/AuthProvider.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/[app_id]/index.tsx
  • web/oss/src/state/access/atoms.ts
  • web/oss/src/state/app/atoms/fetcher.ts
  • web/oss/src/state/app/hooks.ts
  • web/oss/src/state/app/selectors/app.ts
  • web/oss/src/state/boot/idleReady.ts
  • web/oss/src/state/org/selectors/org.ts
  • web/oss/src/state/workflow/index.ts
  • web/oss/src/state/workflow/selectors/workflow.ts
  • web/packages/agenta-annotation-ui/src/components/CreateQueueDrawer/index.tsx
  • web/packages/agenta-entities/src/environment/api/api.ts
  • web/packages/agenta-entities/src/environment/state/store.ts
  • web/packages/agenta-entities/src/gatewayTool/api/api.ts
  • web/packages/agenta-entities/src/gatewayTool/api/client.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolCatalogIntegrations.ts
  • web/packages/agenta-entities/src/gatewayTool/hooks/useToolConnectionsQuery.ts
  • web/packages/agenta-entities/src/gatewayTrigger/api/api.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSchedules.ts
  • web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSubscriptions.ts
  • web/packages/agenta-entities/src/loadable/controller.ts
  • web/packages/agenta-entities/src/trace/api/api.ts
  • web/packages/agenta-entities/src/trace/api/client.ts
  • web/packages/agenta-entities/src/trace/index.ts
  • web/packages/agenta-entities/src/trace/state/index.ts
  • web/packages/agenta-entities/src/trace/state/store.ts
  • web/packages/agenta-entities/src/workflow/api/api.ts
  • web/packages/agenta-entities/src/workflow/index.ts
  • web/packages/agenta-entities/src/workflow/state/evaluatorUtils.ts
  • web/packages/agenta-entities/src/workflow/state/index.ts
  • web/packages/agenta-entities/src/workflow/state/inspectMeta.ts
  • web/packages/agenta-entities/src/workflow/state/persistedAgentType.ts
  • web/packages/agenta-entities/src/workflow/state/persistedCatalog.ts
  • web/packages/agenta-entities/src/workflow/state/persistedInspect.ts
  • web/packages/agenta-entities/src/workflow/state/store.ts
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentTemplateControl.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/ReferenceToolFormView.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/TriggerManagementSection.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/components/PlaygroundConfigSection.tsx
  • web/packages/agenta-entity-ui/src/selection/adapters/index.ts
  • web/packages/agenta-entity-ui/src/selection/adapters/useEnrichedEvaluatorAdapter.ts
  • web/packages/agenta-entity-ui/src/selection/index.ts
  • web/packages/agenta-playground/src/state/execution/executionItems.ts
  • web/packages/agenta-playground/src/state/execution/executionRunner.ts
  • web/packages/agenta-sdk/src/config.ts
  • web/packages/agenta-sdk/src/resources.ts
  • web/packages/agenta-shared/src/api/axios.ts
  • web/packages/agenta-shared/src/api/index.ts
  • web/packages/agenta-shared/src/utils/createBatchFetcher.ts
  • web/packages/agenta-ui/src/drill-in/context/DrillInUIContext.tsx

Comment on lines +450 to +455
// Agent identified early (persisted agent-type map) but the
// revision hasn't resolved the flag yet — hold the chat pane's
// shape instead of a blank canvas until the host mounts.
if (isAgentConfig && singleEntityQuery.isPending) {
return <AgentChatSkeleton key="agent-generation-skeleton" />
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

ast-grep outline web/oss/src/components/Playground/Components/MainLayout/index.tsx --view expanded

Repository: Agenta-AI/agenta

Length of output: 597


🏁 Script executed:

sed -n '100,240p' web/oss/src/components/Playground/Components/MainLayout/index.tsx
printf '\n----\n'
sed -n '430,475p' web/oss/src/components/Playground/Components/MainLayout/index.tsx
printf '\n----\n'
rg -n "isAgentModeAtomFamily|playgroundEarlyAgentStateAtom|layoutEntityIds|singleEntityQuery|isComparisonView|configEntityIds" web/oss/src/components/Playground -n

Repository: Agenta-AI/agenta

Length of output: 19733


🏁 Script executed:

rg -n "selectors\.query\(|workflowMolecule\.selectors\.query|query\(singleEntityId\)|isPending" web/oss/src -g '*.ts' -g '*.tsx'

Repository: Agenta-AI/agenta

Length of output: 37772


🏁 Script executed:

ast-grep outline web/oss/src/state/workflow/selectors/workflow.ts --view expanded
printf '\n----\n'
sed -n '1,220p' web/oss/src/state/workflow/selectors/workflow.ts

Repository: Agenta-AI/agenta

Length of output: 7332


🏁 Script executed:

rg -n "workflowMolecule|selectors:.*query|queryAtomFamily|workflowDetailQueryAtomFamily|createEntityController|empty string|if \(!id\)" web/oss/src/state web/oss/src/components/Playground web/oss/src/components/AgentChatSlice -g '*.ts' -g '*.tsx'

Repository: Agenta-AI/agenta

Length of output: 17763


🏁 Script executed:

ast-grep outline web/oss/src/state/entities/workflow --view expanded
printf '\n----\n'
rg -n "createEntityController|queryAtomFamily|selectors:.*query|workflowMolecule" web/oss/src/state/entities/workflow -g '*.ts' -g '*.tsx'

Repository: Agenta-AI/agenta

Length of output: 333


🏁 Script executed:

fd -a "workflow*.ts" web/oss/src
printf '\n----\n'
fd -a "*workflow*" web/oss/src/state web/oss/src/components | head -n 200

Repository: Agenta-AI/agenta

Length of output: 518


🏁 Script executed:

rg -n --fixed-strings "workflowMolecule" web/oss/src/state web/oss/src/components web/oss/src/entities -g '*.ts' -g '*.tsx'
printf '\n----\n'
rg -n --fixed-strings "queryAtomFamily" web/oss/src/state web/oss/src/components web/oss/src/entities -g '*.ts' -g '*.tsx'

Repository: Agenta-AI/agenta

Length of output: 32317


🏁 Script executed:

fd -a "workflow*.ts" . | head -n 200
printf '\n----\n'
fd -a "createEntityController.ts" . | head -n 50

Repository: Agenta-AI/agenta

Length of output: 579


🏁 Script executed:

ast-grep outline web/packages/agenta-entities/src --view expanded
printf '\n----\n'
rg -n --fixed-strings "workflowMolecule" web/packages/agenta-entities/src -g '*.ts' -g '*.tsx'

Repository: Agenta-AI/agenta

Length of output: 50373


🏁 Script executed:

ast-grep outline web/packages/agenta-entities/src/workflow/state/molecule.ts --view expanded
printf '\n----\n'
sed -n '1,220p' web/packages/agenta-entities/src/workflow/state/molecule.ts

Repository: Agenta-AI/agenta

Length of output: 11166


🏁 Script executed:

rg -n "workflowQueryAtomFamily|atomFamily\\(\\(workflowId: string\\)|queryAtomFamily = atomFamily|isPlaceholderId|isLocalDraftId" web/packages/agenta-entities/src/workflow/state/store.ts
printf '\n----\n'
sed -n '1,220p' web/packages/agenta-entities/src/workflow/state/store.ts

Repository: Agenta-AI/agenta

Length of output: 10022


🏁 Script executed:

sed -n '1090,1185p' web/packages/agenta-entities/src/workflow/state/store.ts
printf '\n----\n'
sed -n '1185,1235p' web/packages/agenta-entities/src/workflow/state/store.ts

Repository: Agenta-AI/agenta

Length of output: 8016


Make the agent skeleton key unique per rendered entity.

Because this branch sits inside layoutEntityIds.map(...), a multi-entity render gives every match the same <AgentChatSkeleton key="agent-generation-skeleton" />, which can collapse entries in React. Key it by variantId instead.

🛡️ Proposed defensive fix
                                     if (isAgentConfig && singleEntityQuery.isPending) {
-                                        return <AgentChatSkeleton key="agent-generation-skeleton" />
+                                        return (
+                                            <AgentChatSkeleton
+                                                key={`agent-generation-skeleton-${variantId}`}
+                                            />
+                                        )
                                     }
📝 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.

Suggested change
// Agent identified early (persisted agent-type map) but the
// revision hasn't resolved the flag yet — hold the chat pane's
// shape instead of a blank canvas until the host mounts.
if (isAgentConfig && singleEntityQuery.isPending) {
return <AgentChatSkeleton key="agent-generation-skeleton" />
}
if (isAgentConfig && singleEntityQuery.isPending) {
return (
<AgentChatSkeleton
key={`agent-generation-skeleton-${variantId}`}
/>
)
}

Comment thread web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx Outdated
Comment on lines +1 to +34
import {atom} from "jotai"

/**
* One-shot "the browser has had a spare (idle) moment since load" flag.
*
* Gate NON-critical bootstrap queries (entitlements, billing, permission catalogs) on this so they
* don't fire in the same burst as the first-paint-critical requests — on a capacity-limited backend
* a flood of concurrent requests on load saturates the workers and slows the critical ones. Flips
* true on the first `requestIdleCallback` (or within ~2s via its timeout, whichever comes first),
* then stays true for the session, so it defers the FIRST load without re-deferring on every read.
*
* SSR-safe: resolves immediately when there is no `window` (deferral is a client-only concern).
*/
const idleReadyStateAtom = atom(false)

idleReadyStateAtom.onMount = (set) => {
if (typeof window === "undefined") {
set(true)
return
}
const w = window as Window & {
requestIdleCallback?: (cb: () => void, opts?: {timeout: number}) => number
cancelIdleCallback?: (handle: number) => void
}
if (typeof w.requestIdleCallback === "function") {
const handle = w.requestIdleCallback(() => set(true), {timeout: 2000})
return () => w.cancelIdleCallback?.(handle)
}
// Safari (no rIC): fall back to a short timeout.
const timer = window.setTimeout(() => set(true), 1500)
return () => window.clearTimeout(timer)
}

export const idleReadyAtom = atom((get) => get(idleReadyStateAtom))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Logic is sound (SSR-safe, one-shot, rIC + Safari fallback with cleanup). No functional concerns with the atom itself.

However, the check code styling / TypeScript format job is failing (prettier --check). Run pnpm lint-fix in the web folder and commit the reformatted output before merging.

As per coding guidelines: "Frontend changes require running pnpm lint-fix within the web folder before committing".

🧰 Tools
🪛 GitHub Actions: 11 - check code styling / 1_TypeScript format.txt

[error] 1-1: Command failed with exit code 1: pnpm run format (prettier --check "**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts,mdx}").

🪛 GitHub Actions: 11 - check code styling / TypeScript format

[error] 1-1: Command failed with exit code 1: pnpm run format (prettier --check "**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts,mdx}").

Sources: Coding guidelines, Pipeline failures

Comment on lines +22 to +24
playgroundEarlyAgentStateAtom,
type CurrentWorkflowContext,
type PlaygroundAgentState,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Run pnpm lint-fix to resolve prettier formatting failures.

Pipeline failure: pnpm run format (prettier check) is failing. Per coding guidelines, frontend changes require running pnpm lint-fix within the web folder before committing.

Sources: Coding guidelines, Pipeline failures

Comment on lines +104 to +106
// Persisted agent-type map (cold-reload fallback for playgroundEarlyAgentStateAtom)
export {readPersistedAgentType} from "./persistedAgentType"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

CI prettier --check is failing — run the formatter before merge.

The check code styling / TypeScript format job failed (pnpm run format). Run pnpm lint-fix (or pnpm run format) in web and commit the result so CI passes.

As per coding guidelines: "Frontend changes require running pnpm lint-fix within the web folder before committing".

Sources: Coding guidelines, Pipeline failures

Comment on lines +6 to +12
export {
axios,
createAxiosInstance,
configureAxios,
resetAxiosConfig,
lowPriorityWhenCached,
} from "./axios"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Pipeline format check is failing.

The pnpm run format (prettier --check) pipeline step fails. Run pnpm lint-fix in the web folder before committing, as required by the coding guidelines for frontend changes.

As per coding guidelines: **/*.{js,jsx,ts,tsx}: Frontend changes require running pnpm lint-fix within the web folder before committing.

Sources: Coding guidelines, Pipeline failures

An earlier change commented out the two Crisp effects (configure + accent-color-on-theme),
which also left useEffect/Crisp/ChatboxColors/ThemeMode/getEnv/appTheme unused and failed the
lint check. Restore the effects (both still gated on NEXT_PUBLIC_CRISP_WEBSITE_ID).

@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: 6

🧹 Nitpick comments (1)
web/oss/src/state/url/playground.ts (1)

156-179: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Read-modify-write on a shared localStorage key races across tabs.

readPersistedSelection/writePersistedSelection share a single JSON blob keyed by appId. Two tabs updating different apps' selections concurrently can lose one tab's write (read stale map → overwrite with only its own update). Impact is low since it's self-healing UX state, but a per-app key avoids the read-modify-write entirely.

♻️ Proposed fix: scope storage key per app
-const PERSISTED_SELECTION_KEY = "agenta:playground:last-selection"
-
-const readPersistedSelection = (appId: string): string[] => {
-    if (!isBrowser) return []
-    try {
-        const raw = window.localStorage.getItem(PERSISTED_SELECTION_KEY)
-        const map = raw ? (JSON.parse(raw) as Record<string, string[]>) : {}
-        return sanitizeRevisionList(Array.isArray(map[appId]) ? map[appId] : [])
-    } catch {
-        return []
-    }
-}
-
-const writePersistedSelection = (appId: string, ids: string[]) => {
-    if (!isBrowser) return
-    try {
-        const raw = window.localStorage.getItem(PERSISTED_SELECTION_KEY)
-        const map = raw ? (JSON.parse(raw) as Record<string, string[]>) : {}
-        map[appId] = ids
-        window.localStorage.setItem(PERSISTED_SELECTION_KEY, JSON.stringify(map))
-    } catch {
-        // Quota/serialization failures are non-fatal — the next entry just waits for defaults.
-    }
-}
+const PERSISTED_SELECTION_PREFIX = "agenta:playground:last-selection:"
+
+const readPersistedSelection = (appId: string): string[] => {
+    if (!isBrowser) return []
+    try {
+        const raw = window.localStorage.getItem(`${PERSISTED_SELECTION_PREFIX}${appId}`)
+        return sanitizeRevisionList(raw ? (JSON.parse(raw) as string[]) : [])
+    } catch {
+        return []
+    }
+}
+
+const writePersistedSelection = (appId: string, ids: string[]) => {
+    if (!isBrowser) return
+    try {
+        window.localStorage.setItem(`${PERSISTED_SELECTION_PREFIX}${appId}`, JSON.stringify(ids))
+    } catch {
+        // Quota/serialization failures are non-fatal — the next entry just waits for defaults.
+    }
+}

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b1b5541e-8ab7-44f8-a73c-7110a3844c51

📥 Commits

Reviewing files that changed from the base of the PR and between 8345a1d and 30ffc84.

📒 Files selected for processing (56)
  • .gitignore
  • api/oss/src/apis/fastapi/tools/router.py
  • api/oss/src/core/gateway/connections/service.py
  • api/oss/src/core/gateway/connections/utils.py
  • api/oss/tests/pytest/unit/tools/test_oauth_state_identity.py
  • services/runner/src/responder.ts
  • services/runner/src/tracing/otel.ts
  • services/runner/tests/unit/responder.test.ts
  • web/oss/src/components/AgentChatSlice/AgentChatPanel.tsx
  • web/oss/src/components/AgentChatSlice/AgentConversation.tsx
  • web/oss/src/components/AgentChatSlice/assets/toolDisplay.ts
  • web/oss/src/components/AgentChatSlice/components/AgentChatSkeleton.tsx
  • web/oss/src/components/AgentChatSlice/components/AgentMessage.tsx
  • web/oss/src/components/AgentChatSlice/components/ApprovalDock.tsx
  • web/oss/src/components/AgentChatSlice/components/MountFade.tsx
  • web/oss/src/components/AgentChatSlice/components/SessionRail.tsx
  • web/oss/src/components/AgentChatSlice/components/SessionTabLabel.tsx
  • web/oss/src/components/AgentChatSlice/components/SessionTagBar.tsx
  • web/oss/src/components/AgentChatSlice/components/ToolActivity.tsx
  • web/oss/src/components/AgentChatSlice/components/clientTools/ConnectToolWidget.tsx
  • web/oss/src/components/AgentChatSlice/hooks/useAgentChatQueue.ts
  • web/oss/src/components/AgentChatSlice/state/sessions.ts
  • web/oss/src/components/Layout/Layout.tsx
  • web/oss/src/components/Playground/Components/AgentCatalogPrefetcher.tsx
  • web/oss/src/components/Playground/Components/AgentCommitNotice.tsx
  • web/oss/src/components/Playground/Components/AgentRevisionSelector/index.tsx
  • web/oss/src/components/Playground/Components/MainLayout/index.tsx
  • web/oss/src/components/Playground/Components/PlaygroundVariantConfig/assets/PlaygroundVariantConfigHeader.tsx
  • web/oss/src/components/Playground/Components/PlaygroundVariantConfig/index.tsx
  • web/oss/src/components/Playground/Playground.tsx
  • web/oss/src/components/pages/agent-home/PlaygroundOnboarding/Reveal.tsx
  • web/oss/src/state/url/playground.ts
  • web/oss/src/styles/globals.css
  • web/oss/tailwind.config.ts
  • web/packages/agenta-entities/src/workflow/commitDiff/gatewayName.ts
  • web/packages/agenta-entities/src/workflow/core/schema.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/persistedInspect.ts
  • web/packages/agenta-entities/src/workflow/state/store.ts
  • web/packages/agenta-entities/tests/unit/agent-commit-diff.test.ts
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentTemplateControl.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SchemaPropertyRenderer.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/AgentConfigSkeleton.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/sectionGroups.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/components/PlaygroundConfigSection.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/index.ts
  • web/packages/agenta-entity-ui/src/index.ts
  • web/packages/agenta-playground-ui/src/components/ExecutionItems/index.tsx
  • web/packages/agenta-playground-ui/src/components/index.ts
  • web/packages/agenta-playground/src/state/execution/agentMessageQueue.ts
  • web/packages/agenta-playground/tests/unit/agentMessageQueue.test.ts
  • web/packages/agenta-shared/src/state/agentCommitSignal.ts
  • web/packages/agenta-shared/src/state/index.ts
  • web/packages/agenta-ui/src/RichChatInput/RichChatInput.tsx
  • web/packages/agenta-ui/src/components/presentational/section/ConfigAccordionSection.tsx
✅ Files skipped from review due to trivial changes (7)
  • api/oss/tests/pytest/unit/tools/test_oauth_state_identity.py
  • web/packages/agenta-shared/src/state/index.ts
  • web/packages/agenta-entities/tests/unit/agent-commit-diff.test.ts
  • web/oss/tailwind.config.ts
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/sectionGroups.tsx
  • services/runner/src/tracing/otel.ts
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (10)
  • web/packages/agenta-entities/src/workflow/state/index.ts
  • web/oss/src/components/AgentChatSlice/components/AgentChatSkeleton.tsx
  • web/packages/agenta-entities/src/workflow/index.ts
  • web/oss/src/components/Playground/Components/AgentCatalogPrefetcher.tsx
  • web/oss/src/components/AgentChatSlice/components/AgentMessage.tsx
  • web/oss/src/components/Playground/Components/PlaygroundVariantConfig/assets/PlaygroundVariantConfigHeader.tsx
  • web/packages/agenta-entities/src/workflow/state/persistedInspect.ts
  • web/oss/src/components/Playground/Components/PlaygroundVariantConfig/index.tsx
  • web/packages/agenta-entities/src/workflow/state/store.ts
  • web/oss/src/components/AgentChatSlice/AgentChatPanel.tsx

Comment thread api/oss/src/apis/fastapi/tools/router.py Outdated
Comment thread web/oss/src/components/AgentChatSlice/AgentConversation.tsx Outdated
Comment thread web/oss/src/components/Playground/Components/AgentCommitNotice.tsx
Comment on lines +393 to +397
key={
renderAgentGenerationHost
? "agent-config-host"
: `variant-config-${variantId}`
}

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether any caller passes configEntityIdsOverride with more than one id
rg -n "configEntityIdsOverride" web/oss/src -g '*.tsx' -A5 -B5

Repository: Agenta-AI/agenta

Length of output: 8573


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the MainLayout component structure and inspect the keying logic.
ast-grep outline web/oss/src/components/Playground/Components/MainLayout/index.tsx --view expanded

printf '\n--- MainLayout excerpt ---\n'
sed -n '160,420p' web/oss/src/components/Playground/Components/MainLayout/index.tsx

printf '\n--- Callers / configEntityIds construction ---\n'
sed -n '1,240p' web/oss/src/components/Evaluators/components/ConfigureEvaluator/index.tsx
printf '\n---\n'
sed -n '1,260p' web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx
printf '\n---\n'
sed -n '1,240p' web/oss/src/components/pages/evaluations/NewEvaluation/Components/CreateEvaluatorDrawer/index.tsx

Repository: Agenta-AI/agenta

Length of output: 39912


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the evaluator config IDs atom and the code that builds it.
rg -n "evaluatorConfigEntityIdsAtom|configEntityIds" web/oss/src/components/Evaluators -g '*.ts' -g '*.tsx' -A4 -B4

printf '\n--- atom file(s) ---\n'
fd -a "atoms.ts" web/oss/src/components/Evaluators
fd -a "atoms.tsx" web/oss/src/components/Evaluators

printf '\n--- around evaluatorConfigEntityIdsAtom definition ---\n'
for f in $(rg -l "evaluatorConfigEntityIdsAtom" web/oss/src/components/Evaluators -g '*.ts' -g '*.tsx'); do
  echo "FILE: $f"
  sed -n '1,260p' "$f"
  echo
done

Repository: Agenta-AI/agenta

Length of output: 19915


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect graph mutation logic and node-graph shape constraints.
rg -n "connectDownstreamNode|playgroundNodesAtom|depth > 0|downstreamNodes|changePrimaryNode|add.*downstream|remove.*downstream" web/oss/src web/src -g '*.ts' -g '*.tsx' -A4 -B4

printf '\n--- playground state / graph atoms ---\n'
for f in $(rg -l "playgroundNodesAtom|connectDownstreamNode|changePrimaryNode" web/oss/src web/src -g '*.ts' -g '*.tsx'); do
  echo "FILE: $f"
  sed -n '1,260p' "$f"
  echo
done

Repository: Agenta-AI/agenta

Length of output: 36579


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect how layoutEntityIds is derived in MainLayout and whether it can differ from configEntityIds.
rg -n "layoutEntityIds|renderAgentGenerationHost|isSingleAgentEntity|singleEntityId|configEntityIdsOverride" web/oss/src/components/Playground/Components/MainLayout/index.tsx -A8 -B8

printf '\n--- surrounding MainLayout state derivation ---\n'
sed -n '120,220p' web/oss/src/components/Playground/Components/MainLayout/index.tsx

Repository: Agenta-AI/agenta

Length of output: 16355


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the implementation and contract of connectDownstreamNode / downstream limits.
rg -n "connectDownstreamNode|disconnectSingleDownstreamNode|disconnectDownstreamNode" . -g '*.ts' -g '*.tsx' -A6 -B6

printf '\n--- candidate implementations ---\n'
for f in $(rg -l "disconnectSingleDownstreamNode|connectDownstreamNode" . -g '*.ts' -g '*.tsx'); do
  echo "FILE: $f"
  sed -n '1,260p' "$f"
  echo
done

Repository: Agenta-AI/agenta

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the exact downstream-connection implementation and the entity-id selectors.
sed -n '320,400p' web/packages/agenta-playground/src/state/controllers/playgroundController.ts

printf '\n--- entity id selectors ---\n'
rg -n "selectedEntityIdsAtom|displayedEntityIdsAtom|entityIdsAtom" web/packages/agenta-playground/src/state -g '*.ts' -A6 -B6

Repository: Agenta-AI/agenta

Length of output: 50372


Avoid a shared agent key here
configEntityIdsOverride can hold multiple downstream IDs, while renderAgentGenerationHost only follows layoutEntityIds. If this list ever has more than one item, "agent-config-host" collides across siblings; include variantId in the agent branch too.

Comment thread web/oss/src/components/Playground/Playground.tsx
Comment on lines +1095 to +1101
const unsubPersistSelection = store.sub(playgroundController.selectors.entityIds(), () => {
const appId = store.get(routerAppIdAtom) as string | null
if (!appId) return
const ids = sanitizeRevisionList(store.get(playgroundController.selectors.entityIds()))
if (ids.length > 0) writePersistedSelection(appId, ids)
})
unsubs.push(unsubPersistSelection)

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find flows that clear entityIds to [] while remaining on the same app (not the app-switch reset)
rg -n "setEntityIds,\s*\[\]" web/oss/src -g '*.ts' -g '*.tsx' -A5 -B5

Repository: Agenta-AI/agenta

Length of output: 3320


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant implementation around persisted playground selection.
sed -n '1,260p' web/oss/src/state/url/playground.ts | cat -n
printf '\n--- SNIP ---\n'
sed -n '900,1145p' web/oss/src/state/url/playground.ts | cat -n

printf '\n--- SEARCH ---\n'
rg -n "writePersistedSelection|ensurePlaygroundDefaults|entityIds\\(|persistedSelection|persistSelection|removeItem|delete.*selection|clear.*selection" web/oss/src/state/url/playground.ts web/oss/src/state -g '*.ts' -g '*.tsx' -A4 -B4

Repository: Agenta-AI/agenta

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '599,700p' web/oss/src/state/url/playground.ts | cat -n
printf '\n--- SNIP ---\n'
sed -n '1310,1375p' web/oss/src/state/url/playground.ts | cat -n

printf '\n--- WRITE SEARCH ---\n'
rg -n "PERSISTED_SELECTION_KEY|readPersistedSelection\\(|writePersistedSelection\\(|removeItem\\(PERSISTED_SELECTION_KEY\\)|delete map\\[appId\\]|map\\[appId\\] = ids" web/oss/src/state/url/playground.ts -A3 -B3

Repository: Agenta-AI/agenta

Length of output: 10331


Clear persisted selection when the app selection is emptied
web/oss/src/state/url/playground.ts:1095-1099 writePersistedSelection only runs for non-empty ids, so clearing the selection on the same app leaves the previous value in localStorage and it can reappear on reload. Remove that app entry (or persist []) when ids.length === 0.

…script

json.dumps leaves '<' intact, so an integration_key containing '</script>'
could terminate the completion page's script block (slug is regex-validated
URL-safe; integration_key is not). Escape '<' in every JSON value embedded
in the inline script.
…l both commit-notice rAFs

The per-session Maps (virtuoso snapshots, composer drafts, attachments with
live File blobs) outlived permanently deleted sessions. Move them to
state/sessionEphemera.ts and clear them from deleteSession/resetScope,
alongside the existing sessionMessagesAtom cleanup.

AgentCommitNotice's cleanup cancelled only the outer rAF; a fast active
on->off let the orphaned inner frame flash the notice back in.
…t variants

The fetch-priority refactor routes gatewayTrigger/workflow/trace api calls
through lowPriorityWhenCached and the @agenta/sdk/resources getters; the
unit mocks predated it, so the fakes never intercepted (real 404s) or the
missing export threw.
…t-optimizations-onbig

# Conflicts:
#	web/packages/agenta-entities/tests/unit/retrieveWorkflowRevision.test.ts
#	web/packages/agenta-entities/tests/unit/trace-migration-api.test.ts
@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

A freshly committed revision reads non-agent while its flags load, and the
early app-id signal can't bridge the gap when the router has no app id (the
onboarding flow rewrites the URL via history.replaceState, so routerAppIdAtom
never resolves). Unlatched, isAgentConfig flipped false and back — flipping
splitterKey agent->std->agent and remounting the whole Splitter twice on an
agent self-commit. Latch it exactly like the agent generation host: hold until
the entity loads as a definitively non-agent workflow.
…scope

The founding conversation lived under the fixed 'onboarding' scope forever:
after a reload the app playground (scoped to the app id) couldn't see it, and
the next onboarding entry's scope reset destroyed it permanently. On commit,
move the scope's sessions/open-tabs/active-id into the new app's bucket
(adoptScopeSessionsAtom) and flip the scope provider to the app scope in the
same batched update — the mounted panel re-reads identical sessions under the
new key, so nothing remounts, post-commit chat state lands where the app
playground reads it, and the reset can no longer reach it.
The onboarding composer disabled submit-on-Enter (Enter inserted a newline for
long descriptions) while the composer's shortcut hints still promised '<Enter>
Send' — so Enter appeared to do nothing. Enable it: Enter commits the agent
(same path as the Create-agent button, empty text is a no-op), and Cmd/Shift+
Enter keeps inserting newlines for multi-line descriptions.
The one-pause-per-turn latch gated the client-tool interaction EMIT, not just the
pause, so when an agent requested several connections in one step only the first got
a widget; the rest were force-settled as DEFERRED_NOT_EXECUTED siblings. The model
then didn't reliably re-request them (it narrated 'pending' instead), so the second
connection never appeared and the run continued without it.

Client tools are independent surfaces, unlike a single approval dialog. Emit a widget
for each pending client tool and mark each parked so it isn't force-settled; the turn
still ends once because pause() is idempotent. All requested connections now render
and the user resolves each; no deferral and no dependency on the model re-asking.
@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
api/oss/src/apis/fastapi/tools/router.py (2)

918-923: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Failure cards never notify the initiating opener.

Every failure call omits agenta_url, making AGENTA_POST_MESSAGE_ORIGIN null and skipping both postMessage calls. The requesting connection widget can therefore remain pending. Pass the web URL, but only post when both identity fields exist to avoid untagged multi-flow completions.

Proposed fix
 content=_oauth_card(
     success=False,
     error=...,
+    agenta_url=env.agenta.web_url,
     slug=state_slug,
     integration_key=state_integration,
 ),

Apply this to each failure card, then scope notification:

-    agenta_post_message_origin_js = _json_for_inline_script(agenta_origin)
+    completion_is_scoped = bool(agenta_origin and slug and integration_key)
+    agenta_post_message_origin_js = _json_for_inline_script(
+        agenta_origin if completion_is_scoped else None
+    )

Also applies to: 929-934, 949-954, 970-975, 981-986, 1477-1482, 1683-1702


939-944: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard project_id before calling UUID. decode_oauth_state() returns arbitrary JSON values, so state_payload["project_id"] can be null, a number, or bool; UUID(...) then raises TypeError/AttributeError, which isn’t caught here and turns this callback into a 500.

🧹 Nitpick comments (3)
web/oss/src/components/pages/agent-home/PlaygroundOnboarding/useAgentOnboarding.ts (1)

51-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Condense the new explanatory comments.

Keep only the non-obvious invariant in a single line.

Proposed cleanup
-    /**
-     * Chat scope for the onboarding surface: the fixed onboarding key pre-commit, the created
-     * app's own scope once real (the commit adopts the session into it in the same update).
-     */
+    /** Chat scope before commit, then the created app scope after session adoption. */
     chatScopeKey: string

-                    // Adopt the founding conversation into the new app's chat scope, in the SAME
-                    // batched update as the `chatScopeKey` flip below (`setRealAppId`): the mounted
-                    // panel re-reads identical sessions under the new key, so nothing remounts —
-                    // and after a reload the app playground finds the session (previously it was
-                    // stranded under the fixed onboarding scope, which the next onboarding entry
-                    // wipes — permanently destroying the conversation).
+                    // Move the founding session before switching scopes so reloads preserve it.

As per coding guidelines, “Keep in-code comments to one short line maximum unless a genuinely surprising constraint requires a brief exception.”

Also applies to: 171-176

Source: Coding guidelines

web/oss/src/components/Playground/Playground.tsx (1)

93-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Shorten the new multi-line comments.

These explanations can each be reduced to one line while preserving the relevant invariant.

As per coding guidelines, “Keep in-code comments to one short line maximum unless a genuinely surprising constraint requires a brief exception.”

Also applies to: 118-120, 145-146

Source: Coding guidelines

api/oss/src/apis/fastapi/tools/router.py (1)

897-901: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Condense the new block comments to single lines.

These comments repeat implementation details and can be reduced to concise summaries.

-        # Decode the HMAC-signed state up front to recover BOTH the project scope and the
-        # connection identity. The identity tags every card (success or failure) so the
-        # opener can tell WHICH connect flow finished — the playground can have several
-        # live at once (see ConnectToolWidget), and an untagged completion would settle
-        # all of them.
+        # Recover project scope and connection identity from the signed OAuth state.

As per coding guidelines, “Keep in-code comments to one short line maximum unless a genuinely surprising constraint requires a brief exception.”

Also applies to: 937-938, 1484-1488

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c05028c1-4871-454a-9918-b6ca26416c69

📥 Commits

Reviewing files that changed from the base of the PR and between 30ffc84 and 6538e57.

📒 Files selected for processing (14)
  • api/oss/src/apis/fastapi/tools/router.py
  • services/runner/src/engines/sandbox_agent/client-tools.ts
  • services/runner/tests/unit/client-tools.test.ts
  • web/oss/src/components/AgentChatSlice/AgentConversation.tsx
  • web/oss/src/components/AgentChatSlice/state/sessionEphemera.ts
  • web/oss/src/components/AgentChatSlice/state/sessions.ts
  • web/oss/src/components/Playground/Components/AgentCommitNotice.tsx
  • web/oss/src/components/Playground/Components/MainLayout/index.tsx
  • web/oss/src/components/Playground/Playground.tsx
  • web/oss/src/components/pages/agent-home/PlaygroundOnboarding/useAgentOnboarding.ts
  • web/packages/agenta-entities/tests/unit/agent-build-kit-overlay-api.test.ts
  • web/packages/agenta-entities/tests/unit/gatewayTriggerApi.test.ts
  • web/packages/agenta-entities/tests/unit/retrieveWorkflowRevision.test.ts
  • web/packages/agenta-entities/tests/unit/trace-migration-api.test.ts
✅ Files skipped from review due to trivial changes (1)
  • web/packages/agenta-entities/tests/unit/gatewayTriggerApi.test.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • web/packages/agenta-entities/tests/unit/retrieveWorkflowRevision.test.ts
  • web/oss/src/components/AgentChatSlice/state/sessionEphemera.ts
  • web/packages/agenta-entities/tests/unit/trace-migration-api.test.ts
  • web/packages/agenta-entities/tests/unit/agent-build-kit-overlay-api.test.ts
  • web/oss/src/components/Playground/Components/AgentCommitNotice.tsx
  • web/oss/src/components/AgentChatSlice/state/sessions.ts
  • web/oss/src/components/Playground/Components/MainLayout/index.tsx
  • web/oss/src/components/AgentChatSlice/AgentConversation.tsx

@mmabrouk

Copy link
Copy Markdown
Member

@ardaerzin tested it, two connections after each other don't work for me. The second one succeeds but the frontend thinks it failed

@mmabrouk mmabrouk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

other than the comment above it looks great and much snappier. Commit behvaior is awesome

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jul 10, 2026
…t-optimizations-onbig

# Conflicts:
#	web/oss/src/components/Sidebar/hooks/useWorkflowSwitcher.tsx
Every connect widget opened its OAuth popup with the same window name 'tools_oauth'.
With two connections in a row, the second window.open collided with the first widget's
popup instead of getting its own, so the second flow's tools:oauth:complete message
never reached the second widget — its popup-closed poll then settled it as cancelled
('Connection not completed') even though the OAuth had succeeded. Name the window per
tool-call id so each widget owns its popup.
@ardaerzin

Copy link
Copy Markdown
Contributor Author

@ardaerzin tested it, two connections after each other don't work for me. The second one succeeds but the frontend thinks it failed

@mmabrouk I pushed another commit, can you please check if this issue is still happening

@ardaerzin
ardaerzin merged commit 132014b into big-agents Jul 10, 2026
18 of 23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Frontend Improvement lgtm This PR has been approved by a maintainer size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants