Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum ResourceNodeType {
Tool = 'tool',
MemorySpace = 'memorySpace',
A2A = 'a2a',
Skills = 'skills',
}

export const ResourceNodeTypeToPosition: Record<ResourceNodeType, Position> = {
Expand All @@ -16,16 +17,18 @@ export const ResourceNodeTypeToPosition: Record<ResourceNodeType, Position> = {
[ResourceNodeType.Tool]: Position.Bottom,
[ResourceNodeType.MemorySpace]: Position.Top,
[ResourceNodeType.A2A]: Position.Bottom,
[ResourceNodeType.Skills]: Position.Top,
};

// Consistent ordering for resource node types
// Top: MemorySpace -> Escalation
// Top: MemorySpace -> Escalation -> Skills
// Bottom: Context -> Tool -> MCP -> A2A
export const ResourceNodeTypeOrder: Record<ResourceNodeType, number> = {
[ResourceNodeType.MemorySpace]: 0,
[ResourceNodeType.Escalation]: 1,
[ResourceNodeType.Context]: 2,
[ResourceNodeType.Tool]: 3,
[ResourceNodeType.MCP]: 4,
[ResourceNodeType.A2A]: 5,
[ResourceNodeType.Skills]: 2,
[ResourceNodeType.Context]: 3,
[ResourceNodeType.Tool]: 4,
[ResourceNodeType.MCP]: 5,
[ResourceNodeType.A2A]: 6,
};
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ interface AgentFlowWrapperProps {
definition?: any;
enableTimelinePlayer?: boolean;
enableMemory?: boolean;
enableSkills?: boolean;
enableStickyNotes?: boolean;
enableInstructions?: boolean;
healthScore?: number;
Expand All @@ -371,6 +372,7 @@ const AgentFlowWrapper = ({
definition = sampleAgentDefinition,
enableTimelinePlayer = true,
enableMemory = true,
enableSkills = true,
enableStickyNotes = true,
enableInstructions = false,
healthScore,
Expand Down Expand Up @@ -624,6 +626,7 @@ const AgentFlowWrapper = ({
onSelectResource={handleSelectResource}
enableTimelinePlayer={mode === 'view' && enableTimelinePlayer}
enableMemory={enableMemory}
enableSkills={enableSkills}
enableStickyNotes={enableStickyNotes}
enableInstructions={enableInstructions}
stickyNotes={stickyNotes}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,14 @@ const AGENT_FLOW_FIT_VIEW_OPTIONS = {

// agent node wrapper
const createAgentNodeWrapper = (handlers: {
onAddResource?: (type: 'context' | 'escalation' | 'mcp' | 'tool' | 'memorySpace' | 'a2a') => void;
onAddResource?: (
type: 'context' | 'escalation' | 'mcp' | 'tool' | 'memorySpace' | 'a2a' | 'skills'
) => void;
translations?: AgentNodeTranslations;
suggestionTranslations?: SuggestionTranslations;
enableMcpTools?: boolean;
enableMemory?: boolean;
enableSkills?: boolean;
enableA2a?: boolean;
enableInstructions?: boolean;
healthScore?: number;
Expand Down Expand Up @@ -146,6 +149,15 @@ const createAgentNodeWrapper = (handlers: {
node.data.parentNodeId === props.id
);

const hasSkills =
handlers.enableSkills === true &&
nodes.some(
(node) =>
isAgentFlowResourceNode(node) &&
node.data.type === 'skills' &&
node.data.parentNodeId === props.id
);

// Check if agent itself is running OR if any of its resources are running on view mode OR if it's processing a suggestion
const agentRunning = hasAgentRunning(storeProps.spans);
const resourceRunning = nodes.some(
Expand Down Expand Up @@ -187,6 +199,7 @@ const createAgentNodeWrapper = (handlers: {
hasMcp={hasMcp}
hasMemory={hasMemory}
hasA2a={hasA2a}
hasSkills={hasSkills}
a2aEnabled={handlers.enableA2a === true}
mcpEnabled={handlers.enableMcpTools !== false}
mode={storeProps.mode}
Expand All @@ -198,6 +211,7 @@ const createAgentNodeWrapper = (handlers: {
translations={handlers.translations ?? DefaultAgentNodeTranslations}
suggestionTranslations={handlers.suggestionTranslations ?? DefaultSuggestionTranslations}
enableMemory={handlers.enableMemory === true}
enableSkills={handlers.enableSkills === true}
enableInstructions={handlers.enableInstructions === true}
healthScore={handlers.healthScore}
onHealthScoreClick={handlers.onHealthScoreClick}
Expand Down Expand Up @@ -276,6 +290,7 @@ const AgentFlowInner = memo(
canvasRef,
enableMcpTools,
enableMemory,
enableSkills,
enableA2a,
enableStickyNotes,
enableInstructions,
Expand Down Expand Up @@ -373,7 +388,7 @@ const AgentFlowInner = memo(

const nodeTypes = useMemo(() => {
const handleAddResource = (
type: 'context' | 'escalation' | 'mcp' | 'tool' | 'memorySpace' | 'a2a'
type: 'context' | 'escalation' | 'mcp' | 'tool' | 'memorySpace' | 'a2a' | 'skills'
) => {
// Use createResourcePlaceholder which will either create a placeholder or call onAddResource
createResourcePlaceholder(type);
Expand All @@ -389,6 +404,7 @@ const AgentFlowInner = memo(
suggestionTranslations,
enableMcpTools,
enableMemory,
enableSkills,
enableA2a,
enableInstructions,
healthScore,
Expand Down Expand Up @@ -428,6 +444,7 @@ const AgentFlowInner = memo(
onCollapseResource,
enableMcpTools,
enableMemory,
enableSkills,
suggestionGroup?.metadata?.version,
]);

Expand Down Expand Up @@ -596,6 +613,7 @@ const AgentFlowInner = memo(
if (node.data.isVirtual) return true; // Always keep virtual nodes
if (node.data.type === 'memorySpace') return !!enableMemory;
if (node.data.type === 'a2a') return !!enableA2a;
if (node.data.type === 'skills') return !!enableSkills;
return true;
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,19 @@ interface AgentNodeProps {
hasTool?: boolean;
hasMcp?: boolean;
hasA2a?: boolean;
hasSkills?: boolean;
a2aEnabled?: boolean;
mcpEnabled?: boolean;
hasError?: boolean;
hasSuccess?: boolean;
hasRunning?: boolean;
onAddResource?: (type: 'context' | 'escalation' | 'mcp' | 'tool' | 'memorySpace' | 'a2a') => void;
onAddResource?: (
type: 'context' | 'escalation' | 'mcp' | 'tool' | 'memorySpace' | 'a2a' | 'skills'
) => void;
onAddInstructions?: () => void;
translations: AgentNodeTranslations;
enableMemory?: boolean;
enableSkills?: boolean;
enableInstructions?: boolean;
healthScore?: number;
onHealthScoreClick?: () => void;
Expand All @@ -100,6 +104,7 @@ const AgentNodeComponent = memo((props: NodeProps<Node<AgentNodeData>> & AgentNo
hasTool = false,
hasMcp = false,
hasA2a = false,
hasSkills = false,
a2aEnabled = false,
mcpEnabled = true,
hasError = false,
Expand All @@ -109,6 +114,7 @@ const AgentNodeComponent = memo((props: NodeProps<Node<AgentNodeData>> & AgentNo
onAddInstructions,
translations,
enableMemory,
enableSkills,
enableInstructions = false,
healthScore,
onHealthScoreClick,
Expand Down Expand Up @@ -209,6 +215,8 @@ const AgentNodeComponent = memo((props: NodeProps<Node<AgentNodeData>> & AgentNo

const displayMemory =
enableMemory === true && (mode === 'design' || (mode === 'view' && hasMemory));
const displaySkills =
enableSkills === true && (mode === 'design' || (mode === 'view' && hasSkills));
const displayContext = mode === 'design' || (mode === 'view' && hasContext);
Comment on lines 216 to 220
const displayEscalation = mode === 'design' || (mode === 'view' && hasEscalation);
const displayTool = mode === 'design' || (mode === 'view' && hasTool);
Expand Down Expand Up @@ -256,6 +264,21 @@ const AgentNodeComponent = memo((props: NodeProps<Node<AgentNodeData>> & AgentNo
});
}

if (displaySkills) {
topHandles.push({
id: ResourceNodeType.Skills,
type: 'source',
handleType: 'artifact',
label: translations.skills,
showButton: mode === 'design',
labelBackgroundColor: 'var(--canvas-background-secondary)',
visible: displaySkills,
onAction: (_e: HandleActionEvent) => {
onAddResource?.('skills');
},
});
}

if (topHandles.length) {
configs.push({
position: Position.Top,
Expand Down Expand Up @@ -310,6 +333,7 @@ const AgentNodeComponent = memo((props: NodeProps<Node<AgentNodeData>> & AgentNo
mode,
displayContext,
displayMemory,
displaySkills,
displayMcp,
displayTool,
displayA2a,
Expand Down Expand Up @@ -603,6 +627,7 @@ const AgentNodeWrapper = (props: NodeProps<Node<AgentNodeData>> & AgentNodeProps
hasTool,
hasMcp,
hasA2a,
hasSkills,
a2aEnabled,
mcpEnabled,
hasError,
Expand All @@ -612,6 +637,7 @@ const AgentNodeWrapper = (props: NodeProps<Node<AgentNodeData>> & AgentNodeProps
onAddInstructions,
translations,
enableMemory,
enableSkills,
enableInstructions,
healthScore,
onHealthScoreClick,
Expand All @@ -631,6 +657,7 @@ const AgentNodeWrapper = (props: NodeProps<Node<AgentNodeData>> & AgentNodeProps
hasTool={hasTool}
hasMcp={hasMcp}
hasA2a={hasA2a}
hasSkills={hasSkills}
a2aEnabled={a2aEnabled}
mcpEnabled={mcpEnabled}
hasError={hasError}
Expand All @@ -640,6 +667,7 @@ const AgentNodeWrapper = (props: NodeProps<Node<AgentNodeData>> & AgentNodeProps
onAddInstructions={onAddInstructions}
translations={translations}
enableMemory={enableMemory}
enableSkills={enableSkills}
enableInstructions={enableInstructions}
healthScore={healthScore}
onHealthScoreClick={onHealthScoreClick}
Expand Down
37 changes: 35 additions & 2 deletions packages/apollo-react/src/canvas/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,29 @@ export type AgentFlowA2aResource = {
errorAction?: Omit<ToolbarActionItem, 'id' | 'onAction'>;
};

export type AgentFlowSkillsResource = {
id: string;
type: 'skills';
name: string;
originalName?: string;
description: string;
errors?: ErrorInfo[];
hasBreakpoint?: boolean;
isCurrentBreakpoint?: boolean;
hasGuardrails?: boolean;
projectId?: string;
isDisabled?: boolean;
errorAction?: Omit<ToolbarActionItem, 'id' | 'onAction'>;
};

export type AgentFlowResource =
| AgentFlowContextResource
| AgentFlowEscalationResource
| AgentFlowMcpResource
| AgentFlowToolResource
| AgentFlowMemorySpaceResource
| AgentFlowA2aResource;
| AgentFlowA2aResource
| AgentFlowSkillsResource;
Comment on lines 183 to +186
export type AgentFlowResourceType = AgentFlowResource['type'];

/**
Expand Down Expand Up @@ -276,7 +292,16 @@ export type AgentFlowProps = {
resources: AgentFlowResource[];
allowDragging?: boolean;
initialSelectedResource?: {
type: 'context' | 'escalation' | 'mcp' | 'pane' | 'run' | 'tool' | 'memorySpace' | 'a2a';
type:
| 'context'
| 'escalation'
| 'mcp'
| 'pane'
| 'run'
| 'tool'
| 'memorySpace'
| 'a2a'
| 'skills';
name: string;
} | null;
onSelectResource?: (resourceId: string | null) => void;
Expand Down Expand Up @@ -354,6 +379,8 @@ export type AgentFlowProps = {
enableMcpTools?: boolean;
/** TODO: Remove once memory feature is fully implemented */
enableMemory?: boolean;
/** Enables the "Skills" add-button on the top edge of the agent node. */
enableSkills?: boolean;
enableA2a?: boolean;
enableStickyNotes?: boolean;
enableInstructions?: boolean;
Expand Down Expand Up @@ -465,6 +492,9 @@ export type MemorySpaceResourceData = {
export type A2aResourceData = {
type: 'a2a';
};
export type SkillsResourceData = {
type: 'skills';
};

export type SharedResourceData = {
name: string;
Expand Down Expand Up @@ -512,6 +542,7 @@ export type AgentFlowResourceNodeData = (
| ToolResourceData
| MemorySpaceResourceData
| A2aResourceData
| SkillsResourceData
) &
SharedResourceData;
export type AgentFlowResourceNode = Node<AgentFlowResourceNodeData, 'resource'> & {
Expand Down Expand Up @@ -552,6 +583,7 @@ export interface AgentNodeTranslations {
context: string;
tools: string;
memory: string;
skills: string;
instructions: string;
Comment on lines 583 to 587
addInstructions: string;
// Settings preview
Expand Down Expand Up @@ -579,6 +611,7 @@ export const DefaultAgentNodeTranslations: AgentNodeTranslations = {
context: 'Context',
tools: 'Tools',
memory: 'Memory',
skills: 'Skills',
instructions: 'Instructions',
addInstructions: 'Add Instructions',
// Settings preview
Expand Down
2 changes: 2 additions & 0 deletions packages/apollo-react/src/canvas/utils/auto-layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const getAgentGroupBottomPosition = (
[ResourceNodeType.Tool]: [],
[ResourceNodeType.MemorySpace]: [],
[ResourceNodeType.A2A]: [],
[ResourceNodeType.Skills]: [],
};

// Group nodes by which handle they're connected to
Expand Down Expand Up @@ -94,6 +95,7 @@ const arrangeAgent = (
[ResourceNodeType.Tool]: [],
[ResourceNodeType.MemorySpace]: [],
[ResourceNodeType.A2A]: [],
[ResourceNodeType.Skills]: [],
};

// Group nodes by which handle they're connected to, excluding nodes with explicit positions
Expand Down
Loading