@leadbay/mcp 0.18.2 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/bin.js +280 -270
- package/dist/http-server.js +280 -270
- package/dist/installer-electron.js +1 -1
- package/dist/installer-gui.js +1 -1
- package/package.json +1 -1
package/dist/http-server.js
CHANGED
|
@@ -33,10 +33,10 @@ import {
|
|
|
33
33
|
var leadbay_daily_check_in = `
|
|
34
34
|
## MEMORY
|
|
35
35
|
|
|
36
|
-
Before responding, glance at any \`_meta.agent_memory.summary\` returned by tool calls earlier in this session and reflect its top signals in your reasoning ("Filtering by your stated preference for healthcare"). After any material new
|
|
36
|
+
Before responding, glance at any \`_meta.agent_memory.summary\` returned by tool calls earlier in this session and reflect its top signals in your reasoning ("Filtering by your stated preference for healthcare"). After any material new signal from the user this conversation (sector, region, deal size, communication style, qualification rule, explicit retraction, or recurrence / scheduling preference such as "I do this every day" or "remind me every morning"), call \`leadbay_agent_memory_capture\` to persist it: \`source:"user_stated"\` if literal, \`source:"inferred"\` with confidence <=6 if inferred.
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
Run the Leadbay daily check-in for me. Treat this prompt the same way for any equivalent ask focused on NEW leads from the Discover wishlist: "get me leadbay leads", "best NEW leads to prospect today", "what's new today", "show me my batch", "let's prospect". For follow-up phrasings ("what should I follow up on", "leads I've already worked", "before my trip"), this is the wrong prompt \u2014 route to \`leadbay_followup_check_in\` instead. If the user's intent is ambiguous ("what should I work on?"), ASK once before picking an entry point.
|
|
39
|
+
Run the Leadbay daily check-in for me. Treat this prompt the same way for any equivalent ask focused on NEW leads from the Discover wishlist: "get me leadbay leads", "best NEW leads to prospect today", "what's new today", "show me my batch", "let's prospect", "run my morning check-in", "my daily routine", "I do this every day", "every morning". For follow-up phrasings ("what should I follow up on", "leads I've already worked", "before my trip"), this is the wrong prompt \u2014 route to \`leadbay_followup_check_in\` instead. **Recurrence language ("I do this every day", "every morning", "my routine") always means this prompt \u2014 it is a daily batch check-in, not a follow-up.** If the user's intent is ambiguous ("what should I work on?"), ASK once before picking an entry point.
|
|
40
40
|
|
|
41
41
|
# Resilience rules for Leadbay long-running tools
|
|
42
42
|
|
|
@@ -162,13 +162,66 @@ If the batch returns fewer than 10 qualified leads, top it up: call \`leadbay_bu
|
|
|
162
162
|
|
|
163
163
|
# PHASE 4 \u2014 DEEP DIVE (every promising lead)
|
|
164
164
|
|
|
165
|
+
**Skip Phase 4 if the user's request is primarily to view the batch** (e.g., "show me today's leads", "run my morning check-in", "what's in my inbox") \u2014 proceed directly to NEXT STEPS. Run Phase 4 when the user explicitly asks to research leads, names a specific company, or says "and then research" / "deep dive" / "tell me more about".
|
|
166
|
+
|
|
165
167
|
Call \`leadbay_research_lead_by_id\` on **every** lead from your top 10 that the user might realistically prospect today (filter out clearly weak fits if any). Don't pick just one. **Call it sequentially** \u2014 one at a time, or batches of at most 3 in parallel. Do not fire 10 in parallel \u2014 it triggers transport backpressure that surfaces as \`"Tool permission stream closed"\` errors (see Rule 3 above). If a call fails, retry that single lead once; if the retry also fails, note the lead id and continue. Report Phase 4 results even if 1\u20132 leads were unresearchable.
|
|
166
168
|
|
|
167
169
|
For each researched lead surface:
|
|
168
170
|
- what makes it promising (1\u20132 sentences citing signals from the research)
|
|
169
171
|
- the **recommended contacts** the research returns \u2014 name, title, why they're the right starting point
|
|
170
172
|
|
|
171
|
-
|
|
173
|
+
Contact enrichment is offered in the NEXT STEPS widget below \u2014 do NOT emit a separate prose question here. The widget handles the enrichment offer as one of the selectable options. If the user selects enrichment, call \`leadbay_enrich_contacts({leadId, contactId})\` ONCE PER CONTACT \u2014 the tool takes a single leadId + contactId, never a list. (For bulk title/seniority enrichment across many leads at once, use \`leadbay_enrich_titles({leadIds: [...]})\` instead.) This consumes enrichment quota.
|
|
174
|
+
|
|
175
|
+
# NEXT STEPS
|
|
176
|
+
|
|
177
|
+
**Sequential request gate:** If the user's original message contained the literal phrase "and then" (e.g., "show me X and then do Y"), and all stated actions have been completed this turn, skip the NEXT STEPS widget entirely and emit STOP directly. The user stated their full plan; they do not need a "what next?" prompt.
|
|
178
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, go directly to STOP without any widget.
|
|
179
|
+
- Do NOT skip for: plain single-action requests ("show me today's leads"), recurring requests ("I do this every day"), or multi-step workflows the user didn't pre-specify.
|
|
180
|
+
|
|
181
|
+
**REQUIRED OPTIONS \u2014 triggers and position rules:**
|
|
182
|
+
- **Recurring language** ("every day", "every morning", "I do this every", "remind me", "automate this", "recurring"): add "Schedule 'Daily prospecting check-in' as a recurring task" and place it **first**.
|
|
183
|
+
- **\u22655 leads returned**: add "Build an interactive lead triage board for this batch" and place it **first** (or second if the scheduling offer above also applies).
|
|
184
|
+
|
|
185
|
+
## NEXT STEPS \u2014 after rendering the pull_leads table
|
|
186
|
+
|
|
187
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
188
|
+
|
|
189
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
190
|
+
|
|
191
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
192
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
193
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
194
|
+
|
|
195
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
196
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
197
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
198
|
+
|
|
199
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
Pick 2\u20133 items below based on what was actually observed in the response. The table is the source of truth for which moves are valid.
|
|
206
|
+
|
|
207
|
+
| Observation | Suggest | Calls |
|
|
208
|
+
|------------------------------------------------------------|--------------------------------------------------------------|--------------------------------------------------------|
|
|
209
|
+
| \u2265 5 leads returned (any batch) | "Build an interactive lead triage board for this batch" | emit antArtifact from data in hand (do NOT re-call leadbay_pull_leads) |
|
|
210
|
+
| \`has_more == true\` | "Pull the next page (page N+1 of M)" | leadbay_pull_leads(page = current + 1, lensId = pinned)|
|
|
211
|
+
| \u2265 3 rows have \`qualification_summary.answered == 0\` | "Deepen AI qualification on the rows without \u2756 caps" | leadbay_bulk_qualify_leads(leadIds=[\u2026]) |
|
|
212
|
+
| User points at a single row | "Research [Company] in depth" | leadbay_research_lead_by_id(leadId) |
|
|
213
|
+
| User only has a name (no leadId in context) | "Look up [Company] by name" | leadbay_research_lead_by_name_fuzzy(companyName) |
|
|
214
|
+
| Top row has phone AND email | "Prepare an outreach for [Contact] \u2014 call + email" | leadbay_prepare_outreach(leadId) |
|
|
215
|
+
| Top row has email but no phone | "Draft an outreach email for [Contact]" | leadbay_prepare_outreach(leadId) |
|
|
216
|
+
| Top row has phone but no email | "Show [Contact]'s call details + a 60-second opener" | leadbay_prepare_outreach(leadId) |
|
|
217
|
+
| Top row has contacts but no phone/email | "Order contact enrichment to surface email/phone first" | leadbay_enrich_titles(...) or leadbay_prepare_outreach(leadId, enrich:true) |
|
|
218
|
+
| \`computing_scores == true\` or \`computing_wishlist == true\` | "Scores are still being computed \u2014 re-pull in ~30s" | leadbay_pull_leads (retry with same lensId) |
|
|
219
|
+
| User wants a narrower / wider audience | "Adjust the lens filters (sector / size)" | leadbay_adjust_audience(...) |
|
|
220
|
+
| Phase 4 research was run (\`research_lead_by_id\` called) AND top contacts lack direct email/phone | "Enrich contacts on [Lead1], [Lead2] to get direct emails and phone numbers" | leadbay_enrich_contacts(leadId, contactId) \u2014 ONE call per contact (the tool takes a single leadId + contactId, never a list) |
|
|
221
|
+
If nothing in the menu applies cleanly, suggest only "pull next page" and "research a specific lead in depth" \u2014 never invent a tool that doesn't exist.
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
**Final ordering check (do this before rendering):** Recurring offer \u2192 option 1; triage board \u2192 option 1 (or 2 if scheduling is also required). Swap if needed.
|
|
172
225
|
|
|
173
226
|
# GATE \u2014 STOP
|
|
174
227
|
|
|
@@ -415,7 +468,7 @@ Done. The map is the surface; the drafts are the action; the campaign is the per
|
|
|
415
468
|
var leadbay_prospecting_overview = `
|
|
416
469
|
## MEMORY
|
|
417
470
|
|
|
418
|
-
Before responding, glance at any \`_meta.agent_memory.summary\` returned by tool calls earlier in this session and reflect its top signals in your reasoning ("Filtering by your stated preference for healthcare"). After any material new
|
|
471
|
+
Before responding, glance at any \`_meta.agent_memory.summary\` returned by tool calls earlier in this session and reflect its top signals in your reasoning ("Filtering by your stated preference for healthcare"). After any material new signal from the user this conversation (sector, region, deal size, communication style, qualification rule, explicit retraction, or recurrence / scheduling preference such as "I do this every day" or "remind me every morning"), call \`leadbay_agent_memory_capture\` to persist it: \`source:"user_stated"\` if literal, \`source:"inferred"\` with confidence <=6 if inferred.
|
|
419
472
|
|
|
420
473
|
|
|
421
474
|
# Leadbay Prospecting \u2014 Orientation
|
|
@@ -689,7 +742,7 @@ If the response status is \`applied\`, tell me Leadbay is regenerating intellige
|
|
|
689
742
|
var leadbay_research_a_domain = `
|
|
690
743
|
## MEMORY
|
|
691
744
|
|
|
692
|
-
Before responding, glance at any \`_meta.agent_memory.summary\` returned by tool calls earlier in this session and reflect its top signals in your reasoning ("Filtering by your stated preference for healthcare"). After any material new
|
|
745
|
+
Before responding, glance at any \`_meta.agent_memory.summary\` returned by tool calls earlier in this session and reflect its top signals in your reasoning ("Filtering by your stated preference for healthcare"). After any material new signal from the user this conversation (sector, region, deal size, communication style, qualification rule, explicit retraction, or recurrence / scheduling preference such as "I do this every day" or "remind me every morning"), call \`leadbay_agent_memory_capture\` to persist it: \`source:"user_stated"\` if literal, \`source:"inferred"\` with confidence <=6 if inferred.
|
|
693
746
|
|
|
694
747
|
|
|
695
748
|
IRON LAW \u2014 NO FABRICATION. Every lead id, contact email, custom field id, mapping decision, and tool argument must trace to a value you read from the file the user attached or to an output from a leadbay_* tool call in this session. Do not invent values. Do not "fill in" a missing leadId with a name match. Do not synthesize a CRM id from a guess. If a value is missing, leave the field blank and say so.
|
|
@@ -830,7 +883,7 @@ Then ask me ONCE: "Which of these should we drop?" If I name leads to drop, excl
|
|
|
830
883
|
|
|
831
884
|
# PHASE 3 \u2014 DECIDE THE CAMPAIGN SHAPE
|
|
832
885
|
|
|
833
|
-
If I provided a \`rep_split\` ("one campaign per rep: John gets Tulsa, Sarah gets OKC"), partition the validated leads accordingly. If I didn't, ask ONCE: "Create one campaign for the whole batch, or split per rep / region / sector?" \u2014 surface 2-4 options via \`ask_user_input_v0\` when available, else as a bulleted list.
|
|
886
|
+
If I provided a \`rep_split\` ("one campaign per rep: John gets Tulsa, Sarah gets OKC"), partition the validated leads accordingly. If I didn't, ask ONCE: "Create one campaign for the whole batch, or split per rep / region / sector?" \u2014 surface 2-4 options via your host's choice widget (\`ask_user_input_v0\` or \`AskUserQuestion\`) when available, else as a bulleted list.
|
|
834
887
|
|
|
835
888
|
For each campaign-shape decision, derive a name. Templates:
|
|
836
889
|
- Whole batch: \`"<lens-name> \u2013 <YYYY-MM-DD>"\`
|
|
@@ -847,7 +900,7 @@ Once the campaigns are created, surface this caveat in plain prose:
|
|
|
847
900
|
|
|
848
901
|
> Campaign visibility is currently scoped to the user who CREATED the campaign \u2014 the reps won't see these in their own MCP \`leadbay_list_campaigns\` calls. They CAN see them in the web UI at app.leadbay.ai \u2192 Campaigns. Cross-user MCP visibility would need backend work; flag this as a #3630 US3 product gap if your reps work primarily through MCP.
|
|
849
902
|
|
|
850
|
-
End with a NEXT STEPS chip via \`ask_user_input_v0
|
|
903
|
+
End with a NEXT STEPS chip via your host's choice widget (\`ask_user_input_v0\` or \`AskUserQuestion\`): "View progression on one of these now?" \u2192 routes to \`leadbay_campaign_progression\`.
|
|
851
904
|
|
|
852
905
|
# PHASE 6 \u2014 STOP
|
|
853
906
|
|
|
@@ -863,7 +916,7 @@ If the prompt's body and the tool's RENDERING appear to conflict, the tool's REN
|
|
|
863
916
|
|
|
864
917
|
# PHASE 0 \u2014 PICK THE CAMPAIGN
|
|
865
918
|
|
|
866
|
-
If I gave you a name or id, resolve it. Otherwise call \`leadbay_list_campaigns()\` and surface the active campaigns as a \`single_select\` via \`ask_user_input_v0\` (cap at 4 \u2014 sort by \`updated_at\` desc, archived hidden):
|
|
919
|
+
If I gave you a name or id, resolve it. Otherwise call \`leadbay_list_campaigns()\` and surface the active campaigns as a \`single_select\` via your host's choice widget (\`ask_user_input_v0\` or \`AskUserQuestion\`) (cap at 4 \u2014 sort by \`updated_at\` desc, archived hidden):
|
|
867
920
|
|
|
868
921
|
> Which campaign do you want to work?
|
|
869
922
|
> - <Name 1> \xB7 <N leads> \xB7 last touched <date>
|
|
@@ -896,7 +949,7 @@ Call \`leadbay_campaign_call_sheet({campaign_id})\`. The response carries \`summ
|
|
|
896
949
|
\u{1F4CB} <total_leads> leads \xB7 \u{1F4DE} <leads_with_phone> with a phone \xB7 \u2709 <leads_with_email> with an email \xB7 \u{1F5FA} <leads_with_coords> with coords \xB7 \u{1F534} <leads_without_contacts> need enrichment \xB7 \u2705 <leads_already_contacted> already touched
|
|
897
950
|
\`\`\`
|
|
898
951
|
|
|
899
|
-
**Then PROPOSE the right modes via \`ask_user_input_v0
|
|
952
|
+
**Then PROPOSE the right modes via your host's choice widget (\`ask_user_input_v0\` or \`AskUserQuestion\`)** (2-4 options, sorted by what makes the most sense for THIS campaign's data):
|
|
900
953
|
|
|
901
954
|
- "\u{1F4DE} Start calling now" \u2014 IF \`ready_for_calling\`. Top option when phones are there.
|
|
902
955
|
- "\u2709 Email session instead" \u2014 IF \`ready_for_emailing\` AND \`email_ratio > phone_ratio\`. Don't surface this when calling is more obvious.
|
|
@@ -958,23 +1011,9 @@ Optional: offer to review the \`leadbay_campaign_progression\` for the same camp
|
|
|
958
1011
|
- If the user dictates an outcome that doesn't cleanly map to one of the four epilogue values, ASK ONCE before guessing.
|
|
959
1012
|
`;
|
|
960
1013
|
var PROMPT_META = {
|
|
961
|
-
leadbay_daily_check_in: { "name": "leadbay_daily_check_in", "short_description": `
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
on "leadbay leads", "best NEW leads", "what's new today", "show me the
|
|
965
|
-
day's batch", "let's prospect". Do NOT trigger on follow-up phrasings
|
|
966
|
-
("what should I follow up on", "before my trip") \u2014 those go to
|
|
967
|
-
\`leadbay_followup_check_in\`.
|
|
968
|
-
`, "arguments": [], "expected_calls": ["leadbay_account_status", "leadbay_pull_leads", "leadbay_research_lead_by_id", "leadbay_bulk_qualify_leads", "leadbay_enrich_contacts"], "failure_modes": ["Calls leadbay_report_outreach without explicit user authorization", "Surfaces fewer than 10 leads when more are available, or fails to top up via leadbay_qualify_top_n when the batch is short", `Replaces the canonical pull_leads table layout with prose per row (the per-tool RENDERING block is the structural contract; "Today's nudges" goes above it, not in place of it)`, "Skips the nudge paragraph entirely \u2014 the table alone is fine but adding the nudge is the value-add", "Skips deep research on promising leads (Phase 4) \u2014 the agent must call leadbay_research_lead_by_id on each, not just one", "Triggers contact enrichment without asking the user first (it consumes quota)", "Skips the STOP byproduct and proposes next actions on its own", 'Fires 10 parallel leadbay_research_lead_by_id calls and treats "stream closed" errors as terminal \u2014 must serialize and retry singletons', "Re-pulls leadbay_pull_leads without passing the captured lensId, allowing a backend lens shift to discard the Phase 2 batch", 'Treats a "Request timed out" from leadbay_bulk_qualify_leads as terminal instead of retrying with wait_for_completion:false + qualify_status polling', 'Triggers on a follow-up query (e.g., "leads I should follow up with") that should have routed to `leadbay_followup_check_in` \u2014 the two entry points are different data sources (Discover wishlist vs Monitor view) per \xA71.6'] },
|
|
969
|
-
leadbay_extend_my_lens: { "name": "leadbay_extend_my_lens", "short_description": "Add more leads to the current lens on demand \u2014 for users whose appetite\nexceeds the standard daily fill. The agent picks seeds silently from\nwhat's already on the lens, fires the extra refill, and surfaces the\nqueue confirmation. The user never reviews the seed list.\n", "arguments": [{ "name": "extra_count", "description": "How many extra leads to add. Optional. Omit to use the backend default.", "required": false }], "expected_calls": ["leadbay_account_status", "leadbay_seed_candidates", "leadbay_extend_lens", "leadbay_pull_leads"], "failure_modes": ["Surfaces the seed candidate list to the user instead of picking silently \u2014 the user asked for MORE LEADS, not a candidate review meeting", "Skips the seeded path and calls `leadbay_extend_lens` with no `seed_lead_ids`, losing the bias signal the recommender needs", "On 429, silently retries instead of surfacing the three options (smaller / wait / upgrade) via `ask_user_input_v0`", "Forgets to pre-check `LENS_EXTRA_REFILL` quota in `leadbay_account_status` and burns a wasted API call", "Skips the post-queue pull-leads suggestion, so the user doesn't see what just got added"] },
|
|
970
|
-
leadbay_followup_check_in: { "name": "leadbay_followup_check_in", "short_description": `Run the canonical follow-up check-in: surface KNOWN leads from the
|
|
971
|
-
Monitor view that need re-engagement today, ranked by AI urgency,
|
|
972
|
-
with the canonical pull_followups table layout. Trigger when the
|
|
973
|
-
user asks "follow up", "already known leads", "leads I haven't
|
|
974
|
-
contacted", "leads in [city]", "before my trip", "this week",
|
|
975
|
-
"this month", "what's overdue", "who should I re-engage", or
|
|
976
|
-
anything that implies pre-existing pipeline context.
|
|
977
|
-
`, "arguments": [], "expected_calls": ["leadbay_pull_followups", "leadbay_research_lead_by_id", "leadbay_prepare_outreach"], "failure_modes": ["Calls leadbay_pull_leads (the Discover entry point) instead of leadbay_pull_followups \u2014 these are different data sources; the Discover queue does NOT contain Monitor's known-but-cold pipeline", 'Iterates pages of leadbay_pull_leads filtering by engagement_count to "fake" a follow-up view (a real bug observed in 0.9.0 \u2014 the right move is to call pull_followups directly)', "Replaces the canonical pull_followups table layout with prose per row (the per-tool RENDERING block is the structural contract; commentary belongs above or below)", 'Skips the cross-mode pivot offer at the end ("Want to see NEW leads from your wishlist instead?" routes to leadbay_pull_leads)'] },
|
|
1014
|
+
leadbay_daily_check_in: { "name": "leadbay_daily_check_in", "short_description": 'Morning DISCOVERY workflow \u2014 new leads from the lens wishlist. Trigger\non "show me leads", "what\'s new today", "let\'s prospect", "run my check-in",\n"my morning check-in", "I do this every day", "every morning". Recurrence\nlanguage always means this prompt. Do NOT trigger on follow-up phrasings\n("follow up", "before my trip") \u2014 those go to `leadbay_followup_check_in`.\n', "arguments": [], "expected_calls": ["leadbay_account_status", "leadbay_pull_leads", "leadbay_research_lead_by_id", "leadbay_bulk_qualify_leads", "leadbay_enrich_contacts"], "failure_modes": ["Calls leadbay_report_outreach without explicit user authorization", "Surfaces fewer than 10 leads when more are available, or fails to top up via leadbay_qualify_top_n when the batch is short", `Replaces the canonical pull_leads table layout with prose per row (the per-tool RENDERING block is the structural contract; "Today's nudges" goes above it, not in place of it)`, "Skips the nudge paragraph entirely \u2014 the table alone is fine but adding the nudge is the value-add", `Skips deep research on promising leads (Phase 4) \u2014 the agent must call leadbay_research_lead_by_id on each when the user's intent is to research specific leads; Phase 4 is intentionally skipped for batch-view requests ("show me today's leads", "run my morning check-in") per the Phase 4 skip gate`, "Triggers contact enrichment without asking the user first (it consumes quota)", "Skips the STOP byproduct and proposes next actions on its own", 'Fires 10 parallel leadbay_research_lead_by_id calls and treats "stream closed" errors as terminal \u2014 must serialize and retry singletons', "Re-pulls leadbay_pull_leads without passing the captured lensId, allowing a backend lens shift to discard the Phase 2 batch", 'Treats a "Request timed out" from leadbay_bulk_qualify_leads as terminal instead of retrying with wait_for_completion:false + qualify_status polling', 'Triggers on a follow-up query (e.g., "leads I should follow up with") that should have routed to `leadbay_followup_check_in` \u2014 the two entry points are different data sources (Discover wishlist vs Monitor view) per \xA71.6'] },
|
|
1015
|
+
leadbay_extend_my_lens: { "name": "leadbay_extend_my_lens", "short_description": "Add more leads to the current lens on demand \u2014 for users whose appetite\nexceeds the standard daily fill. The agent picks seeds silently from\nwhat's already on the lens, fires the extra refill, and surfaces the\nqueue confirmation. The user never reviews the seed list.\n", "arguments": [{ "name": "extra_count", "description": "How many extra leads to add. Optional. Omit to use the backend default.", "required": false }], "expected_calls": ["leadbay_account_status", "leadbay_seed_candidates", "leadbay_extend_lens", "leadbay_pull_leads"], "failure_modes": ["Surfaces the seed candidate list to the user instead of picking silently \u2014 the user asked for MORE LEADS, not a candidate review meeting", "Skips the seeded path and calls `leadbay_extend_lens` with no `seed_lead_ids`, losing the bias signal the recommender needs", "On 429, silently retries instead of surfacing the three options (smaller / wait / upgrade) via your host's choice widget (`ask_user_input_v0` or `AskUserQuestion`)", "Forgets to pre-check `LENS_EXTRA_REFILL` quota in `leadbay_account_status` and burns a wasted API call", "Skips the post-queue pull-leads suggestion, so the user doesn't see what just got added"] },
|
|
1016
|
+
leadbay_followup_check_in: { "name": "leadbay_followup_check_in", "short_description": 'Follow-up check-in: surface KNOWN leads from the Monitor view needing\nre-engagement. Trigger on "follow up", "already known leads", "what\'s\noverdue", "before my trip", "who should I re-engage". Do NOT trigger on\n"show me today\'s leads", "my morning check-in", "run my check-in",\n"I do this every day", "every morning" \u2014 those go to\n`leadbay_daily_check_in`.\n', "arguments": [], "expected_calls": ["leadbay_pull_followups", "leadbay_research_lead_by_id", "leadbay_prepare_outreach"], "failure_modes": ["Calls leadbay_pull_leads (the Discover entry point) instead of leadbay_pull_followups \u2014 these are different data sources; the Discover queue does NOT contain Monitor's known-but-cold pipeline", 'Iterates pages of leadbay_pull_leads filtering by engagement_count to "fake" a follow-up view (a real bug observed in 0.9.0 \u2014 the right move is to call pull_followups directly)', "Replaces the canonical pull_followups table layout with prose per row (the per-tool RENDERING block is the structural contract; commentary belongs above or below)", 'Skips the cross-mode pivot offer at the end ("Want to see NEW leads from your wishlist instead?" routes to leadbay_pull_leads)'] },
|
|
978
1017
|
leadbay_import_file: { "name": "leadbay_import_file", "short_description": "Import a user-supplied CSV/file into Leadbay through five phases with\nevidence gates \u2014 scan, derive, resolve identities, preserve & commit,\nthen optionally qualify and report. The job is to maximize how many\nrows the Leadbay system actually ingests and matches.\n", "arguments": [{ "name": "file", "description": "Path or user-visible name of the CSV/file to import. If omitted, use the file the user attached or referenced.", "required": false }, { "name": "instruction", "description": 'Additional user goal, e.g. "then qualify the leads", "preserve owner phone as a custom field", or "only import restaurants in Manhattan".', "required": false }], "expected_calls": ["leadbay_resolve_import_rows", "leadbay_list_mappable_fields", "leadbay_create_custom_field", "leadbay_import_leads", "leadbay_import_and_qualify", "leadbay_add_note", "leadbay_import_status"], "failure_modes": ["Picks LEADBAY_ID from score alone, name-only, fuzzy-name-only, root-domain-only, brand-only, postcode-only, or city-only evidence", "Drops meaningful business notes or CRM record links instead of preserving them as custom fields or lead notes", "Treats a consumer mailbox domain (gmail.com, hotmail.com, ...) as the company domain", "Skips deriving company_domain from a business email when no website column exists (this kills match rate)", "Skips the COLUMN PRESERVATION PLAN byproduct before importing", "Skips the DECISION LOG byproduct before writing LEADBAY_ID", "Returns the imported records WITHOUT writing LEADBAY_ID values back into the user's file (leaves the user no audit trail of what matched)", "Fabricates leadIds, contact emails, or mapping IDs not present in the file or a tool response"] },
|
|
979
1018
|
leadbay_log_outreach: { "name": "leadbay_log_outreach", "short_description": "Log outreach (an email I sent, a call I made, a meeting I had) on a\nspecific lead. Captures verification so the SDR pipeline trusts the entry.\n", "arguments": [{ "name": "lead_id", "description": "The lead UUID. Get it from leadbay_pull_leads or leadbay_research_lead_by_id.", "required": true }, { "name": "summary", "description": "1-2 sentences describing what I did (e.g. 'Sent intro email to CTO citing recent Hornsea contract').", "required": true }], "expected_calls": ["leadbay_report_outreach"], "failure_modes": ["Calls leadbay_report_outreach without first collecting a verification source", "Fabricates a gmail_message_id or calendar_event_id (the human team treats verification as canonical)", "Records outreach to a different lead_id than the one the user supplied", "Skips the dry_run step when the user is unsure what would be sent"] },
|
|
980
1019
|
leadbay_plan_tour_in_city: { "name": "leadbay_plan_tour_in_city", "short_description": "Plan a field sales tour: in one flow, surface follow-ups + fresh\nDiscover leads in the target city via `leadbay_tour_plan`, render\nto a map, draft in-area outreach via `leadbay_prepare_outreach`,\nand optionally persist the selected accounts as a named campaign\nvia `leadbay_create_campaign`. Closes #3630 US1 end-to-end.\n", "arguments": [{ "name": "city", "description": "City or region the user is visiting (e.g. 'Limoges', 'Bay Area'). Used as the geo filter for both Monitor and Discover lookups.", "required": true }, { "name": "date", "description": "When the visit is (e.g. 'May 24', 'next Thursday'). Surfaced in the outreach drafts as 'I'll be in <city> on <date>'.", "required": false }], "expected_calls": ["leadbay_tour_plan", "leadbay_research_lead_by_id", "leadbay_prepare_outreach", "leadbay_create_campaign"], "failure_modes": ["Calls leadbay_followups_map (Monitor-only) instead of leadbay_tour_plan \u2014 loses the Discover (fresh-lead) half that the user explicitly asked for", "Calls leadbay_pull_leads then drops the geo filter \u2014 returns the lens-wide wishlist instead of city-relevant fresh leads", 'Skips the campaign-persist step ("would you like to save these as a tour?") \u2014 leaves the rep with a one-shot map but no follow-up artifact', "Creates a campaign WITHOUT asking the user first \u2014 the persist step is high-intent; offer it, don't assume", "Fabricates lead_ids when seeding the campaign instead of using the ids returned by tour_plan"] },
|
|
@@ -993,9 +1032,9 @@ should I follow up on" to "I'll send via lemlist".
|
|
|
993
1032
|
};
|
|
994
1033
|
var PROMPT_CATALOG_HEADER = `This server exposes the following workflow prompts via \`prompts/list\` and \`prompts/get\`. Some MCP clients render them as slash commands; if your client does not, you (the agent) should invoke them directly via \`prompts/get\` when the user's request matches one of the triggers described below.`;
|
|
995
1034
|
var PROMPT_CATALOG_BULLETS = {
|
|
996
|
-
leadbay_daily_check_in: `- \`leadbay_daily_check_in\`:
|
|
1035
|
+
leadbay_daily_check_in: `- \`leadbay_daily_check_in\`: Morning DISCOVERY workflow \u2014 new leads from the lens wishlist. Trigger on "show me leads", "what's new today", "let's prospect", "run my check-in", "my morning check-in", "I do this every day", "every morning". Recurrence language always means this prompt. Do NOT trigger on follow-up phrasings ("follow up", "before my trip") \u2014 those go to \`leadbay_followup_check_in\`.`,
|
|
997
1036
|
leadbay_extend_my_lens: `- \`leadbay_extend_my_lens\` (optional args: extra_count): Add more leads to the current lens on demand \u2014 for users whose appetite exceeds the standard daily fill. The agent picks seeds silently from what's already on the lens, fires the extra refill, and surfaces the queue confirmation. The user never reviews the seed list.`,
|
|
998
|
-
leadbay_followup_check_in: `- \`leadbay_followup_check_in\`:
|
|
1037
|
+
leadbay_followup_check_in: `- \`leadbay_followup_check_in\`: Follow-up check-in: surface KNOWN leads from the Monitor view needing re-engagement. Trigger on "follow up", "already known leads", "what's overdue", "before my trip", "who should I re-engage". Do NOT trigger on "show me today's leads", "my morning check-in", "run my check-in", "I do this every day", "every morning" \u2014 those go to \`leadbay_daily_check_in\`.`,
|
|
999
1038
|
leadbay_import_file: `- \`leadbay_import_file\` (optional args: file, instruction): Import a user-supplied CSV/file into Leadbay through five phases with evidence gates \u2014 scan, derive, resolve identities, preserve & commit, then optionally qualify and report. The job is to maximize how many rows the Leadbay system actually ingests and matches.`,
|
|
1000
1039
|
leadbay_log_outreach: `- \`leadbay_log_outreach\` (required args: lead_id, summary): Log outreach (an email I sent, a call I made, a meeting I had) on a specific lead. Captures verification so the SDR pipeline trusts the entry.`,
|
|
1001
1040
|
leadbay_plan_tour_in_city: `- \`leadbay_plan_tour_in_city\` (required args: city; optional args: date): Plan a field sales tour: in one flow, surface follow-ups + fresh Discover leads in the target city via \`leadbay_tour_plan\`, render to a map, draft in-area outreach via \`leadbay_prepare_outreach\`, and optionally persist the selected accounts as a named campaign via \`leadbay_create_campaign\`. Closes #3630 US1 end-to-end.`,
|
|
@@ -6631,29 +6670,23 @@ Do not enumerate the affected leads \u2014 that's the job of \`leadbay_pull_lead
|
|
|
6631
6670
|
|
|
6632
6671
|
---
|
|
6633
6672
|
|
|
6673
|
+
---
|
|
6674
|
+
|
|
6634
6675
|
## NEXT STEPS \u2014 after kicking off bulk qualification
|
|
6635
6676
|
|
|
6636
|
-
**
|
|
6677
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
6637
6678
|
|
|
6638
|
-
|
|
6679
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
6639
6680
|
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
question: "What next?",
|
|
6644
|
-
type: "single_select",
|
|
6645
|
-
options: [
|
|
6646
|
-
"<Suggest column from row 1>",
|
|
6647
|
-
"<Suggest column from row 2>",
|
|
6648
|
-
"<Suggest column from row 3>"
|
|
6649
|
-
]
|
|
6650
|
-
}]
|
|
6651
|
-
})
|
|
6652
|
-
\`\`\`
|
|
6681
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
6682
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
6683
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
6653
6684
|
|
|
6654
|
-
|
|
6685
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
6686
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
6687
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
6655
6688
|
|
|
6656
|
-
|
|
6689
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
6657
6690
|
|
|
6658
6691
|
---
|
|
6659
6692
|
|
|
@@ -6785,7 +6818,7 @@ Modern chat hosts (Claude, ChatGPT) expose first-party widgets the agent can rou
|
|
|
6785
6818
|
|---|---|---|
|
|
6786
6819
|
| \`places_map_display_v0\` (Claude) | Result has \u22652 leads with \`location.city\` set, and the user's intent is geographic / "in person" / travel | \`{name: lead.company_name, address: "<city>, <country>", place_id: lead.location.place_id ?? omit, notes: <one-sentence pitch>}\` per location |
|
|
6787
6820
|
| \`message_compose_v1\` (Claude) | You're about to draft outreach (email / message / call opener) | \`{kind: "email", summary_title, variants: [{label, body, subject}]}\` \u2014 2\u20133 variants, labels describe STRATEGY ("Push for alignment", "Reference the M&A signal"), not tone ("Friendly", "Formal") |
|
|
6788
|
-
| \`ask_user_input_v0\` (Claude) | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen |
|
|
6821
|
+
| \`ask_user_input_v0\` (Claude chat / ChatGPT) **or** \`AskUserQuestion\` (Claude cowork / Claude Code) \u2014 whichever is in your tool set; their schemas differ, match the one you have | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen | Per-tool schema in the server instructions + NEXT STEPS routing block. Max 3 questions. |
|
|
6789
6822
|
|
|
6790
6823
|
ChatGPT exposes the same routing pattern via \`_meta.openai/outputTemplate\`. We don't ship any custom widgets ourselves \u2014 this gate is exclusively about routing into the host's first-party widgets when the data shape fits.
|
|
6791
6824
|
|
|
@@ -6877,6 +6910,8 @@ WHEN TO USE: after \`leadbay_list_campaigns\` (or when the user named a specific
|
|
|
6877
6910
|
WHEN NOT TO USE: for cross-campaign pulse (use \`leadbay_list_campaigns\`); to drill into a lead's full timeline (use \`leadbay_get_lead_activities\` or \`leadbay_research_lead_by_id\`); to log outreach (\`leadbay_report_outreach\`).
|
|
6878
6911
|
|
|
6879
6912
|
**Response**: \`{items, pagination, summary, _meta}\`. Use the \`summary\` for the one-line headline; use \`items\` for the per-lead table.
|
|
6913
|
+
|
|
6914
|
+
---
|
|
6880
6915
|
`;
|
|
6881
6916
|
var leadbay_clear_selection = `Clear the user's transient selection.
|
|
6882
6917
|
|
|
@@ -7084,7 +7119,7 @@ Modern chat hosts (Claude, ChatGPT) expose first-party widgets the agent can rou
|
|
|
7084
7119
|
|---|---|---|
|
|
7085
7120
|
| \`places_map_display_v0\` (Claude) | Result has \u22652 leads with \`location.city\` set, and the user's intent is geographic / "in person" / travel | \`{name: lead.company_name, address: "<city>, <country>", place_id: lead.location.place_id ?? omit, notes: <one-sentence pitch>}\` per location |
|
|
7086
7121
|
| \`message_compose_v1\` (Claude) | You're about to draft outreach (email / message / call opener) | \`{kind: "email", summary_title, variants: [{label, body, subject}]}\` \u2014 2\u20133 variants, labels describe STRATEGY ("Push for alignment", "Reference the M&A signal"), not tone ("Friendly", "Formal") |
|
|
7087
|
-
| \`ask_user_input_v0\` (Claude) | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen |
|
|
7122
|
+
| \`ask_user_input_v0\` (Claude chat / ChatGPT) **or** \`AskUserQuestion\` (Claude cowork / Claude Code) \u2014 whichever is in your tool set; their schemas differ, match the one you have | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen | Per-tool schema in the server instructions + NEXT STEPS routing block. Max 3 questions. |
|
|
7088
7123
|
|
|
7089
7124
|
ChatGPT exposes the same routing pattern via \`_meta.openai/outputTemplate\`. We don't ship any custom widgets ourselves \u2014 this gate is exclusively about routing into the host's first-party widgets when the data shape fits.
|
|
7090
7125
|
|
|
@@ -7121,7 +7156,7 @@ Examples that should NOT invoke this tool (sound similar, route elsewhere):
|
|
|
7121
7156
|
|
|
7122
7157
|
\`queued\` \u2192 \u2705 "Queued <N> extra leads on lens <id>. Pull in ~30s." Do NOT
|
|
7123
7158
|
list \`accepted_seeds\`; they're internal.
|
|
7124
|
-
\`quota_exceeded\` \u2192 render three options via \`ask_user_input_v0\` (smaller
|
|
7159
|
+
\`quota_exceeded\` \u2192 render three options via your host's choice widget (\`ask_user_input_v0\` or \`AskUserQuestion\`) (smaller
|
|
7125
7160
|
count / wait until reset / upgrade).
|
|
7126
7161
|
\`refresh_in_progress\` \u2192 "lens is filling, retry in a minute".
|
|
7127
7162
|
\`no_valid_seeds\` \u2192 silently re-call \`leadbay_seed_candidates\`, retry once.
|
|
@@ -7139,7 +7174,7 @@ Queue an additive extra-refill on a lens \u2014 more leads on the same criteria,
|
|
|
7139
7174
|
**Status envelope (translated from raw API errors so the agent routes on \`status\`).**
|
|
7140
7175
|
|
|
7141
7176
|
- \`status: "queued"\` \u2014 fill is queued. \`accepted_seeds\` lists IDs that passed validation. NEXT STEP: call \`leadbay_pull_leads\` in ~30s.
|
|
7142
|
-
- \`status: "quota_exceeded"\` \u2014 daily LENS_EXTRA_REFILL hit. Response carries \`quota: {used_today, resets_at}\` + a \`message\` to surface. **Render three options via \`ask_user_input_v0
|
|
7177
|
+
- \`status: "quota_exceeded"\` \u2014 daily LENS_EXTRA_REFILL hit. Response carries \`quota: {used_today, resets_at}\` + a \`message\` to surface. **Render three options via your host's choice widget (\`ask_user_input_v0\` or \`AskUserQuestion\`)**: (1) smaller \`extra_count\`, (2) wait until \`resets_at\`, (3) upgrade plan (TIER1=150, TIER2=1000). Do NOT silently retry.
|
|
7143
7178
|
- \`status: "refresh_in_progress"\` \u2014 a refresh or extra-refill is already running. Tell the user to wait and call \`leadbay_pull_leads\` in ~30s.
|
|
7144
7179
|
- \`status: "no_valid_seeds"\` \u2014 seeds went stale. Silently re-call \`leadbay_seed_candidates\` and retry once; only surface to the user if the second attempt also fails.
|
|
7145
7180
|
|
|
@@ -7154,27 +7189,19 @@ This tool MUTATES state. The caller (agent or human-in-the-loop) is responsible
|
|
|
7154
7189
|
|
|
7155
7190
|
## NEXT STEPS \u2014 after \`leadbay_extend_lens\`
|
|
7156
7191
|
|
|
7157
|
-
**
|
|
7192
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
7158
7193
|
|
|
7159
|
-
|
|
7194
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
7160
7195
|
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
question: "What next?",
|
|
7165
|
-
type: "single_select",
|
|
7166
|
-
options: [
|
|
7167
|
-
"<Suggest column from row 1>",
|
|
7168
|
-
"<Suggest column from row 2>",
|
|
7169
|
-
"<Suggest column from row 3>"
|
|
7170
|
-
]
|
|
7171
|
-
}]
|
|
7172
|
-
})
|
|
7173
|
-
\`\`\`
|
|
7196
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
7197
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
7198
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
7174
7199
|
|
|
7175
|
-
|
|
7200
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
7201
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
7202
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
7176
7203
|
|
|
7177
|
-
|
|
7204
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
7178
7205
|
|
|
7179
7206
|
---
|
|
7180
7207
|
|
|
@@ -7317,7 +7344,7 @@ Never link a person's name to the company's LinkedIn page (and vice versa) \u201
|
|
|
7317
7344
|
|
|
7318
7345
|
Open with **one short intro sentence** in chat ("Five lead visits across NYC for your trip next week \u2014 three in Midtown, plus Long Island and one in NJ.") and then invoke the widget, then the chat-side list above. **No markdown table.**
|
|
7319
7346
|
|
|
7320
|
-
**After the widget renders, end the turn with the NEXT STEPS surface** \u2014 not with a prose question. See "GATE \u2014 PREFER BUILT-IN HOST WIDGETS" below: surface 2\u20134 mutually-exclusive moves via \`ask_user_input_v0\` if the host exposes it, else as a short bulleted list. "Want me to plot these on a map or jump to outreach for Atlas?" is exactly the prose pattern to AVOID \u2014 it's a \`single_select\` with two options.
|
|
7347
|
+
**After the widget renders, end the turn with the NEXT STEPS surface** \u2014 not with a prose question. See "GATE \u2014 PREFER BUILT-IN HOST WIDGETS" below: surface 2\u20134 mutually-exclusive moves via your host's choice widget (\`ask_user_input_v0\` or \`AskUserQuestion\`) if the host exposes it, else as a short bulleted list. "Want me to plot these on a map or jump to outreach for Atlas?" is exactly the prose pattern to AVOID \u2014 it's a \`single_select\` with two options.
|
|
7321
7348
|
|
|
7322
7349
|
## RENDER \u2014 fallback for hosts without \`places_map_display_v0\`
|
|
7323
7350
|
|
|
@@ -7348,7 +7375,7 @@ Modern chat hosts (Claude, ChatGPT) expose first-party widgets the agent can rou
|
|
|
7348
7375
|
|---|---|---|
|
|
7349
7376
|
| \`places_map_display_v0\` (Claude) | Result has \u22652 leads with \`location.city\` set, and the user's intent is geographic / "in person" / travel | \`{name: lead.company_name, address: "<city>, <country>", place_id: lead.location.place_id ?? omit, notes: <one-sentence pitch>}\` per location |
|
|
7350
7377
|
| \`message_compose_v1\` (Claude) | You're about to draft outreach (email / message / call opener) | \`{kind: "email", summary_title, variants: [{label, body, subject}]}\` \u2014 2\u20133 variants, labels describe STRATEGY ("Push for alignment", "Reference the M&A signal"), not tone ("Friendly", "Formal") |
|
|
7351
|
-
| \`ask_user_input_v0\` (Claude) | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen |
|
|
7378
|
+
| \`ask_user_input_v0\` (Claude chat / ChatGPT) **or** \`AskUserQuestion\` (Claude cowork / Claude Code) \u2014 whichever is in your tool set; their schemas differ, match the one you have | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen | Per-tool schema in the server instructions + NEXT STEPS routing block. Max 3 questions. |
|
|
7352
7379
|
|
|
7353
7380
|
ChatGPT exposes the same routing pattern via \`_meta.openai/outputTemplate\`. We don't ship any custom widgets ourselves \u2014 this gate is exclusively about routing into the host's first-party widgets when the data shape fits.
|
|
7354
7381
|
|
|
@@ -7493,27 +7520,19 @@ Defer the full list of imported leads to \`leadbay_pull_leads\` or \`leadbay_res
|
|
|
7493
7520
|
|
|
7494
7521
|
## NEXT STEPS \u2014 after an import
|
|
7495
7522
|
|
|
7496
|
-
**
|
|
7523
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
7497
7524
|
|
|
7498
|
-
|
|
7525
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
7499
7526
|
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
question: "What next?",
|
|
7504
|
-
type: "single_select",
|
|
7505
|
-
options: [
|
|
7506
|
-
"<Suggest column from row 1>",
|
|
7507
|
-
"<Suggest column from row 2>",
|
|
7508
|
-
"<Suggest column from row 3>"
|
|
7509
|
-
]
|
|
7510
|
-
}]
|
|
7511
|
-
})
|
|
7512
|
-
\`\`\`
|
|
7527
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
7528
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
7529
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
7513
7530
|
|
|
7514
|
-
|
|
7531
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
7532
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
7533
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
7515
7534
|
|
|
7516
|
-
|
|
7535
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
7517
7536
|
|
|
7518
7537
|
---
|
|
7519
7538
|
|
|
@@ -7566,27 +7585,19 @@ Defer the full list of imported leads to \`leadbay_pull_leads\` or \`leadbay_res
|
|
|
7566
7585
|
|
|
7567
7586
|
## NEXT STEPS \u2014 after an import
|
|
7568
7587
|
|
|
7569
|
-
**
|
|
7588
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
7570
7589
|
|
|
7571
|
-
|
|
7590
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
7572
7591
|
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
question: "What next?",
|
|
7577
|
-
type: "single_select",
|
|
7578
|
-
options: [
|
|
7579
|
-
"<Suggest column from row 1>",
|
|
7580
|
-
"<Suggest column from row 2>",
|
|
7581
|
-
"<Suggest column from row 3>"
|
|
7582
|
-
]
|
|
7583
|
-
}]
|
|
7584
|
-
})
|
|
7585
|
-
\`\`\`
|
|
7592
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
7593
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
7594
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
7586
7595
|
|
|
7587
|
-
|
|
7596
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
7597
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
7598
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
7588
7599
|
|
|
7589
|
-
|
|
7600
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
7590
7601
|
|
|
7591
7602
|
---
|
|
7592
7603
|
|
|
@@ -7726,6 +7737,8 @@ WHEN TO USE: the user wants the cross-campaign pulse \u2014 what cohorts are in
|
|
|
7726
7737
|
WHEN NOT TO USE: for per-lead progression inside ONE campaign (use \`leadbay_campaign_progression\`); to create a campaign (\`leadbay_create_campaign\`); to add leads to one (\`leadbay_add_leads_to_campaign\`).
|
|
7727
7738
|
|
|
7728
7739
|
**Response**: \`{campaigns: CampaignWithStats[], _meta}\`. Sort by \`updated_at desc\` when rendering \u2014 recency is the manager's natural lens.
|
|
7740
|
+
|
|
7741
|
+
---
|
|
7729
7742
|
`;
|
|
7730
7743
|
var leadbay_list_lenses = `List all available Leadbay lenses (saved lead-search configurations). Each lens defines a different target market or buyer segment. The lens with \`is_last_active=true\` is used by default for lead discovery.
|
|
7731
7744
|
|
|
@@ -7852,7 +7865,7 @@ Modern chat hosts (Claude, ChatGPT) expose first-party widgets the agent can rou
|
|
|
7852
7865
|
|---|---|---|
|
|
7853
7866
|
| \`places_map_display_v0\` (Claude) | Result has \u22652 leads with \`location.city\` set, and the user's intent is geographic / "in person" / travel | \`{name: lead.company_name, address: "<city>, <country>", place_id: lead.location.place_id ?? omit, notes: <one-sentence pitch>}\` per location |
|
|
7854
7867
|
| \`message_compose_v1\` (Claude) | You're about to draft outreach (email / message / call opener) | \`{kind: "email", summary_title, variants: [{label, body, subject}]}\` \u2014 2\u20133 variants, labels describe STRATEGY ("Push for alignment", "Reference the M&A signal"), not tone ("Friendly", "Formal") |
|
|
7855
|
-
| \`ask_user_input_v0\` (Claude) | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen |
|
|
7868
|
+
| \`ask_user_input_v0\` (Claude chat / ChatGPT) **or** \`AskUserQuestion\` (Claude cowork / Claude Code) \u2014 whichever is in your tool set; their schemas differ, match the one you have | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen | Per-tool schema in the server instructions + NEXT STEPS routing block. Max 3 questions. |
|
|
7856
7869
|
|
|
7857
7870
|
ChatGPT exposes the same routing pattern via \`_meta.openai/outputTemplate\`. We don't ship any custom widgets ourselves \u2014 this gate is exclusively about routing into the host's first-party widgets when the data shape fits.
|
|
7858
7871
|
|
|
@@ -7891,27 +7904,19 @@ render an empty table.
|
|
|
7891
7904
|
|
|
7892
7905
|
## NEXT STEPS \u2014 after \`leadbay_my_lenses\`
|
|
7893
7906
|
|
|
7894
|
-
**
|
|
7907
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
7895
7908
|
|
|
7896
|
-
|
|
7909
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
7897
7910
|
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
question: "What next?",
|
|
7902
|
-
type: "single_select",
|
|
7903
|
-
options: [
|
|
7904
|
-
"<Suggest column from row 1>",
|
|
7905
|
-
"<Suggest column from row 2>",
|
|
7906
|
-
"<Suggest column from row 3>"
|
|
7907
|
-
]
|
|
7908
|
-
}]
|
|
7909
|
-
})
|
|
7910
|
-
\`\`\`
|
|
7911
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
7912
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
7913
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
7911
7914
|
|
|
7912
|
-
|
|
7915
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
7916
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
7917
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
7913
7918
|
|
|
7914
|
-
|
|
7919
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
7915
7920
|
|
|
7916
7921
|
---
|
|
7917
7922
|
|
|
@@ -7989,7 +7994,7 @@ Modern chat hosts (Claude, ChatGPT) expose first-party widgets the agent can rou
|
|
|
7989
7994
|
|---|---|---|
|
|
7990
7995
|
| \`places_map_display_v0\` (Claude) | Result has \u22652 leads with \`location.city\` set, and the user's intent is geographic / "in person" / travel | \`{name: lead.company_name, address: "<city>, <country>", place_id: lead.location.place_id ?? omit, notes: <one-sentence pitch>}\` per location |
|
|
7991
7996
|
| \`message_compose_v1\` (Claude) | You're about to draft outreach (email / message / call opener) | \`{kind: "email", summary_title, variants: [{label, body, subject}]}\` \u2014 2\u20133 variants, labels describe STRATEGY ("Push for alignment", "Reference the M&A signal"), not tone ("Friendly", "Formal") |
|
|
7992
|
-
| \`ask_user_input_v0\` (Claude) | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen |
|
|
7997
|
+
| \`ask_user_input_v0\` (Claude chat / ChatGPT) **or** \`AskUserQuestion\` (Claude cowork / Claude Code) \u2014 whichever is in your tool set; their schemas differ, match the one you have | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen | Per-tool schema in the server instructions + NEXT STEPS routing block. Max 3 questions. |
|
|
7993
7998
|
|
|
7994
7999
|
ChatGPT exposes the same routing pattern via \`_meta.openai/outputTemplate\`. We don't ship any custom widgets ourselves \u2014 this gate is exclusively about routing into the host's first-party widgets when the data shape fits.
|
|
7995
8000
|
|
|
@@ -8004,27 +8009,19 @@ ChatGPT exposes the same routing pattern via \`_meta.openai/outputTemplate\`. We
|
|
|
8004
8009
|
|
|
8005
8010
|
## NEXT STEPS \u2014 after \`leadbay_new_lens\`
|
|
8006
8011
|
|
|
8007
|
-
**
|
|
8012
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
8008
8013
|
|
|
8009
|
-
|
|
8014
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
8010
8015
|
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
question: "What next?",
|
|
8015
|
-
type: "single_select",
|
|
8016
|
-
options: [
|
|
8017
|
-
"<Suggest column from row 1>",
|
|
8018
|
-
"<Suggest column from row 2>",
|
|
8019
|
-
"<Suggest column from row 3>"
|
|
8020
|
-
]
|
|
8021
|
-
}]
|
|
8022
|
-
})
|
|
8023
|
-
\`\`\`
|
|
8016
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
8017
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
8018
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
8024
8019
|
|
|
8025
|
-
|
|
8020
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
8021
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
8022
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
8026
8023
|
|
|
8027
|
-
|
|
8024
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
8028
8025
|
|
|
8029
8026
|
---
|
|
8030
8027
|
|
|
@@ -8093,7 +8090,7 @@ email. Do NOT paste the email body into chat prose alongside.
|
|
|
8093
8090
|
|
|
8094
8091
|
---
|
|
8095
8092
|
|
|
8096
|
-
Prepare a single-lead outreach brief: the full \`lead\` block (score, \`split_ai_summary\`, \`location\`, \`size\`, \`phone_numbers\`, \`website\`, \`description\`, \`social_urls\`, \`social_presence\`), the \`recommended_contact\` (always in the post-enrichment shape \u2014 \`contact_id\`, \`first_name\`, \`last_name\`, \`job_title\`, \`email\`, \`phone_number\`, \`linkedin_page\`, \`is_org_contact\` \u2014 with nulls where data isn't yet enriched), \`additional_contacts_count
|
|
8093
|
+
Prepare a single-lead outreach brief: the full \`lead\` block (score, \`split_ai_summary\`, \`location\`, \`size\`, \`phone_numbers\`, \`website\`, \`description\`, \`social_urls\`, \`social_presence\`), the \`recommended_contact\` (always in the post-enrichment shape \u2014 \`contact_id\`, \`first_name\`, \`last_name\`, \`job_title\`, \`email\`, \`phone_number\`, \`linkedin_page\`, \`is_org_contact\` \u2014 with nulls where data isn't yet enriched), \`additional_contacts_count\`, and an \`enrichment\` block describing async state.
|
|
8097
8094
|
|
|
8098
8095
|
Optionally trigger contact enrichment in-flight with \`enrich:true\`. Enrichment is async (~60s). **Self-polling pattern (no separate tool needed):** re-call \`leadbay_prepare_outreach(leadId)\` without \`enrich\`; check \`enrichment.complete\`. When \`complete: true\`, the recommended contact now carries \`email\` and/or \`phone_number\`.
|
|
8099
8096
|
|
|
@@ -8110,46 +8107,9 @@ WHEN NOT TO USE: across many leads \u2014 use leadbay_enrich_titles for bulk; fo
|
|
|
8110
8107
|
|
|
8111
8108
|
## RENDER \u2014 host-native message composer is the PRIMARY surface
|
|
8112
8109
|
|
|
8113
|
-
\`message_compose_v1\`
|
|
8114
|
-
|
|
8115
|
-
Above the composer, emit ONE short markdown context paragraph: the lead's score callout + sector fit + recommended-contact name (LinkedIn-markdown-linked) + bare phone/email pills. That gives the user the "why this lead" context. The composer below carries the actionable draft.
|
|
8110
|
+
Route every draft through \`message_compose_v1\` (Claude's email composer). Above it, emit ONE short markdown context paragraph: score callout + sector fit + linked contact name + bare phone/email pills. Do NOT paste the email body into chat prose alongside \u2014 the composer IS the visual.
|
|
8116
8111
|
|
|
8117
|
-
**
|
|
8118
|
-
|
|
8119
|
-
\`\`\`
|
|
8120
|
-
message_compose_v1({
|
|
8121
|
-
kind: "email",
|
|
8122
|
-
summary_title: "Outreach to <Contact Name> at <Company>",
|
|
8123
|
-
variants: [{
|
|
8124
|
-
label: "Lead with the M&A signal",
|
|
8125
|
-
subject: "<one-line subject \u2014 references the angle>",
|
|
8126
|
-
body: "<5-8 sentence email; salesperson voice; references signal + a clear next step>"
|
|
8127
|
-
}]
|
|
8128
|
-
})
|
|
8129
|
-
\`\`\`
|
|
8130
|
-
|
|
8131
|
-
**Strategic options (preferred when split_ai_summary surfaces multiple angles):**
|
|
8132
|
-
|
|
8133
|
-
\`\`\`
|
|
8134
|
-
message_compose_v1({
|
|
8135
|
-
kind: "email",
|
|
8136
|
-
summary_title: "Three angles for <Company> outreach",
|
|
8137
|
-
variants: [
|
|
8138
|
-
{ label: "Push for alignment", subject: "...", body: "..." },
|
|
8139
|
-
{ label: "Reference the M&A signal", subject: "...", body: "..." },
|
|
8140
|
-
{ label: "Soft intro \u2014 peer reference", subject: "...", body: "..." }
|
|
8141
|
-
]
|
|
8142
|
-
})
|
|
8143
|
-
\`\`\`
|
|
8144
|
-
|
|
8145
|
-
Constraints:
|
|
8146
|
-
- **Labels describe STRATEGY, not tone.** "Push for alignment", "Reference M&A signal", "Lead with peer reference" \u2014 not "Friendly" / "Formal" / "Aggressive".
|
|
8147
|
-
- **2\u20133 variants when strategic options are clearly distinct.** One variant when you have a single best-angle draft.
|
|
8148
|
-
- Subject required for \`kind: "email"\`. Phone/call openers use \`kind: "other"\` with the opener in \`body\`.
|
|
8149
|
-
|
|
8150
|
-
The composer becomes the single visual. **Don't also paste the email body into chat prose** \u2014 that's just noise next to the composer.
|
|
8151
|
-
|
|
8152
|
-
For phone-only contacts (no email enriched), use \`kind: "other"\` with a 60-second call opener.
|
|
8112
|
+
Variant shape: 1\u20133 entries. Labels describe **strategy** ("Push for alignment", "Reference the M&A signal", "Soft intro \u2014 peer reference"), not tone. \`kind: "email"\` requires \`subject\`; phone/call openers use \`kind: "other"\` with the opener in \`body\`.
|
|
8153
8113
|
|
|
8154
8114
|
## GATE \u2014 PREFER BUILT-IN HOST WIDGETS
|
|
8155
8115
|
|
|
@@ -8161,7 +8121,7 @@ Modern chat hosts (Claude, ChatGPT) expose first-party widgets the agent can rou
|
|
|
8161
8121
|
|---|---|---|
|
|
8162
8122
|
| \`places_map_display_v0\` (Claude) | Result has \u22652 leads with \`location.city\` set, and the user's intent is geographic / "in person" / travel | \`{name: lead.company_name, address: "<city>, <country>", place_id: lead.location.place_id ?? omit, notes: <one-sentence pitch>}\` per location |
|
|
8163
8123
|
| \`message_compose_v1\` (Claude) | You're about to draft outreach (email / message / call opener) | \`{kind: "email", summary_title, variants: [{label, body, subject}]}\` \u2014 2\u20133 variants, labels describe STRATEGY ("Push for alignment", "Reference the M&A signal"), not tone ("Friendly", "Formal") |
|
|
8164
|
-
| \`ask_user_input_v0\` (Claude) | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen |
|
|
8124
|
+
| \`ask_user_input_v0\` (Claude chat / ChatGPT) **or** \`AskUserQuestion\` (Claude cowork / Claude Code) \u2014 whichever is in your tool set; their schemas differ, match the one you have | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen | Per-tool schema in the server instructions + NEXT STEPS routing block. Max 3 questions. |
|
|
8165
8125
|
|
|
8166
8126
|
ChatGPT exposes the same routing pattern via \`_meta.openai/outputTemplate\`. We don't ship any custom widgets ourselves \u2014 this gate is exclusively about routing into the host's first-party widgets when the data shape fits.
|
|
8167
8127
|
|
|
@@ -8242,27 +8202,19 @@ When the response carries \`social_urls\` (the post-fix multi-platform URL block
|
|
|
8242
8202
|
|
|
8243
8203
|
## NEXT STEPS \u2014 after the outreach brief
|
|
8244
8204
|
|
|
8245
|
-
**
|
|
8205
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
8246
8206
|
|
|
8247
|
-
|
|
8207
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
8248
8208
|
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
question: "What next?",
|
|
8253
|
-
type: "single_select",
|
|
8254
|
-
options: [
|
|
8255
|
-
"<Suggest column from row 1>",
|
|
8256
|
-
"<Suggest column from row 2>",
|
|
8257
|
-
"<Suggest column from row 3>"
|
|
8258
|
-
]
|
|
8259
|
-
}]
|
|
8260
|
-
})
|
|
8261
|
-
\`\`\`
|
|
8209
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
8210
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
8211
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
8262
8212
|
|
|
8263
|
-
|
|
8213
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
8214
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
8215
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
8264
8216
|
|
|
8265
|
-
|
|
8217
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
8266
8218
|
|
|
8267
8219
|
---
|
|
8268
8220
|
|
|
@@ -8282,7 +8234,6 @@ Offer 2\u20133 follow-ups. Choose based on enrichment state + available channels
|
|
|
8282
8234
|
| User reports they reached out | "Log this outreach \u2014 creates prospecting action + outcome" | leadbay_report_outreach(leadId, contact_id, ...) |
|
|
8283
8235
|
| User adds context for next time | "Save a note on the contact or company" | leadbay_add_note |
|
|
8284
8236
|
| After a successful exchange | "Update qualification answers based on what you learned" | leadbay_answer_clarification |
|
|
8285
|
-
|
|
8286
8237
|
The "log outreach" step is the most-important follow-up \u2014 it closes the loop and populates history for the next \`leadbay_prepare_outreach\` call. Detect intent from natural language: "I sent the email", "she didn't pick up", "left a voicemail", "they responded yes/no", etc.
|
|
8287
8238
|
`;
|
|
8288
8239
|
var leadbay_preview_bulk_enrichment = `Preview a bulk-enrichment cost given a set of job titles applied to the current selection. Returns \`{selected_leads, enriched_contacts, enrichable_contacts, title_suggestions, auto_included_titles, previously_enriched_titles}\`. \`previously_enriched_titles\` is a newer field (in prod soon) \u2014 when present, the agent can recommend repeating those titles for new leads.
|
|
@@ -8446,27 +8397,19 @@ When the response carries \`social_urls\` (the post-fix multi-platform URL block
|
|
|
8446
8397
|
|
|
8447
8398
|
## NEXT STEPS \u2014 after the follow-ups table
|
|
8448
8399
|
|
|
8449
|
-
**
|
|
8400
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
8450
8401
|
|
|
8451
|
-
|
|
8402
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
8452
8403
|
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
question: "What next?",
|
|
8457
|
-
type: "single_select",
|
|
8458
|
-
options: [
|
|
8459
|
-
"<Suggest column from row 1>",
|
|
8460
|
-
"<Suggest column from row 2>",
|
|
8461
|
-
"<Suggest column from row 3>"
|
|
8462
|
-
]
|
|
8463
|
-
}]
|
|
8464
|
-
})
|
|
8465
|
-
\`\`\`
|
|
8404
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
8405
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
8406
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
8466
8407
|
|
|
8467
|
-
|
|
8408
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
8409
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
8410
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
8468
8411
|
|
|
8469
|
-
|
|
8412
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
8470
8413
|
|
|
8471
8414
|
---
|
|
8472
8415
|
|
|
@@ -8487,7 +8430,6 @@ Always include at least one filter-modification offer (users think in filters: b
|
|
|
8487
8430
|
| User wants to defer a lead | "Snooze [Company] for 3 / 6 / 12 months" | leadbay_set_pushback({ lead_ids:[leadId], status:"3" }) |
|
|
8488
8431
|
| User completed outreach mid-flow | "Log the outreach + record the outcome" | leadbay_report_outreach |
|
|
8489
8432
|
| Discovery mode might fit better | "Looking for NEW leads instead? Switch to discovery." | leadbay_pull_leads |
|
|
8490
|
-
|
|
8491
8433
|
Always offer at least one of: prep outreach, refilter, pushback. Pushback is the canonical way to honor "not now" / "next quarter" \u2014 leads with active pushback are excluded from this view until expiry.
|
|
8492
8434
|
`;
|
|
8493
8435
|
var leadbay_pull_leads = `## WHEN TO USE
|
|
@@ -8614,27 +8556,19 @@ When the response carries \`social_urls\` (the post-fix multi-platform URL block
|
|
|
8614
8556
|
|
|
8615
8557
|
## NEXT STEPS \u2014 after rendering the pull_leads table
|
|
8616
8558
|
|
|
8617
|
-
**
|
|
8559
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
8618
8560
|
|
|
8619
|
-
|
|
8561
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
8620
8562
|
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
question: "What next?",
|
|
8625
|
-
type: "single_select",
|
|
8626
|
-
options: [
|
|
8627
|
-
"<Suggest column from row 1>",
|
|
8628
|
-
"<Suggest column from row 2>",
|
|
8629
|
-
"<Suggest column from row 3>"
|
|
8630
|
-
]
|
|
8631
|
-
}]
|
|
8632
|
-
})
|
|
8633
|
-
\`\`\`
|
|
8563
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
8564
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
8565
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
8634
8566
|
|
|
8635
|
-
|
|
8567
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
8568
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
8569
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
8636
8570
|
|
|
8637
|
-
|
|
8571
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
8638
8572
|
|
|
8639
8573
|
---
|
|
8640
8574
|
|
|
@@ -8644,6 +8578,7 @@ Pick 2\u20133 items below based on what was actually observed in the response. T
|
|
|
8644
8578
|
|
|
8645
8579
|
| Observation | Suggest | Calls |
|
|
8646
8580
|
|------------------------------------------------------------|--------------------------------------------------------------|--------------------------------------------------------|
|
|
8581
|
+
| \u2265 5 leads returned (any batch) | "Build an interactive lead triage board for this batch" | emit antArtifact from data in hand (do NOT re-call leadbay_pull_leads) |
|
|
8647
8582
|
| \`has_more == true\` | "Pull the next page (page N+1 of M)" | leadbay_pull_leads(page = current + 1, lensId = pinned)|
|
|
8648
8583
|
| \u2265 3 rows have \`qualification_summary.answered == 0\` | "Deepen AI qualification on the rows without \u2756 caps" | leadbay_bulk_qualify_leads(leadIds=[\u2026]) |
|
|
8649
8584
|
| User points at a single row | "Research [Company] in depth" | leadbay_research_lead_by_id(leadId) |
|
|
@@ -8654,7 +8589,7 @@ Pick 2\u20133 items below based on what was actually observed in the response. T
|
|
|
8654
8589
|
| Top row has contacts but no phone/email | "Order contact enrichment to surface email/phone first" | leadbay_enrich_titles(...) or leadbay_prepare_outreach(leadId, enrich:true) |
|
|
8655
8590
|
| \`computing_scores == true\` or \`computing_wishlist == true\` | "Scores are still being computed \u2014 re-pull in ~30s" | leadbay_pull_leads (retry with same lensId) |
|
|
8656
8591
|
| User wants a narrower / wider audience | "Adjust the lens filters (sector / size)" | leadbay_adjust_audience(...) |
|
|
8657
|
-
|
|
8592
|
+
| Phase 4 research was run (\`research_lead_by_id\` called) AND top contacts lack direct email/phone | "Enrich contacts on [Lead1], [Lead2] to get direct emails and phone numbers" | leadbay_enrich_contacts(leadId, contactId) \u2014 ONE call per contact (the tool takes a single leadId + contactId, never a list) |
|
|
8658
8593
|
If nothing in the menu applies cleanly, suggest only "pull next page" and "research a specific lead in depth" \u2014 never invent a tool that doesn't exist.
|
|
8659
8594
|
`;
|
|
8660
8595
|
var leadbay_qualify_lead = `Trigger AI qualification for a single lead (web fetch + AI rescore). The operation is asynchronous \u2014 results take ~60s. \`forceFetch:true\` re-runs even if recent data exists.
|
|
@@ -8968,27 +8903,19 @@ When the response carries \`social_urls\` (the post-fix multi-platform URL block
|
|
|
8968
8903
|
|
|
8969
8904
|
## NEXT STEPS \u2014 after the research card
|
|
8970
8905
|
|
|
8971
|
-
**
|
|
8906
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
8972
8907
|
|
|
8973
|
-
|
|
8908
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
8974
8909
|
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
8978
|
-
question: "What next?",
|
|
8979
|
-
type: "single_select",
|
|
8980
|
-
options: [
|
|
8981
|
-
"<Suggest column from row 1>",
|
|
8982
|
-
"<Suggest column from row 2>",
|
|
8983
|
-
"<Suggest column from row 3>"
|
|
8984
|
-
]
|
|
8985
|
-
}]
|
|
8986
|
-
})
|
|
8987
|
-
\`\`\`
|
|
8910
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
8911
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
8912
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
8988
8913
|
|
|
8989
|
-
|
|
8914
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
8915
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
8916
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
8990
8917
|
|
|
8991
|
-
|
|
8918
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
8992
8919
|
|
|
8993
8920
|
---
|
|
8994
8921
|
|
|
@@ -9033,6 +8960,7 @@ out?"\`
|
|
|
9033
8960
|
|
|
9034
8961
|
| Observation | Suggest | Calls |
|
|
9035
8962
|
|--------------------------------------------------------|----------------------------------------------------------|----------------------------------------------------------------|
|
|
8963
|
+
| Lead is clearly not a fit (wrong industry, too small) | "Dislike this lead" | leadbay_dislike_lead({ leadId }) |
|
|
9036
8964
|
| User is done with this lead | "Back to the inbox" | leadbay_pull_leads |
|
|
9037
8965
|
`;
|
|
9038
8966
|
var leadbay_research_lead_by_name_fuzzy = `## WHEN TO USE
|
|
@@ -9156,27 +9084,19 @@ When the response carries \`social_urls\` (the post-fix multi-platform URL block
|
|
|
9156
9084
|
|
|
9157
9085
|
## NEXT STEPS \u2014 after the research card
|
|
9158
9086
|
|
|
9159
|
-
**
|
|
9087
|
+
**ALWAYS render NEXT STEPS via your host's next-step widget.** Use whichever is in your tool set \u2014 the NAME and SCHEMA differ: **\`ask_user_input_v0\`** (Claude chat / ChatGPT) takes plain-string options with \`type:"single_select"\`; **\`AskUserQuestion\`** (Claude cowork / Claude Code) takes object options \`{label, description}\` plus a required short \`header\` (\u226412 chars) and \`multiSelect\`, NO \`type\` field, and never add an "Other" option (the host adds it). Match the schema to the tool you actually have \u2014 the wrong schema fails silently and you fall back to prose. Prose bullets are the fallback ONLY when NEITHER widget exists. Any turn that would end with a choice must be the widget \u2014 the widget IS the question.
|
|
9160
9088
|
|
|
9161
|
-
|
|
9089
|
+
**If the tool result carries a \`next_steps\` object, that is the source of truth \u2014 use it directly.** Each option has a short \`.label\` (\u22645 words) and a full \`.description\`. Map \`next_steps.options[]\` into your host widget VERBATIM and in order: for \`AskUserQuestion\` (cowork / Claude Code) pass each as \`{label, description}\`; for \`ask_user_input_v0\` (Claude chat / ChatGPT, string options only) pass each option's \`.description\` as the string (it's the full sentence). Do NOT reword, reorder, drop, or prose-ify them \u2014 they're built deterministically by the server so the offer (incl. the artifact option at position 0) fires every time. Fall back to the table below only when there is NO \`next_steps\` field.
|
|
9162
9090
|
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
question: "What next?",
|
|
9167
|
-
type: "single_select",
|
|
9168
|
-
options: [
|
|
9169
|
-
"<Suggest column from row 1>",
|
|
9170
|
-
"<Suggest column from row 2>",
|
|
9171
|
-
"<Suggest column from row 3>"
|
|
9172
|
-
]
|
|
9173
|
-
}]
|
|
9174
|
-
})
|
|
9175
|
-
\`\`\`
|
|
9091
|
+
**One exception \u2014 skip the widget** when the user's original message contained a complete sequential instruction chain ("show me X and then do Y") AND all stated steps have been completed. In that case, end with STOP directly \u2014 the user stated their full plan and does not need a "what next?" prompt.
|
|
9092
|
+
- Skip example: "Show me today's leads and then research the top one for me." \u2192 after research completes, emit STOP without the widget.
|
|
9093
|
+
- Do NOT skip for: plain requests ("show me today's leads", "run my check-in"), recurring-language requests ("I do this every day"), or requests where only one action was stated.
|
|
9176
9094
|
|
|
9177
|
-
|
|
9095
|
+
Pick 2\u20134 rows from the (Observation, Suggest, Calls) table below most relevant to the response, then call your host's widget with ITS schema (per the schema rules above \u2014 wrong schema fails silently):
|
|
9096
|
+
- \`ask_user_input_v0\`: \`{questions:[{question,type:"single_select",options:["<Suggest 1>","<Suggest 2>"]}]}\`
|
|
9097
|
+
- \`AskUserQuestion\`: \`{questions:[{question,header:"Next step",multiSelect:false,options:[{label:"<\u22645 words>",description:"<Suggest 1>"}]}]}\`
|
|
9178
9098
|
|
|
9179
|
-
|
|
9099
|
+
User picks \u2192 call the matching \`Calls\` tool. Constraints: 2\u20134 mutually-exclusive options, AskUserQuestion labels \u22645 words (full text in \`description\`), max 3 questions. Table stays internal; never recite it.
|
|
9180
9100
|
|
|
9181
9101
|
---
|
|
9182
9102
|
|
|
@@ -9221,6 +9141,7 @@ out?"\`
|
|
|
9221
9141
|
|
|
9222
9142
|
| Observation | Suggest | Calls |
|
|
9223
9143
|
|--------------------------------------------------------|----------------------------------------------------------|----------------------------------------------------------------|
|
|
9144
|
+
| Lead is clearly not a fit (wrong industry, too small) | "Dislike this lead" | leadbay_dislike_lead({ leadId }) |
|
|
9224
9145
|
| User is done with this lead | "Back to the inbox" | leadbay_pull_leads |
|
|
9225
9146
|
|
|
9226
9147
|
|
|
@@ -9440,7 +9361,7 @@ Modern chat hosts (Claude, ChatGPT) expose first-party widgets the agent can rou
|
|
|
9440
9361
|
|---|---|---|
|
|
9441
9362
|
| \`places_map_display_v0\` (Claude) | Result has \u22652 leads with \`location.city\` set, and the user's intent is geographic / "in person" / travel | \`{name: lead.company_name, address: "<city>, <country>", place_id: lead.location.place_id ?? omit, notes: <one-sentence pitch>}\` per location |
|
|
9442
9363
|
| \`message_compose_v1\` (Claude) | You're about to draft outreach (email / message / call opener) | \`{kind: "email", summary_title, variants: [{label, body, subject}]}\` \u2014 2\u20133 variants, labels describe STRATEGY ("Push for alignment", "Reference the M&A signal"), not tone ("Friendly", "Formal") |
|
|
9443
|
-
| \`ask_user_input_v0\` (Claude) | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen |
|
|
9364
|
+
| \`ask_user_input_v0\` (Claude chat / ChatGPT) **or** \`AskUserQuestion\` (Claude cowork / Claude Code) \u2014 whichever is in your tool set; their schemas differ, match the one you have | The tool's NEXT STEPS block has 2\u20134 mutually-exclusive next moves and the user hasn't already chosen | Per-tool schema in the server instructions + NEXT STEPS routing block. Max 3 questions. |
|
|
9444
9365
|
|
|
9445
9366
|
ChatGPT exposes the same routing pattern via \`_meta.openai/outputTemplate\`. We don't ship any custom widgets ourselves \u2014 this gate is exclusively about routing into the host's first-party widgets when the data shape fits.
|
|
9446
9367
|
|
|
@@ -9458,6 +9379,8 @@ WHEN TO USE: the user signals a *mixed* tour-planning intent \u2014 they want bo
|
|
|
9458
9379
|
WHEN NOT TO USE: if the user only wants follow-ups (use \`leadbay_followups_map\`), only wants new leads (use \`leadbay_pull_leads\`), wants research on one specific account (\`leadbay_research_lead_by_id\`), or wants to persist the tour as a campaign artifact (chain into \`leadbay_create_campaign\` after this).
|
|
9459
9380
|
|
|
9460
9381
|
**Response envelope**: \`{city, city_id, monitor_leads, discover_leads, discover_filter_note, _meta}\` on happy path; \`{status: "ambiguous_locations", location_ambiguities, ...}\` when the passed \`city\` matched multiple admin areas.
|
|
9382
|
+
|
|
9383
|
+
---
|
|
9461
9384
|
`;
|
|
9462
9385
|
var leadbay_update_lens = `Update lens metadata (name, description, mode flags). Does NOT change the audience filter \u2014 use leadbay_update_lens_filter for that.
|
|
9463
9386
|
|
|
@@ -13704,6 +13627,35 @@ function summarise(responses) {
|
|
|
13704
13627
|
}
|
|
13705
13628
|
return { answered, total, avg_qualification_boost: avg, best_response_excerpt: excerpt };
|
|
13706
13629
|
}
|
|
13630
|
+
function buildPullLeadsNextSteps(args) {
|
|
13631
|
+
const { leadCount, hasMore, nextPage } = args;
|
|
13632
|
+
if (leadCount <= 0)
|
|
13633
|
+
return null;
|
|
13634
|
+
const options = [];
|
|
13635
|
+
options.push({
|
|
13636
|
+
label: "Triage board",
|
|
13637
|
+
description: "Build an interactive lead triage board to sort and filter this batch.",
|
|
13638
|
+
kind: "build_artifact"
|
|
13639
|
+
});
|
|
13640
|
+
options.push({
|
|
13641
|
+
label: "Deepen qualification",
|
|
13642
|
+
description: "Run deeper AI qualification on these leads.",
|
|
13643
|
+
kind: "qualify_deeper"
|
|
13644
|
+
});
|
|
13645
|
+
if (hasMore && nextPage != null) {
|
|
13646
|
+
options.push({
|
|
13647
|
+
label: "Next page",
|
|
13648
|
+
description: `Pull page ${nextPage + 1} of this lens.`,
|
|
13649
|
+
kind: "pull_next_page"
|
|
13650
|
+
});
|
|
13651
|
+
}
|
|
13652
|
+
options.push({
|
|
13653
|
+
label: "Refine audience",
|
|
13654
|
+
description: "Adjust the lens audience / filters (sector, size, prompt).",
|
|
13655
|
+
kind: "refine_audience"
|
|
13656
|
+
});
|
|
13657
|
+
return { question: "What do you want to do next?", options: options.slice(0, 4) };
|
|
13658
|
+
}
|
|
13707
13659
|
var pullLeads = {
|
|
13708
13660
|
name: "leadbay_pull_leads",
|
|
13709
13661
|
annotations: {
|
|
@@ -13768,6 +13720,24 @@ var pullLeads = {
|
|
|
13768
13720
|
type: "boolean",
|
|
13769
13721
|
description: "True if scoring is still running."
|
|
13770
13722
|
},
|
|
13723
|
+
next_steps: {
|
|
13724
|
+
type: ["object", "null"],
|
|
13725
|
+
description: "Ready-made NEXT STEPS for the host's choice widget. Each option has a SHORT `label` (\u22645 words, fits AskUserQuestion's label cap on Claude cowork/Claude Code) and a full `description`. For AskUserQuestion (cowork/Claude Code) pass each option as {label, description}. For ask_user_input_v0 (Claude chat/ChatGPT, string-only options) use the `description` as the option string. Use these VERBATIM, in order \u2014 do NOT re-derive, reword, or render as prose when a widget tool exists. options[0] is the artifact offer (build the lead triage board) whenever the batch is non-empty. null only when the batch is empty.",
|
|
13726
|
+
properties: {
|
|
13727
|
+
question: { type: "string" },
|
|
13728
|
+
options: {
|
|
13729
|
+
type: "array",
|
|
13730
|
+
items: {
|
|
13731
|
+
type: "object",
|
|
13732
|
+
properties: {
|
|
13733
|
+
label: { type: "string" },
|
|
13734
|
+
description: { type: "string" },
|
|
13735
|
+
kind: { type: "string" }
|
|
13736
|
+
}
|
|
13737
|
+
}
|
|
13738
|
+
}
|
|
13739
|
+
}
|
|
13740
|
+
},
|
|
13771
13741
|
_meta: {
|
|
13772
13742
|
type: "object",
|
|
13773
13743
|
description: "Operator context: region + last-call latency.",
|
|
@@ -13842,6 +13812,8 @@ var pullLeads = {
|
|
|
13842
13812
|
const currentPage = res.pagination?.page ?? page;
|
|
13843
13813
|
const hasMore = currentPage < totalPages - 1;
|
|
13844
13814
|
const nextPage = hasMore ? currentPage + 1 : null;
|
|
13815
|
+
const leadCount = res.items.length;
|
|
13816
|
+
const nextSteps = buildPullLeadsNextSteps({ leadCount, hasMore, nextPage });
|
|
13845
13817
|
return withAgentMemoryMeta(client, {
|
|
13846
13818
|
lens: { id: lensId },
|
|
13847
13819
|
leads: res.items.map((lead) => ({
|
|
@@ -13853,6 +13825,7 @@ var pullLeads = {
|
|
|
13853
13825
|
next_page: nextPage,
|
|
13854
13826
|
computing_wishlist: res.computing_wishlist,
|
|
13855
13827
|
computing_scores: res.computing_scores,
|
|
13828
|
+
next_steps: nextSteps,
|
|
13856
13829
|
_meta: {
|
|
13857
13830
|
region: client.region,
|
|
13858
13831
|
latency_ms: client.lastMeta?.latency_ms ?? null
|
|
@@ -20140,7 +20113,7 @@ async function readResource(uri, client) {
|
|
|
20140
20113
|
}
|
|
20141
20114
|
|
|
20142
20115
|
// src/host-widgets.ts
|
|
20143
|
-
var BUILTIN_WIDGETS_PARAGRAPH = 'Prefer host-native widgets over inline markdown when the data shape fits. Three to know: (1) `places_map_display_v0` \u2014 for \u22652 locations / map / travel intent. Pass `{name, address, latitude, longitude, notes}` per location; the host enriches via Google Places. (2) `message_compose_v1` \u2014 for any outreach draft (email / message / call opener). Pass 2\u20133 strategic variants with goal-oriented labels ("Push for alignment", "Reference M&A signal") \u2014 NOT tone labels. (3)
|
|
20116
|
+
var BUILTIN_WIDGETS_PARAGRAPH = 'Prefer host-native widgets over inline markdown when the data shape fits. Three to know: (1) `places_map_display_v0` \u2014 for \u22652 locations / map / travel intent. Pass `{name, address, latitude, longitude, notes}` per location; the host enriches via Google Places. (2) `message_compose_v1` \u2014 for any outreach draft (email / message / call opener). Pass 2\u20133 strategic variants with goal-oriented labels ("Push for alignment", "Reference M&A signal") \u2014 NOT tone labels. (3) The next-step / choice widget \u2014 for the NEXT STEPS questions every Leadbay tool emits. Its NAME AND SCHEMA differ by host; use whichever is in your tool set: (a) `ask_user_input_v0` (Claude chat / ChatGPT) \u2014 options are PLAIN STRINGS with `type: "single_select"`, e.g. `{questions:[{question:"What next?",type:"single_select",options:["Build a triage board","Pull next page"]}]}`. (b) `AskUserQuestion` (Claude cowork / Claude Code) \u2014 options are OBJECTS `{label, description}`, plus a required short `header` (\u226412 chars) and a `multiSelect` boolean, and NO `type` field; do not add an "Other" option (the host adds it). e.g. `{questions:[{question:"What next?",header:"Next step",multiSelect:false,options:[{label:"Triage board",description:"Build an interactive board to sort this batch."},{label:"Next page",description:"Pull page 2."}]}]}`. Match the schema to the tool you actually have \u2014 using the string-schema for AskUserQuestion (or vice-versa) makes the call fail and you silently fall back to prose. When the host exposes NEITHER widget, fall back to the per-tool markdown RENDERING block.\n\nWIDGET IS MANDATORY WHEN AVAILABLE: if EITHER `ask_user_input_v0` OR `AskUserQuestion` is present in your tool set, you MUST emit your NEXT STEPS / scheduling / artifact offer by CALLING that widget tool (with its correct schema above) \u2014 do NOT write the options out as a prose question ("Want me to run this every morning?", "Should I build a board?"). Prose for these offers is the FALLBACK reserved ONLY for hosts that expose neither widget. When the widget exists, presenting the same choices as prose instead of calling it is a defect: the user loses the click-to-select surface. So whenever you have a recurring-task offer, an artifact offer, or a 2\u20134 option next-step menu AND a widget tool is available \u2192 call it, every time.';
|
|
20144
20117
|
|
|
20145
20118
|
// src/telemetry.ts
|
|
20146
20119
|
import { PostHog } from "posthog-node";
|
|
@@ -20489,6 +20462,41 @@ var QUOTA_TOPUP = `Quota & top-ups: when a tool returns QUOTA_EXCEEDED / 429, th
|
|
|
20489
20462
|
var VERIFICATION = `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='<the user's literal confirmation>'). Skipping or fabricating verification poisons the human team's pipeline.`;
|
|
20490
20463
|
|
|
20491
20464
|
// src/server.ts
|
|
20465
|
+
var ARTIFACT_PROPOSAL_PARAGRAPH = 'Artifact proposals: after any result the user would scan, compare, plan against, or return to \u2014 or any multi-item result \u2014 DEFAULT TO offering an artifact. Treat the offer as expected, not optional: a multi-item lead batch, a side-by-side comparison, or a research record the user will revisit each SHOULD get an artifact offer unless it clearly falls in the Skip list below. When in doubt, offer. WIDGET IS MANDATORY WHEN AVAILABLE: if your host\'s choice widget is in your tool set \u2014 `ask_user_input_v0` (Claude chat / ChatGPT) OR `AskUserQuestion` (Claude cowork / Claude Code) \u2014 you MUST make the offer by CALLING that widget tool \u2014 never as a prose line ("Want me to build an interactive lead triage board?"). Prose is the fallback ONLY for hosts that expose neither widget. Presenting the offer as prose when a widget exists is a defect \u2014 the user loses the click-to-select surface.\n\nTwo tracks:\n1. A NEXT STEPS menu is already firing (the choice widget with 2\u20134 options): include the artifact offer as one of the slots. It counts toward the 2\u20134 budget \u2014 replace the lowest-priority option when already at 4.\n2. No NEXT STEPS menu is firing: fire a standalone yes/no question by CALLING your host\'s choice widget with ITS schema (see the host-widget section: `ask_user_input_v0` takes string options; `AskUserQuestion` takes {label, description} objects with a `header` and `multiSelect`). The question is "Want me to build a [concrete artifact name] for this?" with yes/no options. Use the schema that matches the widget tool you actually have.\n\nName the artifact concretely from the result you just returned: "Build an interactive lead triage board", "Build a research one-pager for Acme Corp", "Build a side-by-side outreach variant comparison". Never use generic labels.\n\nOn selection of yes/build: build the artifact from data already in hand \u2014 do NOT re-call the tool. Emit with the antArtifact tag: type="text/html" for interactive boards/tables, type="application/vnd.ant.react" for React components, type="text/markdown" for one-pagers.\n\nSkip for: single-action confirmations (like/dislike, report_outreach), mid-flow steps, results a host widget already covers (places_map_display_v0, message_compose_v1).';
|
|
20466
|
+
var SCHEDULED_TASK_PARAGRAPH = `Scheduled task proposals: check the user's LITERAL words. NEVER fire based on the workflow or prompt type \u2014 only on what the user ACTUALLY TYPED.
|
|
20467
|
+
|
|
20468
|
+
NEVER fire for these examples (no recurring language in user message):
|
|
20469
|
+
- 'Show me today\\'s leads.' \u2192 NO
|
|
20470
|
+
- 'What are my leads today?' \u2192 NO
|
|
20471
|
+
- 'Pull my best new prospects.' \u2192 NO
|
|
20472
|
+
- 'Show me my batch.' \u2192 NO
|
|
20473
|
+
- 'Run my morning check-in.' (no recurring qualifier) \u2192 NO
|
|
20474
|
+
|
|
20475
|
+
ONLY fire when the user's message contains explicit recurring language:
|
|
20476
|
+
- 'I do this every day' \u2192 YES
|
|
20477
|
+
- 'every morning' \u2192 YES
|
|
20478
|
+
- 'remind me every week' \u2192 YES
|
|
20479
|
+
- 'automate this' \u2192 YES
|
|
20480
|
+
- 'set this up as recurring' \u2192 YES
|
|
20481
|
+
|
|
20482
|
+
WIDGET IS MANDATORY WHEN AVAILABLE: if your host's choice widget is in your tool set \u2014 \`ask_user_input_v0\` (Claude chat / ChatGPT) OR \`AskUserQuestion\` (Claude cowork / Claude Code) \u2014 you MUST make the scheduling offer by CALLING that widget tool \u2014 never as a prose line ("Want me to run this every morning?"). Prose is the fallback ONLY for hosts that expose neither widget; presenting the offer as prose when a widget exists is a defect.
|
|
20483
|
+
|
|
20484
|
+
Render EVERY question below by CALLING your host's choice widget with ITS schema (see the host-widget section: \`ask_user_input_v0\` takes string options; \`AskUserQuestion\` takes {label, description} objects with a \`header\` and \`multiSelect\`). Use the schema that matches the widget tool you actually have \u2014 never the other one.
|
|
20485
|
+
|
|
20486
|
+
Two tracks \u2014 same as artifact proposals:
|
|
20487
|
+
1. A NEXT STEPS menu is already firing: add "Schedule [Task Name] as a recurring task" as one slot (counts toward the 2\u20134 budget \u2014 replace the lowest-priority option when already at 4).
|
|
20488
|
+
2. No NEXT STEPS menu is firing: fire a standalone yes/no question "Want me to schedule this as a recurring task?" (options: yes / no thanks).
|
|
20489
|
+
|
|
20490
|
+
If the user selects yes, continue with a multi-step flow, one widget call per step:
|
|
20491
|
+
Step 1 \u2014 frequency: "How often?" \u2192 Every day / Every weekday / Weekly / Custom.
|
|
20492
|
+
Step 2a \u2014 if "Every day" or "Every weekday": "What time?" \u2192 Morning (8am) / Midday (12pm) / Afternoon (5pm) / Custom.
|
|
20493
|
+
Step 2b \u2014 if "Weekly": "Which day?" \u2192 Monday / Wednesday / Friday / Custom.
|
|
20494
|
+
Step 2c \u2014 if "Custom" at any step: ask for a free-text description and interpret it to determine the schedule.
|
|
20495
|
+
After the schedule is confirmed: judge whether the scheduled run should also produce an artifact. If yes, offer "Should each run also build an artifact (e.g. a fresh lead board)?" \u2192 yes / no.
|
|
20496
|
+
|
|
20497
|
+
Name the task concretely from context: "Daily prospecting check-in", "Weekly follow-up sweep", "Monday morning lead review". Never use generic labels.
|
|
20498
|
+
|
|
20499
|
+
Skip for: single-action confirmations, mid-flow steps, one-off lookups with no recurrence signal.`;
|
|
20492
20500
|
function buildScoringParagraph(has) {
|
|
20493
20501
|
const base = "Two scoring layers: every lead has a basic `score` (firmographic \u2014 already decent, usually correlates with AI). Roughly the top 10 of each batch are also AI-qualified (targeted web research + qualification questions \u2192 `ai_agent_lead_score`, surfaced as `qualification_summary` on leadbay_pull_leads). Leads past the top ~10 are not worse \u2014 the system is saving resources.";
|
|
20494
20502
|
const deepenTools = [];
|
|
@@ -20603,6 +20611,8 @@ function buildServerInstructions(exposed) {
|
|
|
20603
20611
|
if (has("leadbay_agent_memory_capture")) {
|
|
20604
20612
|
parts.push(AGENT_MEMORY);
|
|
20605
20613
|
}
|
|
20614
|
+
parts.push(ARTIFACT_PROPOSAL_PARAGRAPH);
|
|
20615
|
+
parts.push(SCHEDULED_TASK_PARAGRAPH);
|
|
20606
20616
|
parts.push(BUILTIN_WIDGETS_PARAGRAPH);
|
|
20607
20617
|
return parts.join("\n\n");
|
|
20608
20618
|
}
|
|
@@ -21254,7 +21264,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
21254
21264
|
}
|
|
21255
21265
|
|
|
21256
21266
|
// src/http-server.ts
|
|
21257
|
-
var VERSION = true ? "0.
|
|
21267
|
+
var VERSION = true ? "0.19.0" : "0.0.0-dev";
|
|
21258
21268
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
21259
21269
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
21260
21270
|
var sseSessions = /* @__PURE__ */ new Map();
|