SKILLS/HIVE FRAMEWORK/hive-concepts/SKILL.md
Core concepts for goal-driven agents - architecture, node types (event_loop, function), tool discovery, and workflow overview. Use when starting agent development or need to understand agent fundamentals.
npx skillsauth add mattmre/evokore-mcp hive-conceptsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Foundational knowledge for building goal-driven agents as Python packages.
Agents are built as Python packages:
exports/my_agent/
├── __init__.py # Package exports
├── __main__.py # CLI (run, info, validate, shell)
├── agent.py # Graph construction (goal, edges, agent class)
├── nodes/__init__.py # Node definitions (NodeSpec)
├── config.py # Runtime config
└── README.md # Documentation
Key Principle: Agent is visible and editable during build
Success criteria and constraints (written to agent.py)
goal = Goal(
id="research-goal",
name="Technical Research Agent",
description="Research technical topics thoroughly",
success_criteria=[
SuccessCriterion(
id="completeness",
description="Cover all aspects of topic",
metric="coverage_score",
target=">=0.9",
weight=0.4,
),
# 3-5 success criteria total
],
constraints=[
Constraint(
id="accuracy",
description="All information must be verified",
constraint_type="hard",
category="quality",
),
# 1-5 constraints total
],
)
Unit of work (written to nodes/init.py)
Node Types:
event_loop — Multi-turn streaming loop with tool execution and judge-based evaluation. Works with or without tools.function — Deterministic Python operations. No LLM involved.search_node = NodeSpec(
id="search-web",
name="Search Web",
description="Search for information and extract results",
node_type="event_loop",
input_keys=["query"],
output_keys=["search_results"],
system_prompt="Search the web for: {query}. Use the web_search tool to find results, then call set_output to store them.",
tools=["web_search"],
)
NodeSpec Fields for Event Loop Nodes:
| Field | Default | Description |
|-------|---------|-------------|
| client_facing | False | If True, streams output to user and blocks for input between turns |
| nullable_output_keys | [] | Output keys that may remain unset (for mutually exclusive outputs) |
| max_node_visits | 1 | Max times this node executes per run. Set >1 for feedback loop targets |
Connection between nodes (written to agent.py)
Edge Conditions:
on_success — Proceed if node succeeds (most common)on_failure — Handle errorsalways — Always proceedconditional — Based on expression evaluating node outputEdge Priority:
Priority controls evaluation order when multiple edges leave the same node. Higher priority edges are evaluated first. Use negative priority for feedback edges (edges that loop back to earlier nodes).
# Forward edge (evaluated first)
EdgeSpec(
id="review-to-campaign",
source="review",
target="campaign-builder",
condition=EdgeCondition.CONDITIONAL,
condition_expr="output.get('approved_contacts') is not None",
priority=1,
)
# Feedback edge (evaluated after forward edges)
EdgeSpec(
id="review-feedback",
source="review",
target="extractor",
condition=EdgeCondition.CONDITIONAL,
condition_expr="output.get('redo_extraction') is not None",
priority=-1,
)
For multi-turn conversations with the user, set client_facing=True on a node. The node will:
inject_event()intake_node = NodeSpec(
id="intake",
name="Intake",
description="Gather requirements from the user",
node_type="event_loop",
client_facing=True,
input_keys=[],
output_keys=["repo_url", "project_url"],
system_prompt="You are the intake agent. Ask the user for the repo URL and project URL.",
)
Legacy Note: The old
pause_nodes/entry_pointspattern still works butclient_facing=Trueis preferred for new agents.
STEP 1 / STEP 2 Prompt Pattern: For client-facing nodes, structure the system prompt with two explicit phases:
system_prompt="""\
**STEP 1 — Respond to the user (text only, NO tool calls):**
[Present information, ask questions, etc.]
**STEP 2 — After the user responds, call set_output:**
[Call set_output with the structured outputs]
"""
This prevents the LLM from calling set_output prematurely before the user has had a chance to respond.
Prefer fewer nodes that do more work over many thin single-purpose nodes:
Why: Each node boundary requires serializing outputs and passing context. Fewer nodes means the LLM retains full context of its work within the node. A research node that searches, fetches, and analyzes keeps all the source material in its conversation history.
When a node receives inputs that only arrive on certain edges (e.g., feedback only comes from a review → research feedback loop, not from intake → research), mark those keys as nullable_output_keys:
research_node = NodeSpec(
id="research",
input_keys=["research_brief", "feedback"],
nullable_output_keys=["feedback"], # Not present on first visit
max_node_visits=3,
...
)
An event loop node runs a multi-turn loop:
EventLoopNodes are auto-created by GraphExecutor at runtime. You do NOT need to manually register them. Both GraphExecutor (direct) and AgentRuntime / create_agent_runtime() handle event_loop nodes automatically.
# Direct execution — executor auto-creates EventLoopNodes
from framework.graph.executor import GraphExecutor
from framework.runtime.core import Runtime
runtime = Runtime(storage_path)
executor = GraphExecutor(
runtime=runtime,
llm=llm,
tools=tools,
tool_executor=tool_executor,
storage_path=storage_path,
)
result = await executor.execute(graph=graph, goal=goal, input_data=input_data)
# TUI execution — AgentRuntime also works
from framework.runtime.agent_runtime import create_agent_runtime
runtime = create_agent_runtime(
graph=graph, goal=goal, storage_path=storage_path,
entry_points=[...], llm=llm, tools=tools, tool_executor=tool_executor,
)
Nodes produce structured outputs by calling set_output(key, value) — a synthetic tool injected by the framework. When the LLM calls set_output, the value is stored in the output accumulator and made available to downstream nodes via shared memory.
set_output is NOT a real tool — it is excluded from real_tool_results. For client-facing nodes, this means a turn where the LLM only calls set_output (no other tools) is treated as a conversational boundary and will block for user input.
The judge is the SOLE mechanism for acceptance decisions. Do not add ad-hoc framework gating, output rollback, or premature rejection logic. If the LLM calls set_output too early, fix it with better prompts or a custom judge — not framework-level guards.
The judge controls when a node's loop exits:
evaluate(context) -> JudgeVerdictControls loop behavior:
max_iterations (default 50) — prevents infinite loopsmax_tool_calls_per_turn (default 10) — limits tool calls per LLM responsetool_call_overflow_margin (default 0.5) — wiggle room before discarding extra tool calls (50% means hard cutoff at 150% of limit)stall_detection_threshold (default 3) — detects repeated identical responsesmax_history_tokens (default 32000) — triggers conversation compactionWhen tool results exceed the context window, the framework automatically saves them to a spillover directory and truncates with a hint. Nodes that produce or consume large data should include the data tools:
save_data(filename, data) — Write data to a file in the data directoryload_data(filename, offset=0, limit=50) — Read data with line-based paginationlist_data_files() — List available data filesserve_file_to_user(filename, label="") — Get a clickable file:// URI for the userNote: data_dir is a framework-injected context parameter — the LLM never sees or passes it. GraphExecutor.execute() sets it per-execution via contextvars, so data tools and spillover always share the same session-scoped directory.
These are real MCP tools (not synthetic). Add them to nodes that handle large tool results:
research_node = NodeSpec(
...
tools=["web_search", "web_scrape", "load_data", "save_data", "list_data_files"],
)
Multiple ON_SUCCESS edges from the same source create parallel execution. All branches run concurrently via asyncio.gather(). Parallel event_loop nodes must have disjoint output_keys.
Controls how many times a node can execute in one graph run. Default is 1. Set higher for nodes that are targets of feedback edges (review-reject loops). Set 0 for unlimited (guarded by max_steps).
CRITICAL: Before adding a node with tools, you MUST verify the tools exist.
Tools are provided by MCP servers. Never assume a tool exists - always discover dynamically.
mcp__agent-builder__add_mcp_server(
name="tools",
transport="stdio",
command="python",
args='["mcp_server.py", "--stdio"]',
cwd="../tools"
)
# List all tools from all registered servers
mcp__agent-builder__list_mcp_tools()
# Or list tools from a specific server
mcp__agent-builder__list_mcp_tools(server_name="tools")
Before writing a node with tools=[...]:
list_mcp_tools() to get available toolslist_mcp_tools() first1. CREATE PACKAGE → mkdir + write skeletons
2. DEFINE GOAL → Write to agent.py + config.py
3. FOR EACH NODE:
- Propose design (event_loop for LLM work, function for deterministic)
- User approves
- Write to nodes/__init__.py IMMEDIATELY
- (Optional) Validate with test_node
4. CONNECT EDGES → Update agent.py
- Use priority for feedback edges (negative priority)
- (Optional) Validate with validate_graph
5. FINALIZE → Write agent class to agent.py
6. DONE - Agent ready at exports/my_agent/
Files written immediately. MCP tools optional for validation/testing bookkeeping.
Use hive-concepts when:
Next Steps:
hive-create skillhive-patterns skillAfter writing files, optionally use MCP tools for validation:
test_node - Validate node configuration with mock inputs
mcp__agent-builder__test_node(
node_id="search-web",
test_input='{"query": "test query"}',
mock_llm_response='{"results": "mock output"}'
)
validate_graph - Check graph structure
mcp__agent-builder__validate_graph()
# Returns: unreachable nodes, missing connections, event_loop validation, etc.
configure_loop - Set event loop parameters
mcp__agent-builder__configure_loop(
max_iterations=50,
max_tool_calls_per_turn=10,
stall_detection_threshold=3,
max_history_tokens=32000
)
Key Point: Files are written FIRST. MCP tools are for validation only.
development
Core orchestration framework for model-agnostic multi-agent workflows with handoff protocol, policy governance, and configuration schemas
testing
Specialized skill for triage issue skill workflows.
development
Complete workflow for building, implementing, and testing goal-driven agents. Orchestrates hive-* skills. Use when starting a new agent project, unsure which skill to use, or need end-to-end guidance.
development
Iterative agent testing with session recovery. Execute, analyze, fix, resume from checkpoints. Use when testing an agent, debugging test failures, or verifying fixes without re-running from scratch.