feat: add GCU knowledge to planning
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user