@leadbay/mcp 0.17.3 → 0.18.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 +14 -0
- package/dist/bin.js +683 -67
- package/dist/http-server.js +338 -58
- package/dist/installer-electron.js +1 -1
- package/dist/installer-gui.js +1 -1
- package/package.json +1 -1
package/dist/http-server.js
CHANGED
|
@@ -1756,6 +1756,23 @@ var LeadbayClient = class {
|
|
|
1756
1756
|
await this.resolveOrgId();
|
|
1757
1757
|
await this.resolveTasteProfile();
|
|
1758
1758
|
}
|
|
1759
|
+
// ─── Notifications helpers ────────────────────────────────────────────
|
|
1760
|
+
// Backend exposes `GET /notifications`, `POST /notifications/{id}/seen`,
|
|
1761
|
+
// `POST /notifications/{id}/archive`, plus `GET /ws/ticket?v=1.0` to mint
|
|
1762
|
+
// a one-shot WS URL. See backend/docs/adr/notifications.md for shape.
|
|
1763
|
+
async listNotifications(args = {}) {
|
|
1764
|
+
const params = new URLSearchParams();
|
|
1765
|
+
params.set("archived", String(args.archived ?? false));
|
|
1766
|
+
params.set("page", String(args.page ?? 0));
|
|
1767
|
+
params.set("count", String(args.count ?? 50));
|
|
1768
|
+
return this.request("GET", `/notifications?${params.toString()}`);
|
|
1769
|
+
}
|
|
1770
|
+
async acknowledgeNotification(notificationId, action = "seen") {
|
|
1771
|
+
await this.requestVoid("POST", `/notifications/${notificationId}/${action}`);
|
|
1772
|
+
}
|
|
1773
|
+
async getWsTicket() {
|
|
1774
|
+
return this.request("GET", "/auth/ws?v=1.0");
|
|
1775
|
+
}
|
|
1759
1776
|
makeError(code, message, hint, endpoint, retry_after, http_status) {
|
|
1760
1777
|
const out = { error: true, code, message, hint };
|
|
1761
1778
|
if (endpoint || this._region) {
|
|
@@ -6205,6 +6222,9 @@ var COMPOSITE_FILE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
6205
6222
|
"leadbay_tour_plan"
|
|
6206
6223
|
]);
|
|
6207
6224
|
|
|
6225
|
+
// ../core/dist/notifications/inbox.js
|
|
6226
|
+
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6227
|
+
|
|
6208
6228
|
// ../core/dist/tool-descriptions.generated.js
|
|
6209
6229
|
var leadbay_account_status = `## WHEN TO USE
|
|
6210
6230
|
|
|
@@ -6241,10 +6261,53 @@ Show the user's account state \u2014 admin rights, language, last-active lens, c
|
|
|
6241
6261
|
|
|
6242
6262
|
**After a user tops up, do NOT keep refusing \u2014 RETRY.** If the user signals they topped up / bought credits / added credits, the previous QUOTA_EXCEEDED is invalidated the moment the Stripe webhook lands. RE-CALL \`leadbay_account_status\` to pick up the new state AND retry the originally failed call. The retry itself does not require a successful account_status check first \u2014 a topped-up user has cleared the throttle whether or not your cached snapshot reflects it yet. If the retry hits the wall again, only then re-offer top-up / wait. **A stale quota snapshot is never a reason to gate-keep a topped-up user.**
|
|
6243
6263
|
|
|
6264
|
+
**\`notifications\` block.** The response now includes a top-level \`notifications\` array listing background work the user (or agent) initiated that has since completed (\`bulk_enrich\`, \`bulk_qualify\`, \`import\`). These are signals to revise prior agent outputs the just-finished work might have made stale \u2014 they're NOT a pending-task list for the user. After revising (or confirming nothing is affected), call \`leadbay_acknowledge_notification(notification_id)\`. Full handling protocol below.
|
|
6265
|
+
|
|
6266
|
+
## GATE \u2014 INSPECT \`_meta.notifications\` ON EVERY RESPONSE
|
|
6267
|
+
|
|
6268
|
+
Some Leadbay tool responses include a \`_meta.notifications\` array listing **background work the user (or you) initiated that has since completed**. The three relevant kinds:
|
|
6269
|
+
|
|
6270
|
+
| Kind | What just finished |
|
|
6271
|
+
|---|---|
|
|
6272
|
+
| \`bulk_enrich\` | A bulk contact-enrichment job (emails / phone numbers attached to leads' contacts). |
|
|
6273
|
+
| \`bulk_qualify\` | A bulk lead-qualification job (web-fetch + AI rescore, producing \`ai_agent_lead_score\` and qualification answers). |
|
|
6274
|
+
| \`import\` | A CSV / CRM file import (rows resolved to leads in the user's pipeline). |
|
|
6275
|
+
|
|
6276
|
+
**Your job when you see an entry: revise prior outputs the just-finished work might have made stale.** This is NOT a pending-task list. It's a "your earlier answer used data that has now changed" signal.
|
|
6277
|
+
|
|
6278
|
+
| Kind | Outputs you've produced that may now be stale \u2014 refresh them |
|
|
6279
|
+
|---|---|
|
|
6280
|
+
| \`bulk_enrich\` | Outreach drafts mentioning these leads' contacts; contact lists; recommended-lead lists citing \`contact_count\`; NEXT STEPS that asked the user to wait for emails / phones. Re-fetch via \`leadbay_get_contacts(leadId)\` for the affected leads. |
|
|
6281
|
+
| \`bulk_qualify\` | Lead rankings / shortlists you produced without \`ai_agent_lead_score\`; "today's leads"; followup maps; prepare-outreach picks. Re-pull via \`leadbay_pull_leads\` / \`leadbay_research_lead_by_id\`. |
|
|
6282
|
+
| \`import\` | "Available leads" claims; pulls from the affected lens that ran before the import landed; followup planning that needed the imported set. Re-pull via \`leadbay_pull_leads\` / \`leadbay_pull_followups\`. |
|
|
6283
|
+
|
|
6284
|
+
**After revising (or after confirming no prior output is affected):** call \`leadbay_acknowledge_notification(notification_id)\` so the entry stops resurfacing on every tool response. Ack-and-move-on is correct even when nothing was stale \u2014 that's how the inbox stays focused on what's actually pending.
|
|
6285
|
+
|
|
6286
|
+
**Do NOT** interpret these entries as "things waiting for the user." The user expects you to handle them silently. They are signals to YOU \u2014 agent \u2014 that prior outputs need a refresh.
|
|
6287
|
+
|
|
6288
|
+
**Don't poll.** When you launch a long operation (enrichment / qualification / import), simply continue the conversation \u2014 the next time you call any tool, the completed-work entry will appear in \`_meta.notifications\`. Status tools (\`leadbay_bulk_enrich_status\`, \`leadbay_qualify_status\`, \`leadbay_import_status\`) exist for the case where you want the answer NOW (mid-conversation), not for ambient polling.
|
|
6289
|
+
|
|
6290
|
+
Also surfaced as a top-level \`notifications\` array on \`leadbay_account_status\` \u2014 same shape, same handling.
|
|
6291
|
+
|
|
6292
|
+
|
|
6244
6293
|
WHEN TO USE: at the start of a session to know what the agent can/can't do, after a 429 to explain to the user which resource window was exhausted and when it resets (and to offer the top-up alternative), and after the user signals a top-up so the agent can resume the interrupted workflow.
|
|
6245
6294
|
|
|
6246
6295
|
WHEN NOT TO USE: as a pre-flight gate before bulk ops \u2014 operations themselves return 429; this tool is for context, not gating. And: a recent quota snapshot showing "exhausted" is NOT a reason to refuse a write call when the user has just topped up \u2014 re-call this tool first, then proceed.
|
|
6247
6296
|
`;
|
|
6297
|
+
var leadbay_acknowledge_notification = `Acknowledge a Leadbay notification \u2014 i.e. tell the MCP and the backend "I've seen this and acted on it." Wraps \`POST /1.5/notifications/{id}/seen\` (default) or \`/archive\` (when \`archive:true\`) and drops the entry from the local inbox so subsequent \`_meta.notifications\` payloads stop carrying it.
|
|
6298
|
+
|
|
6299
|
+
**When to call.** After you read an entry from \`_meta.notifications\` or \`account_status.notifications\` and have revised whatever prior output the just-finished background work might have made stale (outreach drafts, lead lists, "available leads" claims, followup plans). Mark-seen tells the human team's pipeline you handled this and prevents the notification from re-surfacing on every subsequent tool response.
|
|
6300
|
+
|
|
6301
|
+
If nothing you produced for the user is affected, ack anyway with \`archive:false\` \u2014 the entry should still clear so the inbox stays focused on what's actually pending.
|
|
6302
|
+
|
|
6303
|
+
Use \`archive:true\` only when you want the row gone from the FE notification dropdown too (e.g. a non-actionable system notification that's already handled). Default behaviour is \`seen\` \u2014 same as the FE dropdown's "click to read" semantics.
|
|
6304
|
+
|
|
6305
|
+
WHEN TO USE: immediately after you finish reviewing / revising in response to a \`_meta.notifications[]\` entry. Idempotent \u2014 calling twice with the same id is safe.
|
|
6306
|
+
|
|
6307
|
+
WHEN NOT TO USE: before doing the revision work; for general "mark all read" sweeps (call once per notification id you've actually consumed).
|
|
6308
|
+
|
|
6309
|
+
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\`.
|
|
6310
|
+
`;
|
|
6248
6311
|
var leadbay_add_leads_to_campaign = `## WHEN TO USE
|
|
6249
6312
|
|
|
6250
6313
|
Trigger phrases: "add leads to <name> campaign", "attach these to <campaign>", "put these in Q2 Push", "add to existing campaign".
|
|
@@ -11148,9 +11211,23 @@ async function completeUploadedChunk(client, upload, mappings, dryRun, perPhaseB
|
|
|
11148
11211
|
await pollPreprocess(client, importId, phaseBudget, ctx, signal);
|
|
11149
11212
|
ctx?.logger?.info?.(`import-leads: preprocess done for importId=${importId}`);
|
|
11150
11213
|
if (dryRun) {
|
|
11151
|
-
return { importId, records: [] };
|
|
11214
|
+
return { importId, records: [], notification_id: null };
|
|
11215
|
+
}
|
|
11216
|
+
let updateMappingsResp = null;
|
|
11217
|
+
try {
|
|
11218
|
+
updateMappingsResp = await client.request("POST", `/imports/${importId}/update_mappings`, mappings);
|
|
11219
|
+
} catch (err) {
|
|
11220
|
+
if (err?.code === "API_ERROR" || err?.code === "NOT_FOUND") {
|
|
11221
|
+
ctx?.logger?.warn?.(`import-leads: update_mappings raw error (${err?.code}); retrying void`);
|
|
11222
|
+
await client.requestVoid("POST", `/imports/${importId}/update_mappings`, mappings);
|
|
11223
|
+
} else {
|
|
11224
|
+
throw err;
|
|
11225
|
+
}
|
|
11226
|
+
}
|
|
11227
|
+
const importNotificationId = updateMappingsResp?.notification_id ?? null;
|
|
11228
|
+
if (importNotificationId) {
|
|
11229
|
+
ctx?.logger?.info?.(`import-leads: notification_id=${importNotificationId} importId=${importId}`);
|
|
11152
11230
|
}
|
|
11153
|
-
await client.requestVoid("POST", `/imports/${importId}/update_mappings`, mappings);
|
|
11154
11231
|
ctx?.logger?.info?.(`import-leads: mappings committed for importId=${importId}`);
|
|
11155
11232
|
const phaseBudget2 = Math.min(perPhaseBudgetMs, Math.max(1, totalDeadline - Date.now()));
|
|
11156
11233
|
await pollProcess(client, importId, phaseBudget2, ctx, signal);
|
|
@@ -11158,7 +11235,7 @@ async function completeUploadedChunk(client, upload, mappings, dryRun, perPhaseB
|
|
|
11158
11235
|
const phaseBudget3 = Math.min(perPhaseBudgetMs, Math.max(1, totalDeadline - Date.now()));
|
|
11159
11236
|
const records = await pollRecordsToTerminal(client, importId, phaseBudget3, chunk.length, ctx, signal);
|
|
11160
11237
|
ctx?.logger?.info?.(`import-leads: ${records.length} records terminal for importId=${importId}`);
|
|
11161
|
-
return { importId, records };
|
|
11238
|
+
return { importId, records, notification_id: importNotificationId };
|
|
11162
11239
|
}
|
|
11163
11240
|
function reconcileOneChunk(prep, chunk, matched, notImported) {
|
|
11164
11241
|
const seenInputIndex = /* @__PURE__ */ new Set();
|
|
@@ -11214,7 +11291,7 @@ function reconcileOneChunk(prep, chunk, matched, notImported) {
|
|
|
11214
11291
|
}
|
|
11215
11292
|
}
|
|
11216
11293
|
}
|
|
11217
|
-
function buildImportLeadsResult(client, prep, importIds, matched, notImported, dryRun, cancelled) {
|
|
11294
|
+
function buildImportLeadsResult(client, prep, importIds, matched, notImported, dryRun, cancelled, notificationIds) {
|
|
11218
11295
|
const leads = [];
|
|
11219
11296
|
const not_imported = [];
|
|
11220
11297
|
if (dryRun) {
|
|
@@ -11279,6 +11356,7 @@ function buildImportLeadsResult(client, prep, importIds, matched, notImported, d
|
|
|
11279
11356
|
leads,
|
|
11280
11357
|
not_imported,
|
|
11281
11358
|
importIds,
|
|
11359
|
+
notification_ids: notificationIds,
|
|
11282
11360
|
region: client.region,
|
|
11283
11361
|
cancelled: cancelled || void 0,
|
|
11284
11362
|
dry_run: dryRun || void 0,
|
|
@@ -11466,6 +11544,7 @@ var importLeads = {
|
|
|
11466
11544
|
leads: [],
|
|
11467
11545
|
not_imported,
|
|
11468
11546
|
importIds: [],
|
|
11547
|
+
notification_ids: [],
|
|
11469
11548
|
region: client.region,
|
|
11470
11549
|
dry_run: dryRun || void 0,
|
|
11471
11550
|
_meta: client.lastMeta ?? {
|
|
@@ -11520,6 +11599,10 @@ var importLeads = {
|
|
|
11520
11599
|
status: "running",
|
|
11521
11600
|
handle_id: reservation.record.bulk_id,
|
|
11522
11601
|
importIds: importIds2,
|
|
11602
|
+
// Notifications fire from update_mappings, which the background
|
|
11603
|
+
// task hasn't called yet at this point. They surface via the WS
|
|
11604
|
+
// listener / catch-up REST on subsequent agent turns.
|
|
11605
|
+
notification_ids: [],
|
|
11523
11606
|
progress: {
|
|
11524
11607
|
phase: reservation.record.status === "complete" ? "complete" : importIds2.length > 0 ? "preprocess" : "queued",
|
|
11525
11608
|
records_processed: reservation.record.status === "complete" ? reservation.record.records_total : 0,
|
|
@@ -11540,6 +11623,7 @@ var importLeads = {
|
|
|
11540
11623
|
}
|
|
11541
11624
|
ctx?.logger?.info?.(`import-leads(${prep.mode}): ${prep.validInputs.length} rows \u2192 ${chunks.length} chunk(s); dry_run=${dryRun}, totalBudgetMs=${totalBudget}`);
|
|
11542
11625
|
const importIds = [];
|
|
11626
|
+
const notificationIds = [];
|
|
11543
11627
|
const matched = /* @__PURE__ */ new Map();
|
|
11544
11628
|
const notImported = /* @__PURE__ */ new Map();
|
|
11545
11629
|
let cancelled = false;
|
|
@@ -11551,6 +11635,9 @@ var importLeads = {
|
|
|
11551
11635
|
for (let i = 0; i < chunks.length; i++) {
|
|
11552
11636
|
const chunk = chunks[i];
|
|
11553
11637
|
const out = await runOneChunk(client, chunk, i, chunks.length, prep.header, prep.mappings, dryRun, perPhaseBudget, totalDeadline, ctx, signal, recordImportId);
|
|
11638
|
+
if (out.notification_id && !notificationIds.includes(out.notification_id)) {
|
|
11639
|
+
notificationIds.push(out.notification_id);
|
|
11640
|
+
}
|
|
11554
11641
|
if (!dryRun) {
|
|
11555
11642
|
reconcileOneChunk(prep, out, matched, notImported);
|
|
11556
11643
|
}
|
|
@@ -11571,7 +11658,7 @@ var importLeads = {
|
|
|
11571
11658
|
throw err;
|
|
11572
11659
|
}
|
|
11573
11660
|
}
|
|
11574
|
-
return buildImportLeadsResult(client, prep, importIds, matched, notImported, dryRun, cancelled);
|
|
11661
|
+
return buildImportLeadsResult(client, prep, importIds, matched, notImported, dryRun, cancelled, notificationIds);
|
|
11575
11662
|
}
|
|
11576
11663
|
};
|
|
11577
11664
|
async function runImportInBackground(client, prep, uploadedChunks, opts, ctx, handleId) {
|
|
@@ -11588,17 +11675,21 @@ async function runImportInBackground(client, prep, uploadedChunks, opts, ctx, ha
|
|
|
11588
11675
|
void (async () => {
|
|
11589
11676
|
const bgCtx = { logger: ctx.logger, bulkTracker: tracker };
|
|
11590
11677
|
const importIds = uploadedChunks.map((chunk) => chunk.importId);
|
|
11678
|
+
const notificationIds = [];
|
|
11591
11679
|
const matched = /* @__PURE__ */ new Map();
|
|
11592
11680
|
const notImported = /* @__PURE__ */ new Map();
|
|
11593
11681
|
try {
|
|
11594
11682
|
const totalDeadline = Date.now() + opts.totalBudget;
|
|
11595
11683
|
for (const upload of uploadedChunks) {
|
|
11596
11684
|
const out = await completeUploadedChunk(client, upload, prep.mappings, opts.dryRun, opts.perPhaseBudget, totalDeadline, bgCtx, void 0);
|
|
11685
|
+
if (out.notification_id && !notificationIds.includes(out.notification_id)) {
|
|
11686
|
+
notificationIds.push(out.notification_id);
|
|
11687
|
+
}
|
|
11597
11688
|
if (!opts.dryRun) {
|
|
11598
11689
|
reconcileOneChunk(prep, out, matched, notImported);
|
|
11599
11690
|
}
|
|
11600
11691
|
}
|
|
11601
|
-
const result = buildImportLeadsResult(client, prep, importIds, matched, notImported, opts.dryRun, false);
|
|
11692
|
+
const result = buildImportLeadsResult(client, prep, importIds, matched, notImported, opts.dryRun, false, notificationIds);
|
|
11602
11693
|
await tracker.markImportComplete(handleId, {
|
|
11603
11694
|
leads: result.leads,
|
|
11604
11695
|
not_imported: result.not_imported,
|
|
@@ -12266,6 +12357,63 @@ var agentMemoryReview = {
|
|
|
12266
12357
|
}
|
|
12267
12358
|
};
|
|
12268
12359
|
|
|
12360
|
+
// ../core/dist/tools/acknowledge-notification.js
|
|
12361
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
12362
|
+
var acknowledgeNotification = {
|
|
12363
|
+
name: "leadbay_acknowledge_notification",
|
|
12364
|
+
annotations: {
|
|
12365
|
+
title: "Acknowledge a Leadbay notification",
|
|
12366
|
+
readOnlyHint: false,
|
|
12367
|
+
destructiveHint: false,
|
|
12368
|
+
idempotentHint: true,
|
|
12369
|
+
openWorldHint: true
|
|
12370
|
+
},
|
|
12371
|
+
description: leadbay_acknowledge_notification,
|
|
12372
|
+
write: true,
|
|
12373
|
+
inputSchema: {
|
|
12374
|
+
type: "object",
|
|
12375
|
+
properties: {
|
|
12376
|
+
notification_id: {
|
|
12377
|
+
type: "string",
|
|
12378
|
+
description: "UUID of the notification to acknowledge. Use the notification_id from `_meta.notifications[]` or `account_status.notifications[]`."
|
|
12379
|
+
},
|
|
12380
|
+
archive: {
|
|
12381
|
+
type: "boolean",
|
|
12382
|
+
description: "If true, archive the notification (won't appear in `archived=false` listings). If false / omitted, mark seen (resets firstSeenAt)."
|
|
12383
|
+
}
|
|
12384
|
+
},
|
|
12385
|
+
required: ["notification_id"],
|
|
12386
|
+
additionalProperties: false
|
|
12387
|
+
},
|
|
12388
|
+
outputSchema: {
|
|
12389
|
+
type: "object",
|
|
12390
|
+
properties: {
|
|
12391
|
+
acknowledged: { type: "boolean" },
|
|
12392
|
+
notification_id: { type: "string" },
|
|
12393
|
+
action: { type: "string", enum: ["seen", "archive"] }
|
|
12394
|
+
},
|
|
12395
|
+
required: ["acknowledged", "notification_id", "action"]
|
|
12396
|
+
},
|
|
12397
|
+
execute: async (client, params, ctx) => {
|
|
12398
|
+
if (!UUID_RE.test(params.notification_id)) {
|
|
12399
|
+
return {
|
|
12400
|
+
error: true,
|
|
12401
|
+
code: "BAD_INPUT",
|
|
12402
|
+
message: "notification_id must be a UUID",
|
|
12403
|
+
hint: "Pass the notification_id verbatim from _meta.notifications[].notification_id or account_status.notifications[].notification_id."
|
|
12404
|
+
};
|
|
12405
|
+
}
|
|
12406
|
+
const action = params.archive ? "archive" : "seen";
|
|
12407
|
+
await client.acknowledgeNotification(params.notification_id, action);
|
|
12408
|
+
ctx?.notificationsInbox?.markSeen(params.notification_id);
|
|
12409
|
+
return {
|
|
12410
|
+
acknowledged: true,
|
|
12411
|
+
notification_id: params.notification_id,
|
|
12412
|
+
action
|
|
12413
|
+
};
|
|
12414
|
+
}
|
|
12415
|
+
};
|
|
12416
|
+
|
|
12269
12417
|
// ../core/dist/tools/select-leads.js
|
|
12270
12418
|
var selectLeads = {
|
|
12271
12419
|
name: "leadbay_select_leads",
|
|
@@ -15338,6 +15486,11 @@ var accountStatus = {
|
|
|
15338
15486
|
type: ["object", "null"],
|
|
15339
15487
|
description: "Per-resource quota state (llm_completion, ai_rescore, web_fetch, LENS_EXTRA_REFILL) across daily/weekly/monthly windows. Null if /quota_status failed (logged in stderr). Pre-check the LENS_EXTRA_REFILL entry before calling leadbay_extend_lens."
|
|
15340
15488
|
},
|
|
15489
|
+
notifications: {
|
|
15490
|
+
type: "array",
|
|
15491
|
+
description: "Terminal bulk-progress notifications the MCP knows about (background work the user or agent started that has since completed). Each entry carries notification_id, kind (bulk_enrich | bulk_qualify | import | other), bulk_progress counters, and a revise_hint pointing at prior agent outputs the just-finished work might have made stale. After revising affected outputs, call leadbay_acknowledge_notification(notification_id) to clear the entry. Empty array when nothing has completed.",
|
|
15492
|
+
items: { type: "object" }
|
|
15493
|
+
},
|
|
15341
15494
|
_meta: {
|
|
15342
15495
|
type: "object",
|
|
15343
15496
|
properties: {
|
|
@@ -15397,6 +15550,13 @@ var accountStatus = {
|
|
|
15397
15550
|
// on /me are intentionally NOT surfaced — they're defunct (see
|
|
15398
15551
|
// SHAPE-DRIFT.md probe round 4).
|
|
15399
15552
|
quota,
|
|
15553
|
+
// Inbox of terminal bulk-progress notifications. Same shape the MCP
|
|
15554
|
+
// server attaches to `_meta.notifications` on every tool response —
|
|
15555
|
+
// duplicated here as a top-level field so the agent's daily-rhythm
|
|
15556
|
+
// check-in (this composite) sees them without having to read _meta.
|
|
15557
|
+
// Empty array when the WS listener isn't wired (OpenClaw, tests) OR
|
|
15558
|
+
// when nothing has completed since the last ack.
|
|
15559
|
+
notifications: ctx?.notificationsInbox?.list() ?? [],
|
|
15400
15560
|
_meta: {
|
|
15401
15561
|
region: client.region
|
|
15402
15562
|
}
|
|
@@ -15410,6 +15570,33 @@ var DEFAULT_COUNT = 10;
|
|
|
15410
15570
|
var MAX_COUNT = 25;
|
|
15411
15571
|
var DEFAULT_PER_LEAD_BUDGET_MS = 9e4;
|
|
15412
15572
|
var DEFAULT_TOTAL_BUDGET_MS2 = 5 * 6e4;
|
|
15573
|
+
async function launchBulkQualify(client, leadIds, ctx) {
|
|
15574
|
+
await client.acquireSelectionLock();
|
|
15575
|
+
try {
|
|
15576
|
+
try {
|
|
15577
|
+
const qs = leadIds.map((id) => `leadIds=${encodeURIComponent(id)}`).join("&");
|
|
15578
|
+
await client.requestVoid("POST", `/leads/selection/select?${qs}`);
|
|
15579
|
+
try {
|
|
15580
|
+
const resp = await client.request("POST", "/leads/selection/web_fetch?force_fetch=false", {});
|
|
15581
|
+
return { resp, quotaExceeded: false };
|
|
15582
|
+
} catch (err) {
|
|
15583
|
+
if (err?.code === "QUOTA_EXCEEDED") {
|
|
15584
|
+
ctx?.logger?.warn?.("bulk_qualify_leads: 429 on bulk /leads/selection/web_fetch \u2014 no leads queued");
|
|
15585
|
+
return { resp: null, quotaExceeded: true };
|
|
15586
|
+
}
|
|
15587
|
+
throw err;
|
|
15588
|
+
}
|
|
15589
|
+
} finally {
|
|
15590
|
+
try {
|
|
15591
|
+
await client.requestVoid("POST", "/leads/selection/clear");
|
|
15592
|
+
} catch (e) {
|
|
15593
|
+
ctx?.logger?.warn?.(`bulk_qualify_leads: selection.clear failed: ${e?.message ?? e?.code}`);
|
|
15594
|
+
}
|
|
15595
|
+
}
|
|
15596
|
+
} finally {
|
|
15597
|
+
client.releaseSelectionLock();
|
|
15598
|
+
}
|
|
15599
|
+
}
|
|
15413
15600
|
var bulkQualifyLeads = {
|
|
15414
15601
|
name: "leadbay_bulk_qualify_leads",
|
|
15415
15602
|
annotations: {
|
|
@@ -15577,69 +15764,50 @@ var bulkQualifyLeads = {
|
|
|
15577
15764
|
per_lead_budget_ms: perLeadBudget,
|
|
15578
15765
|
total_budget_ms: totalBudget
|
|
15579
15766
|
});
|
|
15580
|
-
|
|
15581
|
-
|
|
15767
|
+
let launchedCount = 0;
|
|
15768
|
+
let notificationId = null;
|
|
15582
15769
|
let quotaExceeded2 = false;
|
|
15770
|
+
let failed2 = [];
|
|
15583
15771
|
if (!reservation.reused) {
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
|
|
15592
|
-
|
|
15593
|
-
|
|
15594
|
-
failed2.push({ lead_id: leadId, error: "lead not found" });
|
|
15595
|
-
} else {
|
|
15596
|
-
failed2.push({
|
|
15597
|
-
lead_id: leadId,
|
|
15598
|
-
error: err?.message ?? err?.code ?? "unknown"
|
|
15599
|
-
});
|
|
15600
|
-
}
|
|
15601
|
-
}
|
|
15602
|
-
}
|
|
15603
|
-
if (failed2.length === candidates.length || launched2.length > 0 || quotaExceeded2) {
|
|
15604
|
-
await ctx.bulkTracker.markLaunched(reservation.record.bulk_id);
|
|
15772
|
+
const launch = await launchBulkQualify(client, candidates, ctx);
|
|
15773
|
+
quotaExceeded2 = launch.quotaExceeded;
|
|
15774
|
+
notificationId = launch.resp?.notification_id ?? null;
|
|
15775
|
+
const queuedIds = launch.resp?.queued_ids ?? [];
|
|
15776
|
+
const skippedIds = launch.resp?.skipped_ids ?? [];
|
|
15777
|
+
launchedCount = queuedIds.length;
|
|
15778
|
+
const seen = /* @__PURE__ */ new Set([...queuedIds, ...skippedIds]);
|
|
15779
|
+
failed2 = candidates.filter((id) => !seen.has(id)).map((id) => ({ lead_id: id, error: "not_queued" }));
|
|
15780
|
+
if (queuedIds.length > 0 || quotaExceeded2 || skippedIds.length > 0 || failed2.length === candidates.length) {
|
|
15781
|
+
await ctx.bulkTracker.markLaunched(reservation.record.bulk_id, notificationId);
|
|
15605
15782
|
}
|
|
15783
|
+
} else {
|
|
15784
|
+
notificationId = reservation.record.notification_id ?? null;
|
|
15785
|
+
launchedCount = reservation.record.lead_ids.length;
|
|
15606
15786
|
}
|
|
15607
15787
|
const out = {
|
|
15608
15788
|
status: "running",
|
|
15609
15789
|
handle_id: reservation.record.bulk_id,
|
|
15610
15790
|
qualify_id: reservation.record.bulk_id,
|
|
15611
15791
|
lead_ids: candidates,
|
|
15612
|
-
launched_count:
|
|
15792
|
+
launched_count: launchedCount,
|
|
15613
15793
|
failed: failed2,
|
|
15614
15794
|
quota_exceeded: quotaExceeded2,
|
|
15615
15795
|
lens_id: lensId,
|
|
15796
|
+
notification_id: notificationId,
|
|
15616
15797
|
_meta: { region: client.region }
|
|
15617
15798
|
};
|
|
15618
15799
|
return out;
|
|
15619
15800
|
}
|
|
15620
|
-
const
|
|
15621
|
-
const
|
|
15622
|
-
|
|
15623
|
-
|
|
15624
|
-
|
|
15625
|
-
|
|
15626
|
-
|
|
15627
|
-
await client.requestVoid("POST", `/leads/${leadId}/web_fetch?force_fetch=false`);
|
|
15628
|
-
launched.push(leadId);
|
|
15629
|
-
} catch (err) {
|
|
15630
|
-
if (err?.code === "QUOTA_EXCEEDED") {
|
|
15631
|
-
quotaExceeded = true;
|
|
15632
|
-
ctx?.logger?.warn?.(`bulk_qualify_leads: 429 mid-fanout after launching ${launched.length}/${candidates.length} \u2014 stopping further launches but polling those already in flight`);
|
|
15633
|
-
} else if (err?.code === "NOT_FOUND") {
|
|
15634
|
-
failed.push({ lead_id: leadId, error: "lead not found" });
|
|
15635
|
-
} else {
|
|
15636
|
-
failed.push({
|
|
15637
|
-
lead_id: leadId,
|
|
15638
|
-
error: err?.message ?? err?.code ?? "unknown"
|
|
15639
|
-
});
|
|
15640
|
-
}
|
|
15641
|
-
}
|
|
15801
|
+
const inlineLaunch = await launchBulkQualify(client, candidates, ctx);
|
|
15802
|
+
const quotaExceeded = inlineLaunch.quotaExceeded;
|
|
15803
|
+
const launched = inlineLaunch.resp?.queued_ids ?? [];
|
|
15804
|
+
const inlineSkipped = inlineLaunch.resp?.skipped_ids ?? [];
|
|
15805
|
+
const inlineNotificationId = inlineLaunch.resp?.notification_id ?? null;
|
|
15806
|
+
if (inlineNotificationId) {
|
|
15807
|
+
ctx?.logger?.info?.(`bulk_qualify_leads: launched bulk progress_notification_id=${inlineNotificationId} queued=${launched.length} skipped=${inlineSkipped.length}`);
|
|
15642
15808
|
}
|
|
15809
|
+
const inlineFailedSeen = /* @__PURE__ */ new Set([...launched, ...inlineSkipped]);
|
|
15810
|
+
const failed = candidates.filter((id) => !inlineFailedSeen.has(id)).map((id) => ({ lead_id: id, error: "not_queued" }));
|
|
15643
15811
|
let progressDone = 0;
|
|
15644
15812
|
const progressTotal = launched.length;
|
|
15645
15813
|
if (progressTotal > 0) {
|
|
@@ -16352,6 +16520,7 @@ var importAndQualify = {
|
|
|
16352
16520
|
...chosenBudgets ? { chosen_budgets: chosenBudgets } : {},
|
|
16353
16521
|
qualify_id: null,
|
|
16354
16522
|
import_ids: queued.importIds,
|
|
16523
|
+
notification_ids: queued.notification_ids ?? [],
|
|
16355
16524
|
imported: queued.leads.map((l) => ({
|
|
16356
16525
|
leadId: l.leadId,
|
|
16357
16526
|
...l.domain ? { domain: l.domain } : {},
|
|
@@ -16376,6 +16545,7 @@ var importAndQualify = {
|
|
|
16376
16545
|
...chosenBudgets ? { chosen_budgets: chosenBudgets } : {},
|
|
16377
16546
|
qualify_id: null,
|
|
16378
16547
|
import_ids: queued.importIds,
|
|
16548
|
+
notification_ids: queued.notification_ids ?? [],
|
|
16379
16549
|
imported: [],
|
|
16380
16550
|
not_imported: [],
|
|
16381
16551
|
qualified: [],
|
|
@@ -16413,6 +16583,7 @@ var importAndQualify = {
|
|
|
16413
16583
|
...chosenBudgets ? { chosen_budgets: chosenBudgets } : {},
|
|
16414
16584
|
qualify_id: null,
|
|
16415
16585
|
import_ids: importResult.importIds,
|
|
16586
|
+
notification_ids: importResult.notification_ids ?? [],
|
|
16416
16587
|
imported: [],
|
|
16417
16588
|
not_imported: importResult.not_imported.map(toNotImportedEntry),
|
|
16418
16589
|
qualified: [],
|
|
@@ -16450,6 +16621,7 @@ var importAndQualify = {
|
|
|
16450
16621
|
...chosenBudgets ? { chosen_budgets: chosenBudgets } : {},
|
|
16451
16622
|
qualify_id: null,
|
|
16452
16623
|
import_ids: importResult.importIds,
|
|
16624
|
+
notification_ids: importResult.notification_ids ?? [],
|
|
16453
16625
|
imported,
|
|
16454
16626
|
not_imported,
|
|
16455
16627
|
qualified: [],
|
|
@@ -16560,6 +16732,7 @@ var importAndQualify = {
|
|
|
16560
16732
|
...chosenBudgets ? { chosen_budgets: chosenBudgets } : {},
|
|
16561
16733
|
qualify_id: reservation.record.bulk_id,
|
|
16562
16734
|
import_ids: importResult.importIds,
|
|
16735
|
+
notification_ids: importResult.notification_ids ?? [],
|
|
16563
16736
|
imported,
|
|
16564
16737
|
not_imported,
|
|
16565
16738
|
qualified,
|
|
@@ -16901,6 +17074,14 @@ var importStatus = {
|
|
|
16901
17074
|
};
|
|
16902
17075
|
|
|
16903
17076
|
// ../core/dist/composite/qualify-status.js
|
|
17077
|
+
async function readNotification(client, notificationId) {
|
|
17078
|
+
try {
|
|
17079
|
+
const page = await client.listNotifications({ archived: false, count: 50 });
|
|
17080
|
+
return page.items.find((n) => n.id === notificationId) ?? null;
|
|
17081
|
+
} catch {
|
|
17082
|
+
return null;
|
|
17083
|
+
}
|
|
17084
|
+
}
|
|
16904
17085
|
var qualifyStatus = {
|
|
16905
17086
|
name: "leadbay_qualify_status",
|
|
16906
17087
|
annotations: {
|
|
@@ -17055,6 +17236,16 @@ var qualifyStatus = {
|
|
|
17055
17236
|
const { _stillRunning, _failedCode, ...rest } = r;
|
|
17056
17237
|
qualified.push(rest);
|
|
17057
17238
|
}
|
|
17239
|
+
let bulkProgress = null;
|
|
17240
|
+
let inProgressFlag = null;
|
|
17241
|
+
const notifId = record.notification_id ?? null;
|
|
17242
|
+
if (notifId) {
|
|
17243
|
+
const n = await readNotification(client, notifId);
|
|
17244
|
+
if (n) {
|
|
17245
|
+
bulkProgress = n.bulk_progress;
|
|
17246
|
+
inProgressFlag = n.in_progress;
|
|
17247
|
+
}
|
|
17248
|
+
}
|
|
17058
17249
|
const out = {
|
|
17059
17250
|
qualify_id: record.bulk_id,
|
|
17060
17251
|
launched_at: record.launched_at,
|
|
@@ -17066,6 +17257,9 @@ var qualifyStatus = {
|
|
|
17066
17257
|
still_running,
|
|
17067
17258
|
failed,
|
|
17068
17259
|
not_in_lens: [...notInLensSet],
|
|
17260
|
+
notification_id: notifId,
|
|
17261
|
+
bulk_progress: bulkProgress,
|
|
17262
|
+
in_progress: inProgressFlag,
|
|
17069
17263
|
region: client.region,
|
|
17070
17264
|
_meta: client.lastMeta ?? {
|
|
17071
17265
|
region: client.region,
|
|
@@ -17078,6 +17272,9 @@ var qualifyStatus = {
|
|
|
17078
17272
|
out.per_lead_budget_ms = record.per_lead_budget_ms;
|
|
17079
17273
|
if (record.total_budget_ms !== void 0)
|
|
17080
17274
|
out.total_budget_ms = record.total_budget_ms;
|
|
17275
|
+
if (bulkProgress && bulkProgress.quota_hit_count > 0) {
|
|
17276
|
+
out.quota_hit_hint = "Some leads hit the AI-credits quota during qualification. Top up via leadbay_create_topup_link to clear the throttle immediately, or wait until the daily/weekly window resets.";
|
|
17277
|
+
}
|
|
17081
17278
|
return out;
|
|
17082
17279
|
}
|
|
17083
17280
|
};
|
|
@@ -17348,6 +17545,7 @@ var enrichTitles = {
|
|
|
17348
17545
|
bulk_id: res.record.bulk_id,
|
|
17349
17546
|
launched_at: res.record.launched_at,
|
|
17350
17547
|
durability: res.record.durability,
|
|
17548
|
+
notification_id: res.record.notification_id ?? null,
|
|
17351
17549
|
seconds_since_original_launch: bulkSecondsSinceOriginal ?? 0,
|
|
17352
17550
|
titles: params.titles,
|
|
17353
17551
|
email,
|
|
@@ -17363,8 +17561,9 @@ var enrichTitles = {
|
|
|
17363
17561
|
total: 3,
|
|
17364
17562
|
message: `Launching enrichment for ${params.titles.length} title${params.titles.length === 1 ? "" : "s"}\u2026`
|
|
17365
17563
|
});
|
|
17564
|
+
let launchResp = null;
|
|
17366
17565
|
try {
|
|
17367
|
-
await client.
|
|
17566
|
+
launchResp = await client.request("POST", "/leads/selection/enrichment/launch", { titles: params.titles, email, phone });
|
|
17368
17567
|
} catch (err) {
|
|
17369
17568
|
const aborted = err?.name === "AbortError" || ctx?.signal?.aborted === true;
|
|
17370
17569
|
if (bulkRecord && tracker) {
|
|
@@ -17388,9 +17587,10 @@ var enrichTitles = {
|
|
|
17388
17587
|
}
|
|
17389
17588
|
throw err;
|
|
17390
17589
|
}
|
|
17590
|
+
const notificationId = launchResp?.notification_id ?? null;
|
|
17391
17591
|
if (bulkRecord && tracker) {
|
|
17392
17592
|
try {
|
|
17393
|
-
await tracker.markLaunched(bulkRecord.bulk_id);
|
|
17593
|
+
await tracker.markLaunched(bulkRecord.bulk_id, notificationId);
|
|
17394
17594
|
} catch (e) {
|
|
17395
17595
|
ctx?.logger?.warn?.(`enrich_titles: tracker.markLaunched failed: ${e?.message ?? e}`);
|
|
17396
17596
|
return {
|
|
@@ -17418,8 +17618,9 @@ var enrichTitles = {
|
|
|
17418
17618
|
bulk_id: bulkRecord?.bulk_id,
|
|
17419
17619
|
launched_at: bulkRecord?.launched_at,
|
|
17420
17620
|
durability: bulkRecord?.durability,
|
|
17421
|
-
|
|
17422
|
-
|
|
17621
|
+
notification_id: notificationId,
|
|
17622
|
+
message: notificationId ? "Enrichment job launched. The MCP is now listening for the backend notification \u2014 when enrichment finishes, a `_meta.notifications` entry will surface on your next tool response (also visible in `leadbay_account_status.notifications`)." : bulkRecord ? "Enrichment job launched. Backend did not return a notification id this time; poll via leadbay_bulk_enrich_status with the bulk_id." : "Enrichment job launched. No bulk_id tracker configured \u2014 poll leadbay_get_contacts per lead after ~60s; contact.enrichment.done flips to true.",
|
|
17623
|
+
next_action: notificationId ? "Wait for the next `_meta.notifications` entry (typically <2 min for a small batch). If you want progress sooner, call leadbay_bulk_enrich_status({bulk_id})." : bulkRecord ? "Call leadbay_bulk_enrich_status({bulk_id}) after ~60s; pass include_contacts=true for the final read." : "Wait ~60s, then call leadbay_research_lead_by_id or leadbay_get_contacts on the leads you care about."
|
|
17423
17624
|
};
|
|
17424
17625
|
} finally {
|
|
17425
17626
|
try {
|
|
@@ -17435,6 +17636,14 @@ var enrichTitles = {
|
|
|
17435
17636
|
};
|
|
17436
17637
|
|
|
17437
17638
|
// ../core/dist/composite/bulk-enrich-status.js
|
|
17639
|
+
async function readNotification2(client, notificationId) {
|
|
17640
|
+
try {
|
|
17641
|
+
const page = await client.listNotifications({ archived: false, count: 50 });
|
|
17642
|
+
return page.items.find((n) => n.id === notificationId) ?? null;
|
|
17643
|
+
} catch {
|
|
17644
|
+
return null;
|
|
17645
|
+
}
|
|
17646
|
+
}
|
|
17438
17647
|
var STATUS_FETCH_CONCURRENCY = 5;
|
|
17439
17648
|
async function pMap(items, fn, concurrency) {
|
|
17440
17649
|
const out = new Array(items.length);
|
|
@@ -17599,6 +17808,53 @@ var bulkEnrichStatus = {
|
|
|
17599
17808
|
launched_at: record.launched_at
|
|
17600
17809
|
};
|
|
17601
17810
|
}
|
|
17811
|
+
const notifId = record.notification_id ?? null;
|
|
17812
|
+
if (notifId) {
|
|
17813
|
+
const n = await readNotification2(client, notifId);
|
|
17814
|
+
if (n && n.bulk_progress) {
|
|
17815
|
+
const bp = n.bulk_progress;
|
|
17816
|
+
const inProgress = n.in_progress;
|
|
17817
|
+
let leads2 = [];
|
|
17818
|
+
if (!inProgress && includeContacts) {
|
|
17819
|
+
leads2 = await pMap(record.lead_ids, async (leadId) => {
|
|
17820
|
+
try {
|
|
17821
|
+
const out = await getContacts.execute(client, { leadId });
|
|
17822
|
+
const contacts = Array.isArray(out?.contacts) ? out.contacts : [];
|
|
17823
|
+
return { lead_id: leadId, contacts };
|
|
17824
|
+
} catch {
|
|
17825
|
+
return { lead_id: leadId };
|
|
17826
|
+
}
|
|
17827
|
+
}, STATUS_FETCH_CONCURRENCY);
|
|
17828
|
+
} else {
|
|
17829
|
+
leads2 = record.lead_ids.map((id) => ({ lead_id: id }));
|
|
17830
|
+
}
|
|
17831
|
+
ctx?.logger?.info?.(`bulk.status_checked_via_notification bulk_id=${record.bulk_id} notification_id=${notifId} done=${bp.success_count}/${bp.total_count} in_progress=${inProgress} wall_ms=${Date.now() - startMs}`);
|
|
17832
|
+
return {
|
|
17833
|
+
bulk_id: record.bulk_id,
|
|
17834
|
+
notification_id: notifId,
|
|
17835
|
+
launched_at: record.launched_at,
|
|
17836
|
+
status: record.status,
|
|
17837
|
+
durability: record.durability,
|
|
17838
|
+
titles: record.titles,
|
|
17839
|
+
email: record.email,
|
|
17840
|
+
phone: record.phone,
|
|
17841
|
+
lens_id: record.lens_id,
|
|
17842
|
+
leads: leads2,
|
|
17843
|
+
overall_progress: {
|
|
17844
|
+
done: bp.success_count + bp.failure_count + bp.quota_hit_count,
|
|
17845
|
+
total: bp.total_count,
|
|
17846
|
+
done_ratio: bp.total_count === 0 ? 0 : (bp.success_count + bp.failure_count + bp.quota_hit_count) / bp.total_count
|
|
17847
|
+
},
|
|
17848
|
+
bulk_progress: bp,
|
|
17849
|
+
in_progress: inProgress,
|
|
17850
|
+
all_done: !inProgress,
|
|
17851
|
+
...bp.quota_hit_count > 0 ? {
|
|
17852
|
+
quota_hit_hint: "Some contacts could not be enriched because the AI-credits quota was hit. Top up via leadbay_create_topup_link or wait for the window reset."
|
|
17853
|
+
} : {}
|
|
17854
|
+
};
|
|
17855
|
+
}
|
|
17856
|
+
ctx?.logger?.info?.(`bulk_enrich_status: notification ${notifId} not yet visible; falling back to per-lead fan-out`);
|
|
17857
|
+
}
|
|
17602
17858
|
let doneSoFar = 0;
|
|
17603
17859
|
const totalLeads = record.lead_ids.length;
|
|
17604
17860
|
const results = await pMap(record.lead_ids, async (leadId) => {
|
|
@@ -19392,7 +19648,13 @@ var compositeReadTools = [
|
|
|
19392
19648
|
// didn't deliver"). Does not mutate Leadbay state; emits a PostHog
|
|
19393
19649
|
// event only. Companion to leadbay_report_outreach (which DOES write
|
|
19394
19650
|
// to the backend and stays gated behind LEADBAY_MCP_WRITE).
|
|
19395
|
-
reportFriction
|
|
19651
|
+
reportFriction,
|
|
19652
|
+
// Notification ack — ALWAYS exposed even though it POSTs to /seen.
|
|
19653
|
+
// _meta.notifications surfaces terminal bulk-progress notifications on
|
|
19654
|
+
// every tool response regardless of write gating; without ack the agent
|
|
19655
|
+
// sees the same entries on every call forever. Pairing the surfacing
|
|
19656
|
+
// channel with the clearing tool is non-optional.
|
|
19657
|
+
acknowledgeNotification
|
|
19396
19658
|
];
|
|
19397
19659
|
var compositeWriteTools = [
|
|
19398
19660
|
bulkQualifyLeads,
|
|
@@ -20211,6 +20473,22 @@ function buildServer(client, opts = {}) {
|
|
|
20211
20473
|
});
|
|
20212
20474
|
}
|
|
20213
20475
|
};
|
|
20476
|
+
const maybeAttachNotifications = (result) => {
|
|
20477
|
+
const inbox = opts.notificationsInbox;
|
|
20478
|
+
if (!inbox) return;
|
|
20479
|
+
if (result === null || typeof result !== "object" || Array.isArray(result)) {
|
|
20480
|
+
return;
|
|
20481
|
+
}
|
|
20482
|
+
const entries = inbox.list();
|
|
20483
|
+
if (entries.length === 0) return;
|
|
20484
|
+
const envelope = result;
|
|
20485
|
+
const target = envelope.__markdown_envelope === true && envelope.structured !== null && typeof envelope.structured === "object" && !Array.isArray(envelope.structured) ? envelope.structured : envelope;
|
|
20486
|
+
const existingMeta = target._meta && typeof target._meta === "object" && !Array.isArray(target._meta) ? target._meta : {};
|
|
20487
|
+
target._meta = {
|
|
20488
|
+
...existingMeta,
|
|
20489
|
+
notifications: entries
|
|
20490
|
+
};
|
|
20491
|
+
};
|
|
20214
20492
|
const isLeadbayBusinessError = (err) => err != null && typeof err === "object" && err.error === true && typeof err.code === "string";
|
|
20215
20493
|
const buildBusinessCtx = (toolName, envelope, triggered_by) => {
|
|
20216
20494
|
const meta = envelope._meta ?? {};
|
|
@@ -20330,11 +20608,13 @@ function buildServer(client, opts = {}) {
|
|
|
20330
20608
|
const result = await tool.execute(client, args, {
|
|
20331
20609
|
logger: opts.logger,
|
|
20332
20610
|
bulkTracker: opts.bulkTracker,
|
|
20611
|
+
notificationsInbox: opts.notificationsInbox,
|
|
20333
20612
|
signal: extra.signal,
|
|
20334
20613
|
progress,
|
|
20335
20614
|
elicit
|
|
20336
20615
|
});
|
|
20337
20616
|
maybeAttachUpdate(name, result);
|
|
20617
|
+
maybeAttachNotifications(result);
|
|
20338
20618
|
if (result && typeof result === "object" && result.error === true) {
|
|
20339
20619
|
const envText = formatErrorForLLM(result);
|
|
20340
20620
|
const envDur = Date.now() - callStart;
|
|
@@ -20629,7 +20909,7 @@ function parseWriteEnv(env = process.env) {
|
|
|
20629
20909
|
}
|
|
20630
20910
|
|
|
20631
20911
|
// src/http-server.ts
|
|
20632
|
-
var VERSION = true ? "0.
|
|
20912
|
+
var VERSION = true ? "0.18.0" : "0.0.0-dev";
|
|
20633
20913
|
var PORT = Number(process.env.PORT ?? 8080);
|
|
20634
20914
|
var HOST = process.env.HOST ?? "0.0.0.0";
|
|
20635
20915
|
var sseSessions = /* @__PURE__ */ new Map();
|