Skip to main content

The research agent

The agent works out who the people in the CRM are. It is an eve app, it is its own deployment, and it owns every piece of intelligence in the product. The API reports that something happened; the agent decides what it means.

What it's made of​

🔧 20 authored toolsread_crm_history, search_crm, identify_contact, research_person, enrich_company, record_fact, schedule_recheck, and more.
📄 Prose skillsevidence.md, identity-matching.md, data-boundaries.md, writing-a-brief.md — instructions the agent reads, versioned like code.
⏱️ One scheduledispatch decides nothing. It leases what is due and starts a session per row.
📦 A sandboxbash, grep, glob and a /workspace, with deny-all egress.

It runs itself​

The work queue is a database table. claimDue leases rows with FOR UPDATE SKIP LOCKED, so two dispatchers take disjoint work and a run that dies frees its row when the lease expires. Anything that looks like "every N minutes, the oldest ten contacts" belongs in a task's dueAt, not in a cron expression.

When the agent wants another look at somebody it calls schedule_recheck and says why — and the reason is shown to the rep, because an agent that cannot say why it will be back in fourteen days does not have a reason, it has a default.

Nothing about it is request-response. Close the browser and it keeps going, on its own schedule, against its own budget.

Budget​

Every session gets a research budget. Every vendor call charges it. Running out is a normal ending — not an error. The budget keeps a single contact from consuming an unbounded number of lookups.

Two dispatch lanes​

The dispatcher drains the queue in two independent lanes:

LaneKindsHow it runsPer tick
Visiblelogos, portraitsDirectly — no model, no session60, six at a time
Researcheverything elseOne eve session per row12

Neither kind in the visible lane has anything to decide — a logo is a domain in, one call out, write. Routing it through a model session would buy a context window to make no decisions with. Lanes are why a queue of thirty "identify this contact" rows never delays a single logo.

Task priority​

One ordered list decides what a rep sees resolve first:

PriorityTaskWhy
900logo + real nameOn every row of the list, before a rep opens anything. One call, seconds.
800portraitThe face.
500workspace profileWho we are — every later session opens with it.
300requestedA rep pressed Research.
200meetingA meeting is coming.
100identifyA new contact.
50sweepThe sign-in backfill.
40company briefThe written brief.
0recheckCome back in ninety days.

Starting now instead of on the minute​

POST /internal/crm/dispatch drains both lanes on demand, and the API calls it (fire-and-forget) after writing any task row. Add a company and its logo appears; add a contact and the research starts. The cron is a backstop rather than the only door. Overlapping pokes collapse into a single trailing drain so a sync creating forty contacts doesn't launch forty sessions at once.

The framework is the source of truth​

The complete eve documentation ships inside the installed package and matches the version exactly. Guessing at the API is expensive in a specific way: it typechecks, builds, and then behaves differently from what was assumed.

Hrida.ai is proprietary software of Zlabs Innovation. See the license for terms. © 2026 Zlabs Innovation.