@leadbay/mcp 0.21.3 → 0.22.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 +10 -0
- package/dist/bin.js +244 -4
- package/dist/http-server.js +218 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog — @leadbay/mcp
|
|
2
2
|
|
|
3
|
+
## 0.22.0 — 2026-06-20
|
|
4
|
+
|
|
5
|
+
Headless artifact SDK + two always-on read tools, so the user's Claude (cowork) can build interactive HTML artifacts that call Leadbay.
|
|
6
|
+
|
|
7
|
+
- **New package `@leadbay/components`** — vanilla view-models (`lb.field` / `action` / `resource` / `list`, plus domain helpers `outreach`, `note`, `like`/`dislike`, `leadHistory`, `leadProfile`, `callList`, `enrichment`, `teamActivity`) that own a control's data lifecycle: populate-from-API, value/loading/error, validation, polling, request sequencing, a 30s call timeout, and the `report_outreach` verification + `_triggered_by` footguns. The agent owns 100% of markup; the library renders nothing. The build emits a minified runtime into core; a `components:check` drift guard fails CI if the committed runtime goes stale.
|
|
8
|
+
- **`leadbay_artifact_kit`** (always-on read) — returns the runtime string + a usage guide the agent reads to assemble an artifact. Lives in `tools/` (granular-shaped) so a kit fetch carries no `_triggered_by` mandate.
|
|
9
|
+
- **`leadbay_team_activity`** (always-on read) — per-rep activity leaderboard + activity trend for a look-back window, wrapping `/kpi/users` + `/kpi/trends` (the web Dashboard-Manager data). Admins get the whole org; non-admins are scoped to themselves by the backend.
|
|
10
|
+
|
|
11
|
+
Validated by agent-dogfood tests: an independent agent builds a working cold-call sheet + a manager dashboard from the usage guide alone, and jsdom asserts the real tool calls fire with the right args. (Core 0.8.4, components 0.3.1.)
|
|
12
|
+
|
|
3
13
|
## 0.21.3 — 2026-06-19
|
|
4
14
|
|
|
5
15
|
Kills the 401 startup "reconnect Leadbay" hallucination — the assistant told users to re-authenticate on a connection that actually worked (product#3761). Fixed at every layer that produced or surfaced the spurious 401:
|
package/dist/bin.js
CHANGED
|
@@ -5163,6 +5163,7 @@ var init_composite_file_names = __esm({
|
|
|
5163
5163
|
"leadbay_resolve_import_rows",
|
|
5164
5164
|
"leadbay_scan_portfolio_signals",
|
|
5165
5165
|
"leadbay_seed_candidates",
|
|
5166
|
+
"leadbay_team_activity",
|
|
5166
5167
|
"leadbay_tour_plan"
|
|
5167
5168
|
]);
|
|
5168
5169
|
}
|
|
@@ -5467,7 +5468,7 @@ var init_notifications = __esm({
|
|
|
5467
5468
|
});
|
|
5468
5469
|
|
|
5469
5470
|
// ../core/dist/tool-descriptions.generated.js
|
|
5470
|
-
var leadbay_account_history, leadbay_account_status, leadbay_acknowledge_notification, leadbay_add_contact, leadbay_add_leads_to_campaign, leadbay_add_note, leadbay_adjust_audience, leadbay_agent_memory_capture, leadbay_agent_memory_recall, leadbay_agent_memory_review, leadbay_answer_clarification, leadbay_bulk_enrich_status, leadbay_bulk_qualify_leads, leadbay_campaign_call_sheet, leadbay_campaign_progression, leadbay_clear_selection, leadbay_clear_user_prompt, leadbay_create_campaign, leadbay_create_custom_field, leadbay_create_lens, leadbay_create_lens_draft, leadbay_create_topup_link, leadbay_deselect_leads, leadbay_discover_leads, leadbay_dislike_lead, leadbay_dismiss_clarification, leadbay_enrich_contacts, leadbay_enrich_titles, leadbay_extend_lens, leadbay_followups_map, leadbay_get_clarification, leadbay_get_contacts, leadbay_get_enrichment_job_titles, leadbay_get_epilogue_responses, leadbay_get_lead_activities, leadbay_get_lead_notes, leadbay_get_lead_profile, leadbay_get_lens_filter, leadbay_get_lens_scoring, leadbay_get_prospecting_actions, leadbay_get_quota, leadbay_get_selection_ids, leadbay_get_taste_profile, leadbay_get_user_prompt, leadbay_get_web_fetch, leadbay_import_and_qualify, leadbay_import_leads, leadbay_import_status, leadbay_launch_bulk_enrichment, leadbay_like_lead, leadbay_list_campaigns, leadbay_list_lenses, leadbay_list_locations, leadbay_list_mappable_fields, leadbay_list_sectors, leadbay_login, leadbay_my_lenses, leadbay_new_lens, leadbay_open_billing_portal, leadbay_pick_clarification, leadbay_pin_contact, leadbay_prepare_outreach, leadbay_preview_bulk_enrichment, leadbay_promote_lens, leadbay_pull_followups, leadbay_pull_leads, leadbay_qualify_lead, leadbay_qualify_status, leadbay_recall_ordered_titles, leadbay_refine_prompt, leadbay_remove_contact, leadbay_remove_epilogue, leadbay_remove_leads_from_campaign, leadbay_remove_pushback, leadbay_report_friction, leadbay_report_outreach, leadbay_research_lead_by_id, leadbay_research_lead_by_name_fuzzy, leadbay_resolve_import_rows, leadbay_scan_portfolio_signals, leadbay_seed_candidates, leadbay_select_leads, leadbay_send_feedback, leadbay_set_active_lens, leadbay_set_epilogue_status, leadbay_set_pushback, leadbay_set_user_prompt, leadbay_tour_plan, leadbay_unpin_contact, leadbay_update_contact, leadbay_update_lens, leadbay_update_lens_filter;
|
|
5471
|
+
var leadbay_account_history, leadbay_account_status, leadbay_acknowledge_notification, leadbay_add_contact, leadbay_add_leads_to_campaign, leadbay_add_note, leadbay_adjust_audience, leadbay_agent_memory_capture, leadbay_agent_memory_recall, leadbay_agent_memory_review, leadbay_answer_clarification, leadbay_artifact_kit, leadbay_bulk_enrich_status, leadbay_bulk_qualify_leads, leadbay_campaign_call_sheet, leadbay_campaign_progression, leadbay_clear_selection, leadbay_clear_user_prompt, leadbay_create_campaign, leadbay_create_custom_field, leadbay_create_lens, leadbay_create_lens_draft, leadbay_create_topup_link, leadbay_deselect_leads, leadbay_discover_leads, leadbay_dislike_lead, leadbay_dismiss_clarification, leadbay_enrich_contacts, leadbay_enrich_titles, leadbay_extend_lens, leadbay_followups_map, leadbay_get_clarification, leadbay_get_contacts, leadbay_get_enrichment_job_titles, leadbay_get_epilogue_responses, leadbay_get_lead_activities, leadbay_get_lead_notes, leadbay_get_lead_profile, leadbay_get_lens_filter, leadbay_get_lens_scoring, leadbay_get_prospecting_actions, leadbay_get_quota, leadbay_get_selection_ids, leadbay_get_taste_profile, leadbay_get_user_prompt, leadbay_get_web_fetch, leadbay_import_and_qualify, leadbay_import_leads, leadbay_import_status, leadbay_launch_bulk_enrichment, leadbay_like_lead, leadbay_list_campaigns, leadbay_list_lenses, leadbay_list_locations, leadbay_list_mappable_fields, leadbay_list_sectors, leadbay_login, leadbay_my_lenses, leadbay_new_lens, leadbay_open_billing_portal, leadbay_pick_clarification, leadbay_pin_contact, leadbay_prepare_outreach, leadbay_preview_bulk_enrichment, leadbay_promote_lens, leadbay_pull_followups, leadbay_pull_leads, leadbay_qualify_lead, leadbay_qualify_status, leadbay_recall_ordered_titles, leadbay_refine_prompt, leadbay_remove_contact, leadbay_remove_epilogue, leadbay_remove_leads_from_campaign, leadbay_remove_pushback, leadbay_report_friction, leadbay_report_outreach, leadbay_research_lead_by_id, leadbay_research_lead_by_name_fuzzy, leadbay_resolve_import_rows, leadbay_scan_portfolio_signals, leadbay_seed_candidates, leadbay_select_leads, leadbay_send_feedback, leadbay_set_active_lens, leadbay_set_epilogue_status, leadbay_set_pushback, leadbay_set_user_prompt, leadbay_team_activity, leadbay_tour_plan, leadbay_unpin_contact, leadbay_update_contact, leadbay_update_lens, leadbay_update_lens_filter;
|
|
5471
5472
|
var init_tool_descriptions_generated = __esm({
|
|
5472
5473
|
"../core/dist/tool-descriptions.generated.js"() {
|
|
5473
5474
|
"use strict";
|
|
@@ -5863,6 +5864,53 @@ WHEN TO USE: after leadbay_refine_prompt returns \`status='clarification_pending
|
|
|
5863
5864
|
WHEN NOT TO USE: to set a brand-new prompt \u2014 use leadbay_refine_prompt.
|
|
5864
5865
|
|
|
5865
5866
|
This tool MUTATES state. The caller (agent or human-in-the-loop) is responsible for confirming intent before invocation; the MCP server does not soft-prompt for confirmation. See \`annotations.destructiveHint\`.
|
|
5867
|
+
`;
|
|
5868
|
+
leadbay_artifact_kit = `## WHEN TO USE
|
|
5869
|
+
|
|
5870
|
+
Trigger phrases: "build me a dashboard", "build a call sheet", "interactive artifact", "make a page with buttons", "build an artifact to work my leads", "interactive lead triage board", "a page with buttons that log my calls".
|
|
5871
|
+
|
|
5872
|
+
**Memory:** recall + capture via \`leadbay_agent_memory_*\` tools.
|
|
5873
|
+
|
|
5874
|
+
Do NOT use for: "show me today's leads" \u2192 \`leadbay_pull_leads\`; "leads I should follow up with" \u2192 \`leadbay_pull_followups\`; "log that I emailed" \u2192 \`leadbay_report_outreach\`.
|
|
5875
|
+
|
|
5876
|
+
Prefer when: the user wants a clickable/interactive HTML artifact whose controls call Leadbay tools \u2014 not a one-off data answer
|
|
5877
|
+
|
|
5878
|
+
Examples that SHOULD invoke this tool:
|
|
5879
|
+
- "Build me an interactive call sheet for these leads."
|
|
5880
|
+
- "Make an artifact with buttons to log call outcomes per lead."
|
|
5881
|
+
- "I want a clickable lead-triage board I can work down."
|
|
5882
|
+
|
|
5883
|
+
Examples that should NOT invoke this tool (sound similar, route elsewhere):
|
|
5884
|
+
- "Show me today's leads."
|
|
5885
|
+
- "Which leads should I follow up with this week?"
|
|
5886
|
+
- "Log that I emailed Acme's CTO."
|
|
5887
|
+
|
|
5888
|
+
## RENDER (quick)
|
|
5889
|
+
|
|
5890
|
+
Do NOT render this tool's result as prose or a table. It returns
|
|
5891
|
+
\`runtime\` (a JS string) + \`usage_guide\` (the recipe) + \`version\`. Inline
|
|
5892
|
+
\`runtime\` as ONE \`<script>\` (it self-attaches \`window.LeadbayArtifacts\`),
|
|
5893
|
+
build view-models with \`lb.field/action/resource/list\` + the domain helpers,
|
|
5894
|
+
bind them to your own HTML, and pass the tools you call as the artifact's
|
|
5895
|
+
\`mcp_tools\`. READ \`usage_guide\` first. The artifact is the answer.
|
|
5896
|
+
|
|
5897
|
+
---
|
|
5898
|
+
|
|
5899
|
+
Hands you everything to build an interactive HTML **artifact** the user runs inside their own Claude (cowork): headless **domain view-models** + a markdown usage guide. A view-model owns a control's whole data lifecycle \u2014 populate a dropdown's options from a Leadbay call, hold the value, expose loading/error, validate, encapsulate the API call + business rules. You own 100% of the markup, layout, and style. The library renders nothing.
|
|
5900
|
+
|
|
5901
|
+
Returns \`{ runtime, usage_guide, version }\`:
|
|
5902
|
+
- \`runtime\` \u2014 a minified, zero-dependency vanilla IIFE that self-attaches \`window.LeadbayArtifacts\` (call it \`lb\`). Inline once as \`<script>\`.
|
|
5903
|
+
- \`usage_guide\` \u2014 the full recipe: \`lb.field\` / \`lb.action\` / the \`lb.bind*\` sugar, a copy-paste cold-call call sheet, and the write-call rules. READ IT before building.
|
|
5904
|
+
|
|
5905
|
+
The model \u2014 two layers. Primitives: \`lb.field\` (value + API-populated options), \`lb.action\` (write/submit), \`lb.resource\` (load-on-click / poll-until-done / \`.refresh()\`), \`lb.list\` (paginated rows); each exposes \`.loading/.error/.subscribe\`, and you bind them to YOUR native elements with \`lb.bindSelect/bindValue/bindAction\` (no style imposed). Domain components (pre-wired, footguns baked in): \`lb.campaigns\`, \`lb.outreach\`, \`lb.note\`, \`lb.like/dislike\`, \`lb.leadHistory\`, \`lb.leadProfile\`, \`lb.callList\`, \`lb.enrichment\` (launch + poll), \`lb.teamActivity\` (manager leaderboard + trend). TanStack-Query's headless-view-model separation, vanilla (cowork is inline-only \u2014 no React).
|
|
5906
|
+
|
|
5907
|
+
Canonical uses: a cold-call sheet (\`lb.callList\` + per-row \`lb.outreach\`/\`lb.leadHistory\`); a manager dashboard (\`lb.teamActivity\` \u2192 leaderboard table + Chart.js trend); a live enrichment view (\`lb.enrichment\` \u2192 progress + refresh). Live auto-poll is host-dependent \u2014 always wire a Refresh.
|
|
5908
|
+
|
|
5909
|
+
Write-call footguns (in the guide, repeated because they bite): for \`leadbay_report_outreach\` (status/disposition) the \`args\` MUST include \`verification:{source:"user_confirmed",ref:"\u2026"}\` AND \`_triggered_by:"<the user's request>"\`, or the call is rejected. \`leadbay_add_leads_to_campaign\` needs \`_triggered_by\` too. \`leadbay_add_note\`/\`leadbay_like_lead\`/\`leadbay_dislike_lead\` need only their own args. Snoozing (pushback) and standalone status are advanced-gated \u2014 not callable from a default artifact; use \`report_outreach\`'s \`epilogue_status\` for outcomes.
|
|
5910
|
+
|
|
5911
|
+
WHEN TO USE: the user asks for a clickable / interactive artifact, dashboard, or call sheet that DOES things (not just displays data).
|
|
5912
|
+
|
|
5913
|
+
WHEN NOT TO USE: the user wants a plain data answer (route to leadbay_pull_leads / leadbay_pull_followups) or to log a single real outreach you just did (leadbay_report_outreach).
|
|
5866
5914
|
`;
|
|
5867
5915
|
leadbay_bulk_enrich_status = `Check status + per-lead contacts for a bulk enrichment you previously launched via leadbay_enrich_titles. Returns the \`bulk_id\`, progress per lead (done/total enrichable contacts), and overall progress. When \`include_contacts=true\` (opt-in), includes each contact's email/phone/job_title/enrichment.done.
|
|
5868
5916
|
|
|
@@ -8867,6 +8915,50 @@ WHEN TO USE: low-level.
|
|
|
8867
8915
|
WHEN NOT TO USE: from agent flow \u2014 use leadbay_refine_prompt, which polls for follow-up clarifications.
|
|
8868
8916
|
|
|
8869
8917
|
This tool MUTATES state. The caller (agent or human-in-the-loop) is responsible for confirming intent before invocation; the MCP server does not soft-prompt for confirmation. See \`annotations.destructiveHint\`.
|
|
8918
|
+
`;
|
|
8919
|
+
leadbay_team_activity = `## WHEN TO USE
|
|
8920
|
+
|
|
8921
|
+
Trigger phrases: "how is my team doing", "team activity", "top performers", "rep leaderboard", "manager dashboard", "who's most active this week", "activity by rep".
|
|
8922
|
+
|
|
8923
|
+
**Memory:** recall + capture via \`leadbay_agent_memory_*\` tools.
|
|
8924
|
+
|
|
8925
|
+
Do NOT use for: "show me leads" \u2192 \`leadbay_pull_leads\`; "leads I should follow up with" \u2192 \`leadbay_pull_followups\`; "how is this campaign progressing" \u2192 \`leadbay_campaign_progression\`.
|
|
8926
|
+
|
|
8927
|
+
Prefer when: a manager wants team-wide / per-rep activity aggregates, not a lead list
|
|
8928
|
+
|
|
8929
|
+
Examples that SHOULD invoke this tool:
|
|
8930
|
+
- "How is my team doing this month?"
|
|
8931
|
+
- "Who are my top performers in the last two weeks?"
|
|
8932
|
+
- "Show me activity by rep."
|
|
8933
|
+
|
|
8934
|
+
Examples that should NOT invoke this tool (sound similar, route elsewhere):
|
|
8935
|
+
- "Show me today's leads."
|
|
8936
|
+
- "How is the Q3 Outbound campaign progressing?"
|
|
8937
|
+
- "Which leads should I follow up with?"
|
|
8938
|
+
|
|
8939
|
+
## RENDER (quick)
|
|
8940
|
+
|
|
8941
|
+
Per-rep leaderboard table (sort by total_activities desc) + an activity
|
|
8942
|
+
time-series. Columns worth showing: name, total_activities, notes,
|
|
8943
|
+
contacts_added, meetings_or_interest, lost. Feeds a manager dashboard
|
|
8944
|
+
artifact (table + Chart.js line of \`trend\`).
|
|
8945
|
+
|
|
8946
|
+
---
|
|
8947
|
+
|
|
8948
|
+
Manager / team KPIs \u2014 the data behind the web app's Dashboard-Manager screen, exposed so an agent (or an artifact) can render a team dashboard.
|
|
8949
|
+
|
|
8950
|
+
Returns \`{ range, reps, trend }\`:
|
|
8951
|
+
- \`range\` \u2014 \`{ from, to, periodicity }\` (the resolved look-back window).
|
|
8952
|
+
- \`reps\` \u2014 per-rep activity, sorted by \`total_activities\` desc (the leaderboard). Each: \`{ user_id, name, email, total_activities, likes, saves, website_clicks, exported, profile_views, contacts_added, contacts_purchased, notes, meetings_or_interest, could_not_reach, lost, still_chasing }\`. The \`epilogue_*\`-derived fields (\`meetings_or_interest\`, \`lost\`, \u2026) are the outcome signal.
|
|
8953
|
+
- \`trend\` \u2014 \`[{ date, count }]\` activity over time for the window (feed a chart).
|
|
8954
|
+
|
|
8955
|
+
Params: \`weeks\` (default 4) OR explicit \`from\`/\`to\` (YYYY-MM-DD); \`periodicity\` (DAILY|WEEKLY, default WEEKLY); \`user_ids\` (omit for the whole team). Admins get the whole org; non-admins are scoped to themselves by the backend.
|
|
8956
|
+
|
|
8957
|
+
Wraps \`GET /kpi/users\` + \`GET /kpi/trends\`. Quota / remaining capacity for the org lives on leadbay_account_status (don't duplicate it here).
|
|
8958
|
+
|
|
8959
|
+
WHEN TO USE: a manager wants to see team-wide or per-rep activity (leaderboard, who's active, activity trend).
|
|
8960
|
+
|
|
8961
|
+
WHEN NOT TO USE: the user wants a lead list (leadbay_pull_leads / leadbay_pull_followups) or one campaign's funnel (leadbay_campaign_progression).
|
|
8870
8962
|
`;
|
|
8871
8963
|
leadbay_tour_plan = `## WHEN TO USE
|
|
8872
8964
|
|
|
@@ -15652,7 +15744,13 @@ var init_research_lead_by_id = __esm({
|
|
|
15652
15744
|
},
|
|
15653
15745
|
agent_memory: { type: "object" }
|
|
15654
15746
|
},
|
|
15655
|
-
|
|
15747
|
+
// _meta is an open envelope: the MCP server layer injects
|
|
15748
|
+
// additional keys (latency_ms, notifications, update_available)
|
|
15749
|
+
// after the composite returns. Mirror the sibling tools
|
|
15750
|
+
// (leadbay_pull_leads et al.) and leave _meta open so those
|
|
15751
|
+
// server-side additions don't fail outputSchema validation
|
|
15752
|
+
// with -32602 (data/_meta must NOT have additional properties).
|
|
15753
|
+
additionalProperties: true
|
|
15656
15754
|
}
|
|
15657
15755
|
},
|
|
15658
15756
|
required: [
|
|
@@ -21405,6 +21503,84 @@ var init_report_friction = __esm({
|
|
|
21405
21503
|
}
|
|
21406
21504
|
});
|
|
21407
21505
|
|
|
21506
|
+
// ../core/dist/composite/team-activity.js
|
|
21507
|
+
function mapRep(r) {
|
|
21508
|
+
const n = (v) => v ?? 0;
|
|
21509
|
+
return {
|
|
21510
|
+
user_id: r.user?.id,
|
|
21511
|
+
name: r.user?.name ?? null,
|
|
21512
|
+
email: r.user?.email ?? null,
|
|
21513
|
+
total_activities: n(r.total_activities),
|
|
21514
|
+
likes: n(r.likes),
|
|
21515
|
+
saves: n(r.saves),
|
|
21516
|
+
website_clicks: n(r.website),
|
|
21517
|
+
exported: n(r.exported),
|
|
21518
|
+
profile_views: n(r.lead_profile_views),
|
|
21519
|
+
contacts_added: n(r.create_lead_contact),
|
|
21520
|
+
contacts_purchased: n(r.purchase_lead_contact),
|
|
21521
|
+
notes: n(r.create_lead_note),
|
|
21522
|
+
// Epilogue outcomes logged in the window — the "deals" signal.
|
|
21523
|
+
meetings_or_interest: n(r.epilogue_interest_validated_or_meeting_planed),
|
|
21524
|
+
could_not_reach: n(r.epilogue_could_not_reach_still_trying),
|
|
21525
|
+
lost: n(r.epilogue_not_interested_lost),
|
|
21526
|
+
still_chasing: n(r.epilogue_still_chasing)
|
|
21527
|
+
};
|
|
21528
|
+
}
|
|
21529
|
+
var DAY_MS, ymd, teamActivity;
|
|
21530
|
+
var init_team_activity = __esm({
|
|
21531
|
+
"../core/dist/composite/team-activity.js"() {
|
|
21532
|
+
"use strict";
|
|
21533
|
+
init_tool_descriptions_generated();
|
|
21534
|
+
DAY_MS = 864e5;
|
|
21535
|
+
ymd = (d) => d.toISOString().slice(0, 10);
|
|
21536
|
+
teamActivity = {
|
|
21537
|
+
name: "leadbay_team_activity",
|
|
21538
|
+
annotations: {
|
|
21539
|
+
title: "Team activity + per-rep KPIs",
|
|
21540
|
+
readOnlyHint: true,
|
|
21541
|
+
destructiveHint: false,
|
|
21542
|
+
idempotentHint: true,
|
|
21543
|
+
openWorldHint: true
|
|
21544
|
+
},
|
|
21545
|
+
description: leadbay_team_activity,
|
|
21546
|
+
write: false,
|
|
21547
|
+
inputSchema: {
|
|
21548
|
+
type: "object",
|
|
21549
|
+
properties: {
|
|
21550
|
+
weeks: { type: "number", description: "Look-back window in weeks (default 4). Ignored if from/to given." },
|
|
21551
|
+
from: { type: "string", description: "ISO date YYYY-MM-DD (start). Overrides weeks." },
|
|
21552
|
+
to: { type: "string", description: "ISO date YYYY-MM-DD (end). Overrides weeks." },
|
|
21553
|
+
periodicity: { type: "string", enum: ["DAILY", "WEEKLY"], description: "Trend bucketing (default WEEKLY)." },
|
|
21554
|
+
user_ids: {
|
|
21555
|
+
type: "array",
|
|
21556
|
+
items: { type: "string" },
|
|
21557
|
+
description: "Specific rep user-ids; omit for the whole team."
|
|
21558
|
+
}
|
|
21559
|
+
},
|
|
21560
|
+
additionalProperties: false
|
|
21561
|
+
},
|
|
21562
|
+
execute: async (client, params, _ctx) => {
|
|
21563
|
+
const periodicity = params.periodicity ?? "WEEKLY";
|
|
21564
|
+
const to = params.to ?? ymd(/* @__PURE__ */ new Date());
|
|
21565
|
+
const from = params.from ?? ymd(new Date(Date.parse(to) - Math.max(1, params.weeks ?? 4) * 7 * DAY_MS));
|
|
21566
|
+
const ids = params.user_ids && params.user_ids.length > 0 ? params.user_ids.join(",") : "ALL";
|
|
21567
|
+
const qs = `from=${from}&to=${to}&periodicity=${periodicity}&user_ids=${encodeURIComponent(ids)}`;
|
|
21568
|
+
const [usersRaw, trendRaw] = await Promise.all([
|
|
21569
|
+
client.request("GET", `/kpi/users?${qs}`),
|
|
21570
|
+
client.request("GET", `/kpi/trends?${qs}`)
|
|
21571
|
+
]);
|
|
21572
|
+
const reps = (usersRaw ?? []).map(mapRep).sort((a, b) => b.total_activities - a.total_activities);
|
|
21573
|
+
return {
|
|
21574
|
+
range: { from, to, periodicity },
|
|
21575
|
+
reps,
|
|
21576
|
+
trend: (trendRaw ?? []).map((t) => ({ date: t.date, count: t.count ?? 0 })),
|
|
21577
|
+
_meta: { region: client.region }
|
|
21578
|
+
};
|
|
21579
|
+
}
|
|
21580
|
+
};
|
|
21581
|
+
}
|
|
21582
|
+
});
|
|
21583
|
+
|
|
21408
21584
|
// ../core/dist/tools/send-feedback.js
|
|
21409
21585
|
var MESSAGE_MAX, sendFeedback;
|
|
21410
21586
|
var init_send_feedback = __esm({
|
|
@@ -21489,6 +21665,55 @@ var init_send_feedback = __esm({
|
|
|
21489
21665
|
}
|
|
21490
21666
|
});
|
|
21491
21667
|
|
|
21668
|
+
// ../core/dist/artifact-runtime.generated.js
|
|
21669
|
+
var ARTIFACT_KIT_VERSION, ARTIFACT_RUNTIME, ARTIFACT_USAGE_GUIDE;
|
|
21670
|
+
var init_artifact_runtime_generated = __esm({
|
|
21671
|
+
"../core/dist/artifact-runtime.generated.js"() {
|
|
21672
|
+
"use strict";
|
|
21673
|
+
ARTIFACT_KIT_VERSION = "0.3.1";
|
|
21674
|
+
ARTIFACT_RUNTIME = '"use strict";(()=>{var _=Object.defineProperty;var k=(e,t,n)=>t in e?_(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var o=(e,t,n)=>k(e,typeof t!="symbol"?t+"":t,n);var T="0.3.1",c=class extends Error{constructor(n,i={}){super(n);o(this,"code");o(this,"raw");this.name="LbError",this.code=i.code,this.raw=i.raw}},p=null,f=3e4;function E(){let e=globalThis.cowork;return e&&typeof e.callMcpTool=="function"?(t,n)=>e.callMcpTool(t,n):null}function v(e){if(e&&typeof e=="object"&&"content"in e){let t=e.content;if(Array.isArray(t)&&t[0]&&typeof t[0].text=="string")return t[0].text}return null}function y(e){if(!e||typeof e!="object")return e;let t=e;if(t.isError)throw new c(v(e)??"tool call failed",{raw:e});if("structuredContent"in t&&t.structuredContent!=null)return t.structuredContent;let n=v(e);if(n!=null)try{return JSON.parse(n)}catch{return n}return e}function L(e){return e instanceof Error?e.message:String(e)}function m(e){let t=e instanceof c?e.code:void 0;return{message:L(e),unavailable:t==="unavailable",code:t}}function S(e={}){p=e.call??null,f=e.timeoutMs??3e4}async function w(e,t){if(!f||f<=0)return e;let n,i=new Promise((r,s)=>{n=setTimeout(()=>s(new c(`"${t}" timed out after ${f}ms`,{code:"timeout"})),f)});try{return await Promise.race([e,i])}finally{n&&clearTimeout(n)}}async function a(e,t={}){if(p)return y(await w(Promise.resolve(p(e,t)),e));let n=E();if(!n)throw new c("Leadbay bridge unavailable (window.cowork absent)",{code:"unavailable"});return y(await w(Promise.resolve(n(e,t)),e))}var d=class{constructor(){o(this,"subs",new Set)}subscribe(t){return this.subs.add(t),t(this),()=>this.subs.delete(t)}emit(){for(let t of this.subs)t(this)}};function A(e){return Array.isArray(e)?e.map(t=>t&&typeof t=="object"?t:{value:t,label:String(t)}):[]}var h=class extends d{constructor(n={}){super();o(this,"kind");o(this,"value");o(this,"options",[]);o(this,"loading",!1);o(this,"error",null);o(this,"ready",!1);o(this,"cfg");o(this,"depUnsubs",[]);o(this,"seq",0);this.cfg=n,this.kind=n.kind,this.value=n.value??"";for(let i of n.dependsOn??[]){let r=i.value;this.depUnsubs.push(i.subscribe(()=>{i.value!==r&&(r=i.value,this.cfg.load&&this.load())}))}n.load&&(n.autoLoad??!0)&&this.load()}async load(){if(!this.cfg.load)return;let n=++this.seq;this.loading=!0,this.error=null,this.emit();try{let i=await this.cfg.load();if(n!==this.seq)return;this.options=this.cfg.options?this.cfg.options(i):A(i),this.ready=!0;let r=this.value==null?"":String(this.value);this.options.length&&(r===""||!this.options.some(s=>String(s.value)===r))&&(this.value=this.options[0].value)}catch(i){if(n!==this.seq)return;this.options=[],this.error=m(i)}finally{n===this.seq&&(this.loading=!1,this.emit())}}setValue(n){this.value=n;let i=this.validate();this.error=i?{message:i,unavailable:!1}:null,this.emit()}validate(){return this.cfg.validate?this.cfg.validate(this.value):null}get valid(){return this.validate()==null}reset(){this.value=this.cfg.value??"",this.error=null,this.emit()}dispose(){for(let n of this.depUnsubs)n();this.depUnsubs=[]}},l=class extends d{constructor(n){super();o(this,"loading",!1);o(this,"error",null);o(this,"lastResult",null);o(this,"cfg");this.cfg=n}async run(){if(this.loading)return;for(let i of this.cfg.fields??[]){let r=i.validate();if(r!=null){this.error={message:r,unavailable:!1},this.emit();return}}if(this.cfg.confirm&&typeof globalThis.confirm=="function"&&!globalThis.confirm(this.cfg.confirm))return;this.loading=!0,this.error=null,this.emit();let n;try{let i=typeof this.cfg.args=="function"?this.cfg.args():this.cfg.args??{};n=await a(this.cfg.tool,i)}catch(i){this.error=m(i),this.loading=!1,this.emit(),this.cfg.onError?.(this.error);return}return this.lastResult=n,this.loading=!1,this.emit(),this.cfg.onSuccess?.(n),n}reset(){this.error=null,this.lastResult=null,this.emit()}},u=class extends d{constructor(n){super();o(this,"data",null);o(this,"loading",!1);o(this,"refreshing",!1);o(this,"error",null);o(this,"done",!1);o(this,"cfg");o(this,"timer",null);o(this,"seq",0);this.cfg=n,(n.autoLoad??!0)&&this.load()}async load(){this.clearTimer();let n=++this.seq;this.data==null?this.loading=!0:this.refreshing=!0,this.error=null,this.emit();try{let r=await this.cfg.load();if(n!==this.seq)return;this.data=r,this.done=this.cfg.until?this.cfg.until(r):!0,this.cfg.pollEvery&&!this.done&&(this.timer=setTimeout(()=>void this.load(),this.cfg.pollEvery))}catch(r){if(n!==this.seq)return;this.error=m(r)}finally{n===this.seq&&(this.loading=!1,this.refreshing=!1,this.emit())}}refresh(){return this.load()}stop(){this.clearTimer()}clearTimer(){this.timer&&(clearTimeout(this.timer),this.timer=null)}},b=class extends d{constructor(n){super();o(this,"items",[]);o(this,"page",0);o(this,"pageSize");o(this,"total",0);o(this,"loading",!1);o(this,"error",null);o(this,"cfg");o(this,"seq",0);this.cfg=n,this.pageSize=n.pageSize??20,(n.autoLoad??!0)&&this.loadPage(0)}async loadPage(n){let i=++this.seq;this.loading=!0,this.error=null,this.emit();try{let r=await this.cfg.load({page:n,pageSize:this.pageSize});if(i!==this.seq)return;this.items=r.items??[],this.total=r.total??this.items.length,this.page=n}catch(r){if(i!==this.seq)return;this.error=m(r)}finally{i===this.seq&&(this.loading=!1,this.emit())}}next(){return this.loadPage(this.page+1)}prev(){return this.loadPage(Math.max(0,this.page-1))}get hasMore(){return(this.page+1)*this.pageSize<this.total}};function x(e,t){let n=t.error?.unavailable?"unavailable":t.loading?"loading":t.error?"error":"ready";e.setAttribute("data-lb-state",n),t.error?e.setAttribute("data-lb-error",t.error.message):e.removeAttribute("data-lb-error")}function C(e,t){let n=()=>t.setValue(e.value);e.addEventListener("change",n);let i=t.subscribe(()=>{x(e,t),e.disabled=t.loading,e.innerHTML="";for(let r of t.options){let s=document.createElement("option");s.value=String(r.value),s.textContent=r.label,e.appendChild(s)}e.value=t.value==null?"":String(t.value)});return()=>{e.removeEventListener("change",n),i()}}function I(e,t){let n=e.type==="checkbox",i=e.tagName==="SELECT"?"change":"input",r=()=>t.setValue(n?e.checked:e.value);e.addEventListener(i,r);let s=t.subscribe(()=>{if(n)e.checked=!!t.value;else{let g=t.value==null?"":String(t.value);e.value!==g&&(e.value=g)}e.setAttribute("data-lb-state",t.error?"error":"ready"),t.error?e.setAttribute("data-lb-error",t.error.message):e.removeAttribute("data-lb-error")});return()=>{e.removeEventListener(i,r),s()}}function R(e,t){let n=r=>{r.preventDefault(),t.run()};e.addEventListener("click",n);let i=t.subscribe(()=>{let r=t.error?.unavailable?"unavailable":t.loading?"loading":t.error?"error":t.lastResult!=null?"success":"idle";e.setAttribute("data-lb-state",r),"disabled"in e&&(e.disabled=t.loading),t.error?e.setAttribute("data-lb-error",t.error.message):e.removeAttribute("data-lb-error")});return()=>{e.removeEventListener("click",n),i()}}var O=["STILL_CHASING","COULD_NOT_REACH_STILL_TRYING","INTEREST_VALIDATED_OR_MEETING_PLANED","NOT_INTERESTED_LOST"];function P(e){return new h({kind:"select",load:()=>a("leadbay_list_campaigns",{_triggered_by:e}),options:t=>(t?.campaigns??[]).map(i=>{let r=i?.campaign??i;return r?.id?{value:r.id,label:r.name??r.ai_generated_name??String(r.id)}:null}).filter(i=>i!=null)})}function M(e){return new l({tool:"leadbay_report_outreach",fields:e.note?[e.note]:[],args:()=>({lead_id:e.leadId,...e.status?{epilogue_status:e.status.value}:{},note:e.note?e.note.value:"",verification:{source:"user_confirmed",ref:e.ref??"logged from artifact"},_triggered_by:e.ask})})}function F(e){return new l({tool:"leadbay_add_note",fields:[e.note],args:()=>({leadId:e.leadId,note:e.note.value})})}function q(e){return new l({tool:"leadbay_like_lead",args:{lead_id:e}})}function H(e){return new l({tool:"leadbay_dislike_lead",args:{lead_id:e}})}function z(e,t){return new u({autoLoad:!1,load:()=>a("leadbay_account_history",{leadId:e,_triggered_by:t})})}function N(e,t){return new u({autoLoad:!1,load:()=>a("leadbay_research_lead_by_id",{leadId:e,_triggered_by:t})})}function U(e){let t=null;return new u({pollEvery:e.pollEvery??4e3,until:n=>!!n?.all_done,load:async()=>{if(!t){let n=await a("leadbay_enrich_titles",{...e.leadIds?{leadIds:e.leadIds}:{},titles:e.titles,email:e.email??!0,phone:e.phone??!1,_triggered_by:e.ask});if(t=n?.bulk_id??null,!t)return{all_done:!0,no_job:!0,mode:n?.mode,preview:n?.preview}}return a("leadbay_bulk_enrich_status",{bulk_id:t,_triggered_by:e.ask})}})}function D(e){let t=e.source??"followups";return new b({pageSize:e.pageSize??20,load:async({page:n,pageSize:i})=>{let s=t==="campaign"?await a("leadbay_campaign_call_sheet",{campaign_id:e.campaignId,page:n,count:i,_triggered_by:e.ask}):await a("leadbay_pull_followups",{page:n,count:i,...e.city?{city:e.city}:{},_triggered_by:e.ask}),g=s.leads??s.items??[];return{items:g,total:s.total_leads??s.pagination?.total??g.length}}})}function V(e){return new u({load:()=>a("leadbay_team_activity",{weeks:e.weeks??4,_triggered_by:e.ask})})}var j={VERSION:T,configure:S,call:a,field:e=>new h(e),action:e=>new l(e),resource:e=>new u(e),list:e=>new b(e),bindSelect:C,bindValue:I,bindAction:R,campaigns:P,outreach:M,note:F,like:q,dislike:H,leadHistory:z,leadProfile:N,enrichment:U,callList:D,teamActivity:V,EPILOGUE_STATUSES:O};typeof globalThis<"u"&&(globalThis.LeadbayArtifacts=j);})();';
|
|
21675
|
+
ARTIFACT_USAGE_GUIDE = '# Leadbay Artifact Kit \u2014 headless domain components\n\nYou are building a single-file HTML **artifact** the user runs inside cowork. This\nkit gives you **headless view-models** that own a control\'s whole data lifecycle \u2014\nload/populate from a Leadbay call, hold value/state, poll, validate, and\nencapsulate the API call + business rules. **You own 100% of markup/layout/style.**\nThe library renders nothing. Inline the runtime once as a `<script>`; it exposes\none global `window.LeadbayArtifacts` (call it `lb`). Vanilla, no React, no build.\n\nPass every tool you use as the artifact\'s `mcp_tools` so the host permits it.\n\n## Two layers\n\n**Primitives** (generic):\n- `lb.field({ load, options, value, validate, dependsOn })` \u2014 a value + optionally\n API-populated options. `.value/.setValue/.options/.loading/.error/.valid/.subscribe`.\n- `lb.action({ tool, args, fields, confirm, onSuccess, onError })` \u2014 a write/submit.\n `.run()/.loading/.error/.lastResult/.subscribe`.\n- `lb.resource({ load, pollEvery?, until?, autoLoad? })` \u2014 one read that may change:\n load-on-click or poll-until-`until`. `.data/.loading/.refreshing/.error/.done/.load()/.refresh()/.stop()/.subscribe`.\n- `lb.list({ load, pageSize })` \u2014 paginated rows. `.items/.page/.total/.loading/.loadPage(n)/.next()/.prev()/.hasMore/.subscribe`.\n\n`.error` is `{ message, unavailable } | null`. `subscribe(cb)` fires immediately\nthen on every change \u2014 render your own DOM from it.\n\n**Domain components** (pre-wired \u2014 bake in the tool name, arg shape, and footguns):\n\n| Call | Returns | For |\n|---|---|---|\n| `lb.campaigns(ask)` | field | a campaign `<select>`, options from `leadbay_list_campaigns` |\n| `lb.outreach({leadId, ask, status?, note?})` | action | log a call \u2192 `report_outreach` (verification + `_triggered_by` baked in) |\n| `lb.note({leadId, note})` | action | add a note \u2192 `add_note` |\n| `lb.like(leadId)` / `lb.dislike(leadId)` | action | taste signal |\n| `lb.leadHistory(leadId, ask)` | resource (lazy) | notes + activities + engagement \u2192 `account_history` |\n| `lb.leadProfile(leadId, ask)` | resource (lazy) | full lead profile \u2192 `research_lead_by_id` |\n| `lb.callList({source:\'followups\'\\|\'campaign\', campaignId?, city?, ask})` | list | a cold-call list (Monitor or a campaign) |\n| `lb.enrichment({leadIds, titles, ask, pollEvery?})` | resource (polling) | launch + watch contact enrichment |\n| `lb.teamActivity({weeks, ask})` | resource | manager leaderboard + activity trend \u2192 `leadbay_team_activity` |\n\n`lb.EPILOGUE_STATUSES` = the 4 disposition values\n(`STILL_CHASING`, `COULD_NOT_REACH_STILL_TRYING`, `INTEREST_VALIDATED_OR_MEETING_PLANED`, `NOT_INTERESTED_LOST`).\n\n**Binding sugar** (optional; binds a view-model to YOUR native element, no style):\n`lb.bindSelect(selectEl, field)` (populates options + value), `lb.bindValue(inputEl, field)`,\n`lb.bindAction(buttonEl, action)`. They set `data-lb-state`\n(`ready|loading|error|success|unavailable`) + `data-lb-error` on your element as\nstyling hooks. For lists/resources, use `.subscribe()` and render yourself.\n\n`ask` is the user\'s request this artifact serves \u2014 it becomes `_triggered_by`.\n\n## Recipe: cold-call sheet (one row per lead)\n\n```js\nconst lb = window.LeadbayArtifacts; lb.configure();\nconst ASK = "<the user\'s request>";\n\nconst list = lb.callList({ source: "campaign", campaignId: CID, ask: ASK });\nlist.subscribe((l) => renderRows(l.items, l.loading)); // your render\n\n// per lead row (call when you build a row):\nfunction wireRow(lead, els) {\n const status = lb.field({ value: "STILL_CHASING" }); // static-enum <select>\n const note = lb.field({ validate: (v) => (v && v.trim() ? null : "Add a note") });\n lb.bindValue(els.status, status);\n lb.bindValue(els.note, note);\n lb.bindAction(els.log, lb.outreach({ leadId: lead.id, ask: ASK, status, note }));\n lb.bindAction(els.like, lb.like(lead.id));\n\n const history = lb.leadHistory(lead.id, ASK); // lazy\n history.subscribe((h) => renderHistory(els.history, h));\n els.expand.onclick = () => history.load(); // load on click\n}\n```\n\n## Recipe: manager dashboard\n\n```js\nconst team = lb.teamActivity({ weeks: 4, ask: ASK });\nteam.subscribe((t) => {\n if (t.loading) showSpinner();\n if (t.data) {\n renderLeaderboard(t.data.reps); // sorted by total_activities; cols: name, notes, meetings_or_interest, lost\u2026\n renderTrendChart(t.data.trend); // [{date,count}] \u2192 Chart.js (allowed from CDN)\n }\n});\nrefreshBtn.onclick = () => team.refresh();\n```\n\n## Recipe: live enrichment\n\n```js\nconst job = lb.enrichment({ leadIds: [LEAD], titles: ["CEO", "VP Sales"], ask: ASK });\njob.subscribe((j) => {\n const p = j.data && j.data.overall_progress; // {done,total,done_ratio}\n renderBar(p);\n if (j.done) renderContacts(j.data.leads); // enriched contacts\n});\nrefreshBtn.onclick = () => job.refresh();\n```\n\n## Write-call rules\n\nThe domain factories handle these for you. If you hand-roll an action:\n`leadbay_report_outreach` args MUST include `verification:{source:"user_confirmed", ref}`\nAND `_triggered_by`; `leadbay_add_leads_to_campaign` needs `_triggered_by`;\n`add_note`/`like_lead`/`dislike_lead` take only their own args. `epilogue_status` is\none of `lb.EPILOGUE_STATUSES`. Snoozing (pushback) and org WON/LOST status are\nadvanced-gated \u2014 not callable from a default artifact; use the epilogue values.\n\n## Degradation + live updates\n\nIf the host bridge is absent, a view-model\'s `.error` is set with `.error.unavailable\n=== true` (bind helpers set `data-lb-state="unavailable"`) \u2014 nothing throws. Every\ncall also has a **30s timeout** (configurable via `lb.configure({ timeoutMs })`): a\nhost call that never settles becomes `.error` with `code:"timeout"`, so a control is\nnever stuck loading forever \u2014 always render the `.error` branch so the user can retry.\nAuto-poll (`pollEvery`) depends on the cowork host serving FRESH reads; `.refresh()`\nis the guaranteed manual path \u2014 always wire a Refresh control for polling resources.';
|
|
21676
|
+
}
|
|
21677
|
+
});
|
|
21678
|
+
|
|
21679
|
+
// ../core/dist/tools/artifact-kit.js
|
|
21680
|
+
var artifactKit;
|
|
21681
|
+
var init_artifact_kit = __esm({
|
|
21682
|
+
"../core/dist/tools/artifact-kit.js"() {
|
|
21683
|
+
"use strict";
|
|
21684
|
+
init_tool_descriptions_generated();
|
|
21685
|
+
init_artifact_runtime_generated();
|
|
21686
|
+
artifactKit = {
|
|
21687
|
+
name: "leadbay_artifact_kit",
|
|
21688
|
+
annotations: {
|
|
21689
|
+
title: "Artifact component kit",
|
|
21690
|
+
readOnlyHint: true,
|
|
21691
|
+
destructiveHint: false,
|
|
21692
|
+
idempotentHint: true,
|
|
21693
|
+
openWorldHint: false
|
|
21694
|
+
},
|
|
21695
|
+
description: leadbay_artifact_kit,
|
|
21696
|
+
write: false,
|
|
21697
|
+
inputSchema: {
|
|
21698
|
+
type: "object",
|
|
21699
|
+
properties: {},
|
|
21700
|
+
additionalProperties: false
|
|
21701
|
+
},
|
|
21702
|
+
// No outputSchema by design: the return is three opaque strings. Declaring a
|
|
21703
|
+
// schema would enroll the tool in the output-schema-conformance drift-catcher
|
|
21704
|
+
// (an existing test file we don't modify). The server still emits the
|
|
21705
|
+
// plain-object return as structuredContent: { runtime, usage_guide, version }.
|
|
21706
|
+
execute: async (_client, _params, _ctx) => {
|
|
21707
|
+
return {
|
|
21708
|
+
version: ARTIFACT_KIT_VERSION,
|
|
21709
|
+
runtime: ARTIFACT_RUNTIME,
|
|
21710
|
+
usage_guide: ARTIFACT_USAGE_GUIDE
|
|
21711
|
+
};
|
|
21712
|
+
}
|
|
21713
|
+
};
|
|
21714
|
+
}
|
|
21715
|
+
});
|
|
21716
|
+
|
|
21492
21717
|
// ../core/dist/index.js
|
|
21493
21718
|
var dist_exports = {};
|
|
21494
21719
|
__export(dist_exports, {
|
|
@@ -21519,6 +21744,7 @@ __export(dist_exports, {
|
|
|
21519
21744
|
answerClarification: () => answerClarification,
|
|
21520
21745
|
appendEntry: () => appendEntry,
|
|
21521
21746
|
appendTombstone: () => appendTombstone,
|
|
21747
|
+
artifactKit: () => artifactKit,
|
|
21522
21748
|
assertSafeInsight: () => assertSafeInsight,
|
|
21523
21749
|
bulkEnrichStatus: () => bulkEnrichStatus,
|
|
21524
21750
|
bulkQualifyLeads: () => bulkQualifyLeads,
|
|
@@ -21624,6 +21850,7 @@ __export(dist_exports, {
|
|
|
21624
21850
|
setEpilogueStatus: () => setEpilogueStatus,
|
|
21625
21851
|
setPushback: () => setPushback,
|
|
21626
21852
|
setUserPrompt: () => setUserPrompt,
|
|
21853
|
+
teamActivity: () => teamActivity,
|
|
21627
21854
|
toInboxEntry: () => toInboxEntry,
|
|
21628
21855
|
tools: () => tools,
|
|
21629
21856
|
tourPlan: () => tourPlan,
|
|
@@ -21731,7 +21958,9 @@ var init_dist = __esm({
|
|
|
21731
21958
|
init_answer_clarification();
|
|
21732
21959
|
init_report_outreach();
|
|
21733
21960
|
init_report_friction();
|
|
21961
|
+
init_team_activity();
|
|
21734
21962
|
init_send_feedback();
|
|
21963
|
+
init_artifact_kit();
|
|
21735
21964
|
init_bulk_store();
|
|
21736
21965
|
agentMemoryTools = [
|
|
21737
21966
|
agentMemoryRecall,
|
|
@@ -21816,6 +22045,11 @@ var init_dist = __esm({
|
|
|
21816
22045
|
// answer to "which of my leads have signal X" that previously forced a
|
|
21817
22046
|
// per-lead research_lead_by_id loop (issue #3704).
|
|
21818
22047
|
scanPortfolioSignals,
|
|
22048
|
+
// Team activity — manager-facing per-rep leaderboard + activity trend (wraps
|
|
22049
|
+
// /kpi/users + /kpi/trends, the web Dashboard-Manager surface). Read-only;
|
|
22050
|
+
// always exposed so a manager artifact can render team KPIs. Non-admins are
|
|
22051
|
+
// scoped to themselves by the backend.
|
|
22052
|
+
teamActivity,
|
|
21819
22053
|
recallOrderedTitles,
|
|
21820
22054
|
accountStatus,
|
|
21821
22055
|
bulkEnrichStatus,
|
|
@@ -21854,7 +22088,13 @@ var init_dist = __esm({
|
|
|
21854
22088
|
// every tool response regardless of write gating; without ack the agent
|
|
21855
22089
|
// sees the same entries on every call forever. Pairing the surfacing
|
|
21856
22090
|
// channel with the clearing tool is non-optional.
|
|
21857
|
-
acknowledgeNotification
|
|
22091
|
+
acknowledgeNotification,
|
|
22092
|
+
// Artifact kit — ALWAYS exposed, read-only. Returns the headless
|
|
22093
|
+
// @leadbay/components runtime + usage guide the agent uses to BUILD an
|
|
22094
|
+
// interactive HTML artifact (cold-call sheet, lead-triage board) whose
|
|
22095
|
+
// buttons call Leadbay writes. No backend call; granular-shaped (lives in
|
|
22096
|
+
// tools/) so it carries no _triggered_by mandate for a kit fetch.
|
|
22097
|
+
artifactKit
|
|
21858
22098
|
];
|
|
21859
22099
|
compositeWriteTools = [
|
|
21860
22100
|
bulkQualifyLeads,
|
|
@@ -26184,7 +26424,7 @@ var OAUTH_BASE_URLS = {
|
|
|
26184
26424
|
fr: "https://staging.api.leadbay.app"
|
|
26185
26425
|
}
|
|
26186
26426
|
};
|
|
26187
|
-
var VERSION = "0.
|
|
26427
|
+
var VERSION = "0.22.0";
|
|
26188
26428
|
var HELP = `
|
|
26189
26429
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
26190
26430
|
|
package/dist/http-server.js
CHANGED
|
@@ -6333,6 +6333,7 @@ var COMPOSITE_FILE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
6333
6333
|
"leadbay_resolve_import_rows",
|
|
6334
6334
|
"leadbay_scan_portfolio_signals",
|
|
6335
6335
|
"leadbay_seed_candidates",
|
|
6336
|
+
"leadbay_team_activity",
|
|
6336
6337
|
"leadbay_tour_plan"
|
|
6337
6338
|
]);
|
|
6338
6339
|
|
|
@@ -6733,6 +6734,53 @@ WHEN NOT TO USE: to set a brand-new prompt \u2014 use leadbay_refine_prompt.
|
|
|
6733
6734
|
|
|
6734
6735
|
This tool MUTATES state. The caller (agent or human-in-the-loop) is responsible for confirming intent before invocation; the MCP server does not soft-prompt for confirmation. See \`annotations.destructiveHint\`.
|
|
6735
6736
|
`;
|
|
6737
|
+
var leadbay_artifact_kit = `## WHEN TO USE
|
|
6738
|
+
|
|
6739
|
+
Trigger phrases: "build me a dashboard", "build a call sheet", "interactive artifact", "make a page with buttons", "build an artifact to work my leads", "interactive lead triage board", "a page with buttons that log my calls".
|
|
6740
|
+
|
|
6741
|
+
**Memory:** recall + capture via \`leadbay_agent_memory_*\` tools.
|
|
6742
|
+
|
|
6743
|
+
Do NOT use for: "show me today's leads" \u2192 \`leadbay_pull_leads\`; "leads I should follow up with" \u2192 \`leadbay_pull_followups\`; "log that I emailed" \u2192 \`leadbay_report_outreach\`.
|
|
6744
|
+
|
|
6745
|
+
Prefer when: the user wants a clickable/interactive HTML artifact whose controls call Leadbay tools \u2014 not a one-off data answer
|
|
6746
|
+
|
|
6747
|
+
Examples that SHOULD invoke this tool:
|
|
6748
|
+
- "Build me an interactive call sheet for these leads."
|
|
6749
|
+
- "Make an artifact with buttons to log call outcomes per lead."
|
|
6750
|
+
- "I want a clickable lead-triage board I can work down."
|
|
6751
|
+
|
|
6752
|
+
Examples that should NOT invoke this tool (sound similar, route elsewhere):
|
|
6753
|
+
- "Show me today's leads."
|
|
6754
|
+
- "Which leads should I follow up with this week?"
|
|
6755
|
+
- "Log that I emailed Acme's CTO."
|
|
6756
|
+
|
|
6757
|
+
## RENDER (quick)
|
|
6758
|
+
|
|
6759
|
+
Do NOT render this tool's result as prose or a table. It returns
|
|
6760
|
+
\`runtime\` (a JS string) + \`usage_guide\` (the recipe) + \`version\`. Inline
|
|
6761
|
+
\`runtime\` as ONE \`<script>\` (it self-attaches \`window.LeadbayArtifacts\`),
|
|
6762
|
+
build view-models with \`lb.field/action/resource/list\` + the domain helpers,
|
|
6763
|
+
bind them to your own HTML, and pass the tools you call as the artifact's
|
|
6764
|
+
\`mcp_tools\`. READ \`usage_guide\` first. The artifact is the answer.
|
|
6765
|
+
|
|
6766
|
+
---
|
|
6767
|
+
|
|
6768
|
+
Hands you everything to build an interactive HTML **artifact** the user runs inside their own Claude (cowork): headless **domain view-models** + a markdown usage guide. A view-model owns a control's whole data lifecycle \u2014 populate a dropdown's options from a Leadbay call, hold the value, expose loading/error, validate, encapsulate the API call + business rules. You own 100% of the markup, layout, and style. The library renders nothing.
|
|
6769
|
+
|
|
6770
|
+
Returns \`{ runtime, usage_guide, version }\`:
|
|
6771
|
+
- \`runtime\` \u2014 a minified, zero-dependency vanilla IIFE that self-attaches \`window.LeadbayArtifacts\` (call it \`lb\`). Inline once as \`<script>\`.
|
|
6772
|
+
- \`usage_guide\` \u2014 the full recipe: \`lb.field\` / \`lb.action\` / the \`lb.bind*\` sugar, a copy-paste cold-call call sheet, and the write-call rules. READ IT before building.
|
|
6773
|
+
|
|
6774
|
+
The model \u2014 two layers. Primitives: \`lb.field\` (value + API-populated options), \`lb.action\` (write/submit), \`lb.resource\` (load-on-click / poll-until-done / \`.refresh()\`), \`lb.list\` (paginated rows); each exposes \`.loading/.error/.subscribe\`, and you bind them to YOUR native elements with \`lb.bindSelect/bindValue/bindAction\` (no style imposed). Domain components (pre-wired, footguns baked in): \`lb.campaigns\`, \`lb.outreach\`, \`lb.note\`, \`lb.like/dislike\`, \`lb.leadHistory\`, \`lb.leadProfile\`, \`lb.callList\`, \`lb.enrichment\` (launch + poll), \`lb.teamActivity\` (manager leaderboard + trend). TanStack-Query's headless-view-model separation, vanilla (cowork is inline-only \u2014 no React).
|
|
6775
|
+
|
|
6776
|
+
Canonical uses: a cold-call sheet (\`lb.callList\` + per-row \`lb.outreach\`/\`lb.leadHistory\`); a manager dashboard (\`lb.teamActivity\` \u2192 leaderboard table + Chart.js trend); a live enrichment view (\`lb.enrichment\` \u2192 progress + refresh). Live auto-poll is host-dependent \u2014 always wire a Refresh.
|
|
6777
|
+
|
|
6778
|
+
Write-call footguns (in the guide, repeated because they bite): for \`leadbay_report_outreach\` (status/disposition) the \`args\` MUST include \`verification:{source:"user_confirmed",ref:"\u2026"}\` AND \`_triggered_by:"<the user's request>"\`, or the call is rejected. \`leadbay_add_leads_to_campaign\` needs \`_triggered_by\` too. \`leadbay_add_note\`/\`leadbay_like_lead\`/\`leadbay_dislike_lead\` need only their own args. Snoozing (pushback) and standalone status are advanced-gated \u2014 not callable from a default artifact; use \`report_outreach\`'s \`epilogue_status\` for outcomes.
|
|
6779
|
+
|
|
6780
|
+
WHEN TO USE: the user asks for a clickable / interactive artifact, dashboard, or call sheet that DOES things (not just displays data).
|
|
6781
|
+
|
|
6782
|
+
WHEN NOT TO USE: the user wants a plain data answer (route to leadbay_pull_leads / leadbay_pull_followups) or to log a single real outreach you just did (leadbay_report_outreach).
|
|
6783
|
+
`;
|
|
6736
6784
|
var leadbay_bulk_enrich_status = `Check status + per-lead contacts for a bulk enrichment you previously launched via leadbay_enrich_titles. Returns the \`bulk_id\`, progress per lead (done/total enrichable contacts), and overall progress. When \`include_contacts=true\` (opt-in), includes each contact's email/phone/job_title/enrichment.done.
|
|
6737
6785
|
|
|
6738
6786
|
WHEN TO USE: poll this after leadbay_enrich_titles returns a \`bulk_id\`. Default \`include_contacts=false\` for cheap status polls; set \`include_contacts=true\` once \`all_done\` flips for the final read.
|
|
@@ -9737,6 +9785,50 @@ WHEN NOT TO USE: from agent flow \u2014 use leadbay_refine_prompt, which polls f
|
|
|
9737
9785
|
|
|
9738
9786
|
This tool MUTATES state. The caller (agent or human-in-the-loop) is responsible for confirming intent before invocation; the MCP server does not soft-prompt for confirmation. See \`annotations.destructiveHint\`.
|
|
9739
9787
|
`;
|
|
9788
|
+
var leadbay_team_activity = `## WHEN TO USE
|
|
9789
|
+
|
|
9790
|
+
Trigger phrases: "how is my team doing", "team activity", "top performers", "rep leaderboard", "manager dashboard", "who's most active this week", "activity by rep".
|
|
9791
|
+
|
|
9792
|
+
**Memory:** recall + capture via \`leadbay_agent_memory_*\` tools.
|
|
9793
|
+
|
|
9794
|
+
Do NOT use for: "show me leads" \u2192 \`leadbay_pull_leads\`; "leads I should follow up with" \u2192 \`leadbay_pull_followups\`; "how is this campaign progressing" \u2192 \`leadbay_campaign_progression\`.
|
|
9795
|
+
|
|
9796
|
+
Prefer when: a manager wants team-wide / per-rep activity aggregates, not a lead list
|
|
9797
|
+
|
|
9798
|
+
Examples that SHOULD invoke this tool:
|
|
9799
|
+
- "How is my team doing this month?"
|
|
9800
|
+
- "Who are my top performers in the last two weeks?"
|
|
9801
|
+
- "Show me activity by rep."
|
|
9802
|
+
|
|
9803
|
+
Examples that should NOT invoke this tool (sound similar, route elsewhere):
|
|
9804
|
+
- "Show me today's leads."
|
|
9805
|
+
- "How is the Q3 Outbound campaign progressing?"
|
|
9806
|
+
- "Which leads should I follow up with?"
|
|
9807
|
+
|
|
9808
|
+
## RENDER (quick)
|
|
9809
|
+
|
|
9810
|
+
Per-rep leaderboard table (sort by total_activities desc) + an activity
|
|
9811
|
+
time-series. Columns worth showing: name, total_activities, notes,
|
|
9812
|
+
contacts_added, meetings_or_interest, lost. Feeds a manager dashboard
|
|
9813
|
+
artifact (table + Chart.js line of \`trend\`).
|
|
9814
|
+
|
|
9815
|
+
---
|
|
9816
|
+
|
|
9817
|
+
Manager / team KPIs \u2014 the data behind the web app's Dashboard-Manager screen, exposed so an agent (or an artifact) can render a team dashboard.
|
|
9818
|
+
|
|
9819
|
+
Returns \`{ range, reps, trend }\`:
|
|
9820
|
+
- \`range\` \u2014 \`{ from, to, periodicity }\` (the resolved look-back window).
|
|
9821
|
+
- \`reps\` \u2014 per-rep activity, sorted by \`total_activities\` desc (the leaderboard). Each: \`{ user_id, name, email, total_activities, likes, saves, website_clicks, exported, profile_views, contacts_added, contacts_purchased, notes, meetings_or_interest, could_not_reach, lost, still_chasing }\`. The \`epilogue_*\`-derived fields (\`meetings_or_interest\`, \`lost\`, \u2026) are the outcome signal.
|
|
9822
|
+
- \`trend\` \u2014 \`[{ date, count }]\` activity over time for the window (feed a chart).
|
|
9823
|
+
|
|
9824
|
+
Params: \`weeks\` (default 4) OR explicit \`from\`/\`to\` (YYYY-MM-DD); \`periodicity\` (DAILY|WEEKLY, default WEEKLY); \`user_ids\` (omit for the whole team). Admins get the whole org; non-admins are scoped to themselves by the backend.
|
|
9825
|
+
|
|
9826
|
+
Wraps \`GET /kpi/users\` + \`GET /kpi/trends\`. Quota / remaining capacity for the org lives on leadbay_account_status (don't duplicate it here).
|
|
9827
|
+
|
|
9828
|
+
WHEN TO USE: a manager wants to see team-wide or per-rep activity (leaderboard, who's active, activity trend).
|
|
9829
|
+
|
|
9830
|
+
WHEN NOT TO USE: the user wants a lead list (leadbay_pull_leads / leadbay_pull_followups) or one campaign's funnel (leadbay_campaign_progression).
|
|
9831
|
+
`;
|
|
9740
9832
|
var leadbay_tour_plan = `## WHEN TO USE
|
|
9741
9833
|
|
|
9742
9834
|
Trigger phrases: "visiting <city> in <N> days", "field tour in <city>", "plan a tour in <city>", "who should I meet in <city>", "customers plus prospects in <city>", "tour itinerary".
|
|
@@ -15995,7 +16087,13 @@ var researchLeadById = {
|
|
|
15995
16087
|
},
|
|
15996
16088
|
agent_memory: { type: "object" }
|
|
15997
16089
|
},
|
|
15998
|
-
|
|
16090
|
+
// _meta is an open envelope: the MCP server layer injects
|
|
16091
|
+
// additional keys (latency_ms, notifications, update_available)
|
|
16092
|
+
// after the composite returns. Mirror the sibling tools
|
|
16093
|
+
// (leadbay_pull_leads et al.) and leave _meta open so those
|
|
16094
|
+
// server-side additions don't fail outputSchema validation
|
|
16095
|
+
// with -32602 (data/_meta must NOT have additional properties).
|
|
16096
|
+
additionalProperties: true
|
|
15999
16097
|
}
|
|
16000
16098
|
},
|
|
16001
16099
|
required: [
|
|
@@ -20960,6 +21058,77 @@ var reportFriction = {
|
|
|
20960
21058
|
}
|
|
20961
21059
|
};
|
|
20962
21060
|
|
|
21061
|
+
// ../core/dist/composite/team-activity.js
|
|
21062
|
+
var DAY_MS = 864e5;
|
|
21063
|
+
var ymd = (d) => d.toISOString().slice(0, 10);
|
|
21064
|
+
function mapRep(r) {
|
|
21065
|
+
const n = (v) => v ?? 0;
|
|
21066
|
+
return {
|
|
21067
|
+
user_id: r.user?.id,
|
|
21068
|
+
name: r.user?.name ?? null,
|
|
21069
|
+
email: r.user?.email ?? null,
|
|
21070
|
+
total_activities: n(r.total_activities),
|
|
21071
|
+
likes: n(r.likes),
|
|
21072
|
+
saves: n(r.saves),
|
|
21073
|
+
website_clicks: n(r.website),
|
|
21074
|
+
exported: n(r.exported),
|
|
21075
|
+
profile_views: n(r.lead_profile_views),
|
|
21076
|
+
contacts_added: n(r.create_lead_contact),
|
|
21077
|
+
contacts_purchased: n(r.purchase_lead_contact),
|
|
21078
|
+
notes: n(r.create_lead_note),
|
|
21079
|
+
// Epilogue outcomes logged in the window — the "deals" signal.
|
|
21080
|
+
meetings_or_interest: n(r.epilogue_interest_validated_or_meeting_planed),
|
|
21081
|
+
could_not_reach: n(r.epilogue_could_not_reach_still_trying),
|
|
21082
|
+
lost: n(r.epilogue_not_interested_lost),
|
|
21083
|
+
still_chasing: n(r.epilogue_still_chasing)
|
|
21084
|
+
};
|
|
21085
|
+
}
|
|
21086
|
+
var teamActivity = {
|
|
21087
|
+
name: "leadbay_team_activity",
|
|
21088
|
+
annotations: {
|
|
21089
|
+
title: "Team activity + per-rep KPIs",
|
|
21090
|
+
readOnlyHint: true,
|
|
21091
|
+
destructiveHint: false,
|
|
21092
|
+
idempotentHint: true,
|
|
21093
|
+
openWorldHint: true
|
|
21094
|
+
},
|
|
21095
|
+
description: leadbay_team_activity,
|
|
21096
|
+
write: false,
|
|
21097
|
+
inputSchema: {
|
|
21098
|
+
type: "object",
|
|
21099
|
+
properties: {
|
|
21100
|
+
weeks: { type: "number", description: "Look-back window in weeks (default 4). Ignored if from/to given." },
|
|
21101
|
+
from: { type: "string", description: "ISO date YYYY-MM-DD (start). Overrides weeks." },
|
|
21102
|
+
to: { type: "string", description: "ISO date YYYY-MM-DD (end). Overrides weeks." },
|
|
21103
|
+
periodicity: { type: "string", enum: ["DAILY", "WEEKLY"], description: "Trend bucketing (default WEEKLY)." },
|
|
21104
|
+
user_ids: {
|
|
21105
|
+
type: "array",
|
|
21106
|
+
items: { type: "string" },
|
|
21107
|
+
description: "Specific rep user-ids; omit for the whole team."
|
|
21108
|
+
}
|
|
21109
|
+
},
|
|
21110
|
+
additionalProperties: false
|
|
21111
|
+
},
|
|
21112
|
+
execute: async (client, params, _ctx) => {
|
|
21113
|
+
const periodicity = params.periodicity ?? "WEEKLY";
|
|
21114
|
+
const to = params.to ?? ymd(/* @__PURE__ */ new Date());
|
|
21115
|
+
const from = params.from ?? ymd(new Date(Date.parse(to) - Math.max(1, params.weeks ?? 4) * 7 * DAY_MS));
|
|
21116
|
+
const ids = params.user_ids && params.user_ids.length > 0 ? params.user_ids.join(",") : "ALL";
|
|
21117
|
+
const qs = `from=${from}&to=${to}&periodicity=${periodicity}&user_ids=${encodeURIComponent(ids)}`;
|
|
21118
|
+
const [usersRaw, trendRaw] = await Promise.all([
|
|
21119
|
+
client.request("GET", `/kpi/users?${qs}`),
|
|
21120
|
+
client.request("GET", `/kpi/trends?${qs}`)
|
|
21121
|
+
]);
|
|
21122
|
+
const reps = (usersRaw ?? []).map(mapRep).sort((a, b) => b.total_activities - a.total_activities);
|
|
21123
|
+
return {
|
|
21124
|
+
range: { from, to, periodicity },
|
|
21125
|
+
reps,
|
|
21126
|
+
trend: (trendRaw ?? []).map((t) => ({ date: t.date, count: t.count ?? 0 })),
|
|
21127
|
+
_meta: { region: client.region }
|
|
21128
|
+
};
|
|
21129
|
+
}
|
|
21130
|
+
};
|
|
21131
|
+
|
|
20963
21132
|
// ../core/dist/tools/send-feedback.js
|
|
20964
21133
|
var MESSAGE_MAX = 4e3;
|
|
20965
21134
|
var sendFeedback = {
|
|
@@ -21037,6 +21206,41 @@ var sendFeedback = {
|
|
|
21037
21206
|
}
|
|
21038
21207
|
};
|
|
21039
21208
|
|
|
21209
|
+
// ../core/dist/artifact-runtime.generated.js
|
|
21210
|
+
var ARTIFACT_KIT_VERSION = "0.3.1";
|
|
21211
|
+
var ARTIFACT_RUNTIME = '"use strict";(()=>{var _=Object.defineProperty;var k=(e,t,n)=>t in e?_(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var o=(e,t,n)=>k(e,typeof t!="symbol"?t+"":t,n);var T="0.3.1",c=class extends Error{constructor(n,i={}){super(n);o(this,"code");o(this,"raw");this.name="LbError",this.code=i.code,this.raw=i.raw}},p=null,f=3e4;function E(){let e=globalThis.cowork;return e&&typeof e.callMcpTool=="function"?(t,n)=>e.callMcpTool(t,n):null}function v(e){if(e&&typeof e=="object"&&"content"in e){let t=e.content;if(Array.isArray(t)&&t[0]&&typeof t[0].text=="string")return t[0].text}return null}function y(e){if(!e||typeof e!="object")return e;let t=e;if(t.isError)throw new c(v(e)??"tool call failed",{raw:e});if("structuredContent"in t&&t.structuredContent!=null)return t.structuredContent;let n=v(e);if(n!=null)try{return JSON.parse(n)}catch{return n}return e}function L(e){return e instanceof Error?e.message:String(e)}function m(e){let t=e instanceof c?e.code:void 0;return{message:L(e),unavailable:t==="unavailable",code:t}}function S(e={}){p=e.call??null,f=e.timeoutMs??3e4}async function w(e,t){if(!f||f<=0)return e;let n,i=new Promise((r,s)=>{n=setTimeout(()=>s(new c(`"${t}" timed out after ${f}ms`,{code:"timeout"})),f)});try{return await Promise.race([e,i])}finally{n&&clearTimeout(n)}}async function a(e,t={}){if(p)return y(await w(Promise.resolve(p(e,t)),e));let n=E();if(!n)throw new c("Leadbay bridge unavailable (window.cowork absent)",{code:"unavailable"});return y(await w(Promise.resolve(n(e,t)),e))}var d=class{constructor(){o(this,"subs",new Set)}subscribe(t){return this.subs.add(t),t(this),()=>this.subs.delete(t)}emit(){for(let t of this.subs)t(this)}};function A(e){return Array.isArray(e)?e.map(t=>t&&typeof t=="object"?t:{value:t,label:String(t)}):[]}var h=class extends d{constructor(n={}){super();o(this,"kind");o(this,"value");o(this,"options",[]);o(this,"loading",!1);o(this,"error",null);o(this,"ready",!1);o(this,"cfg");o(this,"depUnsubs",[]);o(this,"seq",0);this.cfg=n,this.kind=n.kind,this.value=n.value??"";for(let i of n.dependsOn??[]){let r=i.value;this.depUnsubs.push(i.subscribe(()=>{i.value!==r&&(r=i.value,this.cfg.load&&this.load())}))}n.load&&(n.autoLoad??!0)&&this.load()}async load(){if(!this.cfg.load)return;let n=++this.seq;this.loading=!0,this.error=null,this.emit();try{let i=await this.cfg.load();if(n!==this.seq)return;this.options=this.cfg.options?this.cfg.options(i):A(i),this.ready=!0;let r=this.value==null?"":String(this.value);this.options.length&&(r===""||!this.options.some(s=>String(s.value)===r))&&(this.value=this.options[0].value)}catch(i){if(n!==this.seq)return;this.options=[],this.error=m(i)}finally{n===this.seq&&(this.loading=!1,this.emit())}}setValue(n){this.value=n;let i=this.validate();this.error=i?{message:i,unavailable:!1}:null,this.emit()}validate(){return this.cfg.validate?this.cfg.validate(this.value):null}get valid(){return this.validate()==null}reset(){this.value=this.cfg.value??"",this.error=null,this.emit()}dispose(){for(let n of this.depUnsubs)n();this.depUnsubs=[]}},l=class extends d{constructor(n){super();o(this,"loading",!1);o(this,"error",null);o(this,"lastResult",null);o(this,"cfg");this.cfg=n}async run(){if(this.loading)return;for(let i of this.cfg.fields??[]){let r=i.validate();if(r!=null){this.error={message:r,unavailable:!1},this.emit();return}}if(this.cfg.confirm&&typeof globalThis.confirm=="function"&&!globalThis.confirm(this.cfg.confirm))return;this.loading=!0,this.error=null,this.emit();let n;try{let i=typeof this.cfg.args=="function"?this.cfg.args():this.cfg.args??{};n=await a(this.cfg.tool,i)}catch(i){this.error=m(i),this.loading=!1,this.emit(),this.cfg.onError?.(this.error);return}return this.lastResult=n,this.loading=!1,this.emit(),this.cfg.onSuccess?.(n),n}reset(){this.error=null,this.lastResult=null,this.emit()}},u=class extends d{constructor(n){super();o(this,"data",null);o(this,"loading",!1);o(this,"refreshing",!1);o(this,"error",null);o(this,"done",!1);o(this,"cfg");o(this,"timer",null);o(this,"seq",0);this.cfg=n,(n.autoLoad??!0)&&this.load()}async load(){this.clearTimer();let n=++this.seq;this.data==null?this.loading=!0:this.refreshing=!0,this.error=null,this.emit();try{let r=await this.cfg.load();if(n!==this.seq)return;this.data=r,this.done=this.cfg.until?this.cfg.until(r):!0,this.cfg.pollEvery&&!this.done&&(this.timer=setTimeout(()=>void this.load(),this.cfg.pollEvery))}catch(r){if(n!==this.seq)return;this.error=m(r)}finally{n===this.seq&&(this.loading=!1,this.refreshing=!1,this.emit())}}refresh(){return this.load()}stop(){this.clearTimer()}clearTimer(){this.timer&&(clearTimeout(this.timer),this.timer=null)}},b=class extends d{constructor(n){super();o(this,"items",[]);o(this,"page",0);o(this,"pageSize");o(this,"total",0);o(this,"loading",!1);o(this,"error",null);o(this,"cfg");o(this,"seq",0);this.cfg=n,this.pageSize=n.pageSize??20,(n.autoLoad??!0)&&this.loadPage(0)}async loadPage(n){let i=++this.seq;this.loading=!0,this.error=null,this.emit();try{let r=await this.cfg.load({page:n,pageSize:this.pageSize});if(i!==this.seq)return;this.items=r.items??[],this.total=r.total??this.items.length,this.page=n}catch(r){if(i!==this.seq)return;this.error=m(r)}finally{i===this.seq&&(this.loading=!1,this.emit())}}next(){return this.loadPage(this.page+1)}prev(){return this.loadPage(Math.max(0,this.page-1))}get hasMore(){return(this.page+1)*this.pageSize<this.total}};function x(e,t){let n=t.error?.unavailable?"unavailable":t.loading?"loading":t.error?"error":"ready";e.setAttribute("data-lb-state",n),t.error?e.setAttribute("data-lb-error",t.error.message):e.removeAttribute("data-lb-error")}function C(e,t){let n=()=>t.setValue(e.value);e.addEventListener("change",n);let i=t.subscribe(()=>{x(e,t),e.disabled=t.loading,e.innerHTML="";for(let r of t.options){let s=document.createElement("option");s.value=String(r.value),s.textContent=r.label,e.appendChild(s)}e.value=t.value==null?"":String(t.value)});return()=>{e.removeEventListener("change",n),i()}}function I(e,t){let n=e.type==="checkbox",i=e.tagName==="SELECT"?"change":"input",r=()=>t.setValue(n?e.checked:e.value);e.addEventListener(i,r);let s=t.subscribe(()=>{if(n)e.checked=!!t.value;else{let g=t.value==null?"":String(t.value);e.value!==g&&(e.value=g)}e.setAttribute("data-lb-state",t.error?"error":"ready"),t.error?e.setAttribute("data-lb-error",t.error.message):e.removeAttribute("data-lb-error")});return()=>{e.removeEventListener(i,r),s()}}function R(e,t){let n=r=>{r.preventDefault(),t.run()};e.addEventListener("click",n);let i=t.subscribe(()=>{let r=t.error?.unavailable?"unavailable":t.loading?"loading":t.error?"error":t.lastResult!=null?"success":"idle";e.setAttribute("data-lb-state",r),"disabled"in e&&(e.disabled=t.loading),t.error?e.setAttribute("data-lb-error",t.error.message):e.removeAttribute("data-lb-error")});return()=>{e.removeEventListener("click",n),i()}}var O=["STILL_CHASING","COULD_NOT_REACH_STILL_TRYING","INTEREST_VALIDATED_OR_MEETING_PLANED","NOT_INTERESTED_LOST"];function P(e){return new h({kind:"select",load:()=>a("leadbay_list_campaigns",{_triggered_by:e}),options:t=>(t?.campaigns??[]).map(i=>{let r=i?.campaign??i;return r?.id?{value:r.id,label:r.name??r.ai_generated_name??String(r.id)}:null}).filter(i=>i!=null)})}function M(e){return new l({tool:"leadbay_report_outreach",fields:e.note?[e.note]:[],args:()=>({lead_id:e.leadId,...e.status?{epilogue_status:e.status.value}:{},note:e.note?e.note.value:"",verification:{source:"user_confirmed",ref:e.ref??"logged from artifact"},_triggered_by:e.ask})})}function F(e){return new l({tool:"leadbay_add_note",fields:[e.note],args:()=>({leadId:e.leadId,note:e.note.value})})}function q(e){return new l({tool:"leadbay_like_lead",args:{lead_id:e}})}function H(e){return new l({tool:"leadbay_dislike_lead",args:{lead_id:e}})}function z(e,t){return new u({autoLoad:!1,load:()=>a("leadbay_account_history",{leadId:e,_triggered_by:t})})}function N(e,t){return new u({autoLoad:!1,load:()=>a("leadbay_research_lead_by_id",{leadId:e,_triggered_by:t})})}function U(e){let t=null;return new u({pollEvery:e.pollEvery??4e3,until:n=>!!n?.all_done,load:async()=>{if(!t){let n=await a("leadbay_enrich_titles",{...e.leadIds?{leadIds:e.leadIds}:{},titles:e.titles,email:e.email??!0,phone:e.phone??!1,_triggered_by:e.ask});if(t=n?.bulk_id??null,!t)return{all_done:!0,no_job:!0,mode:n?.mode,preview:n?.preview}}return a("leadbay_bulk_enrich_status",{bulk_id:t,_triggered_by:e.ask})}})}function D(e){let t=e.source??"followups";return new b({pageSize:e.pageSize??20,load:async({page:n,pageSize:i})=>{let s=t==="campaign"?await a("leadbay_campaign_call_sheet",{campaign_id:e.campaignId,page:n,count:i,_triggered_by:e.ask}):await a("leadbay_pull_followups",{page:n,count:i,...e.city?{city:e.city}:{},_triggered_by:e.ask}),g=s.leads??s.items??[];return{items:g,total:s.total_leads??s.pagination?.total??g.length}}})}function V(e){return new u({load:()=>a("leadbay_team_activity",{weeks:e.weeks??4,_triggered_by:e.ask})})}var j={VERSION:T,configure:S,call:a,field:e=>new h(e),action:e=>new l(e),resource:e=>new u(e),list:e=>new b(e),bindSelect:C,bindValue:I,bindAction:R,campaigns:P,outreach:M,note:F,like:q,dislike:H,leadHistory:z,leadProfile:N,enrichment:U,callList:D,teamActivity:V,EPILOGUE_STATUSES:O};typeof globalThis<"u"&&(globalThis.LeadbayArtifacts=j);})();';
|
|
21212
|
+
var ARTIFACT_USAGE_GUIDE = '# Leadbay Artifact Kit \u2014 headless domain components\n\nYou are building a single-file HTML **artifact** the user runs inside cowork. This\nkit gives you **headless view-models** that own a control\'s whole data lifecycle \u2014\nload/populate from a Leadbay call, hold value/state, poll, validate, and\nencapsulate the API call + business rules. **You own 100% of markup/layout/style.**\nThe library renders nothing. Inline the runtime once as a `<script>`; it exposes\none global `window.LeadbayArtifacts` (call it `lb`). Vanilla, no React, no build.\n\nPass every tool you use as the artifact\'s `mcp_tools` so the host permits it.\n\n## Two layers\n\n**Primitives** (generic):\n- `lb.field({ load, options, value, validate, dependsOn })` \u2014 a value + optionally\n API-populated options. `.value/.setValue/.options/.loading/.error/.valid/.subscribe`.\n- `lb.action({ tool, args, fields, confirm, onSuccess, onError })` \u2014 a write/submit.\n `.run()/.loading/.error/.lastResult/.subscribe`.\n- `lb.resource({ load, pollEvery?, until?, autoLoad? })` \u2014 one read that may change:\n load-on-click or poll-until-`until`. `.data/.loading/.refreshing/.error/.done/.load()/.refresh()/.stop()/.subscribe`.\n- `lb.list({ load, pageSize })` \u2014 paginated rows. `.items/.page/.total/.loading/.loadPage(n)/.next()/.prev()/.hasMore/.subscribe`.\n\n`.error` is `{ message, unavailable } | null`. `subscribe(cb)` fires immediately\nthen on every change \u2014 render your own DOM from it.\n\n**Domain components** (pre-wired \u2014 bake in the tool name, arg shape, and footguns):\n\n| Call | Returns | For |\n|---|---|---|\n| `lb.campaigns(ask)` | field | a campaign `<select>`, options from `leadbay_list_campaigns` |\n| `lb.outreach({leadId, ask, status?, note?})` | action | log a call \u2192 `report_outreach` (verification + `_triggered_by` baked in) |\n| `lb.note({leadId, note})` | action | add a note \u2192 `add_note` |\n| `lb.like(leadId)` / `lb.dislike(leadId)` | action | taste signal |\n| `lb.leadHistory(leadId, ask)` | resource (lazy) | notes + activities + engagement \u2192 `account_history` |\n| `lb.leadProfile(leadId, ask)` | resource (lazy) | full lead profile \u2192 `research_lead_by_id` |\n| `lb.callList({source:\'followups\'\\|\'campaign\', campaignId?, city?, ask})` | list | a cold-call list (Monitor or a campaign) |\n| `lb.enrichment({leadIds, titles, ask, pollEvery?})` | resource (polling) | launch + watch contact enrichment |\n| `lb.teamActivity({weeks, ask})` | resource | manager leaderboard + activity trend \u2192 `leadbay_team_activity` |\n\n`lb.EPILOGUE_STATUSES` = the 4 disposition values\n(`STILL_CHASING`, `COULD_NOT_REACH_STILL_TRYING`, `INTEREST_VALIDATED_OR_MEETING_PLANED`, `NOT_INTERESTED_LOST`).\n\n**Binding sugar** (optional; binds a view-model to YOUR native element, no style):\n`lb.bindSelect(selectEl, field)` (populates options + value), `lb.bindValue(inputEl, field)`,\n`lb.bindAction(buttonEl, action)`. They set `data-lb-state`\n(`ready|loading|error|success|unavailable`) + `data-lb-error` on your element as\nstyling hooks. For lists/resources, use `.subscribe()` and render yourself.\n\n`ask` is the user\'s request this artifact serves \u2014 it becomes `_triggered_by`.\n\n## Recipe: cold-call sheet (one row per lead)\n\n```js\nconst lb = window.LeadbayArtifacts; lb.configure();\nconst ASK = "<the user\'s request>";\n\nconst list = lb.callList({ source: "campaign", campaignId: CID, ask: ASK });\nlist.subscribe((l) => renderRows(l.items, l.loading)); // your render\n\n// per lead row (call when you build a row):\nfunction wireRow(lead, els) {\n const status = lb.field({ value: "STILL_CHASING" }); // static-enum <select>\n const note = lb.field({ validate: (v) => (v && v.trim() ? null : "Add a note") });\n lb.bindValue(els.status, status);\n lb.bindValue(els.note, note);\n lb.bindAction(els.log, lb.outreach({ leadId: lead.id, ask: ASK, status, note }));\n lb.bindAction(els.like, lb.like(lead.id));\n\n const history = lb.leadHistory(lead.id, ASK); // lazy\n history.subscribe((h) => renderHistory(els.history, h));\n els.expand.onclick = () => history.load(); // load on click\n}\n```\n\n## Recipe: manager dashboard\n\n```js\nconst team = lb.teamActivity({ weeks: 4, ask: ASK });\nteam.subscribe((t) => {\n if (t.loading) showSpinner();\n if (t.data) {\n renderLeaderboard(t.data.reps); // sorted by total_activities; cols: name, notes, meetings_or_interest, lost\u2026\n renderTrendChart(t.data.trend); // [{date,count}] \u2192 Chart.js (allowed from CDN)\n }\n});\nrefreshBtn.onclick = () => team.refresh();\n```\n\n## Recipe: live enrichment\n\n```js\nconst job = lb.enrichment({ leadIds: [LEAD], titles: ["CEO", "VP Sales"], ask: ASK });\njob.subscribe((j) => {\n const p = j.data && j.data.overall_progress; // {done,total,done_ratio}\n renderBar(p);\n if (j.done) renderContacts(j.data.leads); // enriched contacts\n});\nrefreshBtn.onclick = () => job.refresh();\n```\n\n## Write-call rules\n\nThe domain factories handle these for you. If you hand-roll an action:\n`leadbay_report_outreach` args MUST include `verification:{source:"user_confirmed", ref}`\nAND `_triggered_by`; `leadbay_add_leads_to_campaign` needs `_triggered_by`;\n`add_note`/`like_lead`/`dislike_lead` take only their own args. `epilogue_status` is\none of `lb.EPILOGUE_STATUSES`. Snoozing (pushback) and org WON/LOST status are\nadvanced-gated \u2014 not callable from a default artifact; use the epilogue values.\n\n## Degradation + live updates\n\nIf the host bridge is absent, a view-model\'s `.error` is set with `.error.unavailable\n=== true` (bind helpers set `data-lb-state="unavailable"`) \u2014 nothing throws. Every\ncall also has a **30s timeout** (configurable via `lb.configure({ timeoutMs })`): a\nhost call that never settles becomes `.error` with `code:"timeout"`, so a control is\nnever stuck loading forever \u2014 always render the `.error` branch so the user can retry.\nAuto-poll (`pollEvery`) depends on the cowork host serving FRESH reads; `.refresh()`\nis the guaranteed manual path \u2014 always wire a Refresh control for polling resources.';
|
|
21213
|
+
|
|
21214
|
+
// ../core/dist/tools/artifact-kit.js
|
|
21215
|
+
var artifactKit = {
|
|
21216
|
+
name: "leadbay_artifact_kit",
|
|
21217
|
+
annotations: {
|
|
21218
|
+
title: "Artifact component kit",
|
|
21219
|
+
readOnlyHint: true,
|
|
21220
|
+
destructiveHint: false,
|
|
21221
|
+
idempotentHint: true,
|
|
21222
|
+
openWorldHint: false
|
|
21223
|
+
},
|
|
21224
|
+
description: leadbay_artifact_kit,
|
|
21225
|
+
write: false,
|
|
21226
|
+
inputSchema: {
|
|
21227
|
+
type: "object",
|
|
21228
|
+
properties: {},
|
|
21229
|
+
additionalProperties: false
|
|
21230
|
+
},
|
|
21231
|
+
// No outputSchema by design: the return is three opaque strings. Declaring a
|
|
21232
|
+
// schema would enroll the tool in the output-schema-conformance drift-catcher
|
|
21233
|
+
// (an existing test file we don't modify). The server still emits the
|
|
21234
|
+
// plain-object return as structuredContent: { runtime, usage_guide, version }.
|
|
21235
|
+
execute: async (_client, _params, _ctx) => {
|
|
21236
|
+
return {
|
|
21237
|
+
version: ARTIFACT_KIT_VERSION,
|
|
21238
|
+
runtime: ARTIFACT_RUNTIME,
|
|
21239
|
+
usage_guide: ARTIFACT_USAGE_GUIDE
|
|
21240
|
+
};
|
|
21241
|
+
}
|
|
21242
|
+
};
|
|
21243
|
+
|
|
21040
21244
|
// ../core/dist/index.js
|
|
21041
21245
|
var agentMemoryTools = [
|
|
21042
21246
|
agentMemoryRecall,
|
|
@@ -21121,6 +21325,11 @@ var compositeReadTools = [
|
|
|
21121
21325
|
// answer to "which of my leads have signal X" that previously forced a
|
|
21122
21326
|
// per-lead research_lead_by_id loop (issue #3704).
|
|
21123
21327
|
scanPortfolioSignals,
|
|
21328
|
+
// Team activity — manager-facing per-rep leaderboard + activity trend (wraps
|
|
21329
|
+
// /kpi/users + /kpi/trends, the web Dashboard-Manager surface). Read-only;
|
|
21330
|
+
// always exposed so a manager artifact can render team KPIs. Non-admins are
|
|
21331
|
+
// scoped to themselves by the backend.
|
|
21332
|
+
teamActivity,
|
|
21124
21333
|
recallOrderedTitles,
|
|
21125
21334
|
accountStatus,
|
|
21126
21335
|
bulkEnrichStatus,
|
|
@@ -21159,7 +21368,13 @@ var compositeReadTools = [
|
|
|
21159
21368
|
// every tool response regardless of write gating; without ack the agent
|
|
21160
21369
|
// sees the same entries on every call forever. Pairing the surfacing
|
|
21161
21370
|
// channel with the clearing tool is non-optional.
|
|
21162
|
-
acknowledgeNotification
|
|
21371
|
+
acknowledgeNotification,
|
|
21372
|
+
// Artifact kit — ALWAYS exposed, read-only. Returns the headless
|
|
21373
|
+
// @leadbay/components runtime + usage guide the agent uses to BUILD an
|
|
21374
|
+
// interactive HTML artifact (cold-call sheet, lead-triage board) whose
|
|
21375
|
+
// buttons call Leadbay writes. No backend call; granular-shaped (lives in
|
|
21376
|
+
// tools/) so it carries no _triggered_by mandate for a kit fetch.
|
|
21377
|
+
artifactKit
|
|
21163
21378
|
];
|
|
21164
21379
|
var compositeWriteTools = [
|
|
21165
21380
|
bulkQualifyLeads,
|
|
@@ -22616,7 +22831,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
22616
22831
|
}
|
|
22617
22832
|
|
|
22618
22833
|
// src/http-server.ts
|
|
22619
|
-
var VERSION = true ? "0.
|
|
22834
|
+
var VERSION = true ? "0.22.0" : "0.0.0-dev";
|
|
22620
22835
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
22621
22836
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
22622
22837
|
var sseSessions = /* @__PURE__ */ new Map();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leadbay/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"mcpName": "io.github.leadbay/leadbay-mcp",
|
|
5
5
|
"description": "Model Context Protocol (MCP) server for Leadbay — AI lead discovery, qualification, and enrichment for Claude Desktop, Cursor, and Claude Code.",
|
|
6
6
|
"type": "module",
|