@sellable/mcp 0.1.151 → 0.1.152
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/README.md +4 -3
- package/agents/post-find-leads-filter-scout.md +5 -4
- package/agents/post-find-leads-message-scout.md +15 -14
- package/agents/source-scout-linkedin-engagement.md +6 -5
- package/agents/source-scout-prospeo-contact.md +4 -4
- package/agents/source-scout-sales-nav.md +4 -4
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/tools/cells.js +1 -1
- package/dist/tools/leads.d.ts +36 -3
- package/dist/tools/leads.js +83 -71
- package/dist/tools/prompts.js +9 -9
- package/dist/tools/registry.d.ts +17 -0
- package/dist/tools/rubrics.js +23 -20
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +59 -56
- package/skills/create-campaign-v2/SKILL.md +43 -42
- package/skills/create-campaign-v2/SOUL.md +16 -13
- package/skills/create-campaign-v2/core/auto-execute.README.md +16 -17
- package/skills/create-campaign-v2/core/auto-execute.yaml +8 -7
- package/skills/create-campaign-v2/core/flow.v2.json +81 -149
- package/skills/create-campaign-v2/core/policy.md +13 -12
- package/skills/create-campaign-v2/references/approval-gate-framing.md +4 -3
- package/skills/create-campaign-v2/references/filter-leads.md +5 -4
- package/skills/create-campaign-v2/references/lead-validation-preview.md +2 -2
- package/skills/create-campaign-v2/references/sample-validation-loop.md +32 -27
- package/skills/create-campaign-v2/references/step-13-import-leads.md +29 -28
- package/skills/create-campaign-v2/references/watch-guide-narration.md +27 -28
- package/skills/create-campaign-v2-tail/SKILL.md +44 -44
- package/skills/create-rubric/SKILL.md +5 -5
- package/skills/find-leads/SKILL.md +2 -2
- package/skills/generate-messages/SKILL.md +2 -1
- package/skills/providers/prospeo.md +3 -3
- package/skills/providers/sales-nav.md +7 -7
- package/skills/providers/signal-discovery.md +11 -11
package/dist/tools/leads.js
CHANGED
|
@@ -29,29 +29,30 @@ const defaultSignalDiscoveryConfig = {
|
|
|
29
29
|
};
|
|
30
30
|
const defaultCampaignSourceDefaults = {
|
|
31
31
|
reviewBatch: {
|
|
32
|
-
defaultSize:
|
|
33
|
-
minProjectedPass:
|
|
32
|
+
defaultSize: 15,
|
|
33
|
+
minProjectedPass: 1,
|
|
34
34
|
},
|
|
35
35
|
planning: {
|
|
36
36
|
minFitRate: 0.1,
|
|
37
37
|
},
|
|
38
38
|
providers: {
|
|
39
39
|
"signal-discovery": {
|
|
40
|
-
targetGoodFitLeads:
|
|
41
|
-
defaultFitRate: 0.
|
|
40
|
+
targetGoodFitLeads: 300,
|
|
41
|
+
defaultFitRate: 0.2,
|
|
42
42
|
maxSourceCandidates: 2500,
|
|
43
43
|
postCoverageBuffer: 1.2,
|
|
44
44
|
},
|
|
45
45
|
"sales-nav": {
|
|
46
|
-
targetGoodFitLeads:
|
|
46
|
+
targetGoodFitLeads: 300,
|
|
47
47
|
maxSourceCandidates: 2500,
|
|
48
48
|
},
|
|
49
49
|
prospeo: {
|
|
50
|
-
targetGoodFitLeads:
|
|
50
|
+
targetGoodFitLeads: 300,
|
|
51
51
|
maxSourceCandidates: 2500,
|
|
52
52
|
},
|
|
53
53
|
},
|
|
54
54
|
};
|
|
55
|
+
const defaultProviderSourceListTarget = 1000;
|
|
55
56
|
const prospeoFilterValueSchema = {
|
|
56
57
|
type: "object",
|
|
57
58
|
description: "Include/exclude list filter (values must match Prospeo enums)",
|
|
@@ -517,7 +518,7 @@ function buildSourceImportWatchNarration({ provider, selectedPostCount, estimate
|
|
|
517
518
|
: ""}`
|
|
518
519
|
: `the approved ${providerLabel} source`;
|
|
519
520
|
const targetDetail = typeof targetLeadCount === "number"
|
|
520
|
-
? ` Targeting ${targetLeadCount.toLocaleString("en-US")} source leads before the
|
|
521
|
+
? ` Targeting ${targetLeadCount.toLocaleString("en-US")} source leads before the first review sample is processed.`
|
|
521
522
|
: "";
|
|
522
523
|
return {
|
|
523
524
|
stage: "find-leads",
|
|
@@ -525,9 +526,9 @@ function buildSourceImportWatchNarration({ provider, selectedPostCount, estimate
|
|
|
525
526
|
? "Scraping source leads from posts"
|
|
526
527
|
: "Importing source leads",
|
|
527
528
|
visibleState: `The browser is showing ${providerLabel} import progress for ${sourceDetail}.${targetDetail}`,
|
|
528
|
-
agentIntent: "Codex is materializing the approved source into a lead list before
|
|
529
|
-
nextAction: "Wait for source leads, then
|
|
530
|
-
safety: "This step materializes the source list;
|
|
529
|
+
agentIntent: "Codex is materializing the approved source into a lead list before copying the confirmed list into the campaign.",
|
|
530
|
+
nextAction: "Wait for source leads, then confirm the list and review the first sample",
|
|
531
|
+
safety: "This step materializes the source list; the first review sample is processed only after the later filter and message approvals.",
|
|
531
532
|
progressLabel: "Source scouting",
|
|
532
533
|
};
|
|
533
534
|
}
|
|
@@ -551,8 +552,8 @@ function buildSourceImportRecoveryWatchNarration(args) {
|
|
|
551
552
|
headline: args.reason === "failed" || args.reason === "zero"
|
|
552
553
|
? "Source import needs attention"
|
|
553
554
|
: "Source import still running",
|
|
554
|
-
visibleState: `${reasonCopy} The browser should stay on the
|
|
555
|
-
agentIntent: "Codex is holding the campaign before filter-choice until
|
|
555
|
+
visibleState: `${reasonCopy} The browser should stay on the source-list import screen.`,
|
|
556
|
+
agentIntent: "Codex is holding the campaign before filter-choice until confirmed source rows exist in the campaign table.",
|
|
556
557
|
nextAction: args.reason === "failed" || args.reason === "zero"
|
|
557
558
|
? "Retry the import or change the approved source"
|
|
558
559
|
: "Wait again, retry readiness, or change the source",
|
|
@@ -666,8 +667,8 @@ function buildFilterChoiceWatchNarration({ sourceLeadCount, reviewRowCount, samp
|
|
|
666
667
|
? ` from ${sourceLeadCount.toLocaleString("en-US")} source candidate${sourceLeadCount === 1 ? "" : "s"}`
|
|
667
668
|
: "";
|
|
668
669
|
const reviewCopy = typeof reviewRowCount === "number" && reviewRowCount > 0
|
|
669
|
-
? `${reviewRowCount.toLocaleString("en-US")} review lead${reviewRowCount === 1 ? " is" : "s are"} in the campaign table${sourceCopy}.`
|
|
670
|
-
: `The
|
|
670
|
+
? `${reviewRowCount.toLocaleString("en-US")} review/process lead${reviewRowCount === 1 ? " is" : "s are"} in the campaign table${sourceCopy}.`
|
|
671
|
+
: `The review/process sample is in the campaign table${sourceCopy}.`;
|
|
671
672
|
const assessment = analyzeFilterChoiceSample(sampleRows);
|
|
672
673
|
const fitCount = assessment.strongFitCount + assessment.adjacentCount;
|
|
673
674
|
const cleanupCount = assessment.riskCount + assessment.unknownCount;
|
|
@@ -734,7 +735,7 @@ function buildSelectedPostApprovalWatchNarration(selectedPostCount) {
|
|
|
734
735
|
stage: "find-leads",
|
|
735
736
|
headline: "Approve selected-post scrape",
|
|
736
737
|
visibleState: `${selectedPostCount.toLocaleString("en-US")} LinkedIn post${selectedPostCount === 1 ? "" : "s"} selected in Signal Discovery.`,
|
|
737
|
-
agentIntent: "Codex is asking before scraping this selected engager pool into a source list
|
|
738
|
+
agentIntent: "Codex is asking before scraping this selected engager pool into a source list.",
|
|
738
739
|
nextAction: `Approve scraping ${selectedPostCount.toLocaleString("en-US")} Signal Discovery post${selectedPostCount === 1 ? "" : "s"}`,
|
|
739
740
|
safety: "Scrape approval is the next gate.",
|
|
740
741
|
};
|
|
@@ -761,10 +762,11 @@ function buildSignalDiscoverySourceRecommendation({ selectedPosts, }) {
|
|
|
761
762
|
|
|
762
763
|
Use Signal Discovery first.
|
|
763
764
|
|
|
764
|
-
**
|
|
765
|
-
**
|
|
765
|
+
**Goal:** ~${targetGoodFitLeads.toLocaleString("en-US")} good-fit prospects after cleanup, enrichment, and filters<br>
|
|
766
|
+
**Working assumption:** ~${Math.round(defaultFitRate * 100)}% of raw post engagers become good-fit prospects<br>
|
|
767
|
+
**Engagers needed:** ~${sourceCandidateTarget.toLocaleString("en-US")} raw engagers<br>
|
|
766
768
|
**Planning floor:** continue with Signal Discovery only when sampled/projected fit is at least ${Math.round(minPlanningFitRate * 100)}% after cleanup; below that, switch to Sales Nav recent activity<br>
|
|
767
|
-
**Review checkpoint:**
|
|
769
|
+
**Review checkpoint:** copy the confirmed source list into the campaign, then process the first ${reviewBatchSize.toLocaleString("en-US")} leads for fit and message review before scaling
|
|
768
770
|
|
|
769
771
|
### Selected posts
|
|
770
772
|
|
|
@@ -779,9 +781,9 @@ ${tableRows || "| Selected posts | Campaign-matched public engagement | - |"}
|
|
|
779
781
|
|
|
780
782
|
Approve scraping these ${selectedCount} posts.
|
|
781
783
|
|
|
782
|
-
This gives enough volume to
|
|
784
|
+
This gives enough volume to target ~${targetGoodFitLeads.toLocaleString("en-US")} good-fit prospects after cleanup, while keeping the source tied to people already engaging with the campaign's strongest public buying signals.
|
|
783
785
|
|
|
784
|
-
**First pass:** build the source list, then
|
|
786
|
+
**First pass:** build the source list, copy it into the campaign, then process only the first ${reviewBatchSize.toLocaleString("en-US")} leads so we can inspect quality before scaling.
|
|
785
787
|
|
|
786
788
|
**Fallback:** if the sampled/projected fit rate is below ${Math.round(minPlanningFitRate * 100)}%, or if the review batch is too vendor-heavy, agency-heavy, or off-ICP, switch to Sales Nav recent activity.
|
|
787
789
|
|
|
@@ -1324,7 +1326,7 @@ export const leadToolDefinitions = [
|
|
|
1324
1326
|
},
|
|
1325
1327
|
{
|
|
1326
1328
|
name: "import_leads",
|
|
1327
|
-
description: "Create/select a source lead list and start the provider import/export job. Requires provider prompt preflight via get_provider_prompt for the active provider. For Sales Nav/Prospeo, targetLeadCount is the approved source-list export count computed from projected good-fit math, not the later
|
|
1329
|
+
description: "Create/select a source lead list and start the provider import/export job. Requires provider prompt preflight via get_provider_prompt for the active provider. For Sales Nav/Prospeo, targetLeadCount is the approved source-list export count computed from projected good-fit math, not the later 15-row review/process sample size. On success, this tool owns moving the watched campaign to confirm-lead-list with import/progress narration after a leadListId/jobId exists; do not call update_campaign to fix that step. After the user confirms the source list, call confirm_lead_list to copy the confirmed source rows into the campaign table and use only the first review/process sample for the initial flow.",
|
|
1328
1330
|
inputSchema: {
|
|
1329
1331
|
type: "object",
|
|
1330
1332
|
properties: {
|
|
@@ -1351,7 +1353,7 @@ export const leadToolDefinitions = [
|
|
|
1351
1353
|
},
|
|
1352
1354
|
targetLeadCount: {
|
|
1353
1355
|
type: "number",
|
|
1354
|
-
description: "Provider source-list target count (max per provider from config). For Sales Nav/Prospeo this should be the export/materialization count needed to hit projected good-fit goals, not the
|
|
1356
|
+
description: "Provider source-list target count (max per provider from config). For Sales Nav/Prospeo this should be the export/materialization count needed to hit projected good-fit goals, not the 15-row review/process sample size.",
|
|
1355
1357
|
},
|
|
1356
1358
|
mode: {
|
|
1357
1359
|
type: "string",
|
|
@@ -1373,7 +1375,7 @@ export const leadToolDefinitions = [
|
|
|
1373
1375
|
},
|
|
1374
1376
|
targetEngagerCount: {
|
|
1375
1377
|
type: "number",
|
|
1376
|
-
description: "Signal Discovery: target number of post engagers/source candidates to scrape.
|
|
1378
|
+
description: "Signal Discovery: target number of post engagers/source candidates to scrape. Default planning target is about 300 good fits at a 20% raw-engager fit assumption, or about 1500 engagers. If the sampled/projected fit rate is below the 10% planning floor after cleanup, switch to the next provider instead of scaling noisy engagers. Limits selected posts before starting scrape.",
|
|
1377
1379
|
},
|
|
1378
1380
|
maxPostsToScrape: {
|
|
1379
1381
|
type: "number",
|
|
@@ -1417,7 +1419,7 @@ export const leadToolDefinitions = [
|
|
|
1417
1419
|
},
|
|
1418
1420
|
{
|
|
1419
1421
|
name: "confirm_lead_list",
|
|
1420
|
-
description: "After the user confirms the lead list looks good,
|
|
1422
|
+
description: "After the user confirms the lead list looks good, copy the confirmed source list into the campaign table and mark the first review/process sample for the flow. This tool owns moving the watched campaign to filter-choice with sample-assessment narration only after non-empty campaign rows exist; do not call update_campaign to fix filter-choice afterward. selectedLeadListId remains the source lead list; workflowTableId is the campaign table.",
|
|
1421
1423
|
inputSchema: {
|
|
1422
1424
|
type: "object",
|
|
1423
1425
|
properties: {
|
|
@@ -1447,7 +1449,11 @@ export const leadToolDefinitions = [
|
|
|
1447
1449
|
},
|
|
1448
1450
|
targetLeadCount: {
|
|
1449
1451
|
type: "number",
|
|
1450
|
-
description: "
|
|
1452
|
+
description: "Deprecated alias for reviewBatchLimit. Does not cap the campaign-table clone; confirmed source rows are copied into the campaign table.",
|
|
1453
|
+
},
|
|
1454
|
+
reviewBatchLimit: {
|
|
1455
|
+
type: "number",
|
|
1456
|
+
description: "Number of campaign rows to use as the initial review/process sample. Defaults to 15.",
|
|
1451
1457
|
},
|
|
1452
1458
|
confirmed: {
|
|
1453
1459
|
type: "boolean",
|
|
@@ -2399,12 +2405,17 @@ export async function importLeads(input) {
|
|
|
2399
2405
|
const maxImportCount = getMaxImportCount(provider);
|
|
2400
2406
|
const normalizedTargetLeadCount = normalizeTargetLeadCount(targetLeadCount, maxImportCount);
|
|
2401
2407
|
const requestedLeadCount = normalizedTargetLeadCount ??
|
|
2402
|
-
(provider === "sales-nav" || provider === "prospeo"
|
|
2408
|
+
(provider === "sales-nav" || provider === "prospeo"
|
|
2409
|
+
? defaultProviderSourceListTarget
|
|
2410
|
+
: undefined);
|
|
2403
2411
|
const cappedTargetLeadCount = requestedLeadCount !== undefined
|
|
2404
2412
|
? Math.min(requestedLeadCount, maxImportCount)
|
|
2405
2413
|
: undefined;
|
|
2406
2414
|
// === SIGNAL DISCOVERY FLOW ===
|
|
2407
2415
|
if (provider === "signal-discovery") {
|
|
2416
|
+
const defaultSignalTargetEngagers = getSignalDiscoverySourcePlanDefaults().sourceCandidateTarget;
|
|
2417
|
+
const effectiveTargetEngagerCount = normalizePositiveInteger(targetEngagerCount) ??
|
|
2418
|
+
defaultSignalTargetEngagers;
|
|
2408
2419
|
// Get selected posts from the campaign's signal search tabs
|
|
2409
2420
|
// Note: API returns flat fields (postUrl, postContent, authorName, etc.)
|
|
2410
2421
|
const tabsResponse = await api.get(`/api/v3/campaigns/${campaignOfferId}/signal-discovery/tabs`);
|
|
@@ -2449,7 +2460,7 @@ export async function importLeads(input) {
|
|
|
2449
2460
|
}
|
|
2450
2461
|
const uniqueSelectedPosts = Array.from(uniqueByUrl.values());
|
|
2451
2462
|
const importSelection = selectSignalPostsForImport(uniqueSelectedPosts, {
|
|
2452
|
-
targetEngagerCount,
|
|
2463
|
+
targetEngagerCount: effectiveTargetEngagerCount,
|
|
2453
2464
|
maxPostsToScrape,
|
|
2454
2465
|
});
|
|
2455
2466
|
const postsToScrape = importSelection.posts;
|
|
@@ -2485,8 +2496,7 @@ export async function importLeads(input) {
|
|
|
2485
2496
|
provider: "signal-discovery",
|
|
2486
2497
|
selectedPostCount: postsToScrape.length,
|
|
2487
2498
|
estimatedEngagers: result.estimatedEngagers,
|
|
2488
|
-
targetLeadCount:
|
|
2489
|
-
result.estimatedEngagers,
|
|
2499
|
+
targetLeadCount: effectiveTargetEngagerCount,
|
|
2490
2500
|
}),
|
|
2491
2501
|
}
|
|
2492
2502
|
: {}),
|
|
@@ -2498,7 +2508,7 @@ export async function importLeads(input) {
|
|
|
2498
2508
|
estimatedEngagers: result.estimatedEngagers,
|
|
2499
2509
|
selectedPostCount: postsToScrape.length,
|
|
2500
2510
|
availableSelectedPostCount: uniqueSelectedPosts.length,
|
|
2501
|
-
targetEngagerCount:
|
|
2511
|
+
targetEngagerCount: effectiveTargetEngagerCount,
|
|
2502
2512
|
maxPostsToScrape: normalizePositiveInteger(maxPostsToScrape) ?? null,
|
|
2503
2513
|
limitedSelectedPosts: importSelection.limited,
|
|
2504
2514
|
targetLeadCount: cappedTargetLeadCount ?? null,
|
|
@@ -2540,7 +2550,7 @@ export async function importLeads(input) {
|
|
|
2540
2550
|
searchId,
|
|
2541
2551
|
workflowTableId: leadListId,
|
|
2542
2552
|
campaignOfferId,
|
|
2543
|
-
targetLeadCount: cappedTargetLeadCount ??
|
|
2553
|
+
targetLeadCount: cappedTargetLeadCount ?? defaultProviderSourceListTarget,
|
|
2544
2554
|
...(normalizedMode ? { mode: normalizedMode } : {}),
|
|
2545
2555
|
});
|
|
2546
2556
|
}
|
|
@@ -2548,7 +2558,7 @@ export async function importLeads(input) {
|
|
|
2548
2558
|
return api.post(`/api/v3/lead-lists/${leadListId}/prospeo-import/start`, {
|
|
2549
2559
|
searchId,
|
|
2550
2560
|
campaignOfferId,
|
|
2551
|
-
targetLeadCount: cappedTargetLeadCount,
|
|
2561
|
+
targetLeadCount: cappedTargetLeadCount ?? defaultProviderSourceListTarget,
|
|
2552
2562
|
...(normalizedMode ? { mode: normalizedMode } : {}),
|
|
2553
2563
|
});
|
|
2554
2564
|
}
|
|
@@ -2664,7 +2674,7 @@ export async function cancelLeadImport(input) {
|
|
|
2664
2674
|
}
|
|
2665
2675
|
export async function confirmLeadList(input) {
|
|
2666
2676
|
const api = getApi();
|
|
2667
|
-
const { campaignOfferId, currentStep, confirmed, sourceLeadListId, campaignName, keepInSync, jobId, targetLeadCount, } = input;
|
|
2677
|
+
const { campaignOfferId, currentStep, confirmed, sourceLeadListId, campaignName, keepInSync, jobId, reviewBatchLimit, targetLeadCount, } = input;
|
|
2668
2678
|
assertInteractionApproval({
|
|
2669
2679
|
campaignId: campaignOfferId,
|
|
2670
2680
|
action: "confirm-lead-list",
|
|
@@ -2736,7 +2746,7 @@ export async function confirmLeadList(input) {
|
|
|
2736
2746
|
leadListConfig?.importStatus === "complete" &&
|
|
2737
2747
|
leadListRowCount > 0 &&
|
|
2738
2748
|
leadListRowCount < signalSourceTargetLeadCount) {
|
|
2739
|
-
throw new Error(`Signal Discovery source list is under capacity: it completed with ${leadListRowCount.toLocaleString("en-US")} source candidates, below the approved ${signalSourceTargetLeadCount.toLocaleString("en-US")} source-candidate target. Do not
|
|
2749
|
+
throw new Error(`Signal Discovery source list is under capacity: it completed with ${leadListRowCount.toLocaleString("en-US")} source candidates, below the approved ${signalSourceTargetLeadCount.toLocaleString("en-US")} source-candidate target. Do not confirm this source list. Select more posts, rerun Signal Discovery, or move to Sales Nav/Prospeo.`);
|
|
2740
2750
|
}
|
|
2741
2751
|
const progressProcessed = typeof importProgress?.processed === "number"
|
|
2742
2752
|
? importProgress.processed
|
|
@@ -2770,12 +2780,17 @@ export async function confirmLeadList(input) {
|
|
|
2770
2780
|
};
|
|
2771
2781
|
}
|
|
2772
2782
|
else {
|
|
2783
|
+
const readinessTargetLeadCount = typeof leadListConfig?.targetLeadCount === "number" &&
|
|
2784
|
+
Number.isFinite(leadListConfig.targetLeadCount) &&
|
|
2785
|
+
leadListConfig.targetLeadCount > 0
|
|
2786
|
+
? leadListConfig.targetLeadCount
|
|
2787
|
+
: undefined;
|
|
2773
2788
|
readiness = await waitForLeadListReady({
|
|
2774
2789
|
leadListId: resolvedLeadListId,
|
|
2775
2790
|
campaignOfferId,
|
|
2776
2791
|
provider: resolvedProvider,
|
|
2777
2792
|
jobId,
|
|
2778
|
-
targetLeadCount,
|
|
2793
|
+
targetLeadCount: readinessTargetLeadCount,
|
|
2779
2794
|
timeoutMs: 5000,
|
|
2780
2795
|
intervalMs: 1000,
|
|
2781
2796
|
});
|
|
@@ -2814,7 +2829,6 @@ export async function confirmLeadList(input) {
|
|
|
2814
2829
|
campaignOfferId,
|
|
2815
2830
|
campaignName,
|
|
2816
2831
|
keepInSync,
|
|
2817
|
-
...(typeof targetLeadCount === "number" ? { targetLeadCount } : {}),
|
|
2818
2832
|
currentStep: null,
|
|
2819
2833
|
})
|
|
2820
2834
|
.catch((error) => {
|
|
@@ -2824,15 +2838,13 @@ export async function confirmLeadList(input) {
|
|
|
2824
2838
|
throw error;
|
|
2825
2839
|
});
|
|
2826
2840
|
const campaignTableId = importResult.workflowTableId ?? importResult.campaignTableId;
|
|
2827
|
-
const
|
|
2828
|
-
|
|
2829
|
-
|
|
2841
|
+
const defaults = loadCampaignSourceDefaults();
|
|
2842
|
+
const requestedReviewBatchLimit = normalizePositiveInteger(reviewBatchLimit) ??
|
|
2843
|
+
normalizePositiveInteger(targetLeadCount) ??
|
|
2844
|
+
defaults.reviewBatch.defaultSize;
|
|
2830
2845
|
const importedRowIds = Array.isArray(importResult.rowIds)
|
|
2831
2846
|
? importResult.rowIds.filter((rowId) => typeof rowId === "string" && rowId.trim().length > 0)
|
|
2832
2847
|
: [];
|
|
2833
|
-
const overflowRowIds = campaignTableId && requestedTargetLeadCount !== null
|
|
2834
|
-
? importedRowIds.slice(requestedTargetLeadCount)
|
|
2835
|
-
: [];
|
|
2836
2848
|
const importedRowCount = importedRowIds.length > 0
|
|
2837
2849
|
? importedRowIds.length
|
|
2838
2850
|
: typeof importResult.rowCount === "number"
|
|
@@ -2843,21 +2855,22 @@ export async function confirmLeadList(input) {
|
|
|
2843
2855
|
: typeof importResult.leadsImported === "number"
|
|
2844
2856
|
? importResult.leadsImported
|
|
2845
2857
|
: 0;
|
|
2846
|
-
const keptReviewRowCount =
|
|
2847
|
-
? Math.min(importedRowCount, requestedTargetLeadCount)
|
|
2848
|
-
: importedRowCount;
|
|
2858
|
+
const keptReviewRowCount = Math.min(importedRowCount, requestedReviewBatchLimit);
|
|
2849
2859
|
const remainingRowCount = typeof importResult.remainingRowCount === "number"
|
|
2850
2860
|
? importResult.remainingRowCount
|
|
2851
2861
|
: 0;
|
|
2852
2862
|
const importStatus = String(importResult.importStatus ?? "").toLowerCase();
|
|
2863
|
+
const copyStillRunning = importResult.async === true &&
|
|
2864
|
+
(importResult.hybrid === true || remainingRowCount > 0);
|
|
2853
2865
|
const campaignTableReady = importResult.campaignTableReady === true ||
|
|
2866
|
+
(importResult.hybrid === true && importedRowCount > 0) ||
|
|
2854
2867
|
(!importResult.async &&
|
|
2855
2868
|
importStatus !== "pending" &&
|
|
2856
2869
|
importStatus !== "partial" &&
|
|
2857
2870
|
remainingRowCount <= 0 &&
|
|
2858
2871
|
importedRowCount > 0);
|
|
2859
2872
|
if (!campaignTableReady) {
|
|
2860
|
-
throw new Error("Campaign
|
|
2873
|
+
throw new Error("Campaign source rows are still copying and no review sample rows are available yet. Stay on lead import until the campaign table has rows, then retry confirm_lead_list or call wait_for_campaign_table_ready.");
|
|
2861
2874
|
}
|
|
2862
2875
|
if (importedRowCount <= 0) {
|
|
2863
2876
|
const recoveryNarration = buildSourceImportRecoveryWatchNarration({
|
|
@@ -2866,17 +2879,10 @@ export async function confirmLeadList(input) {
|
|
|
2866
2879
|
});
|
|
2867
2880
|
throw new Error(`${recoveryNarration.headline}: No usable review rows were kept for the campaign table. Retry the import or change the approved source before continuing. ${recoveryNarration.safety}`);
|
|
2868
2881
|
}
|
|
2869
|
-
if (campaignTableId && overflowRowIds.length > 0) {
|
|
2870
|
-
const deleteBatchSize = 25;
|
|
2871
|
-
for (let index = 0; index < overflowRowIds.length; index += deleteBatchSize) {
|
|
2872
|
-
const batch = overflowRowIds.slice(index, index + deleteBatchSize);
|
|
2873
|
-
await Promise.all(batch.map((rowId) => api.delete(`/api/v3/workflow-tables/${campaignTableId}/rows/${rowId}`)));
|
|
2874
|
-
}
|
|
2875
|
-
}
|
|
2876
2882
|
let reviewSampleRows = [];
|
|
2877
2883
|
if (campaignTableId && effectiveCurrentStep === "filter-choice") {
|
|
2878
2884
|
try {
|
|
2879
|
-
const sampleLimit = Math.min(Math.max(keptReviewRowCount, 1),
|
|
2885
|
+
const sampleLimit = Math.min(Math.max(keptReviewRowCount, 1), requestedReviewBatchLimit);
|
|
2880
2886
|
const sample = await getTableRowsMinimal(campaignTableId, {
|
|
2881
2887
|
limit: sampleLimit,
|
|
2882
2888
|
page: 1,
|
|
@@ -2924,31 +2930,37 @@ export async function confirmLeadList(input) {
|
|
|
2924
2930
|
workflowTableId: campaignTableId ?? null,
|
|
2925
2931
|
reviewBatchRowIds: importedRowIds.slice(0, keptReviewRowCount),
|
|
2926
2932
|
reviewBatchRowCount: keptReviewRowCount,
|
|
2933
|
+
copiedCampaignRowCount: importedRowCount,
|
|
2927
2934
|
},
|
|
2928
2935
|
branchBasisFields: [
|
|
2929
2936
|
"campaign revision or updatedAt",
|
|
2930
2937
|
"brief hash",
|
|
2931
2938
|
"selectedLeadListId",
|
|
2932
2939
|
"workflowTableId",
|
|
2933
|
-
"
|
|
2940
|
+
"first review/process sample row ids/hash",
|
|
2934
2941
|
"filter choice at branch start",
|
|
2935
2942
|
],
|
|
2936
2943
|
promptRequired: 'Load get_subskill_prompt({ subskillName: "generate-messages", offset, limit }) until hasMore=false before drafting.',
|
|
2937
2944
|
},
|
|
2938
|
-
boundedReviewBatch:
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2945
|
+
boundedReviewBatch: {
|
|
2946
|
+
requestedTargetLeadCount: requestedReviewBatchLimit,
|
|
2947
|
+
requestedReviewBatchLimit,
|
|
2948
|
+
importedRowCount,
|
|
2949
|
+
keptRowCount: keptReviewRowCount,
|
|
2950
|
+
reviewRowCount: keptReviewRowCount,
|
|
2951
|
+
copiedCampaignRowCount: importedRowCount,
|
|
2952
|
+
sourceCandidateRowCount: importResult.sourceRowCount ??
|
|
2953
|
+
importResult.clonedSourceRowCount ??
|
|
2954
|
+
leadListRowCount,
|
|
2955
|
+
remainingCopyRowCount: remainingRowCount,
|
|
2956
|
+
copyStillRunning,
|
|
2957
|
+
trimmedOverflowRowCount: 0,
|
|
2958
|
+
},
|
|
2959
|
+
message: copyStillRunning
|
|
2960
|
+
? `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.`
|
|
2961
|
+
: importedRowCount > keptReviewRowCount
|
|
2962
|
+
? `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.`
|
|
2963
|
+
: `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.`,
|
|
2952
2964
|
};
|
|
2953
2965
|
}
|
|
2954
2966
|
export function getProviderPrompt(input) {
|
|
@@ -3058,7 +3070,7 @@ Use Signal Discovery first.
|
|
|
3058
3070
|
|
|
3059
3071
|
**Recommendation:** approve scraping the ${selectionResult.selectedCount} selected Signal Discovery post${selectionResult.selectedCount === 1 ? "" : "s"}.
|
|
3060
3072
|
|
|
3061
|
-
**First pass:** build the source list, then
|
|
3073
|
+
**First pass:** build the source list, copy it into the campaign, then process only the first ${reviewBatchSize.toLocaleString("en-US")} leads so we can inspect quality before scaling.
|
|
3062
3074
|
|
|
3063
3075
|
Approval card should say:
|
|
3064
3076
|
|
|
@@ -3071,7 +3083,7 @@ Approval card should say:
|
|
|
3071
3083
|
criteriaCount: selectionResult.criteriaCount,
|
|
3072
3084
|
message: `${sourceRecommendation}
|
|
3073
3085
|
|
|
3074
|
-
Selected ${selectionResult.selectedCount} posts with ${selectionResult.criteriaCount} ICP criteria persisted. Ask the user to approve
|
|
3086
|
+
Selected ${selectionResult.selectedCount} posts with ${selectionResult.criteriaCount} ICP criteria persisted. Ask the user to approve this specific scraping action once; after approval, call import_leads immediately and do not repeat this source card.`,
|
|
3075
3087
|
};
|
|
3076
3088
|
}
|
|
3077
3089
|
export async function setHeadlineICPCriteria(input) {
|
package/dist/tools/prompts.js
CHANGED
|
@@ -260,7 +260,7 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
260
260
|
"campaignBrief",
|
|
261
261
|
"source decision and selectedLeadList/source state",
|
|
262
262
|
"workflowTableId",
|
|
263
|
-
"
|
|
263
|
+
"first review/process sample rows from selectedLeadList with row ids/hash",
|
|
264
264
|
"filter choice and filter/rubric basis when present",
|
|
265
265
|
],
|
|
266
266
|
scouts: scouts.map((agent) => ({
|
|
@@ -297,11 +297,11 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
297
297
|
nextStage: "message-review",
|
|
298
298
|
},
|
|
299
299
|
messageDraftBranchContract: {
|
|
300
|
-
firstAllowedStart: "after confirm_lead_list
|
|
300
|
+
firstAllowedStart: "after confirm_lead_list copies source rows and the first review/process sample exists",
|
|
301
301
|
forbiddenStarts: [
|
|
302
302
|
"source recommendation",
|
|
303
303
|
"provider import job alone",
|
|
304
|
-
"zero-row review
|
|
304
|
+
"zero-row review/process sample",
|
|
305
305
|
],
|
|
306
306
|
runtimeProofTransport: "CampaignOffer.watchNarration.workerDetails.messageDraftBuilder",
|
|
307
307
|
runtimeProofRequiredFields: [
|
|
@@ -313,7 +313,7 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
313
313
|
"basisToken",
|
|
314
314
|
"basis.selectedLeadListId",
|
|
315
315
|
"basis.workflowTableId",
|
|
316
|
-
"basis.
|
|
316
|
+
"basis.reviewSampleRowHash or basis.reviewSampleRowIds",
|
|
317
317
|
],
|
|
318
318
|
promptRequired: 'get_subskill_prompt({ subskillName: "generate-messages", offset, limit }) until hasMore=false',
|
|
319
319
|
basisFields: [
|
|
@@ -321,7 +321,7 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
321
321
|
"brief hash",
|
|
322
322
|
"selectedLeadListId",
|
|
323
323
|
"workflowTableId",
|
|
324
|
-
"
|
|
324
|
+
"first review/process sample row ids/hash",
|
|
325
325
|
"filter choice",
|
|
326
326
|
"filter/rubric basis when present",
|
|
327
327
|
],
|
|
@@ -337,12 +337,12 @@ export function getPostFindLeadsScoutRegistry() {
|
|
|
337
337
|
"outputHash",
|
|
338
338
|
"error or retry detail",
|
|
339
339
|
],
|
|
340
|
-
reusePolicy: "The first completed Message Draft Builder recommendation remains the default review candidate. Later Lead Fit Builder, Filter Leads, enrichment, or rubric completion may make an enriched rewrite available, but does not automatically retry or replace the initial draft unless campaign/brief/source/list/table/review-
|
|
340
|
+
reusePolicy: "The first completed Message Draft Builder recommendation remains the default review candidate. Later Lead Fit Builder, Filter Leads, enrichment, or rubric completion may make an enriched rewrite available, but does not automatically retry or replace the initial draft unless campaign/brief/source/list/table/review-sample identity mismatches or the initial output failed.",
|
|
341
341
|
},
|
|
342
342
|
usage: {
|
|
343
|
-
codex: "After confirm_lead_list
|
|
344
|
-
claude: "After confirm_lead_list
|
|
345
|
-
parentThreadRule: 'Named agents are optional acceleration, but message drafting is not optional. If post-find-leads-message-scout is available, run it as the background Message Draft Builder after the filter-choice answer. If it is absent, do not customer-surface install status; the main thread must execute the same message branch from CampaignOffer state, selected source state, workflowTableId, and
|
|
343
|
+
codex: "After confirm_lead_list copies source rows and the first review/process sample exists, ask the filter-choice question immediately. Do not spawn returned post-lead scout names before that question. Once the user answers, spawn Message Draft Builder from the same campaign/table basis. If the user chooses filters, also spawn Lead Fit Builder, move to Filter Rules, save rubrics, ask for filter approval, then keep the browser on Filter Leads while the message recommendation is reviewed. If filters are skipped, move to Messages/message review.",
|
|
344
|
+
claude: "After confirm_lead_list copies source rows and the first review/process sample exists, ask the filter-choice question immediately. Do not invoke returned post-lead Task/Agent names before that question. Once the user answers, invoke Message Draft Builder from the same campaign/table basis. If the user chooses filters, also invoke Lead Fit Builder, move to Filter Rules, save rubrics, ask for filter approval, then keep the browser on Filter Leads while the message recommendation is reviewed. If filters are skipped, move to Messages/message review.",
|
|
345
|
+
parentThreadRule: 'Named agents are optional acceleration, but message drafting is not optional. If post-find-leads-message-scout is available, run it as the background Message Draft Builder after the filter-choice answer. If it is absent, do not customer-surface install status; the main thread must execute the same message branch from CampaignOffer state, selected source state, workflowTableId, and first review/process sample rows. Local markdown/json files are not normal-path inputs. The filter-choice question is the first post-import user gate; do not load post-lead registries, filter references, or the full generate-messages prompt before it. Message drafting starts after the filter-choice answer, must load get_subskill_prompt({ subskillName: "generate-messages", offset, limit }) until hasMore=false, must read live campaign/review-sample state through scoped MCP/product tools, and must reject mismatched selectedLeadListId/workflowTableId/campaign/workspace input. On the filter path, keep the browser on Filter Rules after save_rubrics so the user can approve the saved criteria; only then move to Filter Leads and wait there for message approval. Enrichment, filtering, Generate Message cells, sender setup, sequence attach, and launch wait for template approval on the Use Template path. On the skip path, move to Messages/message review and wait for message approval before enrichment or Settings. Do not render message review from checklist or shortcut instructions; message review requires a messageDraftRecommendation whose basis proves the full generate-messages prompt ran for the current campaign/table/review sample. Do not automatically rerun Message Draft Builder after filters/enrichment finish; show the initial draft by default and offer an enriched rewrite only with explicit user opt-in.',
|
|
346
346
|
},
|
|
347
347
|
};
|
|
348
348
|
}
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -1391,6 +1391,7 @@ export declare const allTools: ({
|
|
|
1391
1391
|
campaignName?: undefined;
|
|
1392
1392
|
keepInSync?: undefined;
|
|
1393
1393
|
jobId?: undefined;
|
|
1394
|
+
reviewBatchLimit?: undefined;
|
|
1394
1395
|
selections?: undefined;
|
|
1395
1396
|
selectionMode?: undefined;
|
|
1396
1397
|
};
|
|
@@ -1566,6 +1567,7 @@ export declare const allTools: ({
|
|
|
1566
1567
|
campaignName?: undefined;
|
|
1567
1568
|
keepInSync?: undefined;
|
|
1568
1569
|
jobId?: undefined;
|
|
1570
|
+
reviewBatchLimit?: undefined;
|
|
1569
1571
|
selections?: undefined;
|
|
1570
1572
|
selectionMode?: undefined;
|
|
1571
1573
|
};
|
|
@@ -1640,6 +1642,7 @@ export declare const allTools: ({
|
|
|
1640
1642
|
campaignName?: undefined;
|
|
1641
1643
|
keepInSync?: undefined;
|
|
1642
1644
|
jobId?: undefined;
|
|
1645
|
+
reviewBatchLimit?: undefined;
|
|
1643
1646
|
selections?: undefined;
|
|
1644
1647
|
selectionMode?: undefined;
|
|
1645
1648
|
};
|
|
@@ -1786,6 +1789,7 @@ export declare const allTools: ({
|
|
|
1786
1789
|
campaignName?: undefined;
|
|
1787
1790
|
keepInSync?: undefined;
|
|
1788
1791
|
jobId?: undefined;
|
|
1792
|
+
reviewBatchLimit?: undefined;
|
|
1789
1793
|
selections?: undefined;
|
|
1790
1794
|
selectionMode?: undefined;
|
|
1791
1795
|
};
|
|
@@ -1874,6 +1878,7 @@ export declare const allTools: ({
|
|
|
1874
1878
|
campaignName?: undefined;
|
|
1875
1879
|
keepInSync?: undefined;
|
|
1876
1880
|
jobId?: undefined;
|
|
1881
|
+
reviewBatchLimit?: undefined;
|
|
1877
1882
|
selections?: undefined;
|
|
1878
1883
|
selectionMode?: undefined;
|
|
1879
1884
|
};
|
|
@@ -1971,6 +1976,7 @@ export declare const allTools: ({
|
|
|
1971
1976
|
campaignName?: undefined;
|
|
1972
1977
|
keepInSync?: undefined;
|
|
1973
1978
|
jobId?: undefined;
|
|
1979
|
+
reviewBatchLimit?: undefined;
|
|
1974
1980
|
selections?: undefined;
|
|
1975
1981
|
selectionMode?: undefined;
|
|
1976
1982
|
};
|
|
@@ -2050,6 +2056,7 @@ export declare const allTools: ({
|
|
|
2050
2056
|
campaignName?: undefined;
|
|
2051
2057
|
keepInSync?: undefined;
|
|
2052
2058
|
jobId?: undefined;
|
|
2059
|
+
reviewBatchLimit?: undefined;
|
|
2053
2060
|
selections?: undefined;
|
|
2054
2061
|
selectionMode?: undefined;
|
|
2055
2062
|
};
|
|
@@ -2668,6 +2675,7 @@ export declare const allTools: ({
|
|
|
2668
2675
|
campaignName?: undefined;
|
|
2669
2676
|
keepInSync?: undefined;
|
|
2670
2677
|
jobId?: undefined;
|
|
2678
|
+
reviewBatchLimit?: undefined;
|
|
2671
2679
|
selections?: undefined;
|
|
2672
2680
|
selectionMode?: undefined;
|
|
2673
2681
|
};
|
|
@@ -2799,6 +2807,7 @@ export declare const allTools: ({
|
|
|
2799
2807
|
campaignName?: undefined;
|
|
2800
2808
|
keepInSync?: undefined;
|
|
2801
2809
|
jobId?: undefined;
|
|
2810
|
+
reviewBatchLimit?: undefined;
|
|
2802
2811
|
selections?: undefined;
|
|
2803
2812
|
selectionMode?: undefined;
|
|
2804
2813
|
};
|
|
@@ -2918,6 +2927,7 @@ export declare const allTools: ({
|
|
|
2918
2927
|
campaignName?: undefined;
|
|
2919
2928
|
keepInSync?: undefined;
|
|
2920
2929
|
jobId?: undefined;
|
|
2930
|
+
reviewBatchLimit?: undefined;
|
|
2921
2931
|
selections?: undefined;
|
|
2922
2932
|
selectionMode?: undefined;
|
|
2923
2933
|
};
|
|
@@ -2995,6 +3005,7 @@ export declare const allTools: ({
|
|
|
2995
3005
|
campaignName?: undefined;
|
|
2996
3006
|
keepInSync?: undefined;
|
|
2997
3007
|
jobId?: undefined;
|
|
3008
|
+
reviewBatchLimit?: undefined;
|
|
2998
3009
|
selections?: undefined;
|
|
2999
3010
|
selectionMode?: undefined;
|
|
3000
3011
|
};
|
|
@@ -3034,6 +3045,10 @@ export declare const allTools: ({
|
|
|
3034
3045
|
type: string;
|
|
3035
3046
|
description: string;
|
|
3036
3047
|
};
|
|
3048
|
+
reviewBatchLimit: {
|
|
3049
|
+
type: string;
|
|
3050
|
+
description: string;
|
|
3051
|
+
};
|
|
3037
3052
|
confirmed: {
|
|
3038
3053
|
type: string;
|
|
3039
3054
|
description: string;
|
|
@@ -3192,6 +3207,7 @@ export declare const allTools: ({
|
|
|
3192
3207
|
campaignName?: undefined;
|
|
3193
3208
|
keepInSync?: undefined;
|
|
3194
3209
|
jobId?: undefined;
|
|
3210
|
+
reviewBatchLimit?: undefined;
|
|
3195
3211
|
};
|
|
3196
3212
|
required: string[];
|
|
3197
3213
|
};
|
|
@@ -3271,6 +3287,7 @@ export declare const allTools: ({
|
|
|
3271
3287
|
campaignName?: undefined;
|
|
3272
3288
|
keepInSync?: undefined;
|
|
3273
3289
|
jobId?: undefined;
|
|
3290
|
+
reviewBatchLimit?: undefined;
|
|
3274
3291
|
selections?: undefined;
|
|
3275
3292
|
selectionMode?: undefined;
|
|
3276
3293
|
};
|