@sellable/mcp 0.1.84 → 0.1.86
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/campaigns.js
CHANGED
|
@@ -124,8 +124,8 @@ export const campaignToolDefinitions = [
|
|
|
124
124
|
{
|
|
125
125
|
name: "create_campaign",
|
|
126
126
|
description: 'Create a new campaign offer OR resume an existing one. Low-level write tool: load create-campaign workflow instructions first via get_subskill_prompt({ subskillName: "create-campaign" }) (or bootstrap_create_campaign + nextStep). If campaignId is provided, this tool returns the watchUrl + state for that campaign instead of creating a new campaign.\n\n' +
|
|
127
|
-
"INPUTS:\n- Pass either `clientProspectId` (preferred) or `senderLinkedinUrl` for net-new campaigns.\n- `clientProspectId` is the EnrichedProspect row ID for the campaign
|
|
128
|
-
"PREREQUISITES:\n- If you provide `senderLinkedinUrl`, it is used as a
|
|
127
|
+
"INPUTS:\n- Pass either `clientProspectId` (preferred) or `senderLinkedinUrl` for net-new campaigns.\n- `clientProspectId` is the EnrichedProspect row ID for the campaign identity/client prospect, used by ICP scoring, message generation, and brief rendering.\n- `senderLinkedinUrl` may be passed when `clientProspectId` is not yet materialized; the backend will attempt to resolve a campaign identity prospect from it.\n\n" +
|
|
128
|
+
"PREREQUISITES:\n- If you provide `senderLinkedinUrl`, it is used as a campaign identity bootstrap signal, not as a connected-sender availability check.\n- `clientProspectId` is optional when sender URL is available and allows a direct materialized campaign identity context.\n- `clientProspectId` is preferred for deterministic campaign identity resolution.",
|
|
129
129
|
inputSchema: {
|
|
130
130
|
type: "object",
|
|
131
131
|
properties: {
|
|
@@ -139,13 +139,13 @@ export const campaignToolDefinitions = [
|
|
|
139
139
|
},
|
|
140
140
|
clientProspectId: {
|
|
141
141
|
type: "string",
|
|
142
|
-
description: "Use this for existing
|
|
142
|
+
description: "Use this for existing campaign identity/client prospect rows. For brand-new campaign identities, pass `senderLinkedinUrl` and the backend will attempt prospect lookup/creation during campaign creation.",
|
|
143
143
|
},
|
|
144
144
|
// `senderLinkedinUrl` (not `linkedinUrl`) to disambiguate from
|
|
145
145
|
// prospect-side `linkedinUrl` fields used in other tools (research-prospect etc.).
|
|
146
146
|
senderLinkedinUrl: {
|
|
147
147
|
type: "string",
|
|
148
|
-
description: "Optional
|
|
148
|
+
description: "Optional campaign identity LinkedIn profile URL. Use when the client prospect ID is not yet materialized and backend bootstrap should be used.",
|
|
149
149
|
},
|
|
150
150
|
offerPositioning: {
|
|
151
151
|
type: "object",
|
|
@@ -623,7 +623,7 @@ export async function createCampaign(input) {
|
|
|
623
623
|
`or all create fields. Missing: ${missing.join(", ")}.\n\n` +
|
|
624
624
|
"Remediation:\n" +
|
|
625
625
|
'- For full workflow, call get_subskill_prompt({ subskillName: "create-campaign" }) and follow it.\n' +
|
|
626
|
-
"- For net-new campaign creation: run the `research-sender` flow first, call `complete_sender_research`, then pass either `clientProspectId` (preferred) or `senderLinkedinUrl` and let the backend resolve
|
|
626
|
+
"- For net-new campaign creation: run the `research-sender` flow first as campaign identity/client prospect research, call `complete_sender_research`, then pass either `clientProspectId` (preferred) or `senderLinkedinUrl` and let the backend resolve campaign identity context at campaign creation time.\n" +
|
|
627
627
|
"- For resume, call create_campaign with campaignId only.");
|
|
628
628
|
}
|
|
629
629
|
assertNetNewCreateCampaignResearchReady();
|
|
@@ -88,21 +88,21 @@ export function assertResearchPromptLoaded(mode = "any") {
|
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
90
|
if (mode === "sender") {
|
|
91
|
-
throw new Error('FLOW_PRECONDITION: net-new create_campaign requires
|
|
91
|
+
throw new Error('FLOW_PRECONDITION: net-new create_campaign requires campaign identity research preload. Call get_subskill_prompt({ subskillName: "research-sender" }) and run client/company prospect research before create_campaign.');
|
|
92
92
|
}
|
|
93
93
|
throw new Error("FLOW_PRECONDITION: research preload missing. Call get_subskill_prompt with the matching research wrapper (research-sender or research-prospect), then run research before continuing.");
|
|
94
94
|
}
|
|
95
95
|
export function assertNetNewCreateCampaignResearchReady() {
|
|
96
96
|
const senderResearchPromptState = researchPromptStates.sender;
|
|
97
97
|
if (!senderResearchPromptState) {
|
|
98
|
-
throw new Error('FLOW_PRECONDITION: net-new create_campaign requires
|
|
98
|
+
throw new Error('FLOW_PRECONDITION: net-new create_campaign requires campaign identity research preload. Call get_subskill_prompt({ subskillName: "research-sender" }) and run client/company prospect research before create_campaign.');
|
|
99
99
|
}
|
|
100
100
|
if (!senderResearchCompletionState) {
|
|
101
|
-
throw new Error("FLOW_PRECONDITION: completed
|
|
101
|
+
throw new Error("FLOW_PRECONDITION: completed campaign identity research is required before create_campaign. After running research-sender for client/company prospect context (parallel batch of fetch_linkedin_profile + fetch_company + WebSearches), call complete_sender_research({ depth, proofItemsFound, caseStudyItemsFound, credibilitySignalsFound, notes? }).");
|
|
102
102
|
}
|
|
103
103
|
if (new Date(senderResearchCompletionState.completedAt).getTime() <
|
|
104
104
|
new Date(senderResearchPromptState.loadedAt).getTime()) {
|
|
105
|
-
throw new Error("FLOW_PRECONDITION:
|
|
105
|
+
throw new Error("FLOW_PRECONDITION: campaign identity research completion is older than the latest research-sender prompt load. Re-run client/company prospect research and call complete_sender_research again.");
|
|
106
106
|
}
|
|
107
107
|
return {
|
|
108
108
|
ok: true,
|
package/package.json
CHANGED
|
@@ -227,17 +227,19 @@ Validated draft directory:
|
|
|
227
227
|
three specific options plus `Other / custom`. Do not ask this as plain
|
|
228
228
|
numbered chat.
|
|
229
229
|
|
|
230
|
-
Then run one lightweight
|
|
231
|
-
|
|
230
|
+
Then run one lightweight campaign identity research pass before campaign
|
|
231
|
+
shell creation. The legacy subskill/tool names are `research-sender` and
|
|
232
|
+
`complete_sender_research`, but treat them as client/company prospect research
|
|
233
|
+
for the campaign, not as a connected-sender availability check. Prefer loading
|
|
232
234
|
`get_subskill_prompt({ subskillName: "research-sender" })`, running its
|
|
233
235
|
fetch/profile/company/WebSearch batch when those tools are exposed, and
|
|
234
236
|
calling `complete_sender_research`. If WebSearch is unavailable, use the
|
|
235
237
|
available MCP profile/company/post tools, call `complete_sender_research`
|
|
236
|
-
with the evidence counts found, and continue with explicit gaps.
|
|
237
|
-
questions should use the confirmed company context and
|
|
238
|
-
positioning options so they do not feel generic. If
|
|
239
|
-
unavailable, use neutral/custom intake options instead of
|
|
240
|
-
vertical-specific options.
|
|
238
|
+
with the evidence counts found, and continue with explicit gaps. Setup
|
|
239
|
+
questions and the campaign brief should use the confirmed company context and
|
|
240
|
+
researched proof / positioning options so they do not feel generic. If
|
|
241
|
+
identity is still unavailable, use neutral/custom intake options instead of
|
|
242
|
+
guessed vertical-specific options.
|
|
241
243
|
|
|
242
244
|
- Before the identity gate, use this customer-facing shape:
|
|
243
245
|
|
|
@@ -299,6 +301,14 @@ Validated draft directory:
|
|
|
299
301
|
Do not mention internal artifact names, local folders, shell commands, or
|
|
300
302
|
persistence in this preamble.
|
|
301
303
|
|
|
304
|
+
Before calling `create_campaign` for the watchable shell, make sure the
|
|
305
|
+
campaign identity research marker is satisfied: load `research-sender` if it
|
|
306
|
+
has not already been loaded, do the lightweight profile/company lookup, and
|
|
307
|
+
call `complete_sender_research`. This is still client/company prospect context
|
|
308
|
+
for the campaign brief. Do not call `list_senders`, do not ask the user to
|
|
309
|
+
connect a sender, and do not surface sender availability until
|
|
310
|
+
Settings/final handoff.
|
|
311
|
+
|
|
302
312
|
- Before asking for brief approval, render a slim approval brief in the chat and
|
|
303
313
|
keep the rich current brief in `brief.md`. Use rendered Markdown directly:
|
|
304
314
|
`##` heading, bold field labels, numbered section labels, and short bullets.
|
|
@@ -353,9 +363,9 @@ Validated draft directory:
|
|
|
353
363
|
`RevOps`, `outbound systems`, or `pipeline architecture` unless they are
|
|
354
364
|
translated into what the user can understand.
|
|
355
365
|
|
|
356
|
-
After rendering that brief, ask for brief approval
|
|
357
|
-
|
|
358
|
-
|
|
366
|
+
After rendering that brief, ask for brief approval before lead sourcing in
|
|
367
|
+
hosted net-new runs. The user-facing choice should be approve/revise language,
|
|
368
|
+
not "looks good".
|
|
359
369
|
Approval options should refer to what the user just read, e.g. `Approve this
|
|
360
370
|
brief`, `Revise target`, `Revise offer/proof`, and `Other / custom`.
|
|
361
371
|
When the user chose `Find people for me` and the host supports subagents,
|
|
@@ -379,13 +389,13 @@ brief`, `Revise target`, `Revise offer/proof`, and `Other / custom`.
|
|
|
379
389
|
the visible approved brief; if the sidecar is still running, wait quietly or
|
|
380
390
|
finish the write synchronously at that boundary.
|
|
381
391
|
|
|
382
|
-
- After the brief is approved
|
|
392
|
+
- After the brief is approved, show the next progress line:
|
|
383
393
|
`Cool. Now I'm going to find people who are both a good fit and active enough
|
|
384
394
|
to be worth a LinkedIn test. I'll compare source paths by expected volume,
|
|
385
395
|
sampled ICP fit, activity/warmth signal, cleanup risk, and tradeoffs. This
|
|
386
396
|
usually takes ~3-5 min, and I'll show you the source decision + sample before
|
|
387
397
|
anything goes live.`
|
|
388
|
-
- After the lead sample/source decision is ready and approved
|
|
398
|
+
- After the lead sample/source decision is ready and approved,
|
|
389
399
|
show the next progress line:
|
|
390
400
|
`Lead source is set. I'll import the first 10-row review batch into the
|
|
391
401
|
campaign table, then use that same batch to tighten the fit filter and draft the
|
|
@@ -544,8 +554,14 @@ workstreams`, `in parallel`, or `background` unless parallel branches were
|
|
|
544
554
|
|
|
545
555
|
<conditional_gates>
|
|
546
556
|
|
|
547
|
-
There are
|
|
557
|
+
There are four customer-visible gates in the net-new hosted flow:
|
|
548
558
|
|
|
559
|
+
- `brief-review` asks whether to approve the rendered campaign brief before
|
|
560
|
+
lead sourcing starts. Do not skip this by inferring approval from setup
|
|
561
|
+
answers.
|
|
562
|
+
- `lead-review` / source decision asks whether to approve the selected source
|
|
563
|
+
path and sample before moving into filter/message work. Do not skip this by
|
|
564
|
+
compressing the source decision into message review.
|
|
549
565
|
- `message-review` is the mandatory pre-import message quality gate. It asks
|
|
550
566
|
only whether to proceed with the selected message (`approve-message`) or run
|
|
551
567
|
one more messaging revision (`revise-messaging`). `approve-message` authorizes
|
|
@@ -112,23 +112,20 @@
|
|
|
112
112
|
"label": "Campaign brief interview",
|
|
113
113
|
"onEnter": [
|
|
114
114
|
{
|
|
115
|
-
"action": "
|
|
115
|
+
"action": "resolve_campaign_identity_before_strategy_packet",
|
|
116
116
|
"optional": false,
|
|
117
117
|
"when": "before_founder_strategy_source_packet",
|
|
118
118
|
"allowedTools": [
|
|
119
|
-
"list_senders",
|
|
120
|
-
"get_sender",
|
|
121
119
|
"fetch_company",
|
|
122
120
|
"fetch_linkedin_profile",
|
|
123
121
|
"WebFetch",
|
|
124
122
|
"WebSearch"
|
|
125
123
|
],
|
|
126
124
|
"maxCalls": {
|
|
127
|
-
"list_senders": 1,
|
|
128
125
|
"identityLookup": 1,
|
|
129
126
|
"companyLookupAfterIdentityConfirm": 1
|
|
130
127
|
},
|
|
131
|
-
"quietlyCheckConnectedSendersFirst":
|
|
128
|
+
"quietlyCheckConnectedSendersFirst": false,
|
|
132
129
|
"preferUserLinkedInWhenSenderInferenceIsWeak": true,
|
|
133
130
|
"doNotPresentSenderPickerBeforeIdentityInference": true,
|
|
134
131
|
"confirmCompanyBeforeStrategyQuestions": true,
|
|
@@ -149,7 +146,7 @@
|
|
|
149
146
|
"chatRenderRule": "If the sender/company context has multiple campaignable product lines, services, or offers, ask exactly one single-choice structured campaign-focus question before buyer/offer/proof/source. Do not ask numbered plain-chat choices when the structured question tool is available. Do not use checkbox or multi-select wording."
|
|
150
147
|
},
|
|
151
148
|
{
|
|
152
|
-
"action": "
|
|
149
|
+
"action": "run_campaign_identity_research_before_strategy_packet",
|
|
153
150
|
"target": "research-sender",
|
|
154
151
|
"when": "after_identity_and_campaign_focus_before_founder_strategy_source_packet",
|
|
155
152
|
"allowedTools": [
|
|
@@ -316,8 +313,6 @@
|
|
|
316
313
|
"get_subskill_asset",
|
|
317
314
|
"get_auth_status",
|
|
318
315
|
"get_active_workspace",
|
|
319
|
-
"list_senders",
|
|
320
|
-
"get_sender",
|
|
321
316
|
"complete_sender_research",
|
|
322
317
|
"fetch_company",
|
|
323
318
|
"fetch_company_posts",
|