Lead session follow-ups (same chat, same lead)

Question: "Rerun Hunt Forge and resynthesize with our current info."

One lead per chat

Each demo/API chat binds one CRM lead (contact_id + user-pasted fields). Follow-up hunts must reuse that bound record — not a legal entity name discovered during enrichment (e.g. user pasted "Hoops & Gears" but the graph found HG AutoTech LLC).

Do Don't
Reuse first enrich_lead_session lead (contact_id intact) Rebuild lead from summary_markdown / entities[]
Pass prompts: ["<user request verbatim>"] Pass refresh: true for partial retries
Let the server load prior_backfill from the chat Drop chat_session_id on follow-up

Tool

POST /api/v1/enrich/lead-session or MCP enrich_lead_session

{
  "lead": {
    "contact_id": 1737992709,
    "company": "Hoops & Gears and Clark",
    "domain": "hgautotech.com",
    "source_system": "demo_chat"
  },
  "chat_session_id": "ab62eb36cd134d70a58bc174d6b09185",
  "prompts": [
    "Rerun the Hunt Forge dig-deeper pass and resynthesize the summary with the current graph."
  ]
}

refresh vs follow-up

User intent Args
"From scratch" / "ignore cache" refresh: true + same bound lead
"Rerun Hunt Forge" / "resynthesize" / "dig deeper on X" prompts: [...] only — no refresh
Gap button from UI prompts: [<button text>]

refresh: true bypasses agent warm-seed and re-runs all hunt slots from zero. It does not mean "retry one model."

Hunt Forge / Swift / Deep (dig-deeper)

Middle-pass parallel slots (UI labels Hunt Forge, Hunt Swift, Hunt Deep) map to internal dig-deeper models. They are not research_model picker values.

There is no public research_model: data411-hunt-forge arg on enrich_lead_session.

Slot rerun (one model)

POST /api/v1/enrich/lead-session/slot-rerun or MCP rerun_hunt_slot

{
  "lead": { "contact_id": 1737992709, "company": "Hoops & Gears and Clark" },
  "chat_session_id": "ab62eb36cd134d70a58bc174d6b09185",
  "model_id": "data411-hunt-forge",
  "prior_backfill": { "...": "from last enrich_lead_session backfill" },
  "refresh_summary": true
}

model_id accepts public ids (data411-hunt-forge, data411-hunt-swift) or provider slugs (x-ai/grok-build-0.1). Runs one slot with refresh=true, delta-merges into prior_backfill, updates tool_trace.parallel_results for that model.

Hosted UI: Hunt controls panel on the last hunt turn → per slot row.

Prefer async jobs for slot reruns (hosted UI and headless agents):

{
  "route": "enrich/lead-session/slot-rerun",
  "payload": {
    "lead": { "contact_id": 1737992709, "company": "Hoops & Gears and Clark" },
    "chat_session_id": "ab62eb36cd134d70a58bc174d6b09185",
    "model_id": "data411-hunt-forge",
    "prior_backfill": { "...": "from last enrich_lead_session backfill" }
  }
}

Submit at POST /api/v1/enrich/jobs — default precharge $0.35 per slot (bill-on-match refund on no usable rows). Sync POST /api/v1/enrich/lead-session/slot-rerun remains for one-shot callers.

Batch slot rerun (pending + error)

POST /api/v1/enrich/jobs with route: enrich/lead-session/slots-rerun (no sync HTTP route).

Runs every pending or error parallel slot in one job: vendor hunts in parallel, trace upsert per slot, one delta synthesis at the end (not N synth passes).

{
  "route": "enrich/lead-session/slots-rerun",
  "payload": {
    "lead": { "contact_id": 1737992709, "company": "Hoops & Gears and Clark" },
    "chat_session_id": "ab62eb36cd134d70a58bc174d6b09185",
    "model_ids": ["data411-hunt-ground", "data411-crimson-span"],
    "prior_backfill": { "...": "current graph" },
    "progress_key": "job:slots:optional"
  },
  "bill_up_to_usd": 0.70
}
Field Notes
model_ids Public ids (data411-hunt-forge, …) or provider slugs; server rejects ids that are not pending or error in the latest trace
bill_up_to_usd Default N × $0.35 when omitted (N = non-empty model_ids count); override up to job max
progress_key Optional live board key for poll progress

Hosted UI: Run pending & failed (N) in hunt controls (post-hunt only; disabled while streaming or another slot job is busy).

Excludes done, cancelled, repetition, and running slots. Does not auto-resynthesize the summary — use Resynthesize if prose is stale after batch completes.

Resynthesize only

POST /api/v1/enrich/lead-session/synthesize-patch or MCP resynthesize_lead_graph

{
  "lead": { "contact_id": 1737992709, "company": "Hoops & Gears and Clark" },
  "chat_session_id": "ab62eb36cd134d70a58bc174d6b09185",
  "prior_backfill": { "...": "current graph" },
  "refresh_summary": true,
  "prompts": ["Emphasize owner phones and legal contacts in the summary."]
}

No vendor spend — LLM resynthesis from the existing graph only. Hosted UI: Resynthesize in the hunt controls panel.

Summary history (hosted UI)

Each time a follow-up session, slot rerun, batch slot rerun, or synthesize-patch changes summary_markdown, the server archives the prior version on backfill.summary_history[] (with graph_fingerprint for phones/contacts/emails).

Partial routes return the updated backfill including summary_history; no extra API.

Investigate unmerged finding

POST /api/v1/enrich/lead-session/finding-rerun or MCP investigate_unmerged_finding

{
  "lead": { "contact_id": 1737992709, "company": "Hoops & Gears and Clark" },
  "chat_session_id": "ab62eb36cd134d70a58bc174d6b09185",
  "candidate_id": "a1b2c3d4e5f6g7h8",
  "prior_backfill": { "unmerged_candidates": [{ "id": "a1b2c3d4e5f6g7h8", "label": "…" }] }
}

candidate_id is on backfill.unmerged_candidates[].id (stable SHA1 at merge time). Hosted UI: Investigate on each row in Findings not merged.

Agent behavior checklist

  1. Read the user's latest message into prompts if the model omitted it.
  2. Copy the first enrich_lead_session lead object for every follow-up.
  3. Never set lead.company to a discovered DBA/legal name unless the user pasted a new CRM export.
  4. Explain MULTI_LEAD_REFUSED as "same chat, different lead shape" — fix by reusing bound contact_id.