@sellable/mcp 0.1.952 → 0.1.953
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.d.ts +20 -0
- package/dist/tools/campaigns.js +14 -1
- package/dist/tools/navigation.d.ts +3 -0
- package/dist/tools/navigation.js +4 -0
- package/dist/tools/registry.d.ts +18 -0
- package/package.json +1 -1
- package/skills/create-campaign-v2/SKILL.md +7 -0
- package/skills/create-campaign-v2/references/filter-leads.md +3 -1
- package/skills/create-campaign-v2/references/message-approach.md +70 -0
- package/skills/create-campaign-v2/references/sample-validation-loop.md +2 -1
- package/skills/create-campaign-v2-tail/SKILL.md +9 -1
- package/skills/first-campaign-onboarding/SKILL.md +34 -10
|
@@ -141,6 +141,7 @@ export interface CreateCampaignInput {
|
|
|
141
141
|
offerPositioning?: unknown;
|
|
142
142
|
campaignBrief?: string;
|
|
143
143
|
messageGenerationMode?: "template" | "ai-generated";
|
|
144
|
+
messageApproach?: "conversation" | "message_test";
|
|
144
145
|
currentStep?: string | null;
|
|
145
146
|
watchNarration?: unknown;
|
|
146
147
|
leadSourceType?: string | null;
|
|
@@ -167,6 +168,7 @@ export interface UpdateCampaignInput {
|
|
|
167
168
|
interactionMode?: InteractionMode;
|
|
168
169
|
enableICPFilters?: boolean | null;
|
|
169
170
|
useMessagingTemplate?: boolean | null;
|
|
171
|
+
messageApproach?: "conversation" | "message_test";
|
|
170
172
|
autoApproveFollowups?: boolean;
|
|
171
173
|
enableFollowUps?: boolean;
|
|
172
174
|
senderIds?: string[];
|
|
@@ -251,6 +253,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
251
253
|
offerPositioning?: undefined;
|
|
252
254
|
campaignBrief?: undefined;
|
|
253
255
|
messageGenerationMode?: undefined;
|
|
256
|
+
messageApproach?: undefined;
|
|
254
257
|
currentStep?: undefined;
|
|
255
258
|
watchNarration?: undefined;
|
|
256
259
|
leadSourceType?: undefined;
|
|
@@ -300,6 +303,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
300
303
|
offerPositioning?: undefined;
|
|
301
304
|
campaignBrief?: undefined;
|
|
302
305
|
messageGenerationMode?: undefined;
|
|
306
|
+
messageApproach?: undefined;
|
|
303
307
|
currentStep?: undefined;
|
|
304
308
|
watchNarration?: undefined;
|
|
305
309
|
leadSourceType?: undefined;
|
|
@@ -351,6 +355,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
351
355
|
offerPositioning?: undefined;
|
|
352
356
|
campaignBrief?: undefined;
|
|
353
357
|
messageGenerationMode?: undefined;
|
|
358
|
+
messageApproach?: undefined;
|
|
354
359
|
currentStep?: undefined;
|
|
355
360
|
watchNarration?: undefined;
|
|
356
361
|
leadSourceType?: undefined;
|
|
@@ -398,6 +403,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
398
403
|
offerPositioning?: undefined;
|
|
399
404
|
campaignBrief?: undefined;
|
|
400
405
|
messageGenerationMode?: undefined;
|
|
406
|
+
messageApproach?: undefined;
|
|
401
407
|
currentStep?: undefined;
|
|
402
408
|
watchNarration?: undefined;
|
|
403
409
|
leadSourceType?: undefined;
|
|
@@ -445,6 +451,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
445
451
|
offerPositioning?: undefined;
|
|
446
452
|
campaignBrief?: undefined;
|
|
447
453
|
messageGenerationMode?: undefined;
|
|
454
|
+
messageApproach?: undefined;
|
|
448
455
|
currentStep?: undefined;
|
|
449
456
|
watchNarration?: undefined;
|
|
450
457
|
leadSourceType?: undefined;
|
|
@@ -535,6 +542,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
535
542
|
offerPositioning?: undefined;
|
|
536
543
|
campaignBrief?: undefined;
|
|
537
544
|
messageGenerationMode?: undefined;
|
|
545
|
+
messageApproach?: undefined;
|
|
538
546
|
currentStep?: undefined;
|
|
539
547
|
watchNarration?: undefined;
|
|
540
548
|
leadSourceType?: undefined;
|
|
@@ -593,6 +601,11 @@ export declare const campaignToolDefinitions: ({
|
|
|
593
601
|
enum: string[];
|
|
594
602
|
description: string;
|
|
595
603
|
};
|
|
604
|
+
messageApproach: {
|
|
605
|
+
type: string;
|
|
606
|
+
enum: string[];
|
|
607
|
+
description: string;
|
|
608
|
+
};
|
|
596
609
|
currentStep: {
|
|
597
610
|
type: string[];
|
|
598
611
|
description: string;
|
|
@@ -971,6 +984,11 @@ export declare const campaignToolDefinitions: ({
|
|
|
971
984
|
type: string[];
|
|
972
985
|
description: string;
|
|
973
986
|
};
|
|
987
|
+
messageApproach: {
|
|
988
|
+
type: string;
|
|
989
|
+
enum: string[];
|
|
990
|
+
description: string;
|
|
991
|
+
};
|
|
974
992
|
autoApproveFollowups: {
|
|
975
993
|
type: string;
|
|
976
994
|
description: string;
|
|
@@ -1038,6 +1056,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
1038
1056
|
offerPositioning?: undefined;
|
|
1039
1057
|
campaignBrief?: undefined;
|
|
1040
1058
|
messageGenerationMode?: undefined;
|
|
1059
|
+
messageApproach?: undefined;
|
|
1041
1060
|
currentStep?: undefined;
|
|
1042
1061
|
watchNarration?: undefined;
|
|
1043
1062
|
leadSourceType?: undefined;
|
|
@@ -1088,6 +1107,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
1088
1107
|
senderLinkedinUrl?: undefined;
|
|
1089
1108
|
offerPositioning?: undefined;
|
|
1090
1109
|
messageGenerationMode?: undefined;
|
|
1110
|
+
messageApproach?: undefined;
|
|
1091
1111
|
currentStep?: undefined;
|
|
1092
1112
|
watchNarration?: undefined;
|
|
1093
1113
|
leadSourceType?: undefined;
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -447,6 +447,11 @@ export const campaignToolDefinitions = [
|
|
|
447
447
|
enum: ["template", "ai-generated"],
|
|
448
448
|
description: "Defaults to template for autonomous messaging steps. Use ai-generated only when the user explicitly approves freeform AI-generated messages instead of an approved {{token}} template.",
|
|
449
449
|
},
|
|
450
|
+
messageApproach: {
|
|
451
|
+
type: "string",
|
|
452
|
+
enum: ["conversation", "message_test"],
|
|
453
|
+
description: 'How first messages are written. "conversation": each first message is written for that person by the backend writer and opens a conversation, with no pitch; the campaign must have NO approved message template and no {{token}} copy in the brief. "message_test": one approved message template goes to everyone, for testing copy. Omit to leave the campaign as it is.',
|
|
454
|
+
},
|
|
450
455
|
currentStep: {
|
|
451
456
|
type: ["string", "null"],
|
|
452
457
|
description: "Workflow step ID (headless or UI step ID such as filter-rules)",
|
|
@@ -569,6 +574,11 @@ export const campaignToolDefinitions = [
|
|
|
569
574
|
type: ["boolean", "null"],
|
|
570
575
|
description: "Whether to use the messaging template step",
|
|
571
576
|
},
|
|
577
|
+
messageApproach: {
|
|
578
|
+
type: "string",
|
|
579
|
+
enum: ["conversation", "message_test"],
|
|
580
|
+
description: 'How first messages are written. "conversation": each first message is written for that person by the backend writer and opens a conversation, with no pitch; the campaign must have NO approved message template and no {{token}} copy in the brief. "message_test": one approved message template goes to everyone, for testing copy. Omit to leave the campaign as it is.',
|
|
581
|
+
},
|
|
572
582
|
autoApproveFollowups: {
|
|
573
583
|
type: "boolean",
|
|
574
584
|
description: "Whether drafted campaign follow-ups are automatically approved/sent. This does not draft, approve, or send an individual prospect message during the settings update.",
|
|
@@ -1046,7 +1056,10 @@ export async function createCampaign(input) {
|
|
|
1046
1056
|
const isAutonomousMessageMint = currentStep === "auto-execute-leads" ||
|
|
1047
1057
|
currentStep === "validate-sample" ||
|
|
1048
1058
|
currentStep === "auto-execute-messaging";
|
|
1049
|
-
|
|
1059
|
+
// A conversation campaign has no template by design: each first message is
|
|
1060
|
+
// written for the person by the backend writer.
|
|
1061
|
+
const explicitlyAllowsAiGenerated = input.messageGenerationMode === "ai-generated" ||
|
|
1062
|
+
input.messageApproach === "conversation";
|
|
1050
1063
|
const hasTemplateToken = /\{\{\s*\S[^}]*\}\}/.test(briefContent);
|
|
1051
1064
|
if (isAutonomousMessageMint &&
|
|
1052
1065
|
!explicitlyAllowsAiGenerated &&
|
|
@@ -12,6 +12,9 @@ export type CampaignOfferNavigation = {
|
|
|
12
12
|
useMessagingTemplate?: boolean | null;
|
|
13
13
|
messagingTemplateId?: string | null;
|
|
14
14
|
messagingTemplate?: unknown | null;
|
|
15
|
+
messageGenerator?: {
|
|
16
|
+
mode?: string | null;
|
|
17
|
+
} | null;
|
|
15
18
|
leadSourceType?: string | null;
|
|
16
19
|
leadSourceProvider?: string | null;
|
|
17
20
|
selectedLeadListId?: string | null;
|
package/dist/tools/navigation.js
CHANGED
|
@@ -140,6 +140,10 @@ function hasActiveRubrics(campaign) {
|
|
|
140
140
|
function hasApprovedMessageTemplate(campaign) {
|
|
141
141
|
if (campaign.messagingTemplateId || campaign.messagingTemplate)
|
|
142
142
|
return true;
|
|
143
|
+
// A conversation campaign never has a template; its messages are written
|
|
144
|
+
// per person, so the message approach is already settled.
|
|
145
|
+
if (campaign.messageGenerator?.mode === "CONVERSATION")
|
|
146
|
+
return true;
|
|
143
147
|
const briefContent = getBriefContent(campaign.campaignBrief);
|
|
144
148
|
if (!briefContent.trim())
|
|
145
149
|
return false;
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -2334,6 +2334,7 @@ export declare const allTools: ({
|
|
|
2334
2334
|
offerPositioning?: undefined;
|
|
2335
2335
|
campaignBrief?: undefined;
|
|
2336
2336
|
messageGenerationMode?: undefined;
|
|
2337
|
+
messageApproach?: undefined;
|
|
2337
2338
|
currentStep?: undefined;
|
|
2338
2339
|
watchNarration?: undefined;
|
|
2339
2340
|
leadSourceType?: undefined;
|
|
@@ -2383,6 +2384,7 @@ export declare const allTools: ({
|
|
|
2383
2384
|
offerPositioning?: undefined;
|
|
2384
2385
|
campaignBrief?: undefined;
|
|
2385
2386
|
messageGenerationMode?: undefined;
|
|
2387
|
+
messageApproach?: undefined;
|
|
2386
2388
|
currentStep?: undefined;
|
|
2387
2389
|
watchNarration?: undefined;
|
|
2388
2390
|
leadSourceType?: undefined;
|
|
@@ -2434,6 +2436,7 @@ export declare const allTools: ({
|
|
|
2434
2436
|
offerPositioning?: undefined;
|
|
2435
2437
|
campaignBrief?: undefined;
|
|
2436
2438
|
messageGenerationMode?: undefined;
|
|
2439
|
+
messageApproach?: undefined;
|
|
2437
2440
|
currentStep?: undefined;
|
|
2438
2441
|
watchNarration?: undefined;
|
|
2439
2442
|
leadSourceType?: undefined;
|
|
@@ -2481,6 +2484,7 @@ export declare const allTools: ({
|
|
|
2481
2484
|
offerPositioning?: undefined;
|
|
2482
2485
|
campaignBrief?: undefined;
|
|
2483
2486
|
messageGenerationMode?: undefined;
|
|
2487
|
+
messageApproach?: undefined;
|
|
2484
2488
|
currentStep?: undefined;
|
|
2485
2489
|
watchNarration?: undefined;
|
|
2486
2490
|
leadSourceType?: undefined;
|
|
@@ -2528,6 +2532,7 @@ export declare const allTools: ({
|
|
|
2528
2532
|
offerPositioning?: undefined;
|
|
2529
2533
|
campaignBrief?: undefined;
|
|
2530
2534
|
messageGenerationMode?: undefined;
|
|
2535
|
+
messageApproach?: undefined;
|
|
2531
2536
|
currentStep?: undefined;
|
|
2532
2537
|
watchNarration?: undefined;
|
|
2533
2538
|
leadSourceType?: undefined;
|
|
@@ -2618,6 +2623,7 @@ export declare const allTools: ({
|
|
|
2618
2623
|
offerPositioning?: undefined;
|
|
2619
2624
|
campaignBrief?: undefined;
|
|
2620
2625
|
messageGenerationMode?: undefined;
|
|
2626
|
+
messageApproach?: undefined;
|
|
2621
2627
|
currentStep?: undefined;
|
|
2622
2628
|
watchNarration?: undefined;
|
|
2623
2629
|
leadSourceType?: undefined;
|
|
@@ -2676,6 +2682,11 @@ export declare const allTools: ({
|
|
|
2676
2682
|
enum: string[];
|
|
2677
2683
|
description: string;
|
|
2678
2684
|
};
|
|
2685
|
+
messageApproach: {
|
|
2686
|
+
type: string;
|
|
2687
|
+
enum: string[];
|
|
2688
|
+
description: string;
|
|
2689
|
+
};
|
|
2679
2690
|
currentStep: {
|
|
2680
2691
|
type: string[];
|
|
2681
2692
|
description: string;
|
|
@@ -3054,6 +3065,11 @@ export declare const allTools: ({
|
|
|
3054
3065
|
type: string[];
|
|
3055
3066
|
description: string;
|
|
3056
3067
|
};
|
|
3068
|
+
messageApproach: {
|
|
3069
|
+
type: string;
|
|
3070
|
+
enum: string[];
|
|
3071
|
+
description: string;
|
|
3072
|
+
};
|
|
3057
3073
|
autoApproveFollowups: {
|
|
3058
3074
|
type: string;
|
|
3059
3075
|
description: string;
|
|
@@ -3121,6 +3137,7 @@ export declare const allTools: ({
|
|
|
3121
3137
|
offerPositioning?: undefined;
|
|
3122
3138
|
campaignBrief?: undefined;
|
|
3123
3139
|
messageGenerationMode?: undefined;
|
|
3140
|
+
messageApproach?: undefined;
|
|
3124
3141
|
currentStep?: undefined;
|
|
3125
3142
|
watchNarration?: undefined;
|
|
3126
3143
|
leadSourceType?: undefined;
|
|
@@ -3171,6 +3188,7 @@ export declare const allTools: ({
|
|
|
3171
3188
|
senderLinkedinUrl?: undefined;
|
|
3172
3189
|
offerPositioning?: undefined;
|
|
3173
3190
|
messageGenerationMode?: undefined;
|
|
3191
|
+
messageApproach?: undefined;
|
|
3174
3192
|
currentStep?: undefined;
|
|
3175
3193
|
watchNarration?: undefined;
|
|
3176
3194
|
leadSourceType?: undefined;
|
package/package.json
CHANGED
|
@@ -80,6 +80,12 @@ approval and campaign setup validation.
|
|
|
80
80
|
Use `research-sender`, call `complete_sender_research` before shell creation,
|
|
81
81
|
and carry proof gaps into the brief.
|
|
82
82
|
|
|
83
|
+
## Message Approach
|
|
84
|
+
|
|
85
|
+
Before `create_campaign`, load `references/message-approach.md` and choose how
|
|
86
|
+
first messages are written: Conversations (each written for the person, no
|
|
87
|
+
template) or Message Lab (one approved template). Pass it as `messageApproach`.
|
|
88
|
+
|
|
83
89
|
## Brief Provenance
|
|
84
90
|
|
|
85
91
|
When rendering the first brief, do not add bracketed inline source tags. Start
|
|
@@ -308,6 +314,7 @@ Route user copy feedback before `approve-message` back to Message Drafting; pare
|
|
|
308
314
|
|
|
309
315
|
Load references only when needed:
|
|
310
316
|
|
|
317
|
+
- `references/message-approach.md` before `create_campaign`.
|
|
311
318
|
- `references/watch-link-handoff.md` for watch-link handoff.
|
|
312
319
|
- `references/watch-guide-narration.md` for watch narration.
|
|
313
320
|
- `references/lead-validation-preview.md` for legacy/debug preview shapes.
|
|
@@ -540,4 +540,6 @@ marketplace actors, wrong function, or no budget path, classify it as
|
|
|
540
540
|
|
|
541
541
|
After filters are saved, wait for explicit filter approval before Filter Leads.
|
|
542
542
|
Then wait for message template/token approval before enrichment, scoring, or
|
|
543
|
-
Generate Message cells are queued.
|
|
543
|
+
Generate Message cells are queued. A Conversations campaign
|
|
544
|
+
(`references/message-approach.md`) has no template to approve: queue the
|
|
545
|
+
sample once the filters are settled.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Message Approach
|
|
2
|
+
|
|
3
|
+
Every campaign writes its first message one of two ways. Decide which before
|
|
4
|
+
`create_campaign`, pass it as `messageApproach`, and say which you picked in
|
|
5
|
+
one plain line the customer can correct.
|
|
6
|
+
|
|
7
|
+
- **Conversations** (`messageApproach: "conversation"`). Each first message is
|
|
8
|
+
written for that one person by Sellable's writer and opens a conversation.
|
|
9
|
+
It carries no pitch and no ask; the offer comes up once they reply. Choose
|
|
10
|
+
it when replies have an owner: reply drafting is on for the campaign (the
|
|
11
|
+
default), so each reply arrives as a draft to approve, or the workspace's
|
|
12
|
+
Reply Agent answers.
|
|
13
|
+
- **Message Lab** (`messageApproach: "message_test"`). One approved message
|
|
14
|
+
goes to everyone, so the customer can test which copy wins. This is the
|
|
15
|
+
template flow described in the create-campaign-v2 prompt and flow file. Choose it when the
|
|
16
|
+
customer wants to approve exact wording, test copy against copy, or has
|
|
17
|
+
switched reply drafting off.
|
|
18
|
+
|
|
19
|
+
When the customer has not said, use Conversations if replies have an owner and
|
|
20
|
+
Message Lab otherwise. "Same message to everyone", "test my copy" or "I want
|
|
21
|
+
to approve the wording" means Message Lab. "Write each one", "personal",
|
|
22
|
+
"conversational" or "use the reply agent" means Conversations. Change an
|
|
23
|
+
existing campaign with `update_campaign({ campaignId, messageApproach })`.
|
|
24
|
+
|
|
25
|
+
**A Conversations campaign has no template.** The backend writes each message
|
|
26
|
+
only when the campaign carries no approved template, so:
|
|
27
|
+
|
|
28
|
+
- write no `## Approved Message Template` section, no `{{token}}` copy and no
|
|
29
|
+
first-message wording into the brief, and keep pitch wording out of `dos`;
|
|
30
|
+
- put the offer and the proof under headings that say "for replies only";
|
|
31
|
+
- when the first message goes out as a DM after the connection request is
|
|
32
|
+
accepted (the recommended sequence), add this line to the campaign `dos`
|
|
33
|
+
with the sender's first name: "This first message goes out as a LinkedIn DM
|
|
34
|
+
after they accept {sender}'s connection request: thank them for
|
|
35
|
+
connecting.";
|
|
36
|
+
- skip Message Drafting, the message template review and the template
|
|
37
|
+
approval. After the filters are saved, queue enrichment for the sample;
|
|
38
|
+
fit scoring and Generate Message follow on their own;
|
|
39
|
+
- review the generated messages themselves before showing or approving
|
|
40
|
+
them: the person and company match the profile, no pitch, and nothing
|
|
41
|
+
about the person that their profile does not show (a guessed city, a guessed
|
|
42
|
+
role). Leave a message that fails out of the sample, say so in one line,
|
|
43
|
+
and do not approve that row until it is rewritten
|
|
44
|
+
(`queue_campaign_cells` with `columnRole: "generateMessage"`, that row,
|
|
45
|
+
`forceRerun: true`). `get_campaign` reports the saved approach as
|
|
46
|
+
`campaignOffer.messageApproach`.
|
|
47
|
+
|
|
48
|
+
**The writer sounds like the sender only when it knows the sender.** With
|
|
49
|
+
exactly one sender attached to the campaign and that sender's context saved,
|
|
50
|
+
it writes the full message, including a line about the sender. With no sender,
|
|
51
|
+
several senders, or no saved context it writes the short form: a greeting,
|
|
52
|
+
one plain line about the person's work, and a light line about their town. So in a Conversations campaign:
|
|
53
|
+
|
|
54
|
+
1. Ask for the LinkedIn connection before the sample, not after it.
|
|
55
|
+
2. Once `list_senders` shows the account, attach it
|
|
56
|
+
(`update_campaign({ campaignId, senderIds: [id] })`), read it with
|
|
57
|
+
`get_sender`, and save what the profile research found with
|
|
58
|
+
`set_sender_context` (pass `get_sender`'s `generationContext.updatedAt` as
|
|
59
|
+
`expectedUpdatedAt`, null on the first save). Use facts from the sender's
|
|
60
|
+
own profile only: what they built and did (`workStories`), plain personal
|
|
61
|
+
facts (`personalNotes`), a few lines in their own words from their posts
|
|
62
|
+
or About (`voiceExamples`), and what they may share or will not say
|
|
63
|
+
(`sourcesAndLimits`).
|
|
64
|
+
3. Generate or regenerate the sample after that
|
|
65
|
+
(`queue_campaign_cells` with `columnRole: "generateMessage"`, the sample
|
|
66
|
+
rows, `forceRerun: true`).
|
|
67
|
+
|
|
68
|
+
If the customer has not connected an account when the sample is ready, show
|
|
69
|
+
the short-form sample and say the messages gain a line about them once the
|
|
70
|
+
account is connected.
|
|
@@ -54,7 +54,8 @@ rerun `import_leads`.
|
|
|
54
54
|
only after saved-filter approval; `validate-sample` is the logical loop name,
|
|
55
55
|
not a visible route)
|
|
56
56
|
- Approved message template/token rules exist in the campaign brief before any
|
|
57
|
-
enrichment or scoring cells are queued
|
|
57
|
+
enrichment or scoring cells are queued (Message Lab only; a Conversations
|
|
58
|
+
campaign has no template)
|
|
58
59
|
- Confirmed campaign rows from Step 13, with the internal execution slice stored
|
|
59
60
|
in `WorkflowTable.config.mcp.reviewBatch` (default 15)
|
|
60
61
|
- Config from `auto-execute.yaml`: `sample.sampleSize`,
|
|
@@ -8,6 +8,13 @@ visibility: internal
|
|
|
8
8
|
|
|
9
9
|
This is the tail detail extracted from the main create-campaign-v2 SKILL.md to keep the entry prompt slim. The agent loads this on-demand BEFORE entering the auto-execute or validate-sample steps. Follow this verbatim.
|
|
10
10
|
|
|
11
|
+
**Conversations campaigns** (`messageApproach: "conversation"`, see
|
|
12
|
+
`create-campaign-v2` `references/message-approach.md`) have no message
|
|
13
|
+
template. Every rule below that requires an approved template, `{{...}}`
|
|
14
|
+
tokens in the brief, Message Drafting, or template approval before queueing
|
|
15
|
+
applies to Message Lab campaigns only. In a Conversations campaign the same
|
|
16
|
+
cascade writes each message once a row passes the fit check.
|
|
17
|
+
|
|
11
18
|
CampaignOffer state and the watch link are canonical. Disk artifacts are
|
|
12
19
|
optional debug/UAT diagnostics; normal customer runs should not expose local
|
|
13
20
|
draft files. Resume, gating, and handoff read campaign state first.
|
|
@@ -380,7 +387,8 @@ the output — not to generate messages manually.
|
|
|
380
387
|
**What Step 15 does:**
|
|
381
388
|
|
|
382
389
|
1. Before queueing or waiting on Generate Message cells, confirm the minted
|
|
383
|
-
campaign brief still contains `{{...}}` in `## Approved Message Template
|
|
390
|
+
campaign brief still contains `{{...}}` in `## Approved Message Template`
|
|
391
|
+
(Message Lab only; a Conversations campaign must have no template).
|
|
384
392
|
If it does not, fail before the cascade runs. Do not repair after mint; the
|
|
385
393
|
template must be present during mint so Step 15 never starts in freeform
|
|
386
394
|
generation mode.
|
|
@@ -48,7 +48,9 @@ reply to either the same way.)
|
|
|
48
48
|
A reply in that thread is explicit permission to begin preparation, not
|
|
49
49
|
permission to send. It needs no mention of this Agent.
|
|
50
50
|
|
|
51
|
-
- **The reply contains a LinkedIn profile URL
|
|
51
|
+
- **The reply contains a LinkedIn profile URL or handle.** A single word such
|
|
52
|
+
as `csreyes92` or `@csreyes92` is a handle: read it as
|
|
53
|
+
`https://www.linkedin.com/in/{handle}/`. Send one short line first so
|
|
52
54
|
the customer is not left looking at silence: "Got it. Researching you and
|
|
53
55
|
your company now. I'll be back in a few minutes with a first campaign to
|
|
54
56
|
review." Send it before the Live State Check and before loading any other
|
|
@@ -278,6 +280,15 @@ for the message template: those are yours to decide and report.
|
|
|
278
280
|
- Do not paste the full brief into Slack. Save it on the campaign and post the
|
|
279
281
|
five-line summary below.
|
|
280
282
|
|
|
283
|
+
**How the first message is written.** The first campaign uses the
|
|
284
|
+
Conversations approach from the workflow's `references/message-approach.md`:
|
|
285
|
+
create it with `messageApproach: "conversation"`, write no message template,
|
|
286
|
+
and let Sellable's writer write each first message for the person. Use Message
|
|
287
|
+
Lab (`"message_test"`, one approved message to everyone) only when the
|
|
288
|
+
customer asks to send the same message to everyone, to test copy, or to
|
|
289
|
+
approve exact wording; then the template steps of the workflow apply and you
|
|
290
|
+
approve the template yourself as before.
|
|
291
|
+
|
|
281
292
|
**Approval 1: direction.** After research, write the brief, create the
|
|
282
293
|
campaign with it (`create_campaign`), and post one message in this shape. The
|
|
283
294
|
saved brief is the full brief; this message is how the customer approves it,
|
|
@@ -292,6 +303,7 @@ Here's the first campaign I'd run for {company}.
|
|
|
292
303
|
*Offer* {what you would get to once they reply, and the small ask, one line}
|
|
293
304
|
*Proof* {the proof you would lead with; if you found none, say so and what you would use instead}
|
|
294
305
|
*Where I'll look* {where you will find them first, in plain words}
|
|
306
|
+
*Messages* Written for each person to open a conversation; the offer comes once they reply. Say "same message to everyone" if you'd rather test one message.
|
|
295
307
|
|
|
296
308
|
Full brief: {campaign link}
|
|
297
309
|
|
|
@@ -325,13 +337,17 @@ do-not-contact gate; do not ask again before the import.
|
|
|
325
337
|
|
|
326
338
|
**Between 1 and 2.** Once the direction and the do-not-contact answer are in,
|
|
327
339
|
reply first, before any tool call: "On it. I'll find the people, check who
|
|
328
|
-
fits and write the
|
|
329
|
-
minutes.
|
|
340
|
+
fits and write the messages. Your sample will be here in about 15 to 20
|
|
341
|
+
minutes. While I work: connect the LinkedIn account that will send,
|
|
342
|
+
https://app.sellable.dev/linkedin-accounts It takes about two minutes, it is
|
|
343
|
+
only used to send what you approve, and once it's connected I write the
|
|
344
|
+
messages in your voice." Then set `interactionMode: "autonomous"` on the campaign with
|
|
330
345
|
`update_campaign` and pass `confirmed: true` where a search or import tool
|
|
331
346
|
asks for it: the direction approval is that confirmation. Do the work without
|
|
332
347
|
asking: pick where to look, find and import the people within the flow's
|
|
333
|
-
caps, use filters and write the rules,
|
|
334
|
-
|
|
348
|
+
caps, use filters and write the rules, and prepare the 15-person sample. In
|
|
349
|
+
Conversations there is no template to write: queue the sample once the rules
|
|
350
|
+
are saved and the messages are written for you.
|
|
335
351
|
|
|
336
352
|
- **Keep the customer posted.** Whenever about five minutes have passed since
|
|
337
353
|
your last message, post one short line in plain words on where you are
|
|
@@ -353,6 +369,13 @@ caps, use filters and write the rules, write the message, and prepare the
|
|
|
353
369
|
- **Let the sample finish.** The flow stops at the first passing message; here
|
|
354
370
|
wait until the 15-person sample has finished scoring and writing, because
|
|
355
371
|
the customer is shown three people and a count.
|
|
372
|
+
- **The sender, as soon as there is one.** Check `list_senders` when the
|
|
373
|
+
import finishes and again before you show the sample. When the customer's
|
|
374
|
+
account is there, attach it to the campaign, save what your research found
|
|
375
|
+
about them as sender context, and write or rewrite the sample after that,
|
|
376
|
+
as `references/message-approach.md` describes. A sample written
|
|
377
|
+
before the account was connected is the short form, with nothing about the
|
|
378
|
+
sender.
|
|
356
379
|
- **Check before you show.** For each message you are about to show, compare
|
|
357
380
|
the company it names with the person's own headline. Leave out any that
|
|
358
381
|
disagree and say that you dropped them.
|
|
@@ -361,7 +384,7 @@ caps, use filters and write the rules, write the message, and prepare the
|
|
|
361
384
|
real message each would receive:
|
|
362
385
|
|
|
363
386
|
```text
|
|
364
|
-
Your sample is ready: {
|
|
387
|
+
Your sample is ready: {the number you show below} real people and the message each would get.
|
|
365
388
|
|
|
366
389
|
{Name} · {title}, {company} · {why they are here, a few words}
|
|
367
390
|
"{the message they would receive}"
|
|
@@ -370,10 +393,11 @@ Your sample is ready: {n} real people and the message each would get.
|
|
|
370
393
|
|
|
371
394
|
{passed} of {checked} people I checked fit. {one line on why the rest did not}
|
|
372
395
|
What I chose: {where the people came from and how many are on the list, and the fit rules in a few words}
|
|
373
|
-
|
|
396
|
+
Each message is written for that person and has no pitch on purpose. {the offer} comes once they reply.
|
|
397
|
+
{only if no sending account is connected yet: "These are the short version. Connect your LinkedIn account and I'll rewrite them with a line about you: https://app.sellable.dev/linkedin-accounts"}
|
|
374
398
|
Everyone and every message: {campaign link}
|
|
375
399
|
|
|
376
|
-
Reply approve to use these people and
|
|
400
|
+
Reply approve to use these people and messages like these, or tell me what to change.
|
|
377
401
|
Nothing has been sent.
|
|
378
402
|
```
|
|
379
403
|
|
|
@@ -383,8 +407,8 @@ the customer kept as approved in the table, not one or two. A person the
|
|
|
383
407
|
customer dropped stays unapproved; never approve them later, in bulk or
|
|
384
408
|
otherwise.
|
|
385
409
|
|
|
386
|
-
**Approval 3: launch.** The only thing
|
|
387
|
-
`list_senders`.
|
|
410
|
+
**Approval 3: launch.** The only thing that can still be missing is the
|
|
411
|
+
sending account. Call `list_senders`.
|
|
388
412
|
|
|
389
413
|
- **No usable sender.** Ask for that one thing: "One thing left: connect the
|
|
390
414
|
LinkedIn account that will send. https://app.sellable.dev/linkedin-accounts
|