Skip to content

Commit 132d00d

Browse files
Merge pull request #5769 from aden-hive/queen-mode-separation
Queen mode separation: building, staging, and running modes
2 parents c7818c2 + a604fee commit 132d00d

32 files changed

+2925
-840
lines changed

core/framework/agents/hive_coder/agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
id="dynamic-tool-discovery",
7070
description=(
7171
"Always discover available tools dynamically via "
72-
"discover_mcp_tools before referencing tools in agent designs"
72+
"list_agent_tools before referencing tools in agent designs"
7373
),
7474
constraint_type="hard",
7575
category="correctness",

core/framework/agents/hive_coder/nodes/__init__.py

Lines changed: 231 additions & 130 deletions
Large diffs are not rendered by default.

core/framework/agents/hive_coder/reference/anti_patterns.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ profile_setup → daily_intake → update_tracker → analyze_progress → gener
4848
```
4949
`analyze_progress` has no tools. `schedule_reminders` just sets one boolean. `report` just presents analysis. `update_tracker` and `generate_plan` are sequential autonomous work.
5050

51-
**Good example** (3 nodes):
51+
**Good example** (2 nodes):
5252
```
53-
intake (client-facing) → process (autonomous: track + analyze + plan) → intake (loop back)
53+
process (autonomous: track + analyze + plan) → review (client-facing) → process (loop back)
5454
```
55-
One client-facing node handles ALL user interaction (setup, logging, reports). One autonomous node handles ALL backend work (CSV update, analysis, plan generation) with tools and context preserved.
55+
The queen handles intake (gathering requirements from the user) and passes the task via `run_agent_with_input(task)`. One autonomous node handles ALL backend work (CSV update, analysis, plan generation) with tools and context preserved. One client-facing node handles review/approval when needed.
5656

5757
12. **Adding framework gating for LLM behavior** — Don't add output rollback, premature rejection, or interaction protocol injection. Fix with better prompts or custom judges.
5858

@@ -109,3 +109,5 @@ def test_research_routes_back_to_interact(self):
109109
25. **Manually wiring browser tools on event_loop nodes** — If the agent needs browser automation, use `node_type="gcu"` which auto-includes all browser tools and prepends best-practices guidance. Do NOT manually list browser tool names on event_loop nodes — they may not exist in the MCP server or may be incomplete. See the GCU Guide appendix.
110110

111111
26. **Using GCU nodes as regular graph nodes** — GCU nodes (`node_type="gcu"`) are exclusively subagents. They must ONLY appear in a parent node's `sub_agents=["gcu-node-id"]` list and be invoked via `delegate_to_sub_agent()`. They must NEVER be connected via edges, used as entry nodes, or used as terminal nodes. If a GCU node appears as an edge source or target, the graph will fail pre-load validation.
112+
113+
27. **Adding a client-facing intake node to worker agents** — The queen owns intake. She defines the entry node's `input_keys` at build time and fills them via `run_agent_with_input(task)` at run time. Worker agents should start with an autonomous processing node, NOT a client-facing intake node that asks the user for requirements. Client-facing nodes in workers are for mid-execution review/approval only.

core/framework/agents/hive_coder/reference/file_templates.md

Lines changed: 29 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -57,51 +57,28 @@ metadata = AgentMetadata()
5757

5858
from framework.graph import NodeSpec
5959

60-
# Node 1: Intake (client-facing)
61-
intake_node = NodeSpec(
62-
id="intake",
63-
name="Intake",
64-
description="Gather requirements from the user",
60+
# Node 1: Process (autonomous entry node)
61+
# The queen handles intake and passes structured input via
62+
# run_agent_with_input(task). NO client-facing intake node.
63+
# The queen defines input_keys at build time and fills them at run time.
64+
process_node = NodeSpec(
65+
id="process",
66+
name="Process",
67+
description="Execute the task using available tools",
6568
node_type="event_loop",
66-
client_facing=True,
6769
max_node_visits=0, # Unlimited for forever-alive
68-
input_keys=["topic"],
69-
output_keys=["brief"],
70-
success_criteria="The brief is specific and actionable.",
71-
system_prompt="""\
72-
You are an intake specialist.
73-
74-
**STEP 1 — Read and respond (text only, NO tool calls):**
75-
1. Read the topic provided
76-
2. If vague, ask 1-2 clarifying questions
77-
3. If clear, confirm your understanding
78-
79-
**STEP 2 — After the user confirms, call set_output:**
80-
- set_output("brief", "Clear description of what to do")
81-
""",
82-
tools=[],
83-
)
84-
85-
# Node 2: Worker (autonomous)
86-
worker_node = NodeSpec(
87-
id="worker",
88-
name="Worker",
89-
description="Do the main work",
90-
node_type="event_loop",
91-
max_node_visits=0,
92-
input_keys=["brief", "feedback"],
70+
input_keys=["user_request", "feedback"],
9371
output_keys=["results"],
9472
nullable_output_keys=["feedback"], # Only on feedback edge
9573
success_criteria="Results are complete and accurate.",
9674
system_prompt="""\
97-
You are a worker agent. Given a brief, do the work.
98-
99-
If feedback is provided, this is a follow-up — address the feedback.
75+
You are a processing agent. Your task is in memory under "user_request". \
76+
If "feedback" is present, this is a revision — address the feedback.
10077
10178
Work in phases:
10279
1. Use tools to gather/process data
10380
2. Analyze results
104-
3. Call set_output for each key in a SEPARATE turn:
81+
3. Call set_output in a SEPARATE turn:
10582
- set_output("results", "structured results")
10683
""",
10784
tools=["web_search", "web_scrape", "save_data", "load_data", "list_data_files"],
@@ -115,7 +92,7 @@ review_node = NodeSpec(
11592
node_type="event_loop",
11693
client_facing=True,
11794
max_node_visits=0,
118-
input_keys=["results", "brief"],
95+
input_keys=["results", "user_request"],
11996
output_keys=["next_action", "feedback"],
12097
nullable_output_keys=["feedback"],
12198
success_criteria="User has reviewed and decided next steps.",
@@ -128,14 +105,14 @@ Present the results to the user.
128105
3. Ask: satisfied, or want changes?
129106
130107
**STEP 2 — After user responds, call set_output:**
131-
- set_output("next_action", "new_topic") — if starting fresh
108+
- set_output("next_action", "done") — if satisfied
132109
- set_output("next_action", "revise") — if changes needed
133110
- set_output("feedback", "what to change") — only if revising
134111
""",
135112
tools=[],
136113
)
137114

138-
__all__ = ["intake_node", "worker_node", "review_node"]
115+
__all__ = ["process_node", "review_node"]
139116
```
140117

141118
## agent.py
@@ -155,7 +132,7 @@ from framework.runtime.agent_runtime import AgentRuntime, create_agent_runtime
155132
from framework.runtime.execution_stream import EntryPointSpec
156133

157134
from .config import default_config, metadata
158-
from .nodes import intake_node, worker_node, review_node
135+
from .nodes import process_node, review_node
159136

160137
# Goal definition
161138
goal = Goal(
@@ -172,27 +149,26 @@ goal = Goal(
172149
)
173150

174151
# Node list
175-
nodes = [intake_node, worker_node, review_node]
152+
nodes = [process_node, review_node]
176153

177154
# Edge definitions
178155
edges = [
179-
EdgeSpec(id="intake-to-worker", source="intake", target="worker",
180-
condition=EdgeCondition.ON_SUCCESS, priority=1),
181-
EdgeSpec(id="worker-to-review", source="worker", target="review",
156+
EdgeSpec(id="process-to-review", source="process", target="review",
182157
condition=EdgeCondition.ON_SUCCESS, priority=1),
183-
# Feedback loop
184-
EdgeSpec(id="review-to-worker", source="review", target="worker",
158+
# Feedback loop — revise results
159+
EdgeSpec(id="review-to-process", source="review", target="process",
185160
condition=EdgeCondition.CONDITIONAL,
186161
condition_expr="str(next_action).lower() == 'revise'", priority=2),
187-
# Loop back for new topic
188-
EdgeSpec(id="review-to-intake", source="review", target="intake",
162+
# Loop back for next task (queen sends new input)
163+
EdgeSpec(id="review-done", source="review", target="process",
189164
condition=EdgeCondition.CONDITIONAL,
190-
condition_expr="str(next_action).lower() == 'new_topic'", priority=1),
165+
condition_expr="str(next_action).lower() == 'done'", priority=1),
191166
]
192167

193-
# Graph configuration
194-
entry_node = "intake"
195-
entry_points = {"start": "intake"}
168+
# Graph configuration — entry is the autonomous process node
169+
# The queen handles intake and passes the task via run_agent_with_input(task)
170+
entry_node = "process"
171+
entry_points = {"start": "process"}
196172
pause_nodes = []
197173
terminal_nodes = [] # Forever-alive
198174

@@ -208,7 +184,7 @@ class MyAgent:
208184
self.goal = goal
209185
self.nodes = nodes
210186
self.edges = edges
211-
self.entry_node = entry_node
187+
self.entry_node = entry_node # "process" — autonomous entry
212188
self.entry_points = entry_points
213189
self.pause_nodes = pause_nodes
214190
self.terminal_nodes = terminal_nodes
@@ -498,7 +474,7 @@ def tui():
498474
llm = LiteLLMProvider(model=agent.config.model, api_key=agent.config.api_key, api_base=agent.config.api_base)
499475
runtime = create_agent_runtime(
500476
graph=agent._build_graph(), goal=agent.goal, storage_path=storage,
501-
entry_points=[EntryPointSpec(id="start", name="Start", entry_node="intake", trigger_type="manual", isolation_level="isolated")],
477+
entry_points=[EntryPointSpec(id="start", name="Start", entry_node="process", trigger_type="manual", isolation_level="isolated")],
502478
llm=llm, tools=list(agent._tool_registry.get_tools().values()), tool_executor=agent._tool_registry.get_executor())
503479
await runtime.start()
504480
try:

core/framework/agents/hive_coder/reference/framework_guide.md

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,19 @@ downstream node only sees the serialized summary string.
131131
- A "report" node that presents analysis → merge into the client-facing node
132132
- A "confirm" or "schedule" node that doesn't call any external service → remove
133133

134-
**Typical agent structure (3 nodes):**
134+
**Typical agent structure (2 nodes):**
135135
```
136-
intake (client-facing) ←→ process (autonomous) ←→ review (client-facing)
136+
process (autonomous) ←→ review (client-facing)
137137
```
138-
Or for simpler agents, just 2 nodes:
138+
The queen owns intake — she gathers requirements from the user, then
139+
passes structured input via `run_agent_with_input(task)`. When building
140+
the agent, design the entry node's `input_keys` to match what the queen
141+
will provide at run time. Worker agents should NOT have a client-facing
142+
intake node. Client-facing nodes are for mid-execution review/approval only.
143+
144+
For simpler agents, just 1 autonomous node:
139145
```
140-
interact (client-facing) → process (autonomous) → interact (loop)
146+
process (autonomous) — loops back to itself
141147
```
142148

143149
### nullable_output_keys
@@ -397,7 +403,7 @@ from .agent import (
397403
### Reference Agent
398404

399405
See `exports/gmail_inbox_guardian/agent.py` for a complete example with:
400-
- Primary client-facing intake node (user configures rules)
406+
- Primary client-facing node (user configures rules)
401407
- Timer-based scheduled inbox checks (every 20 min)
402408
- Webhook-triggered email event handling
403409
- Shared isolation for memory access across streams
@@ -413,13 +419,13 @@ See `exports/gmail_inbox_guardian/agent.py` for a complete example with:
413419
## Tool Discovery
414420

415421
Do NOT rely on a static tool list — it will be outdated. Always use
416-
`list_agent_tools()` to get available tool names grouped by category.
417-
For full schemas with parameter details, use `discover_mcp_tools()`.
422+
`list_agent_tools()` to discover available tools, grouped by category.
418423

419424
```
420-
list_agent_tools() # all available tools
421-
list_agent_tools("exports/my_agent/mcp_servers.json") # specific agent
422-
discover_mcp_tools() # full schemas with params
425+
list_agent_tools() # names + descriptions, all groups
426+
list_agent_tools(output_schema="full") # include input_schema
427+
list_agent_tools(group="gmail") # only gmail_* tools
428+
list_agent_tools("exports/my_agent/mcp_servers.json") # specific agent's tools
423429
```
424430

425431
After building, validate tools exist: `validate_agent_tools("exports/{name}")`

core/framework/agents/hive_coder/reference/gcu_guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Do NOT use GCU for:
2121
- Same underlying `EventLoopNode` class — no new imports needed
2222
- `tools=[]` is correct — tools are auto-populated at runtime
2323

24-
## GCU Architecture Pattern
24+
## GCU Architecture Pattern
2525

2626
GCU nodes are **subagents** — invoked via `delegate_to_sub_agent()`, not connected via edges.
2727

0 commit comments

Comments
 (0)