@sellable/mcp 0.1.144 → 0.1.145
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 +14 -3
- package/agents/source-scout-linkedin-engagement.md +4 -3
- package/agents/source-scout-prospeo-contact.md +1 -1
- package/agents/source-scout-sales-nav.md +3 -2
- package/dist/server.js +29 -59
- package/dist/tools/auth.js +5 -5
- package/dist/tools/leads.js +72 -17
- package/dist/tools/registry.d.ts +4186 -0
- package/dist/tools/registry.js +59 -0
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +10 -9
- package/skills/create-campaign-v2/SKILL.md +6 -6
- package/skills/create-campaign-v2/SOUL.md +2 -2
- package/skills/create-campaign-v2/core/auto-execute.README.md +4 -4
- package/skills/create-campaign-v2/core/auto-execute.yaml +4 -4
- package/skills/create-campaign-v2/core/flow.v2.json +2 -2
- package/skills/create-campaign-v2/references/approval-gate-framing.md +1 -1
- package/skills/create-campaign-v2/references/filter-leads.md +42 -0
- package/skills/create-campaign-v2/references/sample-validation-loop.md +13 -13
- package/skills/create-campaign-v2/references/step-13-import-leads.md +9 -6
- package/skills/create-campaign-v2/references/watch-guide-narration.md +2 -2
- package/skills/load-voice/SKILL.md +129 -0
- package/skills/providers/prospeo.md +2 -1
- package/skills/providers/sales-nav.md +4 -2
- package/skills/providers/signal-discovery.md +12 -10
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { authToolDefinitions } from "./auth.js";
|
|
2
|
+
import { blueprintCommitToolDefinitions } from "./blueprint-commit.js";
|
|
3
|
+
import { bootstrapToolDefinitions } from "./bootstrap.js";
|
|
4
|
+
import { campaignToolDefinitions } from "./campaigns.js";
|
|
5
|
+
import { cellToolDefinitions } from "./cells.js";
|
|
6
|
+
import { startCliLoginToolDef, waitForCliLoginToolDef } from "./cli-login.js";
|
|
7
|
+
import { contextToolDefinitions } from "./context.js";
|
|
8
|
+
import { directCampaignToolDefinitions } from "./direct-campaigns.js";
|
|
9
|
+
import { engageBootstrapToolDefinitions } from "./engage-bootstrap.js";
|
|
10
|
+
import { engageDiscoveryToolDefinitions } from "./engage-discovery.js";
|
|
11
|
+
import { engageMemoryToolDefinitions } from "./engage-memory.js";
|
|
12
|
+
import { engageStateToolDefinitions } from "./engage-state.js";
|
|
13
|
+
import { enrichmentToolDefinitions } from "./enrichment.js";
|
|
14
|
+
import { frameworkToolDefinitions } from "./framework.js";
|
|
15
|
+
import { leadToolDefinitions } from "./leads.js";
|
|
16
|
+
import { linkedinToolDefinitions } from "./linkedin.js";
|
|
17
|
+
import { navigationToolDefinitions } from "./navigation.js";
|
|
18
|
+
import { onDemandToolDefinitions } from "./one-off.js";
|
|
19
|
+
import { processingToolDefinitions } from "./processing.js";
|
|
20
|
+
import { promptToolDefinitions } from "./prompts.js";
|
|
21
|
+
import { readinessToolDefinitions } from "./readiness.js";
|
|
22
|
+
import { rowToolDefinitions } from "./rows.js";
|
|
23
|
+
import { rubricToolDefinitions } from "./rubrics.js";
|
|
24
|
+
import { senderToolDefinitions } from "./senders.js";
|
|
25
|
+
import { sequencerToolDefinitions } from "./sequencer.js";
|
|
26
|
+
import { tableToolDefinitions } from "./tables.js";
|
|
27
|
+
import { verifyRowToolDefinitions } from "./verify-row.js";
|
|
28
|
+
import { workspaceToolDefinitions } from "./workspaces.js";
|
|
29
|
+
export const allTools = [
|
|
30
|
+
...campaignToolDefinitions,
|
|
31
|
+
...authToolDefinitions,
|
|
32
|
+
startCliLoginToolDef,
|
|
33
|
+
waitForCliLoginToolDef,
|
|
34
|
+
...bootstrapToolDefinitions,
|
|
35
|
+
...engageBootstrapToolDefinitions,
|
|
36
|
+
...engageDiscoveryToolDefinitions,
|
|
37
|
+
...workspaceToolDefinitions,
|
|
38
|
+
...frameworkToolDefinitions,
|
|
39
|
+
...contextToolDefinitions,
|
|
40
|
+
...navigationToolDefinitions,
|
|
41
|
+
...leadToolDefinitions,
|
|
42
|
+
...enrichmentToolDefinitions,
|
|
43
|
+
...processingToolDefinitions,
|
|
44
|
+
...rubricToolDefinitions,
|
|
45
|
+
...readinessToolDefinitions,
|
|
46
|
+
...rowToolDefinitions,
|
|
47
|
+
...cellToolDefinitions,
|
|
48
|
+
...promptToolDefinitions,
|
|
49
|
+
...linkedinToolDefinitions,
|
|
50
|
+
...onDemandToolDefinitions,
|
|
51
|
+
...directCampaignToolDefinitions,
|
|
52
|
+
...senderToolDefinitions,
|
|
53
|
+
...engageStateToolDefinitions,
|
|
54
|
+
...engageMemoryToolDefinitions,
|
|
55
|
+
...sequencerToolDefinitions,
|
|
56
|
+
...tableToolDefinitions,
|
|
57
|
+
...blueprintCommitToolDefinitions,
|
|
58
|
+
...verifyRowToolDefinitions,
|
|
59
|
+
];
|
package/package.json
CHANGED
|
@@ -152,13 +152,14 @@ For Signal Discovery, the customer-facing approval card must use the exact
|
|
|
152
152
|
action shape "Approve scraping N Signal Discovery posts?" and the chat summary
|
|
153
153
|
should be a compact `## Source Recommendation` block with:
|
|
154
154
|
|
|
155
|
-
-
|
|
156
|
-
-
|
|
157
|
-
-
|
|
155
|
+
- good-fit target: about 150 prospects after cleanup, enrichment, and filters
|
|
156
|
+
- source-candidate plan: about 1,000 raw engagers using a conservative 15%
|
|
157
|
+
fit-rate assumption unless sampled data supports a different number
|
|
158
|
+
- review checkpoint: import the first 25 leads for fit and message review
|
|
158
159
|
- a selected-post table with post author/topic, why it fits, and visible
|
|
159
160
|
engagement
|
|
160
161
|
- total visible pool and estimated good-fit pool
|
|
161
|
-
- first pass: build the source list, then import only
|
|
162
|
+
- first pass: build the source list, then import only the review
|
|
162
163
|
batch
|
|
163
164
|
- fallback: switch to Sales Nav recent activity if the review batch is
|
|
164
165
|
vendor-heavy, agency-heavy, or off-ICP
|
|
@@ -251,7 +252,7 @@ After every `update_campaign({ campaignId, currentStep })`, use
|
|
|
251
252
|
`get_campaign_navigation_state` when available as a compact orientation check:
|
|
252
253
|
match the saved campaign state to the expected watch-link step, explain the
|
|
253
254
|
current state in one sentence, and only then continue. Sender selection belongs
|
|
254
|
-
at Settings after message approval and
|
|
255
|
+
at Settings after message approval and review-batch validation. After message
|
|
255
256
|
validation, use Settings to help the user connect or select a LinkedIn sender.
|
|
256
257
|
Explain Slack reply review before launch. After sender selection, attach the
|
|
257
258
|
recommended sequence and move the watched UI to Send. Do not start the campaign
|
|
@@ -614,9 +615,9 @@ updates.
|
|
|
614
615
|
until `hasMore=false`. The create-campaign message-review safety gate is a
|
|
615
616
|
supplemental approval checklist, not a replacement for the long prompt. Use
|
|
616
617
|
campaign state, campaign brief content, selected source state, and imported
|
|
617
|
-
review-batch rows as the source of truth; do not read stale local markdown
|
|
618
|
-
inspect the database directly, or synthesize
|
|
619
|
-
general knowledge.
|
|
618
|
+
review-batch rows as the source of truth; do not read stale local markdown
|
|
619
|
+
such as `message-validation.md`, inspect the database directly, or synthesize
|
|
620
|
+
local validation artifacts from general knowledge.
|
|
620
621
|
5. Create the campaign shell early with the v1 brief so the user can open the
|
|
621
622
|
watch link and see useful setup state immediately. Import only the first
|
|
622
623
|
bounded review batch after the source is attached to the campaign; do not
|
|
@@ -640,7 +641,7 @@ updates.
|
|
|
640
641
|
`mcp__sellable__update_campaign({ campaignId, currentStep })` before major
|
|
641
642
|
visible work so the user can watch progress in the app: `create-offer` for
|
|
642
643
|
the brief, `pick-provider` or the selected provider step while sourcing,
|
|
643
|
-
`filter-choice` after the
|
|
644
|
+
`filter-choice` after the review batch, `create-icp-rubric` as soon
|
|
644
645
|
as filters are approved, `apply-icp-rubric` after rubrics save and while
|
|
645
646
|
waiting for message-template approval, `validate-sample` while the approved
|
|
646
647
|
template unlocks bounded enrichment/filter scoring on Filter Leads,
|
|
@@ -167,9 +167,9 @@ selected posts exist:
|
|
|
167
167
|
|
|
168
168
|
Use Signal Discovery first.
|
|
169
169
|
|
|
170
|
-
**
|
|
171
|
-
**
|
|
172
|
-
**
|
|
170
|
+
**Good-fit target:** ~150 prospects after cleanup, enrichment, and filters<br>
|
|
171
|
+
**Source-candidate plan:** scrape ~1,000 raw engagers using a conservative 15% fit-rate assumption<br>
|
|
172
|
+
**Review checkpoint:** import the first 25 leads into the campaign for fit and message review before scaling<br>
|
|
173
173
|
|
|
174
174
|
### Selected posts
|
|
175
175
|
|
|
@@ -186,12 +186,12 @@ Use Signal Discovery first.
|
|
|
186
186
|
|
|
187
187
|
Approve scraping these 3 posts.
|
|
188
188
|
|
|
189
|
-
This gives enough volume to
|
|
189
|
+
This gives enough volume to work toward ~150 good-fit prospects, while
|
|
190
190
|
keeping the source tied to people already engaging with Claude Code outbound /
|
|
191
191
|
AI-native sales workflows.
|
|
192
192
|
|
|
193
|
-
**First pass:** build the source list, then import only
|
|
194
|
-
|
|
193
|
+
**First pass:** build the source list, then import only the 25-lead review
|
|
194
|
+
batch so we can inspect fit and messages before scaling.
|
|
195
195
|
|
|
196
196
|
**Fallback:** if the review batch is too vendor-heavy, agency-heavy, or
|
|
197
197
|
off-ICP, switch to Sales Nav recent activity.
|
|
@@ -238,8 +238,8 @@ for source approval before import.
|
|
|
238
238
|
|
|
239
239
|
For Signal Discovery, the second gate is not "approve source" in the abstract.
|
|
240
240
|
It is a concrete scrape approval: show a compact `## Source Recommendation`
|
|
241
|
-
with the ~
|
|
242
|
-
table, total visible pool, estimated good-fit pool, first-pass
|
|
241
|
+
with the ~150 good-fit prospect goal, 15% raw-engager assumption, selected-post
|
|
242
|
+
table, total visible pool, estimated good-fit pool, first-pass review
|
|
243
243
|
batch, and fallback. The approval question should be "Approve scraping N Signal
|
|
244
244
|
Discovery posts?"
|
|
245
245
|
|
|
@@ -33,16 +33,16 @@ the imported review batch for validation).
|
|
|
33
33
|
|
|
34
34
|
- **`sampleSize`** — How many rows the validation loop pulls from the
|
|
35
35
|
imported review batch. In v2 this matches the review-batch cap
|
|
36
|
-
(default
|
|
36
|
+
(default 25), so the user sees enough real rows to judge the campaign
|
|
37
37
|
without spending credits on hundreds more.
|
|
38
38
|
- **`minProjectedPass`** — Passing-message floor required before handoff.
|
|
39
|
-
The default requires
|
|
39
|
+
The default requires 5 passing examples from the 25-row review batch.
|
|
40
40
|
Math:
|
|
41
41
|
```text
|
|
42
42
|
projectedPass = round(passInSample / sampleSize * importLimit)
|
|
43
43
|
```
|
|
44
|
-
With defaults (sampleSize=
|
|
45
|
-
the actual review-batch pass count.
|
|
44
|
+
With defaults (sampleSize=25, importLimit=25), `projectedPass` equals
|
|
45
|
+
the actual review-batch pass count. 5 passing rows is exactly at the
|
|
46
46
|
floor.
|
|
47
47
|
- **`maxRevisionRounds`** — Hard cap before escalating to the user. A
|
|
48
48
|
revision round is one full sample pass that triggered brief revision
|
|
@@ -17,7 +17,7 @@ import:
|
|
|
17
17
|
# Initial review batch imported in Step 13 before the sample validation
|
|
18
18
|
# loop. Import + enrichment must never exceed this cap before the user
|
|
19
19
|
# reviews the sample and explicitly asks to expand.
|
|
20
|
-
importLimit:
|
|
20
|
+
importLimit: 25
|
|
21
21
|
# Provider is inherited from the campaign-attached source association created
|
|
22
22
|
# during find-leads. Step 13 may re-run provider preflight in memory, but it
|
|
23
23
|
# does not choose a new source.
|
|
@@ -27,11 +27,11 @@ sample:
|
|
|
27
27
|
# Rows pulled from the imported cohort for the validation loop. In v2 this
|
|
28
28
|
# equals the review-batch cap so the user sees a focused first test batch
|
|
29
29
|
# without spending credits on hundreds of rows.
|
|
30
|
-
sampleSize:
|
|
30
|
+
sampleSize: 25
|
|
31
31
|
# Projected first-batch passing count required to proceed to sample
|
|
32
32
|
# messaging. This is not approval to scale the full source list.
|
|
33
33
|
# projectedPass = round(passInSample / sampleSize * importLimit).
|
|
34
|
-
minProjectedPass:
|
|
34
|
+
minProjectedPass: 5
|
|
35
35
|
# Hard cap on revision loops before escalating to the user. On a stale
|
|
36
36
|
# resume the counter does NOT reset — the 3-round cap holds across
|
|
37
37
|
# sessions.
|
|
@@ -106,7 +106,7 @@ handoff:
|
|
|
106
106
|
# Orientation surfaced in Step 16 ("awaiting-user-greenlight") along with
|
|
107
107
|
# the watch link. Keep this short and user-facing.
|
|
108
108
|
orientation: >-
|
|
109
|
-
Review the first
|
|
109
|
+
Review the first 25 enriched leads and messages. If they look good,
|
|
110
110
|
attach a sender in Settings, accept the recommended sequence, and start
|
|
111
111
|
the campaign without spending credits on more leads first.
|
|
112
112
|
|
|
@@ -460,7 +460,7 @@
|
|
|
460
460
|
"requiredInlineFields": [
|
|
461
461
|
"primary source and exact filters/recipe",
|
|
462
462
|
"specific source action awaiting approval",
|
|
463
|
-
"for Signal Discovery: Source Recommendation markdown with
|
|
463
|
+
"for Signal Discovery: Source Recommendation markdown with ~150 good-fit prospects as the target, source-candidate plan ~1,000 raw engagers at a conservative ~15% fit assumption, review checkpoint size, selected-post table, total visible pool, estimated good-fit pool, first pass, and fallback",
|
|
464
464
|
"for Signal Discovery: selected post count and target engager/source-candidate volume",
|
|
465
465
|
"bounded review batch size",
|
|
466
466
|
"runner-up and why it lost",
|
|
@@ -541,7 +541,7 @@
|
|
|
541
541
|
"importLimit"
|
|
542
542
|
],
|
|
543
543
|
"requiredValues": {
|
|
544
|
-
"importLimit":
|
|
544
|
+
"importLimit": 25
|
|
545
545
|
},
|
|
546
546
|
"modeAddHandshake": {
|
|
547
547
|
"firstCallReturns": "needsModeSelection when adding to an existing campaign-attached list",
|
|
@@ -15,7 +15,7 @@ packet.
|
|
|
15
15
|
|
|
16
16
|
The approval gate used to be the place where the user authorized
|
|
17
17
|
spend/send-adjacent mutation. It is now compatibility-only. New runs import the
|
|
18
|
-
bounded
|
|
18
|
+
bounded review batch after source approval, save rubrics and the approved
|
|
19
19
|
message template after post-import review, and only then queue the cascade.
|
|
20
20
|
|
|
21
21
|
If `campaign-shell.json` exists, a draft `CampaignOffer` row already exists but
|
|
@@ -174,6 +174,14 @@ If more than roughly 20% of the review batch appears to be the forbidden side,
|
|
|
174
174
|
classify it as a list/source problem and return `revise-find-leads`; do not
|
|
175
175
|
hide that problem behind narrow rubrics.
|
|
176
176
|
|
|
177
|
+
## Suppression And Relationship Safety
|
|
178
|
+
|
|
179
|
+
Do not put DNC or one-off relationship-safety notes into production rubrics
|
|
180
|
+
unless the review batch shows that the pattern will leak at meaningful volume
|
|
181
|
+
and normal DNC/domain suppression will not catch it. Former employers, existing
|
|
182
|
+
customers, investors, partner lists, and one-off do-not-contact domains usually
|
|
183
|
+
belong in the recommendation as DNC/suppression instructions outside the rubric JSON.
|
|
184
|
+
|
|
177
185
|
## Production Rubric Shape
|
|
178
186
|
|
|
179
187
|
Confirmed rubrics must use the production `LeadScoringRubric` fields exactly:
|
|
@@ -265,6 +273,40 @@ Interpretation:
|
|
|
265
273
|
- Density dramatically below the band: likely source contamination, ICP drift,
|
|
266
274
|
or provider-side weakness; investigate before launch.
|
|
267
275
|
|
|
276
|
+
## Post-Filter Signal Enrichment Pass (Sales Nav lane)
|
|
277
|
+
|
|
278
|
+
Run this only for Sales Nav FIT rows when the downstream message step does not
|
|
279
|
+
already have row-level post evidence. In normal campaign-backed runs, use live
|
|
280
|
+
campaign-table row state as the source of truth. In explicit debug/UAT artifact
|
|
281
|
+
flows, append the diagnostic copy back to `lead-sample.json`.
|
|
282
|
+
|
|
283
|
+
This pass exists to protect the Earned-right and Read-as-1:1 substance filters.
|
|
284
|
+
Without row-level posts, message generation is likely to fall back to generic
|
|
285
|
+
category openers or presumed intent instead of evidence the recipient can
|
|
286
|
+
recognize.
|
|
287
|
+
|
|
288
|
+
Contract:
|
|
289
|
+
|
|
290
|
+
- scope: Sales Nav FIT rows only; do not enrich MAYBE or REJECT rows for this
|
|
291
|
+
pass
|
|
292
|
+
- budget cap: top 10 FIT rows by filter score
|
|
293
|
+
- tool: `mcp__sellable__fetch_linkedin_posts`, called per row
|
|
294
|
+
- fetch: most recent 3-5 public posts when available
|
|
295
|
+
- target surface: store each result as `recent_posts[]` entries with
|
|
296
|
+
`{ url, posted_at, excerpt }`; keep `recent_posts: []` for quiet profiles
|
|
297
|
+
- order of operations: run AFTER `lead-filter.md` is written, BEFORE
|
|
298
|
+
`message-validation.md` starts drafting
|
|
299
|
+
|
|
300
|
+
Record enrichment outcome in the filter summary: rows attempted, rows with
|
|
301
|
+
posts, rows with zero public posts, and tool failures. If more than half of the
|
|
302
|
+
attempted rows have zero posts, flag low signal density and tell message
|
|
303
|
+
generation to prefer trigger, peer-observation, or non-post proof instead of a
|
|
304
|
+
post-quote opener.
|
|
305
|
+
|
|
306
|
+
Do not convert "recently posted" or post availability into an ICP fit rubric.
|
|
307
|
+
The posts are message evidence only; buyer qualification still comes from role,
|
|
308
|
+
authority, account fit, capacity, geography, and exclusions.
|
|
309
|
+
|
|
268
310
|
## Decision Rules
|
|
269
311
|
|
|
270
312
|
- Use `confirmed` when the source lane is viable and the rubrics are enough to
|
|
@@ -6,7 +6,7 @@ on every revision round.
|
|
|
6
6
|
|
|
7
7
|
## Principle
|
|
8
8
|
|
|
9
|
-
We spend a bounded review batch (default
|
|
9
|
+
We spend a bounded review batch (default 25 rows) to prove fit before the
|
|
10
10
|
user spends credits on hundreds more leads. The sample loop has one job:
|
|
11
11
|
answer the question "do we have enough real passing examples for the user to
|
|
12
12
|
judge this campaign?" Message generation starts earlier: the first row that
|
|
@@ -23,7 +23,7 @@ auto-revise leads.
|
|
|
23
23
|
- `CampaignOffer.currentStep === "apply-icp-rubric"` (the watched app is
|
|
24
24
|
already on Filter Leads after `save_rubrics`; `validate-sample` is the
|
|
25
25
|
logical loop name, not a visible route)
|
|
26
|
-
- Imported review batch from Step 13 (size: `importLimit`, default
|
|
26
|
+
- Imported review batch from Step 13 (size: `importLimit`, default 25)
|
|
27
27
|
- Config from `auto-execute.yaml`: `sample.sampleSize`,
|
|
28
28
|
`sample.minProjectedPass`, `sample.maxRevisionRounds`
|
|
29
29
|
- Persisted counter `revisionRound` (starts at 0 on first entry; persists
|
|
@@ -56,7 +56,7 @@ auto-revise leads.
|
|
|
56
56
|
6. wait_for_rubric_results(sample, targetCount = <cohortSize>, minPassedCount = 1)
|
|
57
57
|
- cohortSize = stats.totalRows of the enrichment batch, or the
|
|
58
58
|
imported batch count
|
|
59
|
-
- default targetCount=
|
|
59
|
+
- default targetCount=25 matches the default review batch, but pass the
|
|
60
60
|
explicit batch count anyway so future larger expansion batches do not
|
|
61
61
|
accidentally stop early
|
|
62
62
|
(see §Known Tool Behaviors #3)
|
|
@@ -172,8 +172,8 @@ company, enrichCellId, enrichStatus) over the default shape.
|
|
|
172
172
|
|
|
173
173
|
### 5. `wait_for_rubric_results` can timeout with enough signal to decide
|
|
174
174
|
|
|
175
|
-
Observed: a
|
|
176
|
-
and partial stats such as
|
|
175
|
+
Observed: a 25-row review batch may return `ready=false`, `reason="timeout"`,
|
|
176
|
+
and partial stats such as 20/25 scored, 4 passing, 4 messages generated. That is
|
|
177
177
|
enough to diagnose an underperforming sample. Waiting again without active
|
|
178
178
|
processing makes the experience feel frozen.
|
|
179
179
|
|
|
@@ -190,16 +190,16 @@ once only if active processing is still visible.
|
|
|
190
190
|
projectedPass = round(passInSample / sampleSize * importLimit)
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
-
Worked examples with defaults (sampleSize=
|
|
193
|
+
Worked examples with defaults (sampleSize=25, importLimit=25):
|
|
194
194
|
|
|
195
|
-
| passInSample | projectedPass | Handoff? (minProjectedPass=
|
|
195
|
+
| passInSample | projectedPass | Handoff? (minProjectedPass=5) |
|
|
196
196
|
| ------------ | ------------- | ----------------------------- |
|
|
197
197
|
| 0 | 0 | No — escalate or revise |
|
|
198
|
-
|
|
|
199
|
-
|
|
|
200
|
-
|
|
|
198
|
+
| 3 | 3 | No — escalate or revise |
|
|
199
|
+
| 5 | 5 | Yes — exactly at floor |
|
|
200
|
+
| 8 | 8 | Yes |
|
|
201
201
|
| 10 | 10 | Yes |
|
|
202
|
-
|
|
|
202
|
+
| 25 | 25 | Yes |
|
|
203
203
|
|
|
204
204
|
When non-default importLimit/sampleSize are configured, the math scales
|
|
205
205
|
the same way. In the default review-batch mode, the sample size equals the
|
|
@@ -263,8 +263,8 @@ rate is zero with no pattern that brief editing could fix.
|
|
|
263
263
|
|
|
264
264
|
Signal examples:
|
|
265
265
|
|
|
266
|
-
-
|
|
267
|
-
-
|
|
266
|
+
- 25/25 rows are wrong function / wrong seniority / wrong geo.
|
|
267
|
+
- 20/25 rows enrich to empty strings for the tokens the brief needs.
|
|
268
268
|
- Rubric passes 0 rows and the failure reasons are all "not ICP."
|
|
269
269
|
- **Marketplace supply-side contamination:** >20% of sample rows match
|
|
270
270
|
the brief §14 forbidden side (e.g. Skillsync sample contains
|
|
@@ -55,7 +55,7 @@ Supported branches:
|
|
|
55
55
|
`import_leads({ campaignOfferId })` for Signal Discovery unless selected posts
|
|
56
56
|
already exist on the campaign. Provider import materializes the source lead
|
|
57
57
|
list from the approved source-capacity plan; it is not the same target as the
|
|
58
|
-
|
|
58
|
+
default 25-row campaign review batch.
|
|
59
59
|
- **Supplied LinkedIn profile CSV** — confirm `load_csv_linkedin_leads` only
|
|
60
60
|
after rubrics and the approved message set are ready. Batch/materialize the
|
|
61
61
|
uploaded CSV into a Sellable lead-list table. Persist the returned
|
|
@@ -106,9 +106,9 @@ fills the source lead list; it does **not** clone rows into the campaign table:
|
|
|
106
106
|
```text
|
|
107
107
|
import_leads({
|
|
108
108
|
campaignOfferId,
|
|
109
|
-
targetLeadCount: <
|
|
109
|
+
targetLeadCount: <sourceCandidateTarget; provider default>,
|
|
110
110
|
// Signal Discovery only:
|
|
111
|
-
targetEngagerCount: <ceil(
|
|
111
|
+
targetEngagerCount: <ceil(targetGoodFitLeads / sampledFitRateAfterCleanup)>,
|
|
112
112
|
maxPostsToScrape: <postsNeeded from approved math>
|
|
113
113
|
})
|
|
114
114
|
```
|
|
@@ -120,9 +120,12 @@ so dedup ratios are visible.
|
|
|
120
120
|
|
|
121
121
|
For Signal Discovery, do not scrape every currently selected/promoted sample
|
|
122
122
|
post by default. Before `import_leads`, reconcile selected posts with the
|
|
123
|
-
approved source math.
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
approved source math. The default good-fit target is 150 and the conservative
|
|
124
|
+
fallback fit rate is 15%, so the default source-candidate plan is about 1,000
|
|
125
|
+
raw engagers. If the sampled fit rate is stronger or weaker, use the sampled
|
|
126
|
+
rate with a conservative cleanup factor, cap the source candidates at the
|
|
127
|
+
approved provider limit, and select only enough posts to reach that engager
|
|
128
|
+
count. The subsequent
|
|
126
129
|
`confirm_lead_list` call still uses `targetLeadCount: <importLimit>` so only the
|
|
127
130
|
bounded review batch enters the campaign table.
|
|
128
131
|
|
|
@@ -178,8 +178,8 @@ Source approved and import starting:
|
|
|
178
178
|
{
|
|
179
179
|
"stage": "review-batch",
|
|
180
180
|
"headline": "Importing the review batch",
|
|
181
|
-
"visibleState": "The browser is still showing the approved LinkedIn Engagement source
|
|
182
|
-
"agentIntent": "Codex is importing only the bounded
|
|
181
|
+
"visibleState": "The browser is still showing the approved LinkedIn Engagement source candidates.",
|
|
182
|
+
"agentIntent": "Codex is importing only the bounded review batch into the campaign now.",
|
|
183
183
|
"nextAction": "Review batch ready"
|
|
184
184
|
}
|
|
185
185
|
```
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: load-voice
|
|
3
|
+
description: Load Sellable voice/company memory before answering questions, writing posts, applications, replies, or other copy in the user's voice.
|
|
4
|
+
visibility: public
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Glob
|
|
8
|
+
- Grep
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Load Sellable Voice
|
|
12
|
+
|
|
13
|
+
<role>
|
|
14
|
+
You are the Sellable voice loader. Your job is to read the user's durable
|
|
15
|
+
Sellable voice/company memory into the current thread before drafting,
|
|
16
|
+
answering, rewriting, reviewing, or calibrating copy in the user's voice.
|
|
17
|
+
|
|
18
|
+
This is a read-only usage workflow. Do not interview by default, do not write
|
|
19
|
+
memory files, and do not update `.sellable/**` unless the user explicitly asks
|
|
20
|
+
to switch into `$sellable:interview`.
|
|
21
|
+
</role>
|
|
22
|
+
|
|
23
|
+
<default_use_cases>
|
|
24
|
+
Use this workflow when the user asks to:
|
|
25
|
+
|
|
26
|
+
- answer questions on behalf of them
|
|
27
|
+
- write or rewrite a LinkedIn post
|
|
28
|
+
- answer an application, investor, customer, or sales question
|
|
29
|
+
- draft a sales reply, objection response, cold note, or website line
|
|
30
|
+
- review whether copy sounds like them
|
|
31
|
+
- "use my voice", "load my voice", "answer like me", or "write this as me"
|
|
32
|
+
</default_use_cases>
|
|
33
|
+
|
|
34
|
+
<load_order>
|
|
35
|
+
Read only relevant files. Start with the active `.sellable/configs` memory
|
|
36
|
+
layer, not random repo docs.
|
|
37
|
+
|
|
38
|
+
If `.sellable/configs` is not visible from the current working directory, use
|
|
39
|
+
`Glob` to find `**/.sellable/configs/core/my-company.md`, excluding
|
|
40
|
+
`node_modules`, `.next`, `dist`, and `.git`, then use the nearest parent as the
|
|
41
|
+
Sellable memory root.
|
|
42
|
+
|
|
43
|
+
## Core Voice And Judgment
|
|
44
|
+
|
|
45
|
+
Read these when present:
|
|
46
|
+
|
|
47
|
+
- `.sellable/configs/core/about-me.md`
|
|
48
|
+
- `.sellable/configs/core/anti-ai-writing-style.md`
|
|
49
|
+
- `.sellable/configs/core/decision-rules.md`
|
|
50
|
+
- `.sellable/configs/core/context-modes.md`
|
|
51
|
+
|
|
52
|
+
## Company Truth And Proof
|
|
53
|
+
|
|
54
|
+
Read these when present:
|
|
55
|
+
|
|
56
|
+
- `.sellable/configs/core/my-company.md`
|
|
57
|
+
- `.sellable/configs/core/answer-bank.md`
|
|
58
|
+
- `.sellable/configs/core/proof-ledger.md`
|
|
59
|
+
- `.sellable/configs/core/story-bank.md`
|
|
60
|
+
- `.sellable/configs/core/wins-ledger.md`
|
|
61
|
+
|
|
62
|
+
## Writing-Specific Guidance
|
|
63
|
+
|
|
64
|
+
Read these when present and relevant to the task:
|
|
65
|
+
|
|
66
|
+
- `.sellable/configs/writing/styleguide-core.md`
|
|
67
|
+
- `.sellable/configs/writing/comments.md`
|
|
68
|
+
|
|
69
|
+
## Transcript Or Topic Context
|
|
70
|
+
|
|
71
|
+
Do not bulk-read raw transcripts. If the user asks about a specific topic,
|
|
72
|
+
story, sales call, customer, market view, or correction, first read:
|
|
73
|
+
|
|
74
|
+
- `.sellable/configs/core/transcripts/INDEX.md`
|
|
75
|
+
- the most relevant file under `.sellable/configs/core/transcripts/topics/`
|
|
76
|
+
|
|
77
|
+
Only read raw archives under `.sellable/interviews/**` when the index/topic file
|
|
78
|
+
points to one and the current task needs that source detail.
|
|
79
|
+
</load_order>
|
|
80
|
+
|
|
81
|
+
<operating_rules>
|
|
82
|
+
|
|
83
|
+
- Keep the loader lightweight. The first pass should usually be 6-10 files, not
|
|
84
|
+
every file under `.sellable`.
|
|
85
|
+
- Apply the loaded profile silently after a short confirmation. Do not paste a
|
|
86
|
+
long summary of the memory unless the user asks for one.
|
|
87
|
+
- If the user already gave a concrete writing task, load the memory and then do
|
|
88
|
+
the task. Do not stop at "I loaded the files."
|
|
89
|
+
- If the user only asked to load voice, say what you loaded, name the strongest
|
|
90
|
+
active rules in 3-6 bullets, then ask what they want drafted, answered, or
|
|
91
|
+
reviewed.
|
|
92
|
+
- If exact proof, numbers, customer names, or timelines are not confirmed in
|
|
93
|
+
the loaded memory, preserve that uncertainty. Do not launder uncertain proof
|
|
94
|
+
into clean claims.
|
|
95
|
+
- Use the current `context-modes.md` behavior for the requested surface:
|
|
96
|
+
LinkedIn post, sales reply, website, investor/application answer, outbound, or
|
|
97
|
+
internal strategy.
|
|
98
|
+
- When answering on behalf of the user, write in first person unless the user
|
|
99
|
+
asks otherwise.
|
|
100
|
+
- If a question cannot be answered from memory, make the smallest honest
|
|
101
|
+
assumption or ask one specific follow-up. Do not invent private history,
|
|
102
|
+
metrics, customer facts, or beliefs.
|
|
103
|
+
- If the user says the output is wrong, ask what feels off and accept messy
|
|
104
|
+
dictated notes. Use that correction in the current thread. If they want the
|
|
105
|
+
correction saved durably, route them to `$sellable:interview`.
|
|
106
|
+
</operating_rules>
|
|
107
|
+
|
|
108
|
+
<response_shape>
|
|
109
|
+
After loading memory, use this shape when there is no concrete draft task yet:
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
I loaded your Sellable voice/company memory from {n} files. I’ll apply it
|
|
113
|
+
silently unless you override it.
|
|
114
|
+
|
|
115
|
+
The active rules I’m carrying forward:
|
|
116
|
+
- ...
|
|
117
|
+
|
|
118
|
+
What should I answer, write, or review?
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
When there is already a concrete task, keep the confirmation to one sentence,
|
|
122
|
+
then produce the answer or draft.
|
|
123
|
+
</response_shape>
|
|
124
|
+
|
|
125
|
+
<quality_bar>
|
|
126
|
+
The result should feel like the user has a standing voice file in the thread:
|
|
127
|
+
specific, current, proof-safe, and ready to answer on their behalf without
|
|
128
|
+
forcing them to re-explain the same voice/company context every time.
|
|
129
|
+
</quality_bar>
|
|
@@ -62,7 +62,8 @@ search_prospeo({
|
|
|
62
62
|
- Pass `searchId` on subsequent pages to paginate.
|
|
63
63
|
- Use `import_leads` with `provider: \"prospeo\"` and the `searchId` to create a lead list and start import.
|
|
64
64
|
- **IMPORTANT:** If `import_leads` returns `needsModeSelection: true`, use `AskUserQuestion` to ask "add to existing leads or replace?" Do NOT assume.
|
|
65
|
-
-
|
|
65
|
+
- Default source target is 300+ good-fit leads, capped at 2,500 source candidates for now.
|
|
66
|
+
- After the list finishes and the user confirms it looks good, call `confirm_lead_list` with the `jobId` and the review-batch `targetLeadCount` from the active campaign defaults to import only the review batch into the campaign table.
|
|
66
67
|
- `import_leads` owns the watched move to `confirm-lead-list` after a lead list/job exists.
|
|
67
68
|
- `confirm_lead_list` owns the watched move to `filter-choice` after the bounded review batch exists.
|
|
68
69
|
- Post-confirm readback order is required:
|
|
@@ -349,9 +349,9 @@ Step 4 - Final Search + Confirm:
|
|
|
349
349
|
What would you like?"
|
|
350
350
|
|
|
351
351
|
User: "Save it"
|
|
352
|
-
→ import_leads({campaignOfferId: "cmp_xxx", searchId: "search_xxx", targetLeadCount:
|
|
352
|
+
→ import_leads({campaignOfferId: "cmp_xxx", searchId: "search_xxx", targetLeadCount: 300})
|
|
353
353
|
→ (wait for lead list to finish, ask user to confirm)
|
|
354
|
-
→ confirm_lead_list({campaignOfferId: "cmp_xxx", jobId: "<jobId from import_leads>", targetLeadCount:
|
|
354
|
+
→ confirm_lead_list({campaignOfferId: "cmp_xxx", jobId: "<jobId from import_leads>", targetLeadCount: 25})
|
|
355
355
|
→ wait_for_campaign_table_ready({campaignId: "cmp_xxx"})
|
|
356
356
|
→ get_campaign_context({campaignId: "cmp_xxx", refresh: true}) + get_rows_minimal(...)
|
|
357
357
|
</iteration_example>
|
|
@@ -382,6 +382,8 @@ User: "Save it"
|
|
|
382
382
|
<limits>
|
|
383
383
|
- 25 results per page
|
|
384
384
|
- Maximum 100 pages (2,500 leads)
|
|
385
|
+
- Default source target: 300+ good-fit leads, capped at 2,500 source
|
|
386
|
+
candidates for now
|
|
385
387
|
- Maximum 5 search calls per session
|
|
386
388
|
- If user needs more than 2,500: explain limit, suggest splitting by region
|
|
387
389
|
</limits>
|