@sellable/mcp 0.1.209 → 0.1.211

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 CHANGED
@@ -262,11 +262,10 @@ Provider preflight contract:
262
262
 
263
263
  Parallel execution contract:
264
264
 
265
- - Source scout names come from `agents/registry.json` and are exposed at runtime
266
- through `get_source_scout_registry`. Add new scouts there first; installer,
267
- Codex config, Claude agent files, and prompts can consume the registry for
268
- explicit source-comparison/debug runs. Normal create-campaign source work
269
- stays inline in the parent thread with the active provider MCP tools.
265
+ - Source work stays inline in the parent thread with active provider MCP tools.
266
+ `get_source_scout_registry` intentionally returns no custom source-scout
267
+ agents in the normal package; do not add source scouts back without an
268
+ explicit source-comparison/debug requirement.
270
269
  - `get_post_find_leads_scout_registry` returns only the Message Drafting worker
271
270
  for normal create-campaign runs. After `confirm_lead_list` imports a non-empty
272
271
  review batch and rows are proven, ask the filter-choice question immediately.
@@ -17,20 +17,19 @@ campaign state. The main thread owns approval and campaign writes.
17
17
 
18
18
  ## Source Of Truth
19
19
 
20
- Use the live campaign inputs supplied by the parent thread:
20
+ Use the lean handoff supplied by the parent thread:
21
21
 
22
22
  - `campaignId`
23
- - campaign revision or `campaignUpdatedAt`
24
- - campaign brief summary from the parent, then the current `campaignBrief` /
25
- campaign brief content loaded through Sellable tools
26
- - selected source decision and provider state
27
- - `selectedLeadListId` or selected source list context
28
23
  - `workflowTableId`
29
- - compact initial campaign-table execution-slice basis: copied source row count,
30
- review-batch row count, and review-batch basis/hash. Do not require the
31
- parent to paste a long review-batch row-id list.
32
- - filter basis at branch start: `pending`, `use-filters`, or `skip-filters`
33
- - any already-saved fit/rubric result summaries supplied by the parent
24
+ - concise brief summary: offer, buyer, source context, safe proof, blocked claims
25
+ - concise source summary and source-use rule
26
+ - 3-5 sample workflow-table rows with `rowId`, name, title, company, and short
27
+ signal
28
+ - optional `campaignName`, `selectedLeadListId`, and filter choice
29
+
30
+ Do not require campaign revision, brief hash, copied row count, review-batch
31
+ count, row hash, or a long review-batch row-id list. The branch can verify the
32
+ current state with live Sellable tools.
34
33
 
35
34
  Do not require or hunt for `brief.md`, `lead-review.md`, or `lead-sample.json`.
36
35
  Those files are optional debug context only when the parent explicitly provides
@@ -40,34 +39,47 @@ read stale local markdown files to reconstruct campaign state.
40
39
  All live reads must come from scoped MCP/product tools by campaign and
41
40
  workspace, such as `get_campaign`, `get_campaign_context`, and
42
41
  `get_rows_minimal({ tableId: workflowTableId })`, or from equivalent parent
43
- thread payloads. Load the current campaign brief and the current review-batch
44
- rows from those tools before drafting. Reject the task as `blocked` if the
45
- campaign id, workspace, `selectedLeadListId`, `workflowTableId`, review-batch
46
- row count, or review-batch basis/hash does not match the branch input.
42
+ thread payloads. Load the current campaign brief/context and use the provided
43
+ sample rows as the drafting sample. Reject the task as `blocked` if the campaign
44
+ id, workspace, or `workflowTableId` does not match the branch input.
45
+
46
+ Reference assets are packaged MCP assets. Load them only through
47
+ `get_subskill_asset`; do not use shell commands, local `Read`, `rg`, `cat`,
48
+ `wc`, plugin-cache paths, repo paths, or `/Users/...` paths to find them. If a
49
+ required message reference cannot be loaded through the MCP asset loader, return
50
+ `blocked` or `retry-needed`.
47
51
 
48
- ## Required First Steps
52
+ ## Required Work Loop
49
53
 
50
- 1. Load the current campaign brief and compact campaign/table state:
51
- campaign name, `campaignId`, `selectedLeadListId`, `workflowTableId`,
52
- copied source row count, review-batch row count, review-batch basis/hash,
53
- selected source summary, filter choice, and parent-supplied brief summary.
54
- Then read the current campaign/table state through scoped Sellable tools.
55
- Do not ask the parent to paste row data or a long row-id list.
56
- 2. Load the full normal-path message prompt:
54
+ 1. Load live campaign context:
55
+ `get_campaign`, `get_campaign_context`, and row details for the provided
56
+ sample row ids when available. Do not ask the parent for hashes or row-count
57
+ bookkeeping.
58
+ 2. Load the full normal-path message prompt, all chunks:
57
59
 
58
60
  `get_subskill_prompt({ subskillName: "generate-messages" })`
59
61
 
60
- 3. Load the reference assets required by that prompt's Reference Asset Loading
61
- pack, including validation/QA criteria. If a required validation asset cannot
62
- load, return `blocked` or `retry-needed` instead of drafting from memory.
63
- 4. Use that prompt as the drafting and QA contract. Do not use any alternate
64
- prompt, examples-only shortcut, or create-campaign safety/checklist
65
- instructions as a substitute for the full message prompt.
66
- 5. Draft only from the current campaign brief, selected source context, and
67
- review-batch rows loaded through scoped Sellable tools.
68
- 6. Run the normal message validation/QA rules before final return. The final
69
- recommendation must include the QA pass/fail receipt, not just draft copy.
70
- 7. Keep the work provisional until the user chooses `Use Template` in Messages.
62
+ 3. Load every packaged reference asset required by that prompt's Reference Asset
63
+ Loading section with `get_subskill_asset`. If a required asset cannot load
64
+ through the MCP asset loader, return `blocked` or `retry-needed` instead of
65
+ drafting from memory.
66
+ 4. Build positioning in a compact working note: buyer, pain, product, mechanism,
67
+ proof boundary, source-use rule, and CTA.
68
+ 5. Draft 3 distinct first-message options: signal-led, product/mechanism-led,
69
+ and proof/credibility-led.
70
+ 6. Combine the strongest opener, product line, mechanism line, proof treatment,
71
+ and CTA into one reusable template.
72
+ 7. Define token fill rules and fallbacks, then render one good sample.
73
+ 8. Before returning, load the validation prompt:
74
+
75
+ `get_subskill_prompt({ subskillName: "create-campaign-v2-validation" })`
76
+
77
+ Use it only as a validation contract for the candidate message in this live
78
+ campaign branch. Do not write dry-mode artifacts. Run the final candidate
79
+ against token safety, proof safety, source safety, Thomas filters, AI tells,
80
+ single-send rule, and "would I take this call?". If it fails, revise once and
81
+ validate again.
82
+ 9. Keep the work provisional until the user chooses `Use Template` in Messages.
71
83
 
72
84
  ## Owned Output
73
85
 
@@ -76,15 +88,14 @@ Return the following to the parent thread:
76
88
  - proposed first-message template using supported `{{...}}` tokens
77
89
  - token fill rules and fallbacks
78
90
  - one rendered good-fill sample for a plausible passing campaign-table row
79
- - one omit/fallback sample when the row signal is not safe
80
- - pass/fail notes against the generate-messages quality gates
81
- - `qaReceipt` / validation notes showing token safety, proof safety, fit
82
- uncertainty, bad-fill avoidance, and "would I take this call?" judgment
83
91
  - message-draft runtime status: `ready`, `blocked`, `retry-needed`, or `stale`
84
- - basis token containing campaign revision/updatedAt, brief hash,
85
- `selectedLeadListId`, `workflowTableId`, compact execution-slice row
86
- count/hash, filter choice, and rubric/filter basis when present
87
- - output timestamp/hash and any retry/error detail
92
+ - approve-or-revise recommendation
93
+ - validation status only: `passed`, `revised-then-passed`, or `blocked`
94
+ - output timestamp/hash and any blocked/retry detail
95
+
96
+ Do not return or render `renderedFallbackSample`, `concerns`, or a full
97
+ `qaReceipt` in the normal happy path. Validation is an internal gate; summarize
98
+ only if blocked or retry-needed.
88
99
 
89
100
  Do not write local markdown/json artifacts in normal live campaign runs. Return
90
101
  the recommendation directly to the parent thread. Emit debug artifacts only when
@@ -108,23 +119,21 @@ exists and points at the current non-empty campaign-table execution slice.
108
119
  ## Basis Changes And Rewrites
109
120
 
110
121
  The first completed recommendation is the default message review candidate.
111
- Do not automatically retry or regenerate only because Prospect Filters finished,
112
- rubrics were saved, Filter Leads completed, enrichment cells populated, or more
113
- row data became available after this branch started.
114
-
115
- Treat later filter/enrichment data as optional rewrite context. If campaign id,
116
- brief hash, selected source, `selectedLeadListId`, `workflowTableId`, and
117
- execution-slice row count/hash still match, keep the initial recommendation usable
118
- and report `status: ready` with `basisStatus: "usable_initial"` or
122
+ Do not automatically retry or regenerate only because filters were saved, Filter
123
+ Leads completed, enrichment cells populated, or more row data became available
124
+ after this branch started.
125
+
126
+ Treat later filter/enrichment data as optional rewrite context. If campaign id
127
+ and `workflowTableId` still match, keep the initial recommendation usable and
128
+ report `status: ready` with `basisStatus: "usable_initial"` or
119
129
  `"enriched_rewrite_available"`. The parent thread may offer the user a choice
120
130
  to keep the initial draft or rewrite with enriched/filter data, but the rewrite
121
131
  must be explicit user opt-in.
122
132
 
123
133
  Retry or regenerate without asking only when the initial recommendation is
124
134
  missing, failed, structurally invalid, unsafe, or mismatched on campaign id,
125
- brief hash, selected source, `selectedLeadListId`, `workflowTableId`, or
126
- execution-slice row count/hash. Filter/rubric/enrichment basis drift alone is not a stale
127
- blocker.
135
+ `workflowTableId`, or provided sample rows. Filter/rubric/enrichment basis drift
136
+ alone is not a stale blocker.
128
137
 
129
138
  ## User Revision Feedback And QA
130
139
 
@@ -132,17 +141,19 @@ If the parent sends user feedback, a QA request, or a rewrite request about the
132
141
  template before `approve-message`, treat it as Message Drafting work, not a
133
142
  campaign write. Use the current `messageDraftRecommendation`, basis token/hash,
134
143
  campaign/table basis, and latest user feedback as inputs. Load or reuse the full
135
- `generate-messages` contract and validation assets, then return a revised or
136
- QA-only recommendation with:
144
+ `generate-messages` contract, all referenced assets, and
145
+ `create-campaign-v2-validation`, then return a revised or QA-only recommendation
146
+ with:
137
147
 
138
148
  - revised proposed template
139
149
  - what changed and why
140
150
  - token fill rule/fallback changes, if any
141
151
  - one rendered good-fill sample
142
- - `qaReceipt` with pass/fail notes against the loaded validation assets
143
- - pass/fail notes against the same quality gates
152
+ - validation status: `passed`, `revised-then-passed`, or `blocked`
144
153
  - updated output timestamp/hash and basis token
145
154
 
155
+ Do not return a full QA receipt unless the result is blocked or retry-needed.
156
+
146
157
  Keep the revision grounded in the same source/list/table rows unless the parent
147
158
  explicitly supplies a new selected list or review slice. Do not call
148
159
  `update_campaign_brief`, do not persist the template, and do not approve your
@@ -201,20 +212,18 @@ enablement`, or omit it.
201
212
  Return a concise status with:
202
213
 
203
214
  - prompt basis loaded: `generate-messages`
215
+ - validation prompt loaded: `create-campaign-v2-validation`
204
216
  - live campaign basis used
205
217
  - proposed template
206
218
  - token fill rules/fallbacks
207
219
  - one rendered passing-row sample
208
- - one rendered omit/fallback sample
209
- - quality-gate pass/fail summary
210
220
  - whether final template review is ready or needs revision
211
221
 
212
222
  When the parent will show the recommendation in chat, keep the customer-facing
213
223
  message review lightweight. Format only the approval target and one strong
214
- good-fill example as Markdown with distinct copy blocks. Keep token rules,
215
- omit/fallback examples, and bad-fill avoidance notes in your internal
216
- recommendation so the parent can persist them to the campaign brief after
217
- approval; do not print them in the default chat approval packet.
224
+ good-fill example as Markdown with distinct copy blocks. Keep validation notes,
225
+ bad-fill avoidance notes, and QA details internal unless blocked; do not print
226
+ them in the default chat approval packet.
218
227
 
219
228
  ````markdown
220
229
  ## Message Template
@@ -1,199 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
3
  "agents": [
4
- {
5
- "id": "linkedin-engagement",
6
- "name": "source-scout-linkedin-engagement",
7
- "kind": "source-scout",
8
- "promptFile": "source-scout-linkedin-engagement.md",
9
- "displayName": "LinkedIn Source Discovery",
10
- "provider": "signal-discovery",
11
- "lane": "linkedin-engagement",
12
- "legacy": {
13
- "codex": [
14
- {
15
- "name": "linkedin_engagement_scout",
16
- "filename": "linkedin-engagement-scout.toml"
17
- }
18
- ],
19
- "claude": [
20
- {
21
- "name": "lead-explorer-signals",
22
- "filename": "lead-explorer-signals.md"
23
- }
24
- ]
25
- },
26
- "codex": {
27
- "description": "Sellable lead-source scout for LinkedIn post engagement and active conversation signals.",
28
- "model": "gpt-5.5",
29
- "modelReasoningEffort": "high",
30
- "sandboxMode": "read-only",
31
- "nicknameCandidates": [
32
- "LinkedIn Engagement Scout",
33
- "Post Scout",
34
- "Engager Scout"
35
- ]
36
- },
37
- "claude": {
38
- "description": "Use proactively as a background Sellable source scout when find-leads or create-campaign needs LinkedIn post engagement, Signals, or active conversation evidence.",
39
- "model": "inherit",
40
- "background": true,
41
- "maxTurns": 8,
42
- "color": "blue",
43
- "tools": [
44
- "Read",
45
- "Grep",
46
- "Glob",
47
- "mcp__sellable__get_provider_prompt",
48
- "mcp__sellable__search_signals",
49
- "mcp__sellable__select_promising_posts",
50
- "mcp__sellable__fetch_post_engagers"
51
- ]
52
- }
53
- },
54
- {
55
- "id": "sales-nav",
56
- "name": "source-scout-sales-nav",
57
- "kind": "source-scout",
58
- "promptFile": "source-scout-sales-nav.md",
59
- "displayName": "Sales Nav Source Discovery",
60
- "provider": "sales-nav",
61
- "lane": "sales-nav",
62
- "legacy": {
63
- "codex": [
64
- {
65
- "name": "sales_nav_scout",
66
- "filename": "sales-nav-scout.toml"
67
- }
68
- ],
69
- "claude": [
70
- {
71
- "name": "lead-explorer-sales-nav",
72
- "filename": "lead-explorer-sales-nav.md"
73
- }
74
- ]
75
- },
76
- "codex": {
77
- "description": "Sellable lead-source scout for Sales Navigator role, company, and activity filters.",
78
- "model": "gpt-5.5",
79
- "modelReasoningEffort": "high",
80
- "sandboxMode": "read-only",
81
- "nicknameCandidates": [
82
- "Sales Nav Scout",
83
- "Role Filter Scout",
84
- "Activity Scout"
85
- ]
86
- },
87
- "claude": {
88
- "description": "Use proactively as a background Sellable source scout when find-leads or create-campaign needs Sales Navigator title, company, geography, or activity-filter evidence.",
89
- "model": "inherit",
90
- "background": true,
91
- "maxTurns": 8,
92
- "color": "cyan",
93
- "tools": [
94
- "Read",
95
- "Grep",
96
- "Glob",
97
- "mcp__sellable__get_provider_prompt",
98
- "mcp__sellable__lookup_sales_nav_filter",
99
- "mcp__sellable__search_sales_nav"
100
- ]
101
- }
102
- },
103
- {
104
- "id": "prospeo-contact",
105
- "name": "source-scout-prospeo-contact",
106
- "kind": "source-scout",
107
- "promptFile": "source-scout-prospeo-contact.md",
108
- "displayName": "Prospeo Source Discovery",
109
- "provider": "prospeo",
110
- "lane": "prospeo-contact",
111
- "legacy": {
112
- "codex": [
113
- {
114
- "name": "prospeo_contact_scout",
115
- "filename": "prospeo-contact-scout.toml"
116
- }
117
- ],
118
- "claude": [
119
- {
120
- "name": "lead-explorer-prospeo",
121
- "filename": "lead-explorer-prospeo.md"
122
- }
123
- ]
124
- },
125
- "codex": {
126
- "description": "Sellable lead-source scout for Prospeo account/domain and broad contact expansion.",
127
- "model": "gpt-5.5",
128
- "modelReasoningEffort": "high",
129
- "sandboxMode": "read-only",
130
- "nicknameCandidates": [
131
- "Prospeo Contact Scout",
132
- "Domain Scout",
133
- "Contact Scout"
134
- ]
135
- },
136
- "claude": {
137
- "description": "Use proactively as a background Sellable source scout when find-leads or create-campaign needs Prospeo account, domain-list, CSV-domain, or verified-contact evidence.",
138
- "model": "inherit",
139
- "background": true,
140
- "maxTurns": 8,
141
- "color": "green",
142
- "tools": [
143
- "Read",
144
- "Grep",
145
- "Glob",
146
- "mcp__sellable__get_provider_prompt",
147
- "mcp__sellable__load_csv_domains",
148
- "mcp__sellable__save_domain_filters",
149
- "mcp__sellable__search_prospeo"
150
- ]
151
- }
152
- },
153
- {
154
- "id": "filter-leads",
155
- "name": "post-find-leads-filter-scout",
156
- "kind": "post-find-leads-scout",
157
- "promptFile": "post-find-leads-filter-scout.md",
158
- "displayName": "Prospect Filters",
159
- "target": "filter-leads",
160
- "inputs": [
161
- "campaignId",
162
- "campaignBrief summary plus branch-loaded current campaignBrief",
163
- "source decision and selectedLeadList/source state",
164
- "workflowTableId",
165
- "compact review-batch count/hash or branch-loaded review rows"
166
- ],
167
- "producesArtifacts": [],
168
- "optionalProducesArtifacts": [],
169
- "ownership": "prospect-fit criteria, false-positive patterns, keep/exclude rules, ability-to-pay checks, and production rubric translation only",
170
- "codex": {
171
- "description": "Prospect Filters worker for campaign-backed fit criteria and rubric persistence after review-batch import and filter approval.",
172
- "model": "gpt-5.5",
173
- "modelReasoningEffort": "high",
174
- "sandboxMode": "read-only",
175
- "nicknameCandidates": [
176
- "Prospect Filters",
177
- "Fit Criteria",
178
- "Rubric Builder"
179
- ]
180
- },
181
- "claude": {
182
- "description": "Use proactively as Prospect Filters after review-batch import and filter approval to persist campaign rubrics from campaign state.",
183
- "model": "inherit",
184
- "background": true,
185
- "maxTurns": 8,
186
- "color": "yellow",
187
- "tools": [
188
- "mcp__sellable__get_subskill_prompt",
189
- "mcp__sellable__get_subskill_asset",
190
- "mcp__sellable__get_campaign",
191
- "mcp__sellable__get_campaign_context",
192
- "mcp__sellable__get_rows_minimal",
193
- "mcp__sellable__save_rubrics"
194
- ]
195
- }
196
- },
197
4
  {
198
5
  "id": "message-generation",
199
6
  "name": "post-find-leads-message-scout",
@@ -203,16 +10,17 @@
203
10
  "target": "generate-messages",
204
11
  "inputs": [
205
12
  "campaignId",
206
- "campaignBrief",
207
- "selected source state",
208
- "selectedLeadListId",
209
13
  "workflowTableId",
210
- "imported review-batch rows"
14
+ "concise brief summary",
15
+ "concise source summary/source-use rule",
16
+ "3-5 sample workflow-table rows",
17
+ "optional campaignName, selectedLeadListId, filter choice"
211
18
  ],
212
19
  "producesArtifacts": [
213
20
  "template recommendation",
214
21
  "token fill rules",
215
- "rendered sample"
22
+ "one rendered good-fill sample",
23
+ "validation status"
216
24
  ],
217
25
  "optionalProducesArtifacts": [],
218
26
  "ownership": "message strategy, proof inventory, token rules, skeptical-prospect review, and selected winner only",
@@ -234,8 +42,8 @@
234
42
  "maxTurns": 10,
235
43
  "color": "magenta",
236
44
  "tools": [
237
- "Read",
238
45
  "mcp__sellable__get_subskill_prompt",
46
+ "mcp__sellable__get_subskill_asset",
239
47
  "mcp__sellable__get_campaign",
240
48
  "mcp__sellable__get_campaign_context",
241
49
  "mcp__sellable__get_rows_minimal"
@@ -660,12 +660,10 @@ function analyzeFilterChoiceSample(sampleRows) {
660
660
  const fitRatio = fitCount / sampleCount;
661
661
  const cleanupRatio = cleanupCount / sampleCount;
662
662
  const adjacentRatio = adjacentIndexes.length / sampleCount;
663
- const maxCleanRiskCount = Math.max(2, Math.floor(sampleCount * 0.12));
664
- const recommendation = fitRatio >= 0.75 &&
665
- cleanupCount <= maxCleanRiskCount &&
666
- adjacentRatio <= 0.3
663
+ const allReviewedRowsLookFit = fitCount === sampleCount && cleanupCount === 0;
664
+ const recommendation = allReviewedRowsLookFit && adjacentRatio <= 0.3
667
665
  ? "skip"
668
- : fitRatio >= 0.6 && cleanupRatio <= 0.3
666
+ : fitRatio >= 0.75 && cleanupRatio <= 0.3
669
667
  ? "light-filter"
670
668
  : "add-filter";
671
669
  return {
@@ -697,17 +695,20 @@ function buildFilterChoiceWatchNarration({ sourceLeadCount, reviewRowCount, samp
697
695
  : "Sample rows were not available to inspect, so this is a manual filter decision.";
698
696
  const fitExamples = formatExamples("Fit examples:", assessment.strongExamples);
699
697
  const riskExamples = formatExamples("Cleanup examples:", assessment.riskExamples);
698
+ const lightFilterReason = cleanupCount > 0
699
+ ? `${cleanupCount} of ${assessment.sampleCount} need cleanup or manual review`
700
+ : `${assessment.adjacentCount} of ${assessment.sampleCount} look adjacent instead of clear target-role fits`;
700
701
  const headline = assessment.recommendation === "skip"
701
- ? "Filtering may not be needed"
702
+ ? "Filters optional for clean sample"
702
703
  : assessment.recommendation === "light-filter"
703
704
  ? "I recommend light cleanup filters"
704
705
  : assessment.recommendation === "manual-review"
705
706
  ? "Review filters or skip"
706
707
  : "I recommend adding filters";
707
708
  const agentIntent = assessment.recommendation === "skip"
708
- ? `Codex is not recommending filters because the sample already looks clean. ${fitExamples} Add filters only for a narrow exclusion.`
709
+ ? `Codex is not recommending filters because every reviewed row already looks like a target-role fit. ${fitExamples} Add filters only if the user wants an extra narrow exclusion.`
709
710
  : assessment.recommendation === "light-filter"
710
- ? `Codex is recommending light filters because most rows fit, but ${cleanupCount} of ${assessment.sampleCount} look adjacent, unknown, or risky. ${riskExamples}`
711
+ ? `Codex is recommending light filters because the sample is strong but not perfectly clean: ${lightFilterReason}. ${riskExamples}`
711
712
  : assessment.recommendation === "manual-review"
712
713
  ? "Codex could not inspect the review rows, so it is pausing for a manual filter decision instead of assuming filters are required."
713
714
  : `Codex is recommending filters because ${cleanupCount} of ${assessment.sampleCount} sampled rows look adjacent, unknown, or risky before message review. ${riskExamples}`;