The Agent Node supports sub-agents, sub-workflows, canvas node tools, an orchestrator pattern, skills, MCP connections, tool calling, optional persistent memory (per canvas node, with optional sharing to other agents), and optional human review. This page describes the architecture.
Sub-Agents
Sub-agents are agent nodes that an orchestrator can call via the call_sub_agent tool.
- Orchestrator:
isOrchestrator: true,subAgentLabels: ["researchAgent", "summarizerAgent"] - Sub-agents: Must use
$input.textin their User Message so they receive the orchestrator's prompt - Tool:
call_sub_agentwithsub_agent_label(enum) andprompt - Depth limit: Max 5 nested sub-agent calls
Execution Flow
- Orchestrator decides to call a sub-agent
_execute_sub_agent_toolfinds the target agent by label- Builds
synthetic_inputs = {"input": {"text": prompt}} - Runs
_execute_agent_nodefor the sub-agent - Result is returned to the orchestrator
Parallel Sub-Agent Execution
When the orchestrator returns multiple call_sub_agent tool calls in a single turn, they are executed in parallel. For example, if the user asks about "Paris" and the orchestrator calls both distanceAgent and foodAgent, both run concurrently instead of sequentially.
- Benefit: Total time is roughly the slowest sub-agent, not the sum of all.
- Hint: Encourage the orchestrator to call multiple sub-agents in one turn when the task requires it.
Sub-Workflows
When subWorkflowIds is configured, the agent can call other workflows via the call_sub_workflow tool.
- Config:
subWorkflowIds: ["workflow-uuid-1", "workflow-uuid-2"] - Tool:
call_sub_workflowwithworkflow_id(enum) andinputs(object) - Depth limit: Max 5 nested sub-workflow calls
Execution Flow
- Agent decides to call a sub-workflow
_execute_sub_workflow_toolfetches the workflow fromworkflow_cache- Builds
enriched_inputs = {"headers": {}, "query": {}, "body": inputs} - Runs
WorkflowExecutor.execute()for the sub-workflow - Result is returned to the agent
Sub-workflows cannot pause for HITL in v1. If a nested sub-workflow tries to enter a pending review state, the execution fails instead of suspending inside the nested call.
Canvas Node Tools
Canvas node tools are workflow nodes connected to an agent through the tool-input handle. During agent setup, _build_node_tool_schemas scans those edges and adds one OpenAI-compatible tool schema per connected node.
- Tool source:
_source: "node_tool" - Tool name: derived from the connected node label, with suffixes added for duplicates
- Tool parameters: built from the node's
agentProvidedFields - Fixed fields: all node fields not listed in
agentProvidedFieldsstay in the node configuration
Execution Flow
- Agent decides to call a connected node tool
_execute_node_toolcopies the node data- Agent-provided arguments are merged into the configured node fields
execute_noderuns that node once with branch scheduling disabled- The original node data is restored
- The node output is returned to the agent as the tool result
Tool nodes are excluded from normal workflow scheduling so they do not run twice. They execute only through the agent tool call path.
Human-in-the-Loop
When hitlEnabled is set on an agent, the agent receives a request_human_review tool that it can call at specific approval checkpoints.
- The system prompt and node HITL guidelines tell the agent which actions require approval
- When the agent reaches one of those steps, it calls
request_human_review - Heym stores a workflow execution snapshot and creates a public review request
- The node result is marked
pendingwith a review URL - The agent's
reviewoutput handle is scheduled immediately so notification branches can run with the pending payload - Main execution scheduling stops until the reviewer responds
- On
accept,edit, orrefuse, the executor is rebuilt from the stored snapshot - The paused agent continues with the approved review context, or exits immediately on
refuse - Downstream nodes continue after the resumed agent finishes, without rerunning the
reviewbranch - If a later action also requires approval, the agent can create another HITL checkpoint in the same run
The reviewer-facing summary is generated from the review request itself, so the node field can stay focused on approval policy and timing.
This lets agent workflows wait for external approval while preserving conversation history, completed node outputs, loop state, and other runtime context.
Orchestrator Tool Executor
The custom tool executor routes:
- Sub-agent calls (
_source == "sub_agent") →_execute_sub_agent_tool - Sub-workflow calls (
_source == "sub_workflow") →_execute_sub_workflow_tool - Canvas node tools (
_source == "node_tool") →_execute_node_tool - Human review (
_source == "hitl") → creates a pending HITL checkpoint - Other tools →
_unified_tool_executor(Python, MCP, skill tools)
It is used when HITL is enabled, when isOrchestrator with subAgentLabels is set, or when subWorkflowIds is configured.
Skills
Skills extend the agent's system context and can add Python tools.
- Shape:
AgentSkill={ id, name, content, files?, timeoutSeconds? } - Content: Prepended to the system instruction (joined with
---) - Python tools: For each skill with
.pyfiles, askill_{skill_name}tool is added - Execution:
skill_python_executor.pyruns scripts withuv run pythonin a temp dir; args as JSON on stdin, result as JSON on stdout
Skills can be added by dropping a .zip or .md file onto the Skills area in the Agent Node config.
MCP Client
The MCP (Model Context Protocol) client connects to external tool servers.
- Transports:
stdio(command + args) orsse(url + headers) - List tools:
list_mcp_tools→ClientSession.list_tools()→ converted to OpenAI function format with_source: "mcp" - Execute:
execute_mcp_tool→ClientSession.call_tool()→ result via_extract_tool_result
Configure MCP connections in the MCP Tab or on the agent node's mcpConnections.
Tool Calling
The LLM service runs an execute_with_tools loop:
- Context compression check — before each iteration, estimate token usage and compress if needed (see below)
- Call the model with
toolsandtool_choice: "auto" - If
tool_callsexist: for each call, runtool_executor(tool_def, name, args, timeout) - Append tool result to messages, repeat
- Stop when no more tool calls or
max_tool_iterationsreached
Tool Dispatch
_unified_tool_executor routes by _source:
| Source | Handler |
|---|---|
mcp | execute_mcp_tool |
skill | execute_skill_python |
| (default) | execute_tool (Python code tools) |
Tools use OpenAI function-calling format: name, description, parameters (JSON schema).
Context Compression
To prevent context overflow on long-running agents, Heym automatically compresses the accumulated messages list before each tool iteration.
Algorithm
- Estimate tokens:
total_chars / 4across all messages (fast, no tokenizer needed) - Threshold check: if
estimated_tokens < context_limit × 0.80, skip compression - Context limit: try
client.models.retrieve(model).context_window; fall back to a built-in table (gpt-4o→ 128K,claude-3-5-sonnet→ 200K,gemini-2.0-flash→ 1M, etc.); default 128K - Anchor preservation: always keep
- First
systemmessage (agent identity and instructions) - First
usermessage (original task) - Last
usermessage (most recent instruction)
- First
- Summarize middle: everything between first and last user message is serialized and sent to the same model with a summarization prompt
- Rebuild:
[system, first_user, assistant(summary), last_user]
Compression is skipped if there are fewer than 2 distinct user messages (no middle to summarize) or if the LLM summarization call fails (safe fallback: return original messages).
Observability
Each compression event is recorded as:
- A
_context_compressionentry intool_calls_collected→ visible in Execution History run detail - An
on_tool_callevent withphase: "compression"→ rendered in the Debug panel asContext compressed (N messages → summary) - A
context.compressionLLM trace entry → visible in the Traces tab with before/after token estimates
Agent tool calls also emit richer observability:
- Per-call lifecycle fields on
tool_calls:tool_call_id,status(success/error/pending/timeout/cancelled),started_at,finished_at,elapsed_ms - Aggregate
tool_metricson the agent result (counts by status plus total/max duration);_context_compressionentries are excluded from those counts - Opt-in OpenTelemetry child spans named
heym.agent.tool.execute(see OpenTelemetry Tracing) - Persisted LLM trace / execution-history
tool_callspayloads are redacted and size-bounded; live HITL resume state keeps originals for matching
Related
- Why Heym – Multi-agent orchestration and AI-native features
- Agent Node – Configuration and parameters
- Agent Persistent Memory – Knowledge graph per agent node and peer sharing
- Human-in-the-Loop – Review links, pending payloads, and resume behavior
- OpenTelemetry Tracing – Workflow, node, and Agent tool spans
- Node Types – Agent and related nodes
- Parallel Execution – DAG-based and sub-agent parallel execution
- MCP Tab – Configure MCP connections
- Expression DSL –
$input.textfor sub-agents