Skip to content

feat(frontend): agent config playground controls#4775

Open
mmabrouk wants to merge 2 commits into
mainfrom
feat/agent-playground-ui
Open

feat(frontend): agent config playground controls#4775
mmabrouk wants to merge 2 commits into
mainfrom
feat/agent-playground-ui

Conversation

@mmabrouk

@mmabrouk mmabrouk commented Jun 19, 2026

Copy link
Copy Markdown
Member

Agent-workflows: functional PR set

Sliced by functional area, final code only (no intermediate churn). Most PRs are independent off main; two pairs are stacked. This PR's base is main.

Context

The playground needs to edit a typed agent config: instructions, model, tools, MCP servers, and runtime settings (harness, sandbox, permission policy). The backend already exposes an agent_config catalog type generated from the SDK model, and ships a thin x-ag-type-ref: "agent_config" marker on the field. This PR is the frontend that resolves that marker and renders the form. It branches from main and is one functional slice of the agent-workflows feature, showing the final code for the playground controls.

What this changes

The schema renderer now recognizes the agent_config marker and dispatches the whole config object to one composite control, AgentConfigControl. That control does not invent new widgets. It reuses the model selector, the tool picker, the enum selects, and a textarea, and adds one new piece: McpServerItemControl, a JSON editor for a single MCP server entry. The create-app dropdown and the create-app type modal both gain an "Agent" option with a robot icon. An agent app runs in chat mode: is_chat is now true for type chat or agent.

Key architectural decision to review

The form is driven by the catalog type, not hand-built. The backend marks the field with x-ag-type-ref, and the frontend dispatches on that marker (SchemaPropertyRenderer.tsx:130). The control then reads each sub-field's schema off schema.properties and renders it with an existing control. This keeps the form in sync with the typed schema as long as the property names match: agents_md, model, tools, mcp_servers, harness, sandbox, permission_policy. The tradeoff is the coupling lives in property-name string keys, not in types. If the SDK renames a field, the control silently drops it rather than failing the build. Scrutinize whether that contract is documented and tested well enough to survive a backend rename, and whether the two read-back fallbacks (agents_md falling back to instructions) are the right place for legacy compatibility.

The second decision worth a look: tools and mcp_servers are both flat arrays on the config, edited as raw JSON, because the backend resolver parses them. The MCP editor only propagates onChange when the text parses as JSON, and keeps invalid text local until it does. That matches how ToolItemControl already works, so it is consistent, but it means a half-typed server entry is held outside the form value.

How to review this PR

Read AgentConfigControl.tsx first. Check that every sub-control receives the matching props.<field> schema and that setField merges rather than replaces the config object. Confirm the tool helpers (handleAddTool, handleRemoveToolByName, selectedToolNames) all key off toolName, which reads function.name. A tool object missing that path would be invisible to the selected-set and the remove-by-name path, which is the likely regression.

Then read McpServerItemControl.tsx. The lastExternalRef guard resets the editor text only on external value changes (add, remove, reorder) and not on the user's own keystrokes. Check that the parse-then-propagate path does not clobber in-progress edits, and that an empty string maps to {} rather than throwing.

Then appUtils.ts:211 for the is_chat change, and the two create-app menus plus iconHelpers.tsx for the Agent option and robot icon.

Tests / notes

No automated tests ship with this slice. Watch the schema-name contract: the control depends on the SDK field names staying stable, and a rename surfaces as a missing sub-control rather than a type error.

@vercel

vercel Bot commented Jun 19, 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 Jun 19, 2026 8:32pm

Request Review

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. feature Frontend labels Jun 19, 2026
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1a2f0111-c55c-4532-8897-e42e8e0d00b6

📥 Commits

Reviewing files that changed from the base of the PR and between 7120276 and 5922820.

📒 Files selected for processing (1)
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/McpServerItemControl.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/McpServerItemControl.tsx

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added support for agent app type with dedicated icon.
    • Introduced comprehensive agent configuration interface enabling tools and MCP server management.
    • Added dedicated editor for MCP server configuration within agent setup workflows.

Walkthrough

Adds "agent" as a recognized AppType alongside "chat" and "completion", treats agent as chat mode in ephemeral app creation, maps the agent type to RobotIcon in getAppTypeIcon, introduces McpServerItemControl for editing MCP server entries, adds AgentConfigControl as a composite schema-driven editor for agent_config records, and wires the new control type through SchemaPropertyRenderer and the barrel exports.

Changes

Agent App Type Support

Layer / File(s) Summary
Agent type registration and icon mapping
web/packages/agenta-entities/src/workflow/state/appUtils.ts, web/oss/src/components/pages/prompts/assets/iconHelpers.tsx
AppType union adds "agent"; createEphemeralAppFromTemplate sets flags.is_chat true for agent type; getAppTypeIcon imports RobotIcon and returns it when appType contains "agent".
McpServerItemControl component
web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/McpServerItemControl.tsx
New memoized component edits a single mcp_servers array entry: normalizes value via toServerObj, syncs external/local text state, propagates only valid plain-object JSON via onChange, renders with SharedEditor or a textarea fallback, and shows a conditional delete button.
AgentConfigControl + SchemaPropertyRenderer wiring + barrel exports
web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentConfigControl.tsx, SchemaPropertyRenderer.tsx, index.ts
AgentConfigControl normalizes agent_config values, manages tools and mcp_servers arrays with add/change/delete flows (including agenta_metadata merge and isBuiltinPayloadMatch removal), computes agentsMd, and renders instructions, model selector, tool rows, MCP server rows, and harness/sandbox/permission enum controls. SchemaPropertyRenderer detects x-ag-type="agent_config" via getControlType and routes rendering to AgentConfigControl. Barrel re-exports both new components and their Props types.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(frontend): agent config playground controls' clearly describes the main feature being added: frontend controls for agent configuration in the playground.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, providing context, architectural decisions, and detailed review guidance for agent config playground controls implementation.
Docstring Coverage ✅ Passed Docstring coverage is 60.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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/agent-playground-ui

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 and usage tips.

@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

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/pages/app-management/modals/CreateAppTypeModal/index.tsx (1)

4-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the comment to reflect three app types.

The comment references "two built-in app types (Chat / Completion)" but the modal now includes a third type (Agent).

📝 Proposed fix
- * Onboarding modal that surfaces the two built-in app types (Chat /
- * Completion) as large, equal-weight choices. Used by the welcome-card
+ * Onboarding modal that surfaces the built-in app types (Chat, Completion,
+ * and Agent) as large, equal-weight choices. Used by the welcome-card
🧹 Nitpick comments (2)
web/oss/src/components/pages/app-management/modals/CreateAppTypeModal/index.tsx (1)

126-126: ⚡ Quick win

Consider grid layout with three items.

The grid uses grid-cols-2 but now displays three options, which will create an asymmetric 2+1 layout. Consider switching to grid-cols-3 for equal spacing or use a flex layout that wraps naturally.

♻️ Proposed fix for three-column grid
-            <div className="grid grid-cols-2 gap-3">
+            <div className="grid grid-cols-3 gap-3">
web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentConfigControl.tsx (1)

49-56: ⚡ Quick win

Consider memoizing AgentConfigControl for performance.

This is a complex composite control with multiple child controls and array mappings. Wrapping the component with React.memo would prevent unnecessary re-renders when parent props haven't changed.

♻️ Proposed fix
-export function AgentConfigControl({
+export const AgentConfigControl = memo(function AgentConfigControl({
     schema,
     value,
     onChange,
     withTooltip,
     disabled,
     className,
 }: AgentConfigControlProps) {
+    // ... component body
+})
-}

Don't forget to import memo:

-import {useCallback, useMemo} from "react"
+import {memo, useCallback, useMemo} from "react"

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 20bbe915-9e47-49b1-baf5-80f46cfeddd5

📥 Commits

Reviewing files that changed from the base of the PR and between a97e608 and 77b6dcb.

📒 Files selected for processing (8)
  • web/oss/src/components/pages/app-management/components/CreateAppDropdown/index.tsx
  • web/oss/src/components/pages/app-management/modals/CreateAppTypeModal/index.tsx
  • web/oss/src/components/pages/prompts/assets/iconHelpers.tsx
  • web/packages/agenta-entities/src/workflow/state/appUtils.ts
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentConfigControl.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/McpServerItemControl.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SchemaPropertyRenderer.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/index.ts

Comment on lines +62 to +65
const setField = useCallback(
(key: string, fieldValue: unknown) => onChange({...config, [key]: fieldValue}),
[config, onChange],
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stabilize the setField callback to prevent unnecessary re-renders.

The setField callback includes config in its dependency array, causing it to be recreated on every config change. This breaks referential stability and can trigger unnecessary re-renders in child components that receive callbacks derived from setField (like setTools).

🚀 Proposed fix using functional update
 const setField = useCallback(
-    (key: string, fieldValue: unknown) => onChange({...config, [key]: fieldValue}),
-    [config, onChange],
+    (key: string, fieldValue: unknown) => {
+        onChange((prev) => ({...(prev ?? {}), [key]: fieldValue}))
+    },
+    [onChange],
 )

If onChange doesn't support functional updates, wrap it:

+const stableOnChange = useCallback((updater: (prev: Record<string, unknown>) => Record<string, unknown>) => {
+    onChange(updater(config))
+}, [onChange, config])
+
 const setField = useCallback(
-    (key: string, fieldValue: unknown) => onChange({...config, [key]: fieldValue}),
-    [config, onChange],
+    (key: string, fieldValue: unknown) => stableOnChange((prev) => ({...prev, [key]: fieldValue})),
+    [stableOnChange],
 )

@mmabrouk

Copy link
Copy Markdown
Member Author

Reviewer guide: interesting code

The agent config form is schema-generated, not hand-built. Read these in order:

  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SchemaPropertyRenderer.tsx:130 — the agent_config marker dispatch (x-ag-type-ref first, then x-ag-type); the render case is at line 430.
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentConfigControl.tsx:62setField keeps the rest of the config intact on every write; the tool and mcp_servers handlers below it write flat arrays the backend resolver parses.
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentConfigControl.tsx:149agents_md is the catalog field; instructions is read only as a legacy fallback, and the control writes agents_md only.
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/McpServerItemControl.tsx:69handleEditorChange emits onChange only when the JSON parses; the useEffect above re-syncs editor text on external add/remove/reorder. Watch for an edit loop or stale text here.
  • web/packages/agenta-entities/src/workflow/state/appUtils.ts:211is_chat covers agent, so the agent app routes through chat mode.

if (xAgTypeRef === "code" || xAgType === "code") {
return "code"
}
if (xAgTypeRef === "agent_config" || xAgType === "agent_config") {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is the whole schema-to-form contract: an `agent_config` x-ag-type-ref (or x-ag-type) routes the field to AgentConfigControl. The typed shape stays in the SDK `AgentConfigSchema`; the playground just follows the marker. Confirm the agent service actually ships this ref on the config field, otherwise the field falls through to a generic control.

const agentsMd =
(config.agents_md as string | null | undefined) ??
(config.instructions as string | null | undefined) ??
null

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Read fallback: the editor populates from `agents_md`, then `instructions` as a legacy key. But every write goes to `agents_md` only (setField below). So a config stored under the old `instructions` key shows in the editor, and the first edit silently migrates it to `agents_md`. Confirm `agents_md` is the field the backend reads, so this migration lands somewhere valid.

}
}, [value])

const handleEditorChange = useCallback(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

`onChange` fires only when the text parses as JSON; invalid text stays in the editor and is not propagated. That keeps a half-typed server out of the config, but it also means a user can leave the editor showing text that was never saved. Worth confirming the surrounding form signals unsaved/invalid state, otherwise an MCP server edit can look applied but silently not be.

@mmabrouk

Copy link
Copy Markdown
Member Author

Reviewer guide: interesting code

Suggested reading order and the load-bearing lines:

  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SchemaPropertyRenderer.tsx:130 — the dispatch. x-ag-type-ref: "agent_config" (or x-ag-type) routes the whole field to one composite control. This is the seam between the backend catalog type and the form.
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentConfigControl.tsx:41toolName reads function.name. Every tool helper (selected-set, remove-by-name) keys off it; a tool object missing that path is invisible to those paths.
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentConfigControl.tsx:148agents_md falls back to the legacy instructions key on read, but always writes back to agents_md. Worth confirming this one-way migration is intended.
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/McpServerItemControl.tsx:62 — the lastExternalRef guard distinguishes external value changes from the user's own keystrokes, so typing does not reset the editor. Parse-then-propagate keeps invalid JSON local until it parses.
  • web/packages/agenta-entities/src/workflow/state/appUtils.ts:211 — an agent app is chat-mode: is_chat is now true for chat or agent. The backend also infers is_chat from messages-in.

if (xAgTypeRef === "code" || xAgType === "code") {
return "code"
}
if (xAgTypeRef === "agent_config" || xAgType === "agent_config") {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is the dispatch seam. x-ag-type-ref: "agent_config" routes the whole field object to one composite control instead of per-property rendering. The control then reads each sub-field's schema off schema.properties by name, so the form stays correct only as long as the SDK field names hold. A backend rename surfaces as a missing sub-control, not a type error.

}

/** Read the function name of a tool object (the gateway slug for Composio tools). */
function toolName(tool: unknown): string | undefined {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Every tool helper keys off this toolName (it reads function.name): the selected-set, remove-by-name, and the visible/added check all depend on it. A tool object that lacks function.name is silently invisible to those paths, so it can neither be deduped nor removed by name. Worth confirming every tool shape the picker emits carries that field.

// Reset the editor text when the value changes from outside (add/remove/reorder).
const lastExternalRef = useRef<string>(safeStringify(serverObj ?? {}))
useEffect(() => {
const next = safeStringify(toServerObj(value) ?? {})

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The lastExternalRef guard is the subtle part: it resets the editor text only when the value changes from outside (add/remove/reorder), not on the user's own keystrokes. onChange propagates only when the text parses as JSON, so a half-typed server entry is held local and never reaches the form value. Same model as ToolItemControl, just flagging that in-progress invalid JSON is intentionally not persisted.

@mmabrouk mmabrouk left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Codex subagent review for #4775

I found one blocking correctness concern, but GitHub would not allow this account to submit REQUEST_CHANGES on its own PR, so this is posted as a comment review.

Findings:

  • P1 web/oss/src/components/pages/app-management/components/CreateAppDropdown/index.tsx:37 and web/oss/src/components/pages/app-management/modals/CreateAppTypeModal/index.tsx:51 - The Agent option is unconditional, but createEphemeralAppFromTemplate only succeeds if matchTemplateForType finds an application catalog template for agent. I verified this against the actual backend stack rather than relying only on #4779 docs: #4772's services/oss/src/agent/app.py registers the handler directly and explicitly notes there is no agenta:builtin:agent:v0 / first-class workflow type yet. With this PR and no agent catalog template, users can select Agent and always hit Couldn't start app creation — please retry. Please gate the Agent option on a real catalog template, or land the first-class template in the dependency, so the UI does not surface a dead create path.

  • P2 web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentConfigControl.tsx:215 - This ToolSelectorPopover omits onRemoveBuiltinTool, while the existing PromptSchemaControl passes it. Built-in tools still render as selected through selectedTools, but clicking the checked built-in item does nothing because BuiltinToolsPane only removes via onRemoveBuiltinTool. Users can still remove the card manually, but the selector toggle is broken and inconsistent with prompt tools. Please add the same payload-based remove handler used by the prompt control.

  • P2 web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/McpServerItemControl.tsx:69 - The MCP editor treats any parseable JSON as valid and calls onChange, including arrays and primitives. mcp_servers is an array of server objects, so storing [], 1, or "x" creates an invalid config rather than keeping the editor state local/invalid. Please only propagate object values (empty string -> {} is fine), or attach a validation schema that rejects non-object JSON.

I did not run tests; this was a GitHub diff/context review. Residual risk: the agent_config property-name contract still needs coverage because a backend rename would silently drop a subcontrol.

@mmabrouk mmabrouk force-pushed the feat/agent-playground-ui branch from 77b6dcb to 7120276 Compare June 19, 2026 20:00
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Jun 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-production-1543.up.railway.app/w
Project agenta-oss-pr-4775
Image tag pr-4775-d30ebd6
Status Deployed
Railway logs Open logs
Workflow logs View workflow run
Updated at 2026-06-19T20:10:53.147Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Frontend size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant