@leadbay/mcp 0.32.0 → 0.32.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/CHANGELOG.md +28 -0
- package/README.md +2 -1
- package/dist/bin.js +145 -22
- package/dist/http-server.js +192 -19
- package/dist/installer-electron.js +1 -1
- package/dist/installer-gui.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog — @leadbay/mcp
|
|
2
2
|
|
|
3
|
+
## 0.32.4 — 2026-09-01
|
|
4
|
+
|
|
5
|
+
`leadbay_account_status.notifications` was permanently `[]` on the hosted server
|
|
6
|
+
(product#4009). Same shape as product#4005: `buildServerFromClient` builds every
|
|
7
|
+
hosted server without a `notificationsInbox`, `BuildServerOptions` makes it
|
|
8
|
+
optional, so it compiled and failed silently.
|
|
9
|
+
|
|
10
|
+
**Not fixed by wiring the inbox, because the inbox cannot exist there.** It is
|
|
11
|
+
fed by a WS listener whose ticket (`GET /auth/ws`) is per-account, which is
|
|
12
|
+
meaningless on a multi-tenant process, and the streamable transport builds a
|
|
13
|
+
fresh `Server` per request so nothing would survive to be cached. Porting it
|
|
14
|
+
would be the same mistake in a third place.
|
|
15
|
+
|
|
16
|
+
`GET /notifications` is already per-account and already durable. On hosted the
|
|
17
|
+
ledger IS the inbox, so `account_status` reads it on demand when no inbox is
|
|
18
|
+
wired. Stdio is unchanged and still free — the WS listener already has the
|
|
19
|
+
answer, and the ledger is not called.
|
|
20
|
+
|
|
21
|
+
- `fetchTerminalNotifications(client)` in `notifications/catch-up.ts` returns
|
|
22
|
+
terminal, unseen entries directly. `isTerminalUnseen` is extracted so inbox
|
|
23
|
+
seeding and the inbox-less read cannot disagree about what counts.
|
|
24
|
+
- A notifications failure resolves to `[]` rather than throwing. `account_status`
|
|
25
|
+
is the daily entry point; a ledger hiccup must not take the check-in down.
|
|
26
|
+
- **Deliberately not done: `_meta.notifications` still does not ride hosted tool
|
|
27
|
+
responses.** Reviving it would mean a `GET /notifications` per tool call to
|
|
28
|
+
decorate every response. The cost lands on the check-in entry point only,
|
|
29
|
+
which is where the daily-rhythm channel is actually read.
|
|
30
|
+
|
|
3
31
|
## 0.32.0 — 2026-09-01
|
|
4
32
|
|
|
5
33
|
A poll-budget timeout stops being an error (product#4007). The import wizard's
|
package/README.md
CHANGED
|
@@ -634,7 +634,7 @@ Use `dry_run: true` to validate domain formatting and wizard reachability withou
|
|
|
634
634
|
| `LEADBAY_MOCK` | no | unset | `"1"` serves all reads from on-disk fixtures (dev only) |
|
|
635
635
|
| `LEADBAY_MOCK_DIR` | no | `./.context/leadbay-live-shapes/` | Fixture dir for mock mode |
|
|
636
636
|
| `LEADBAY_LOG_LEVEL` | no | `error` | `debug` \| `info` \| `error`, logs to stderr |
|
|
637
|
-
| `LEADBAY_TIMEOUT_MS` | no | (
|
|
637
|
+
| `LEADBAY_TIMEOUT_MS` | no | `600000` | Backstop deadline for a single outbound Leadbay request, for the case where nothing cancels it. Not a latency budget: long work (enrichment, bulk qualify, import) is launched and polled, and a cancelled tool call already closes its own requests. On expiry the socket is closed and the tool returns a `TIMEOUT` error. Set `0` to disable the backstop. |
|
|
638
638
|
|
|
639
639
|
> ⚠️ **Set `LEADBAY_REGION` explicitly.** If you don't, the server probes BOTH `api-us.leadbay.app` and `api-fr.leadbay.app` in parallel with your bearer token attached, sending the token to a backend that doesn't own your account. The `install` and `login` subcommands enforce `--region` for exactly this reason; the runtime auto-probe is a backwards-compat fallback, not a recommended setting.
|
|
640
640
|
|
|
@@ -657,6 +657,7 @@ Use `dry_run: true` to validate domain formatting and wizard reachability withou
|
|
|
657
657
|
| `mcp tool called` | Every tool invocation | `tool`, `ok`, `duration_ms`, `format`, `bytes`, `error_code` (if failed) |
|
|
658
658
|
| `mcp quota hit` | When the API returns `QUOTA_EXCEEDED` (HTTP 429/402) | `tool`, `retry_after_s`, `endpoint` |
|
|
659
659
|
| `mcp topup link created` | When `leadbay_create_topup_link` returns a checkout URL | `tool` (the URL itself is **never** captured) |
|
|
660
|
+
| `mcp tool timeout` | When an outbound Leadbay request exceeds `LEADBAY_TIMEOUT_MS` | `tool`, `timeout_ms`, `endpoint`, `region` |
|
|
660
661
|
|
|
661
662
|
After your first authenticated call, your PostHog `distinctId` is set to your Leadbay account email so MCP events consolidate with web-app events for the same person. Events also carry `$groups.organization` so org-level rollups work.
|
|
662
663
|
|
package/dist/bin.js
CHANGED
|
@@ -11,20 +11,47 @@ var __export = (target, all) => {
|
|
|
11
11
|
|
|
12
12
|
// ../core/dist/client.js
|
|
13
13
|
import https from "https";
|
|
14
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
14
15
|
import { readdirSync, readFileSync, existsSync } from "fs";
|
|
15
16
|
import { join } from "path";
|
|
16
|
-
function
|
|
17
|
+
function defaultTimeoutMs() {
|
|
18
|
+
const raw = process.env.LEADBAY_TIMEOUT_MS;
|
|
19
|
+
if (raw === void 0 || raw.trim() === "")
|
|
20
|
+
return DEFAULT_REQUEST_TIMEOUT_MS;
|
|
21
|
+
const n = Number(raw);
|
|
22
|
+
return Number.isFinite(n) ? n : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
23
|
+
}
|
|
24
|
+
function runWithRequestSignal(signal, fn) {
|
|
25
|
+
return requestSignalStore.run(signal, fn);
|
|
26
|
+
}
|
|
27
|
+
function makeCancelledError(method, url) {
|
|
28
|
+
const err = new Error(`Request cancelled: ${method} ${url}`);
|
|
29
|
+
err.name = "AbortError";
|
|
30
|
+
err.code = "CANCELLED";
|
|
31
|
+
return err;
|
|
32
|
+
}
|
|
33
|
+
function httpsRequest(method, url, headers, body, timeoutMs, signal) {
|
|
34
|
+
const deadlineMs = timeoutMs ?? defaultTimeoutMs();
|
|
35
|
+
const abortSignal = signal ?? requestSignalStore.getStore();
|
|
36
|
+
const abortSafe = method.toUpperCase() === "GET";
|
|
17
37
|
return new Promise((resolve, reject) => {
|
|
18
38
|
const start = Date.now();
|
|
39
|
+
if (abortSignal?.aborted) {
|
|
40
|
+
reject(makeCancelledError(method, url));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
19
43
|
const parsed = new URL(url);
|
|
20
44
|
const reqHeaders = { ...headers };
|
|
21
45
|
if (body !== void 0) {
|
|
22
46
|
reqHeaders["Content-Length"] = Buffer.byteLength(body);
|
|
23
47
|
}
|
|
24
48
|
let deadline;
|
|
49
|
+
let onAbort;
|
|
25
50
|
const clearDeadline = () => {
|
|
26
51
|
if (deadline !== void 0)
|
|
27
52
|
clearTimeout(deadline);
|
|
53
|
+
if (onAbort)
|
|
54
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
28
55
|
};
|
|
29
56
|
const req = https.request({
|
|
30
57
|
hostname: parsed.hostname,
|
|
@@ -45,15 +72,24 @@ function httpsRequest(method, url, headers, body, timeoutMs) {
|
|
|
45
72
|
});
|
|
46
73
|
});
|
|
47
74
|
});
|
|
48
|
-
if (
|
|
75
|
+
if (deadlineMs > 0) {
|
|
49
76
|
deadline = setTimeout(() => {
|
|
50
77
|
req.destroy?.();
|
|
51
|
-
const err = new Error(`Request timed out after ${
|
|
78
|
+
const err = new Error(`Request timed out after ${deadlineMs}ms: ${method} ${url}`);
|
|
52
79
|
err.code = "TIMEOUT";
|
|
80
|
+
err.timeout_ms = deadlineMs;
|
|
53
81
|
reject(err);
|
|
54
|
-
},
|
|
82
|
+
}, deadlineMs);
|
|
55
83
|
deadline.unref?.();
|
|
56
84
|
}
|
|
85
|
+
if (abortSignal && abortSafe) {
|
|
86
|
+
onAbort = () => {
|
|
87
|
+
req.destroy?.();
|
|
88
|
+
clearDeadline();
|
|
89
|
+
reject(makeCancelledError(method, url));
|
|
90
|
+
};
|
|
91
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
92
|
+
}
|
|
57
93
|
req.on("error", (e) => {
|
|
58
94
|
clearDeadline();
|
|
59
95
|
reject(e);
|
|
@@ -177,7 +213,7 @@ function parseRetryAfter(value) {
|
|
|
177
213
|
}
|
|
178
214
|
return null;
|
|
179
215
|
}
|
|
180
|
-
var LENS_CACHE_TTL_MS, TASTE_CACHE_TTL_MS, ME_CACHE_TTL_MS, MAX_CONCURRENT, REGIONS, API_VERSION, API_PREFIX, _mockFixtures, _mockJournal, LeadbayClient;
|
|
216
|
+
var LENS_CACHE_TTL_MS, TASTE_CACHE_TTL_MS, ME_CACHE_TTL_MS, MAX_CONCURRENT, DEFAULT_REQUEST_TIMEOUT_MS, requestSignalStore, REGIONS, API_VERSION, API_PREFIX, _mockFixtures, _mockJournal, LeadbayClient;
|
|
181
217
|
var init_client = __esm({
|
|
182
218
|
"../core/dist/client.js"() {
|
|
183
219
|
"use strict";
|
|
@@ -185,6 +221,8 @@ var init_client = __esm({
|
|
|
185
221
|
TASTE_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
186
222
|
ME_CACHE_TTL_MS = 60 * 1e3;
|
|
187
223
|
MAX_CONCURRENT = 5;
|
|
224
|
+
DEFAULT_REQUEST_TIMEOUT_MS = 6e5;
|
|
225
|
+
requestSignalStore = new AsyncLocalStorage();
|
|
188
226
|
REGIONS = {
|
|
189
227
|
us: "https://api-us.leadbay.app",
|
|
190
228
|
fr: "https://api-fr.leadbay.app"
|
|
@@ -408,6 +446,8 @@ var init_client = __esm({
|
|
|
408
446
|
throw this.mapErrorResponse(res.status, res.body, path, res.headers);
|
|
409
447
|
}
|
|
410
448
|
return JSON.parse(res.body);
|
|
449
|
+
} catch (e) {
|
|
450
|
+
throw this.mapTransportError(e, `${method} ${path}`);
|
|
411
451
|
} finally {
|
|
412
452
|
this.releaseSemaphore();
|
|
413
453
|
}
|
|
@@ -439,6 +479,8 @@ var init_client = __esm({
|
|
|
439
479
|
if (res.status < 200 || res.status >= 300) {
|
|
440
480
|
throw this.mapErrorResponse(res.status, res.body, path, res.headers);
|
|
441
481
|
}
|
|
482
|
+
} catch (e) {
|
|
483
|
+
throw this.mapTransportError(e, `${method} ${path}`);
|
|
442
484
|
} finally {
|
|
443
485
|
this.releaseSemaphore();
|
|
444
486
|
}
|
|
@@ -476,6 +518,8 @@ var init_client = __esm({
|
|
|
476
518
|
throw this.mapErrorResponse(res.status, res.body, path, res.headers);
|
|
477
519
|
}
|
|
478
520
|
return JSON.parse(res.body);
|
|
521
|
+
} catch (e) {
|
|
522
|
+
throw this.mapTransportError(e, `${method} ${path}`);
|
|
479
523
|
} finally {
|
|
480
524
|
this.releaseSemaphore();
|
|
481
525
|
}
|
|
@@ -536,6 +580,29 @@ var init_client = __esm({
|
|
|
536
580
|
would_call: { method, path: fullPath, body: journalBody }
|
|
537
581
|
};
|
|
538
582
|
}
|
|
583
|
+
/**
|
|
584
|
+
* Turn httpsRequest's raw TIMEOUT rejection into the `{error:true, code, …}`
|
|
585
|
+
* envelope every other failure already speaks, so the agent gets something it
|
|
586
|
+
* can read out to the user and act on rather than a bare Error string. Any
|
|
587
|
+
* other rejection (ECONNRESET, DNS, a mapped 4xx/5xx) passes through untouched
|
|
588
|
+
* — this is a translation, not a catch-all.
|
|
589
|
+
*
|
|
590
|
+
* The code stays "TIMEOUT" so the hosted auth probe's existing branch
|
|
591
|
+
* (auth-http.ts) keeps classifying it as a transient fault and moves to the
|
|
592
|
+
* sibling region instead of declaring a live token expired.
|
|
593
|
+
*/
|
|
594
|
+
mapTransportError(e, endpoint) {
|
|
595
|
+
const err = e;
|
|
596
|
+
if (err?.code !== "TIMEOUT")
|
|
597
|
+
return e;
|
|
598
|
+
const ms = err.timeout_ms ?? defaultTimeoutMs();
|
|
599
|
+
const envelope = this.makeError("TIMEOUT", `Leadbay did not respond within ${ms}ms \u2014 the request was cancelled`, "The connection was accepted but no response came back, so this is a Leadbay-side stall, not a bad request. It is transient: retry the same call once. If it times out again, tell the user Leadbay is not responding right now and offer to report it with leadbay_report_friction.", endpoint);
|
|
600
|
+
if (envelope._meta) {
|
|
601
|
+
envelope._meta.timeout_ms = ms;
|
|
602
|
+
envelope._meta.latency_ms = ms;
|
|
603
|
+
}
|
|
604
|
+
return envelope;
|
|
605
|
+
}
|
|
539
606
|
mapErrorResponse(status, rawBody, endpoint, headers) {
|
|
540
607
|
let parsed;
|
|
541
608
|
try {
|
|
@@ -657,6 +724,8 @@ var init_client = __esm({
|
|
|
657
724
|
this.telemetryEnabledFromStamp = false;
|
|
658
725
|
}
|
|
659
726
|
return observed;
|
|
727
|
+
} catch (e) {
|
|
728
|
+
throw this.mapTransportError(e, "GET /users/me");
|
|
660
729
|
} finally {
|
|
661
730
|
this.releaseSemaphore();
|
|
662
731
|
}
|
|
@@ -5499,6 +5568,28 @@ var init_inbox = __esm({
|
|
|
5499
5568
|
});
|
|
5500
5569
|
|
|
5501
5570
|
// ../core/dist/notifications/catch-up.js
|
|
5571
|
+
function isTerminalUnseen(n) {
|
|
5572
|
+
if (!n.bulk_progress)
|
|
5573
|
+
return false;
|
|
5574
|
+
if (n.in_progress)
|
|
5575
|
+
return false;
|
|
5576
|
+
if (n.first_seen_at)
|
|
5577
|
+
return false;
|
|
5578
|
+
return true;
|
|
5579
|
+
}
|
|
5580
|
+
async function fetchTerminalNotifications(client, opts = {}) {
|
|
5581
|
+
try {
|
|
5582
|
+
const page = await client.listNotifications({
|
|
5583
|
+
archived: false,
|
|
5584
|
+
page: 0,
|
|
5585
|
+
count: opts.count ?? DEFAULT_COUNT
|
|
5586
|
+
});
|
|
5587
|
+
return page.items.filter(isTerminalUnseen).map(toInboxEntry);
|
|
5588
|
+
} catch (err) {
|
|
5589
|
+
opts.logger?.warn?.(`notifications.fetch_terminal failed: ${err?.message ?? err?.code ?? err}`);
|
|
5590
|
+
return [];
|
|
5591
|
+
}
|
|
5592
|
+
}
|
|
5502
5593
|
async function catchUpNotifications(client, inbox, opts = {}) {
|
|
5503
5594
|
const count = opts.count ?? DEFAULT_COUNT;
|
|
5504
5595
|
let added = 0;
|
|
@@ -5509,11 +5600,7 @@ async function catchUpNotifications(client, inbox, opts = {}) {
|
|
|
5509
5600
|
count
|
|
5510
5601
|
});
|
|
5511
5602
|
for (const n of page.items) {
|
|
5512
|
-
if (!n
|
|
5513
|
-
continue;
|
|
5514
|
-
if (n.in_progress)
|
|
5515
|
-
continue;
|
|
5516
|
-
if (n.first_seen_at)
|
|
5603
|
+
if (!isTerminalUnseen(n))
|
|
5517
5604
|
continue;
|
|
5518
5605
|
const sizeBefore = inbox.size();
|
|
5519
5606
|
inbox.record(n);
|
|
@@ -5530,6 +5617,7 @@ var DEFAULT_COUNT;
|
|
|
5530
5617
|
var init_catch_up = __esm({
|
|
5531
5618
|
"../core/dist/notifications/catch-up.js"() {
|
|
5532
5619
|
"use strict";
|
|
5620
|
+
init_revise_hint();
|
|
5533
5621
|
DEFAULT_COUNT = 50;
|
|
5534
5622
|
}
|
|
5535
5623
|
});
|
|
@@ -20649,6 +20737,7 @@ var init_account_status = __esm({
|
|
|
20649
20737
|
"../core/dist/composite/account-status.js"() {
|
|
20650
20738
|
"use strict";
|
|
20651
20739
|
init_agent_memory();
|
|
20740
|
+
init_catch_up();
|
|
20652
20741
|
init_credits_helpers();
|
|
20653
20742
|
init_tool_descriptions_generated();
|
|
20654
20743
|
accountStatus = {
|
|
@@ -20770,6 +20859,8 @@ var init_account_status = __esm({
|
|
|
20770
20859
|
ctx?.logger?.warn?.(`account_status: quota_status failed: ${err?.message ?? err?.code ?? err}`);
|
|
20771
20860
|
}
|
|
20772
20861
|
}
|
|
20862
|
+
const inbox = ctx?.notificationsInbox;
|
|
20863
|
+
const notifications = inbox ? inbox.list() : await fetchTerminalNotifications(client, { logger: ctx?.logger });
|
|
20773
20864
|
const lensId = me.last_requested_lens ?? null;
|
|
20774
20865
|
const lensAsked = typeof ctx?.triggered_by === "string" && /\b(lens|lenses|audience|targeting|segment|filter)\b/i.test(ctx.triggered_by);
|
|
20775
20866
|
let last_requested_lens_name = null;
|
|
@@ -20810,13 +20901,12 @@ var init_account_status = __esm({
|
|
|
20810
20901
|
// on /me are intentionally NOT surfaced — they're defunct (see
|
|
20811
20902
|
// SHAPE-DRIFT.md probe round 4).
|
|
20812
20903
|
quota,
|
|
20813
|
-
//
|
|
20814
|
-
//
|
|
20815
|
-
//
|
|
20816
|
-
//
|
|
20817
|
-
//
|
|
20818
|
-
|
|
20819
|
-
notifications: ctx?.notificationsInbox?.list() ?? [],
|
|
20904
|
+
// Terminal bulk-progress notifications. Same shape the MCP server
|
|
20905
|
+
// attaches to `_meta.notifications` on every tool response — carried
|
|
20906
|
+
// here as a top-level field so the agent's daily-rhythm check-in (this
|
|
20907
|
+
// composite) sees them without reading _meta. See the read above for
|
|
20908
|
+
// why hosted takes a different path to the same list.
|
|
20909
|
+
notifications,
|
|
20820
20910
|
// Non-null ONLY when the quota_status call failed. The agent must treat
|
|
20821
20911
|
// this as "could not read quota" — NOT as zero usage, and NOT as a broken
|
|
20822
20912
|
// login (the token just authenticated /users/me above). product#3761.
|
|
@@ -26464,6 +26554,7 @@ __export(dist_exports, {
|
|
|
26464
26554
|
AgentMemorySourceSchema: () => AgentMemorySourceSchema,
|
|
26465
26555
|
AgentMemoryTombstoneSchema: () => AgentMemoryTombstoneSchema,
|
|
26466
26556
|
COMPOSITE_FILE_TOOL_NAMES: () => COMPOSITE_FILE_TOOL_NAMES,
|
|
26557
|
+
DEFAULT_REQUEST_TIMEOUT_MS: () => DEFAULT_REQUEST_TIMEOUT_MS,
|
|
26467
26558
|
GETTING_STARTED_MANIFEST: () => GETTING_STARTED_MANIFEST,
|
|
26468
26559
|
InMemoryBulkStore: () => InMemoryBulkStore,
|
|
26469
26560
|
LeadbayClient: () => LeadbayClient,
|
|
@@ -26587,6 +26678,7 @@ __export(dist_exports, {
|
|
|
26587
26678
|
resolveImportRows: () => resolveImportRows,
|
|
26588
26679
|
resolveRegion: () => resolveRegion,
|
|
26589
26680
|
reviseHintFor: () => reviseHintFor,
|
|
26681
|
+
runWithRequestSignal: () => runWithRequestSignal,
|
|
26590
26682
|
scanPortfolioSignals: () => scanPortfolioSignals,
|
|
26591
26683
|
seedCandidates: () => seedCandidates,
|
|
26592
26684
|
selectLeads: () => selectLeads,
|
|
@@ -29837,6 +29929,7 @@ var BUILTIN_WIDGETS_PARAGRAPH = 'Prefer host-native widgets over inline markdown
|
|
|
29837
29929
|
|
|
29838
29930
|
// src/server.ts
|
|
29839
29931
|
init_dist();
|
|
29932
|
+
init_dist();
|
|
29840
29933
|
|
|
29841
29934
|
// src/telemetry.ts
|
|
29842
29935
|
import { PostHog } from "posthog-node";
|
|
@@ -29850,6 +29943,7 @@ var EMBEDDED_SENTRY_DSN = "https://301f1c433433b76132956ed5415bea19@o45058744368
|
|
|
29850
29943
|
// src/telemetry-events.ts
|
|
29851
29944
|
var EV_TOOL_CALL = "mcp tool called";
|
|
29852
29945
|
var EV_QUOTA_HIT = "mcp quota hit";
|
|
29946
|
+
var EV_TOOL_TIMEOUT = "mcp tool timeout";
|
|
29853
29947
|
var EV_TOPUP_LINK = "mcp topup link created";
|
|
29854
29948
|
var EV_STARTUP = "mcp startup";
|
|
29855
29949
|
var EV_MCP_UPDATE_CHECK = "mcp update check";
|
|
@@ -29860,6 +29954,7 @@ var EV_MCP_VERSION_UPDATED = "mcp version updated";
|
|
|
29860
29954
|
var EV_AGENT_MEMORY_CAPTURED = "agent_memory_captured";
|
|
29861
29955
|
var EV_AGENT_MEMORY_RECALLED = "agent_memory_recalled";
|
|
29862
29956
|
var EV_AGENT_MEMORY_PRUNED = "agent_memory_pruned";
|
|
29957
|
+
var DURATION_PLAUSIBILITY_CEILING_MS = 6e5;
|
|
29863
29958
|
var EV_FRICTION_REPORTED = "mcp friction reported";
|
|
29864
29959
|
var EV_COMPOSITE_CALL = "mcp composite call";
|
|
29865
29960
|
|
|
@@ -29873,6 +29968,8 @@ var NOOP_TELEMETRY = {
|
|
|
29873
29968
|
},
|
|
29874
29969
|
captureQuotaHit: (_props, _identity) => {
|
|
29875
29970
|
},
|
|
29971
|
+
captureToolTimeout: (_props, _identity) => {
|
|
29972
|
+
},
|
|
29876
29973
|
captureTopupLink: (_props, _identity) => {
|
|
29877
29974
|
},
|
|
29878
29975
|
captureStartup: (_props, _identity) => {
|
|
@@ -29907,6 +30004,13 @@ function parseTelemetryEnv(raw) {
|
|
|
29907
30004
|
if (v === "false" || v === "0" || v === "no" || v === "off") return false;
|
|
29908
30005
|
return true;
|
|
29909
30006
|
}
|
|
30007
|
+
function withPlausibleDuration(props) {
|
|
30008
|
+
const { duration_ms, ...rest } = props;
|
|
30009
|
+
if (Number.isFinite(duration_ms) && duration_ms >= 0 && duration_ms <= DURATION_PLAUSIBILITY_CEILING_MS) {
|
|
30010
|
+
return { ...rest, duration_ms };
|
|
30011
|
+
}
|
|
30012
|
+
return { ...rest, duration_ms_raw: duration_ms, duration_implausible: true };
|
|
30013
|
+
}
|
|
29910
30014
|
function initTelemetry(opts) {
|
|
29911
30015
|
if (!parseTelemetryEnv(process.env.LEADBAY_TELEMETRY_ENABLED)) return NOOP_TELEMETRY;
|
|
29912
30016
|
if (process.env.NODE_ENV === "test") return NOOP_TELEMETRY;
|
|
@@ -30075,14 +30179,17 @@ function initTelemetry(opts) {
|
|
|
30075
30179
|
return identityPromise;
|
|
30076
30180
|
},
|
|
30077
30181
|
captureToolCall(props, identity) {
|
|
30078
|
-
emit(EV_TOOL_CALL,
|
|
30182
|
+
emit(EV_TOOL_CALL, withPlausibleDuration(props), identity);
|
|
30079
30183
|
},
|
|
30080
30184
|
captureCompositeCall(props, identity) {
|
|
30081
|
-
emit(EV_COMPOSITE_CALL,
|
|
30185
|
+
emit(EV_COMPOSITE_CALL, withPlausibleDuration(props), identity);
|
|
30082
30186
|
},
|
|
30083
30187
|
captureQuotaHit(props, identity) {
|
|
30084
30188
|
emit(EV_QUOTA_HIT, { ...props }, identity);
|
|
30085
30189
|
},
|
|
30190
|
+
captureToolTimeout(props, identity) {
|
|
30191
|
+
emit(EV_TOOL_TIMEOUT, { ...props }, identity);
|
|
30192
|
+
},
|
|
30086
30193
|
captureTopupLink(props, identity) {
|
|
30087
30194
|
emit(EV_TOPUP_LINK, { ...props }, identity);
|
|
30088
30195
|
},
|
|
@@ -30992,6 +31099,16 @@ function buildServer(client, opts = {}) {
|
|
|
30992
31099
|
source: "business"
|
|
30993
31100
|
};
|
|
30994
31101
|
};
|
|
31102
|
+
const captureTimeoutAlert = (toolName, envelope, triggeredBy) => {
|
|
31103
|
+
const ms = envelope._meta?.timeout_ms;
|
|
31104
|
+
telemetry.captureToolTimeout({
|
|
31105
|
+
tool: toolName,
|
|
31106
|
+
...typeof ms === "number" ? { timeout_ms: ms } : {},
|
|
31107
|
+
...envelope._meta?.endpoint ? { endpoint: envelope._meta.endpoint } : {},
|
|
31108
|
+
...envelope._meta?.region ? { region: envelope._meta.region } : {},
|
|
31109
|
+
...triggeredBy !== void 0 ? { triggered_by: triggeredBy } : {}
|
|
31110
|
+
});
|
|
31111
|
+
};
|
|
30995
31112
|
const captureAgentMemoryTelemetry = (toolName, result) => {
|
|
30996
31113
|
if (!result || typeof result !== "object") return;
|
|
30997
31114
|
const meta = result._meta ?? {};
|
|
@@ -31156,7 +31273,7 @@ ${url}
|
|
|
31156
31273
|
isError: true
|
|
31157
31274
|
};
|
|
31158
31275
|
}
|
|
31159
|
-
const result = await tool.execute(client, args, {
|
|
31276
|
+
const result = await runWithRequestSignal(extra.signal, () => tool.execute(client, args, {
|
|
31160
31277
|
logger: opts.logger,
|
|
31161
31278
|
bulkTracker: opts.bulkTracker,
|
|
31162
31279
|
notificationsInbox: opts.notificationsInbox,
|
|
@@ -31189,7 +31306,7 @@ ${url}
|
|
|
31189
31306
|
...report.tool_called ? { tool_called: report.tool_called } : {},
|
|
31190
31307
|
...report.severity ? { severity: report.severity } : {}
|
|
31191
31308
|
}) === true
|
|
31192
|
-
});
|
|
31309
|
+
}));
|
|
31193
31310
|
await maybeAttachUpdate(name, result);
|
|
31194
31311
|
maybeAttachNotifications(result);
|
|
31195
31312
|
if (result && typeof result === "object" && result.error === true) {
|
|
@@ -31205,6 +31322,9 @@ ${url}
|
|
|
31205
31322
|
endpoint: result._meta?.endpoint
|
|
31206
31323
|
});
|
|
31207
31324
|
}
|
|
31325
|
+
if (envCode === "TIMEOUT") {
|
|
31326
|
+
captureTimeoutAlert(name, result, triggered_by);
|
|
31327
|
+
}
|
|
31208
31328
|
telemetry.captureToolCall({
|
|
31209
31329
|
tool: name,
|
|
31210
31330
|
ok: false,
|
|
@@ -31335,6 +31455,9 @@ ${url}
|
|
|
31335
31455
|
endpoint: err._meta?.endpoint
|
|
31336
31456
|
});
|
|
31337
31457
|
}
|
|
31458
|
+
if (!skipAnalytics && err.code === "TIMEOUT") {
|
|
31459
|
+
captureTimeoutAlert(name, err, triggered_by);
|
|
31460
|
+
}
|
|
31338
31461
|
const httpStatus2 = err._meta?.http_status;
|
|
31339
31462
|
if (!skipAnalytics) {
|
|
31340
31463
|
telemetry.captureToolCall({
|
|
@@ -32838,7 +32961,7 @@ var OAUTH_BASE_URLS = {
|
|
|
32838
32961
|
fr: "https://staging.api.leadbay.app"
|
|
32839
32962
|
}
|
|
32840
32963
|
};
|
|
32841
|
-
var VERSION = "0.32.
|
|
32964
|
+
var VERSION = "0.32.4";
|
|
32842
32965
|
var HELP = `
|
|
32843
32966
|
leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
|
|
32844
32967
|
|
package/dist/http-server.js
CHANGED
|
@@ -2792,30 +2792,59 @@ function getPrompt(name, args = {}) {
|
|
|
2792
2792
|
|
|
2793
2793
|
// ../core/dist/client.js
|
|
2794
2794
|
import https from "https";
|
|
2795
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
2795
2796
|
import { readdirSync, readFileSync, existsSync } from "fs";
|
|
2796
2797
|
import { join } from "path";
|
|
2797
2798
|
var LENS_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
2798
2799
|
var TASTE_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
2799
2800
|
var ME_CACHE_TTL_MS = 60 * 1e3;
|
|
2800
2801
|
var MAX_CONCURRENT = 5;
|
|
2802
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 6e5;
|
|
2803
|
+
function defaultTimeoutMs() {
|
|
2804
|
+
const raw = process.env.LEADBAY_TIMEOUT_MS;
|
|
2805
|
+
if (raw === void 0 || raw.trim() === "")
|
|
2806
|
+
return DEFAULT_REQUEST_TIMEOUT_MS;
|
|
2807
|
+
const n = Number(raw);
|
|
2808
|
+
return Number.isFinite(n) ? n : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
2809
|
+
}
|
|
2810
|
+
var requestSignalStore = new AsyncLocalStorage();
|
|
2811
|
+
function runWithRequestSignal(signal, fn) {
|
|
2812
|
+
return requestSignalStore.run(signal, fn);
|
|
2813
|
+
}
|
|
2814
|
+
function makeCancelledError(method, url) {
|
|
2815
|
+
const err = new Error(`Request cancelled: ${method} ${url}`);
|
|
2816
|
+
err.name = "AbortError";
|
|
2817
|
+
err.code = "CANCELLED";
|
|
2818
|
+
return err;
|
|
2819
|
+
}
|
|
2801
2820
|
var REGIONS = {
|
|
2802
2821
|
us: "https://api-us.leadbay.app",
|
|
2803
2822
|
fr: "https://api-fr.leadbay.app"
|
|
2804
2823
|
};
|
|
2805
2824
|
var API_VERSION = "1.6";
|
|
2806
2825
|
var API_PREFIX = `/${API_VERSION}`;
|
|
2807
|
-
function httpsRequest(method, url, headers, body, timeoutMs) {
|
|
2826
|
+
function httpsRequest(method, url, headers, body, timeoutMs, signal) {
|
|
2827
|
+
const deadlineMs = timeoutMs ?? defaultTimeoutMs();
|
|
2828
|
+
const abortSignal = signal ?? requestSignalStore.getStore();
|
|
2829
|
+
const abortSafe = method.toUpperCase() === "GET";
|
|
2808
2830
|
return new Promise((resolve, reject) => {
|
|
2809
2831
|
const start = Date.now();
|
|
2832
|
+
if (abortSignal?.aborted) {
|
|
2833
|
+
reject(makeCancelledError(method, url));
|
|
2834
|
+
return;
|
|
2835
|
+
}
|
|
2810
2836
|
const parsed = new URL(url);
|
|
2811
2837
|
const reqHeaders = { ...headers };
|
|
2812
2838
|
if (body !== void 0) {
|
|
2813
2839
|
reqHeaders["Content-Length"] = Buffer.byteLength(body);
|
|
2814
2840
|
}
|
|
2815
2841
|
let deadline;
|
|
2842
|
+
let onAbort;
|
|
2816
2843
|
const clearDeadline = () => {
|
|
2817
2844
|
if (deadline !== void 0)
|
|
2818
2845
|
clearTimeout(deadline);
|
|
2846
|
+
if (onAbort)
|
|
2847
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
2819
2848
|
};
|
|
2820
2849
|
const req = https.request({
|
|
2821
2850
|
hostname: parsed.hostname,
|
|
@@ -2836,15 +2865,24 @@ function httpsRequest(method, url, headers, body, timeoutMs) {
|
|
|
2836
2865
|
});
|
|
2837
2866
|
});
|
|
2838
2867
|
});
|
|
2839
|
-
if (
|
|
2868
|
+
if (deadlineMs > 0) {
|
|
2840
2869
|
deadline = setTimeout(() => {
|
|
2841
2870
|
req.destroy?.();
|
|
2842
|
-
const err = new Error(`Request timed out after ${
|
|
2871
|
+
const err = new Error(`Request timed out after ${deadlineMs}ms: ${method} ${url}`);
|
|
2843
2872
|
err.code = "TIMEOUT";
|
|
2873
|
+
err.timeout_ms = deadlineMs;
|
|
2844
2874
|
reject(err);
|
|
2845
|
-
},
|
|
2875
|
+
}, deadlineMs);
|
|
2846
2876
|
deadline.unref?.();
|
|
2847
2877
|
}
|
|
2878
|
+
if (abortSignal && abortSafe) {
|
|
2879
|
+
onAbort = () => {
|
|
2880
|
+
req.destroy?.();
|
|
2881
|
+
clearDeadline();
|
|
2882
|
+
reject(makeCancelledError(method, url));
|
|
2883
|
+
};
|
|
2884
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
2885
|
+
}
|
|
2848
2886
|
req.on("error", (e) => {
|
|
2849
2887
|
clearDeadline();
|
|
2850
2888
|
reject(e);
|
|
@@ -3166,6 +3204,8 @@ var LeadbayClient = class _LeadbayClient {
|
|
|
3166
3204
|
throw this.mapErrorResponse(res.status, res.body, path, res.headers);
|
|
3167
3205
|
}
|
|
3168
3206
|
return JSON.parse(res.body);
|
|
3207
|
+
} catch (e) {
|
|
3208
|
+
throw this.mapTransportError(e, `${method} ${path}`);
|
|
3169
3209
|
} finally {
|
|
3170
3210
|
this.releaseSemaphore();
|
|
3171
3211
|
}
|
|
@@ -3197,6 +3237,8 @@ var LeadbayClient = class _LeadbayClient {
|
|
|
3197
3237
|
if (res.status < 200 || res.status >= 300) {
|
|
3198
3238
|
throw this.mapErrorResponse(res.status, res.body, path, res.headers);
|
|
3199
3239
|
}
|
|
3240
|
+
} catch (e) {
|
|
3241
|
+
throw this.mapTransportError(e, `${method} ${path}`);
|
|
3200
3242
|
} finally {
|
|
3201
3243
|
this.releaseSemaphore();
|
|
3202
3244
|
}
|
|
@@ -3234,6 +3276,8 @@ var LeadbayClient = class _LeadbayClient {
|
|
|
3234
3276
|
throw this.mapErrorResponse(res.status, res.body, path, res.headers);
|
|
3235
3277
|
}
|
|
3236
3278
|
return JSON.parse(res.body);
|
|
3279
|
+
} catch (e) {
|
|
3280
|
+
throw this.mapTransportError(e, `${method} ${path}`);
|
|
3237
3281
|
} finally {
|
|
3238
3282
|
this.releaseSemaphore();
|
|
3239
3283
|
}
|
|
@@ -3294,6 +3338,29 @@ var LeadbayClient = class _LeadbayClient {
|
|
|
3294
3338
|
would_call: { method, path: fullPath, body: journalBody }
|
|
3295
3339
|
};
|
|
3296
3340
|
}
|
|
3341
|
+
/**
|
|
3342
|
+
* Turn httpsRequest's raw TIMEOUT rejection into the `{error:true, code, …}`
|
|
3343
|
+
* envelope every other failure already speaks, so the agent gets something it
|
|
3344
|
+
* can read out to the user and act on rather than a bare Error string. Any
|
|
3345
|
+
* other rejection (ECONNRESET, DNS, a mapped 4xx/5xx) passes through untouched
|
|
3346
|
+
* — this is a translation, not a catch-all.
|
|
3347
|
+
*
|
|
3348
|
+
* The code stays "TIMEOUT" so the hosted auth probe's existing branch
|
|
3349
|
+
* (auth-http.ts) keeps classifying it as a transient fault and moves to the
|
|
3350
|
+
* sibling region instead of declaring a live token expired.
|
|
3351
|
+
*/
|
|
3352
|
+
mapTransportError(e, endpoint) {
|
|
3353
|
+
const err = e;
|
|
3354
|
+
if (err?.code !== "TIMEOUT")
|
|
3355
|
+
return e;
|
|
3356
|
+
const ms = err.timeout_ms ?? defaultTimeoutMs();
|
|
3357
|
+
const envelope = this.makeError("TIMEOUT", `Leadbay did not respond within ${ms}ms \u2014 the request was cancelled`, "The connection was accepted but no response came back, so this is a Leadbay-side stall, not a bad request. It is transient: retry the same call once. If it times out again, tell the user Leadbay is not responding right now and offer to report it with leadbay_report_friction.", endpoint);
|
|
3358
|
+
if (envelope._meta) {
|
|
3359
|
+
envelope._meta.timeout_ms = ms;
|
|
3360
|
+
envelope._meta.latency_ms = ms;
|
|
3361
|
+
}
|
|
3362
|
+
return envelope;
|
|
3363
|
+
}
|
|
3297
3364
|
mapErrorResponse(status, rawBody, endpoint, headers) {
|
|
3298
3365
|
let parsed;
|
|
3299
3366
|
try {
|
|
@@ -3415,6 +3482,8 @@ var LeadbayClient = class _LeadbayClient {
|
|
|
3415
3482
|
this.telemetryEnabledFromStamp = false;
|
|
3416
3483
|
}
|
|
3417
3484
|
return observed;
|
|
3485
|
+
} catch (e) {
|
|
3486
|
+
throw this.mapTransportError(e, "GET /users/me");
|
|
3418
3487
|
} finally {
|
|
3419
3488
|
this.releaseSemaphore();
|
|
3420
3489
|
}
|
|
@@ -8032,9 +8101,82 @@ var COMPOSITE_FILE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
8032
8101
|
"leadbay_tour_plan"
|
|
8033
8102
|
]);
|
|
8034
8103
|
|
|
8104
|
+
// ../core/dist/notifications/revise-hint.js
|
|
8105
|
+
var HINT_BULK_ENRICH = "Contact enrichment just finished. Revise any prior output that named these leads' contacts (outreach drafts, contact lists, recommended-lead lists with contact_count, NEXT STEPS asking the user to wait for emails / phones). Re-fetch contacts via leadbay_get_contacts for the affected leads.";
|
|
8106
|
+
var HINT_BULK_QUALIFY = "Lead qualification just finished. Revise any prior lead list / ranking / outreach shortlist that depended on ai_agent_lead_score for these leads \u2014 today's leads, top-of-inbox, followups maps, prepare-outreach shortlists. Re-pull qualification answers via leadbay_research_lead_by_id or re-rank via leadbay_pull_leads.";
|
|
8107
|
+
var HINT_IMPORT = "CSV / CRM import just finished. Revise any prior output that referenced 'leads available' before the import landed \u2014 lead lists pulled from the affected lens, 'what's new today', followup planning. Re-pull the affected lens via leadbay_pull_leads / leadbay_pull_followups.";
|
|
8108
|
+
var HINT_OTHER = "Background work just completed. If you referenced its subject in prior output, re-fetch the affected data and revise.";
|
|
8109
|
+
function reviseHintFor(kind) {
|
|
8110
|
+
switch (kind) {
|
|
8111
|
+
case "bulk_enrich":
|
|
8112
|
+
return HINT_BULK_ENRICH;
|
|
8113
|
+
case "bulk_qualify":
|
|
8114
|
+
return HINT_BULK_QUALIFY;
|
|
8115
|
+
case "import":
|
|
8116
|
+
return HINT_IMPORT;
|
|
8117
|
+
default:
|
|
8118
|
+
return HINT_OTHER;
|
|
8119
|
+
}
|
|
8120
|
+
}
|
|
8121
|
+
function inferKind(n) {
|
|
8122
|
+
if (n.links.some((l) => l.type === "bulk_enrichment"))
|
|
8123
|
+
return "bulk_enrich";
|
|
8124
|
+
if (n.file_import_id)
|
|
8125
|
+
return "import";
|
|
8126
|
+
if (n.bulk_progress)
|
|
8127
|
+
return "bulk_qualify";
|
|
8128
|
+
return "other";
|
|
8129
|
+
}
|
|
8130
|
+
function anchorIdFor(n, kind) {
|
|
8131
|
+
if (kind === "bulk_enrich") {
|
|
8132
|
+
const link = n.links.find((l) => l.type === "bulk_enrichment");
|
|
8133
|
+
return link ? String(link.id) : null;
|
|
8134
|
+
}
|
|
8135
|
+
if (kind === "import")
|
|
8136
|
+
return n.file_import_id;
|
|
8137
|
+
return null;
|
|
8138
|
+
}
|
|
8139
|
+
function toInboxEntry(n) {
|
|
8140
|
+
const kind = inferKind(n);
|
|
8141
|
+
return {
|
|
8142
|
+
notification_id: n.id,
|
|
8143
|
+
kind,
|
|
8144
|
+
anchor_id: anchorIdFor(n, kind),
|
|
8145
|
+
title: n.title,
|
|
8146
|
+
bulk_progress: n.bulk_progress,
|
|
8147
|
+
completed_at: n.updated_at,
|
|
8148
|
+
revise_hint: reviseHintFor(kind)
|
|
8149
|
+
};
|
|
8150
|
+
}
|
|
8151
|
+
|
|
8035
8152
|
// ../core/dist/notifications/inbox.js
|
|
8036
8153
|
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
8037
8154
|
|
|
8155
|
+
// ../core/dist/notifications/catch-up.js
|
|
8156
|
+
var DEFAULT_COUNT = 50;
|
|
8157
|
+
function isTerminalUnseen(n) {
|
|
8158
|
+
if (!n.bulk_progress)
|
|
8159
|
+
return false;
|
|
8160
|
+
if (n.in_progress)
|
|
8161
|
+
return false;
|
|
8162
|
+
if (n.first_seen_at)
|
|
8163
|
+
return false;
|
|
8164
|
+
return true;
|
|
8165
|
+
}
|
|
8166
|
+
async function fetchTerminalNotifications(client, opts = {}) {
|
|
8167
|
+
try {
|
|
8168
|
+
const page = await client.listNotifications({
|
|
8169
|
+
archived: false,
|
|
8170
|
+
page: 0,
|
|
8171
|
+
count: opts.count ?? DEFAULT_COUNT
|
|
8172
|
+
});
|
|
8173
|
+
return page.items.filter(isTerminalUnseen).map(toInboxEntry);
|
|
8174
|
+
} catch (err) {
|
|
8175
|
+
opts.logger?.warn?.(`notifications.fetch_terminal failed: ${err?.message ?? err?.code ?? err}`);
|
|
8176
|
+
return [];
|
|
8177
|
+
}
|
|
8178
|
+
}
|
|
8179
|
+
|
|
8038
8180
|
// ../core/dist/tool-descriptions.generated.js
|
|
8039
8181
|
var leadbay_account_history = `## WHEN TO USE
|
|
8040
8182
|
|
|
@@ -22421,6 +22563,8 @@ var accountStatus = {
|
|
|
22421
22563
|
ctx?.logger?.warn?.(`account_status: quota_status failed: ${err?.message ?? err?.code ?? err}`);
|
|
22422
22564
|
}
|
|
22423
22565
|
}
|
|
22566
|
+
const inbox = ctx?.notificationsInbox;
|
|
22567
|
+
const notifications = inbox ? inbox.list() : await fetchTerminalNotifications(client, { logger: ctx?.logger });
|
|
22424
22568
|
const lensId = me.last_requested_lens ?? null;
|
|
22425
22569
|
const lensAsked = typeof ctx?.triggered_by === "string" && /\b(lens|lenses|audience|targeting|segment|filter)\b/i.test(ctx.triggered_by);
|
|
22426
22570
|
let last_requested_lens_name = null;
|
|
@@ -22461,13 +22605,12 @@ var accountStatus = {
|
|
|
22461
22605
|
// on /me are intentionally NOT surfaced — they're defunct (see
|
|
22462
22606
|
// SHAPE-DRIFT.md probe round 4).
|
|
22463
22607
|
quota,
|
|
22464
|
-
//
|
|
22465
|
-
//
|
|
22466
|
-
//
|
|
22467
|
-
//
|
|
22468
|
-
//
|
|
22469
|
-
|
|
22470
|
-
notifications: ctx?.notificationsInbox?.list() ?? [],
|
|
22608
|
+
// Terminal bulk-progress notifications. Same shape the MCP server
|
|
22609
|
+
// attaches to `_meta.notifications` on every tool response — carried
|
|
22610
|
+
// here as a top-level field so the agent's daily-rhythm check-in (this
|
|
22611
|
+
// composite) sees them without reading _meta. See the read above for
|
|
22612
|
+
// why hosted takes a different path to the same list.
|
|
22613
|
+
notifications,
|
|
22471
22614
|
// Non-null ONLY when the quota_status call failed. The agent must treat
|
|
22472
22615
|
// this as "could not read quota" — NOT as zero usage, and NOT as a broken
|
|
22473
22616
|
// login (the token just authenticated /users/me above). product#3761.
|
|
@@ -22481,7 +22624,7 @@ var accountStatus = {
|
|
|
22481
22624
|
|
|
22482
22625
|
// ../core/dist/composite/bulk-qualify-leads.js
|
|
22483
22626
|
var PAGE_SIZE = 50;
|
|
22484
|
-
var
|
|
22627
|
+
var DEFAULT_COUNT2 = 10;
|
|
22485
22628
|
var MAX_COUNT = 25;
|
|
22486
22629
|
var DEFAULT_PER_LEAD_BUDGET_MS = 9e4;
|
|
22487
22630
|
var DEFAULT_TOTAL_BUDGET_MS2 = 5 * 6e4;
|
|
@@ -22529,7 +22672,7 @@ var bulkQualifyLeads = {
|
|
|
22529
22672
|
properties: {
|
|
22530
22673
|
count: {
|
|
22531
22674
|
type: "number",
|
|
22532
|
-
description: `How many fresh leads to qualify (default ${
|
|
22675
|
+
description: `How many fresh leads to qualify (default ${DEFAULT_COUNT2}, max ${MAX_COUNT})`
|
|
22533
22676
|
},
|
|
22534
22677
|
leadIds: {
|
|
22535
22678
|
type: "array",
|
|
@@ -22620,7 +22763,7 @@ var bulkQualifyLeads = {
|
|
|
22620
22763
|
]
|
|
22621
22764
|
},
|
|
22622
22765
|
execute: async (client, params, ctx) => {
|
|
22623
|
-
const wantCount = Math.min(params.count ??
|
|
22766
|
+
const wantCount = Math.min(params.count ?? DEFAULT_COUNT2, MAX_COUNT);
|
|
22624
22767
|
const perLeadBudget = params.per_lead_budget_ms ?? DEFAULT_PER_LEAD_BUDGET_MS;
|
|
22625
22768
|
const totalBudget = params.total_budget_ms ?? DEFAULT_TOTAL_BUDGET_MS2;
|
|
22626
22769
|
const totalDeadline = Date.now() + totalBudget;
|
|
@@ -27663,6 +27806,7 @@ var EMBEDDED_SENTRY_DSN = "https://301f1c433433b76132956ed5415bea19@o45058744368
|
|
|
27663
27806
|
// src/telemetry-events.ts
|
|
27664
27807
|
var EV_TOOL_CALL = "mcp tool called";
|
|
27665
27808
|
var EV_QUOTA_HIT = "mcp quota hit";
|
|
27809
|
+
var EV_TOOL_TIMEOUT = "mcp tool timeout";
|
|
27666
27810
|
var EV_TOPUP_LINK = "mcp topup link created";
|
|
27667
27811
|
var EV_STARTUP = "mcp startup";
|
|
27668
27812
|
var EV_MCP_UPDATE_CHECK = "mcp update check";
|
|
@@ -27673,6 +27817,7 @@ var EV_MCP_VERSION_UPDATED = "mcp version updated";
|
|
|
27673
27817
|
var EV_AGENT_MEMORY_CAPTURED = "agent_memory_captured";
|
|
27674
27818
|
var EV_AGENT_MEMORY_RECALLED = "agent_memory_recalled";
|
|
27675
27819
|
var EV_AGENT_MEMORY_PRUNED = "agent_memory_pruned";
|
|
27820
|
+
var DURATION_PLAUSIBILITY_CEILING_MS = 6e5;
|
|
27676
27821
|
var EV_FRICTION_REPORTED = "mcp friction reported";
|
|
27677
27822
|
var EV_COMPOSITE_CALL = "mcp composite call";
|
|
27678
27823
|
|
|
@@ -27686,6 +27831,8 @@ var NOOP_TELEMETRY = {
|
|
|
27686
27831
|
},
|
|
27687
27832
|
captureQuotaHit: (_props, _identity) => {
|
|
27688
27833
|
},
|
|
27834
|
+
captureToolTimeout: (_props, _identity) => {
|
|
27835
|
+
},
|
|
27689
27836
|
captureTopupLink: (_props, _identity) => {
|
|
27690
27837
|
},
|
|
27691
27838
|
captureStartup: (_props, _identity) => {
|
|
@@ -27720,6 +27867,13 @@ function parseTelemetryEnv(raw) {
|
|
|
27720
27867
|
if (v === "false" || v === "0" || v === "no" || v === "off") return false;
|
|
27721
27868
|
return true;
|
|
27722
27869
|
}
|
|
27870
|
+
function withPlausibleDuration(props) {
|
|
27871
|
+
const { duration_ms, ...rest } = props;
|
|
27872
|
+
if (Number.isFinite(duration_ms) && duration_ms >= 0 && duration_ms <= DURATION_PLAUSIBILITY_CEILING_MS) {
|
|
27873
|
+
return { ...rest, duration_ms };
|
|
27874
|
+
}
|
|
27875
|
+
return { ...rest, duration_ms_raw: duration_ms, duration_implausible: true };
|
|
27876
|
+
}
|
|
27723
27877
|
function initTelemetry(opts) {
|
|
27724
27878
|
if (!parseTelemetryEnv(process.env.LEADBAY_TELEMETRY_ENABLED)) return NOOP_TELEMETRY;
|
|
27725
27879
|
if (process.env.NODE_ENV === "test") return NOOP_TELEMETRY;
|
|
@@ -27888,14 +28042,17 @@ function initTelemetry(opts) {
|
|
|
27888
28042
|
return identityPromise;
|
|
27889
28043
|
},
|
|
27890
28044
|
captureToolCall(props, identity) {
|
|
27891
|
-
emit(EV_TOOL_CALL,
|
|
28045
|
+
emit(EV_TOOL_CALL, withPlausibleDuration(props), identity);
|
|
27892
28046
|
},
|
|
27893
28047
|
captureCompositeCall(props, identity) {
|
|
27894
|
-
emit(EV_COMPOSITE_CALL,
|
|
28048
|
+
emit(EV_COMPOSITE_CALL, withPlausibleDuration(props), identity);
|
|
27895
28049
|
},
|
|
27896
28050
|
captureQuotaHit(props, identity) {
|
|
27897
28051
|
emit(EV_QUOTA_HIT, { ...props }, identity);
|
|
27898
28052
|
},
|
|
28053
|
+
captureToolTimeout(props, identity) {
|
|
28054
|
+
emit(EV_TOOL_TIMEOUT, { ...props }, identity);
|
|
28055
|
+
},
|
|
27899
28056
|
captureTopupLink(props, identity) {
|
|
27900
28057
|
emit(EV_TOPUP_LINK, { ...props }, identity);
|
|
27901
28058
|
},
|
|
@@ -28783,6 +28940,16 @@ function buildServer(client, opts = {}) {
|
|
|
28783
28940
|
source: "business"
|
|
28784
28941
|
};
|
|
28785
28942
|
};
|
|
28943
|
+
const captureTimeoutAlert = (toolName, envelope, triggeredBy) => {
|
|
28944
|
+
const ms = envelope._meta?.timeout_ms;
|
|
28945
|
+
telemetry2.captureToolTimeout({
|
|
28946
|
+
tool: toolName,
|
|
28947
|
+
...typeof ms === "number" ? { timeout_ms: ms } : {},
|
|
28948
|
+
...envelope._meta?.endpoint ? { endpoint: envelope._meta.endpoint } : {},
|
|
28949
|
+
...envelope._meta?.region ? { region: envelope._meta.region } : {},
|
|
28950
|
+
...triggeredBy !== void 0 ? { triggered_by: triggeredBy } : {}
|
|
28951
|
+
});
|
|
28952
|
+
};
|
|
28786
28953
|
const captureAgentMemoryTelemetry = (toolName, result) => {
|
|
28787
28954
|
if (!result || typeof result !== "object") return;
|
|
28788
28955
|
const meta = result._meta ?? {};
|
|
@@ -28947,7 +29114,7 @@ ${url}
|
|
|
28947
29114
|
isError: true
|
|
28948
29115
|
};
|
|
28949
29116
|
}
|
|
28950
|
-
const result = await tool.execute(client, args, {
|
|
29117
|
+
const result = await runWithRequestSignal(extra.signal, () => tool.execute(client, args, {
|
|
28951
29118
|
logger: opts.logger,
|
|
28952
29119
|
bulkTracker: opts.bulkTracker,
|
|
28953
29120
|
notificationsInbox: opts.notificationsInbox,
|
|
@@ -28980,7 +29147,7 @@ ${url}
|
|
|
28980
29147
|
...report.tool_called ? { tool_called: report.tool_called } : {},
|
|
28981
29148
|
...report.severity ? { severity: report.severity } : {}
|
|
28982
29149
|
}) === true
|
|
28983
|
-
});
|
|
29150
|
+
}));
|
|
28984
29151
|
await maybeAttachUpdate(name, result);
|
|
28985
29152
|
maybeAttachNotifications(result);
|
|
28986
29153
|
if (result && typeof result === "object" && result.error === true) {
|
|
@@ -28996,6 +29163,9 @@ ${url}
|
|
|
28996
29163
|
endpoint: result._meta?.endpoint
|
|
28997
29164
|
});
|
|
28998
29165
|
}
|
|
29166
|
+
if (envCode === "TIMEOUT") {
|
|
29167
|
+
captureTimeoutAlert(name, result, triggered_by);
|
|
29168
|
+
}
|
|
28999
29169
|
telemetry2.captureToolCall({
|
|
29000
29170
|
tool: name,
|
|
29001
29171
|
ok: false,
|
|
@@ -29126,6 +29296,9 @@ ${url}
|
|
|
29126
29296
|
endpoint: err._meta?.endpoint
|
|
29127
29297
|
});
|
|
29128
29298
|
}
|
|
29299
|
+
if (!skipAnalytics && err.code === "TIMEOUT") {
|
|
29300
|
+
captureTimeoutAlert(name, err, triggered_by);
|
|
29301
|
+
}
|
|
29129
29302
|
const httpStatus2 = err._meta?.http_status;
|
|
29130
29303
|
if (!skipAnalytics) {
|
|
29131
29304
|
telemetry2.captureToolCall({
|
|
@@ -29374,7 +29547,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
29374
29547
|
}
|
|
29375
29548
|
|
|
29376
29549
|
// src/http-server.ts
|
|
29377
|
-
var VERSION = true ? "0.32.
|
|
29550
|
+
var VERSION = true ? "0.32.4" : "0.0.0-dev";
|
|
29378
29551
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
29379
29552
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
29380
29553
|
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.32.
|
|
3
|
+
"version": "0.32.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",
|