@sellable/mcp 0.1.555 → 0.1.557

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.
Files changed (63) hide show
  1. package/README.md +13 -2
  2. package/agents/registry.json +2 -2
  3. package/dist/api.js +3 -6
  4. package/dist/auth.d.ts +0 -6
  5. package/dist/auth.js +2 -44
  6. package/dist/refill-run-client.d.ts +0 -5
  7. package/dist/refill-run-client.js +0 -15
  8. package/dist/refill-run-loop.d.ts +1 -12
  9. package/dist/refill-run-loop.js +13 -158
  10. package/dist/server.js +23 -0
  11. package/dist/tools/auth.d.ts +0 -5
  12. package/dist/tools/auth.js +12 -49
  13. package/dist/tools/campaign-message-preparation.d.ts +0 -62
  14. package/dist/tools/campaign-message-preparation.js +0 -41
  15. package/dist/tools/campaigns.js +2 -2
  16. package/dist/tools/csv-dnc.js +2 -2
  17. package/dist/tools/evergreen-refill-plan.d.ts +0 -3
  18. package/dist/tools/evergreen-refill-plan.js +7 -29
  19. package/dist/tools/find-leads-runs.d.ts +151 -0
  20. package/dist/tools/find-leads-runs.js +98 -0
  21. package/dist/tools/leads.d.ts +317 -32
  22. package/dist/tools/leads.js +171 -10
  23. package/dist/tools/model-quality.js +4 -6
  24. package/dist/tools/prompts.d.ts +3 -3
  25. package/dist/tools/prompts.js +7 -15
  26. package/dist/tools/provider-preflight.d.ts +65 -2
  27. package/dist/tools/provider-preflight.js +97 -10
  28. package/dist/tools/readiness.d.ts +89 -5
  29. package/dist/tools/readiness.js +66 -0
  30. package/dist/tools/refill-executors.d.ts +0 -38
  31. package/dist/tools/refill-executors.js +3 -222
  32. package/dist/tools/refill-sends-v2.d.ts +1 -112
  33. package/dist/tools/refill-sends-v2.js +2 -302
  34. package/dist/tools/refill-sends.d.ts +32 -678
  35. package/dist/tools/refill-sends.js +13 -274
  36. package/dist/tools/refill-target-plan.js +14 -486
  37. package/dist/tools/registry.d.ts +330 -115
  38. package/dist/tools/registry.js +7 -1
  39. package/dist/tools/scheduler-fill-capacity.js +1 -1
  40. package/dist/tools/scheduler-run.d.ts +0 -71
  41. package/dist/tools/scheduler-run.js +1 -203
  42. package/dist/tools/setup-evergreen-campaigns.js +1 -1
  43. package/dist/tools/workspace-context.d.ts +1 -1
  44. package/dist/tools/workspace-context.js +3 -8
  45. package/dist/tools/workspace-export.js +2 -2
  46. package/dist/tools/workspaces.d.ts +2 -48
  47. package/dist/tools/workspaces.js +5 -48
  48. package/package.json +1 -1
  49. package/skills/create-campaign/SKILL.md +3 -3
  50. package/skills/create-campaign-v2/SKILL.md +1 -1
  51. package/skills/create-evergreen-campaigns/SKILL.md +16 -16
  52. package/skills/find-leads/SKILL.md +48 -630
  53. package/skills/find-leads-v2/SKILL.md +70 -0
  54. package/skills/find-leads-v2/core/flow.v1.json +31 -0
  55. package/skills/refill-sends/SKILL.md +353 -91
  56. package/skills/refill-sends-v2/SKILL.md +6 -6
  57. package/skills/refill-sends-v2-workflow/SKILL.md +5 -5
  58. package/skills/refill-sends-v2-workflow/core/flow.v1.json +8 -8
  59. package/skills/refill-sends-workflow/SKILL.md +743 -100
  60. package/skills/refill-sends-workflow/core/flow.v1.json +1 -185
  61. package/dist/refill-contract.d.ts +0 -157
  62. package/dist/refill-contract.js +0 -487
  63. package/skills/refill-sends-workflow/core/contract.v2.json +0 -543
@@ -1,10 +1,84 @@
1
+ import { getApi } from "../api.js";
2
+ import { getConfig } from "../auth.js";
3
+ export const FIND_LEADS_WORKFLOW_VERSION = "find-leads-v1";
4
+ function diagnostic(code, message, nextAction) {
5
+ return { ok: false, code, message, nextAction, resumable: true };
6
+ }
7
+ export async function preflightFindLeadsProvider(input) {
8
+ if (input.workflowVersion !== FIND_LEADS_WORKFLOW_VERSION) {
9
+ return diagnostic("find_leads_mcp_runtime_outdated", `Find Leads requires ${FIND_LEADS_WORKFLOW_VERSION}; received ${input.workflowVersion}.`, "update the Sellable MCP/plugin, restart the MCP server, and retry Find Leads.");
10
+ }
11
+ let workspaceId = null;
12
+ try {
13
+ const config = getConfig();
14
+ workspaceId = config.activeWorkspaceId || config.workspaceId || null;
15
+ }
16
+ catch {
17
+ return diagnostic("find_leads_auth_required", "Sellable authentication is required before Find Leads can start.", "Run the Sellable first-run login flow, then retry bootstrap_find_leads.");
18
+ }
19
+ if (!workspaceId) {
20
+ return diagnostic("find_leads_workspace_required", "Find Leads needs an active request-scoped workspace.", "Run list_workspaces, set_active_workspace, and retry.");
21
+ }
22
+ if (input.providerFacts && !input.providerFacts.configured) {
23
+ const code = input.provider === "sales-nav"
24
+ ? "find_leads_sales_nav_unavailable"
25
+ : input.provider === "prospeo"
26
+ ? "find_leads_prospeo_unavailable"
27
+ : "find_leads_signal_capacity_unavailable";
28
+ const nextAction = input.provider === "sales-nav"
29
+ ? "Connect a LinkedIn account with an active Sales Navigator search session, then retry preflight."
30
+ : input.provider === "prospeo"
31
+ ? "Configure Prospeo and ensure enough credits are available, then retry preflight."
32
+ : "Wait for Signal Discovery capacity or choose another provider, then retry preflight.";
33
+ return diagnostic(code, input.providerFacts.reason || `${input.provider} is not ready`, nextAction);
34
+ }
35
+ if (!Number.isInteger(input.runVersion) || (input.runVersion ?? -1) < 0) {
36
+ return diagnostic("find_leads_run_version_required", "A current durable run version is required to persist provider preflight.", "Call get_find_leads_run, then retry preflight with the returned version.");
37
+ }
38
+ const receipt = {
39
+ workflowVersion: FIND_LEADS_WORKFLOW_VERSION,
40
+ promptHash: input.promptHash || `builtin:${FIND_LEADS_WORKFLOW_VERSION}:${input.provider}`,
41
+ provider: input.provider,
42
+ checkedAt: new Date().toISOString(),
43
+ };
44
+ const response = await getApi().patch(`/api/v3/find-leads/runs/${encodeURIComponent(input.runId)}`, {
45
+ action: "record_receipt",
46
+ version: input.runVersion,
47
+ kind: "provider_prompt",
48
+ provider: input.provider.replace(/-/g, "_"),
49
+ receipt,
50
+ }, { workspaceId });
51
+ markProviderPromptLoaded({
52
+ provider: input.provider,
53
+ findLeadsRunId: input.runId,
54
+ });
55
+ return { ok: true, provider: input.provider, receipt, run: response };
56
+ }
57
+ export const findLeadsProviderPreflightToolDefinitions = [
58
+ {
59
+ name: "preflight_find_leads_provider",
60
+ description: "Persist the selected provider prompt/version receipt and return actionable setup diagnostics before any provider call.",
61
+ inputSchema: {
62
+ type: "object",
63
+ properties: {
64
+ runId: { type: "string" },
65
+ runVersion: { type: "number" },
66
+ provider: { type: "string", enum: ["sales-nav", "prospeo", "signal-discovery"] },
67
+ workflowVersion: { type: "string" },
68
+ promptHash: { type: "string" },
69
+ },
70
+ required: ["runId", "runVersion", "provider", "workflowVersion"],
71
+ additionalProperties: false,
72
+ },
73
+ },
74
+ ];
1
75
  const providerPromptLoadedState = new Map();
2
- function scopedKey(provider, campaignOfferId) {
3
- return `${campaignOfferId ?? "*"}::${provider}`;
76
+ function scopedKey(provider, campaignOfferId, findLeadsRunId) {
77
+ return `${campaignOfferId ? `campaign:${campaignOfferId}` : findLeadsRunId ? `run:${findLeadsRunId}` : "provider:*"}::${provider}`;
4
78
  }
5
- function resolveLoadedState(provider, campaignOfferId) {
6
- if (campaignOfferId) {
7
- return (providerPromptLoadedState.get(scopedKey(provider, campaignOfferId)) ??
79
+ function resolveLoadedState(provider, campaignOfferId, findLeadsRunId) {
80
+ if (campaignOfferId || findLeadsRunId) {
81
+ return (providerPromptLoadedState.get(scopedKey(provider, campaignOfferId, findLeadsRunId)) ??
8
82
  null);
9
83
  }
10
84
  const providerScoped = providerPromptLoadedState.get(scopedKey(provider));
@@ -14,32 +88,45 @@ export function markProviderPromptLoaded(params) {
14
88
  const entry = {
15
89
  provider: params.provider,
16
90
  campaignOfferId: params.campaignOfferId ?? null,
91
+ findLeadsRunId: params.findLeadsRunId ?? null,
17
92
  loadedAt: new Date().toISOString(),
18
93
  };
19
- providerPromptLoadedState.set(scopedKey(params.provider, params.campaignOfferId), entry);
94
+ providerPromptLoadedState.set(scopedKey(params.provider, params.campaignOfferId, params.findLeadsRunId), entry);
20
95
  return {
21
96
  ...entry,
22
- scope: entry.campaignOfferId ? "campaign" : "provider",
97
+ scope: entry.campaignOfferId
98
+ ? "campaign"
99
+ : entry.findLeadsRunId
100
+ ? "find-leads-run"
101
+ : "provider",
23
102
  };
24
103
  }
25
104
  export function assertProviderPromptLoaded(params) {
26
- const loadedState = resolveLoadedState(params.provider, params.campaignOfferId);
105
+ const loadedState = resolveLoadedState(params.provider, params.campaignOfferId, params.findLeadsRunId);
27
106
  if (loadedState) {
28
107
  return {
29
108
  ok: true,
30
109
  provider: params.provider,
31
110
  campaignOfferId: params.campaignOfferId ?? null,
111
+ findLeadsRunId: params.findLeadsRunId ?? null,
32
112
  loadedAt: loadedState.loadedAt,
33
- scope: loadedState.campaignOfferId ? "campaign" : "provider",
113
+ scope: loadedState.campaignOfferId
114
+ ? "campaign"
115
+ : loadedState.findLeadsRunId
116
+ ? "find-leads-run"
117
+ : "provider",
34
118
  };
35
119
  }
36
120
  const scopeText = params.campaignOfferId
37
121
  ? `campaign ${params.campaignOfferId}`
38
- : "this run";
122
+ : params.findLeadsRunId
123
+ ? `Find Leads run ${params.findLeadsRunId}`
124
+ : "this provider session";
39
125
  const blockingMessage = `Provider prompt preflight missing for ${params.provider} (${scopeText}).`;
40
126
  const diagnostics = {
41
127
  provider: params.provider,
42
128
  campaignOfferId: params.campaignOfferId ?? null,
129
+ findLeadsRunId: params.findLeadsRunId ?? null,
43
130
  safeToProceed: false,
44
131
  blockingErrors: [
45
132
  `${blockingMessage} Call get_provider_prompt({ provider: "${params.provider}"${params.campaignOfferId
@@ -8,6 +8,7 @@ type WaitForCampaignTableReadyInput = {
8
8
  type WaitForLeadListReadyInput = {
9
9
  leadListId?: string;
10
10
  campaignOfferId?: string;
11
+ findLeadsRunId?: string;
11
12
  workspaceId?: string;
12
13
  provider?: "apollo" | "sales-nav" | "prospeo" | "signal-discovery" | "csv-linkedin";
13
14
  jobId?: string;
@@ -52,6 +53,7 @@ export declare const readinessToolDefinitions: ({
52
53
  };
53
54
  leadListId?: undefined;
54
55
  campaignOfferId?: undefined;
56
+ findLeadsRunId?: undefined;
55
57
  workspaceId?: undefined;
56
58
  provider?: undefined;
57
59
  jobId?: undefined;
@@ -75,6 +77,10 @@ export declare const readinessToolDefinitions: ({
75
77
  type: string;
76
78
  description: string;
77
79
  };
80
+ findLeadsRunId: {
81
+ type: string;
82
+ description: string;
83
+ };
78
84
  workspaceId: {
79
85
  type: string;
80
86
  description: string;
@@ -280,16 +286,74 @@ export declare function waitForCampaignTableReady(input: WaitForCampaignTableRea
280
286
  export declare function waitForLeadListReady(input: WaitForLeadListReadyInput): Promise<{
281
287
  ready: boolean;
282
288
  reason: string;
289
+ runId: string;
290
+ leadListId: string;
283
291
  attempts: number;
284
292
  elapsedMs: number;
285
- leadListId: null;
286
293
  provider?: undefined;
287
- rowCount?: undefined;
294
+ jobId?: undefined;
288
295
  status?: undefined;
296
+ rowCount?: undefined;
297
+ counts?: undefined;
298
+ retryable?: undefined;
299
+ warning?: undefined;
300
+ targetLeadCount?: undefined;
301
+ error?: undefined;
302
+ sourceShortfall?: undefined;
303
+ materializing?: undefined;
304
+ } | {
305
+ ready: boolean;
306
+ reason: string | undefined;
307
+ runId: any;
308
+ leadListId: any;
309
+ provider: string | null;
310
+ jobId: any;
311
+ status: any;
312
+ rowCount: number;
313
+ counts: any;
314
+ retryable: any;
315
+ attempts: number;
316
+ elapsedMs: number;
317
+ warning?: undefined;
318
+ targetLeadCount?: undefined;
319
+ error?: undefined;
320
+ sourceShortfall?: undefined;
321
+ materializing?: undefined;
322
+ } | {
323
+ ready: boolean;
324
+ reason: string;
325
+ runId: string;
326
+ leadListId: any;
327
+ provider: string | null;
328
+ jobId: any;
329
+ status: any;
330
+ rowCount: number;
331
+ retryable: boolean;
332
+ attempts: number;
333
+ elapsedMs: number;
334
+ warning: string | undefined;
335
+ counts?: undefined;
289
336
  targetLeadCount?: undefined;
290
337
  error?: undefined;
291
338
  sourceShortfall?: undefined;
339
+ materializing?: undefined;
340
+ } | {
341
+ ready: boolean;
342
+ reason: string;
343
+ attempts: number;
344
+ elapsedMs: number;
345
+ leadListId: null;
346
+ runId?: undefined;
347
+ provider?: undefined;
348
+ jobId?: undefined;
349
+ status?: undefined;
350
+ rowCount?: undefined;
351
+ counts?: undefined;
352
+ retryable?: undefined;
292
353
  warning?: undefined;
354
+ targetLeadCount?: undefined;
355
+ error?: undefined;
356
+ sourceShortfall?: undefined;
293
357
  materializing?: undefined;
294
358
  } | {
295
359
  ready: boolean;
@@ -301,9 +365,13 @@ export declare function waitForLeadListReady(input: WaitForLeadListReadyInput):
301
365
  rowCount: number;
302
366
  status: string;
303
367
  targetLeadCount: number | undefined;
368
+ runId?: undefined;
369
+ jobId?: undefined;
370
+ counts?: undefined;
371
+ retryable?: undefined;
372
+ warning?: undefined;
304
373
  error?: undefined;
305
374
  sourceShortfall?: undefined;
306
- warning?: undefined;
307
375
  materializing?: undefined;
308
376
  } | {
309
377
  ready: boolean;
@@ -316,8 +384,12 @@ export declare function waitForLeadListReady(input: WaitForLeadListReadyInput):
316
384
  status: string | null;
317
385
  targetLeadCount: number | undefined;
318
386
  error: string;
319
- sourceShortfall?: undefined;
387
+ runId?: undefined;
388
+ jobId?: undefined;
389
+ counts?: undefined;
390
+ retryable?: undefined;
320
391
  warning?: undefined;
392
+ sourceShortfall?: undefined;
321
393
  materializing?: undefined;
322
394
  } | {
323
395
  ready: boolean;
@@ -330,8 +402,12 @@ export declare function waitForLeadListReady(input: WaitForLeadListReadyInput):
330
402
  status: string;
331
403
  targetLeadCount: number | undefined;
332
404
  error: string | null;
333
- sourceShortfall?: undefined;
405
+ runId?: undefined;
406
+ jobId?: undefined;
407
+ counts?: undefined;
408
+ retryable?: undefined;
334
409
  warning?: undefined;
410
+ sourceShortfall?: undefined;
335
411
  materializing?: undefined;
336
412
  } | {
337
413
  ready: boolean;
@@ -345,6 +421,10 @@ export declare function waitForLeadListReady(input: WaitForLeadListReadyInput):
345
421
  sourceShortfall: boolean;
346
422
  warning: string | undefined;
347
423
  reason?: undefined;
424
+ runId?: undefined;
425
+ jobId?: undefined;
426
+ counts?: undefined;
427
+ retryable?: undefined;
348
428
  error?: undefined;
349
429
  materializing?: undefined;
350
430
  } | {
@@ -360,6 +440,10 @@ export declare function waitForLeadListReady(input: WaitForLeadListReadyInput):
360
440
  materializing: boolean | undefined;
361
441
  warning: string | undefined;
362
442
  error: string | undefined;
443
+ runId?: undefined;
444
+ jobId?: undefined;
445
+ counts?: undefined;
446
+ retryable?: undefined;
363
447
  sourceShortfall?: undefined;
364
448
  }>;
365
449
  export {};
@@ -109,6 +109,10 @@ export const readinessToolDefinitions = [
109
109
  type: "string",
110
110
  description: "Campaign offer ID (used to resolve selectedLeadListId if leadListId is missing).",
111
111
  },
112
+ findLeadsRunId: {
113
+ type: "string",
114
+ description: "Durable campaignless run/list ID. When present, poll the authoritative run snapshot instead of campaign/table config.",
115
+ },
112
116
  workspaceId: {
113
117
  type: "string",
114
118
  description: "Explicit request-scoped workspace id for scheduled/yolo refill automation.",
@@ -204,6 +208,68 @@ export async function waitForLeadListReady(input) {
204
208
  const intervalMs = Math.max(500, input.intervalMs ?? DEFAULT_INTERVAL_MS);
205
209
  const requireRows = input.requireRows !== false;
206
210
  const requireComplete = input.requireComplete !== false;
211
+ if (input.findLeadsRunId) {
212
+ if (input.campaignOfferId || (input.leadListId && input.leadListId !== input.findLeadsRunId)) {
213
+ return {
214
+ ready: false,
215
+ reason: "find_leads_target_mismatch",
216
+ runId: input.findLeadsRunId,
217
+ leadListId: input.leadListId ?? input.findLeadsRunId,
218
+ attempts: 0,
219
+ elapsedMs: 0,
220
+ };
221
+ }
222
+ const startedAt = Date.now();
223
+ let attempts = 0;
224
+ let snapshot = null;
225
+ while (Date.now() - startedAt <= effectiveTimeoutMs) {
226
+ attempts += 1;
227
+ snapshot = await apiGet(`/api/v3/find-leads/runs/${encodeURIComponent(input.findLeadsRunId)}`);
228
+ const status = String(snapshot.status ?? "").toLowerCase();
229
+ const terminal = snapshot.terminal === true ||
230
+ ["ready", "completed", "empty", "failed", "cancelled"].includes(status);
231
+ if (terminal) {
232
+ const rowCount = Number(snapshot.counts?.rows ?? snapshot.rowCount ?? snapshot.counts?.materialized ?? 0);
233
+ const failed = status === "failed";
234
+ const cancelled = status === "cancelled" || snapshot.cancelled === true;
235
+ return {
236
+ ready: !failed && !cancelled,
237
+ reason: failed ? "import_failed" : cancelled ? "cancelled" : undefined,
238
+ runId: snapshot.runId ?? input.findLeadsRunId,
239
+ leadListId: snapshot.leadListId ?? input.findLeadsRunId,
240
+ provider: String(snapshot.provider ?? "").replaceAll("_", "-") || null,
241
+ jobId: snapshot.importJobId ?? null,
242
+ status: snapshot.status,
243
+ rowCount,
244
+ counts: snapshot.counts ?? { rows: rowCount },
245
+ retryable: snapshot.retryable ?? false,
246
+ attempts,
247
+ elapsedMs: Date.now() - startedAt,
248
+ };
249
+ }
250
+ if (Date.now() - startedAt >= effectiveTimeoutMs)
251
+ break;
252
+ await sleep(intervalMs);
253
+ }
254
+ return {
255
+ ready: false,
256
+ reason: guardApplied ? "tool_timeout_guard" : "timeout",
257
+ runId: input.findLeadsRunId,
258
+ leadListId: snapshot?.leadListId ?? input.findLeadsRunId,
259
+ provider: snapshot?.provider
260
+ ? String(snapshot.provider).replaceAll("_", "-")
261
+ : null,
262
+ jobId: snapshot?.importJobId ?? null,
263
+ status: snapshot?.status,
264
+ rowCount: Number(snapshot?.counts?.rows ?? snapshot?.rowCount ?? 0),
265
+ retryable: true,
266
+ attempts,
267
+ elapsedMs: Date.now() - startedAt,
268
+ warning: guardApplied
269
+ ? `Stopped after ${effectiveTimeoutMs}ms to avoid host tool timeout (requested ${requestedTimeoutMs}ms). Re-run wait_for_lead_list_ready with the same findLeadsRunId.`
270
+ : undefined,
271
+ };
272
+ }
207
273
  let leadListId = input.leadListId;
208
274
  let provider = input.provider;
209
275
  let targetLeadCount = input.targetLeadCount;
@@ -1,5 +1,4 @@
1
1
  import { searchSignals } from "./leads.js";
2
- import { type SchedulerExpectedTargetInput } from "./scheduler-run.js";
3
2
  export type RefillSendsApprovalMode = "approve" | "mark_ready";
4
3
  export type PaidInmailRefreshAction = {
5
4
  senderId: string;
@@ -135,43 +134,6 @@ export declare function sleep(ms: number): Promise<unknown>;
135
134
  export declare function recordValue(value: unknown): Record<string, unknown> | null;
136
135
  export declare function stringValue(value: unknown): string | null;
137
136
  export declare function numberValue(value: unknown): number | null;
138
- export type SchedulerChangedCount = {
139
- campaignId: string;
140
- tableId: string;
141
- actionType: string;
142
- count: number;
143
- };
144
- export type SchedulerChangedCounts = {
145
- complete: true;
146
- truncated: false;
147
- total: number;
148
- byCampaignTableActionType: SchedulerChangedCount[];
149
- byCampaignTableActionTypeSender: Array<SchedulerChangedCount & {
150
- senderId: string;
151
- }>;
152
- };
153
- export declare function normalizeSchedulerChangedCounts(value: unknown): SchedulerChangedCounts | null;
154
- export declare function normalizeSchedulerPrimitiveResult(value: unknown, expected?: {
155
- expectedTargets: SchedulerExpectedTargetInput[];
156
- expectedTargetsHash: string;
157
- maxPlacements: number;
158
- }): {
159
- status: "scheduler_receipt_incomplete";
160
- schedulerStatus: string;
161
- receipt: {} | null;
162
- retryAfterMs: number | null;
163
- changedCounts: null;
164
- auditComplete: false;
165
- blocker: "scheduler_receipt_incomplete";
166
- } | {
167
- status: string;
168
- receipt: {} | null;
169
- retryAfterMs: number | null;
170
- changedCounts: SchedulerChangedCounts;
171
- auditComplete: true;
172
- schedulerStatus?: undefined;
173
- blocker?: undefined;
174
- };
175
137
  export declare function stringArray(value: unknown): string[];
176
138
  export declare function prepareRowSelectorValue(value: unknown): PrepareRowSelector | undefined;
177
139
  export declare function uniqueStrings(values: Array<string | null | undefined>): string[];