Releases: wboudiche/recruiter-agent
Release list
v0.1.0-alpha.1 — bundled SearXNG + polish
Post-alpha polish: free-by-default search, more honest UI feedback, and a handful of latent bug fixes surfaced by real use.
Added
- SearXNG is now bundled in compose.
docker compose up -dnow starts a self-hosted SearXNG container alongside the rest of the stack. Configure by settingSEARXNG_SECRET(recommended:openssl rand -hex 32) and pickingsearxngas the search provider in Settings → Sourcing — no third-party account, no card. Config lives insearxng/settings.yml. - Per-source result limit input in the Search tab. A small numeric control (1–30, default 5) next to the source chips lets you request more results per search.
- Paginated SearXNG provider. The provider now walks
pageno=1..5until it haslimitdistinct results (URL-deduped). Earlier it asked only for page 1, solimit_per_sourceabove ~10 was silently ignored.
Changed
- SMTP settings form now pre-fills from saved values. The non-secret SMTP fields round-trip through
GET /api/settings. The password remains write-only; leaving it blank on save now keeps the stored password instead of overwriting it. - "Needs paste" is now explicit. When a LinkedIn URL can't be auto-extracted, the kanban card label, the detail page stage, and an amber banner all say "needs paste" instead of the misleading "extracting".
- Gmail channel hidden from the Notify wizard. The backend route returns 501 for that channel, so the option is removed until it's wired up.
Fixed
PATCH /api/candidates/{id}no longer crashes withMissingGreenletwhen the candidate row has a non-nullupdated_at.- The e2e candidate-edit spec restores the original email in
test.afterEachand asserts the restore response, so a mid-test failure no longer leaks dirty state into subsequent runs. tests/unit/test_webpage_fetcher.pywas asserting on stale error copy from a previous rewrite. Updated to assert on the HTTP status embedded in the message instead.
Upgrade
cd ~/recruiter-agent
git pull
echo "SEARXNG_SECRET=$(openssl rand -hex 32)" >> .env
docker compose up -d --buildIf you were running a standalone searxng container outside compose, remove it first (docker rm -f searxng) — the bundled service replaces it.
Full commit log: v0.1.0-alpha...v0.1.0-alpha.1
v0.1.0-alpha — first public preview
Changelog
All notable user-facing changes are recorded here. The format is loosely based
on Keep a Changelog and the project
follows semantic versioning post-1.0.
v0.1.0-alpha — 2026-05-24
First public preview. Single-tenant, self-hosted recruiter assistant: add a
candidate from a URL, CV, paste, or search, and the system extracts a
structured profile, enriches it from public sources, scores it against your
job's weighted criteria, and lets you invite the candidate by email with a
calendar attachment.
Added
- Job + criteria. Create jobs with title, description, and 3–6 weighted
criteria. The LLM proposes initial criteria from the JD; weights can be
edited freely and are normalized to sum to 1.0. - Candidate sources. LinkedIn URL · GitHub URL · arbitrary web page ·
paste · PDF/DOCX upload · search-and-add across LinkedIn / GitHub / Web. - LinkedIn extraction tiers. Apify (commercial scrape) → Playwright with
a storedli_atcookie → GitHub-by-name fallback → manual paste prompt. - Pipeline. Async orchestrator runs
fetch → extract → enrich → score
with SSE-pushed stage updates to the kanban. - Scoring. LLM-driven per-criterion score (0–100) with a one-sentence
rationale and a weighted overall. - Validate → Notify wizard. Four-step flow (channel → slots → LLM-drafted
email → confirm) that sends an SMTP invite with an ICS attachment and
moves the candidate to Invited. - Candidate editing. Inline edit form for
full_name,email,phone,
headline,location,summary(the rest stay LLM-extracted). - Search-query suggester. "Suggest from JD" button in the Search tab
generates a source-aware query (LinkedIn-style keywords / GitHub
language:filters / quoted Web Boolean) from the job description. - Conversational agent. 11 registered tools, reachable from the Chat
panel on every candidate detail page. Reversible actions go through an
undo store. - Settings. LLM (provider, key, model id, monthly spend cap) · Sourcing
(search provider + key, GitHub PAT, Apify token + actor, LinkedIn cookie
or credentials) · Enrichment (Twitter, YouTube, Stack Exchange) ·
Notifications (SMTP host/port/user/password/from) · Profile (recruiter
name + email). - Structured rejection. Reject dialog captures a reason; banner on the
detail page until the candidate is Unrejected. - Eager-seed bootstrap. First start with
RECRUITER_DEFAULT_ACCOUNT_*
set creates the admin row before serving requests. - e2e tests. Playwright suite (auth · candidate edit · search-query
suggester) underrecruiter-frontend/e2e/runnable vianpm run e2e. - CI. GitHub Actions runs backend pytest (testcontainers postgres) and
frontend vitest + typecheck on every push and PR.
Security
- All secrets in the DB (LLM keys, search keys, GitHub PAT, Apify token,
LinkedIn cookie + password, SMTP password, OAuth tokens) are encrypted
with Fernet underRECRUITER_SETTINGS_KEY. - Session cookies are HttpOnly, SameSite=Strict, SHA-256-hashed at rest;
login is rate-limited (5/minute). - Compose refuses to start when required secrets are missing — no weak
fallback can ship. - CSRF Origin allowlist gates every mutating endpoint, configurable via
RECRUITER_ALLOWED_ORIGINS.
Known limitations
- Skills / experience / education / links are LLM-extracted only — not
editable in the UI yet. Edit via SQL or re-paste the source if extraction
got it wrong. - Gmail + Google Calendar channel is not wired — only SMTP is functional;
the Gmail option is hidden from the Notify wizard. - SerpAPI is the default search provider but requires an API key. Brave
and SearXNG are supported alternatives. - Playwright e2e is not yet in CI — runs locally against
docker compose up -d. CI runs backend pytest + frontend vitest + typecheck only. - No upgrade story between releases yet — schema migrations work, but
there is no formal pre-upgrade backup procedure documented beyond
"back upRECRUITER_SETTINGS_KEYand the postgres volume."
Recently fixed (since the previous unstable HEAD)
PATCH /api/candidates/{id}no longer crashes withMissingGreenletwhen
the candidate row had a non-nullupdated_at.- SMTP settings form now pre-fills host/port/user/from on open and keeps the
stored password on partial updates instead of silently overwriting it
with an empty string. - LinkedIn-URL applications stuck waiting on manual paste now surface an
explicit "needs paste" banner instead of an indefinite "extracting" label. - LLM gateway errors propagate the upstream response body (e.g. LINAGORA's
"Model not found") instead of just the HTTP status. - LLM tab exposes a per-provider Model input.
- README rewritten and shortened (~215 lines, down from ~666).
- First-run admin user is eager-seeded at backend startup rather than lazily
on first successful login.