@sellable/mcp 0.1.105 → 0.1.107
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/tools/leads.js +9 -0
- package/dist/tools/rubrics.d.ts +48 -0
- package/dist/tools/rubrics.js +62 -1
- package/package.json +1 -1
- package/skills/create-campaign-v2/core/flow.v2.json +8 -2
- package/skills/create-campaign-v2-tail/SKILL.md +16 -2
- package/skills/providers/signal-discovery.md +8 -0
package/dist/tools/leads.js
CHANGED
|
@@ -2366,6 +2366,15 @@ export async function selectPromisingPosts(input) {
|
|
|
2366
2366
|
headlineICPCriteria,
|
|
2367
2367
|
rubricGuidelines: headlineICPCriteria,
|
|
2368
2368
|
});
|
|
2369
|
+
if (selectionResult.selectedCount <= 0) {
|
|
2370
|
+
return {
|
|
2371
|
+
success: false,
|
|
2372
|
+
selectedCount: selectionResult.selectedCount,
|
|
2373
|
+
unselectedCount: selectionResult.unselectedCount,
|
|
2374
|
+
criteriaCount: selectionResult.criteriaCount,
|
|
2375
|
+
message: "No Signal Discovery posts were selected for this campaign. Do not import yet. Re-run a campaign-scoped search_signals call, use recommendedPostIds from the current campaign search state, or ask the user to promote posts in the UI before retrying select_promising_posts.",
|
|
2376
|
+
};
|
|
2377
|
+
}
|
|
2369
2378
|
// Update currentStep if provided (via v2 endpoint)
|
|
2370
2379
|
if (currentStep) {
|
|
2371
2380
|
await api.put(`/api/v2/campaign-offers/${campaignOfferId}`, {
|
package/dist/tools/rubrics.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ type WaitForRubricResultsInput = {
|
|
|
32
32
|
campaignOfferId?: string;
|
|
33
33
|
tableId?: string;
|
|
34
34
|
targetCount?: number;
|
|
35
|
+
minPassedCount?: number;
|
|
35
36
|
timeoutMs?: number;
|
|
36
37
|
intervalMs?: number;
|
|
37
38
|
includeRows?: boolean;
|
|
@@ -44,6 +45,9 @@ type WorkflowTableStats = {
|
|
|
44
45
|
messagesCount?: number;
|
|
45
46
|
needsApprovalCount?: number;
|
|
46
47
|
processingCount?: number;
|
|
48
|
+
queuedCount?: number;
|
|
49
|
+
cancellableCount?: number;
|
|
50
|
+
readyEnrichCount?: number;
|
|
47
51
|
failedCount?: number;
|
|
48
52
|
passRate?: {
|
|
49
53
|
completed: number;
|
|
@@ -109,6 +113,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
109
113
|
maxProspects?: undefined;
|
|
110
114
|
tableId?: undefined;
|
|
111
115
|
targetCount?: undefined;
|
|
116
|
+
minPassedCount?: undefined;
|
|
112
117
|
timeoutMs?: undefined;
|
|
113
118
|
intervalMs?: undefined;
|
|
114
119
|
includeRows?: undefined;
|
|
@@ -141,6 +146,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
141
146
|
maxProspects?: undefined;
|
|
142
147
|
tableId?: undefined;
|
|
143
148
|
targetCount?: undefined;
|
|
149
|
+
minPassedCount?: undefined;
|
|
144
150
|
timeoutMs?: undefined;
|
|
145
151
|
intervalMs?: undefined;
|
|
146
152
|
includeRows?: undefined;
|
|
@@ -198,6 +204,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
198
204
|
maxProspects?: undefined;
|
|
199
205
|
tableId?: undefined;
|
|
200
206
|
targetCount?: undefined;
|
|
207
|
+
minPassedCount?: undefined;
|
|
201
208
|
timeoutMs?: undefined;
|
|
202
209
|
intervalMs?: undefined;
|
|
203
210
|
includeRows?: undefined;
|
|
@@ -255,6 +262,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
255
262
|
maxProspects?: undefined;
|
|
256
263
|
tableId?: undefined;
|
|
257
264
|
targetCount?: undefined;
|
|
265
|
+
minPassedCount?: undefined;
|
|
258
266
|
timeoutMs?: undefined;
|
|
259
267
|
intervalMs?: undefined;
|
|
260
268
|
includeRows?: undefined;
|
|
@@ -314,6 +322,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
314
322
|
maxProspects?: undefined;
|
|
315
323
|
tableId?: undefined;
|
|
316
324
|
targetCount?: undefined;
|
|
325
|
+
minPassedCount?: undefined;
|
|
317
326
|
timeoutMs?: undefined;
|
|
318
327
|
intervalMs?: undefined;
|
|
319
328
|
includeRows?: undefined;
|
|
@@ -343,6 +352,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
343
352
|
maxProspects?: undefined;
|
|
344
353
|
tableId?: undefined;
|
|
345
354
|
targetCount?: undefined;
|
|
355
|
+
minPassedCount?: undefined;
|
|
346
356
|
timeoutMs?: undefined;
|
|
347
357
|
intervalMs?: undefined;
|
|
348
358
|
includeRows?: undefined;
|
|
@@ -372,6 +382,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
372
382
|
checkName?: undefined;
|
|
373
383
|
tableId?: undefined;
|
|
374
384
|
targetCount?: undefined;
|
|
385
|
+
minPassedCount?: undefined;
|
|
375
386
|
timeoutMs?: undefined;
|
|
376
387
|
intervalMs?: undefined;
|
|
377
388
|
includeRows?: undefined;
|
|
@@ -397,6 +408,10 @@ export declare const rubricToolDefinitions: ({
|
|
|
397
408
|
type: string;
|
|
398
409
|
description: string;
|
|
399
410
|
};
|
|
411
|
+
minPassedCount: {
|
|
412
|
+
type: string;
|
|
413
|
+
description: string;
|
|
414
|
+
};
|
|
400
415
|
timeoutMs: {
|
|
401
416
|
type: string;
|
|
402
417
|
description: string;
|
|
@@ -496,6 +511,7 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
496
511
|
elapsedMs: number;
|
|
497
512
|
tableId: string;
|
|
498
513
|
passRate: {
|
|
514
|
+
minPassedCount?: number | undefined;
|
|
499
515
|
completed: number;
|
|
500
516
|
passed: number;
|
|
501
517
|
percent: number;
|
|
@@ -507,7 +523,10 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
507
523
|
diagnostic?: undefined;
|
|
508
524
|
guidance?: undefined;
|
|
509
525
|
} | {
|
|
526
|
+
stats: WorkflowTableStats;
|
|
527
|
+
rows?: import("./rows.js").LightweightRow[] | undefined;
|
|
510
528
|
ready: boolean;
|
|
529
|
+
partial: boolean;
|
|
511
530
|
reason: string;
|
|
512
531
|
attempts: number;
|
|
513
532
|
elapsedMs: number;
|
|
@@ -518,6 +537,7 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
518
537
|
pending: number;
|
|
519
538
|
percent: number;
|
|
520
539
|
targetCount: number;
|
|
540
|
+
minPassedCount: number;
|
|
521
541
|
};
|
|
522
542
|
partialResult: {
|
|
523
543
|
completed: number;
|
|
@@ -525,7 +545,35 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
525
545
|
pending: number;
|
|
526
546
|
percent: number;
|
|
527
547
|
targetCount: number;
|
|
548
|
+
minPassedCount: number;
|
|
549
|
+
enoughToDiagnose: boolean;
|
|
550
|
+
floorMet: boolean;
|
|
551
|
+
};
|
|
552
|
+
diagnostic?: undefined;
|
|
553
|
+
guidance?: undefined;
|
|
554
|
+
} | {
|
|
555
|
+
ready: boolean;
|
|
556
|
+
reason: string;
|
|
557
|
+
attempts: number;
|
|
558
|
+
elapsedMs: number;
|
|
559
|
+
tableId: string;
|
|
560
|
+
passRate: {
|
|
561
|
+
minPassedCount?: number | undefined;
|
|
562
|
+
completed: number;
|
|
563
|
+
passed: number;
|
|
564
|
+
pending: number;
|
|
565
|
+
percent: number;
|
|
566
|
+
targetCount: number;
|
|
567
|
+
};
|
|
568
|
+
partialResult: {
|
|
528
569
|
enoughToDiagnose: boolean;
|
|
570
|
+
floorMet: boolean;
|
|
571
|
+
minPassedCount?: number | undefined;
|
|
572
|
+
completed: number;
|
|
573
|
+
passed: number;
|
|
574
|
+
pending: number;
|
|
575
|
+
percent: number;
|
|
576
|
+
targetCount: number;
|
|
529
577
|
};
|
|
530
578
|
diagnostic: {
|
|
531
579
|
totalRows: number;
|
package/dist/tools/rubrics.js
CHANGED
|
@@ -103,6 +103,11 @@ function resolveMaxProspects(value) {
|
|
|
103
103
|
const parsed = typeof value === "number" && !Number.isNaN(value) ? value : fallback;
|
|
104
104
|
return Math.min(MAX_PROSPECTS_LIMIT, Math.max(1, Math.floor(parsed)));
|
|
105
105
|
}
|
|
106
|
+
function resolveMinPassedCount(value) {
|
|
107
|
+
if (typeof value !== "number" || Number.isNaN(value))
|
|
108
|
+
return null;
|
|
109
|
+
return Math.min(MAX_PROSPECTS_LIMIT, Math.max(1, Math.floor(value)));
|
|
110
|
+
}
|
|
106
111
|
function mergeRubricsWithExisting(draftRubrics, existingRubrics) {
|
|
107
112
|
const existingByCheckName = new Map();
|
|
108
113
|
existingRubrics.forEach((item) => {
|
|
@@ -348,6 +353,10 @@ export const rubricToolDefinitions = [
|
|
|
348
353
|
type: "number",
|
|
349
354
|
description: `Number of completed rubric results to wait for (default ${DEFAULT_MAX_PROSPECTS}).`,
|
|
350
355
|
},
|
|
356
|
+
minPassedCount: {
|
|
357
|
+
type: "number",
|
|
358
|
+
description: "Optional pass floor for bounded create-campaign samples. When this floor is met and remaining target rows are resolved as failed/not processing, the tool returns ready:true with partial:true instead of forcing a timeout.",
|
|
359
|
+
},
|
|
351
360
|
timeoutMs: {
|
|
352
361
|
type: "number",
|
|
353
362
|
description: `Max time to wait in ms (default ${DEFAULT_TIMEOUT_MS}).`,
|
|
@@ -625,6 +634,7 @@ export async function waitForRubricResults(input) {
|
|
|
625
634
|
const timeoutMs = Math.max(5000, input.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
626
635
|
const intervalMs = Math.max(500, input.intervalMs ?? DEFAULT_INTERVAL_MS);
|
|
627
636
|
const targetCount = resolveMaxProspects(input.targetCount);
|
|
637
|
+
const minPassedCount = resolveMinPassedCount(input.minPassedCount);
|
|
628
638
|
const includeRows = input.includeRows !== false;
|
|
629
639
|
let tableId = input.tableId;
|
|
630
640
|
if (!tableId && input.campaignOfferId) {
|
|
@@ -642,10 +652,18 @@ export async function waitForRubricResults(input) {
|
|
|
642
652
|
const stats = await api.get(`/api/v3/workflow-tables/${tableId}/stats`);
|
|
643
653
|
lastStats = stats;
|
|
644
654
|
const completed = stats.passRate?.completed ?? 0;
|
|
655
|
+
const passed = stats.passRate?.passed ?? 0;
|
|
645
656
|
const totalRows = stats.totalRows ?? 0;
|
|
646
657
|
const effectiveTarget = totalRows > 0 ? Math.min(targetCount, totalRows) : targetCount;
|
|
658
|
+
const pending = Math.max(effectiveTarget - completed, 0);
|
|
659
|
+
const failedCount = stats.failedCount ?? 0;
|
|
660
|
+
const processingCount = stats.processingCount ?? 0;
|
|
661
|
+
const queuedCount = stats.queuedCount ?? 0;
|
|
662
|
+
const cancellableCount = stats.cancellableCount ?? 0;
|
|
663
|
+
const minPassFloorMet = minPassedCount !== null && passed >= minPassedCount;
|
|
664
|
+
const unresolvedRowsResolvedAsFailures = pending > 0 && completed + failedCount >= effectiveTarget;
|
|
665
|
+
const noActiveProcessing = processingCount === 0 && queuedCount === 0 && cancellableCount === 0;
|
|
647
666
|
if (completed >= effectiveTarget) {
|
|
648
|
-
const passed = stats.passRate?.passed ?? 0;
|
|
649
667
|
const percent = completed > 0 ? Math.round((passed / completed) * 100) : 0;
|
|
650
668
|
const rowSnapshot = includeRows
|
|
651
669
|
? await getTableRowsMinimal(tableId, {
|
|
@@ -662,6 +680,46 @@ export async function waitForRubricResults(input) {
|
|
|
662
680
|
passed,
|
|
663
681
|
percent,
|
|
664
682
|
targetCount: effectiveTarget,
|
|
683
|
+
...(minPassedCount !== null ? { minPassedCount } : {}),
|
|
684
|
+
},
|
|
685
|
+
...(rowSnapshot ? { rows: rowSnapshot.rows } : {}),
|
|
686
|
+
stats,
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
if (minPassFloorMet && noActiveProcessing) {
|
|
690
|
+
const percent = completed > 0 ? Math.round((passed / completed) * 100) : 0;
|
|
691
|
+
const reason = unresolvedRowsResolvedAsFailures
|
|
692
|
+
? "min_passed_count_met_with_resolved_failures"
|
|
693
|
+
: "min_passed_count_met_no_active_processing";
|
|
694
|
+
const rowSnapshot = includeRows
|
|
695
|
+
? await getTableRowsMinimal(tableId, {
|
|
696
|
+
limit: effectiveTarget,
|
|
697
|
+
})
|
|
698
|
+
: null;
|
|
699
|
+
return {
|
|
700
|
+
ready: true,
|
|
701
|
+
partial: true,
|
|
702
|
+
reason,
|
|
703
|
+
attempts,
|
|
704
|
+
elapsedMs: Date.now() - start,
|
|
705
|
+
tableId,
|
|
706
|
+
passRate: {
|
|
707
|
+
completed,
|
|
708
|
+
passed,
|
|
709
|
+
pending,
|
|
710
|
+
percent,
|
|
711
|
+
targetCount: effectiveTarget,
|
|
712
|
+
minPassedCount,
|
|
713
|
+
},
|
|
714
|
+
partialResult: {
|
|
715
|
+
completed,
|
|
716
|
+
passed,
|
|
717
|
+
pending,
|
|
718
|
+
percent,
|
|
719
|
+
targetCount: effectiveTarget,
|
|
720
|
+
minPassedCount,
|
|
721
|
+
enoughToDiagnose: true,
|
|
722
|
+
floorMet: true,
|
|
665
723
|
},
|
|
666
724
|
...(rowSnapshot ? { rows: rowSnapshot.rows } : {}),
|
|
667
725
|
stats,
|
|
@@ -687,6 +745,7 @@ export async function waitForRubricResults(input) {
|
|
|
687
745
|
pending,
|
|
688
746
|
percent,
|
|
689
747
|
targetCount: effectiveTarget,
|
|
748
|
+
...(minPassedCount !== null ? { minPassedCount } : {}),
|
|
690
749
|
},
|
|
691
750
|
partialResult: {
|
|
692
751
|
completed,
|
|
@@ -694,7 +753,9 @@ export async function waitForRubricResults(input) {
|
|
|
694
753
|
pending,
|
|
695
754
|
percent,
|
|
696
755
|
targetCount: effectiveTarget,
|
|
756
|
+
...(minPassedCount !== null ? { minPassedCount } : {}),
|
|
697
757
|
enoughToDiagnose: completed > 0,
|
|
758
|
+
floorMet: minPassedCount !== null && passed >= minPassedCount,
|
|
698
759
|
},
|
|
699
760
|
diagnostic: {
|
|
700
761
|
totalRows,
|
package/package.json
CHANGED
|
@@ -1943,17 +1943,23 @@
|
|
|
1943
1943
|
{
|
|
1944
1944
|
"tool": "wait_for_rubric_results",
|
|
1945
1945
|
"requiredFields": [
|
|
1946
|
-
"targetCount"
|
|
1946
|
+
"targetCount",
|
|
1947
|
+
"minPassedCount"
|
|
1947
1948
|
],
|
|
1948
1949
|
"targetCountSource": "stats.totalRows_or_imported_batch_count",
|
|
1950
|
+
"minPassedCountSource": "sample.minProjectedPass (3)",
|
|
1949
1951
|
"requiredValues": {
|
|
1950
1952
|
"includeRows": false
|
|
1951
1953
|
},
|
|
1952
|
-
"note": "The shell-first flow tests 15 leads first; always pass cohortSize explicitly instead of relying on default 25 behavior.",
|
|
1954
|
+
"note": "The shell-first flow tests 15 leads first; always pass cohortSize explicitly instead of relying on default 25 behavior. Pass minPassedCount so a 15-row batch with resolved failures can advance when the 3-pass floor is already met.",
|
|
1953
1955
|
"readVia": "stats_only_tool_result",
|
|
1954
1956
|
"extractFields": [
|
|
1955
1957
|
"ready",
|
|
1958
|
+
"partial",
|
|
1959
|
+
"reason",
|
|
1956
1960
|
"passRate.completed",
|
|
1961
|
+
"passRate.passed",
|
|
1962
|
+
"passRate.minPassedCount",
|
|
1957
1963
|
"stats"
|
|
1958
1964
|
],
|
|
1959
1965
|
"doNotRetain": "rows_payload",
|
|
@@ -224,6 +224,17 @@ campaignOfferId, confirmed: true })` -> `search_signals({ campaignOfferId,
|
|
|
224
224
|
selectionMode: "replace", selections, headlineICPCriteria })` ->
|
|
225
225
|
`import_leads({ campaignOfferId, provider: "signal-discovery",
|
|
226
226
|
targetLeadCount: importLimit })`.
|
|
227
|
+
For Signal Discovery, the promotion/select step is load-bearing. Use
|
|
228
|
+
post IDs from the current campaign-scoped `search_signals` response or
|
|
229
|
+
posts the user has visibly promoted in the campaign UI. Never use post IDs
|
|
230
|
+
copied only from a source-scout summary unless they have been re-resolved
|
|
231
|
+
through the current campaign search state. After `select_promising_posts`,
|
|
232
|
+
require `selectedCount > 0` before calling `import_leads`. If it returns
|
|
233
|
+
`selectedCount: 0`, do not switch providers and do not retry import.
|
|
234
|
+
Explain that the campaign has no promoted Signal Discovery posts yet,
|
|
235
|
+
re-run a narrow campaign-scoped `search_signals` call to recover current
|
|
236
|
+
post rows, or ask the user to promote the desired posts in the UI and then
|
|
237
|
+
retry `import_leads`.
|
|
227
238
|
Source approval is the explicit confirmation for this bounded review
|
|
228
239
|
batch; do not ask for a second yes/no gate between
|
|
229
240
|
`select_promising_posts` and `import_leads`.
|
|
@@ -275,11 +286,14 @@ Shape:
|
|
|
275
286
|
queue_cells({ tableId: workflowTableId, cellIds: reviewBatchEnrichCellIds })
|
|
276
287
|
wait_for_campaign_table_ready({ tableId: workflowTableId })
|
|
277
288
|
get_rows_minimal({ tableId: workflowTableId })
|
|
278
|
-
wait_for_rubric_results({ tableId: workflowTableId, targetCount: cohortSize, includeRows: false })
|
|
289
|
+
wait_for_rubric_results({ tableId: workflowTableId, targetCount: cohortSize, minPassedCount: minProjectedPass, includeRows: false })
|
|
279
290
|
passInSample = count of first sampleSize review-batch rows with passesRubric === true
|
|
280
291
|
projectedPass = round(passInSample / sampleSize * importLimit)
|
|
281
292
|
|
|
282
|
-
if wait_for_rubric_results.ready ===
|
|
293
|
+
if wait_for_rubric_results.ready === true and partial === true and reason === "min_passed_count_met_with_resolved_failures":
|
|
294
|
+
use the partial passRate/stats as a valid sample diagnostic
|
|
295
|
+
continue if projectedPass >= minProjectedPass and generated messages are present for the passing rows
|
|
296
|
+
else if wait_for_rubric_results.ready === false and reason === "timeout":
|
|
283
297
|
use the partial passRate/stats as the sample diagnostic
|
|
284
298
|
if active processing is visible:
|
|
285
299
|
wait one more time at most
|
|
@@ -377,6 +377,14 @@ selections, headlineICPCriteria })`, then call
|
|
|
377
377
|
targetLeadCount })` for the approved review batch without asking for another
|
|
378
378
|
yes/no gate. Do not import more than the approved bounded batch.
|
|
379
379
|
|
|
380
|
+
The promotion/select step is required campaign state. Use post IDs from the
|
|
381
|
+
current campaign-scoped search result, not stale IDs copied from a source review
|
|
382
|
+
or scout summary. If `select_promising_posts` returns `selectedCount: 0`, stop:
|
|
383
|
+
do not call `import_leads`, do not switch providers automatically, and do not
|
|
384
|
+
claim the source failed. Re-run a narrow campaign-scoped search to recover
|
|
385
|
+
current post rows, or have the user promote the posts in the UI and then retry
|
|
386
|
+
the bounded import.
|
|
387
|
+
|
|
380
388
|
```json
|
|
381
389
|
import_leads({
|
|
382
390
|
"campaignOfferId": "cmp_xxx",
|