@sellable/mcp 0.1.191 → 0.1.193
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 +1 -1
- package/agents/post-find-leads-message-scout.md +7 -7
- package/agents/registry.json +2 -2
- package/dist/auth.js +1 -1
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/server.js +22 -0
- package/dist/tools/campaign-processing.d.ts +383 -0
- package/dist/tools/campaign-processing.js +304 -0
- package/dist/tools/leads.d.ts +68 -19
- package/dist/tools/leads.js +65 -9
- package/dist/tools/prompts.d.ts +1 -1
- package/dist/tools/prompts.js +3 -3
- package/dist/tools/registry.d.ts +289 -37
- package/dist/tools/registry.js +2 -0
- package/dist/tools/rows.d.ts +2 -0
- package/dist/tools/rubrics.js +1 -1
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +8 -6
- package/skills/create-campaign-v2/SKILL.md +8 -9
- package/skills/create-campaign-v2/SOUL.md +2 -2
- package/skills/create-campaign-v2/core/flow.v2.json +1 -1
- package/skills/create-campaign-v2/core/policy.md +3 -3
- package/skills/create-campaign-v2/references/approval-gate-framing.md +5 -5
- package/skills/create-campaign-v2/references/filter-leads.md +5 -3
- package/skills/create-campaign-v2/references/parallel-critique-protocol.md +2 -2
- package/skills/create-campaign-v2/references/sample-validation-loop.md +46 -66
- package/skills/create-campaign-v2/references/step-15-re-cascade.md +20 -13
- package/skills/create-campaign-v2-tail/SKILL.md +52 -65
- package/skills/generate-messages/SKILL.md +2 -2
|
@@ -27,8 +27,8 @@ Re-cascade runs whenever ALL of the following are true:
|
|
|
27
27
|
to continue processing more campaign rows.
|
|
28
28
|
2. A subsequent check of rubric state shows rows that were pending at
|
|
29
29
|
first message-generation pass are now passed.
|
|
30
|
-
3. Those newly-passed rows do NOT yet have generated messages
|
|
31
|
-
(`
|
|
30
|
+
3. Those newly-passed rows do NOT yet have current-revision generated messages
|
|
31
|
+
(`currentRevisionGeneratedMessagesCount` flat relative to pre-cascade).
|
|
32
32
|
4. Step 15 has NOT yet transitioned to `awaiting-user-greenlight`.
|
|
33
33
|
|
|
34
34
|
Before that first generated-message approval, do not run this loop just to wait
|
|
@@ -41,25 +41,31 @@ rows graduated.
|
|
|
41
41
|
## Re-Cascade Loop Shape
|
|
42
42
|
|
|
43
43
|
```text
|
|
44
|
-
1.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
- messageStatus = "pending" OR messagesCount = 0
|
|
44
|
+
1. use selector dry-run only if debugging:
|
|
45
|
+
`select_campaign_cells({ columnRole: "generateMessage", rowSelector: { type: "needsGeneratedMessage" } })`
|
|
46
|
+
The normal path can skip this and queue directly.
|
|
48
47
|
|
|
49
|
-
2. if
|
|
48
|
+
2. if selectedCellCount == 0:
|
|
50
49
|
proceed to awaiting-user-greenlight
|
|
51
50
|
EXIT re-cascade loop
|
|
52
51
|
|
|
53
52
|
3. else:
|
|
54
|
-
|
|
53
|
+
queue_campaign_cells({
|
|
55
54
|
tableId,
|
|
56
|
-
|
|
55
|
+
columnRole: "generateMessage",
|
|
56
|
+
rowSelector: { type: "needsGeneratedMessage" }
|
|
57
57
|
})
|
|
58
58
|
|
|
59
|
-
4.
|
|
59
|
+
4. wait_for_campaign_processing({
|
|
60
|
+
tableId,
|
|
61
|
+
minGeneratedMessages: 1,
|
|
62
|
+
templateRevision: "current"
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
5. re-check token contract on the cascaded subset (strict mode default)
|
|
60
66
|
— see references/thomas-revision-filters.md + gold-standard-message-patterns.md
|
|
61
67
|
|
|
62
|
-
|
|
68
|
+
6. go back to step 1 until selectedCellCount == 0
|
|
63
69
|
```
|
|
64
70
|
|
|
65
71
|
## Cap
|
|
@@ -86,9 +92,10 @@ silently skipped.
|
|
|
86
92
|
## Hard Rules
|
|
87
93
|
|
|
88
94
|
- Generate Message cells do not always auto-cascade on late-passed rows —
|
|
89
|
-
Step 15 must explicitly queue
|
|
95
|
+
Step 15 must explicitly queue `needsGeneratedMessage` cells.
|
|
90
96
|
- Re-cascade iteration cap = 3. Trip escalates.
|
|
91
|
-
- `
|
|
97
|
+
- `currentRevisionGeneratedMessagesCount` flat + rubric graduating rows ⇒
|
|
98
|
+
re-cascade required.
|
|
92
99
|
- Token contract enforcement applies per-cascade, not once-for-all.
|
|
93
100
|
- Re-cascade NEVER re-runs `import_leads`, `check_rubric`, or
|
|
94
101
|
`enrich_with_prospeo`. Those are Step 13 / Step 14 actions.
|
|
@@ -30,9 +30,8 @@ Step 13 — materialize source list + confirm initial campaign slice
|
|
|
30
30
|
materialize/reuse approved source with campaignOfferId
|
|
31
31
|
import_leads(source-list target; SalesNav/Prospeo default ~1000, Signal Discovery default ~1500 engagers)
|
|
32
32
|
wait_for_lead_list_ready
|
|
33
|
-
confirm_lead_list(reviewBatchLimit=15)
|
|
33
|
+
confirm_lead_list(reviewBatchLimit=15) # compact reviewBatch metadata is stored on the campaign table
|
|
34
34
|
wait_for_campaign_table_ready # campaign table exists
|
|
35
|
-
get_rows_minimal # read initial campaign-table execution slice
|
|
36
35
|
update_campaign(currentStep=filter-choice)
|
|
37
36
|
|
|
38
37
|
Post-import main thread
|
|
@@ -45,22 +44,17 @@ Post-import main thread
|
|
|
45
44
|
only then start the initial-slice cascade
|
|
46
45
|
|
|
47
46
|
Step 14 — kick initial-slice cascade + observe campaign rows
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
get_rows_minimal # read passesRubric + message cell status per row
|
|
51
|
-
wait_for_rubric_results(minPassedCount=1, includeRows=false)
|
|
47
|
+
queue_campaign_cells(columnRole=enrich, rowSelector=reviewBatch) <-- starts bounded chain
|
|
48
|
+
wait_for_campaign_processing(minPassedCount=1)
|
|
52
49
|
if at least one row passes: update_campaign(currentStep=auto-execute-messaging)
|
|
53
50
|
compute projectedPass for later reporting / revision decisions
|
|
54
|
-
if zero rows pass: diagnose brief-vs-list; if brief: update_campaign_brief + re-queue + wait
|
|
51
|
+
if zero rows pass: diagnose brief-vs-list; if brief: update_campaign_brief + re-queue + wait with changed args
|
|
55
52
|
(check_rubric / bulk_enrich_with_prospeo are NOT called here —
|
|
56
|
-
cascade already did them.
|
|
57
|
-
read-only observation helper if you need to block until the first
|
|
58
|
-
passing filtered row exists.)
|
|
53
|
+
cascade already did them.)
|
|
59
54
|
|
|
60
55
|
Step 15 — observe messaging
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
(rare) queue_cells on any pending Generate Message cells
|
|
56
|
+
queue_campaign_cells(columnRole=generateMessage, rowSelector=needsGeneratedMessage)
|
|
57
|
+
wait_for_campaign_processing({ minGeneratedMessages: 1, templateRevision: "current" })
|
|
64
58
|
token-contract spot check via get_rows
|
|
65
59
|
update_campaign(currentStep=auto-execute-messaging) with review-ready narration
|
|
66
60
|
ask the user to approve the generated message before Settings
|
|
@@ -99,19 +93,14 @@ Message` column's http_request writes those cells via the cascade.
|
|
|
99
93
|
- Do NOT call `check_rubric`, `enrich_with_prospeo`, or
|
|
100
94
|
`bulk_enrich_with_prospeo` in the tail. Those are direct-API
|
|
101
95
|
mutation tools that fetch data to the caller without writing to
|
|
102
|
-
the workflow table cells. Step
|
|
96
|
+
the workflow table cells. Step 14's `queue_campaign_cells` call
|
|
103
97
|
triggers the column-level enrichment that populates those cells.
|
|
104
98
|
Running these tools in the tail produces duplicate cost with no
|
|
105
99
|
cell side effect.
|
|
106
|
-
- `
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
path, but use `wait_for_rubric_results({ targetCount: cohortSize })`
|
|
111
|
-
when the table-level wait returns before rubric cells finish. In
|
|
112
|
-
create-campaign-v2 Step 14, pass `minPassedCount: 1`; one passing
|
|
113
|
-
filtered row is enough to start observing Generate Message, even if
|
|
114
|
-
other sample rows are still processing.
|
|
100
|
+
- `wait_for_campaign_processing` is the stats-only observation helper for the
|
|
101
|
+
tail. In Step 14 pass `minPassedCount: 1`; in Step 15 pass
|
|
102
|
+
`minGeneratedMessages: 1` and `templateRevision: "current"`. Do not repoll
|
|
103
|
+
identical args after a timeout.
|
|
115
104
|
- `start_campaign` is FORBIDDEN in the autonomous tail. It belongs only
|
|
116
105
|
in the Claude-greenlight path, AFTER the user signals "start". See
|
|
117
106
|
`references/final-handoff-contract.md`.
|
|
@@ -121,15 +110,16 @@ Message` column's http_request writes those cells via the cascade.
|
|
|
121
110
|
- You MAY NOT call `start_campaign` while ANY passing row has an
|
|
122
111
|
empty `Generate Message` cell. If you discover empty message cells in
|
|
123
112
|
the greenlight turn, ABORT greenlight and return to Step 15 first.
|
|
124
|
-
- You MAY NOT call `attach_sequence` while ANY passing row has an
|
|
125
|
-
empty `Generate Message` cell. Before `attach_sequence`, call
|
|
126
|
-
`
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
113
|
+
- You MAY NOT call `attach_sequence` while ANY reviewed passing row has an
|
|
114
|
+
empty or stale `Generate Message` cell. Before `attach_sequence`, call
|
|
115
|
+
`select_campaign_cells({ tableId, columnRole: "generateMessage",
|
|
116
|
+
rowSelector: { type: "needsGeneratedMessage" } })`. If any cells are
|
|
117
|
+
selected, call `queue_campaign_cells` with the same selector and wait with
|
|
118
|
+
`wait_for_campaign_processing({ minGeneratedMessages: 1,
|
|
119
|
+
templateRevision: "current" })`. If rows truly won't message, ESCALATE.
|
|
120
|
+
- You MAY NOT advance past Step 14 without calling `queue_campaign_cells` for
|
|
121
|
+
`{ columnRole: "enrich", rowSelector: { type: "reviewBatch" } }`. Without it,
|
|
122
|
+
every downstream cell stays `pending` and the campaign ships empty.
|
|
133
123
|
- You MAY NOT queue enrichment for rows outside the configured internal
|
|
134
124
|
execution slice before the user approves expansion. Full-list enrichment/message
|
|
135
125
|
generation is a credit-spend decision and must happen after the user
|
|
@@ -141,9 +131,8 @@ All subsequent steps read the already-parsed config. Do not re-load mid-run,
|
|
|
141
131
|
and do not read repo-local config files; packaged Claude Code and Codex runs
|
|
142
132
|
must use the MCP asset loader.
|
|
143
133
|
Load each subskill prompt (`create-campaign-v2`, `research-sender`,
|
|
144
|
-
`generate-messages`) at most once per run.
|
|
145
|
-
|
|
146
|
-
chunks once and remember; do not restart the same prompt from offset 0 later.
|
|
134
|
+
`generate-messages`) at most once per run. If a tool result already told
|
|
135
|
+
you to load a prompt, remember it; do not restart the same prompt later.
|
|
147
136
|
|
|
148
137
|
After every `update_campaign({ currentStep: ... })` in the tail, narrate
|
|
149
138
|
what changed and orient the user to what the already-open app will show next —
|
|
@@ -265,16 +254,17 @@ searchId, targetLeadCount: 1000 })`.
|
|
|
265
254
|
the source list and `workflowTableId` is the campaign table.
|
|
266
255
|
4. `wait_for_campaign_table_ready` until the initial campaign-table execution slice rows are
|
|
267
256
|
available in the campaign table.
|
|
268
|
-
5.
|
|
269
|
-
15-row internal execution slice
|
|
257
|
+
5. Confirm the compact `reviewBatch` returned by `confirm_lead_list` has the
|
|
258
|
+
expected 15-row internal execution slice metadata. Do not fetch row payloads
|
|
259
|
+
or queue cells in Step 13.
|
|
270
260
|
6. If the import returns zero usable leads, ESCALATE per
|
|
271
261
|
`references/escalation-ladder.md` (hard fail).
|
|
272
262
|
7. `update_campaign({ campaignId, currentStep: "filter-choice" })`.
|
|
273
263
|
8. Describe the filter-choice screen now visible in the already-open app. Do not
|
|
274
264
|
repeat the watch URL.
|
|
275
265
|
|
|
276
|
-
**Do NOT call `check_rubric`, `
|
|
277
|
-
`
|
|
266
|
+
**Do NOT call `check_rubric`, `wait_for_campaign_processing`,
|
|
267
|
+
`queue_campaign_cells`, `enrich_with_prospeo`, or `bulk_enrich_with_prospeo` in Step 13.**
|
|
278
268
|
Those are direct-API tools that fetch enrichment/scoring data to the
|
|
279
269
|
caller or start the workflow-table cascade too early. The cascade starts in
|
|
280
270
|
Step 14 only after `save_rubrics` and `update_campaign_brief` have both
|
|
@@ -299,18 +289,16 @@ message is ready. It does NOT call `check_rubric`,
|
|
|
299
289
|
Shape:
|
|
300
290
|
|
|
301
291
|
```text
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
get_rows_minimal({ tableId: workflowTableId })
|
|
305
|
-
wait_for_rubric_results({ tableId: workflowTableId, targetCount: cohortSize, minPassedCount: 1, includeRows: false })
|
|
292
|
+
queue_campaign_cells({ tableId: workflowTableId, columnRole: "enrich", rowSelector: { type: "reviewBatch" } })
|
|
293
|
+
wait_for_campaign_processing({ tableId: workflowTableId, minPassedCount: 1 })
|
|
306
294
|
passInSample = count of first sampleSize review/process sample rows with passesRubric === true
|
|
307
295
|
projectedPass = round(passInSample / sampleSize * importLimit)
|
|
308
296
|
|
|
309
|
-
if
|
|
297
|
+
if wait_for_campaign_processing.ready === true and passRate.passed >= 1:
|
|
310
298
|
advance to Step 15 to observe or queue Generate Message for currently passing rows
|
|
311
299
|
do not wait for every sample row to finish before message generation starts
|
|
312
300
|
stop for review as soon as one passing generated message is ready
|
|
313
|
-
else if
|
|
301
|
+
else if wait_for_campaign_processing.ready === false and reason === "timeout":
|
|
314
302
|
use the partial passRate/stats as the sample diagnostic
|
|
315
303
|
if passRate.passed >= 1:
|
|
316
304
|
advance to Step 15 to observe/queue Generate Message for passing rows
|
|
@@ -322,9 +310,10 @@ else if wait_for_rubric_results.ready === false and reason === "timeout":
|
|
|
322
310
|
ask whether to revise source, revise filter/rubric, or wait once only if still processing
|
|
323
311
|
else:
|
|
324
312
|
diagnose brief-vs-list per sample-validation-loop.md
|
|
325
|
-
- brief: autonomous update_campaign_brief, then re-kick cascade
|
|
326
|
-
(
|
|
327
|
-
|
|
313
|
+
- brief: autonomous update_campaign_brief, then re-kick cascade with
|
|
314
|
+
queue_campaign_cells({ columnRole: "enrich", rowSelector:
|
|
315
|
+
{ type: "reviewBatch" }, forceRerun: true }) and wait for the
|
|
316
|
+
new results
|
|
328
317
|
- list: ESCALATE (no auto-revision of leads)
|
|
329
318
|
- unknown: ESCALATE
|
|
330
319
|
revisionRound += 1
|
|
@@ -361,8 +350,8 @@ Entered on `CampaignOffer.currentStep === "auto-execute-messaging"`.
|
|
|
361
350
|
|
|
362
351
|
**Messages are produced by the `Generate Message` column cascade, not
|
|
363
352
|
by a separate tool.** Do NOT call a `generate_messages` MCP tool —
|
|
364
|
-
that tool does not exist; `generate-messages`
|
|
365
|
-
|
|
353
|
+
that tool does not exist; the full `generate-messages` subskill prompt is the
|
|
354
|
+
Message Draft Builder contract for the approved template. In the autonomous tail, messages
|
|
366
355
|
flow through the column pipeline: `Enrich Prospect` →
|
|
367
356
|
`DNC Check` → `ICP Score` → `Passes Rubric` → `Generate Message`.
|
|
368
357
|
Each column's http_request auto-fires when its upstream dependency
|
|
@@ -373,21 +362,19 @@ the output — not to generate messages manually.
|
|
|
373
362
|
|
|
374
363
|
**What Step 15 does:**
|
|
375
364
|
|
|
376
|
-
1.
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
minPassedCount: 1, minMessagesCount: 1, includeRows: false })` until the
|
|
390
|
-
first passing generated message is ready. Do not wait for every passing row's
|
|
365
|
+
1. Before queueing or waiting on Generate Message cells, confirm the minted
|
|
366
|
+
campaign brief still contains `{{...}}` in `## Approved Message Template`.
|
|
367
|
+
If it does not, fail before the cascade runs. Do not repair after mint; the
|
|
368
|
+
template must be present during mint so Step 15 never starts in freeform
|
|
369
|
+
generation mode.
|
|
370
|
+
2. Use `select_campaign_cells({ tableId, columnRole: "generateMessage",
|
|
371
|
+
rowSelector: { type: "needsGeneratedMessage" } })` only if you need a dry-run
|
|
372
|
+
count. If any passing row has a pending, empty, or stale Generate Message
|
|
373
|
+
cell, queue it explicitly with `queue_campaign_cells({ tableId, columnRole:
|
|
374
|
+
"generateMessage", rowSelector: { type: "needsGeneratedMessage" } })`.
|
|
375
|
+
3. `wait_for_campaign_processing({ tableId, minGeneratedMessages: 1,
|
|
376
|
+
templateRevision: "current" })` until the first current-revision generated
|
|
377
|
+
message is ready. Do not wait for every passing row's
|
|
391
378
|
Generate Message cell, and do not add "one more wait" for a stronger sample.
|
|
392
379
|
Remaining review/process sample rows can continue processing in the background.
|
|
393
380
|
4. Read the first ready generated message back via `get_rows` (full) and
|
|
@@ -46,8 +46,8 @@ Reject the branch as blocked if the campaign id, selected lead list, workflow
|
|
|
46
46
|
table, or review-batch row ids/hash do not match. Do not reconstruct state from
|
|
47
47
|
stale local markdown artifacts or direct database reads.
|
|
48
48
|
|
|
49
|
-
Return
|
|
50
|
-
sample, concerns, status, basis token, output timestamp/hash, and error or
|
|
49
|
+
Return message-draft output only: template recommendation, token fill rules,
|
|
50
|
+
rendered sample, concerns, status, basis token, output timestamp/hash, and error or
|
|
51
51
|
retry detail. Do not write message cells, enrich rows, update the campaign
|
|
52
52
|
brief, attach sequence, or imply send readiness from this branch.
|
|
53
53
|
|