@leadbay/mcp 0.39.2 → 0.39.4
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/dist/bin.js +258 -40
- package/dist/http-server.js +250 -40
- package/dist/installer-electron.js +1 -1
- package/dist/installer-gui.js +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -14249,8 +14249,8 @@ function summarise(responses) {
|
|
|
14249
14249
|
return { answered, total, avg_qualification_boost: avg, best_response_excerpt: excerpt };
|
|
14250
14250
|
}
|
|
14251
14251
|
function buildPullLeadsNextSteps(args) {
|
|
14252
|
-
const { leadCount, hasMore, nextPage, computingWishlist, computingScores } = args;
|
|
14253
|
-
if (
|
|
14252
|
+
const { leadCount: leadCount2, hasMore, nextPage, computingWishlist, computingScores } = args;
|
|
14253
|
+
if (leadCount2 <= 0) {
|
|
14254
14254
|
if (computingWishlist || computingScores) {
|
|
14255
14255
|
return {
|
|
14256
14256
|
question: "This lens is still warming up. What next?",
|
|
@@ -14503,15 +14503,15 @@ var init_pull_leads = __esm({
|
|
|
14503
14503
|
const currentPage = res.pagination?.page ?? page;
|
|
14504
14504
|
const hasMore = currentPage < totalPages - 1;
|
|
14505
14505
|
const nextPage = hasMore ? currentPage + 1 : null;
|
|
14506
|
-
const
|
|
14506
|
+
const leadCount2 = res.items.length;
|
|
14507
14507
|
const nextSteps = buildPullLeadsNextSteps({
|
|
14508
|
-
leadCount,
|
|
14508
|
+
leadCount: leadCount2,
|
|
14509
14509
|
hasMore,
|
|
14510
14510
|
nextPage,
|
|
14511
14511
|
computingWishlist: res.computing_wishlist,
|
|
14512
14512
|
computingScores: res.computing_scores
|
|
14513
14513
|
});
|
|
14514
|
-
const lensIsEmpty =
|
|
14514
|
+
const lensIsEmpty = leadCount2 === 0 && (res.pagination?.total ?? 0) === 0;
|
|
14515
14515
|
const emptyReason = lensIsEmpty ? await diagnoseEmptyLens(client, lensId, {
|
|
14516
14516
|
wishlist: res.computing_wishlist,
|
|
14517
14517
|
scores: res.computing_scores
|
|
@@ -14970,8 +14970,16 @@ function townName(value) {
|
|
|
14970
14970
|
}
|
|
14971
14971
|
function cityHintCore(cityHint) {
|
|
14972
14972
|
const head = cityHint.split(",")[0] ?? "";
|
|
14973
|
-
const
|
|
14974
|
-
|
|
14973
|
+
const segments = cityHint.split(",").map((part) => part.trim()).filter(Boolean);
|
|
14974
|
+
for (let take = segments.length; take > 0; take -= 1) {
|
|
14975
|
+
const candidate = segments.slice(0, take).join(" ");
|
|
14976
|
+
const expanded = expandAlias(candidate);
|
|
14977
|
+
if (expanded !== candidate) {
|
|
14978
|
+
return { name: townName(expanded), isCity: true };
|
|
14979
|
+
}
|
|
14980
|
+
}
|
|
14981
|
+
const prefixed = ADMIN_PREFIX.test(normalizeGeo(head));
|
|
14982
|
+
return { name: townName(head), isCity: prefixed };
|
|
14975
14983
|
}
|
|
14976
14984
|
function stateFieldOf(lead) {
|
|
14977
14985
|
const state = lead?.location?.state;
|
|
@@ -20808,27 +20816,167 @@ var init_bulk_enrich_status = __esm({
|
|
|
20808
20816
|
}
|
|
20809
20817
|
});
|
|
20810
20818
|
|
|
20811
|
-
// ../core/dist/composite/
|
|
20812
|
-
function
|
|
20813
|
-
|
|
20814
|
-
return [];
|
|
20815
|
-
return s.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean);
|
|
20819
|
+
// ../core/dist/composite/_sector-resolver.js
|
|
20820
|
+
function normalizeSectorText(value) {
|
|
20821
|
+
return (value ?? "").normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^0-9a-z]+/g, " ").trim();
|
|
20816
20822
|
}
|
|
20817
|
-
function
|
|
20818
|
-
|
|
20819
|
-
|
|
20820
|
-
|
|
20821
|
-
|
|
20822
|
-
|
|
20823
|
+
function singular(token) {
|
|
20824
|
+
if (token.length > 4 && token.endsWith("ies"))
|
|
20825
|
+
return `${token.slice(0, -3)}y`;
|
|
20826
|
+
return token.length > 3 && token.endsWith("s") ? token.slice(0, -1) : token;
|
|
20827
|
+
}
|
|
20828
|
+
function sectorTokens(value) {
|
|
20829
|
+
const all = normalizeSectorText(value).split(" ").filter(Boolean).map(singular);
|
|
20830
|
+
const core = all.filter((t) => !FILLER.has(t));
|
|
20831
|
+
return core.length > 0 ? core : all;
|
|
20832
|
+
}
|
|
20833
|
+
function sectorLabel(row) {
|
|
20834
|
+
if (!row)
|
|
20835
|
+
return "";
|
|
20836
|
+
const label = row.label;
|
|
20837
|
+
if (typeof label === "string" && label.length > 0)
|
|
20838
|
+
return label;
|
|
20839
|
+
return typeof row.name === "string" ? row.name : "";
|
|
20840
|
+
}
|
|
20841
|
+
function leadCount(row) {
|
|
20842
|
+
const n = row.number_of_leads;
|
|
20843
|
+
return typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
20844
|
+
}
|
|
20845
|
+
function rankCandidates(scored, tieBreak) {
|
|
20846
|
+
const out = [];
|
|
20847
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20848
|
+
scored.sort((a, b) => b.score - a.score || (tieBreak === "breadth" ? leadCount(b.row) - leadCount(a.row) || b.closeness - a.closeness : b.closeness - a.closeness || leadCount(b.row) - leadCount(a.row)) || sectorLabel(a.row).length - sectorLabel(b.row).length).forEach(({ row, score }) => {
|
|
20849
|
+
const label = sectorLabel(row);
|
|
20850
|
+
if (!label || seen.has(label) || out.length >= MAX_CANDIDATES)
|
|
20851
|
+
return;
|
|
20852
|
+
seen.add(label);
|
|
20853
|
+
out.push({ id: row.id, name: label, score: Number(score.toFixed(2)) });
|
|
20854
|
+
});
|
|
20855
|
+
return out;
|
|
20856
|
+
}
|
|
20857
|
+
function matchSector(text3, taxonomy) {
|
|
20858
|
+
const wanted = normalizeSectorText(text3);
|
|
20859
|
+
if (!wanted)
|
|
20860
|
+
return { kind: "unresolved", candidates: [] };
|
|
20861
|
+
const exact = taxonomy.filter((row) => normalizeSectorText(sectorLabel(row)) === wanted);
|
|
20862
|
+
if (exact.length > 0) {
|
|
20863
|
+
const label = sectorLabel(exact.reduce((best, row) => leadCount(row) > leadCount(best) ? row : best));
|
|
20864
|
+
const ids = taxonomy.filter((row) => sectorLabel(row) === label).map((row) => row.id);
|
|
20865
|
+
return { kind: "resolved", label, ids, exact: true };
|
|
20866
|
+
}
|
|
20867
|
+
const want = new Set(sectorTokens(text3));
|
|
20868
|
+
const sameWords = taxonomy.filter((row) => {
|
|
20869
|
+
const have = new Set(sectorTokens(sectorLabel(row)));
|
|
20870
|
+
return have.size === want.size && [...want].every((token) => have.has(token));
|
|
20871
|
+
});
|
|
20872
|
+
const sameWordLabels = new Set(sameWords.map((row) => sectorLabel(row)).filter(Boolean));
|
|
20873
|
+
if (sameWordLabels.size === 1) {
|
|
20874
|
+
const label = [...sameWordLabels][0];
|
|
20875
|
+
const ids = taxonomy.filter((row) => sectorLabel(row) === label).map((row) => row.id);
|
|
20876
|
+
return { kind: "resolved", label, ids, exact: false };
|
|
20877
|
+
}
|
|
20878
|
+
const scored = [];
|
|
20879
|
+
for (const row of taxonomy) {
|
|
20880
|
+
const have = new Set(sectorTokens(sectorLabel(row)));
|
|
20881
|
+
if (have.size === 0)
|
|
20882
|
+
continue;
|
|
20823
20883
|
let overlap = 0;
|
|
20824
|
-
for (const
|
|
20825
|
-
if (have.has(
|
|
20884
|
+
for (const token of want)
|
|
20885
|
+
if (have.has(token))
|
|
20826
20886
|
overlap += 1;
|
|
20827
|
-
|
|
20828
|
-
|
|
20829
|
-
|
|
20830
|
-
|
|
20887
|
+
if (overlap === 0)
|
|
20888
|
+
continue;
|
|
20889
|
+
scored.push({ row, score: overlap / want.size, closeness: overlap / have.size });
|
|
20890
|
+
}
|
|
20891
|
+
const full = scored.filter((s) => s.score >= 1);
|
|
20892
|
+
return {
|
|
20893
|
+
kind: "unresolved",
|
|
20894
|
+
candidates: full.length > 0 ? rankCandidates(full, "breadth") : rankCandidates(scored, "closeness").slice(0, MAX_PARTIAL_CANDIDATES)
|
|
20895
|
+
};
|
|
20896
|
+
}
|
|
20897
|
+
function sectorSections(taxonomy) {
|
|
20898
|
+
const roots = taxonomy.filter((row) => !row.parent);
|
|
20899
|
+
const labels = [];
|
|
20900
|
+
for (const row of roots.sort((a, b) => leadCount(b) - leadCount(a))) {
|
|
20901
|
+
const label = sectorLabel(row);
|
|
20902
|
+
if (label && !labels.includes(label))
|
|
20903
|
+
labels.push(label);
|
|
20904
|
+
}
|
|
20905
|
+
return labels;
|
|
20906
|
+
}
|
|
20907
|
+
async function fetchSectorTaxonomy(client, ctx) {
|
|
20908
|
+
const me = await client.resolveMe().catch(() => null);
|
|
20909
|
+
const lang = me?.language ?? "en";
|
|
20910
|
+
const taxonomy = await client.request("GET", `/sectors/all?lang=${encodeURIComponent(lang)}&includeInvisible=false`);
|
|
20911
|
+
const unlabelled = taxonomy.filter((row) => !sectorLabel(row)).length;
|
|
20912
|
+
if (unlabelled > 0) {
|
|
20913
|
+
ctx?.logger?.warn?.(`sector taxonomy: ${unlabelled}/${taxonomy.length} row(s) carry no label`);
|
|
20914
|
+
}
|
|
20915
|
+
return taxonomy;
|
|
20916
|
+
}
|
|
20917
|
+
function resolveSectorValues(values, taxonomy) {
|
|
20918
|
+
const out = {
|
|
20919
|
+
values: [],
|
|
20920
|
+
rewritten: [],
|
|
20921
|
+
unresolved: [],
|
|
20922
|
+
sections: sectorSections(taxonomy)
|
|
20923
|
+
};
|
|
20924
|
+
for (const raw of values) {
|
|
20925
|
+
const asked = typeof raw === "string" ? raw : String(raw ?? "");
|
|
20926
|
+
if (NUMERIC_ID.test(asked.trim())) {
|
|
20927
|
+
out.values.push(asked.trim());
|
|
20928
|
+
continue;
|
|
20929
|
+
}
|
|
20930
|
+
const match = matchSector(asked, taxonomy);
|
|
20931
|
+
if (match.kind === "resolved") {
|
|
20932
|
+
out.values.push(match.label);
|
|
20933
|
+
if (match.label !== asked)
|
|
20934
|
+
out.rewritten.push({ asked, used: match.label });
|
|
20935
|
+
continue;
|
|
20936
|
+
}
|
|
20937
|
+
out.values.push(asked);
|
|
20938
|
+
out.unresolved.push({ asked, closest: match.candidates });
|
|
20939
|
+
}
|
|
20940
|
+
return out;
|
|
20831
20941
|
}
|
|
20942
|
+
var FILLER, MAX_CANDIDATES, MAX_PARTIAL_CANDIDATES, NUMERIC_ID;
|
|
20943
|
+
var init_sector_resolver = __esm({
|
|
20944
|
+
"../core/dist/composite/_sector-resolver.js"() {
|
|
20945
|
+
"use strict";
|
|
20946
|
+
FILLER = /* @__PURE__ */ new Set([
|
|
20947
|
+
// en
|
|
20948
|
+
"and",
|
|
20949
|
+
"of",
|
|
20950
|
+
"the",
|
|
20951
|
+
"for",
|
|
20952
|
+
"or",
|
|
20953
|
+
"activity",
|
|
20954
|
+
// fr
|
|
20955
|
+
"et",
|
|
20956
|
+
"de",
|
|
20957
|
+
"des",
|
|
20958
|
+
"du",
|
|
20959
|
+
"d",
|
|
20960
|
+
"la",
|
|
20961
|
+
"le",
|
|
20962
|
+
"les",
|
|
20963
|
+
"l",
|
|
20964
|
+
"en",
|
|
20965
|
+
"aux",
|
|
20966
|
+
"au",
|
|
20967
|
+
"activite",
|
|
20968
|
+
"pour",
|
|
20969
|
+
"sur",
|
|
20970
|
+
"par",
|
|
20971
|
+
"a"
|
|
20972
|
+
]);
|
|
20973
|
+
MAX_CANDIDATES = 8;
|
|
20974
|
+
MAX_PARTIAL_CANDIDATES = 4;
|
|
20975
|
+
NUMERIC_ID = /^\d+$/;
|
|
20976
|
+
}
|
|
20977
|
+
});
|
|
20978
|
+
|
|
20979
|
+
// ../core/dist/composite/adjust-audience.js
|
|
20832
20980
|
async function resolveSectors(client, texts, ctx) {
|
|
20833
20981
|
const looksLikeId2 = (s) => /^\d+$/.test(s);
|
|
20834
20982
|
const direct = texts.filter(looksLikeId2);
|
|
@@ -20838,18 +20986,18 @@ async function resolveSectors(client, texts, ctx) {
|
|
|
20838
20986
|
const me = await client.resolveMe().catch(() => null);
|
|
20839
20987
|
const lang = me?.language ?? "en";
|
|
20840
20988
|
const taxonomy = await client.request("GET", `/sectors/all?lang=${encodeURIComponent(lang)}&includeInvisible=false`);
|
|
20841
|
-
const nullNames = taxonomy.filter((s) => !s
|
|
20989
|
+
const nullNames = taxonomy.filter((s) => !sectorLabel(s)).length;
|
|
20842
20990
|
if (nullNames > 0) {
|
|
20843
20991
|
ctx?.logger?.warn?.(`adjust_audience: /sectors/all returned ${nullNames}/${taxonomy.length} sector(s) with a null/missing name`);
|
|
20844
20992
|
}
|
|
20845
20993
|
const resolved = [...direct];
|
|
20846
20994
|
const ambiguities = [];
|
|
20847
20995
|
for (const text3 of free) {
|
|
20848
|
-
const
|
|
20849
|
-
if (
|
|
20850
|
-
resolved.push(
|
|
20996
|
+
const match = matchSector(text3, taxonomy);
|
|
20997
|
+
if (match.kind === "resolved") {
|
|
20998
|
+
resolved.push(...match.ids);
|
|
20851
20999
|
} else {
|
|
20852
|
-
ambiguities.push({ sector_text: text3, matches });
|
|
21000
|
+
ambiguities.push({ sector_text: text3, matches: match.candidates });
|
|
20853
21001
|
}
|
|
20854
21002
|
}
|
|
20855
21003
|
return { resolved, ambiguities };
|
|
@@ -20956,6 +21104,7 @@ var init_adjust_audience = __esm({
|
|
|
20956
21104
|
"use strict";
|
|
20957
21105
|
init_geo_helpers();
|
|
20958
21106
|
init_country_guard();
|
|
21107
|
+
init_sector_resolver();
|
|
20959
21108
|
init_tool_descriptions_generated();
|
|
20960
21109
|
adjustAudience = {
|
|
20961
21110
|
name: "leadbay_adjust_audience",
|
|
@@ -22173,12 +22322,68 @@ function sortFilterLists(filters) {
|
|
|
22173
22322
|
}
|
|
22174
22323
|
return out;
|
|
22175
22324
|
}
|
|
22325
|
+
function isSectorRejection(err) {
|
|
22326
|
+
const e = err;
|
|
22327
|
+
return !!e && e.code === "BAD_INPUT" && typeof e.message === "string" && e.message.includes("filters.sectors");
|
|
22328
|
+
}
|
|
22329
|
+
async function submitSearch(client, body, opts, ctx) {
|
|
22330
|
+
try {
|
|
22331
|
+
return {
|
|
22332
|
+
ok: true,
|
|
22333
|
+
data: await client.request("POST", "/mcp/search", body, opts),
|
|
22334
|
+
rewritten: []
|
|
22335
|
+
};
|
|
22336
|
+
} catch (err) {
|
|
22337
|
+
const asked = body.filters?.sectors;
|
|
22338
|
+
if (!isSectorRejection(err) || !Array.isArray(asked) || asked.length === 0) {
|
|
22339
|
+
throw err;
|
|
22340
|
+
}
|
|
22341
|
+
const taxonomy = await fetchSectorTaxonomy(client, ctx).catch(() => {
|
|
22342
|
+
throw err;
|
|
22343
|
+
});
|
|
22344
|
+
const fix = resolveSectorValues(asked, taxonomy);
|
|
22345
|
+
if (fix.unresolved.length === 0 && fix.rewritten.length === 0)
|
|
22346
|
+
throw err;
|
|
22347
|
+
if (fix.unresolved.length > 0)
|
|
22348
|
+
return { ok: false, choice: sectorChoice(fix) };
|
|
22349
|
+
const retried = { ...body, filters: { ...body.filters, sectors: fix.values } };
|
|
22350
|
+
return {
|
|
22351
|
+
ok: true,
|
|
22352
|
+
data: await client.request("POST", "/mcp/search", retried, opts),
|
|
22353
|
+
rewritten: fix.rewritten
|
|
22354
|
+
};
|
|
22355
|
+
}
|
|
22356
|
+
}
|
|
22357
|
+
function sectorNote(rewritten) {
|
|
22358
|
+
if (rewritten.length === 0)
|
|
22359
|
+
return void 0;
|
|
22360
|
+
const pairs = rewritten.map((r) => `"${r.asked}" \u2192 "${r.used}"`).join(", ");
|
|
22361
|
+
return {
|
|
22362
|
+
sectors_note: `Leadbay's sector taxonomy spells these differently, so the search ran on ${pairs}. Tell the user which sector was searched.`,
|
|
22363
|
+
sectors_used: rewritten
|
|
22364
|
+
};
|
|
22365
|
+
}
|
|
22366
|
+
function sectorChoice(fix) {
|
|
22367
|
+
const names = fix.unresolved.map((u) => `"${u.asked}"`).join(", ");
|
|
22368
|
+
return {
|
|
22369
|
+
mode: "needs_sector_choice",
|
|
22370
|
+
submitted: false,
|
|
22371
|
+
unresolved_sectors: fix.unresolved.map((u) => ({
|
|
22372
|
+
asked: u.asked,
|
|
22373
|
+
closest: u.closest.map((c) => c.name)
|
|
22374
|
+
})),
|
|
22375
|
+
sector_sections: fix.sections,
|
|
22376
|
+
resolved_sectors: fix.rewritten,
|
|
22377
|
+
hint: `No sector in this workspace's taxonomy is named ${names || "that"}. Nothing was submitted and nothing was spent. Re-call with a label copied character for character from \`closest\` or \`sector_sections\` (a section covers everything under it), or drop \`filters.sectors\` and put the wording in \`query\` / \`example_lead.description\` instead \u2014 the search then RANKS on it rather than fencing on it, which is usually what the user meant. \`leadbay_list_sectors\` returns the full taxonomy if none of these fit.`
|
|
22378
|
+
};
|
|
22379
|
+
}
|
|
22176
22380
|
var DEFAULT_WAIT_SECONDS, findNewLeads;
|
|
22177
22381
|
var init_find_new_leads = __esm({
|
|
22178
22382
|
"../core/dist/composite/find-new-leads.js"() {
|
|
22179
22383
|
"use strict";
|
|
22180
22384
|
init_mcp_job_helpers();
|
|
22181
22385
|
init_country_guard();
|
|
22386
|
+
init_sector_resolver();
|
|
22182
22387
|
init_tool_descriptions_generated();
|
|
22183
22388
|
DEFAULT_WAIT_SECONDS = 45;
|
|
22184
22389
|
findNewLeads = {
|
|
@@ -22219,7 +22424,7 @@ var init_find_new_leads = __esm({
|
|
|
22219
22424
|
},
|
|
22220
22425
|
filters: {
|
|
22221
22426
|
type: "object",
|
|
22222
|
-
description: "HARD constraints (the seed only shapes ranking). Sector/location labels resolve at submit;
|
|
22427
|
+
description: "HARD constraints (the seed only shapes ranking). Sector/location labels resolve at submit. `sectors` must be a label from Leadbay's own taxonomy (leadbay_list_sectors) \u2014 everyday wording like 'Professional Services' is not one; a spelling/plural slip is corrected for you, but a word with no label comes back as mode:'needs_sector_choice' with the labels to pick from. An unresolvable location is a 400 naming it.",
|
|
22223
22428
|
properties: {
|
|
22224
22429
|
sectors: { type: "array", items: { type: "string" } },
|
|
22225
22430
|
locations: { type: "array", items: { type: "string" } },
|
|
@@ -22391,19 +22596,27 @@ var init_find_new_leads = __esm({
|
|
|
22391
22596
|
dry_run: dryRun
|
|
22392
22597
|
});
|
|
22393
22598
|
if (dryRun === true) {
|
|
22394
|
-
const
|
|
22599
|
+
const attempt2 = await submitSearch(client, body, void 0, ctx);
|
|
22600
|
+
if (!attempt2.ok)
|
|
22601
|
+
return { ...attempt2.choice, ...countryNote, region: client.region };
|
|
22395
22602
|
return {
|
|
22396
22603
|
dry_run: true,
|
|
22397
|
-
...
|
|
22604
|
+
...attempt2.data,
|
|
22605
|
+
...sectorNote(attempt2.rewritten),
|
|
22398
22606
|
...countryNote,
|
|
22399
22607
|
region: client.region
|
|
22400
22608
|
};
|
|
22401
22609
|
}
|
|
22402
22610
|
if (isPaid && !consented) {
|
|
22403
|
-
|
|
22404
|
-
|
|
22405
|
-
|
|
22406
|
-
|
|
22611
|
+
let forecast = null;
|
|
22612
|
+
let quoteRewritten = [];
|
|
22613
|
+
if (!vetoed) {
|
|
22614
|
+
const attempt2 = await submitSearch(client, { ...body, dry_run: true }, void 0, ctx);
|
|
22615
|
+
if (!attempt2.ok)
|
|
22616
|
+
return { ...attempt2.choice, ...countryNote, region: client.region };
|
|
22617
|
+
forecast = attempt2.data;
|
|
22618
|
+
quoteRewritten = attempt2.rewritten;
|
|
22619
|
+
}
|
|
22407
22620
|
return {
|
|
22408
22621
|
mode: "needs_confirmation",
|
|
22409
22622
|
submitted: false,
|
|
@@ -22416,11 +22629,15 @@ var init_find_new_leads = __esm({
|
|
|
22416
22629
|
estimated_cost: forecast?.estimated_cost ?? null,
|
|
22417
22630
|
items_requested: forecast?.items_requested ?? null,
|
|
22418
22631
|
hint: vetoed ? "confirm:false vetoed the run \u2014 nothing was submitted. Re-call with confirm:true to proceed, or drop qualify/channels for a free search." : "Tell the user what will run and that it uses their plan's quota (no amounts, no money), get an explicit go-ahead, then re-call with confirm:true. For a free search instead: omit qualify and channels.",
|
|
22632
|
+
...sectorNote(quoteRewritten),
|
|
22419
22633
|
...countryNote,
|
|
22420
22634
|
region: client.region
|
|
22421
22635
|
};
|
|
22422
22636
|
}
|
|
22423
|
-
const
|
|
22637
|
+
const attempt = await submitSearch(client, body, { preSendSignal: ctx?.signal }, ctx);
|
|
22638
|
+
if (!attempt.ok)
|
|
22639
|
+
return { ...attempt.choice, ...countryNote, region: client.region };
|
|
22640
|
+
const submit = attempt.data;
|
|
22424
22641
|
const mocked = mockedSubmitPreview(submit, "leadbay_find_new_leads", client.region);
|
|
22425
22642
|
if (mocked)
|
|
22426
22643
|
return mocked;
|
|
@@ -22471,6 +22688,7 @@ var init_find_new_leads = __esm({
|
|
|
22471
22688
|
since: snapshot.next_since ?? null,
|
|
22472
22689
|
suggested_wait_seconds: done ? 0 : 60
|
|
22473
22690
|
},
|
|
22691
|
+
...sectorNote(attempt.rewritten),
|
|
22474
22692
|
...countryNote,
|
|
22475
22693
|
region: client.region
|
|
22476
22694
|
};
|
|
@@ -24019,8 +24237,8 @@ var init_report_outreach = __esm({
|
|
|
24019
24237
|
if (!params.dry_run && params.verification.source === "user_confirmed" && typeof ctx?.elicit === "function") {
|
|
24020
24238
|
try {
|
|
24021
24239
|
const targetIds = params.lead_ids ?? [params.lead_id];
|
|
24022
|
-
const
|
|
24023
|
-
const elicitMsg =
|
|
24240
|
+
const leadCount2 = targetIds.length;
|
|
24241
|
+
const elicitMsg = leadCount2 === 1 ? `An AI agent wants to log outreach on lead ${targetIds[0]}: "${params.note}". The agent claims you confirmed this. Type your literal confirmation to proceed; cancel to reject.` : `An AI agent wants to log outreach on ${leadCount2} leads: "${params.note}". The agent claims you confirmed this. Type your literal confirmation to proceed; cancel to reject.`;
|
|
24024
24242
|
const result = await ctx.elicit({
|
|
24025
24243
|
message: elicitMsg,
|
|
24026
24244
|
requestedSchema: {
|
|
@@ -31378,7 +31596,7 @@ var OAUTH_BASE_URLS = {
|
|
|
31378
31596
|
fr: "https://staging.api.leadbay.app"
|
|
31379
31597
|
}
|
|
31380
31598
|
};
|
|
31381
|
-
var VERSION = "0.39.
|
|
31599
|
+
var VERSION = "0.39.4";
|
|
31382
31600
|
var HELP = `
|
|
31383
31601
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
31384
31602
|
|
package/dist/http-server.js
CHANGED
|
@@ -16691,8 +16691,8 @@ function summarise(responses) {
|
|
|
16691
16691
|
return { answered, total, avg_qualification_boost: avg, best_response_excerpt: excerpt };
|
|
16692
16692
|
}
|
|
16693
16693
|
function buildPullLeadsNextSteps(args) {
|
|
16694
|
-
const { leadCount, hasMore, nextPage, computingWishlist, computingScores } = args;
|
|
16695
|
-
if (
|
|
16694
|
+
const { leadCount: leadCount2, hasMore, nextPage, computingWishlist, computingScores } = args;
|
|
16695
|
+
if (leadCount2 <= 0) {
|
|
16696
16696
|
if (computingWishlist || computingScores) {
|
|
16697
16697
|
return {
|
|
16698
16698
|
question: "This lens is still warming up. What next?",
|
|
@@ -16937,15 +16937,15 @@ var pullLeads = {
|
|
|
16937
16937
|
const currentPage = res.pagination?.page ?? page;
|
|
16938
16938
|
const hasMore = currentPage < totalPages - 1;
|
|
16939
16939
|
const nextPage = hasMore ? currentPage + 1 : null;
|
|
16940
|
-
const
|
|
16940
|
+
const leadCount2 = res.items.length;
|
|
16941
16941
|
const nextSteps = buildPullLeadsNextSteps({
|
|
16942
|
-
leadCount,
|
|
16942
|
+
leadCount: leadCount2,
|
|
16943
16943
|
hasMore,
|
|
16944
16944
|
nextPage,
|
|
16945
16945
|
computingWishlist: res.computing_wishlist,
|
|
16946
16946
|
computingScores: res.computing_scores
|
|
16947
16947
|
});
|
|
16948
|
-
const lensIsEmpty =
|
|
16948
|
+
const lensIsEmpty = leadCount2 === 0 && (res.pagination?.total ?? 0) === 0;
|
|
16949
16949
|
const emptyReason = lensIsEmpty ? await diagnoseEmptyLens(client, lensId, {
|
|
16950
16950
|
wishlist: res.computing_wishlist,
|
|
16951
16951
|
scores: res.computing_scores
|
|
@@ -17382,8 +17382,16 @@ function townName(value) {
|
|
|
17382
17382
|
}
|
|
17383
17383
|
function cityHintCore(cityHint) {
|
|
17384
17384
|
const head = cityHint.split(",")[0] ?? "";
|
|
17385
|
-
const
|
|
17386
|
-
|
|
17385
|
+
const segments = cityHint.split(",").map((part) => part.trim()).filter(Boolean);
|
|
17386
|
+
for (let take = segments.length; take > 0; take -= 1) {
|
|
17387
|
+
const candidate = segments.slice(0, take).join(" ");
|
|
17388
|
+
const expanded = expandAlias(candidate);
|
|
17389
|
+
if (expanded !== candidate) {
|
|
17390
|
+
return { name: townName(expanded), isCity: true };
|
|
17391
|
+
}
|
|
17392
|
+
}
|
|
17393
|
+
const prefixed = ADMIN_PREFIX.test(normalizeGeo(head));
|
|
17394
|
+
return { name: townName(head), isCity: prefixed };
|
|
17387
17395
|
}
|
|
17388
17396
|
function stateFieldOf(lead) {
|
|
17389
17397
|
const state = lead?.location?.state;
|
|
@@ -22998,27 +23006,161 @@ var bulkEnrichStatus = {
|
|
|
22998
23006
|
}
|
|
22999
23007
|
};
|
|
23000
23008
|
|
|
23001
|
-
// ../core/dist/composite/
|
|
23002
|
-
|
|
23003
|
-
|
|
23004
|
-
|
|
23005
|
-
|
|
23009
|
+
// ../core/dist/composite/_sector-resolver.js
|
|
23010
|
+
var FILLER = /* @__PURE__ */ new Set([
|
|
23011
|
+
// en
|
|
23012
|
+
"and",
|
|
23013
|
+
"of",
|
|
23014
|
+
"the",
|
|
23015
|
+
"for",
|
|
23016
|
+
"or",
|
|
23017
|
+
"activity",
|
|
23018
|
+
// fr
|
|
23019
|
+
"et",
|
|
23020
|
+
"de",
|
|
23021
|
+
"des",
|
|
23022
|
+
"du",
|
|
23023
|
+
"d",
|
|
23024
|
+
"la",
|
|
23025
|
+
"le",
|
|
23026
|
+
"les",
|
|
23027
|
+
"l",
|
|
23028
|
+
"en",
|
|
23029
|
+
"aux",
|
|
23030
|
+
"au",
|
|
23031
|
+
"activite",
|
|
23032
|
+
"pour",
|
|
23033
|
+
"sur",
|
|
23034
|
+
"par",
|
|
23035
|
+
"a"
|
|
23036
|
+
]);
|
|
23037
|
+
function normalizeSectorText(value) {
|
|
23038
|
+
return (value ?? "").normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^0-9a-z]+/g, " ").trim();
|
|
23006
23039
|
}
|
|
23007
|
-
function
|
|
23008
|
-
|
|
23009
|
-
|
|
23010
|
-
|
|
23011
|
-
|
|
23012
|
-
|
|
23040
|
+
function singular(token) {
|
|
23041
|
+
if (token.length > 4 && token.endsWith("ies"))
|
|
23042
|
+
return `${token.slice(0, -3)}y`;
|
|
23043
|
+
return token.length > 3 && token.endsWith("s") ? token.slice(0, -1) : token;
|
|
23044
|
+
}
|
|
23045
|
+
function sectorTokens(value) {
|
|
23046
|
+
const all = normalizeSectorText(value).split(" ").filter(Boolean).map(singular);
|
|
23047
|
+
const core = all.filter((t) => !FILLER.has(t));
|
|
23048
|
+
return core.length > 0 ? core : all;
|
|
23049
|
+
}
|
|
23050
|
+
function sectorLabel(row) {
|
|
23051
|
+
if (!row)
|
|
23052
|
+
return "";
|
|
23053
|
+
const label = row.label;
|
|
23054
|
+
if (typeof label === "string" && label.length > 0)
|
|
23055
|
+
return label;
|
|
23056
|
+
return typeof row.name === "string" ? row.name : "";
|
|
23057
|
+
}
|
|
23058
|
+
function leadCount(row) {
|
|
23059
|
+
const n = row.number_of_leads;
|
|
23060
|
+
return typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
23061
|
+
}
|
|
23062
|
+
var MAX_CANDIDATES = 8;
|
|
23063
|
+
var MAX_PARTIAL_CANDIDATES = 4;
|
|
23064
|
+
function rankCandidates(scored, tieBreak) {
|
|
23065
|
+
const out = [];
|
|
23066
|
+
const seen = /* @__PURE__ */ new Set();
|
|
23067
|
+
scored.sort((a, b) => b.score - a.score || (tieBreak === "breadth" ? leadCount(b.row) - leadCount(a.row) || b.closeness - a.closeness : b.closeness - a.closeness || leadCount(b.row) - leadCount(a.row)) || sectorLabel(a.row).length - sectorLabel(b.row).length).forEach(({ row, score }) => {
|
|
23068
|
+
const label = sectorLabel(row);
|
|
23069
|
+
if (!label || seen.has(label) || out.length >= MAX_CANDIDATES)
|
|
23070
|
+
return;
|
|
23071
|
+
seen.add(label);
|
|
23072
|
+
out.push({ id: row.id, name: label, score: Number(score.toFixed(2)) });
|
|
23073
|
+
});
|
|
23074
|
+
return out;
|
|
23075
|
+
}
|
|
23076
|
+
function matchSector(text3, taxonomy) {
|
|
23077
|
+
const wanted = normalizeSectorText(text3);
|
|
23078
|
+
if (!wanted)
|
|
23079
|
+
return { kind: "unresolved", candidates: [] };
|
|
23080
|
+
const exact = taxonomy.filter((row) => normalizeSectorText(sectorLabel(row)) === wanted);
|
|
23081
|
+
if (exact.length > 0) {
|
|
23082
|
+
const label = sectorLabel(exact.reduce((best, row) => leadCount(row) > leadCount(best) ? row : best));
|
|
23083
|
+
const ids = taxonomy.filter((row) => sectorLabel(row) === label).map((row) => row.id);
|
|
23084
|
+
return { kind: "resolved", label, ids, exact: true };
|
|
23085
|
+
}
|
|
23086
|
+
const want = new Set(sectorTokens(text3));
|
|
23087
|
+
const sameWords = taxonomy.filter((row) => {
|
|
23088
|
+
const have = new Set(sectorTokens(sectorLabel(row)));
|
|
23089
|
+
return have.size === want.size && [...want].every((token) => have.has(token));
|
|
23090
|
+
});
|
|
23091
|
+
const sameWordLabels = new Set(sameWords.map((row) => sectorLabel(row)).filter(Boolean));
|
|
23092
|
+
if (sameWordLabels.size === 1) {
|
|
23093
|
+
const label = [...sameWordLabels][0];
|
|
23094
|
+
const ids = taxonomy.filter((row) => sectorLabel(row) === label).map((row) => row.id);
|
|
23095
|
+
return { kind: "resolved", label, ids, exact: false };
|
|
23096
|
+
}
|
|
23097
|
+
const scored = [];
|
|
23098
|
+
for (const row of taxonomy) {
|
|
23099
|
+
const have = new Set(sectorTokens(sectorLabel(row)));
|
|
23100
|
+
if (have.size === 0)
|
|
23101
|
+
continue;
|
|
23013
23102
|
let overlap = 0;
|
|
23014
|
-
for (const
|
|
23015
|
-
if (have.has(
|
|
23103
|
+
for (const token of want)
|
|
23104
|
+
if (have.has(token))
|
|
23016
23105
|
overlap += 1;
|
|
23017
|
-
|
|
23018
|
-
|
|
23019
|
-
|
|
23020
|
-
|
|
23106
|
+
if (overlap === 0)
|
|
23107
|
+
continue;
|
|
23108
|
+
scored.push({ row, score: overlap / want.size, closeness: overlap / have.size });
|
|
23109
|
+
}
|
|
23110
|
+
const full = scored.filter((s) => s.score >= 1);
|
|
23111
|
+
return {
|
|
23112
|
+
kind: "unresolved",
|
|
23113
|
+
candidates: full.length > 0 ? rankCandidates(full, "breadth") : rankCandidates(scored, "closeness").slice(0, MAX_PARTIAL_CANDIDATES)
|
|
23114
|
+
};
|
|
23115
|
+
}
|
|
23116
|
+
function sectorSections(taxonomy) {
|
|
23117
|
+
const roots = taxonomy.filter((row) => !row.parent);
|
|
23118
|
+
const labels = [];
|
|
23119
|
+
for (const row of roots.sort((a, b) => leadCount(b) - leadCount(a))) {
|
|
23120
|
+
const label = sectorLabel(row);
|
|
23121
|
+
if (label && !labels.includes(label))
|
|
23122
|
+
labels.push(label);
|
|
23123
|
+
}
|
|
23124
|
+
return labels;
|
|
23021
23125
|
}
|
|
23126
|
+
async function fetchSectorTaxonomy(client, ctx) {
|
|
23127
|
+
const me = await client.resolveMe().catch(() => null);
|
|
23128
|
+
const lang = me?.language ?? "en";
|
|
23129
|
+
const taxonomy = await client.request("GET", `/sectors/all?lang=${encodeURIComponent(lang)}&includeInvisible=false`);
|
|
23130
|
+
const unlabelled = taxonomy.filter((row) => !sectorLabel(row)).length;
|
|
23131
|
+
if (unlabelled > 0) {
|
|
23132
|
+
ctx?.logger?.warn?.(`sector taxonomy: ${unlabelled}/${taxonomy.length} row(s) carry no label`);
|
|
23133
|
+
}
|
|
23134
|
+
return taxonomy;
|
|
23135
|
+
}
|
|
23136
|
+
var NUMERIC_ID = /^\d+$/;
|
|
23137
|
+
function resolveSectorValues(values, taxonomy) {
|
|
23138
|
+
const out = {
|
|
23139
|
+
values: [],
|
|
23140
|
+
rewritten: [],
|
|
23141
|
+
unresolved: [],
|
|
23142
|
+
sections: sectorSections(taxonomy)
|
|
23143
|
+
};
|
|
23144
|
+
for (const raw of values) {
|
|
23145
|
+
const asked = typeof raw === "string" ? raw : String(raw ?? "");
|
|
23146
|
+
if (NUMERIC_ID.test(asked.trim())) {
|
|
23147
|
+
out.values.push(asked.trim());
|
|
23148
|
+
continue;
|
|
23149
|
+
}
|
|
23150
|
+
const match = matchSector(asked, taxonomy);
|
|
23151
|
+
if (match.kind === "resolved") {
|
|
23152
|
+
out.values.push(match.label);
|
|
23153
|
+
if (match.label !== asked)
|
|
23154
|
+
out.rewritten.push({ asked, used: match.label });
|
|
23155
|
+
continue;
|
|
23156
|
+
}
|
|
23157
|
+
out.values.push(asked);
|
|
23158
|
+
out.unresolved.push({ asked, closest: match.candidates });
|
|
23159
|
+
}
|
|
23160
|
+
return out;
|
|
23161
|
+
}
|
|
23162
|
+
|
|
23163
|
+
// ../core/dist/composite/adjust-audience.js
|
|
23022
23164
|
async function resolveSectors(client, texts, ctx) {
|
|
23023
23165
|
const looksLikeId2 = (s) => /^\d+$/.test(s);
|
|
23024
23166
|
const direct = texts.filter(looksLikeId2);
|
|
@@ -23028,18 +23170,18 @@ async function resolveSectors(client, texts, ctx) {
|
|
|
23028
23170
|
const me = await client.resolveMe().catch(() => null);
|
|
23029
23171
|
const lang = me?.language ?? "en";
|
|
23030
23172
|
const taxonomy = await client.request("GET", `/sectors/all?lang=${encodeURIComponent(lang)}&includeInvisible=false`);
|
|
23031
|
-
const nullNames = taxonomy.filter((s) => !s
|
|
23173
|
+
const nullNames = taxonomy.filter((s) => !sectorLabel(s)).length;
|
|
23032
23174
|
if (nullNames > 0) {
|
|
23033
23175
|
ctx?.logger?.warn?.(`adjust_audience: /sectors/all returned ${nullNames}/${taxonomy.length} sector(s) with a null/missing name`);
|
|
23034
23176
|
}
|
|
23035
23177
|
const resolved = [...direct];
|
|
23036
23178
|
const ambiguities = [];
|
|
23037
23179
|
for (const text3 of free) {
|
|
23038
|
-
const
|
|
23039
|
-
if (
|
|
23040
|
-
resolved.push(
|
|
23180
|
+
const match = matchSector(text3, taxonomy);
|
|
23181
|
+
if (match.kind === "resolved") {
|
|
23182
|
+
resolved.push(...match.ids);
|
|
23041
23183
|
} else {
|
|
23042
|
-
ambiguities.push({ sector_text: text3, matches });
|
|
23184
|
+
ambiguities.push({ sector_text: text3, matches: match.candidates });
|
|
23043
23185
|
}
|
|
23044
23186
|
}
|
|
23045
23187
|
return { resolved, ambiguities };
|
|
@@ -24334,6 +24476,61 @@ function sortFilterLists(filters) {
|
|
|
24334
24476
|
}
|
|
24335
24477
|
return out;
|
|
24336
24478
|
}
|
|
24479
|
+
function isSectorRejection(err) {
|
|
24480
|
+
const e = err;
|
|
24481
|
+
return !!e && e.code === "BAD_INPUT" && typeof e.message === "string" && e.message.includes("filters.sectors");
|
|
24482
|
+
}
|
|
24483
|
+
async function submitSearch(client, body, opts, ctx) {
|
|
24484
|
+
try {
|
|
24485
|
+
return {
|
|
24486
|
+
ok: true,
|
|
24487
|
+
data: await client.request("POST", "/mcp/search", body, opts),
|
|
24488
|
+
rewritten: []
|
|
24489
|
+
};
|
|
24490
|
+
} catch (err) {
|
|
24491
|
+
const asked = body.filters?.sectors;
|
|
24492
|
+
if (!isSectorRejection(err) || !Array.isArray(asked) || asked.length === 0) {
|
|
24493
|
+
throw err;
|
|
24494
|
+
}
|
|
24495
|
+
const taxonomy = await fetchSectorTaxonomy(client, ctx).catch(() => {
|
|
24496
|
+
throw err;
|
|
24497
|
+
});
|
|
24498
|
+
const fix = resolveSectorValues(asked, taxonomy);
|
|
24499
|
+
if (fix.unresolved.length === 0 && fix.rewritten.length === 0)
|
|
24500
|
+
throw err;
|
|
24501
|
+
if (fix.unresolved.length > 0)
|
|
24502
|
+
return { ok: false, choice: sectorChoice(fix) };
|
|
24503
|
+
const retried = { ...body, filters: { ...body.filters, sectors: fix.values } };
|
|
24504
|
+
return {
|
|
24505
|
+
ok: true,
|
|
24506
|
+
data: await client.request("POST", "/mcp/search", retried, opts),
|
|
24507
|
+
rewritten: fix.rewritten
|
|
24508
|
+
};
|
|
24509
|
+
}
|
|
24510
|
+
}
|
|
24511
|
+
function sectorNote(rewritten) {
|
|
24512
|
+
if (rewritten.length === 0)
|
|
24513
|
+
return void 0;
|
|
24514
|
+
const pairs = rewritten.map((r) => `"${r.asked}" \u2192 "${r.used}"`).join(", ");
|
|
24515
|
+
return {
|
|
24516
|
+
sectors_note: `Leadbay's sector taxonomy spells these differently, so the search ran on ${pairs}. Tell the user which sector was searched.`,
|
|
24517
|
+
sectors_used: rewritten
|
|
24518
|
+
};
|
|
24519
|
+
}
|
|
24520
|
+
function sectorChoice(fix) {
|
|
24521
|
+
const names = fix.unresolved.map((u) => `"${u.asked}"`).join(", ");
|
|
24522
|
+
return {
|
|
24523
|
+
mode: "needs_sector_choice",
|
|
24524
|
+
submitted: false,
|
|
24525
|
+
unresolved_sectors: fix.unresolved.map((u) => ({
|
|
24526
|
+
asked: u.asked,
|
|
24527
|
+
closest: u.closest.map((c) => c.name)
|
|
24528
|
+
})),
|
|
24529
|
+
sector_sections: fix.sections,
|
|
24530
|
+
resolved_sectors: fix.rewritten,
|
|
24531
|
+
hint: `No sector in this workspace's taxonomy is named ${names || "that"}. Nothing was submitted and nothing was spent. Re-call with a label copied character for character from \`closest\` or \`sector_sections\` (a section covers everything under it), or drop \`filters.sectors\` and put the wording in \`query\` / \`example_lead.description\` instead \u2014 the search then RANKS on it rather than fencing on it, which is usually what the user meant. \`leadbay_list_sectors\` returns the full taxonomy if none of these fit.`
|
|
24532
|
+
};
|
|
24533
|
+
}
|
|
24337
24534
|
var findNewLeads = {
|
|
24338
24535
|
name: "leadbay_find_new_leads",
|
|
24339
24536
|
annotations: {
|
|
@@ -24372,7 +24569,7 @@ var findNewLeads = {
|
|
|
24372
24569
|
},
|
|
24373
24570
|
filters: {
|
|
24374
24571
|
type: "object",
|
|
24375
|
-
description: "HARD constraints (the seed only shapes ranking). Sector/location labels resolve at submit;
|
|
24572
|
+
description: "HARD constraints (the seed only shapes ranking). Sector/location labels resolve at submit. `sectors` must be a label from Leadbay's own taxonomy (leadbay_list_sectors) \u2014 everyday wording like 'Professional Services' is not one; a spelling/plural slip is corrected for you, but a word with no label comes back as mode:'needs_sector_choice' with the labels to pick from. An unresolvable location is a 400 naming it.",
|
|
24376
24573
|
properties: {
|
|
24377
24574
|
sectors: { type: "array", items: { type: "string" } },
|
|
24378
24575
|
locations: { type: "array", items: { type: "string" } },
|
|
@@ -24544,19 +24741,27 @@ var findNewLeads = {
|
|
|
24544
24741
|
dry_run: dryRun
|
|
24545
24742
|
});
|
|
24546
24743
|
if (dryRun === true) {
|
|
24547
|
-
const
|
|
24744
|
+
const attempt2 = await submitSearch(client, body, void 0, ctx);
|
|
24745
|
+
if (!attempt2.ok)
|
|
24746
|
+
return { ...attempt2.choice, ...countryNote, region: client.region };
|
|
24548
24747
|
return {
|
|
24549
24748
|
dry_run: true,
|
|
24550
|
-
...
|
|
24749
|
+
...attempt2.data,
|
|
24750
|
+
...sectorNote(attempt2.rewritten),
|
|
24551
24751
|
...countryNote,
|
|
24552
24752
|
region: client.region
|
|
24553
24753
|
};
|
|
24554
24754
|
}
|
|
24555
24755
|
if (isPaid && !consented) {
|
|
24556
|
-
|
|
24557
|
-
|
|
24558
|
-
|
|
24559
|
-
|
|
24756
|
+
let forecast = null;
|
|
24757
|
+
let quoteRewritten = [];
|
|
24758
|
+
if (!vetoed) {
|
|
24759
|
+
const attempt2 = await submitSearch(client, { ...body, dry_run: true }, void 0, ctx);
|
|
24760
|
+
if (!attempt2.ok)
|
|
24761
|
+
return { ...attempt2.choice, ...countryNote, region: client.region };
|
|
24762
|
+
forecast = attempt2.data;
|
|
24763
|
+
quoteRewritten = attempt2.rewritten;
|
|
24764
|
+
}
|
|
24560
24765
|
return {
|
|
24561
24766
|
mode: "needs_confirmation",
|
|
24562
24767
|
submitted: false,
|
|
@@ -24569,11 +24774,15 @@ var findNewLeads = {
|
|
|
24569
24774
|
estimated_cost: forecast?.estimated_cost ?? null,
|
|
24570
24775
|
items_requested: forecast?.items_requested ?? null,
|
|
24571
24776
|
hint: vetoed ? "confirm:false vetoed the run \u2014 nothing was submitted. Re-call with confirm:true to proceed, or drop qualify/channels for a free search." : "Tell the user what will run and that it uses their plan's quota (no amounts, no money), get an explicit go-ahead, then re-call with confirm:true. For a free search instead: omit qualify and channels.",
|
|
24777
|
+
...sectorNote(quoteRewritten),
|
|
24572
24778
|
...countryNote,
|
|
24573
24779
|
region: client.region
|
|
24574
24780
|
};
|
|
24575
24781
|
}
|
|
24576
|
-
const
|
|
24782
|
+
const attempt = await submitSearch(client, body, { preSendSignal: ctx?.signal }, ctx);
|
|
24783
|
+
if (!attempt.ok)
|
|
24784
|
+
return { ...attempt.choice, ...countryNote, region: client.region };
|
|
24785
|
+
const submit = attempt.data;
|
|
24577
24786
|
const mocked = mockedSubmitPreview(submit, "leadbay_find_new_leads", client.region);
|
|
24578
24787
|
if (mocked)
|
|
24579
24788
|
return mocked;
|
|
@@ -24624,6 +24833,7 @@ var findNewLeads = {
|
|
|
24624
24833
|
since: snapshot.next_since ?? null,
|
|
24625
24834
|
suggested_wait_seconds: done ? 0 : 60
|
|
24626
24835
|
},
|
|
24836
|
+
...sectorNote(attempt.rewritten),
|
|
24627
24837
|
...countryNote,
|
|
24628
24838
|
region: client.region
|
|
24629
24839
|
};
|
|
@@ -26105,8 +26315,8 @@ var reportOutreach = {
|
|
|
26105
26315
|
if (!params.dry_run && params.verification.source === "user_confirmed" && typeof ctx?.elicit === "function") {
|
|
26106
26316
|
try {
|
|
26107
26317
|
const targetIds = params.lead_ids ?? [params.lead_id];
|
|
26108
|
-
const
|
|
26109
|
-
const elicitMsg =
|
|
26318
|
+
const leadCount2 = targetIds.length;
|
|
26319
|
+
const elicitMsg = leadCount2 === 1 ? `An AI agent wants to log outreach on lead ${targetIds[0]}: "${params.note}". The agent claims you confirmed this. Type your literal confirmation to proceed; cancel to reject.` : `An AI agent wants to log outreach on ${leadCount2} leads: "${params.note}". The agent claims you confirmed this. Type your literal confirmation to proceed; cancel to reject.`;
|
|
26110
26320
|
const result = await ctx.elicit({
|
|
26111
26321
|
message: elicitMsg,
|
|
26112
26322
|
requestedSchema: {
|
|
@@ -28660,7 +28870,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
28660
28870
|
}
|
|
28661
28871
|
|
|
28662
28872
|
// src/http-server.ts
|
|
28663
|
-
var VERSION = true ? "0.39.
|
|
28873
|
+
var VERSION = true ? "0.39.4" : "0.0.0-dev";
|
|
28664
28874
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
28665
28875
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
28666
28876
|
var logger = {
|
package/dist/installer-gui.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leadbay/mcp",
|
|
3
|
-
"version": "0.39.
|
|
3
|
+
"version": "0.39.4",
|
|
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",
|