feat: add GCU knowledge to planning

This commit is contained in:
Richard Tang
2026-03-09 17:02:13 -07:00
parent bdcbcff6f3
commit f6f398b6b1
4 changed files with 93 additions and 13 deletions
+11 -4
View File
@@ -35,15 +35,21 @@ def _build_appendices() -> str:
# Shared appendices — appended to every coding node's system prompt.
_appendices = _build_appendices()
# GCU first-class section for building phase (when GCU is enabled).
# This is placed prominently in the main prompt body, not as an appendix.
# GCU first-class section (when GCU is enabled).
# Placed prominently in the main prompt body, not as an appendix.
_gcu_building_section = (
("\n\n# GCU Nodes — Browser Automation\n\n" + _gcu_guide)
if _is_gcu_enabled() and _gcu_guide
else ""
)
# Tools available to both coder (worker) and queen.
_gcu_planning_section = (
("\n\n# GCU Nodes — Browser Automation\n\n" + _gcu_guide)
if _is_gcu_enabled() and _gcu_guide
else ""
)
# Tools available to phases.
_SHARED_TOOLS = [
# File I/O
"read_file",
@@ -335,7 +341,7 @@ use box-drawing characters and clear flow arrows:
gather
subagent: gcu_search
input: user_request
tools: web_search,
tools: load_data,
save_data
on_success
@@ -1057,4 +1063,5 @@ __all__ = [
"_package_builder_knowledge",
"_appendices",
"_gcu_building_section",
"_gcu_planning_section",
]
@@ -42,6 +42,7 @@ async def create_queen(
_appendices,
_building_knowledge,
_gcu_building_section,
_gcu_planning_section,
_planning_knowledge,
_shared_building_knowledge,
_queen_behavior_always,
@@ -155,6 +156,7 @@ async def create_queen(
+ _queen_behavior_always
+ _queen_behavior_planning
+ _planning_knowledge
+ _gcu_planning_section
+ worker_identity
)
phase_state.prompt_planning = _queen_identity_planning + _planning_body
+52 -4
View File
@@ -514,6 +514,12 @@ def register_queen_lifecycle_tools(
"Use your coding tools to modify the agent, then call "
"load_built_agent(path) to stage it again."
)
# Nudge the queen to start coding instead of blocking for user input.
if phase_state is not None and phase_state.inject_notification:
await phase_state.inject_notification(
"[PHASE CHANGE] Switched to BUILDING phase. "
"Start implementing the changes now."
)
return json.dumps(result)
_stop_edit_tool = Tool(
@@ -609,19 +615,54 @@ def register_queen_lifecycle_tools(
"""Wrapper: scaffold or just switch to building phase."""
agent_name = (inputs.get("agent_name") or "").strip()
# No agent_name → just switch to building (for fixing existing agent)
# No agent_name → try to fall back to the session's current agent,
# or fail with actionable guidance.
if not agent_name:
runtime = _get_runtime()
if runtime is None:
# Try to resolve agent_name from the current session
fallback_path = getattr(session, "worker_path", None)
if fallback_path is not None:
agent_name = Path(fallback_path).name
else:
# Server path: check SessionManager
if session_manager is not None and manager_session_id:
srv_session = session_manager.get_session(manager_session_id)
if srv_session and getattr(srv_session, "worker_path", None):
fallback_path = srv_session.worker_path
agent_name = Path(fallback_path).name
if not agent_name:
return json.dumps(
{"error": "No worker loaded. Provide agent_name to scaffold a new agent."}
{
"error": (
"No agent_name provided and no agent loaded in this session. "
"To fix: call list_agents() to find the agent name, then call "
"initialize_and_build_agent(agent_name='<name>') to scaffold it."
)
}
)
# Fall back succeeded — switch to building without scaffolding
logger.info(
"initialize_and_build_agent: no agent_name provided, "
"falling back to session agent '%s'",
agent_name,
)
if phase_state is not None:
await phase_state.switch_to_building(source="tool")
if phase_state.inject_notification:
await phase_state.inject_notification(
"[PHASE CHANGE] Switched to BUILDING phase. "
"Start implementing the fix now."
)
return json.dumps(
{
"status": "editing",
"phase": "building",
"agent_name": agent_name,
"warning": (
f"No agent_name provided — using session agent '{agent_name}'. "
f"Agent files are at exports/{agent_name}/."
),
"message": (
"Switched to BUILDING phase. Full coding tools restored. "
"Implement the fix, then call load_built_agent(path) to reload."
@@ -643,6 +684,13 @@ def register_queen_lifecycle_tools(
if parsed.get("success", True):
if phase_state is not None:
await phase_state.switch_to_building(source="tool")
# Inject a continuation message so the queen starts
# building immediately instead of blocking for user input.
if phase_state.inject_notification:
await phase_state.inject_notification(
"[PHASE CHANGE] Agent scaffolded and switched to BUILDING phase. "
"Start implementing the agent nodes now."
)
except (json.JSONDecodeError, KeyError, TypeError):
pass
return result_str
@@ -116,16 +116,39 @@ customize_node = NodeSpec(
"for each selected job, saved as HTML, and Gmail drafts created in user's inbox."
),
system_prompt="""\
You are a career coach creating personalized application materials.
You are a career coach creating personalized application materials and Gmail drafts.
**CRITICAL: You MUST create Gmail drafts for each selected job using gmail_create_draft.**
**PROCESS:**
1. Create application_materials.html using save_data and append_data.
2. Generate resume customization list and professional cold email for each selected job.
3. Serve the file to the user.
4. Create Gmail drafts using gmail_create_draft.
2. For each selected job:
a. Generate a specific resume customization list
b. Create a professional cold outreach email
c. **IMMEDIATELY call gmail_create_draft** with:
- to: hiring manager or recruiter email (if available) or company email
- subject: "Application for [Job Title] - [Your Name]"
- html: the professional cold email in HTML format
3. Serve the application_materials.html file to the user.
4. Confirm each Gmail draft was created successfully.
**EMAIL REQUIREMENTS:**
- Professional, personalized cold outreach email
- Reference specific company details and role
- Mention 2-3 relevant qualifications from their resume
- Include clear call-to-action
- Professional email signature
- Format as HTML with proper structure
**Gmail Draft Creation:**
For each job, you MUST call gmail_create_draft(to="[email]", subject="[subject]", html="[email_html]")
- Extract company email from job listing if available
- Use generic format like "careers@[company].com" if no specific email
- Subject format: "Application for [Job Title] - [Applicant Name]"
- HTML email body with proper formatting
**FINISH:**
Call set_output("application_materials", "Completed")
Only call set_output("application_materials", "Completed") AFTER creating ALL Gmail drafts.
""",
tools=["save_data", "append_data", "serve_file_to_user", "gmail_create_draft"],
)