@sellable/mcp 0.1.105 → 0.1.106
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 +45 -0
- package/dist/tools/rubrics.js +60 -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;
|
|
@@ -109,6 +110,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
109
110
|
maxProspects?: undefined;
|
|
110
111
|
tableId?: undefined;
|
|
111
112
|
targetCount?: undefined;
|
|
113
|
+
minPassedCount?: undefined;
|
|
112
114
|
timeoutMs?: undefined;
|
|
113
115
|
intervalMs?: undefined;
|
|
114
116
|
includeRows?: undefined;
|
|
@@ -141,6 +143,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
141
143
|
maxProspects?: undefined;
|
|
142
144
|
tableId?: undefined;
|
|
143
145
|
targetCount?: undefined;
|
|
146
|
+
minPassedCount?: undefined;
|
|
144
147
|
timeoutMs?: undefined;
|
|
145
148
|
intervalMs?: undefined;
|
|
146
149
|
includeRows?: undefined;
|
|
@@ -198,6 +201,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
198
201
|
maxProspects?: undefined;
|
|
199
202
|
tableId?: undefined;
|
|
200
203
|
targetCount?: undefined;
|
|
204
|
+
minPassedCount?: undefined;
|
|
201
205
|
timeoutMs?: undefined;
|
|
202
206
|
intervalMs?: undefined;
|
|
203
207
|
includeRows?: undefined;
|
|
@@ -255,6 +259,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
255
259
|
maxProspects?: undefined;
|
|
256
260
|
tableId?: undefined;
|
|
257
261
|
targetCount?: undefined;
|
|
262
|
+
minPassedCount?: undefined;
|
|
258
263
|
timeoutMs?: undefined;
|
|
259
264
|
intervalMs?: undefined;
|
|
260
265
|
includeRows?: undefined;
|
|
@@ -314,6 +319,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
314
319
|
maxProspects?: undefined;
|
|
315
320
|
tableId?: undefined;
|
|
316
321
|
targetCount?: undefined;
|
|
322
|
+
minPassedCount?: undefined;
|
|
317
323
|
timeoutMs?: undefined;
|
|
318
324
|
intervalMs?: undefined;
|
|
319
325
|
includeRows?: undefined;
|
|
@@ -343,6 +349,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
343
349
|
maxProspects?: undefined;
|
|
344
350
|
tableId?: undefined;
|
|
345
351
|
targetCount?: undefined;
|
|
352
|
+
minPassedCount?: undefined;
|
|
346
353
|
timeoutMs?: undefined;
|
|
347
354
|
intervalMs?: undefined;
|
|
348
355
|
includeRows?: undefined;
|
|
@@ -372,6 +379,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
372
379
|
checkName?: undefined;
|
|
373
380
|
tableId?: undefined;
|
|
374
381
|
targetCount?: undefined;
|
|
382
|
+
minPassedCount?: undefined;
|
|
375
383
|
timeoutMs?: undefined;
|
|
376
384
|
intervalMs?: undefined;
|
|
377
385
|
includeRows?: undefined;
|
|
@@ -397,6 +405,10 @@ export declare const rubricToolDefinitions: ({
|
|
|
397
405
|
type: string;
|
|
398
406
|
description: string;
|
|
399
407
|
};
|
|
408
|
+
minPassedCount: {
|
|
409
|
+
type: string;
|
|
410
|
+
description: string;
|
|
411
|
+
};
|
|
400
412
|
timeoutMs: {
|
|
401
413
|
type: string;
|
|
402
414
|
description: string;
|
|
@@ -496,6 +508,7 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
496
508
|
elapsedMs: number;
|
|
497
509
|
tableId: string;
|
|
498
510
|
passRate: {
|
|
511
|
+
minPassedCount?: number | undefined;
|
|
499
512
|
completed: number;
|
|
500
513
|
passed: number;
|
|
501
514
|
percent: number;
|
|
@@ -507,7 +520,10 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
507
520
|
diagnostic?: undefined;
|
|
508
521
|
guidance?: undefined;
|
|
509
522
|
} | {
|
|
523
|
+
stats: WorkflowTableStats;
|
|
524
|
+
rows?: import("./rows.js").LightweightRow[] | undefined;
|
|
510
525
|
ready: boolean;
|
|
526
|
+
partial: boolean;
|
|
511
527
|
reason: string;
|
|
512
528
|
attempts: number;
|
|
513
529
|
elapsedMs: number;
|
|
@@ -518,6 +534,7 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
518
534
|
pending: number;
|
|
519
535
|
percent: number;
|
|
520
536
|
targetCount: number;
|
|
537
|
+
minPassedCount: number;
|
|
521
538
|
};
|
|
522
539
|
partialResult: {
|
|
523
540
|
completed: number;
|
|
@@ -525,7 +542,35 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
525
542
|
pending: number;
|
|
526
543
|
percent: number;
|
|
527
544
|
targetCount: number;
|
|
545
|
+
minPassedCount: number;
|
|
546
|
+
enoughToDiagnose: boolean;
|
|
547
|
+
floorMet: boolean;
|
|
548
|
+
};
|
|
549
|
+
diagnostic?: undefined;
|
|
550
|
+
guidance?: undefined;
|
|
551
|
+
} | {
|
|
552
|
+
ready: boolean;
|
|
553
|
+
reason: string;
|
|
554
|
+
attempts: number;
|
|
555
|
+
elapsedMs: number;
|
|
556
|
+
tableId: string;
|
|
557
|
+
passRate: {
|
|
558
|
+
minPassedCount?: number | undefined;
|
|
559
|
+
completed: number;
|
|
560
|
+
passed: number;
|
|
561
|
+
pending: number;
|
|
562
|
+
percent: number;
|
|
563
|
+
targetCount: number;
|
|
564
|
+
};
|
|
565
|
+
partialResult: {
|
|
528
566
|
enoughToDiagnose: boolean;
|
|
567
|
+
floorMet: boolean;
|
|
568
|
+
minPassedCount?: number | undefined;
|
|
569
|
+
completed: number;
|
|
570
|
+
passed: number;
|
|
571
|
+
pending: number;
|
|
572
|
+
percent: number;
|
|
573
|
+
targetCount: number;
|
|
529
574
|
};
|
|
530
575
|
diagnostic: {
|
|
531
576
|
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,17 @@ 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 needsEnrichCount = stats.needsEnrichCount ?? 0;
|
|
662
|
+
const minPassFloorMet = minPassedCount !== null && passed >= minPassedCount;
|
|
663
|
+
const unresolvedRowsResolvedAsFailures = pending > 0 && completed + failedCount >= effectiveTarget;
|
|
664
|
+
const noActiveProcessing = processingCount === 0 && needsEnrichCount === 0;
|
|
647
665
|
if (completed >= effectiveTarget) {
|
|
648
|
-
const passed = stats.passRate?.passed ?? 0;
|
|
649
666
|
const percent = completed > 0 ? Math.round((passed / completed) * 100) : 0;
|
|
650
667
|
const rowSnapshot = includeRows
|
|
651
668
|
? await getTableRowsMinimal(tableId, {
|
|
@@ -662,6 +679,45 @@ export async function waitForRubricResults(input) {
|
|
|
662
679
|
passed,
|
|
663
680
|
percent,
|
|
664
681
|
targetCount: effectiveTarget,
|
|
682
|
+
...(minPassedCount !== null ? { minPassedCount } : {}),
|
|
683
|
+
},
|
|
684
|
+
...(rowSnapshot ? { rows: rowSnapshot.rows } : {}),
|
|
685
|
+
stats,
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
if (minPassFloorMet &&
|
|
689
|
+
unresolvedRowsResolvedAsFailures &&
|
|
690
|
+
noActiveProcessing) {
|
|
691
|
+
const percent = completed > 0 ? Math.round((passed / completed) * 100) : 0;
|
|
692
|
+
const rowSnapshot = includeRows
|
|
693
|
+
? await getTableRowsMinimal(tableId, {
|
|
694
|
+
limit: effectiveTarget,
|
|
695
|
+
})
|
|
696
|
+
: null;
|
|
697
|
+
return {
|
|
698
|
+
ready: true,
|
|
699
|
+
partial: true,
|
|
700
|
+
reason: "min_passed_count_met_with_resolved_failures",
|
|
701
|
+
attempts,
|
|
702
|
+
elapsedMs: Date.now() - start,
|
|
703
|
+
tableId,
|
|
704
|
+
passRate: {
|
|
705
|
+
completed,
|
|
706
|
+
passed,
|
|
707
|
+
pending,
|
|
708
|
+
percent,
|
|
709
|
+
targetCount: effectiveTarget,
|
|
710
|
+
minPassedCount,
|
|
711
|
+
},
|
|
712
|
+
partialResult: {
|
|
713
|
+
completed,
|
|
714
|
+
passed,
|
|
715
|
+
pending,
|
|
716
|
+
percent,
|
|
717
|
+
targetCount: effectiveTarget,
|
|
718
|
+
minPassedCount,
|
|
719
|
+
enoughToDiagnose: true,
|
|
720
|
+
floorMet: true,
|
|
665
721
|
},
|
|
666
722
|
...(rowSnapshot ? { rows: rowSnapshot.rows } : {}),
|
|
667
723
|
stats,
|
|
@@ -687,6 +743,7 @@ export async function waitForRubricResults(input) {
|
|
|
687
743
|
pending,
|
|
688
744
|
percent,
|
|
689
745
|
targetCount: effectiveTarget,
|
|
746
|
+
...(minPassedCount !== null ? { minPassedCount } : {}),
|
|
690
747
|
},
|
|
691
748
|
partialResult: {
|
|
692
749
|
completed,
|
|
@@ -694,7 +751,9 @@ export async function waitForRubricResults(input) {
|
|
|
694
751
|
pending,
|
|
695
752
|
percent,
|
|
696
753
|
targetCount: effectiveTarget,
|
|
754
|
+
...(minPassedCount !== null ? { minPassedCount } : {}),
|
|
697
755
|
enoughToDiagnose: completed > 0,
|
|
756
|
+
floorMet: minPassedCount !== null && passed >= minPassedCount,
|
|
698
757
|
},
|
|
699
758
|
diagnostic: {
|
|
700
759
|
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",
|