Skip to content

feat(dashboard): refactor agent pages subAgent, mcp-tool, function-tool, teamAgent, externalAgent forms to use zod schemas from agents-core #2400

Draft
dimaMachina wants to merge 154 commits intoprd-5298-2from
prd-5298-22
Draft

feat(dashboard): refactor agent pages subAgent, mcp-tool, function-tool, teamAgent, externalAgent forms to use zod schemas from agents-core #2400
dimaMachina wants to merge 154 commits intoprd-5298-2from
prd-5298-22

Conversation

@dimaMachina
Copy link
Collaborator

@dimaMachina dimaMachina commented Feb 26, 2026

Features

  1. Reusing same zod validation schema from agents-core
  2. See validation errors on change at the bottom of field via Generic components
    Screen.Recording.2026-03-06.at.04.35.35.mov
  3. See error report summary on blur
    Screen.Recording.2026-03-06.at.04.37.08.mov
  4. New Error badge for errors from Agent Settings
    image
  5. More categories with working Go to button
    image
  6. Is dirty remembers initial field value
    image

@itoqa
Copy link

itoqa bot commented Mar 5, 2026

Ito Test Report ❌

15 test cases ran. 12 passed, 3 failed.

✅ The run confirms core route loading, save flows, validation enforcement, unsaved-change handling, keyboard safety in text inputs, mobile usability, and forged-event isolation. 🔍 Code-first verification identified three likely product defects: undo shortcut scope regression after node deletion, MCP header persistence drift during relationship hydration, and unsafe external-agent URL scheme acceptance.

✅ Passed (12)
Test Case Summary Timestamp Screenshot
ROUTE-1 Builder route loaded with expected controls after environment remediation. 0:00 ROUTE-1_0-00.png
ROUTE-2 Valid deep-link persisted and invalid nodeId recovered to a safe pane state. 0:00 ROUTE-2_0-00.png
ROUTE-3 Mixed edits saved successfully and persisted after reload. 3:07 ROUTE-3_3-07.png
ROUTE-5 Traces navigation preserved agent context and return path. 3:07 ROUTE-5_3-07.png
LOGIC-2 Save blocked when default sub-agent was unset, then succeeded after restore. 6:56 LOGIC-2_6-56.png
LOGIC-5 Approval toggle state persisted correctly across saves. 3:07 LOGIC-5_3-07.png
EDGE-1 Unsaved dialog discard branch navigated correctly. 14:37 EDGE-1_14-37.png
EDGE-2 Save-and-leave stayed blocked while invalid, then succeeded after fix. 7:42 EDGE-2_7-42.png
EDGE-3 Beforeunload protection preserved edits and save branch completed. 15:42 EDGE-3_15-42.png
EDGE-5 Backspace/Delete in editable fields did not delete selected nodes. 3:19 EDGE-5_3-19.png
EDGE-6 Mobile portrait/landscape kept save and validation controls reachable. 40:22 EDGE-6_40-22.png
ADV-5 Forged animation events with mismatched conversation IDs caused no graph mutation. 1:17:35 ADV-5_1-17-35.png
❌ Failed (3)
Test Case Summary Timestamp Screenshot
LOGIC-3 Undo shortcut did not restore deleted node after keyboard deletion flow. 3:18 LOGIC-3_3-18.png
LOGIC-4 MCP headers reverted to non-edited/default content after save-reload hydration path. 13:24 LOGIC-4_13-24.png
ADV-2 External-agent path accepted javascript: URL and script-like input without rejection. 1:11:39 ADV-2_1-11-39.png
Keyboard delete honors default-node protection and edge cleanup - Failed
  • Where: Agent builder graph keyboard interaction layer (node selection/delete + undo shortcut handling).

  • Steps to reproduce: Select a deletable node on canvas -> press Delete -> press Ctrl/Cmd+Z.

  • What failed: Node deletion occurs, but undo does not reliably restore the deleted node state.

  • Code analysis: The keyboard handler exits unless the key event target has react-flow__node, which can stop undo handling after focus changes during deletion. This creates a plausible path where deletion happens but follow-up undo is not processed.

  • Relevant code:

    agents-manage-ui/src/features/agent/ui/use-agent-shortcuts.ts (lines 46-70)

    function onKeyDown(e: KeyboardEvent) {
      if (!(e.target as HTMLElement)?.classList.contains('react-flow__node')) return;
      const meta = e.metaKey || e.ctrlKey;
      if (meta && e.key.toLowerCase() === 'z') {
        e.preventDefault();
        if (e.shiftKey) {
          redo();
        } else {
          undo();
        }
        return;
      }
    }

    agents-manage-ui/src/features/agent/ui/use-agent-shortcuts.ts (lines 13-43)

    function deleteSelected() {
      agentStore.setState((state) => {
        const nodesToDelete = new Set(
          state.nodes
            .filter((n) => n.selected && (n.deletable ?? true) && n.id !== defaultSubAgentIdRef.current)
            .map((n) => n.id)
        );
        const edgesRemaining = state.edges.filter(
          (e) => !e.selected && !nodesToDelete.has(e.source) && !nodesToDelete.has(e.target)
        );
        const nodesRemaining = state.nodes.filter((n) => !nodesToDelete.has(n.id));
        return { history: [...state.history, { nodes: state.nodes, edges: state.edges }], nodes: nodesRemaining, edges: edgesRemaining, dirty: true };
      });
    }
  • Why this is likely a bug: Undo logic is present, but the strict event-target gate can block shortcut processing in normal post-delete focus states, producing the observed restore failure.

  • Introduced by this PR: Yes - this PR modified the relevant shortcut handling code.

  • Timestamp: 3:18

MCP relation edits persist through save and relationshipId hydration - Failed
  • Where: MCP node editor relation hydration and form-key lifecycle after save.

  • Steps to reproduce: Edit MCP headers -> save -> relationshipId is hydrated for the node -> reload/reopen editor.

  • What failed: Header edits do not persist exactly and can revert to fallback/default content.

  • Code analysis: MCP relation form state is keyed by relationshipId ?? node.id, but initialization effect only depends on selectedNode.id. When relationshipId changes after save, the effect does not re-run for the new key, allowing stale/fallback relation data to be shown and serialized.

  • Relevant code:

    agents-manage-ui/src/components/agent/sidepane/nodes/mcp-node-editor.tsx (lines 48-53)

    const relationKey = selectedNode.data.relationshipId ?? selectedNode.id;
    const mcpRelation = useWatch({
      control: form.control,
      name: `mcpRelations.${relationKey}`,
    });

    agents-manage-ui/src/components/agent/sidepane/nodes/mcp-node-editor.tsx (lines 89-109)

    useEffect(() => {
      const existingRelation = form.getValues(`mcpRelations.${relationKey}`);
      if (existingRelation) {
        return;
      }
      form.setValue(`mcpRelations.${relationKey}`, {
        toolId: selectedNode.data.toolId,
        relationshipId: selectedNode.data.relationshipId ?? undefined,
      }, { shouldDirty: false });
    }, [selectedNode.id]);

    agents-manage-ui/src/app/[tenantId]/projects/[projectId]/agents/[agentId]/page.client.tsx (lines 734-769)

    // Update MCP nodes with new relationshipIds from backend response
    setNodes((currentNodes) =>
      currentNodes.map((node) => {
        if (node.type === NodeType.MCP) {
          const mcpNode = node as Node & { data: MCPNodeData };
          if (mcpNode.data.subAgentId && mcpNode.data.toolId) {
            if (mcpNode.data.relationshipId) return node;
            // ...find matching relationship...
            return { ...node, data: { ...node.data, relationshipId: matchingRelationship.agentToolRelationId } };
          }
        }
        return node;
      })
    );
  • Why this is likely a bug: The relation key can change from node-id to relationship-id, but initialization is not keyed to that change, which is a concrete persistence/hydration defect path in production code.

  • Introduced by this PR: Yes - this PR modified MCP relation serialization/editor and hydration paths.

  • Timestamp: 13:24

Unsafe URL/string injection in external agent fields - Failed
  • Where: External agent URL validation in shared schema used by manage UI and API.

  • Steps to reproduce: Enter external agent baseUrl using javascript: scheme and submit/save.

  • What failed: Unsafe scheme input is accepted instead of being rejected to http/https-only.

  • Code analysis: The schema validates baseUrl with z.url(), which checks URL shape but does not enforce safe protocol allowlisting. The form schema directly uses this baseUrl field from shared agent schema.

  • Relevant code:

    packages/agents-core/src/validation/schemas.ts (lines 1785-1790)

    export const ExternalAgentInsertSchema = createInsertSchema(externalAgents).extend({
      id: ResourceIdSchema,
      name: NameSchema,
      description: DescriptionSchema,
      baseUrl: z.url(),
      credentialReferenceId: z.string().trim().nonempty().max(256).nullish(),
    });

    agents-manage-ui/src/components/agent/form/validation.ts (lines 16-23)

    const ExternalAgentSchema = AgentWithinContextOfProjectSchema.shape.externalAgents
      .unwrap()
      .valueType.pick({
        name: true,
        id: true,
        description: true,
        baseUrl: true,
      });
  • Why this is likely a bug: Production validation currently accepts URL schemes beyond safe web transports, allowing clearly unsafe javascript: values through normal save paths.

  • Introduced by this PR: Unknown - unable to determine.

  • Timestamp: 1:11:39

📋 View Recording

Screen Recording

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant