@sellable/mcp 0.1.152 → 0.1.154
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 +0 -0
- package/dist/index.js +0 -0
- package/dist/tools/leads.d.ts +1 -0
- package/dist/tools/leads.js +16 -11
- package/dist/tools/readiness.d.ts +7 -1
- package/dist/tools/readiness.js +10 -17
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +28 -2
- package/skills/create-campaign-v2/SKILL.md +16 -3
- package/skills/create-campaign-v2/core/flow.v2.json +77 -12
- package/skills/create-campaign-v2/references/step-13-import-leads.md +17 -7
- package/skills/providers/signal-discovery.md +36 -12
- package/skills/research/config.json +0 -9
package/dist/index-dev.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/tools/leads.d.ts
CHANGED
|
@@ -2638,6 +2638,7 @@ export declare function confirmLeadList(input: ConfirmLeadListInput): Promise<{
|
|
|
2638
2638
|
remainingCopyRowCount: number;
|
|
2639
2639
|
copyStillRunning: boolean;
|
|
2640
2640
|
trimmedOverflowRowCount: number;
|
|
2641
|
+
sourceShortfall: boolean;
|
|
2641
2642
|
};
|
|
2642
2643
|
message: string;
|
|
2643
2644
|
}>;
|
package/dist/tools/leads.js
CHANGED
|
@@ -53,6 +53,11 @@ const defaultCampaignSourceDefaults = {
|
|
|
53
53
|
},
|
|
54
54
|
};
|
|
55
55
|
const defaultProviderSourceListTarget = 1000;
|
|
56
|
+
// Mirror the web app's Signal Discovery Harvest caps. The MCP tool selects and
|
|
57
|
+
// describes the scrape before the web API runs, so it must use the same capped
|
|
58
|
+
// capacity math instead of raw visible engagement counts.
|
|
59
|
+
const signalDiscoveryReactionFetchLimit = 1000;
|
|
60
|
+
const signalDiscoveryCommentFetchLimit = 1000;
|
|
56
61
|
const prospeoFilterValueSchema = {
|
|
57
62
|
type: "object",
|
|
58
63
|
description: "Include/exclude list filter (values must match Prospeo enums)",
|
|
@@ -341,11 +346,15 @@ function normalizePositiveInteger(value) {
|
|
|
341
346
|
}
|
|
342
347
|
return Math.floor(numeric);
|
|
343
348
|
}
|
|
349
|
+
function estimateScrapableSignalEngagers(post) {
|
|
350
|
+
return (Math.min(post.likes, signalDiscoveryReactionFetchLimit) +
|
|
351
|
+
Math.min(post.comments, signalDiscoveryCommentFetchLimit));
|
|
352
|
+
}
|
|
344
353
|
export function selectSignalPostsForImport(posts, options) {
|
|
345
354
|
const normalizedTargetEngagers = normalizePositiveInteger(options.targetEngagerCount);
|
|
346
355
|
const normalizedMaxPosts = normalizePositiveInteger(options.maxPostsToScrape);
|
|
347
356
|
if (!normalizedTargetEngagers && !normalizedMaxPosts) {
|
|
348
|
-
const availableEngagers = posts.reduce((sum, post) => sum + post
|
|
357
|
+
const availableEngagers = posts.reduce((sum, post) => sum + estimateScrapableSignalEngagers(post), 0);
|
|
349
358
|
return {
|
|
350
359
|
posts,
|
|
351
360
|
estimatedEngagers: availableEngagers,
|
|
@@ -355,8 +364,8 @@ export function selectSignalPostsForImport(posts, options) {
|
|
|
355
364
|
limited: false,
|
|
356
365
|
};
|
|
357
366
|
}
|
|
358
|
-
const ranked = [...posts].sort((a, b) => b
|
|
359
|
-
const availableEngagers = ranked.reduce((sum, post) => sum + post
|
|
367
|
+
const ranked = [...posts].sort((a, b) => estimateScrapableSignalEngagers(b) - estimateScrapableSignalEngagers(a));
|
|
368
|
+
const availableEngagers = ranked.reduce((sum, post) => sum + estimateScrapableSignalEngagers(post), 0);
|
|
360
369
|
const selected = [];
|
|
361
370
|
let estimatedEngagers = 0;
|
|
362
371
|
for (const post of ranked) {
|
|
@@ -364,7 +373,7 @@ export function selectSignalPostsForImport(posts, options) {
|
|
|
364
373
|
break;
|
|
365
374
|
}
|
|
366
375
|
selected.push(post);
|
|
367
|
-
estimatedEngagers += post
|
|
376
|
+
estimatedEngagers += estimateScrapableSignalEngagers(post);
|
|
368
377
|
if (normalizedTargetEngagers &&
|
|
369
378
|
estimatedEngagers >= normalizedTargetEngagers) {
|
|
370
379
|
break;
|
|
@@ -2741,13 +2750,6 @@ export async function confirmLeadList(input) {
|
|
|
2741
2750
|
leadListConfig.targetLeadCount > 0
|
|
2742
2751
|
? leadListConfig.targetLeadCount
|
|
2743
2752
|
: null;
|
|
2744
|
-
if (resolvedProvider === "signal-discovery" &&
|
|
2745
|
-
signalSourceTargetLeadCount !== null &&
|
|
2746
|
-
leadListConfig?.importStatus === "complete" &&
|
|
2747
|
-
leadListRowCount > 0 &&
|
|
2748
|
-
leadListRowCount < signalSourceTargetLeadCount) {
|
|
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.`);
|
|
2750
|
-
}
|
|
2751
2753
|
const progressProcessed = typeof importProgress?.processed === "number"
|
|
2752
2754
|
? importProgress.processed
|
|
2753
2755
|
: null;
|
|
@@ -2955,6 +2957,9 @@ export async function confirmLeadList(input) {
|
|
|
2955
2957
|
remainingCopyRowCount: remainingRowCount,
|
|
2956
2958
|
copyStillRunning,
|
|
2957
2959
|
trimmedOverflowRowCount: 0,
|
|
2960
|
+
sourceShortfall: signalSourceTargetLeadCount !== null &&
|
|
2961
|
+
leadListRowCount > 0 &&
|
|
2962
|
+
leadListRowCount < signalSourceTargetLeadCount,
|
|
2958
2963
|
},
|
|
2959
2964
|
message: copyStillRunning
|
|
2960
2965
|
? `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.`
|
|
@@ -273,6 +273,7 @@ export declare function waitForLeadListReady(input: WaitForLeadListReadyInput):
|
|
|
273
273
|
status?: undefined;
|
|
274
274
|
targetLeadCount?: undefined;
|
|
275
275
|
error?: undefined;
|
|
276
|
+
sourceShortfall?: undefined;
|
|
276
277
|
warning?: undefined;
|
|
277
278
|
} | {
|
|
278
279
|
ready: boolean;
|
|
@@ -285,6 +286,7 @@ export declare function waitForLeadListReady(input: WaitForLeadListReadyInput):
|
|
|
285
286
|
status: string;
|
|
286
287
|
targetLeadCount: number | undefined;
|
|
287
288
|
error?: undefined;
|
|
289
|
+
sourceShortfall?: undefined;
|
|
288
290
|
warning?: undefined;
|
|
289
291
|
} | {
|
|
290
292
|
ready: boolean;
|
|
@@ -297,6 +299,7 @@ export declare function waitForLeadListReady(input: WaitForLeadListReadyInput):
|
|
|
297
299
|
status: string | null;
|
|
298
300
|
targetLeadCount: number | undefined;
|
|
299
301
|
error: string;
|
|
302
|
+
sourceShortfall?: undefined;
|
|
300
303
|
warning?: undefined;
|
|
301
304
|
} | {
|
|
302
305
|
ready: boolean;
|
|
@@ -309,6 +312,7 @@ export declare function waitForLeadListReady(input: WaitForLeadListReadyInput):
|
|
|
309
312
|
status: string;
|
|
310
313
|
targetLeadCount: number | undefined;
|
|
311
314
|
error: string | null;
|
|
315
|
+
sourceShortfall?: undefined;
|
|
312
316
|
warning?: undefined;
|
|
313
317
|
} | {
|
|
314
318
|
ready: boolean;
|
|
@@ -319,9 +323,10 @@ export declare function waitForLeadListReady(input: WaitForLeadListReadyInput):
|
|
|
319
323
|
rowCount: number;
|
|
320
324
|
status: string | null;
|
|
321
325
|
targetLeadCount: number | null;
|
|
326
|
+
sourceShortfall: boolean;
|
|
327
|
+
warning: string | undefined;
|
|
322
328
|
reason?: undefined;
|
|
323
329
|
error?: undefined;
|
|
324
|
-
warning?: undefined;
|
|
325
330
|
} | {
|
|
326
331
|
ready: boolean;
|
|
327
332
|
reason: string;
|
|
@@ -334,5 +339,6 @@ export declare function waitForLeadListReady(input: WaitForLeadListReadyInput):
|
|
|
334
339
|
targetLeadCount: number | undefined;
|
|
335
340
|
warning: string | undefined;
|
|
336
341
|
error: string | undefined;
|
|
342
|
+
sourceShortfall?: undefined;
|
|
337
343
|
}>;
|
|
338
344
|
export {};
|
package/dist/tools/readiness.js
CHANGED
|
@@ -101,7 +101,7 @@ export const readinessToolDefinitions = [
|
|
|
101
101
|
},
|
|
102
102
|
targetLeadCount: {
|
|
103
103
|
type: "number",
|
|
104
|
-
description: "Target number of leads requested. Used as a fallback completion check when status is unavailable. For Signal Discovery, pass the approved source-candidate target; if the completed source list lands below it, the tool returns
|
|
104
|
+
description: "Target number of leads requested. Used as a fallback completion check when status is unavailable. For Signal Discovery, pass the approved source-candidate target; if the completed source list lands below it, the tool still returns ready with a source_shortfall warning so the confirmed list can be copied and the first review sample can proceed.",
|
|
105
105
|
},
|
|
106
106
|
timeoutMs: {
|
|
107
107
|
type: "number",
|
|
@@ -472,23 +472,12 @@ export async function waitForLeadListReady(input) {
|
|
|
472
472
|
}
|
|
473
473
|
}
|
|
474
474
|
if ((!requireRows || rowCount > 0) && importComplete) {
|
|
475
|
-
|
|
476
|
-
|
|
475
|
+
const signalSourceShortfallTarget = provider === "signal-discovery" && typeof targetLeadCount === "number"
|
|
476
|
+
? targetLeadCount
|
|
477
|
+
: null;
|
|
478
|
+
const signalSourceShortfall = signalSourceShortfallTarget !== null &&
|
|
477
479
|
rowCount > 0 &&
|
|
478
|
-
rowCount <
|
|
479
|
-
return {
|
|
480
|
-
ready: false,
|
|
481
|
-
reason: "source_under_capacity",
|
|
482
|
-
leadListId,
|
|
483
|
-
provider: provider ?? null,
|
|
484
|
-
attempts,
|
|
485
|
-
elapsedMs: Date.now() - start,
|
|
486
|
-
rowCount,
|
|
487
|
-
status: lastStatus,
|
|
488
|
-
targetLeadCount,
|
|
489
|
-
error: `Signal Discovery completed with ${rowCount.toLocaleString("en-US")} source candidates, below the approved ${targetLeadCount.toLocaleString("en-US")} source-candidate target. Do not confirm this lead list; select more posts, rerun source discovery, or move to Sales Nav/Prospeo.`,
|
|
490
|
-
};
|
|
491
|
-
}
|
|
480
|
+
rowCount < signalSourceShortfallTarget;
|
|
492
481
|
return {
|
|
493
482
|
ready: true,
|
|
494
483
|
leadListId,
|
|
@@ -498,6 +487,10 @@ export async function waitForLeadListReady(input) {
|
|
|
498
487
|
rowCount,
|
|
499
488
|
status: lastStatus,
|
|
500
489
|
targetLeadCount: targetLeadCount ?? null,
|
|
490
|
+
sourceShortfall: signalSourceShortfall,
|
|
491
|
+
warning: signalSourceShortfall
|
|
492
|
+
? `Signal Discovery completed with ${rowCount.toLocaleString("en-US")} source candidates, below the approved ${signalSourceShortfallTarget.toLocaleString("en-US")} source-candidate target. Confirm/copy the completed list for the first review sample, then add more approved posts or switch provider if the sample quality/scale is not enough.`
|
|
493
|
+
: undefined,
|
|
501
494
|
};
|
|
502
495
|
}
|
|
503
496
|
}
|
package/package.json
CHANGED
|
@@ -192,8 +192,8 @@ should be a compact `## Source Recommendation` block with:
|
|
|
192
192
|
- a selected-post table with post author/topic, why it fits, and visible
|
|
193
193
|
engagement
|
|
194
194
|
- total visible pool and estimated good-fit pool
|
|
195
|
-
- first pass: build the source list, copy
|
|
196
|
-
only the first 15
|
|
195
|
+
- first pass: build the source list, copy all confirmed source rows into the
|
|
196
|
+
campaign, then process only the first 15 rows before scaling
|
|
197
197
|
- fallback: switch to Sales Nav recent activity if sampled/projected fit falls
|
|
198
198
|
below 10%, or if the review batch is vendor-heavy, agency-heavy, or off-ICP
|
|
199
199
|
|
|
@@ -458,6 +458,32 @@ required field is missing, the supplied inputs conflict, or the campaign focus i
|
|
|
458
458
|
genuinely ambiguous. It is fine to include an explicit assumption line in the
|
|
459
459
|
brief; the approval gate lets the user revise it.
|
|
460
460
|
|
|
461
|
+
### YOLO Mode
|
|
462
|
+
|
|
463
|
+
If the invocation or any later user message explicitly asks for "yolo mode",
|
|
464
|
+
"YOLO", `--yolo`, `mode=yolo`, "autopilot", "use best guesses", "answer for
|
|
465
|
+
me", "use best estimates", or "just run it", enable YOLO mode for the rest of
|
|
466
|
+
the run. Treat YOLO as `interactionMode: "autonomous"` plus an intake policy:
|
|
467
|
+
|
|
468
|
+
- If campaign identity is missing, ask only for the LinkedIn profile or company
|
|
469
|
+
website in normal chat; do not ask buyer, offer, proof, source, or filter setup
|
|
470
|
+
questions before that.
|
|
471
|
+
- Treat any freeform directions already provided, or added later by the user, as
|
|
472
|
+
operator directions for the rest of the run. If directions conflict, the newest
|
|
473
|
+
user direction wins.
|
|
474
|
+
- After the lightweight identity/company lookup, infer the buyer segment,
|
|
475
|
+
offer/CTA, proof to use or avoid, first lead source, filter choice, and message
|
|
476
|
+
direction with best estimates from public/company context plus operator
|
|
477
|
+
directions. State the important assumptions in the brief and watch narration.
|
|
478
|
+
- Do not use structured setup questions in YOLO mode. For pre-launch approval
|
|
479
|
+
gates, choose the recommended path yourself when confidence is sufficient, show
|
|
480
|
+
the assumed choice briefly, and continue.
|
|
481
|
+
- Pause only when no reasonable estimate exists, a tool requires missing
|
|
482
|
+
credentials/data, the source/message quality floor fails, or the next action
|
|
483
|
+
would start the live campaign.
|
|
484
|
+
- Never call `start_campaign` from YOLO mode without explicit user launch
|
|
485
|
+
confirmation. Do not invent proof; mark proof gaps and use safer claims.
|
|
486
|
+
|
|
461
487
|
Before the identity gate, use this customer-facing shape:
|
|
462
488
|
|
|
463
489
|
```text
|
|
@@ -81,6 +81,18 @@ Use `research-sender` for concise identity/proof research and call
|
|
|
81
81
|
use the available Sellable profile/company/post tools and carry explicit proof
|
|
82
82
|
gaps into the brief.
|
|
83
83
|
|
|
84
|
+
## YOLO Mode
|
|
85
|
+
|
|
86
|
+
Enable YOLO mode when the user asks for yolo/autopilot, passes `--yolo` or
|
|
87
|
+
`mode=yolo`, or says to use best guesses/estimates and answer for them. Ask only
|
|
88
|
+
for the LinkedIn profile or company website if identity is missing. After the
|
|
89
|
+
identity lookup, infer buyer, offer/CTA, proof, source, filters, and message
|
|
90
|
+
direction from company evidence plus user directions; newest directions win.
|
|
91
|
+
Set `interactionMode: "autonomous"` once `campaignId` exists. Auto-select
|
|
92
|
+
pre-launch choices when confidence is sufficient, but show the assumed choice
|
|
93
|
+
briefly. Pause for missing credentials/data, failed quality floors, or final
|
|
94
|
+
launch. Never call `start_campaign` without explicit launch confirmation.
|
|
95
|
+
|
|
84
96
|
## Structured Questions
|
|
85
97
|
|
|
86
98
|
Use the host-native structured question gate (`request_user_input` or
|
|
@@ -209,7 +221,7 @@ Use Signal Discovery first.
|
|
|
209
221
|
**Working assumption:** ~20% of raw post engagers become good-fit prospects<br>
|
|
210
222
|
**Engagers needed:** ~1,500 raw engagers<br>
|
|
211
223
|
**Planning floor:** continue only when sampled/projected fit is at least 10% after cleanup; below that, switch to Sales Nav recent activity<br>
|
|
212
|
-
**Review checkpoint:** after the source list exists, copy the confirmed source list into the campaign and process only the first 15
|
|
224
|
+
**Review checkpoint:** after the source list exists, copy the entire confirmed source list into the campaign and process only the first 15 rows for fit and message review<br>
|
|
213
225
|
|
|
214
226
|
### Selected posts
|
|
215
227
|
|
|
@@ -230,8 +242,9 @@ This gives enough volume to target ~300 good-fit prospects after cleanup, while
|
|
|
230
242
|
keeping the source tied to people already engaging with Claude Code outbound /
|
|
231
243
|
AI-native sales workflows.
|
|
232
244
|
|
|
233
|
-
**First pass:** build the source list, copy
|
|
234
|
-
only the first 15
|
|
245
|
+
**First pass:** build the source list, copy all confirmed source rows into the
|
|
246
|
+
campaign, then process only the first 15 rows so we can inspect quality before
|
|
247
|
+
scaling.
|
|
235
248
|
|
|
236
249
|
**Fallback:** if sampled/projected fit falls below 10%, or if the review batch
|
|
237
250
|
is too vendor-heavy, agency-heavy, or off-ICP, switch to Sales Nav recent
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"campaignBrief",
|
|
23
23
|
"currentStep",
|
|
24
24
|
"watchNarration",
|
|
25
|
+
"interactionMode",
|
|
25
26
|
"providerSearchAssociation",
|
|
26
27
|
"selectedLeadListId",
|
|
27
28
|
"workflowTableId",
|
|
@@ -78,6 +79,40 @@
|
|
|
78
79
|
"Do not call start_campaign until the user explicitly confirms launch.",
|
|
79
80
|
"Do not use local files as durable state in normal customer runs."
|
|
80
81
|
],
|
|
82
|
+
"yoloMode": {
|
|
83
|
+
"triggerPhrases": [
|
|
84
|
+
"yolo",
|
|
85
|
+
"--yolo",
|
|
86
|
+
"mode=yolo",
|
|
87
|
+
"autopilot",
|
|
88
|
+
"use best guesses",
|
|
89
|
+
"use best estimates",
|
|
90
|
+
"answer for me",
|
|
91
|
+
"just run it"
|
|
92
|
+
],
|
|
93
|
+
"identityRequestOnlyWhenMissing": true,
|
|
94
|
+
"operatorDirectionsRule": "Treat freeform directions supplied at invocation or later as durable operator directions; newest conflicting direction wins.",
|
|
95
|
+
"setInteractionModeAfterCampaignCreate": "autonomous",
|
|
96
|
+
"autoSelectsPreLaunchChoices": [
|
|
97
|
+
"campaign focus",
|
|
98
|
+
"brief approval",
|
|
99
|
+
"source scouting plan",
|
|
100
|
+
"source review/import approval",
|
|
101
|
+
"filters vs skip filters",
|
|
102
|
+
"filter rubric approval",
|
|
103
|
+
"message template approval when recommendation is approve-message",
|
|
104
|
+
"generated message review when quality floor passes",
|
|
105
|
+
"sender selection when exactly one connected sender is safe"
|
|
106
|
+
],
|
|
107
|
+
"mustPauseFor": [
|
|
108
|
+
"missing campaign identity",
|
|
109
|
+
"missing credentials or required data",
|
|
110
|
+
"ambiguous choice with no reasonable estimate",
|
|
111
|
+
"source/message/filter quality floor failure",
|
|
112
|
+
"final live launch confirmation"
|
|
113
|
+
],
|
|
114
|
+
"neverAutoStart": true
|
|
115
|
+
},
|
|
81
116
|
"steps": [
|
|
82
117
|
{
|
|
83
118
|
"id": "bootstrap",
|
|
@@ -156,7 +191,23 @@
|
|
|
156
191
|
"uses": "request_user_input",
|
|
157
192
|
"singleChoice": true,
|
|
158
193
|
"requiredOption": "Other / custom",
|
|
159
|
-
"neverCollectOpenTextWithStructuredQuestion": true
|
|
194
|
+
"neverCollectOpenTextWithStructuredQuestion": true,
|
|
195
|
+
"skipIf": "yolo_mode with any reasonable best-estimate focus"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"action": "infer_strategy_packet_in_yolo_mode",
|
|
199
|
+
"when": "yolo_mode",
|
|
200
|
+
"skipStructuredSetupQuestions": true,
|
|
201
|
+
"inferFields": [
|
|
202
|
+
"buyer segment",
|
|
203
|
+
"offer/CTA",
|
|
204
|
+
"proof to use or avoid",
|
|
205
|
+
"lead source",
|
|
206
|
+
"filter choice",
|
|
207
|
+
"message direction"
|
|
208
|
+
],
|
|
209
|
+
"sourceOfTruth": "identity/company lookup plus operator directions",
|
|
210
|
+
"mustStateAssumptionsInBrief": true
|
|
160
211
|
},
|
|
161
212
|
{
|
|
162
213
|
"action": "create_watchable_campaign_shell_with_v1_brief",
|
|
@@ -231,7 +282,8 @@
|
|
|
231
282
|
{
|
|
232
283
|
"action": "ask_brief_choice",
|
|
233
284
|
"uses": "request_user_input",
|
|
234
|
-
"choices": ["Approve brief", "Revise brief", "Pause here"]
|
|
285
|
+
"choices": ["Approve brief", "Revise brief", "Pause here"],
|
|
286
|
+
"skipIf": "yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails"
|
|
235
287
|
}
|
|
236
288
|
],
|
|
237
289
|
"requiredCampaignState": [
|
|
@@ -283,7 +335,12 @@
|
|
|
283
335
|
"fallback lane if the first lane is weak",
|
|
284
336
|
"what approval authorizes"
|
|
285
337
|
],
|
|
286
|
-
"approvalAuthorizes": "source scouting/search only; no import/send"
|
|
338
|
+
"approvalAuthorizes": "source scouting/search only; no import/send",
|
|
339
|
+
"yoloMode": {
|
|
340
|
+
"autoApproveRecommendedLane": true,
|
|
341
|
+
"mustShowAssumedChoice": true,
|
|
342
|
+
"pauseIfConfidenceLow": true
|
|
343
|
+
}
|
|
287
344
|
},
|
|
288
345
|
"defaultWhenSourceUnspecified": [
|
|
289
346
|
"signal-discovery",
|
|
@@ -325,7 +382,7 @@
|
|
|
325
382
|
"action": "show_pre_scout_source_recommendation",
|
|
326
383
|
"uses": "request_user_input",
|
|
327
384
|
"oneShot": true,
|
|
328
|
-
"skipIf": "approved or leadSourceProvider",
|
|
385
|
+
"skipIf": "approved or leadSourceProvider or yolo_mode auto-selected source_lane_approved",
|
|
329
386
|
"requiredBeforeTools": [
|
|
330
387
|
"get_provider_prompt",
|
|
331
388
|
"search_signals",
|
|
@@ -470,7 +527,8 @@
|
|
|
470
527
|
],
|
|
471
528
|
"requiredNextActionAfterApproval": "ack once; call import_leads immediately",
|
|
472
529
|
"signalDiscoveryNextTool": "import_leads({ provider: \"signal-discovery\", targetEngagerCount, maxPostsToScrape, confirmed: true })"
|
|
473
|
-
}
|
|
530
|
+
},
|
|
531
|
+
"autoSelectIf": "yolo_mode and projected usable pool clears the source quality floor"
|
|
474
532
|
}
|
|
475
533
|
],
|
|
476
534
|
"requiredCampaignState": [
|
|
@@ -615,7 +673,8 @@
|
|
|
615
673
|
{
|
|
616
674
|
"action": "ask_filter_choice",
|
|
617
675
|
"uses": "request_user_input",
|
|
618
|
-
"choices": ["Use filters", "Skip filters", "Revise source"]
|
|
676
|
+
"choices": ["Use filters", "Skip filters", "Revise source"],
|
|
677
|
+
"autoSelectIf": "yolo_mode; choose filters unless source is tightly curated or user directed otherwise"
|
|
619
678
|
}
|
|
620
679
|
],
|
|
621
680
|
"hardRules": [
|
|
@@ -729,7 +788,8 @@
|
|
|
729
788
|
"action": "ask_filter_rubric_review_choice",
|
|
730
789
|
"uses": "request_user_input",
|
|
731
790
|
"choices": ["Approve filters", "Revise filters", "Pause"],
|
|
732
|
-
"purpose": "let the user read saved rubrics before Filter Leads or enrichment"
|
|
791
|
+
"purpose": "let the user read saved rubrics before Filter Leads or enrichment",
|
|
792
|
+
"autoSelectIf": "yolo_mode and rubrics are production-shaped"
|
|
733
793
|
}
|
|
734
794
|
],
|
|
735
795
|
"requiredCampaignState": [
|
|
@@ -809,7 +869,8 @@
|
|
|
809
869
|
{
|
|
810
870
|
"action": "ask_filter_rubric_review_choice",
|
|
811
871
|
"uses": "request_user_input",
|
|
812
|
-
"choices": ["Approve filters", "Revise filters", "Pause"]
|
|
872
|
+
"choices": ["Approve filters", "Revise filters", "Pause"],
|
|
873
|
+
"autoSelectIf": "yolo_mode and rubrics are production-shaped"
|
|
813
874
|
}
|
|
814
875
|
],
|
|
815
876
|
"requiredCampaignState": ["campaignId", "workflowTableId"],
|
|
@@ -947,7 +1008,8 @@
|
|
|
947
1008
|
{
|
|
948
1009
|
"action": "ask_message_review_choice",
|
|
949
1010
|
"uses": "request_user_input",
|
|
950
|
-
"choices": ["approve-message", "revise-messaging"]
|
|
1011
|
+
"choices": ["approve-message", "revise-messaging"],
|
|
1012
|
+
"autoSelectIf": "yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging"
|
|
951
1013
|
},
|
|
952
1014
|
{
|
|
953
1015
|
"action": "sync_approved_message_set_to_campaign_brief",
|
|
@@ -1163,7 +1225,8 @@
|
|
|
1163
1225
|
"Revise filters",
|
|
1164
1226
|
"Revise message template",
|
|
1165
1227
|
"Pause here"
|
|
1166
|
-
]
|
|
1228
|
+
],
|
|
1229
|
+
"autoSelectIf": "yolo_mode and the first passing generated message clears the quality floor"
|
|
1167
1230
|
}
|
|
1168
1231
|
],
|
|
1169
1232
|
"allowedTools": [
|
|
@@ -1238,7 +1301,8 @@
|
|
|
1238
1301
|
"Use this connected sender",
|
|
1239
1302
|
"Connect a different sender in Settings",
|
|
1240
1303
|
"Pause here"
|
|
1241
|
-
]
|
|
1304
|
+
],
|
|
1305
|
+
"autoSelectIf": "yolo_mode and exactly one safe connected sender is available"
|
|
1242
1306
|
},
|
|
1243
1307
|
{
|
|
1244
1308
|
"action": "attach_selected_sender",
|
|
@@ -1266,7 +1330,8 @@
|
|
|
1266
1330
|
"uses": "request_user_input",
|
|
1267
1331
|
"singleChoice": true,
|
|
1268
1332
|
"choices": ["Start campaign", "Review campaign first", "Pause here"],
|
|
1269
|
-
"onUserStart": "claude-greenlight"
|
|
1333
|
+
"onUserStart": "claude-greenlight",
|
|
1334
|
+
"neverAutoSelectInYolo": true
|
|
1270
1335
|
}
|
|
1271
1336
|
],
|
|
1272
1337
|
"allowedTools": [
|
|
@@ -130,13 +130,23 @@ raw engagers. If the sampled fit rate is stronger or weaker, use the sampled
|
|
|
130
130
|
rate with a conservative cleanup factor, cap the source candidates at the
|
|
131
131
|
approved provider limit, and select only enough posts to reach that engager
|
|
132
132
|
count. The planning floor is 10% projected fit after cleanup; if Signal
|
|
133
|
-
Discovery falls below that floor
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
133
|
+
Discovery falls below that floor during the pre-scrape sample, revise the source
|
|
134
|
+
plan before importing. After an approved scrape starts, do not discard a nonempty
|
|
135
|
+
completed source list just because it lands below the source-candidate target:
|
|
136
|
+
confirm/copy the completed list for the first review sample, then add more posts
|
|
137
|
+
or switch provider if the sample quality or scale is not enough. The subsequent
|
|
138
|
+
`confirm_lead_list` call uses `reviewBatchLimit: 15`. It copies the confirmed
|
|
139
|
+
source rows into the campaign table and returns only the first 15 rows as the
|
|
140
|
+
review/process sample.
|
|
141
|
+
|
|
142
|
+
Harvest-specific interpretation: Signal Discovery post comments and reactions
|
|
143
|
+
are paged from Harvest at about 100 records per page, with production caps of
|
|
144
|
+
1,500 reactions and 300 comments per selected post. The source list stores
|
|
145
|
+
headline-passing candidates after that scrape. Therefore `71 source rows` means
|
|
146
|
+
71 candidates passed headline/list filtering and were inserted; it does not
|
|
147
|
+
mean the post scrape stopped after 71 raw engagers. Treat raw visible engagement,
|
|
148
|
+
raw fetched pages, inserted source rows, and the 15-row review/process sample as
|
|
149
|
+
separate numbers.
|
|
140
150
|
|
|
141
151
|
For supplied direct lists, `confirm_lead_list` should receive
|
|
142
152
|
`reviewBatchLimit: 15` so the campaign table can hold the confirmed list while
|
|
@@ -24,12 +24,34 @@ When the user asks to find posts or start searching, **IMMEDIATELY begin Round 1
|
|
|
24
24
|
|
|
25
25
|
<lead_target>
|
|
26
26
|
|
|
27
|
-
- **Default
|
|
28
|
-
|
|
27
|
+
- **Default create-campaign target: ~300 good-fit prospects after cleanup,
|
|
28
|
+
enrichment, and fit filtering.**
|
|
29
|
+
- **Working assumption when no stronger sample exists: ~20% of raw post
|
|
30
|
+
engagers become good-fit prospects, so plan around ~1,500 raw engagers.**
|
|
31
|
+
- Provider/import caps are internal execution limits, not customer-facing
|
|
32
|
+
promises. Do not tell the user the internal provider cap.
|
|
29
33
|
- Quality > Quantity: a few hundred active users > 1000 potentially inactive profiles
|
|
30
34
|
- Focus on ACTIVE platform users who will actually see and respond to outreach
|
|
31
35
|
</lead_target>
|
|
32
36
|
|
|
37
|
+
<harvest_scrape_contract>
|
|
38
|
+
|
|
39
|
+
Production Signal Discovery uses Harvest for post comments and reactions.
|
|
40
|
+
Harvest returns about 100 comments or reactions per page. The source scrape
|
|
41
|
+
pages through Harvest up to the configured per-post caps, currently 1,500
|
|
42
|
+
reactions and 300 comments per selected post.
|
|
43
|
+
|
|
44
|
+
Important interpretation rule: a source list row count is the number of
|
|
45
|
+
headline-passing candidates inserted after scraping and filtering, not the raw
|
|
46
|
+
number of engagers fetched. If a selected post has 1,200 visible engagers and
|
|
47
|
+
the source list lands at 71 rows, read that as 71 headline-passing inserted
|
|
48
|
+
rows, not as "only 71 engagers were scraped." Do not reject a completed
|
|
49
|
+
non-empty source list solely because inserted rows are below the raw engager
|
|
50
|
+
target; confirm/copy it for the 15-row review sample, then decide whether to add
|
|
51
|
+
more posts or switch lanes based on sample quality and scale.
|
|
52
|
+
|
|
53
|
+
</harvest_scrape_contract>
|
|
54
|
+
|
|
33
55
|
<multi_round_search>
|
|
34
56
|
|
|
35
57
|
## Multi-Round Search Strategy
|
|
@@ -98,12 +120,12 @@ You must estimate:
|
|
|
98
120
|
- expected good-fit prospects per right-content post after dedupe/cleanup
|
|
99
121
|
- `postsNeededForTarget`
|
|
100
122
|
- number of right-content posts needed to reach the target good-fit lead
|
|
101
|
-
count, defaulting to
|
|
123
|
+
count, defaulting to 300 for Signal Discovery unless the campaign says
|
|
102
124
|
otherwise
|
|
103
125
|
- `requiredEngagersToScrape`
|
|
104
|
-
- `ceil(targetGoodFitLeads / sampledFitRate)`; for example
|
|
105
|
-
prospects per 100 engagers means a
|
|
106
|
-
1,
|
|
126
|
+
- `ceil(targetGoodFitLeads / sampledFitRate)`; for example the default
|
|
127
|
+
planning assumption of 20 good-fit prospects per 100 engagers means a
|
|
128
|
+
300-good-fit source target needs about 1,500 raw engagers scraped
|
|
107
129
|
- `planningFloor`
|
|
108
130
|
- minimum acceptable sampled/projected fit rate after conservative cleanup;
|
|
109
131
|
default 10%. Below this, do not scale Signal Discovery.
|
|
@@ -119,8 +141,8 @@ Use conservative logic:
|
|
|
119
141
|
those posts in the watched Signal Discovery UI before sampling engagers
|
|
120
142
|
- call `fetch_post_engagers`
|
|
121
143
|
- fetch only a representative first sample, not a full scrape
|
|
122
|
-
- default to
|
|
123
|
-
|
|
144
|
+
- default to a fast first sample of roughly 15 people for one post; when
|
|
145
|
+
comparing multiple posts, inspect roughly 15-40 people total
|
|
124
146
|
- score them against `headlineICPCriteria` or a rough yes/no headline rubric
|
|
125
147
|
- use headline and display-name cues only for this spot check; do not enrich people during viability estimation
|
|
126
148
|
- use that sampled pass rate to extrapolate conservatively
|
|
@@ -130,7 +152,7 @@ Use conservative logic:
|
|
|
130
152
|
- stale posts
|
|
131
153
|
- adjacent communities that are not actual buyers
|
|
132
154
|
4. Explain the pass-through briefly and explicitly:
|
|
133
|
-
- preferred: "`sampledCount`: 40, `passCount`: 9, `passRate`: ~22%, `goodFitPer100Engagers`: ~22 before cleanup / ~13-16 after cleanup, `requiredEngagersToScrape`: ~
|
|
155
|
+
- preferred: "`sampledCount`: 40, `passCount`: 9, `passRate`: ~22%, `goodFitPer100Engagers`: ~22 before cleanup / ~13-16 after cleanup, `requiredEngagersToScrape`: ~1,875-2,300 for a 300-good-fit target after cleanup, `avgReachableEngagersPerPost`: 240, `goodFitProspectsPerPost`: ~31-38, `postsNeededForTarget`: ~8-10, `recentStrongPostCount`: 15-25, `freshEnoughPostCount`: 8-12, `projectedRange`: 300-380 from selected posts"
|
|
134
156
|
- fallback: "I could not fetch engagers, so this is inferred from post and author quality only"
|
|
135
157
|
5. If the evidence is too weak, say so and return a low-confidence estimate instead of pretending precision.
|
|
136
158
|
6. If sampled/projected fit after cleanup is below the 10% planning floor,
|
|
@@ -385,7 +407,7 @@ For `create-campaign-v2` source approval, do not treat the default
|
|
|
385
407
|
`selectionTarget` of 3 posts as enough by itself. Before the final source
|
|
386
408
|
recommendation, estimate source capacity from real sample math:
|
|
387
409
|
|
|
388
|
-
- source target good-fit leads (default
|
|
410
|
+
- source target good-fit leads (default 300 for Signal Discovery unless the
|
|
389
411
|
campaign says otherwise)
|
|
390
412
|
- eligible right-content posts by lane/content type
|
|
391
413
|
- reachable engagers from those posts
|
|
@@ -461,8 +483,10 @@ source-capacity plan without asking for another yes/no gate. Then
|
|
|
461
483
|
the campaign table and returns the first 15 review/process rows. Do not confuse
|
|
462
484
|
the source-candidate target with the review sample size.
|
|
463
485
|
If the completed source scrape comes back below the approved source-candidate
|
|
464
|
-
target,
|
|
465
|
-
|
|
486
|
+
target but has usable rows, still call `confirm_lead_list` so the completed
|
|
487
|
+
source list is copied into the campaign and the first 15 rows can be reviewed.
|
|
488
|
+
Treat the shortfall as a scale warning: add more approved posts or move to Sales
|
|
489
|
+
Nav only after the first sample proves the lane is too thin or off-ICP.
|
|
466
490
|
|
|
467
491
|
The promotion/select step is required campaign state. Use post IDs from the
|
|
468
492
|
current campaign-scoped search result, not stale IDs copied from a source review
|