Node System
AgentDock is built around a powerful node-based architecture using BaseNode
as the foundation for all functionality. This design allows for modular, extensible, and highly configurable systems. BaseNode
supports various node categories, including core functionalities like LLM interaction (AgentNode
) and callable tools, forming the basis for sophisticated agent behaviors.
Looking Ahead: The Workflow Vision
The underlying structure, with defined input
/output
ports and MessageBus
integration, provides the core capabilities necessary to build complex workflow engines and chained execution patterns directly using the open-source library. While the reference client currently focuses on AgentNode
orchestrating individual tool executions, the architecture is designed for much more.
We are actively developing a richer set of node types to unlock true workflow automation, including:
- Event Nodes: To trigger workflows from external sources or schedules.
- Transform & AI Inference Nodes: For powerful data manipulation and specialized AI tasks.
- Connector & Action Nodes: To seamlessly integrate with external services and databases.
- Logic Nodes: For sophisticated flow control like branching and looping.
These planned additions, detailed further in the Workflow Nodes Roadmap, will empower users to build complex, multi-step automations visually and programmatically, extending far beyond the current agent-tool interaction model.
AgentDock Pro aims to enhance this capability by providing advanced tooling, such as visual workflow builders and management interfaces, built upon this expanding core open-source node system.
Core Node Types
While developers can create many types of custom nodes, AgentDock Core provides key foundational types:
BaseNode
The BaseNode
is the foundation of AgentDock's architecture, provided by AgentDock Core. It creates a consistent interface and core functionality for all node types:
- Metadata Management: Each node provides detailed, immutable
NodeMetadata
including:-
category
: 'core' or 'custom' -
label
: Display name -
description
: Functionality overview -
inputs
/outputs
: Defined ports (see below) -
version
: Semantic version string -
compatibility
: Flags indicating core/pro/custom environment support
-
- Port System: Type-safe
inputs
andoutputs
defined using theNodePort
interface:-
id
: Unique port identifier -
type
: Data type string -
label
: Display name -
schema
: Optional Zod schema for validation -
required
: Boolean flag -
defaultValue
: Optional default value
-
- Validation Hooks: Provides methods like
validateInput
,validateOutput
, andvalidateConnection
for ensuring data integrity and connection validity (subclasses can override). - Message Passing: Integrates with a
MessageBus
(set viasetMessageBus
). Nodes can send messages (sendMessage
) and register handlers (addMessageHandler
,removeMessageHandler
), enabling potential asynchronous coordination between nodes. - Lifecycle Management:
initialize()
for setup,execute()
for core logic, andcleanup()
for resource disposal. - Serialization: Includes a
toJSON()
method for converting the node's state (ID, type, config, metadata) to a JSON representation.
// From AgentDock Core
export abstract class BaseNode<TConfig = unknown> {
readonly id: string;
abstract readonly type: string; // e.g., 'core.agent'
protected config: TConfig; // Static configuration
readonly metadata: NodeMetadata;
// Core execution
abstract execute(input: unknown): Promise<unknown>;
// Lifecycle
async initialize(): Promise<void>;
async cleanup(): Promise<void>;
// Validation
validateInput(input: unknown): boolean;
validateOutput(output: unknown): boolean;
validateConnection(sourcePort: string, targetPort: string): boolean;
// Messaging
setMessageBus(messageBus: MessageBus): void;
protected async sendMessage<T>(targetId: string, type: string, payload: T): Promise<string>;
protected addMessageHandler<T>(type: string, handler: MessageHandler<T>): void;
protected removeMessageHandler(type: string): void;
// Serialization
toJSON(): { id: string; type: string; config: TConfig; metadata: NodeMetadata };
}
AgentNode
The AgentNode
(type: 'core.agent'
) is a specialized node in AgentDock Core orchestrating conversational AI interactions:
- Configuration (
AgentNodeConfig
): Initialized with provider details, API keys (primary and fallback), LLM options, and the crucialagentConfig
object containing the full agent definition (personality, nodes, orchestration rules). - LLM Integration: Manages
CoreLLM
instances (primary and optional fallback), handling provider/model derivation logic viacreateLLMInstance
if not explicitly configured. - Dynamic Tool Selection: Determines available tools for each turn via
getAvailableTools
, consulting theToolRegistry
and the agent'sorchestration
rules via an injectedOrchestrationManager
. - Runtime Execution (
handleMessage
): RequiresAgentNodeHandleMessageOptions
including conversationmessages
, theOrchestrationManager
,sessionId
, and optional overrides for system prompts or LLM configuration. - Stream Delegation: Sets up the LLM call (using
streamText
from the LLM service) and returns theAgentDockStreamResult
(containing the stream and promises) immediately. It delegates the consumption of the stream and handling of tool calls/results to the calling adapter/API route. - Error Handling: Implements fallback mechanisms for LLM provider issues.
// From AgentDock Core
export class AgentNode extends BaseNode<AgentNodeConfig> {
readonly type = 'core.agent';
// Main entry point for conversational turns
async handleMessage(options: AgentNodeHandleMessageOptions): Promise<AgentDockStreamResult>;
// Direct execution (less common for conversational flow)
async execute(input: unknown): Promise<unknown>;
// Retrieve token usage from the last interaction
getLastTokenUsage(): LanguageModelUsage | null;
}
Tools as Nodes
Tools in AgentDock are implemented as specialized node types, registered in the NodeRegistry
with isTool: true
:
- Registration: Registered like any other node but with additional tool-specific options (see
NodeRegistry
below). - Schema & Description: Require a
parameters
(Zod schema) anddescription
during registration, used for LLM interaction. - Consistent Interface: Follow the same
BaseNode
pattern (execute
,initialize
,cleanup
). TheNodeRegistry
can wrap them to fit the standard AI SDKTool
interface. - Dynamic Availability: Availability during a conversation is managed by the
AgentNode
using theToolRegistry
andOrchestrationManager
.
Node Registration System
AgentDock uses registry systems to manage node and tool types:
Node Registry
The NodeRegistry
from AgentDock Core provides a central system for discovering and instantiating node types:
- Type Management: Registers
NodeRegistration
objects (containingnodeClass
,version
,isTool
,parameters
,description
) mapped by a unique type string (e.g.,'core.agent'
). - Core vs. Custom: Uses separate methods (
register
for 'core',registerCustomNode
for 'custom') enforcing category constraints. - Instantiation (
create
): Creates node instances, performing version compatibility checks between the registered version and the node class's reported version. - Tool Definition Generation (
getToolDefinitions
): Generates an object mapping tool node types to AI SDK-compatibleTool
objects, automatically wrapping the node'sexecute
method. - Metadata Access (
getNodeMetadata
): Returns comprehensive metadata for all registered core and custom nodes.
Tool Registry
The ToolRegistry
from AgentDock Core manages the runtime availability of tools for specific agent interactions:
- Purpose: Primarily used by
AgentNode
to determine which tools (identified by their node type strings) are available for a given agent configuration during a specific turn. - Filtering (
getToolsForAgent
): Takes a list of node names (from agent config) and returns the corresponding tool objects registered globally. - Global Instance: Typically accessed via a singleton pattern (
getToolRegistry()
).
Custom Node Development
(This section primarily describes the pattern in the NextJS reference implementation, which uses a simplified tool definition format compared to the core NodeRegistry
registration.)
In the open source reference client implementation (NextJS), custom tools are implemented slightly differently, often directly defining objects conforming to the Vercel AI SDK Tool
interface:
Implementation Pattern (Reference Client)
// Example from the NextJS reference implementation (src/nodes/tools)
import { z } from 'zod';
import { Tool } from 'ai'; // Vercel AI SDK Tool type
// ... potentially import React components ...
const myToolSchema = z.object({ /* ... */ });
export const myTool: Tool = {
name: 'my_tool_id', // Matches the key in the tools object
description: 'What this tool does',
parameters: myToolSchema,
execute: async (args) => { /* ... server-side logic ... */ },
// Optional: render function for UI display in NextJS client
// render: (props) => <MyComponent {...props} />
};
// Exported for auto-registration via src/nodes/init.ts
export const tools = {
my_tool_id: myTool,
};
(Note: This reference implementation pattern bypasses direct registration with agentdock-core
's NodeRegistry
for simpler tool definition, relying on its own loading mechanism.)
Security Best Practices
(These apply regardless of the implementation pattern)
- Keep API calls server-side within the tool's
execute
function. - Use environment variables for secrets (API keys).
- Implement robust error handling.
- Consider rate limiting.
Design Patterns
The node system implements several key design patterns:
- Factory Pattern:
NodeRegistry.create
acts as a factory for node instances. - Registry Pattern:
NodeRegistry
andToolRegistry
manage node/tool types. - Observer Pattern (Potential): The
MessageBus
integration allows for observer-like patterns between nodes. - Strategy Pattern: Different node implementations can represent different strategies for achieving a task.
Node Lifecycle
Nodes go through a defined lifecycle managed by the system interacting with them (e.g., an agent runner or workflow engine):
- Registration: Node type is registered with
NodeRegistry
. - Instantiation: Node instance is created via
NodeRegistry.create
. - Initialization: Node's
initialize()
method is called (e.g., before first use). - Execution: Node's
execute()
(orhandleMessage
forAgentNode
) is called potentially multiple times. - Cleanup: Node's
cleanup()
method is called when the instance is no longer needed.
Node Relationships
Nodes can interact or be connected conceptually:
- Message Passing: Asynchronous communication via the
MessageBus
enables decoupled interactions, suitable for complex event-driven workflows. - Tool Invocation:
AgentNode
invokes tool nodes based on LLM requests, orchestrated viaToolRegistry
andOrchestrationManager
. - Workflow Connections: The
input
/output
port system andvalidateConnection
method provide the foundation for defining explicit data flow chains between diverse node types (Event, Transform, Action, Logic, etc.), enabling the construction of complex, automated processes and future visual workflow builders.
Future Enhancements
The node system is designed to support future enhancements:
- Rich Workflow Node Library: Expanding the core library with robust implementations of Event, Transform, AI Inference, Connector, Action, and Logic nodes.
- Visual Node Editor: Leveraging
NodeMetadata
(ports, descriptions) for a UI to connect the full range of workflow nodes. - Node Versioning: Core registry already supports versioning checks.
- Node Marketplace: Sharing custom node implementations.
- Workflow Engine: Building upon the message bus and port connections.
Documentation and Examples
For detailed guidance:
- Review
agentdock-core
source code insrc/nodes/
. - See
src/nodes/custom-tool-contributions.md
in the reference implementation for client-specific tool patterns. - Examine existing tools in the reference implementation's
src/nodes/tools/
directory.