# Vinod Hermes Agent

Hermes is a lead-discovery control loop with a browser dashboard. It runs the Hermes Agent workflow, uses Google Maps and business websites as seed evidence, then rabbit-holes into public owner profiles to qualify owner-level contact leads.

## Local Setup

1. Create `.env` from `.env.example` and set `MINIMAX_API_KEY`.
2. Install the Python scraping tools:

```bash
bash scripts/bootstrap_tools.sh
```

3. Start the dashboard:

```bash
npm start
```

Open `http://localhost:8787`.

## Workflow

- Hermes Agent
  ↓
  Client Input
  ↓
  Maps Discovery
  ↓
  Website Enrichment
  ↓
  Lead Extraction
  ↓
  Quality Gate
  ↓
  Delivery

Self-evolving loop:

- Detect
  ↓
  Classify
  ↓
  Retry
  ↓
  Evolve
  ↓
  Resume

- Google Maps source: discovery uses `zohaibbashir/Google-Maps-Scrapper` by niche and city.
- Every run is locked to the United States. User locations are normalized to `USA`, retry queries keep the USA suffix, and explicit non-USA country names are rejected before scraping.
- Raw discovery CSV files are saved before any cleaning in `data/raw/`.
- Website enrichment uses `tools/hermes_scrapling_enrich.py` with Scrapling for public business pages and owner profile/article pages.
- `scripts/bootstrap_tools.sh` installs the requested public-profile support tools under `tools/vendor`: `joeyism/linkedin_scraper`, `speedyapply/JobSpy`, `josephlimtech/linkedin-profile-scraper-api`, `spinlud/py-linkedin-jobs-scraper`, and `D4Vinci/Scrapling`.
- The same bootstrap also prepares an optional OSINT owner-evidence toolkit under `tools/vendor`: `theHarvester`, `Photon`, `Maigret`, `Sherlock`, `Social Analyzer`, and `Reacher`. Hermes keeps these behind `src/adapters/ownerEvidenceTools.js` and `tools/hermes_owner_evidence_tools.py`; missing tools are reported but do not block a run.
- Optional owner-evidence tools are mapped into the workflow as follows: `theHarvester` and `Photon` support website/domain enrichment, `Maigret`, `Sherlock`, and `Social Analyzer` support username/profile expansion, and `Reacher` is reserved for owner email verification once its service is configured.
- `Owner agents` controls the owner rabbit-hole concurrency from 1 to 50. It is a swarm size for public owner searches and profile/article fetches, not a lead filter.
- Strong seed evidence can auto-escalate a lead to `HERMES_OWNER_AGENT_ESCALATION_SIZE` agents, defaulting to 50, when the website/enrichment shows owner terms, public profile trails, seed contact, or strong review signals.
- `Lead workers` controls how many discovered businesses the backend enriches and qualifies in parallel. The default is 3 and the maximum is 20 to avoid overwhelming public sources.
- For each business, Hermes deploys role-based owner agents for website leadership pages, LinkedIn personal profiles, public social profiles, local articles/interviews, directories, and direct owner-contact searches.
- When MiniMax is configured, Hermes asks it to design separate owner-agent work packets per business. Each work packet has its own hypothesis and public search queries; deterministic role agents fill the remaining swarm capacity.
- LinkedIn profile attempts record all five installed tools. Scrapling is the default public extraction path. When `HERMES_LINKEDIN_SESSION_JSON` or `HERMES_LINKEDIN_LI_AT` is set, Hermes invokes `tools/hermes_linkedin_scraper_profile.py` to run `joeyism/linkedin_scraper`. When `HERMES_LINKEDIN_LI_AT` is set, it also invokes `tools/hermes_linkedin_profile_api.mjs` for `josephlimtech/linkedin-profile-scraper-api`. Profile emails/phones are promoted only when the page/tool output can be tied back to the owner and business.
- When an owner LinkedIn profile is found, Hermes also runs `tools/hermes_jobspy_linkedin_jobs.py` through `speedyapply/JobSpy` for lightweight LinkedIn job/company corroboration. `spinlud/py-linkedin-jobs-scraper` is available through `tools/hermes_py_linkedin_jobs_scraper.py` and runs when `HERMES_ENABLE_PY_LINKEDIN_JOBS=1`.
- Business phone/email from Google Maps or the business website is seed evidence only.
- Owner rabbit-hole enrichment checks public about/team/contact pages, extracts owner candidates, follows public social profile links, and scrapes those owner-profile pages for owner email or owner phone.
- Final Google Maps leads qualify only when owner-level contact is found with recorded source evidence. Business-level contact alone is rejected.
- The dashboard tracks owner rabbit-hole attempts, owner profiles found, and evidence-backed owner contacts so large runs show owner-contact yield while the backend is still working.
- Default portfolio runs can expand up to `HERMES_PORTFOLIO_MAX_PLANNED_LOCATIONS` / `HERMES_MAX_PLANNED_LOCATIONS` USA markets, default `100`, so low owner-contact yield in the first cities does not stop the run after a small search plan.
- Hermes retries one variable at a time: USA query, US location split, fetch mode, timeout, page list, or selector strategy.
- The internal flow watchdog watches every persisted phase event, records alerts, and asks MiniMax for one-variable repair guidance when owner-contact yield stalls or failures appear.
- FlowWatchdog is trained with the repair playbook exposed at `/api/repair-playbook`: syntax checks, dashboard checks, health probes, agentic evals, scale audits, tool bootstrap, bounded node probes, dependency inventory, code patch plans, plugin roles, diagnostic drills, patch templates, execution policy, and guardrails. The dashboard Repairs tab shows the training kit and live MiniMax repair decisions, including selected tool, runbook, patch template, and validation command.
- Owner rabbit-hole searches have a hard per-business deadline controlled by `HERMES_OWNER_HUNT_TIMEOUT_MS`. If one owner profile search hangs, Hermes records a timeout, marks that business owner hunt exhausted, and continues the backlog.
- If dynamic enrichment repairs a weak lead, Hermes records the second owner rabbit-hole attempt separately and counts the owner contact only when that repair returns source-backed owner-contact evidence.
- Successful fixes are stored in `data/skills/hermes-skills.json`.
- Repeated failures are stored in `data/avoid/hermes-avoid.json`.
- Runs can resume from saved state and only rerun failed steps. If auto-recovery is disabled, stale queued/running runs are marked `interrupted` so the dashboard can resume them manually instead of showing a false active run.
- Discovered businesses that were saved before an interruption but not yet enriched are treated as pending work and processed before Hermes searches for more businesses.
- Run checkpoints are saved with per-run serialization and atomic file replacement, so parallel lead workers cannot corrupt or regress the saved run JSON during large batches.
- Exports include `leads.csv`, `leads.json`, and a client-facing `portfolio.md` report. CSV/JSON rows include business details, owner contact fields, owner-contact evidence, owner profiles, owner-agent swarm summary, profile-tool attempt summary, certification status, and evidence notes.

The dashboard runs the business-owner workflow: Google Maps discovery, business website enrichment, owner-profile rabbit-hole research, owner-contact qualification, CSV/JSON export, and client portfolio report.

## Scale Audit

Use the scale auditor to prove a run satisfies the upgraded owner-contact workflow instead of only producing business-level contacts:

```bash
npm run audit:scale -- \
  --niche "salons" \
  --cities "Austin, TX" \
  --target 100 \
  --lead-workers 3 \
  --owner-agents 50 \
  --timeout-ms 3600000 \
  --min-discovered 100 \
  --min-owner-hunts 100 \
  --min-owner-agents 100 \
  --min-owner-contacts 1 \
  --cancel-on-timeout
```

To audit an existing run:

```bash
npm run audit:scale -- --run-id RUN_ID --target 100 --min-discovered 100 --min-owner-hunts 100 --min-owner-agents 100 --min-owner-contacts 1
```

The command exits with code `0` only when the backend run reaches terminal status, discovers the requested scale, runs owner rabbit-hole agents, finds evidence-backed owner email or phone, and exposes owner columns in the final CSV.
Use `--cancel-on-timeout` or `--kill-on-timeout` when launching a long audit from the CLI and you do not want a timed-out audit to keep running in the backend.

## VPS Notes

The app intentionally uses plain Node.js and Python entry points. On a VPS, install Node 18+, Python 3.9-3.12, Chromium dependencies for Playwright/Scrapling, copy the project folder, set `.env`, run `bash scripts/bootstrap_tools.sh`, then run `npm start` under a supervisor such as systemd or pm2.
