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.
packages/core/src/search.ts)
SerperJobSearchProvider, MockJobSearchProviderSERPER_API_KEY env var to activate; falls back to mock provider when absentapps/orchestrator/src/agents/hunter.ts)
apps/orchestrator/src/job-hunt-scheduler.ts)
apps/api/src/routes/jobs.ts)
Searches for job postings matching the given criteria.
Parameters:
keywords (string[]): Search terms (e.g., [“TypeScript”, “React”, “Engineer”])location (string, optional): Job location (e.g., “Remote”, “San Francisco, CA”)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"
}
}
Analyzes the skill gap between job requirements and the user’s profile.
Parameters:
jobDescription (string): The full job posting textprofileId (string): User profile IDReturns:
{
"required": ["TypeScript", "React", "Node.js", "PostgreSQL"],
"present": ["TypeScript", "React"],
"missing": ["Node.js", "PostgreSQL"],
"importance": "high"
}
Importance Levels:
critical: More than 5 missing critical skillshigh: 3-5 missing skillsmedium: 1-2 missing skillslow: No missing skillsSelects the best identity mask and experience blocks to tailor the resume for a specific job.
Parameters:
jobPostingId (string): The job posting IDprofileId (string): User profile IDReturns:
{
"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:
Generates a personalized cover letter for the job opportunity.
Parameters:
jobPostingId (string): The job posting IDprofileId (string): User profile IDReturns:
{
"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:
# 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
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
// }
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}
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"
}
// 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)
// 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
// 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
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.
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;
}
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;
}
# 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
# Full job hunt workflow (requires live DB)
INTEGRATION_POSTGRES_URL=postgresql://... pnpm integration
Ensure keywords array is non-empty in the task payload.
Verify the profileId exists and is accessible. Check API connectivity.
ORCH_AGENT_EXECUTOR configurationOptimization Tips:
MockJobSearchProvider for testingSERPER_API_KEY in .env (never commit)To extend the Hunter Protocol:
JobSearchProvider interfaceHunterAgent with new actiongenerateCoverLetterTemplate() methodtailorResume() selection algorithm