@sellable/mcp 0.1.186 → 0.1.188

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index-dev.js CHANGED
File without changes
package/dist/index.js CHANGED
File without changes
@@ -92,8 +92,8 @@ export async function getAuthStatus() {
92
92
  const envLabel = config.activeEnvName || null;
93
93
  const wsLabel = workspaceName || activeWorkspaceId;
94
94
  const notice = envLabel
95
- ? `I’m building this in ${wsLabel} (${envLabel}).`
96
- : `I’m building this in ${wsLabel}.`;
95
+ ? `You're in ${wsLabel} (${envLabel}).`
96
+ : `You're in ${wsLabel}.`;
97
97
  return {
98
98
  ok: true,
99
99
  configPath,
@@ -137,9 +137,9 @@ export async function getAuthStatus() {
137
137
  " (If your team already uses Sellable, ask an admin to invite you into their shared workspace instead — that gets you straight in.)` " +
138
138
  "5) Call `mcp__sellable__wait_for_cli_login({ sessionId })` using the sessionId returned by start_cli_login. " +
139
139
  "6) If the result is `error.type === 'tool_timeout_guard'`, IMMEDIATELY re-call wait_for_cli_login with the SAME sessionId — do not narrate, do not call start_cli_login again. Loop until you get a different result. " +
140
- "7) On `ok: true`, the user is signed in and `~/.sellable/config.json` has been written. Branch on `isReturningUser`: " +
141
- "if true, say `You're in.\\n\\nWhat is your LinkedIn profile URL or handle?`; " +
142
- "if false, say `You're set up.\\n\\nWhat is your LinkedIn profile URL or handle?`";
140
+ "7) On `ok: true`, the user is signed in and `~/.sellable/config.json` has been written. Branch on `isReturningUser` and use `activeWorkspaceName` when present, otherwise `activeWorkspaceId`, as `{workspaceLabel}`: " +
141
+ "if true, say `You're in {workspaceLabel}.\\n\\nExcited to help you launch your LinkedIn outbound campaign. We're at setup: first I'll use your LinkedIn profile to understand the company, then I'll draft the campaign brief, help choose where to find buyers, review messages, and wait for final launch approval.\\n\\nWhat's your LinkedIn profile URL or handle?`; " +
142
+ "if false, say `You're set up in {workspaceLabel}.\\n\\nExcited to help you launch your LinkedIn outbound campaign. We're at setup: first I'll use your LinkedIn profile to understand the company, then I'll draft the campaign brief, help choose where to find buyers, review messages, and wait for final launch approval.\\n\\nWhat's your LinkedIn profile URL or handle?`";
143
143
  if (error instanceof SellableApiError && error.isAuthError) {
144
144
  return {
145
145
  ...base,
@@ -40,6 +40,26 @@ export function getCampaignBuilderWatchModeParam() {
40
40
  function buildCampaignBuilderWatchPath(campaignId) {
41
41
  return `/campaign-builder/${campaignId}?mode=${getCampaignBuilderWatchModeParam()}`;
42
42
  }
43
+ function isValidBriefHandoffWatchUrl(watchUrl, campaignId) {
44
+ try {
45
+ const url = new URL(watchUrl);
46
+ const mode = url.searchParams.get("mode");
47
+ return (url.pathname === `/campaign-builder/${campaignId}` &&
48
+ (mode === "claude" || mode === "codex") &&
49
+ Boolean(url.searchParams.get("workspaceId")) &&
50
+ Boolean(url.searchParams.get("token")));
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ function assertBriefHandoffWatchUrl(watchUrl, campaignId) {
57
+ if (isValidBriefHandoffWatchUrl(watchUrl, campaignId))
58
+ return;
59
+ throw new Error("create_campaign produced an invalid watchUrl for the brief approval handoff. " +
60
+ "Recover a fresh direct /campaign-builder/{campaignId}?mode={claude|codex}&workspaceId=...&token=... URL " +
61
+ "with create_campaign({ campaignId }) or get_campaign before asking for approval.");
62
+ }
43
63
  export const campaignToolDefinitions = [
44
64
  {
45
65
  name: "get_campaigns",
@@ -648,22 +668,28 @@ function deriveOfferValue(name, briefContent) {
648
668
  return heading.slice(0, 120);
649
669
  return lines[0].slice(0, 120);
650
670
  }
651
- function buildBriefApprovalGate(watchUrl) {
671
+ function buildBriefApprovalGate(campaignId, watchUrl) {
672
+ assertBriefHandoffWatchUrl(watchUrl, campaignId);
652
673
  return {
653
674
  approvalGate: {
654
675
  kind: "brief-review",
655
676
  mustShowBeforeQuestion: [
656
- "the full campaignBrief markdown the user is approving",
657
- `Watch link: ${watchUrl}`,
677
+ "the full campaignBrief markdown the user is approving, shown exactly once",
678
+ `the exact create_campaign.watchUrl value, shown exactly once: ${watchUrl}`,
658
679
  ],
659
680
  questionToolBlockedUntilShown: true,
660
681
  },
661
- nextStep: "Before asking for approval, render the full campaign brief in normal chat text, then print this watch-link handoff exactly in normal chat text: " +
682
+ nextStep: "Before asking for approval, make sure the user has seen the full campaign brief exactly once in normal chat text. " +
683
+ "If you already rendered the brief immediately before this tool call, do not render it again. " +
684
+ "Then print exactly one watch-link handoff using this exact create_campaign.watchUrl value. " +
685
+ "Do not derive, shorten, reconstruct, or print any bare /campaign-builder/{campaignId} URL. " +
686
+ "Print this handoff exactly in normal chat text: " +
662
687
  `"Campaign shell is ready. We'll keep building the campaign in this chat.\n` +
663
688
  `You can watch it being built in real time in the app here:\n\n` +
664
689
  `Watch link: ${watchUrl}\n\n` +
665
690
  `Send changes here. I'll ask for your approval whenever I need your expertise or taste before moving forward." ` +
666
- "Do not call AskUserQuestion or request_user_input until the user has seen both the brief content and this watch-link handoff.",
691
+ "Do not print another Watch link during source/provider selection. " +
692
+ "Do not call AskUserQuestion or request_user_input until the user has seen both the brief content and this exact watch-link handoff.",
667
693
  };
668
694
  }
669
695
  function buildResumeNextStep() {
@@ -804,7 +830,7 @@ export async function createCampaign(input) {
804
830
  createdAt: result.createdAt,
805
831
  currentStep: result.currentStep,
806
832
  watchUrl,
807
- ...buildBriefApprovalGate(watchUrl),
833
+ ...buildBriefApprovalGate(result.id, watchUrl),
808
834
  };
809
835
  }
810
836
  export async function updateCampaign(campaignId, input) {
@@ -146,6 +146,7 @@ export type ConfirmLeadListInput = {
146
146
  keepInSync?: boolean;
147
147
  jobId?: string;
148
148
  reviewBatchLimit?: number;
149
+ allowPartialSourceList?: boolean;
149
150
  /**
150
151
  * Deprecated alias for reviewBatchLimit. Confirming a lead list now copies the
151
152
  * confirmed source into the campaign table; this value no longer caps clone
@@ -244,6 +245,7 @@ export declare const leadToolDefinitions: ({
244
245
  keepInSync?: undefined;
245
246
  jobId?: undefined;
246
247
  reviewBatchLimit?: undefined;
248
+ allowPartialSourceList?: undefined;
247
249
  selections?: undefined;
248
250
  selectionMode?: undefined;
249
251
  };
@@ -420,6 +422,7 @@ export declare const leadToolDefinitions: ({
420
422
  keepInSync?: undefined;
421
423
  jobId?: undefined;
422
424
  reviewBatchLimit?: undefined;
425
+ allowPartialSourceList?: undefined;
423
426
  selections?: undefined;
424
427
  selectionMode?: undefined;
425
428
  };
@@ -495,6 +498,7 @@ export declare const leadToolDefinitions: ({
495
498
  keepInSync?: undefined;
496
499
  jobId?: undefined;
497
500
  reviewBatchLimit?: undefined;
501
+ allowPartialSourceList?: undefined;
498
502
  selections?: undefined;
499
503
  selectionMode?: undefined;
500
504
  };
@@ -642,6 +646,7 @@ export declare const leadToolDefinitions: ({
642
646
  keepInSync?: undefined;
643
647
  jobId?: undefined;
644
648
  reviewBatchLimit?: undefined;
649
+ allowPartialSourceList?: undefined;
645
650
  selections?: undefined;
646
651
  selectionMode?: undefined;
647
652
  };
@@ -731,6 +736,7 @@ export declare const leadToolDefinitions: ({
731
736
  keepInSync?: undefined;
732
737
  jobId?: undefined;
733
738
  reviewBatchLimit?: undefined;
739
+ allowPartialSourceList?: undefined;
734
740
  selections?: undefined;
735
741
  selectionMode?: undefined;
736
742
  };
@@ -829,6 +835,7 @@ export declare const leadToolDefinitions: ({
829
835
  keepInSync?: undefined;
830
836
  jobId?: undefined;
831
837
  reviewBatchLimit?: undefined;
838
+ allowPartialSourceList?: undefined;
832
839
  selections?: undefined;
833
840
  selectionMode?: undefined;
834
841
  };
@@ -909,6 +916,7 @@ export declare const leadToolDefinitions: ({
909
916
  keepInSync?: undefined;
910
917
  jobId?: undefined;
911
918
  reviewBatchLimit?: undefined;
919
+ allowPartialSourceList?: undefined;
912
920
  selections?: undefined;
913
921
  selectionMode?: undefined;
914
922
  };
@@ -1528,6 +1536,7 @@ export declare const leadToolDefinitions: ({
1528
1536
  keepInSync?: undefined;
1529
1537
  jobId?: undefined;
1530
1538
  reviewBatchLimit?: undefined;
1539
+ allowPartialSourceList?: undefined;
1531
1540
  selections?: undefined;
1532
1541
  selectionMode?: undefined;
1533
1542
  };
@@ -1660,6 +1669,7 @@ export declare const leadToolDefinitions: ({
1660
1669
  keepInSync?: undefined;
1661
1670
  jobId?: undefined;
1662
1671
  reviewBatchLimit?: undefined;
1672
+ allowPartialSourceList?: undefined;
1663
1673
  selections?: undefined;
1664
1674
  selectionMode?: undefined;
1665
1675
  };
@@ -1780,6 +1790,7 @@ export declare const leadToolDefinitions: ({
1780
1790
  keepInSync?: undefined;
1781
1791
  jobId?: undefined;
1782
1792
  reviewBatchLimit?: undefined;
1793
+ allowPartialSourceList?: undefined;
1783
1794
  selections?: undefined;
1784
1795
  selectionMode?: undefined;
1785
1796
  };
@@ -1858,6 +1869,7 @@ export declare const leadToolDefinitions: ({
1858
1869
  keepInSync?: undefined;
1859
1870
  jobId?: undefined;
1860
1871
  reviewBatchLimit?: undefined;
1872
+ allowPartialSourceList?: undefined;
1861
1873
  selections?: undefined;
1862
1874
  selectionMode?: undefined;
1863
1875
  };
@@ -1901,6 +1913,10 @@ export declare const leadToolDefinitions: ({
1901
1913
  type: string;
1902
1914
  description: string;
1903
1915
  };
1916
+ allowPartialSourceList: {
1917
+ type: string;
1918
+ description: string;
1919
+ };
1904
1920
  confirmed: {
1905
1921
  type: string;
1906
1922
  description: string;
@@ -2066,6 +2082,7 @@ export declare const leadToolDefinitions: ({
2066
2082
  keepInSync?: undefined;
2067
2083
  jobId?: undefined;
2068
2084
  reviewBatchLimit?: undefined;
2085
+ allowPartialSourceList?: undefined;
2069
2086
  };
2070
2087
  required: string[];
2071
2088
  };
@@ -2146,6 +2163,7 @@ export declare const leadToolDefinitions: ({
2146
2163
  keepInSync?: undefined;
2147
2164
  jobId?: undefined;
2148
2165
  reviewBatchLimit?: undefined;
2166
+ allowPartialSourceList?: undefined;
2149
2167
  selections?: undefined;
2150
2168
  selectionMode?: undefined;
2151
2169
  };
@@ -2388,25 +2406,14 @@ export declare function importLeads(input: ImportLeadsInput): Promise<{
2388
2406
  existingLeadListId: string;
2389
2407
  reusedExistingSourceList: boolean;
2390
2408
  message: string;
2391
- suggestedToolCalls: ({
2409
+ suggestedToolCalls: {
2392
2410
  tool: string;
2393
2411
  args: {
2394
2412
  campaignOfferId: string;
2395
2413
  leadListId: string;
2396
2414
  provider: string;
2397
- sourceLeadListId?: undefined;
2398
- confirmed?: undefined;
2399
2415
  };
2400
- } | {
2401
- tool: string;
2402
- args: {
2403
- campaignOfferId: string;
2404
- sourceLeadListId: string;
2405
- confirmed: boolean;
2406
- leadListId?: undefined;
2407
- provider?: undefined;
2408
- };
2409
- })[];
2416
+ }[];
2410
2417
  error?: undefined;
2411
2418
  needsModeSelection?: undefined;
2412
2419
  modeOptions?: undefined;
@@ -2648,6 +2655,14 @@ export declare function confirmLeadList(input: ConfirmLeadListInput): Promise<{
2648
2655
  copyStillRunning: boolean;
2649
2656
  trimmedOverflowRowCount: number;
2650
2657
  sourceShortfall: boolean;
2658
+ partialSourceListAccepted: boolean;
2659
+ };
2660
+ sourceListSnapshot: {
2661
+ partial: boolean;
2662
+ sourceRowCountAtConfirmation: number;
2663
+ targetLeadCount: number | null;
2664
+ importStatus: string | null;
2665
+ warning: string | null;
2651
2666
  };
2652
2667
  message: string;
2653
2668
  }>;
@@ -1510,6 +1510,10 @@ export const leadToolDefinitions = [
1510
1510
  type: "number",
1511
1511
  description: "Number of campaign rows to use as the initial review/process sample. Defaults to 15.",
1512
1512
  },
1513
+ allowPartialSourceList: {
1514
+ type: "boolean",
1515
+ description: "Explicit override for user-approved early continuation. Default false. When true, confirm_lead_list may copy the currently materialized rows from a still-running source import; use only after the user explicitly asks to keep going with the partial list.",
1516
+ },
1513
1517
  confirmed: {
1514
1518
  type: "boolean",
1515
1519
  description: "Set true after user approval when interaction mode requires confirmation for confirm/import.",
@@ -2398,7 +2402,7 @@ export async function importLeads(input) {
2398
2402
  leadListId: campaignSelectedLeadListId,
2399
2403
  existingLeadListId: campaignSelectedLeadListId,
2400
2404
  reusedExistingSourceList: true,
2401
- message: "A Signal Discovery source list already exists for the approved selected-post scrape. Reuse it and continue with wait_for_lead_list_ready or confirm_lead_list; do not ask add/replace and do not rescrape unless the user approves a different selected-post scrape.",
2405
+ message: "A Signal Discovery source list already exists for the approved selected-post scrape. Reuse it and continue with wait_for_lead_list_ready before confirm_lead_list; do not ask add/replace and do not rescrape unless the user approves a different selected-post scrape.",
2402
2406
  suggestedToolCalls: [
2403
2407
  {
2404
2408
  tool: "wait_for_lead_list_ready",
@@ -2408,14 +2412,6 @@ export async function importLeads(input) {
2408
2412
  provider: "signal-discovery",
2409
2413
  },
2410
2414
  },
2411
- {
2412
- tool: "confirm_lead_list",
2413
- args: {
2414
- campaignOfferId,
2415
- sourceLeadListId: campaignSelectedLeadListId,
2416
- confirmed: true,
2417
- },
2418
- },
2419
2415
  ],
2420
2416
  };
2421
2417
  }
@@ -2585,7 +2581,7 @@ export async function importLeads(input) {
2585
2581
  targetLeadCount: cappedTargetLeadCount ?? null,
2586
2582
  message: `Started scraping ${postsToScrape.length} posts (~${result.estimatedEngagers} people to check). Leads will appear as scraping completes.${importSelection.limited
2587
2583
  ? ` Limited from ${uniqueSelectedPosts.length} selected posts by the approved source-capacity scrape plan.`
2588
- : ""} The watched campaign has been moved to confirm-lead-list with import progress copy; do not call update_campaign to fix that step.`,
2584
+ : ""} The watched campaign has been moved to confirm-lead-list with import progress copy. Keep calling wait_for_lead_list_ready until it reports ready, failed, or cancelled before confirm_lead_list; only confirm a partial list with allowPartialSourceList after the user explicitly asks to keep going early. Do not call update_campaign to fix that step.`,
2589
2585
  };
2590
2586
  }
2591
2587
  // === SALES NAV / PROSPEO FLOW ===
@@ -2698,7 +2694,7 @@ export async function importLeads(input) {
2698
2694
  jobResult,
2699
2695
  jobId,
2700
2696
  targetLeadCount: cappedTargetLeadCount ?? null,
2701
- message: "Import started and the watched campaign has been moved to confirm-lead-list with import progress copy. Review the lead list as it fills; once it looks good, call confirm_lead_list. Do not call update_campaign to fix the import step.",
2697
+ message: "Import started and the watched campaign has been moved to confirm-lead-list with import progress copy. Wait until wait_for_lead_list_ready reports the source import is complete before calling confirm_lead_list. If the user explicitly asks to keep going early, call confirm_lead_list with allowPartialSourceList: true; otherwise keep polling, stop on failure/cancellation, and do not call update_campaign to fix the import step.",
2702
2698
  };
2703
2699
  }
2704
2700
  export async function cancelLeadImport(input) {
@@ -2745,7 +2741,7 @@ export async function cancelLeadImport(input) {
2745
2741
  }
2746
2742
  export async function confirmLeadList(input) {
2747
2743
  const api = getApi();
2748
- const { campaignOfferId, currentStep, confirmed, sourceLeadListId, campaignName, keepInSync, jobId, reviewBatchLimit, targetLeadCount, } = input;
2744
+ const { campaignOfferId, currentStep, confirmed, sourceLeadListId, campaignName, keepInSync, jobId, reviewBatchLimit, allowPartialSourceList, targetLeadCount, } = input;
2749
2745
  assertInteractionApproval({
2750
2746
  campaignId: campaignOfferId,
2751
2747
  action: "confirm-lead-list",
@@ -2859,26 +2855,61 @@ export async function confirmLeadList(input) {
2859
2855
  intervalMs: 1000,
2860
2856
  });
2861
2857
  }
2858
+ let partialSourceListAccepted = false;
2859
+ let partialSourceListWarning = null;
2862
2860
  if (!readiness.ready) {
2863
- if (readiness.reason === "missing_job_id") {
2861
+ if (allowPartialSourceList === true &&
2862
+ readiness.reason === "import_still_running" &&
2863
+ leadListRowCount > 0) {
2864
+ partialSourceListAccepted = true;
2865
+ partialSourceListWarning =
2866
+ "User explicitly asked to continue before the source import completed. This campaign table is based on the currently materialized source-list rows only.";
2867
+ readiness = {
2868
+ ready: true,
2869
+ leadListId: resolvedLeadListId,
2870
+ provider: resolvedProvider ?? null,
2871
+ attempts: typeof readiness.attempts === "number" ? readiness.attempts : 1,
2872
+ elapsedMs: typeof readiness.elapsedMs === "number" ? readiness.elapsedMs : 0,
2873
+ rowCount: leadListRowCount,
2874
+ status: typeof readiness.status === "string"
2875
+ ? readiness.status
2876
+ : leadListConfig?.importStatus ?? null,
2877
+ targetLeadCount: typeof readiness.targetLeadCount === "number"
2878
+ ? readiness.targetLeadCount
2879
+ : typeof leadListConfig?.targetLeadCount === "number"
2880
+ ? leadListConfig.targetLeadCount
2881
+ : null,
2882
+ partialSourceListAccepted: true,
2883
+ };
2884
+ }
2885
+ else if (readiness.reason === "missing_job_id") {
2864
2886
  const recoveryNarration = buildSourceImportRecoveryWatchNarration({
2865
2887
  reason: "pending",
2866
2888
  provider: resolvedProvider,
2867
2889
  });
2868
2890
  throw new Error(`${recoveryNarration.headline}: Import job ID is missing. Keep the campaign at confirm-lead-list; provide the jobId, retry readiness, cancel the import, or re-run-source before confirming. ${recoveryNarration.safety}`);
2869
2891
  }
2870
- if (readiness.reason === "import_failed") {
2892
+ else if (readiness.reason === "cancelled") {
2893
+ const recoveryNarration = buildSourceImportRecoveryWatchNarration({
2894
+ reason: "failed",
2895
+ provider: resolvedProvider,
2896
+ });
2897
+ throw new Error(`${recoveryNarration.headline}: Import was cancelled. Keep the campaign at confirm-lead-list; do not copy partial rows unless the user explicitly asks to continue from the partial list. ${recoveryNarration.safety}`);
2898
+ }
2899
+ else if (readiness.reason === "import_failed") {
2871
2900
  const recoveryNarration = buildSourceImportRecoveryWatchNarration({
2872
2901
  reason: "failed",
2873
2902
  provider: resolvedProvider,
2874
2903
  });
2875
2904
  throw new Error(`${recoveryNarration.headline}: Import failed. Keep the campaign at confirm-lead-list; retry the provider import, cancel it, or re-run-source before confirming. ${recoveryNarration.safety}`);
2876
2905
  }
2877
- const recoveryNarration = buildSourceImportRecoveryWatchNarration({
2878
- reason: "timeout",
2879
- provider: resolvedProvider,
2880
- });
2881
- throw new Error(`${recoveryNarration.headline}: Import still in progress. Keep the campaign at confirm-lead-list; retry readiness, cancel the import, or re-run-source before launching post-import scouts. ${recoveryNarration.safety}`);
2906
+ else {
2907
+ const recoveryNarration = buildSourceImportRecoveryWatchNarration({
2908
+ reason: "timeout",
2909
+ provider: resolvedProvider,
2910
+ });
2911
+ throw new Error(`${recoveryNarration.headline}: Import still in progress. Keep the campaign at confirm-lead-list; retry readiness, cancel the import, or re-run-source before launching post-import scouts. ${recoveryNarration.safety}`);
2912
+ }
2882
2913
  }
2883
2914
  const isTerminalAccessError = (error) => error instanceof SellableApiError && [401, 403, 404].includes(error.status);
2884
2915
  const formatTerminalAccessError = (error) => {
@@ -3022,12 +3053,20 @@ export async function confirmLeadList(input) {
3022
3053
  sourceShortfall: signalSourceTargetLeadCount !== null &&
3023
3054
  leadListRowCount > 0 &&
3024
3055
  leadListRowCount < signalSourceTargetLeadCount,
3056
+ partialSourceListAccepted,
3057
+ },
3058
+ sourceListSnapshot: {
3059
+ partial: partialSourceListAccepted,
3060
+ sourceRowCountAtConfirmation: leadListRowCount,
3061
+ targetLeadCount: readiness.targetLeadCount ?? null,
3062
+ importStatus: readiness.status ?? null,
3063
+ warning: partialSourceListWarning,
3025
3064
  },
3026
3065
  message: copyStillRunning
3027
- ? `First ${importedRowCount.toLocaleString("en-US")} source candidate${importedRowCount === 1 ? "" : "s"} are copied into the campaign table and the rest of the confirmed source list is still copying. Use the first ${keptReviewRowCount.toLocaleString("en-US")} as the review/process sample. The watched campaign is now on filter-choice; ask add filters vs skip filters before loading post-lead workers.`
3066
+ ? `${partialSourceListAccepted ? "Using the user-approved partial source snapshot. " : ""}First ${importedRowCount.toLocaleString("en-US")} source candidate${importedRowCount === 1 ? "" : "s"} are copied into the campaign table and the rest of the confirmed source list is still copying. Use the first ${keptReviewRowCount.toLocaleString("en-US")} as the review/process sample. The watched campaign is now on filter-choice; ask add filters vs skip filters before loading post-lead workers.`
3028
3067
  : importedRowCount > keptReviewRowCount
3029
- ? `Copied ${importedRowCount.toLocaleString("en-US")} source candidate${importedRowCount === 1 ? "" : "s"} into the campaign table. Use the first ${keptReviewRowCount.toLocaleString("en-US")} as the review/process sample. The watched campaign is now on filter-choice; ask add filters vs skip filters before loading post-lead workers.`
3030
- : `Copied ${importedRowCount.toLocaleString("en-US")} source candidate${importedRowCount === 1 ? "" : "s"} into the campaign table for the review/process sample. The watched campaign is now on filter-choice; ask add filters vs skip filters before loading post-lead workers.`,
3068
+ ? `${partialSourceListAccepted ? "Using the user-approved partial source snapshot. " : ""}Copied ${importedRowCount.toLocaleString("en-US")} source candidate${importedRowCount === 1 ? "" : "s"} into the campaign table. Use the first ${keptReviewRowCount.toLocaleString("en-US")} as the review/process sample. The watched campaign is now on filter-choice; ask add filters vs skip filters before loading post-lead workers.`
3069
+ : `${partialSourceListAccepted ? "Using the user-approved partial source snapshot. " : ""}Copied ${importedRowCount.toLocaleString("en-US")} source candidate${importedRowCount === 1 ? "" : "s"} into the campaign table for the review/process sample. The watched campaign is now on filter-choice; ask add filters vs skip filters before loading post-lead workers.`,
3031
3070
  };
3032
3071
  }
3033
3072
  export function getProviderPrompt(input) {
@@ -500,15 +500,18 @@ export async function waitForLeadListReady(input) {
500
500
  await sleep(intervalMs);
501
501
  }
502
502
  const timedOutWithRows = (lastRowCount ?? 0) > 0;
503
+ const stillRunningWithRows = requireComplete && timedOutWithRows;
503
504
  const timeoutReason = missingJobId
504
505
  ? "missing_job_id"
505
- : timedOutWithRows
506
- ? "timeout_with_rows"
507
- : guardApplied
508
- ? "tool_timeout_guard"
509
- : "timeout";
506
+ : stillRunningWithRows
507
+ ? "import_still_running"
508
+ : timedOutWithRows
509
+ ? "timeout_with_rows"
510
+ : guardApplied
511
+ ? "tool_timeout_guard"
512
+ : "timeout";
510
513
  return {
511
- ready: !missingJobId && timedOutWithRows,
514
+ ready: !requireComplete && !missingJobId && timedOutWithRows,
512
515
  reason: timeoutReason,
513
516
  leadListId,
514
517
  provider: provider ?? null,
@@ -517,11 +520,13 @@ export async function waitForLeadListReady(input) {
517
520
  rowCount: lastRowCount,
518
521
  status: lastStatus ?? undefined,
519
522
  targetLeadCount: targetLeadCount ?? undefined,
520
- warning: !missingJobId && timedOutWithRows
521
- ? "Timed out waiting for import status, but rows exist."
522
- : guardApplied
523
- ? `Stopped after ${effectiveTimeoutMs}ms to avoid host tool timeout (requested ${requestedTimeoutMs}ms). Re-run wait_for_lead_list_ready to keep polling.`
524
- : undefined,
523
+ warning: stillRunningWithRows
524
+ ? `Import still appears to be running with ${lastRowCount?.toLocaleString("en-US") ?? "some"} row(s) available. Re-run wait_for_lead_list_ready to keep polling, or only call confirm_lead_list with allowPartialSourceList after the user explicitly asks to continue early.`
525
+ : !missingJobId && timedOutWithRows
526
+ ? "Timed out waiting for import status, but rows exist."
527
+ : guardApplied
528
+ ? `Stopped after ${effectiveTimeoutMs}ms to avoid host tool timeout (requested ${requestedTimeoutMs}ms). Re-run wait_for_lead_list_ready to keep polling.`
529
+ : undefined,
525
530
  error: lastError ?? undefined,
526
531
  };
527
532
  }
@@ -1392,6 +1392,7 @@ export declare const allTools: ({
1392
1392
  keepInSync?: undefined;
1393
1393
  jobId?: undefined;
1394
1394
  reviewBatchLimit?: undefined;
1395
+ allowPartialSourceList?: undefined;
1395
1396
  selections?: undefined;
1396
1397
  selectionMode?: undefined;
1397
1398
  };
@@ -1568,6 +1569,7 @@ export declare const allTools: ({
1568
1569
  keepInSync?: undefined;
1569
1570
  jobId?: undefined;
1570
1571
  reviewBatchLimit?: undefined;
1572
+ allowPartialSourceList?: undefined;
1571
1573
  selections?: undefined;
1572
1574
  selectionMode?: undefined;
1573
1575
  };
@@ -1643,6 +1645,7 @@ export declare const allTools: ({
1643
1645
  keepInSync?: undefined;
1644
1646
  jobId?: undefined;
1645
1647
  reviewBatchLimit?: undefined;
1648
+ allowPartialSourceList?: undefined;
1646
1649
  selections?: undefined;
1647
1650
  selectionMode?: undefined;
1648
1651
  };
@@ -1790,6 +1793,7 @@ export declare const allTools: ({
1790
1793
  keepInSync?: undefined;
1791
1794
  jobId?: undefined;
1792
1795
  reviewBatchLimit?: undefined;
1796
+ allowPartialSourceList?: undefined;
1793
1797
  selections?: undefined;
1794
1798
  selectionMode?: undefined;
1795
1799
  };
@@ -1879,6 +1883,7 @@ export declare const allTools: ({
1879
1883
  keepInSync?: undefined;
1880
1884
  jobId?: undefined;
1881
1885
  reviewBatchLimit?: undefined;
1886
+ allowPartialSourceList?: undefined;
1882
1887
  selections?: undefined;
1883
1888
  selectionMode?: undefined;
1884
1889
  };
@@ -1977,6 +1982,7 @@ export declare const allTools: ({
1977
1982
  keepInSync?: undefined;
1978
1983
  jobId?: undefined;
1979
1984
  reviewBatchLimit?: undefined;
1985
+ allowPartialSourceList?: undefined;
1980
1986
  selections?: undefined;
1981
1987
  selectionMode?: undefined;
1982
1988
  };
@@ -2057,6 +2063,7 @@ export declare const allTools: ({
2057
2063
  keepInSync?: undefined;
2058
2064
  jobId?: undefined;
2059
2065
  reviewBatchLimit?: undefined;
2066
+ allowPartialSourceList?: undefined;
2060
2067
  selections?: undefined;
2061
2068
  selectionMode?: undefined;
2062
2069
  };
@@ -2676,6 +2683,7 @@ export declare const allTools: ({
2676
2683
  keepInSync?: undefined;
2677
2684
  jobId?: undefined;
2678
2685
  reviewBatchLimit?: undefined;
2686
+ allowPartialSourceList?: undefined;
2679
2687
  selections?: undefined;
2680
2688
  selectionMode?: undefined;
2681
2689
  };
@@ -2808,6 +2816,7 @@ export declare const allTools: ({
2808
2816
  keepInSync?: undefined;
2809
2817
  jobId?: undefined;
2810
2818
  reviewBatchLimit?: undefined;
2819
+ allowPartialSourceList?: undefined;
2811
2820
  selections?: undefined;
2812
2821
  selectionMode?: undefined;
2813
2822
  };
@@ -2928,6 +2937,7 @@ export declare const allTools: ({
2928
2937
  keepInSync?: undefined;
2929
2938
  jobId?: undefined;
2930
2939
  reviewBatchLimit?: undefined;
2940
+ allowPartialSourceList?: undefined;
2931
2941
  selections?: undefined;
2932
2942
  selectionMode?: undefined;
2933
2943
  };
@@ -3006,6 +3016,7 @@ export declare const allTools: ({
3006
3016
  keepInSync?: undefined;
3007
3017
  jobId?: undefined;
3008
3018
  reviewBatchLimit?: undefined;
3019
+ allowPartialSourceList?: undefined;
3009
3020
  selections?: undefined;
3010
3021
  selectionMode?: undefined;
3011
3022
  };
@@ -3049,6 +3060,10 @@ export declare const allTools: ({
3049
3060
  type: string;
3050
3061
  description: string;
3051
3062
  };
3063
+ allowPartialSourceList: {
3064
+ type: string;
3065
+ description: string;
3066
+ };
3052
3067
  confirmed: {
3053
3068
  type: string;
3054
3069
  description: string;
@@ -3214,6 +3229,7 @@ export declare const allTools: ({
3214
3229
  keepInSync?: undefined;
3215
3230
  jobId?: undefined;
3216
3231
  reviewBatchLimit?: undefined;
3232
+ allowPartialSourceList?: undefined;
3217
3233
  };
3218
3234
  required: string[];
3219
3235
  };
@@ -3294,6 +3310,7 @@ export declare const allTools: ({
3294
3310
  keepInSync?: undefined;
3295
3311
  jobId?: undefined;
3296
3312
  reviewBatchLimit?: undefined;
3313
+ allowPartialSourceList?: undefined;
3297
3314
  selections?: undefined;
3298
3315
  selectionMode?: undefined;
3299
3316
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.186",
3
+ "version": "0.1.188",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",