diff --git a/README.md b/README.md index c4a80d9..8e249a9 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,12 @@ > **New to Leadbay?** [Create your account here](https://wow.leadbay.ai/?register=true) before installing the plugin. +## How Leadbay thinks (mental model for your agent) + +- **Inbox, not a database.** Each day your user logs back in, a fresh batch of leads is delivered. Batch size is paced by how many leads the user has actually acted on recently — some workflows produce a big stream of smaller prospects, others a narrow stream of bigger ones. Pulling more won't produce more; acting on leads does. +- **Two scoring layers.** Every lead ships with a basic `score` (firmographic — already decent, usually correlates with AI). Roughly the top 10 of each batch are also AI-qualified (targeted web research + qualification questions → `ai_agent_lead_score`). Leads below the top 10 aren't worse — the system is saving resources. The agent can request deeper qualification (`leadbay_bulk_qualify_leads`) or contact enrichment (`leadbay_enrich_titles`) on any lead that looks worth it. +- **Daily rhythm.** The agent works best as a daily check-in: pull fresh leads, skim the auto-qualified top, deepen 1-3 promising ones, propose outreach, then log what actually got sent via `leadbay_report_outreach`. If your host supports scheduling, set up a daily run. + ## Install ```bash diff --git a/packages/core/src/composite/account-status.ts b/packages/core/src/composite/account-status.ts index 67af4d3..0365555 100644 --- a/packages/core/src/composite/account-status.ts +++ b/packages/core/src/composite/account-status.ts @@ -6,7 +6,9 @@ export const accountStatus: Tool> = { description: "Show the user's account state — admin rights, language, last-active lens, current quota usage across " + "daily/weekly/monthly windows for llm_completion / ai_rescore / web_fetch resources, and whether the org's " + - "intelligence is mid-regeneration. " + + "intelligence is mid-regeneration. Quota windows also hint at the user's consumption pace: heavy recent " + + "activity (ai_rescore / web_fetch near their window limits) is a signal that Leadbay will deliver a larger " + + "fresh batch next time the user logs back in, since batch size is paced by real consumption. " + "When to use: at the start of a session to know what the agent can/can't do, or after a 429 to explain to " + "the user which resource window was exhausted and when it resets. " + "When NOT to use: as a pre-flight gate before bulk ops — operations themselves return 429; this tool is " + diff --git a/packages/core/src/composite/bulk-qualify-leads.ts b/packages/core/src/composite/bulk-qualify-leads.ts index ca8373b..4ab9548 100644 --- a/packages/core/src/composite/bulk-qualify-leads.ts +++ b/packages/core/src/composite/bulk-qualify-leads.ts @@ -38,7 +38,11 @@ export const bulkQualifyLeads: Tool = { "until the answers are populated or a budget is exhausted. Already-qualified leads (those with a non-null " + "ai_agent_lead_score) are silently no-ops on the backend, so this composite paginates past them to find " + "fresh candidates. On 429 mid-fanout, stops launching but keeps polling already-launched leads. " + - "When to use: when the user wants more qualified leads than what's currently shown. " + + "Context: Leadbay auto-qualifies roughly the top 10 of each daily batch. Leads below the top ~10 are NOT " + + "worse — the system is saving resources. This tool is how the agent spends more resources to go deeper on " + + "promising-looking leads the user hasn't had time to surface yet. " + + "When to use: when the user wants more qualified leads than what's currently shown, or when a lead looks " + + "promising in leadbay_pull_leads but has an empty qualification_summary. " + "When NOT to use: to qualify a single specific lead — that's leadbay_qualify_lead (granular, advanced).", inputSchema: { type: "object", diff --git a/packages/core/src/composite/enrich-titles.ts b/packages/core/src/composite/enrich-titles.ts index ac86326..f79c5b7 100644 --- a/packages/core/src/composite/enrich-titles.ts +++ b/packages/core/src/composite/enrich-titles.ts @@ -21,14 +21,17 @@ const DEFAULT_CANDIDATE_COUNT = 25; export const enrichTitles: Tool = { name: "leadbay_enrich_titles", description: - "Order contact enrichments by job title across many leads. Two modes: " + + "Order contact enrichments by job title across many leads. Contacts are NOT returned by default with a lead " + + "(Leadbay keeps enrichment out-of-band to control cost); the agent requests them on demand via this tool when " + + "it's ready to actually reach out. Two modes: " + "(A) NO titles param — returns the available titles + Leadbay's title_suggestions + auto_included_titles " + "+ a count of enrichable contacts, so the agent can ask the user which titles to enrich. " + "(B) titles given — calls preview, then launches if there's anything enrichable. " + "On 429 returns {status:'quota_exceeded'} cleanly. Selection lifecycle is wrapped in a try/finally so the " + "user's selection is left clean even on error. " + - "When to use: as the agent's go-to enrichment entry point. " + - "When NOT to use: to enrich a single contact — that's leadbay_enrich_contacts (granular).", + "When to use: as the agent's go-to enrichment entry point, immediately before proposing outreach. " + + "When NOT to use: to enrich a single contact — that's leadbay_enrich_contacts (granular). " + + "Speculatively, before the user has committed to outreaching — enrichment spends credits.", inputSchema: { type: "object", properties: { diff --git a/packages/core/src/composite/pull-leads.ts b/packages/core/src/composite/pull-leads.ts index 536165f..12e9f05 100644 --- a/packages/core/src/composite/pull-leads.ts +++ b/packages/core/src/composite/pull-leads.ts @@ -50,10 +50,16 @@ function summarise(responses: AiAgentResponse[]): QualificationSummary { export const pullLeads: Tool = { name: "leadbay_pull_leads", description: - "Pull up new leads from the user's last-active lens (the canonical 'show me prospects to work on' tool). " + + "Pull up new leads from the user's last-active lens — the canonical 'show me today's prospects' tool. " + + "Leadbay works like an inbox: each time the user logs back in, a fresh batch is delivered, paced by how " + + "many leads they've actually acted on recently. Pulling more won't produce more; user outreach/skips/saves does. " + "Each returned lead carries a one-line qualification_summary built from leadbay_ai_agent_responses, plus " + "the rich tags / scores / recommended_contact_title / engagement counters / in-flight flags from the lead summary. " + - "When to use: as the agent's default opening move when the user wants to see leads. " + + "Roughly the top 10 of the batch come pre-qualified (populated qualification_summary + ai_agent_lead_score); " + + "leads below the top ~10 carry only the basic firmographic `score` — not worse, just resource-saved by the system. " + + "Call leadbay_bulk_qualify_leads to deepen any of them on demand. " + + "When to use: as the agent's default opening move when the user wants to see leads, or as a daily check-in " + + "for what's new today. " + "When NOT to use: when the user has named a specific lens — pass lensId to override the auto-resolution. " + "Replaces the older leadbay_find_prospects (which is removed in v0.2.0).", inputSchema: { diff --git a/packages/core/src/composite/research-lead.ts b/packages/core/src/composite/research-lead.ts index ec42f2d..d4588ae 100644 --- a/packages/core/src/composite/research-lead.ts +++ b/packages/core/src/composite/research-lead.ts @@ -64,6 +64,10 @@ export const researchLead: Tool = { "qualification answers (the agent's knowledge-base food), the structured web-research signals (with hot " + "flags + sources), the enriched contacts, and the recent notes/epilogue/prospecting activity in one call. " + "Order is deliberate: qualification first, then signals, then firmographics, then contacts, then engagement. " + + "Scoring has two layers: the basic `score` (firmographic, always present, already decent) and the AI " + + "qualification layer (`ai_agent_lead_score` + per-question answers + web_fetch signals). The AI layer is " + + "pre-populated for roughly the top 10 of each daily batch, and on-demand (via leadbay_bulk_qualify_leads) " + + "for anything below that. Combine both layers when judging a lead. " + "When to use: when picking up a single lead from leadbay_pull_leads to decide whether to act on it. " + "When NOT to use: across many leads at once — that's leadbay_pull_leads' job. " + "(This composite supersedes the lower-level leadbay_get_lead_profile in agent flow; the granular tool stays " + diff --git a/packages/leadclaw/CHANGELOG.md b/packages/leadclaw/CHANGELOG.md index b3bba24..b00b863 100644 --- a/packages/leadclaw/CHANGELOG.md +++ b/packages/leadclaw/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog — @leadbay/leadclaw +## 0.2.2 — 2026-04-21 + +Docs-only release. Shares the `@leadbay/mcp@0.2.2` mental-model copy updates since both packages consume the same `@leadbay/core` tool descriptions. + +- Plugin manifest description rewritten from "Leadbay lead discovery, qualification, and contact enrichment for AI agents" to a framing that names the inbox model, the two scoring layers, and on-demand deepening. +- Composite tool descriptions (`pull_leads`, `research_lead`, `bulk_qualify_leads`, `enrich_titles`, `account_status`) now teach the agent that Leadbay delivers a fresh batch per user login, paced by recent consumption; that roughly the top 10 are pre-AI-qualified while the rest are resource-saved (not worse); and that contacts are enriched on demand when the agent is ready to reach out. +- No new tools, no schema changes. Version kept in sync with `@leadbay/mcp@0.2.2`. + ## 0.2.1 — 2026-04-21 No functional changes. First release shipping with signed sigstore provenance (repo is now public, which unblocks npm's provenance gate). Version kept in sync with `@leadbay/mcp@0.2.1`. diff --git a/packages/leadclaw/openclaw.plugin.json b/packages/leadclaw/openclaw.plugin.json index 3e6d8a4..c249718 100644 --- a/packages/leadclaw/openclaw.plugin.json +++ b/packages/leadclaw/openclaw.plugin.json @@ -1,8 +1,8 @@ { "id": "leadclaw", "name": "LeadClaw", - "description": "Leadbay lead discovery, qualification, and contact enrichment for AI agents", - "version": "0.2.1", + "description": "Leadbay for AI agents: a daily sales-lead inbox with firmographic + AI qualification layers, plus on-demand deeper qualification and contact enrichment. Each login delivers a fresh batch of leads paced by the user's recent consumption; the agent skims, deepens promising ones, and proposes outreach.", + "version": "0.2.2", "contracts": { "tools": [ "leadbay_login", diff --git a/packages/leadclaw/package.json b/packages/leadclaw/package.json index 8ccd278..da429c7 100644 --- a/packages/leadclaw/package.json +++ b/packages/leadclaw/package.json @@ -1,6 +1,6 @@ { "name": "@leadbay/leadclaw", - "version": "0.2.1", + "version": "0.2.2", "description": "OpenClaw plugin for Leadbay — AI lead discovery, qualification, and enrichment", "type": "module", "main": "dist/index.js", diff --git a/packages/mcp/CHANGELOG.md b/packages/mcp/CHANGELOG.md index b131395..30a3212 100644 --- a/packages/mcp/CHANGELOG.md +++ b/packages/mcp/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog — @leadbay/mcp +## 0.2.2 — 2026-04-21 + +Docs-only release. Teach the agent the Leadbay mental model — agents were calling `pull_leads` like a generic query rather than as a daily inbox, missing that leads past the top ~10 still exist and can be deepened on demand. + +- `SERVER_INSTRUCTIONS` gains three new paragraphs: "How Leadbay works" (inbox + consumption-based pacing), "Two scoring layers" (basic `score` vs AI-qualified top ~10 with `ai_agent_lead_score`), and "Suggested rhythm" (daily check-in + host-agnostic scheduling hint). +- `leadbay_pull_leads`, `leadbay_research_lead`, `leadbay_bulk_qualify_leads`, `leadbay_enrich_titles`, and `leadbay_account_status` descriptions updated to reinforce the same model so the agent sees it at both top level and per-tool. +- New non-regression test asserts the inbox/pace/scoring/daily language stays in `SERVER_INSTRUCTIONS`. +- No schema changes, no tool-shape changes — wire-compatible with 0.2.1. + ## 0.2.1 — 2026-04-21 Docs-only release. diff --git a/packages/mcp/package.json b/packages/mcp/package.json index 5d05b16..4ed1a13 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@leadbay/mcp", - "version": "0.2.1", + "version": "0.2.2", "description": "Model Context Protocol (MCP) server for Leadbay — AI lead discovery, qualification, and enrichment for Claude Desktop, Cursor, and Claude Code.", "type": "module", "bin": { diff --git a/packages/mcp/src/server.ts b/packages/mcp/src/server.ts index e311ac8..8f05a69 100644 --- a/packages/mcp/src/server.ts +++ b/packages/mcp/src/server.ts @@ -13,18 +13,32 @@ import { type ToolLogger, } from "@leadbay/core"; -// Lead-paragraph mandate is the first thing the agent reads — followed by -// three sentences, then per-tool descriptions carry the runbook detail. +// Verification mandate stays first (test asserts report_outreach appears in +// the first 200 chars). Then the mental-model paragraphs teach the agent +// what Leadbay is (an inbox, not a DB), the two scoring layers, the +// start-here sequence, and a recommended daily rhythm. export const SERVER_INSTRUCTIONS = "After every email, call, message, or meeting with a lead's contact, you MUST call leadbay_report_outreach " + "with verification={source, ref} (gmail_message_id from the Gmail send, calendar_event_id from a booking, " + "or user_confirmed=''). Skipping or fabricating verification poisons the " + "human team's pipeline.\n\n" + + "How Leadbay works (mental model): Leadbay is a sales inbox, not a queryable database. Each day the user " + + "logs back in, a fresh batch of leads is delivered. Batch size is paced by how many leads the user has " + + "actually acted on recently — some workflows produce a big stream of smaller prospects, others a narrow " + + "stream of bigger ones. Pulling more won't produce more; the user acting on leads (outreach, skips, saves) does.\n\n" + + "Two scoring layers: every lead has a basic `score` (firmographic — already decent, usually correlates " + + "with AI). Roughly the top 10 of each batch are also AI-qualified (targeted web research + qualification " + + "questions → `ai_agent_lead_score`, surfaced as `qualification_summary` on leadbay_pull_leads). Leads past " + + "the top ~10 are not worse — the system is saving resources. Call leadbay_bulk_qualify_leads for deeper " + + "qualification or leadbay_enrich_titles for contacts on any lead that looks worth it.\n\n" + "Start with leadbay_account_status to see the user's state, then leadbay_pull_leads to surface fresh leads. " + "Use leadbay_research_lead to dig into one lead deeply (qualification answers, signals, contacts). " + "When the user wants more leads, narrower audience, refined criteria, or contact enrichment, use the matching " + "composite tool (bulk_qualify_leads / adjust_audience / refine_prompt / enrich_titles) — they hide lens " + - "permissions, region routing, polling, and selection state from you."; + "permissions, region routing, polling, and selection state from you.\n\n" + + "Suggested rhythm: a healthy agent pattern is a daily check-in — pull fresh leads, skim the auto-qualified " + + "top, deepen 1-3 promising ones, propose outreach to the user, then leadbay_report_outreach on what actually " + + "got sent. If your host supports scheduling, offer to set up a daily run."; interface BuildServerOptions { includeAdvanced?: boolean; diff --git a/packages/mcp/test/server.test.ts b/packages/mcp/test/server.test.ts index 21f7bed..9cec034 100644 --- a/packages/mcp/test/server.test.ts +++ b/packages/mcp/test/server.test.ts @@ -266,6 +266,22 @@ describe("server.instructions — LLM guidance string", () => { expect(SERVER_INSTRUCTIONS).toMatch(/leadbay_research_lead/); expect(SERVER_INSTRUCTIONS).toMatch(/leadbay_account_status/); }); + + it("server instructions teach the inbox + pace + scoring mental model", async () => { + const { SERVER_INSTRUCTIONS } = await import("../src/server.js"); + // Inbox framing — the daily-cadence anchor. + expect(SERVER_INSTRUCTIONS).toMatch(/inbox/i); + // Consumption-based pacing. + expect(SERVER_INSTRUCTIONS).toMatch(/paced|pace/i); + // Two scoring layers with concrete field names. + expect(SERVER_INSTRUCTIONS).toMatch(/two scoring layers/i); + expect(SERVER_INSTRUCTIONS).toMatch(/ai_agent_lead_score/); + // Daily-rhythm recommendation. + expect(SERVER_INSTRUCTIONS).toMatch(/daily|each day/i); + // Points to the on-demand deepening tools. + expect(SERVER_INSTRUCTIONS).toMatch(/leadbay_bulk_qualify_leads/); + expect(SERVER_INSTRUCTIONS).toMatch(/leadbay_enrich_titles/); + }); }); describe("resolveClientFromEnv — region auto-probe", () => {