life-my--midst--in

Hunter Protocol: Autonomous Job Search & Application

The Hunter Protocol is an autonomous job-searching agent that continuously monitors job boards, analyzes skill gaps, tailors resumes, and generates cover letters for job opportunities.

Architecture

Components

  1. SearchProvider (packages/core/src/search.ts)
    • Abstract interface for job board integration
    • Concrete implementations: SerperJobSearchProvider, MockJobSearchProvider
    • Production provider uses Serper (google.serper.dev) for live Google Jobs results
    • Set SERPER_API_KEY env var to activate; falls back to mock provider when absent
    • Handles job discovery, metadata extraction, and response mapping
  2. HunterAgent (apps/orchestrator/src/agents/hunter.ts)
    • Orchestrates the four core tools
    • Executes tasks asynchronously
    • Integrates with LLM for intelligent analysis
  3. JobHuntScheduler (apps/orchestrator/src/job-hunt-scheduler.ts)
    • Manages recurring job hunts for multiple profiles
    • Supports configurable frequencies (daily, weekly, monthly)
    • Tracks execution history
  4. API Routes (apps/api/src/routes/jobs.ts)
    • Job posting CRUD operations
    • Job application tracking
    • Webhook integration points

Core Tools

1. find_jobs(keywords, location)

Searches for job postings matching the given criteria.

Parameters:

Returns:

Example Task:

{
  "id": "hunt-123",
  "role": "hunter",
  "description": "Search for TypeScript jobs in remote",
  "payload": {
    "profileId": "user-456",
    "action": "find_jobs",
    "keywords": ["TypeScript", "Senior Engineer"],
    "location": "Remote"
  }
}

2. analyze_gap(job_description, profile_id)

Analyzes the skill gap between job requirements and the user’s profile.

Parameters:

Returns:

{
  "required": ["TypeScript", "React", "Node.js", "PostgreSQL"],
  "present": ["TypeScript", "React"],
  "missing": ["Node.js", "PostgreSQL"],
  "importance": "high"
}

Importance Levels:

3. tailor_resume(job_id, profile_id)

Selects the best identity mask and experience blocks to tailor the resume for a specific job.

Parameters:

Returns:

{
  "maskId": "mask-789",
  "maskName": "Architect",
  "highlightedExperiences": ["exp-1", "exp-2", "exp-3"],
  "tailoringSummary": "This resume emphasizes your Architect aspects, highlighting 3 relevant experiences."
}

Mask Selection Logic:

4. write_cover_letter(job_id, profile_id)

Generates a personalized cover letter for the job opportunity.

Parameters:

Returns:

{
  "applicationId": "app-999",
  "coverLetterLength": 2847,
  "preview": "Dear Hiring Manager, I am writing to express my strong interest in the Senior TypeScript Engineer position at TechCorp..."
}

Cover Letter Generation:

Configuration

Environment Variables

# Serper API for job search
SERPER_API_KEY=sk_...

# Job Hunt Scheduler
ORCH_JOB_HUNT_ENABLED=true
ORCH_API_BASE_URL=http://localhost:3001

# Agent Executor Mode (for cover letter/resume generation)
ORCH_AGENT_EXECUTOR=local  # or "oss", "stub", "none"

# LLM Configuration (for intelligent analysis)
LOCAL_LLM_API=ollama
LOCAL_LLM_URL=http://localhost:11434
LOCAL_LLM_MODEL=llama3.1:8b
LOCAL_LLM_MODEL_HUNTER=llama3.1:8b

Programmatic Configuration

Start Job Hunt for a Profile:

import { JobHuntScheduler } from "./job-hunt-scheduler";

const scheduler = new JobHuntScheduler(queue, store, runStore, {
  jobs: [
    {
      profileId: "user-123",
      keywords: ["TypeScript", "Senior Engineer"],
      location: "Remote",
      frequency: "weekly",
      autoApply: false,
      minSalary: 150000,
      maxSalary: 250000
    }
  ],
  apiBaseUrl: "http://localhost:3001"
});

scheduler.start();

Check Status:

const status = scheduler.getStatus("user-123");
console.log(status);
// {
//   profileId: "user-123",
//   keywords: [...],
//   frequency: "weekly",
//   lastRun: "2024-01-15T10:30:00Z",
//   isActive: true
// }

API Endpoints

Job Postings

Create Job Posting:

POST /jobs/postings
Content-Type: application/json

{
  "profileId": "user-123",
  "title": "Senior TypeScript Engineer",
  "company": "TechCorp",
  "location": "Remote",
  "salaryRange": "$150k - $200k",
  "descriptionMarkdown": "..."
}

List Job Postings:

GET /jobs/postings?profileId=user-123&status=active

Get Job Posting:

GET /jobs/postings/{id}

Job Applications

Create Application:

POST /jobs/applications
Content-Type: application/json

{
  "profileId": "user-123",
  "jobPostingId": "job-456",
  "status": "draft",
  "coverLetterMarkdown": "..."
}

Get Applications:

GET /jobs/applications?profileId=user-123&status=applied

Update Application Status:

PATCH /jobs/applications/{id}
Content-Type: application/json

{
  "status": "applied",
  "appliedAt": "2024-01-15T14:30:00Z"
}

Integration Examples

Example 1: Auto-Apply Workflow

// 1. Schedule job hunts
const scheduler = new JobHuntScheduler(queue, store, runStore, {
  jobs: [
    {
      profileId: "user-123",
      keywords: ["TypeScript", "React"],
      location: "Remote",
      frequency: "daily",
      autoApply: true
    }
  ]
});
scheduler.start();

// 2. Worker processes hunter tasks
// 3. Results stored in database
// 4. Manual review workflow (or auto-apply if enabled)

Example 2: Gap Analysis for Learning

// Analyze what you need to learn for a dream job
const gapTask: AgentTask = {
  id: "gap-123",
  role: "hunter",
  description: "Analyze skill gap for dream job",
  payload: {
    profileId: "user-123",
    action: "analyze_gap",
    jobDescription: "... full job posting text ..."
  }
};

const result = await hunterAgent.execute(gapTask);
// Use result.output.missing to identify learning priorities

Example 3: Resume Tailoring for Specific Role

// Tailor resume for a specific job opportunity
const tailorTask: AgentTask = {
  id: "tailor-456",
  role: "hunter",
  description: "Tailor resume for TechCorp role",
  payload: {
    profileId: "user-123",
    action: "tailor_resume",
    jobPostingId: "job-789"
  }
};

const result = await hunterAgent.execute(tailorTask);
// Use result.output.maskId and highlightedExperiences to render custom resume

Job Search Frequencies

Daily

Weekly

Monthly

Error Handling

The Hunter agent gracefully handles common failure modes:

Missing Profile:

{
  "status": "failed",
  "notes": "Could not fetch profile for gap analysis"
}

API Connection Issues:

{
  "status": "failed",
  "notes": "Job search failed: connection timeout"
}

Invalid Job Description:

{
  "status": "failed",
  "notes": "No job description provided for gap analysis"
}

Failures are tracked in run history but don’t stop the scheduler from continuing.

Data Models

JobPosting

interface JobPosting {
  id: string; // UUID
  profileId: string;
  title: string;
  company: string;
  descriptionMarkdown?: string;
  url?: string;
  salaryRange?: string;
  location?: string;
  vectors?: number[]; // For semantic search
  status: "active" | "closed" | "applied" | "ignored";
  createdAt: string; // ISO 8601
  updatedAt: string;
}

JobApplication

interface JobApplication {
  id: string; // UUID
  profileId: string;
  jobPostingId: string;
  status: "draft" | "applied" | "interviewing" | "offer" | "rejected" | "withdrawn";
  coverLetterMarkdown?: string;
  resumeSnapshotId?: string; // Reference to exported resume
  appliedAt?: string; // ISO 8601
  notes?: string; // User notes
  createdAt: string;
  updatedAt: string;
}

Testing

Unit Tests

# Test hunter agent
pnpm --filter @in-midst-my-life/orchestrator test -- hunter.test.ts

# Test job hunt scheduler
pnpm --filter @in-midst-my-life/orchestrator test -- job-hunt-scheduler.test.ts

# Test search providers
pnpm --filter @in-midst-my-life/core test -- search.test.ts

Integration Tests

# Full job hunt workflow (requires live DB)
INTEGRATION_POSTGRES_URL=postgresql://... pnpm integration

Future Enhancements

Phase 1 (Current)

Phase 2 (Planned)

Phase 3 (Future)

Troubleshooting

Ensure keywords array is non-empty in the task payload.

“Could not fetch profile for gap analysis”

Verify the profileId exists and is accessible. Check API connectivity.

“Job search failed: connection timeout”

Cover letters not generating

Performance Considerations

Optimization Tips:

Security

Contributing

To extend the Hunter Protocol:

  1. Add new job board: Implement JobSearchProvider interface
  2. Add new analysis tool: Extend HunterAgent with new action
  3. Customize cover letters: Override generateCoverLetterTemplate() method
  4. Add new mask logic: Extend tailorResume() selection algorithm