@sellable/mcp 0.1.80 → 0.1.82
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/agents/post-find-leads-filter-scout.md +19 -8
- package/agents/post-find-leads-message-scout.md +21 -6
- package/agents/registry.json +33 -18
- package/agents/source-scout-linkedin-engagement.md +10 -2
- package/agents/source-scout-prospeo-contact.md +9 -2
- package/agents/source-scout-sales-nav.md +10 -2
- package/dist/server.js +7 -1
- package/dist/tools/bootstrap.js +2 -2
- package/dist/tools/campaigns.d.ts +12 -0
- package/dist/tools/campaigns.js +40 -5
- package/dist/tools/context.d.ts +10 -4
- package/dist/tools/context.js +8 -4
- package/dist/tools/leads.d.ts +6 -3
- package/dist/tools/leads.js +39 -43
- package/dist/tools/navigation.d.ts +28 -1
- package/dist/tools/navigation.js +220 -10
- package/dist/tools/prompts.js +1 -1
- package/dist/tools/provider-preflight.js +2 -3
- package/dist/tools/readiness.d.ts +31 -6
- package/dist/tools/readiness.js +6 -1
- package/dist/tools/rubrics.d.ts +2 -0
- package/dist/tools/rubrics.js +2 -0
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +24 -12
- package/skills/create-campaign-v2/SKILL.md +166 -114
- package/skills/create-campaign-v2/SOUL.md +19 -11
- package/skills/create-campaign-v2/core/auto-execute.yaml +14 -13
- package/skills/create-campaign-v2/core/flow.v2.json +964 -310
- package/skills/create-campaign-v2/references/approval-gate-framing.md +64 -32
- package/skills/create-campaign-v2/references/filter-leads.md +8 -0
- package/skills/create-campaign-v2/references/final-handoff-contract.md +55 -20
- package/skills/create-campaign-v2/references/lead-validation-preview.md +30 -17
- package/skills/create-campaign-v2/references/step-13-import-leads.md +62 -17
- package/skills/create-campaign-v2/references/validation-criteria.md +10 -5
- package/skills/create-campaign-v2/references/watch-link-handoff.md +72 -72
- package/skills/create-campaign-v2-tail/SKILL.md +123 -92
|
@@ -1,28 +1,35 @@
|
|
|
1
|
-
You are
|
|
1
|
+
You are Lead Fit Builder for Sellable create-campaign-v2.
|
|
2
2
|
|
|
3
3
|
Your job starts only after find-leads has produced `lead-review.md` and
|
|
4
4
|
`lead-sample.json`, and the lead source has been approved or auto-confirmed.
|
|
5
5
|
Work only on the lead filter branch. Do not source new leads, draft messages,
|
|
6
|
-
import leads, create campaigns, ask the user questions
|
|
7
|
-
|
|
6
|
+
import leads, create campaigns, or ask the user questions. Your only live
|
|
7
|
+
campaign mutation is calling `save_rubrics` after the production rubrics are
|
|
8
|
+
ready.
|
|
8
9
|
|
|
9
10
|
Required inputs:
|
|
10
11
|
|
|
11
12
|
- `brief.md`
|
|
12
13
|
- `lead-review.md`
|
|
13
14
|
- `lead-sample.json`
|
|
15
|
+
- campaign state from the parent thread
|
|
16
|
+
- campaign table sample from the parent thread
|
|
14
17
|
|
|
15
18
|
Required first steps:
|
|
16
19
|
|
|
17
20
|
1. Read the three required inputs.
|
|
18
21
|
2. Load the filter-leads reference before writing artifacts:
|
|
19
22
|
`get_subskill_asset({ subskillName: "create-campaign-v2", assetPath: "references/filter-leads.md" })`.
|
|
23
|
+
3. Treat campaign state and the campaign table sample as the input of record.
|
|
24
|
+
Disk files are context/debug aids, not durable state.
|
|
20
25
|
|
|
21
26
|
Owned outputs:
|
|
22
27
|
|
|
23
|
-
- `
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
- Durable campaign rubrics via `save_rubrics({ campaignOfferId, leadScoringRubrics })`
|
|
29
|
+
when the filter is confirmed and production-shaped rubrics are safe to write.
|
|
30
|
+
`save_rubrics` is the durable writer.
|
|
31
|
+
- `lead-filter.md` debug artifact after the durable campaign write
|
|
32
|
+
- `rubric.json` debug artifact after the durable campaign write
|
|
26
33
|
|
|
27
34
|
Do not write or modify:
|
|
28
35
|
|
|
@@ -46,12 +53,16 @@ Process:
|
|
|
46
53
|
5. Keep source mechanics out of production rubrics. Engagement, provider,
|
|
47
54
|
priority, or first-send ordering can inform prioritization, but they are not
|
|
48
55
|
standalone ICP qualification rules.
|
|
49
|
-
6.
|
|
50
|
-
`
|
|
56
|
+
6. If status is `confirmed`, call `save_rubrics` with 2-5 production-shaped
|
|
57
|
+
active `leadScoringRubrics` before reporting success. If `save_rubrics`
|
|
58
|
+
fails, stop and report the blocker; do not claim the filter is persisted.
|
|
59
|
+
7. Write `lead-filter.md` and `rubric.json` only as debug artifacts after
|
|
60
|
+
campaign persistence succeeds.
|
|
51
61
|
|
|
52
62
|
Return a concise final status with:
|
|
53
63
|
|
|
54
64
|
- filter status: `confirmed`, `confirm-with-user`, or `revise-find-leads`
|
|
65
|
+
- whether `save_rubrics` succeeded and how many active rubrics were persisted
|
|
55
66
|
- artifacts written
|
|
56
67
|
- strongest keep rules
|
|
57
68
|
- strongest exclusion rules
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
You are
|
|
1
|
+
You are Message Draft Builder for Sellable create-campaign-v2.
|
|
2
2
|
|
|
3
3
|
Your job starts only after find-leads has produced `lead-review.md` and
|
|
4
4
|
`lead-sample.json`, and the lead source has been approved or auto-confirmed.
|
|
5
5
|
Work only on the message generation branch. Do not source new leads, create lead
|
|
6
6
|
filters, import leads, create campaigns, ask the user questions, or mutate live
|
|
7
|
-
campaign state.
|
|
7
|
+
campaign state. Do not call `update_campaign_brief`; the main thread owns the
|
|
8
|
+
approval write.
|
|
8
9
|
|
|
9
10
|
Required inputs:
|
|
10
11
|
|
|
11
12
|
- `brief.md`
|
|
12
13
|
- `lead-review.md`
|
|
13
14
|
- `lead-sample.json`
|
|
15
|
+
- campaign state from the parent thread
|
|
16
|
+
- campaign table sample from the parent thread
|
|
14
17
|
|
|
15
18
|
Required first steps:
|
|
16
19
|
|
|
@@ -18,12 +21,16 @@ Required first steps:
|
|
|
18
21
|
2. Load 100% of the real generate-messages prompt with chunked
|
|
19
22
|
`get_subskill_prompt({ subskillName: "generate-messages", offset, limit })`
|
|
20
23
|
calls until `hasMore` is false.
|
|
24
|
+
3. Treat campaign state and the campaign table sample as the input of record.
|
|
25
|
+
Disk files are context/debug aids, not durable state.
|
|
21
26
|
|
|
22
27
|
Owned outputs:
|
|
23
28
|
|
|
24
|
-
- `
|
|
25
|
-
-
|
|
26
|
-
-
|
|
29
|
+
- proposed template using supported `{{...}}` tokens
|
|
30
|
+
- one sample message rendered from the proposed template
|
|
31
|
+
- `message-validation.md` debug artifact
|
|
32
|
+
- optional `message-prep.md` debug artifact
|
|
33
|
+
- optional `message-candidate-drafts.md` debug artifact
|
|
27
34
|
|
|
28
35
|
Do not write or modify:
|
|
29
36
|
|
|
@@ -48,13 +55,21 @@ Process:
|
|
|
48
55
|
does not exist yet, choose probable good-fit rows from `lead-sample.json` and
|
|
49
56
|
mark the final reconciliation as pending.
|
|
50
57
|
5. Write `message-validation.md`. Do not write `message-review.md`; the parent
|
|
51
|
-
thread owns the joined review after both
|
|
58
|
+
thread owns the joined review after both builders finish.
|
|
59
|
+
6. Return the proposed template and one sample message for approval. Do not
|
|
60
|
+
mutate campaignBrief. The main thread must show readable filters with
|
|
61
|
+
reasons, show one sample message, ask the user to approve or edit, then write
|
|
62
|
+
the approved template with `update_campaign_brief` only after approval.
|
|
63
|
+
7. If the user edits the proposal, the main thread updates the proposal and asks
|
|
64
|
+
again. Do not queue enrichment, validation rows, or Generate Message work
|
|
65
|
+
until the approved template write succeeds.
|
|
52
66
|
|
|
53
67
|
Return a concise final status with:
|
|
54
68
|
|
|
55
69
|
- artifacts written
|
|
56
70
|
- whether the full generate-messages prompt was loaded
|
|
57
71
|
- lead sample basis used
|
|
72
|
+
- proposed template and one sample message
|
|
58
73
|
- selected winner summary
|
|
59
74
|
- whether final reconciliation with `lead-filter.md` is complete or pending
|
|
60
75
|
|
package/agents/registry.json
CHANGED
|
@@ -154,25 +154,33 @@
|
|
|
154
154
|
"name": "post-find-leads-filter-scout",
|
|
155
155
|
"kind": "post-find-leads-scout",
|
|
156
156
|
"promptFile": "post-find-leads-filter-scout.md",
|
|
157
|
-
"displayName": "
|
|
157
|
+
"displayName": "Lead Fit Builder",
|
|
158
158
|
"target": "filter-leads",
|
|
159
|
-
"inputs": [
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
"inputs": [
|
|
160
|
+
"brief.md",
|
|
161
|
+
"lead-review.md",
|
|
162
|
+
"lead-sample.json"
|
|
163
|
+
],
|
|
164
|
+
"producesArtifacts": [
|
|
165
|
+
"lead-filter.md"
|
|
166
|
+
],
|
|
167
|
+
"optionalProducesArtifacts": [
|
|
168
|
+
"rubric.json"
|
|
169
|
+
],
|
|
162
170
|
"ownership": "lead quality, false-positive patterns, keep/exclude rules, ability-to-pay checks, and production rubric translation only",
|
|
163
171
|
"codex": {
|
|
164
|
-
"description": "
|
|
172
|
+
"description": "Lead Fit Builder for campaign-backed lead filtering and rubric persistence after source approval.",
|
|
165
173
|
"model": "gpt-5.5",
|
|
166
174
|
"modelReasoningEffort": "high",
|
|
167
175
|
"sandboxMode": "workspace-write",
|
|
168
176
|
"nicknameCandidates": [
|
|
169
|
-
"Lead
|
|
170
|
-
"
|
|
171
|
-
"Rubric
|
|
177
|
+
"Lead Fit Builder",
|
|
178
|
+
"Fit Builder",
|
|
179
|
+
"Rubric Builder"
|
|
172
180
|
]
|
|
173
181
|
},
|
|
174
182
|
"claude": {
|
|
175
|
-
"description": "Use proactively as
|
|
183
|
+
"description": "Use proactively as Lead Fit Builder after lead source approval to persist campaign rubrics and write lead-filter debug artifacts from campaign state.",
|
|
176
184
|
"model": "inherit",
|
|
177
185
|
"background": true,
|
|
178
186
|
"maxTurns": 8,
|
|
@@ -184,7 +192,8 @@
|
|
|
184
192
|
"Grep",
|
|
185
193
|
"Glob",
|
|
186
194
|
"mcp__sellable__get_subskill_prompt",
|
|
187
|
-
"mcp__sellable__get_subskill_asset"
|
|
195
|
+
"mcp__sellable__get_subskill_asset",
|
|
196
|
+
"mcp__sellable__save_rubrics"
|
|
188
197
|
]
|
|
189
198
|
}
|
|
190
199
|
},
|
|
@@ -193,28 +202,34 @@
|
|
|
193
202
|
"name": "post-find-leads-message-scout",
|
|
194
203
|
"kind": "post-find-leads-scout",
|
|
195
204
|
"promptFile": "post-find-leads-message-scout.md",
|
|
196
|
-
"displayName": "
|
|
205
|
+
"displayName": "Message Draft Builder",
|
|
197
206
|
"target": "generate-messages",
|
|
198
|
-
"inputs": [
|
|
199
|
-
|
|
207
|
+
"inputs": [
|
|
208
|
+
"brief.md",
|
|
209
|
+
"lead-review.md",
|
|
210
|
+
"lead-sample.json"
|
|
211
|
+
],
|
|
212
|
+
"producesArtifacts": [
|
|
213
|
+
"message-validation.md"
|
|
214
|
+
],
|
|
200
215
|
"optionalProducesArtifacts": [
|
|
201
216
|
"message-prep.md",
|
|
202
217
|
"message-candidate-drafts.md"
|
|
203
218
|
],
|
|
204
219
|
"ownership": "proof inventory, token strategy, angle drafting, skeptical-prospect review, and selected winner only",
|
|
205
220
|
"codex": {
|
|
206
|
-
"description": "
|
|
221
|
+
"description": "Message Draft Builder for campaign-backed template proposals and sample messages after source approval.",
|
|
207
222
|
"model": "gpt-5.5",
|
|
208
223
|
"modelReasoningEffort": "high",
|
|
209
224
|
"sandboxMode": "workspace-write",
|
|
210
225
|
"nicknameCandidates": [
|
|
211
|
-
"Message
|
|
212
|
-
"
|
|
213
|
-
"
|
|
226
|
+
"Message Draft Builder",
|
|
227
|
+
"Draft Builder",
|
|
228
|
+
"Template Builder"
|
|
214
229
|
]
|
|
215
230
|
},
|
|
216
231
|
"claude": {
|
|
217
|
-
"description": "Use proactively as
|
|
232
|
+
"description": "Use proactively as Message Draft Builder after lead source approval to propose an approved-template candidate and sample message from campaign state.",
|
|
218
233
|
"model": "inherit",
|
|
219
234
|
"background": true,
|
|
220
235
|
"maxTurns": 10,
|
|
@@ -4,11 +4,19 @@ Your job is to test whether active LinkedIn posts and engagers can produce a war
|
|
|
4
4
|
|
|
5
5
|
Required first step:
|
|
6
6
|
|
|
7
|
-
- Load the canonical provider prompt before searching
|
|
7
|
+
- Load the canonical provider prompt before searching. If the parent supplies a
|
|
8
|
+
draft `campaignOfferId`, call `get_provider_prompt({ provider:
|
|
9
|
+
"signal-discovery", campaignOfferId, confirmed: true })` and include that same
|
|
10
|
+
`campaignOfferId` in `search_signals` so the user can watch source work in the
|
|
11
|
+
campaign UI. Treat that as a campaign-attached persisted search; do not run a
|
|
12
|
+
post-mint search without the campaign ID. If no campaign ID is supplied, run
|
|
13
|
+
campaignless preview mode.
|
|
8
14
|
|
|
9
15
|
Use the inherited Sellable MCP tools when available:
|
|
10
16
|
|
|
11
|
-
- `search_signals` to find recent post lanes.
|
|
17
|
+
- `search_signals` to find recent post lanes. Include `campaignOfferId` whenever
|
|
18
|
+
the parent provides one so selected searches/lists stay attached to the
|
|
19
|
+
campaign.
|
|
12
20
|
- `fetch_post_engagers` to sample engagers from selected posts.
|
|
13
21
|
|
|
14
22
|
Process:
|
|
@@ -4,13 +4,20 @@ Your job is to test whether Prospeo can produce verified-contact scale for the c
|
|
|
4
4
|
|
|
5
5
|
Required first step:
|
|
6
6
|
|
|
7
|
-
- Load the canonical provider prompt before searching
|
|
7
|
+
- Load the canonical provider prompt before searching. If the parent supplies a
|
|
8
|
+
draft `campaignOfferId`, call `get_provider_prompt({ provider: "prospeo",
|
|
9
|
+
campaignOfferId, confirmed: true })` and include that same `campaignOfferId` in
|
|
10
|
+
`search_prospeo` so the user can watch source work in the campaign UI. If no
|
|
11
|
+
campaign ID is supplied, run campaignless preview mode. Treat post-mint
|
|
12
|
+
searches with `campaignOfferId` as campaign-attached persisted search tabs;
|
|
13
|
+
do not run a live campaign search without the campaign ID.
|
|
8
14
|
|
|
9
15
|
Use the inherited Sellable MCP tools when available:
|
|
10
16
|
|
|
11
17
|
- `load_csv_domains` when the parent supplies a CSV on disk and no `domainFilterId` exists.
|
|
12
18
|
- `save_domain_filters` when the parent supplies pasted/raw include or exclude domains and no `domainFilterId` exists.
|
|
13
|
-
- `search_prospeo` for
|
|
19
|
+
- `search_prospeo` for people previews. Include `campaignOfferId` whenever the
|
|
20
|
+
parent provides one so selected searches/lists stay attached to the campaign.
|
|
14
21
|
|
|
15
22
|
Process:
|
|
16
23
|
|
|
@@ -4,12 +4,20 @@ Your job is to test whether Sales Navigator filters can produce a scalable, high
|
|
|
4
4
|
|
|
5
5
|
Required first step:
|
|
6
6
|
|
|
7
|
-
- Load the canonical provider prompt before searching
|
|
7
|
+
- Load the canonical provider prompt before searching. If the parent supplies a
|
|
8
|
+
draft `campaignOfferId`, call `get_provider_prompt({ provider: "sales-nav",
|
|
9
|
+
campaignOfferId, confirmed: true })` and include that same `campaignOfferId` in
|
|
10
|
+
`search_sales_nav` so the user can watch source work in the campaign UI. If no
|
|
11
|
+
campaign ID is supplied, run campaignless preview mode. Treat post-mint
|
|
12
|
+
searches with `campaignOfferId` as campaign-attached persisted search tabs;
|
|
13
|
+
do not run a live campaign search without the campaign ID.
|
|
8
14
|
|
|
9
15
|
Use the inherited Sellable MCP tools when available:
|
|
10
16
|
|
|
11
17
|
- `lookup_sales_nav_filter` before any dynamic Sales Nav filter.
|
|
12
|
-
- `search_sales_nav` for
|
|
18
|
+
- `search_sales_nav` for preview searches. Include `campaignOfferId` whenever
|
|
19
|
+
the parent provides one so selected searches/lists stay attached to the
|
|
20
|
+
campaign.
|
|
13
21
|
|
|
14
22
|
Process:
|
|
15
23
|
|
package/dist/server.js
CHANGED
|
@@ -170,6 +170,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
170
170
|
break;
|
|
171
171
|
case "attach_recommended_sequence":
|
|
172
172
|
result = await attachRecommendedSequence(args);
|
|
173
|
+
if (args?.campaignId) {
|
|
174
|
+
markCampaignContextDirty(args.campaignId, "attach_recommended_sequence");
|
|
175
|
+
}
|
|
173
176
|
break;
|
|
174
177
|
case "bootstrap_create_campaign":
|
|
175
178
|
result = await bootstrapCreateCampaign(args);
|
|
@@ -194,7 +197,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
194
197
|
case "update_campaign":
|
|
195
198
|
result = await updateCampaign(args?.campaignId, args);
|
|
196
199
|
if (result?._campaign?.id) {
|
|
197
|
-
const entry = hydrateCampaignContextFromCampaign(result._campaign, "update_campaign");
|
|
200
|
+
const entry = hydrateCampaignContextFromCampaign(result._campaign, "update_campaign", args?.flowVersion === "v2" ? "v2" : "v1");
|
|
198
201
|
result.navigation = entry.navState;
|
|
199
202
|
delete result._campaign;
|
|
200
203
|
}
|
|
@@ -204,6 +207,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
204
207
|
break;
|
|
205
208
|
case "start_campaign":
|
|
206
209
|
result = await startCampaign(args?.campaignId);
|
|
210
|
+
if (args?.campaignId && result?.success) {
|
|
211
|
+
markCampaignContextDirty(args.campaignId, "start_campaign");
|
|
212
|
+
}
|
|
207
213
|
break;
|
|
208
214
|
case "pause_campaign":
|
|
209
215
|
result = await pauseCampaign(args?.campaignId);
|
package/dist/tools/bootstrap.js
CHANGED
|
@@ -268,9 +268,9 @@ export async function bootstrapCreateCampaign(input = {}) {
|
|
|
268
268
|
: "";
|
|
269
269
|
const nextStep = safeToProceed
|
|
270
270
|
? resumeDetected
|
|
271
|
-
? `Bootstrap complete.${workspaceNotice} Resume
|
|
271
|
+
? `Bootstrap complete.${workspaceNotice} Resume from campaign state and navigation diagnostics first; treat local draft artifacts as debug-only evidence. Then load ${createCampaignSubskill?.name ?? "create-campaign"} instructions with get_subskill_prompt({ subskillName: "${createCampaignSubskill?.name ?? "create-campaign"}" }); if the response has hasMore=true, continue with nextOffset until hasMore=false.`
|
|
272
272
|
: flowVersion === "v2"
|
|
273
|
-
? `Bootstrap complete.${workspaceNotice} Load create-campaign-v2 instructions with get_subskill_prompt({ subskillName: "create-campaign-v2" }); if the response has hasMore=true, continue with nextOffset until hasMore=false. Then
|
|
273
|
+
? `Bootstrap complete.${workspaceNotice} Load create-campaign-v2 instructions with get_subskill_prompt({ subskillName: "create-campaign-v2" }); if the response has hasMore=true, continue with nextOffset until hasMore=false. Preserve the pre-intake sequence: confirm auth/workspace status, identity setup, sender discovery/listing, and lightweight profile/company lookup. Then run the short setup/intake, write the campaign brief, call create_campaign once to mint the watchable shell, surface the returned watch link, and hand off to lead finding.`
|
|
274
274
|
: `Bootstrap complete.${workspaceNotice} Load ${createCampaignSubskill?.name ?? "create-campaign"} instructions with get_subskill_prompt({ subskillName: "${createCampaignSubskill?.name ?? "create-campaign"}" }); if the response has hasMore=true, continue with nextOffset until hasMore=false. Follow that flow before calling create_campaign.`
|
|
275
275
|
: "Bootstrap incomplete. Resolve blockingErrors and rerun bootstrap_create_campaign before provider/search/import tools.";
|
|
276
276
|
// Strip prompt body from createCampaignSubskill — it's loaded via the host
|
|
@@ -128,6 +128,7 @@ export interface CreateCampaignInput {
|
|
|
128
128
|
senderIds?: string[];
|
|
129
129
|
}
|
|
130
130
|
export interface UpdateCampaignInput {
|
|
131
|
+
flowVersion?: "v1" | "v2";
|
|
131
132
|
offerPositioning?: unknown;
|
|
132
133
|
campaignBrief?: unknown;
|
|
133
134
|
leadSourceType?: string | null;
|
|
@@ -170,6 +171,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
170
171
|
enableICPFilters?: undefined;
|
|
171
172
|
useMessagingTemplate?: undefined;
|
|
172
173
|
rubric?: undefined;
|
|
174
|
+
flowVersion?: undefined;
|
|
173
175
|
};
|
|
174
176
|
required: never[];
|
|
175
177
|
additionalProperties?: undefined;
|
|
@@ -204,6 +206,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
204
206
|
enableICPFilters?: undefined;
|
|
205
207
|
useMessagingTemplate?: undefined;
|
|
206
208
|
rubric?: undefined;
|
|
209
|
+
flowVersion?: undefined;
|
|
207
210
|
};
|
|
208
211
|
required: string[];
|
|
209
212
|
additionalProperties?: undefined;
|
|
@@ -281,6 +284,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
281
284
|
enableICPFilters?: undefined;
|
|
282
285
|
useMessagingTemplate?: undefined;
|
|
283
286
|
rubric?: undefined;
|
|
287
|
+
flowVersion?: undefined;
|
|
284
288
|
};
|
|
285
289
|
required: string[];
|
|
286
290
|
additionalProperties: boolean;
|
|
@@ -353,6 +357,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
353
357
|
enableICPFilters?: undefined;
|
|
354
358
|
useMessagingTemplate?: undefined;
|
|
355
359
|
rubric?: undefined;
|
|
360
|
+
flowVersion?: undefined;
|
|
356
361
|
};
|
|
357
362
|
required: never[];
|
|
358
363
|
additionalProperties?: undefined;
|
|
@@ -416,6 +421,11 @@ export declare const campaignToolDefinitions: ({
|
|
|
416
421
|
type: string;
|
|
417
422
|
description: string;
|
|
418
423
|
};
|
|
424
|
+
flowVersion: {
|
|
425
|
+
type: string;
|
|
426
|
+
enum: string[];
|
|
427
|
+
description: string;
|
|
428
|
+
};
|
|
419
429
|
limit?: undefined;
|
|
420
430
|
tableId?: undefined;
|
|
421
431
|
leadLimit?: undefined;
|
|
@@ -462,6 +472,7 @@ export declare const campaignToolDefinitions: ({
|
|
|
462
472
|
enableICPFilters?: undefined;
|
|
463
473
|
useMessagingTemplate?: undefined;
|
|
464
474
|
rubric?: undefined;
|
|
475
|
+
flowVersion?: undefined;
|
|
465
476
|
};
|
|
466
477
|
required: string[];
|
|
467
478
|
additionalProperties?: undefined;
|
|
@@ -485,6 +496,7 @@ export interface UpdateCampaignResult {
|
|
|
485
496
|
name: string;
|
|
486
497
|
currentStep: string | null;
|
|
487
498
|
updatedAt: string;
|
|
499
|
+
remediation?: string;
|
|
488
500
|
navigation?: unknown;
|
|
489
501
|
_campaign?: CampaignOfferNavigation;
|
|
490
502
|
}
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -139,7 +139,7 @@ export const campaignToolDefinitions = [
|
|
|
139
139
|
},
|
|
140
140
|
clientProspectId: {
|
|
141
141
|
type: "string",
|
|
142
|
-
description: "Use this for existing sender prospects. For brand-new senders, pass `senderLinkedinUrl` and the backend will attempt sender lookup/creation during campaign
|
|
142
|
+
description: "Use this for existing sender prospects. For brand-new senders, pass `senderLinkedinUrl` and the backend will attempt sender 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.).
|
|
@@ -254,6 +254,11 @@ export const campaignToolDefinitions = [
|
|
|
254
254
|
type: "array",
|
|
255
255
|
description: "Lead scoring rubric entries",
|
|
256
256
|
},
|
|
257
|
+
flowVersion: {
|
|
258
|
+
type: "string",
|
|
259
|
+
enum: ["v1", "v2"],
|
|
260
|
+
description: "Framework flow version for returned navigation diagnostics (default: v1).",
|
|
261
|
+
},
|
|
257
262
|
},
|
|
258
263
|
required: ["campaignId"],
|
|
259
264
|
},
|
|
@@ -618,7 +623,7 @@ export async function createCampaign(input) {
|
|
|
618
623
|
`or all create fields. Missing: ${missing.join(", ")}.\n\n` +
|
|
619
624
|
"Remediation:\n" +
|
|
620
625
|
'- For full workflow, call get_subskill_prompt({ subskillName: "create-campaign" }) and follow it.\n' +
|
|
621
|
-
"- 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 sender context at
|
|
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 sender context at campaign creation time.\n" +
|
|
622
627
|
"- For resume, call create_campaign with campaignId only.");
|
|
623
628
|
}
|
|
624
629
|
assertNetNewCreateCampaignResearchReady();
|
|
@@ -653,7 +658,7 @@ export async function createCampaign(input) {
|
|
|
653
658
|
if (isAutonomousMessageMint &&
|
|
654
659
|
!explicitlyAllowsAiGenerated &&
|
|
655
660
|
!hasTemplateToken) {
|
|
656
|
-
throw new Error("VALIDATION_ERROR: campaignBrief must include at least one supported {{token}} before
|
|
661
|
+
throw new Error("VALIDATION_ERROR: campaignBrief must include at least one supported {{token}} before entering autonomous messaging.\n\n" +
|
|
657
662
|
"Why this is blocked:\n" +
|
|
658
663
|
"- Generate Message enters template mode only when campaignBrief.content contains {{...}}.\n" +
|
|
659
664
|
"- Without a token, the message cascade uses freeform AI generation and may rewrite the approved message.\n\n" +
|
|
@@ -707,7 +712,11 @@ export async function createCampaign(input) {
|
|
|
707
712
|
}
|
|
708
713
|
export async function updateCampaign(campaignId, input) {
|
|
709
714
|
const api = getApi();
|
|
710
|
-
const { campaignId: _ignore, interactionMode, ...updates } = input;
|
|
715
|
+
const { campaignId: _ignore, interactionMode, flowVersion: _flowVersion, senderIds, currentStep, ...updates } = input;
|
|
716
|
+
const hasSenderIds = Object.prototype.hasOwnProperty.call(input, "senderIds");
|
|
717
|
+
if (!hasSenderIds && currentStep !== undefined) {
|
|
718
|
+
updates.currentStep = currentStep;
|
|
719
|
+
}
|
|
711
720
|
// Normalize campaignBrief: if a plain string is passed, wrap it in the
|
|
712
721
|
// { name, content } structure the UI expects. createCampaign already does
|
|
713
722
|
// this, but updateCampaign was passing the string straight through.
|
|
@@ -747,13 +756,39 @@ export async function updateCampaign(campaignId, input) {
|
|
|
747
756
|
!updates.currentStep) {
|
|
748
757
|
updates.currentStep = "filter-rules";
|
|
749
758
|
}
|
|
750
|
-
|
|
759
|
+
let result = null;
|
|
760
|
+
let senderRouteResult = null;
|
|
761
|
+
const shouldPreserveInteractionModePut = typeof interactionMode === "string" &&
|
|
762
|
+
interactionMode.trim().length > 0 &&
|
|
763
|
+
!hasSenderIds &&
|
|
764
|
+
Object.keys(updates).length === 0;
|
|
765
|
+
if (Object.keys(updates).length > 0 || shouldPreserveInteractionModePut) {
|
|
766
|
+
result = await api.put(`/api/v2/campaign-offers/${campaignId}`, updates);
|
|
767
|
+
}
|
|
768
|
+
if (hasSenderIds) {
|
|
769
|
+
const senderList = senderIds;
|
|
770
|
+
const senderCurrentStep = Array.isArray(senderList) && senderList.length === 0
|
|
771
|
+
? "settings"
|
|
772
|
+
: currentStep;
|
|
773
|
+
const senderPayload = { senderIds: senderList };
|
|
774
|
+
if (senderCurrentStep !== undefined) {
|
|
775
|
+
senderPayload.currentStep = senderCurrentStep;
|
|
776
|
+
}
|
|
777
|
+
senderRouteResult = await api.patch(`/api/v3/campaigns/${campaignId}/senders`, senderPayload);
|
|
778
|
+
result = await api.get(`/api/v2/campaign-offers/${campaignId}`);
|
|
779
|
+
}
|
|
780
|
+
if (!result) {
|
|
781
|
+
result = await api.put(`/api/v2/campaign-offers/${campaignId}`, updates);
|
|
782
|
+
}
|
|
751
783
|
// Serialize to only essential fields for context efficiency
|
|
752
784
|
return {
|
|
753
785
|
id: result.id,
|
|
754
786
|
name: result.name,
|
|
755
787
|
currentStep: result.currentStep,
|
|
756
788
|
updatedAt: result.updatedAt,
|
|
789
|
+
...(senderRouteResult?.remediation
|
|
790
|
+
? { remediation: senderRouteResult.remediation }
|
|
791
|
+
: {}),
|
|
757
792
|
// Provide full campaign snapshot for server-side context hydration.
|
|
758
793
|
_campaign: result,
|
|
759
794
|
};
|
package/dist/tools/context.d.ts
CHANGED
|
@@ -22,8 +22,8 @@ type CampaignContextEntry = {
|
|
|
22
22
|
dirty: boolean;
|
|
23
23
|
dirtyReasons: string[];
|
|
24
24
|
};
|
|
25
|
-
export declare function hydrateCampaignContextFromCampaign(campaign: CampaignOfferNavigation, reason: string): CampaignContextEntry;
|
|
26
|
-
export declare function hydrateCampaignContextFromNavigation(navState: Awaited<ReturnType<typeof getCampaignNavigationState>>, reason: string): CampaignContextEntry;
|
|
25
|
+
export declare function hydrateCampaignContextFromCampaign(campaign: CampaignOfferNavigation, reason: string, flowVersion?: "v1" | "v2"): CampaignContextEntry;
|
|
26
|
+
export declare function hydrateCampaignContextFromNavigation(navState: Awaited<ReturnType<typeof getCampaignNavigationState>>, reason: string, flowVersion?: "v1" | "v2"): CampaignContextEntry;
|
|
27
27
|
export declare function markCampaignContextDirty(campaignId: string, reason: string): void;
|
|
28
28
|
export declare const contextToolDefinitions: {
|
|
29
29
|
name: string;
|
|
@@ -62,8 +62,8 @@ export declare function getCampaignContext(input: GetCampaignContextInput): Prom
|
|
|
62
62
|
dirtyReasons: string[];
|
|
63
63
|
navigation: {
|
|
64
64
|
campaignId: string;
|
|
65
|
-
computedStep: "campaign-created" | "pick-provider" | "provider-search" | "confirm-lead-list";
|
|
66
|
-
blockedAt: ("campaign-created" | "pick-provider" | "provider-search" | "confirm-lead-list") | null;
|
|
65
|
+
computedStep: "running" | "messages" | "settings" | "sequence" | "send" | "campaign-created" | "pick-provider" | "provider-search" | "confirm-lead-list" | "filter-rules";
|
|
66
|
+
blockedAt: ("running" | "messages" | "settings" | "sequence" | "send" | "campaign-created" | "pick-provider" | "provider-search" | "confirm-lead-list" | "filter-rules") | null;
|
|
67
67
|
missing: string[];
|
|
68
68
|
expectedHeadlessStep: string | null;
|
|
69
69
|
headlessCurrentStep: string | null;
|
|
@@ -79,6 +79,12 @@ export declare function getCampaignContext(input: GetCampaignContextInput): Prom
|
|
|
79
79
|
leadSourceProvider: string | null;
|
|
80
80
|
selectedLeadListId: string | null;
|
|
81
81
|
providerCurrentStep: string | null;
|
|
82
|
+
enableICPFilters: boolean | null;
|
|
83
|
+
activeRubricCount: number;
|
|
84
|
+
hasApprovedMessageTemplate: boolean;
|
|
85
|
+
senderCount: number;
|
|
86
|
+
hasSequenceTemplate: boolean;
|
|
87
|
+
campaignStatus: string | null;
|
|
82
88
|
};
|
|
83
89
|
warnings: string[];
|
|
84
90
|
};
|
package/dist/tools/context.js
CHANGED
|
@@ -32,7 +32,7 @@ function deriveProviderConfig(framework, navState) {
|
|
|
32
32
|
return null;
|
|
33
33
|
return framework.providers[providerId] ?? null;
|
|
34
34
|
}
|
|
35
|
-
export function hydrateCampaignContextFromCampaign(campaign, reason) {
|
|
35
|
+
export function hydrateCampaignContextFromCampaign(campaign, reason, flowVersion = "v1") {
|
|
36
36
|
logNavigationDebug("context.hydrate_from_campaign.start", {
|
|
37
37
|
campaignId: campaign.id,
|
|
38
38
|
reason,
|
|
@@ -40,7 +40,6 @@ export function hydrateCampaignContextFromCampaign(campaign, reason) {
|
|
|
40
40
|
workflowTableId: campaign.workflowTableId ?? null,
|
|
41
41
|
leadSourceProvider: campaign.leadSourceProvider ?? null,
|
|
42
42
|
}, campaign.id);
|
|
43
|
-
const flowVersion = "v1";
|
|
44
43
|
const framework = getFrameworkSnapshot(flowVersion);
|
|
45
44
|
const navState = computeCampaignNavigationStateFromCampaign(campaign, {
|
|
46
45
|
includeTableCheck: false,
|
|
@@ -70,7 +69,7 @@ export function hydrateCampaignContextFromCampaign(campaign, reason) {
|
|
|
70
69
|
}, campaign.id);
|
|
71
70
|
return entry;
|
|
72
71
|
}
|
|
73
|
-
export function hydrateCampaignContextFromNavigation(navState, reason) {
|
|
72
|
+
export function hydrateCampaignContextFromNavigation(navState, reason, flowVersion = "v1") {
|
|
74
73
|
logNavigationDebug("context.hydrate_from_navigation.start", {
|
|
75
74
|
campaignId: navState.campaignId,
|
|
76
75
|
reason,
|
|
@@ -78,7 +77,6 @@ export function hydrateCampaignContextFromNavigation(navState, reason) {
|
|
|
78
77
|
blockedAt: navState.blockedAt,
|
|
79
78
|
missing: navState.missing,
|
|
80
79
|
}, navState.campaignId);
|
|
81
|
-
const flowVersion = "v1";
|
|
82
80
|
const framework = getFrameworkSnapshot(flowVersion);
|
|
83
81
|
const providerConfig = deriveProviderConfig(framework, navState);
|
|
84
82
|
const entry = {
|
|
@@ -178,6 +176,12 @@ export function markCampaignContextDirty(campaignId, reason) {
|
|
|
178
176
|
leadSourceProvider: null,
|
|
179
177
|
selectedLeadListId: null,
|
|
180
178
|
providerCurrentStep: null,
|
|
179
|
+
enableICPFilters: null,
|
|
180
|
+
activeRubricCount: 0,
|
|
181
|
+
hasApprovedMessageTemplate: false,
|
|
182
|
+
senderCount: 0,
|
|
183
|
+
hasSequenceTemplate: false,
|
|
184
|
+
campaignStatus: null,
|
|
181
185
|
},
|
|
182
186
|
warnings: [],
|
|
183
187
|
},
|
package/dist/tools/leads.d.ts
CHANGED
|
@@ -114,6 +114,8 @@ export type ImportLeadsInput = {
|
|
|
114
114
|
headlineICPCriteria?: string[];
|
|
115
115
|
rubricGuidelines?: string[];
|
|
116
116
|
};
|
|
117
|
+
type ImportLeadProvider = NonNullable<ImportLeadsInput["provider"]>;
|
|
118
|
+
type Phase126ImportProvider = Exclude<ImportLeadProvider, "apollo">;
|
|
117
119
|
export type CancelLeadImportInput = {
|
|
118
120
|
campaignOfferId: string;
|
|
119
121
|
tableId: string;
|
|
@@ -2331,7 +2333,7 @@ export declare function importLeads(input: ImportLeadsInput): Promise<{
|
|
|
2331
2333
|
createdLeadList?: undefined;
|
|
2332
2334
|
jobResult?: undefined;
|
|
2333
2335
|
} | {
|
|
2334
|
-
provider:
|
|
2336
|
+
provider: Phase126ImportProvider;
|
|
2335
2337
|
leadListId: string;
|
|
2336
2338
|
existingLeadListId: string;
|
|
2337
2339
|
needsModeSelection: boolean;
|
|
@@ -2379,7 +2381,7 @@ export declare function importLeads(input: ImportLeadsInput): Promise<{
|
|
|
2379
2381
|
createdLeadList?: undefined;
|
|
2380
2382
|
jobResult?: undefined;
|
|
2381
2383
|
} | {
|
|
2382
|
-
provider: "
|
|
2384
|
+
provider: "sales-nav" | "prospeo";
|
|
2383
2385
|
leadListId: string;
|
|
2384
2386
|
existingLeadListId: string;
|
|
2385
2387
|
needsModeSelection: boolean;
|
|
@@ -2411,7 +2413,7 @@ export declare function importLeads(input: ImportLeadsInput): Promise<{
|
|
|
2411
2413
|
createdLeadList?: undefined;
|
|
2412
2414
|
jobResult?: undefined;
|
|
2413
2415
|
} | {
|
|
2414
|
-
provider: "
|
|
2416
|
+
provider: "sales-nav" | "prospeo";
|
|
2415
2417
|
leadListId: string;
|
|
2416
2418
|
createdLeadList: any;
|
|
2417
2419
|
jobResult: any;
|
|
@@ -2471,3 +2473,4 @@ export declare function setHeadlineICPCriteria(input: SetHeadlineICPCriteriaInpu
|
|
|
2471
2473
|
criteriaCount: number;
|
|
2472
2474
|
message: string;
|
|
2473
2475
|
}>;
|
|
2476
|
+
export {};
|