{
  "agent": {
    "id": "job_hunter",
    "name": "Job Hunter",
    "version": "1.0.0",
    "description": "Analyze a user's resume to identify their strongest role fits, find 10 matching job opportunities, let the user select which to pursue, then generate a resume customization list and cold outreach email for each selected job."
  },
  "graph": {
    "id": "job_hunter-graph",
    "goal_id": "job-hunter",
    "version": "1.0.0",
    "entry_node": "intake",
    "entry_points": {
      "start": "intake"
    },
    "pause_nodes": [],
    "terminal_nodes": [
      "customize"
    ],
    "nodes": [
      {
        "id": "intake",
        "name": "Intake",
        "description": "Collect resume from user, analyze skills and experience, identify 3-5 specific role types",
        "node_type": "event_loop",
        "input_keys": [],
        "output_keys": [
          "resume_text",
          "role_analysis"
        ],
        "nullable_output_keys": [],
        "input_schema": {},
        "output_schema": {},
        "system_prompt": "You are a career analyst helping a job seeker find their best opportunities.\n\n**STEP 1 \u2014 Greet and collect resume:**\n\nAsk the user to provide their resume. They can either paste the text directly or provide a path to a PDF file. Be friendly and concise:\n\"Please paste your resume below, or provide the file path to your PDF resume (e.g., /path/to/resume.pdf). I'll analyze your experience and identify the roles where you have the strongest chance of success.\"\n\nIf the user provides a file path to a PDF, call pdf_read(file_path=\"<path>\") to extract the text before proceeding.\n\n**STEP 2 \u2014 After the user provides their resume:**\n\nAnalyze the resume thoroughly:\n1. Identify key skills (technical and soft skills)\n2. Summarize years and types of experience\n3. Identify 3-5 SPECIFIC, GRANULAR role types where they're competitive\n\n**IMPORTANT \u2014 Role Specificity:**\nRespect the job seeker by providing granular options, not generic buckets.\n- BAD: \"Software Engineer\" (too broad)\n- GOOD: \"Backend Engineer (Python/Django)\", \"Platform Engineer\", \"API Developer\", \"Data Pipeline Engineer\"\n\nEach role should be distinct and searchable. The more specific, the better the job matches will be\n\nPresent your analysis to the user and ask if they agree with the role types identified. DO NOT ask follow-up questions. DO NOT ask which roles to focus on.\n\n**STEP 3 \u2014 After user confirms roles, call set_output:**\n\nUse set_output to store:\n- set_output(\"resume_text\", \"<the full resume text>\")\n- set_output(\"role_analysis\", \"<JSON with: skills, experience_summary, target_roles (3-5 specific role titles)>\")\n\nIMPORTANT: When the user says \"yes\", \"sure\", \"go ahead\", \"find jobs\" or similar, call set_output IMMEDIATELY. NEVER ask the user to pick between roles.",
        "tools": ["pdf_read"],
        "model": null,
        "function": null,
        "routes": {},
        "max_retries": 3,
        "retry_on": [],
        "max_node_visits": 1,
        "output_model": null,
        "max_validation_retries": 2,
        "client_facing": true,
        "success_criteria": null
      },
      {
        "id": "job-review",
        "name": "Job Review",
        "description": "Present all 10 jobs to the user, let them select which to pursue",
        "node_type": "event_loop",
        "input_keys": [
          "job_listings",
          "resume_text"
        ],
        "output_keys": [
          "selected_jobs"
        ],
        "nullable_output_keys": [],
        "input_schema": {},
        "output_schema": {},
        "system_prompt": "You are helping a job seeker choose which positions to apply to.\n\n**STEP 1 \u2014 Present the jobs (text only, NO tool calls):**\n\nDisplay all 10 jobs in a clear, numbered format:\n\n```\n**Job Opportunities Found:**\n\n1. **[Job Title]** at [Company]\n   Location: [Location]\n   [Brief description - 2-3 lines]\n   URL: [link]\n\n2. **[Job Title]** at [Company]\n   ...\n```\n\nAfter listing all jobs, ask:\n\"Which jobs would you like me to create application materials for? Please list the numbers (e.g., '1, 3, 5') or say 'all' for all of them.\"\n\n**STEP 2 \u2014 After the user responds:**\n\nConfirm their selection and call set_output:\n- set_output(\"selected_jobs\", \"<JSON array of the selected job objects>\")\n\nOnly include the jobs the user explicitly selected.",
        "tools": [],
        "model": null,
        "function": null,
        "routes": {},
        "max_retries": 3,
        "retry_on": [],
        "max_node_visits": 1,
        "output_model": null,
        "max_validation_retries": 2,
        "client_facing": true,
        "success_criteria": null
      },
      {
        "id": "customize",
        "name": "Customize",
        "description": "For each selected job, generate resume customization list and cold outreach email",
        "node_type": "event_loop",
        "input_keys": [
          "selected_jobs",
          "resume_text"
        ],
        "output_keys": [
          "application_materials"
        ],
        "nullable_output_keys": [],
        "input_schema": {},
        "output_schema": {},
        "system_prompt": "You are a career coach creating personalized application materials.\n\n**INPUT:** You have the user's resume and their selected jobs.\n\n**OUTPUT FORMAT: Single HTML Report \u2014 Built Incrementally**\nBuild ONE polished HTML report, but write it in CHUNKS using append_data to avoid token limits.\n\n**CRITICAL: You MUST build the file in multiple append_data calls. NEVER try to write the entire HTML in a single save_data call \u2014 it will exceed the output token limit and fail.**\n\n**PROCESS (follow exactly):**\n\n**Step 1 \u2014 Write HTML header + table of contents:**\nCall save_data to create the file with the HTML head, styles, and TOC:\nInclude: DOCTYPE, head with styles, opening body tag, h1, and the table of contents linking to each selected job. End with the TOC closing div.\n\nCSS to use:\n  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 900px; margin: 0 auto; padding: 40px; line-height: 1.6; }\n  h1 { color: #1a1a1a; border-bottom: 2px solid #0066cc; padding-bottom: 10px; }\n  h2 { color: #0066cc; margin-top: 40px; padding-top: 20px; border-top: 1px solid #e0e0e0; }\n  h3 { color: #333; margin-top: 20px; }\n  .job-section { margin-bottom: 60px; }\n  .email-card { background: #f8f9fa; border-left: 4px solid #0066cc; padding: 20px; margin: 20px 0; white-space: pre-wrap; }\n  .customization-list { background: #fff; border: 1px solid #e0e0e0; padding: 20px; border-radius: 8px; }\n  ul { line-height: 1.8; }\n  .toc { background: #f0f4f8; padding: 20px; border-radius: 8px; margin-bottom: 40px; }\n  .toc a { color: #0066cc; text-decoration: none; }\n  .toc a:hover { text-decoration: underline; }\n  .job-url { color: #666; font-size: 0.9em; }\n\n**Step 2 \u2014 Append each job section ONE AT A TIME:**\nFor EACH selected job, call append_data with that job's section.\nEach section should contain:\n- Job title + company as h2\n- Job URL link\n- Resume Customization List (Priority Changes, Keywords, Experiences to Emphasize, Suggested Rewrites)\n- Cold Outreach Email in an email-card div (subject line + body, under 150 words)\n\n**Step 3 \u2014 Append HTML footer:**\nappend_data(filename=\"application_materials.html\", data=\"</body>\\n</html>\")\n\n**Step 4 \u2014 Serve the file:**\nCall serve_file_to_user(filename=\"application_materials.html\", open_in_browser=true)\nPrint the file_path from the result so the user can click it later.\n\n**Step 5 \u2014 Create Gmail Drafts (in batches of 5):**\nIMPORTANT: Do NOT create all drafts in one turn. Create at most 5 gmail_create_draft calls per turn to stay within tool call limits. If there are more than 5 jobs, create the first 5 drafts, then create the remaining drafts in the next turn.\nFor each selected job, call gmail_create_draft. If it errors, skip ALL remaining drafts and tell the user.\n\n**Step 6 \u2014 Finish:**\nCall set_output(\"application_materials\", \"Created application_materials.html with materials for {N} jobs\")\n\n**IMPORTANT:**\n- Only suggest truthful resume changes \u2014 enhance presentation, never fabricate\n- Cold emails must be professional, personalized, and under 150 words\n- ALWAYS print the full file path so users can easily access the file later\n- If a save_data or append_data call fails with a truncation error, you are writing too much in one call. Break it into smaller chunks.",
        "tools": [
          "save_data",
          "append_data",
          "serve_file_to_user",
          "gmail_create_draft"
        ],
        "model": null,
        "function": null,
        "routes": {},
        "max_retries": 3,
        "retry_on": [],
        "max_node_visits": 1,
        "output_model": null,
        "max_validation_retries": 2,
        "client_facing": true,
        "success_criteria": null
      },
      {
        "id": "job-search",
        "name": "Job Search",
        "description": "Search for 10 jobs matching identified roles by scraping job board sites directly",
        "node_type": "event_loop",
        "input_keys": [
          "role_analysis"
        ],
        "output_keys": [
          "job_listings"
        ],
        "nullable_output_keys": [],
        "input_schema": {},
        "output_schema": {},
        "system_prompt": "You are a job search specialist. Your task is to find 10 relevant job openings.\n\n**INPUT:** You have access to role_analysis containing target roles and skills.\n\n**PROCESS:**\nUse web_scrape to directly scrape job listings from these job boards. Build search URLs with the role title:\n\n**Recommended Job Sites (scrape these directly):**\n1. **LinkedIn Jobs:** https://www.linkedin.com/jobs/search/?keywords={role_title}\n2. **Indeed:** https://www.indeed.com/jobs?q={role_title}\n3. **Glassdoor:** https://www.glassdoor.com/Job/jobs.htm?sc.keyword={role_title}\n4. **Wellfound (Startups):** https://wellfound.com/jobs?q={role_title}\n5. **RemoteOK:** https://remoteok.com/remote-{role_title}-jobs\n\n**Strategy:**\n- For each target role in role_analysis, scrape 1-2 job board search result pages\n- Extract job listings from the scraped HTML\n- If a job looks promising, scrape its detail page for more info\n- Gather 10 quality job listings total across the target roles\n\n**For each job, extract:**\n- Job title\n- Company name\n- Location (or \"Remote\" if applicable)\n- Brief job description/requirements summary\n- URL to the job posting\n- Any info about the hiring manager or company contact if visible\n\n**OUTPUT:** Once you have 10 jobs, call:\nset_output(\"job_listings\", \"<JSON array of 10 job objects with title, company, location, description, url, contact_info>\")\n\nFocus on finding REAL, current job postings with actual URLs the user can visit.",
        "tools": [
          "web_scrape"
        ],
        "model": null,
        "function": null,
        "routes": {},
        "max_retries": 3,
        "retry_on": [],
        "max_node_visits": 1,
        "output_model": null,
        "max_validation_retries": 2,
        "client_facing": false,
        "success_criteria": null
      }
    ],
    "edges": [
      {
        "id": "intake-to-job-search",
        "source": "intake",
        "target": "job-search",
        "condition": "on_success",
        "condition_expr": null,
        "priority": 1,
        "input_mapping": {}
      },
      {
        "id": "job-search-to-job-review",
        "source": "job-search",
        "target": "job-review",
        "condition": "on_success",
        "condition_expr": null,
        "priority": 1,
        "input_mapping": {}
      },
      {
        "id": "job-review-to-customize",
        "source": "job-review",
        "target": "customize",
        "condition": "on_success",
        "condition_expr": null,
        "priority": 1,
        "input_mapping": {}
      }
    ],
    "max_steps": 100,
    "max_retries_per_node": 3,
    "description": "Analyze a user's resume to identify their strongest role fits, find 10 matching job opportunities, let the user select which to pursue, then generate a resume customization list and cold outreach email for each selected job.",
    "created_at": "2026-02-13T18:41:10.324397"
  },
  "goal": {
    "id": "job-hunter",
    "name": "Job Hunter",
    "description": "Analyze a user's resume to identify their strongest role fits, find 10 matching job opportunities, let the user select which to pursue, then generate a resume customization list and cold outreach email for each selected job.",
    "status": "draft",
    "success_criteria": [
      {
        "id": "role-identification",
        "description": "Identifies 3-5 role types that genuinely match the user's experience",
        "metric": "role_match_accuracy",
        "target": ">=0.8",
        "weight": 0.2,
        "met": false
      },
      {
        "id": "job-relevance",
        "description": "Found jobs align with identified roles and user's background",
        "metric": "job_relevance_score",
        "target": ">=0.8",
        "weight": 0.2,
        "met": false
      },
      {
        "id": "customization-quality",
        "description": "Resume changes are specific, actionable, and tailored to each job posting",
        "metric": "customization_specificity",
        "target": ">=0.85",
        "weight": 0.25,
        "met": false
      },
      {
        "id": "email-effectiveness",
        "description": "Cold emails are personalized, professional, and reference specific company/role details",
        "metric": "email_personalization_score",
        "target": ">=0.85",
        "weight": 0.2,
        "met": false
      },
      {
        "id": "user-satisfaction",
        "description": "User approves outputs without major revisions needed",
        "metric": "approval_rate",
        "target": ">=0.9",
        "weight": 0.15,
        "met": false
      }
    ],
    "constraints": [
      {
        "id": "realistic-roles",
        "description": "Only suggest roles the user is realistically qualified for - no aspirational stretch roles",
        "constraint_type": "quality",
        "category": "accuracy",
        "check": ""
      },
      {
        "id": "truthful-customizations",
        "description": "Resume customizations must be truthful - enhance presentation, never fabricate experience",
        "constraint_type": "ethical",
        "category": "integrity",
        "check": ""
      },
      {
        "id": "professional-emails",
        "description": "Cold emails must be professional and not spammy",
        "constraint_type": "quality",
        "category": "tone",
        "check": ""
      },
      {
        "id": "respect-selection",
        "description": "Only customize for jobs the user explicitly selects",
        "constraint_type": "behavioral",
        "category": "user_control",
        "check": ""
      }
    ],
    "context": {},
    "required_capabilities": [],
    "input_schema": {},
    "output_schema": {},
    "version": "1.0.0",
    "parent_version": null,
    "evolution_reason": null,
    "created_at": "2026-02-13 18:23:18.911161",
    "updated_at": "2026-02-13 18:23:18.911164"
  },
  "required_tools": [
    "save_data",
    "append_data",
    "serve_file_to_user",
    "web_scrape",
    "gmail_create_draft",
    "pdf_read"
  ],
  "metadata": {
    "created_at": "2026-02-13T18:41:10.324531",
    "node_count": 4,
    "edge_count": 3
  }
}