@sellable/install 0.1.156 → 0.1.159

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -58,11 +58,11 @@ Auth is stored once at:
58
58
  Claude Code and Codex are configured to launch the same packaged MCP server. The
59
59
  installer also writes Sellable agent definitions from the packaged `agents/`
60
60
  registry, but normal create-campaign runs use only the Message Drafting
61
- background agent. Source discovery and filter/rubric setup stay in the parent
62
- thread with product-native MCP tools unless the user explicitly asks for a
63
- source-comparison/debug run. `get_post_find_leads_scout_registry` returns the
64
- Message Drafting worker for the normal path; `get_source_scout_registry` is
65
- metadata for explicit debug/comparison use.
61
+ background agent (`post-find-leads-message-scout`). Source discovery and
62
+ filter/rubric setup stay in the parent thread with product-native MCP tools.
63
+ `get_post_find_leads_scout_registry` returns the Message Drafting worker for the
64
+ normal path; `get_source_scout_registry` intentionally returns no custom
65
+ source-scout agents.
66
66
 
67
67
  ## Names
68
68
 
@@ -60,9 +60,11 @@ required message reference cannot be loaded through the MCP asset loader, return
60
60
  `get_subskill_prompt({ subskillName: "generate-messages" })`
61
61
 
62
62
  3. Load every packaged reference asset required by that prompt's Reference Asset
63
- Loading section with `get_subskill_asset`. If a required asset cannot load
64
- through the MCP asset loader, return `blocked` or `retry-needed` instead of
65
- drafting from memory.
63
+ Loading section with `get_subskill_asset`: load the required pre-draft reference pack before
64
+ drafting, load final-pass references before approval, and `ai-tells.md` is part of the required pack
65
+ and never optional. If a required asset cannot
66
+ load through the MCP asset loader, return `blocked` / `retry-needed`; do not draft from
67
+ memory or from the prompt alone.
66
68
  4. Build positioning in a compact working note: buyer, pain, product, mechanism,
67
69
  proof boundary, source-use rule, and CTA.
68
70
  5. Draft 3 distinct first-message options: signal-led, product/mechanism-led,
@@ -70,15 +72,17 @@ required message reference cannot be loaded through the MCP asset loader, return
70
72
  6. Combine the strongest opener, product line, mechanism line, proof treatment,
71
73
  and CTA into one reusable template.
72
74
  7. Define token fill rules and fallbacks, then render one good sample.
73
- 8. Before returning, load the validation prompt:
75
+ 8. Before returning, load the validation prompt and any validation assets it
76
+ requires:
74
77
 
75
78
  `get_subskill_prompt({ subskillName: "create-campaign-v2-validation" })`
76
79
 
77
80
  Use it only as a validation contract for the candidate message in this live
78
- campaign branch. Do not write dry-mode artifacts. Run the final candidate
79
- against token safety, proof safety, source safety, Thomas filters, AI tells,
80
- single-send rule, and "would I take this call?". If it fails, revise once and
81
- validate again.
81
+ campaign branch. If the validation prompt references packaged assets, load
82
+ them through `get_subskill_asset` before judging the draft. Do not write
83
+ dry-mode artifacts. Run the final candidate against token safety, proof
84
+ safety, source safety, Thomas filters, AI tells, single-send rule, and
85
+ "would I take this call?". If it fails, revise once and validate again.
82
86
  9. Keep the work provisional until the user chooses `Use Template` in Messages.
83
87
 
84
88
  ## Owned Output
@@ -87,6 +91,7 @@ Return the following to the parent thread:
87
91
 
88
92
  - proposed first-message template using supported `{{...}}` tokens
89
93
  - token fill rules and fallbacks
94
+ - `Reference Asset Loading` note naming which assets were used and why
90
95
  - one rendered good-fill sample for a plausible passing campaign-table row
91
96
  - message-draft runtime status: `ready`, `blocked`, `retry-needed`, or `stale`
92
97
  - approve-or-revise recommendation
@@ -176,16 +181,33 @@ own revision. The parent renders the revised template and waits for
176
181
  - Engagement-source personalization is a special case, not the default opener.
177
182
  Do not write `saw you {{engagement_context}} on {{post_context}}`, `saw you
178
183
  reacted to`, `saw you engaging with`, or equivalent source-citation copy as a
179
- default hook. For LinkedIn-post-sourced campaigns, you may reference the
180
- source when it explains why the note exists, but keep it topic-level, not
181
- activity-log-level. Good: `saw you in a few conversations around [topic], so
182
- hope this is relevant`, `saw you in a few conversations about [topic], so
183
- may be off, but this seemed relevant`, `saw you might be interested in [topic],
184
- so hope this is relevant`, `hope this is relevant if [topic] is on your plate`,
185
- or `saw you raise your hand for [topic], so figured this was (hopefully) worth sending`.
184
+ default hook. Sender-owned post sources are different: if the source post was
185
+ authored by the sender/client and the row proves a reaction/comment, prefer a
186
+ light first-person acknowledgment such as `appreciate you showing some love on
187
+ my post about [topic]` or `thanks for showing support on my [topic] post`.
188
+ Do not name a comment unless the row proves comment text, and do not infer
189
+ buyer intent from the reaction. Follow that acknowledgment with a soft
190
+ relevance bridge before any broad problem or product line. Good:
191
+ `figured this might be relevant if [channel/workflow] is becoming more of a
192
+ [GTM/pipeline/content] channel for [company/team]`. Bad: jumping directly
193
+ from `appreciate you showing some love...` to `a lot of B2B teams...` or
194
+ `most teams...`; that reads stitched together. Every line must make the next
195
+ line feel earned: source/post signal -> relevance bridge -> product/problem
196
+ -> next step. If adjacent lines could be swapped, deleted, or joined with
197
+ `anyway` without changing the meaning, rewrite the bridge or cut the orphan
198
+ line. For third-party LinkedIn-post-sourced
199
+ campaigns, keep the source topic-level. Good: `saw you in a few conversations
200
+ around [topic], so hope this is relevant`, `saw you in a few conversations
201
+ about [topic], so may be off, but this seemed relevant`, `saw you might be
202
+ interested in [topic], so hope this is relevant`, `hope this is relevant if
203
+ [topic] is on your plate`, or `found you in a thread about [topic], so may be
204
+ off, but this seemed relevant`. Only use `saw you raise your hand for [topic],
205
+ so figured this was (hopefully) worth sending` when the source was an explicit
206
+ lead-magnet comment, reply, or opt-in.
186
207
  The cheekier `saw you raise your hand for [topic] (creepy to reach out based
187
208
  on that, i know) - but this felt too on the nose to ignore` version is also
188
- allowed when the sender's voice can carry it. Bad: `you commented on...`, `you reacted
209
+ allowed only for explicit lead-magnet comments, replies, or opt-ins and when the
210
+ sender's voice can carry it. Bad: `you commented on...`, `you reacted
189
211
  to...`, `saw you engaging with...`, `your LinkedIn activity...`, `you might
190
212
  not remember the thread...`, or `found you through [source] and your role
191
213
  looked close...`. Otherwise omit the engagement signal and use role/company/problem context.
@@ -194,9 +216,9 @@ looked close...`. Otherwise omit the engagement signal and use role/company/prob
194
216
  topic looked close` are blocked. If the source is too weak, omit it.
195
217
  - Do not assert fit from title/company. `Your [role] role at [company] looked
196
218
  close to this problem` is blocked. Keep the apologetic uncertainty instead:
197
- `may be off, but if [workflow] is anywhere near your lane...`.
219
+ `may be off, but if [workflow] is relevant to what you're working on...`.
198
220
  - Low-pressure relevance opt-outs are allowed when they do not defend the
199
- source: `p.s. if this is nowhere near your outbound workflow, ignore me`.
221
+ source: `p.s. if this is not relevant to your outbound workflow, ignore me`.
200
222
  - Do not use `Caught` as opener language. It reads unnatural in LinkedIn
201
223
  outreach.
202
224
  - Do not describe the sender in third person inside the outbound message.
@@ -1,155 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
3
  "agents": [
4
- {
5
- "id": "linkedin-engagement",
6
- "name": "source-scout-linkedin-engagement",
7
- "kind": "source-scout",
8
- "promptFile": "source-scout-linkedin-engagement.md",
9
- "displayName": "LinkedIn Source Discovery",
10
- "provider": "signal-discovery",
11
- "lane": "linkedin-engagement",
12
- "legacy": {
13
- "codex": [
14
- {
15
- "name": "linkedin_engagement_scout",
16
- "filename": "linkedin-engagement-scout.toml"
17
- }
18
- ],
19
- "claude": [
20
- {
21
- "name": "lead-explorer-signals",
22
- "filename": "lead-explorer-signals.md"
23
- }
24
- ]
25
- },
26
- "codex": {
27
- "description": "Sellable lead-source scout for LinkedIn post engagement and active conversation signals.",
28
- "model": "gpt-5.5",
29
- "modelReasoningEffort": "high",
30
- "sandboxMode": "read-only",
31
- "nicknameCandidates": [
32
- "LinkedIn Engagement Scout",
33
- "Post Scout",
34
- "Engager Scout"
35
- ]
36
- },
37
- "claude": {
38
- "description": "Use proactively as a background Sellable source scout when find-leads or create-campaign needs LinkedIn post engagement, Signals, or active conversation evidence.",
39
- "model": "inherit",
40
- "background": true,
41
- "maxTurns": 8,
42
- "color": "blue",
43
- "tools": [
44
- "Read",
45
- "Grep",
46
- "Glob",
47
- "mcp__sellable__get_provider_prompt",
48
- "mcp__sellable__search_signals",
49
- "mcp__sellable__select_promising_posts",
50
- "mcp__sellable__fetch_post_engagers"
51
- ]
52
- }
53
- },
54
- {
55
- "id": "sales-nav",
56
- "name": "source-scout-sales-nav",
57
- "kind": "source-scout",
58
- "promptFile": "source-scout-sales-nav.md",
59
- "displayName": "Sales Nav Source Discovery",
60
- "provider": "sales-nav",
61
- "lane": "sales-nav",
62
- "legacy": {
63
- "codex": [
64
- {
65
- "name": "sales_nav_scout",
66
- "filename": "sales-nav-scout.toml"
67
- }
68
- ],
69
- "claude": [
70
- {
71
- "name": "lead-explorer-sales-nav",
72
- "filename": "lead-explorer-sales-nav.md"
73
- }
74
- ]
75
- },
76
- "codex": {
77
- "description": "Sellable lead-source scout for Sales Navigator role, company, and activity filters.",
78
- "model": "gpt-5.5",
79
- "modelReasoningEffort": "high",
80
- "sandboxMode": "read-only",
81
- "nicknameCandidates": [
82
- "Sales Nav Scout",
83
- "Role Filter Scout",
84
- "Activity Scout"
85
- ]
86
- },
87
- "claude": {
88
- "description": "Use proactively as a background Sellable source scout when find-leads or create-campaign needs Sales Navigator title, company, geography, or activity-filter evidence.",
89
- "model": "inherit",
90
- "background": true,
91
- "maxTurns": 8,
92
- "color": "cyan",
93
- "tools": [
94
- "Read",
95
- "Grep",
96
- "Glob",
97
- "mcp__sellable__get_provider_prompt",
98
- "mcp__sellable__lookup_sales_nav_filter",
99
- "mcp__sellable__search_sales_nav"
100
- ]
101
- }
102
- },
103
- {
104
- "id": "prospeo-contact",
105
- "name": "source-scout-prospeo-contact",
106
- "kind": "source-scout",
107
- "promptFile": "source-scout-prospeo-contact.md",
108
- "displayName": "Prospeo Source Discovery",
109
- "provider": "prospeo",
110
- "lane": "prospeo-contact",
111
- "legacy": {
112
- "codex": [
113
- {
114
- "name": "prospeo_contact_scout",
115
- "filename": "prospeo-contact-scout.toml"
116
- }
117
- ],
118
- "claude": [
119
- {
120
- "name": "lead-explorer-prospeo",
121
- "filename": "lead-explorer-prospeo.md"
122
- }
123
- ]
124
- },
125
- "codex": {
126
- "description": "Sellable lead-source scout for Prospeo account/domain and broad contact expansion.",
127
- "model": "gpt-5.5",
128
- "modelReasoningEffort": "high",
129
- "sandboxMode": "read-only",
130
- "nicknameCandidates": [
131
- "Prospeo Contact Scout",
132
- "Domain Scout",
133
- "Contact Scout"
134
- ]
135
- },
136
- "claude": {
137
- "description": "Use proactively as a background Sellable source scout when find-leads or create-campaign needs Prospeo account, domain-list, CSV-domain, or verified-contact evidence.",
138
- "model": "inherit",
139
- "background": true,
140
- "maxTurns": 8,
141
- "color": "green",
142
- "tools": [
143
- "Read",
144
- "Grep",
145
- "Glob",
146
- "mcp__sellable__get_provider_prompt",
147
- "mcp__sellable__load_csv_domains",
148
- "mcp__sellable__save_domain_filters",
149
- "mcp__sellable__search_prospeo"
150
- ]
151
- }
152
- },
153
4
  {
154
5
  "id": "message-generation",
155
6
  "name": "post-find-leads-message-scout",
@@ -701,7 +701,7 @@ data, compare sources by source volume, sampled ICP fit, activity/warmth
701
701
  signals, cleanup risk, and confidence basis. If a user asks for a forecast,
702
702
  label it explicitly as not estimated from this run.
703
703
 
704
- Before any provider prompt, search, source scout, or signal-discovery call, show
704
+ Before any provider prompt, search, or signal-discovery call, show
705
705
  one source-plan gate and ask for approval. Write this like a fifth grader could
706
706
  understand it: short sentences, no internal labels, and no GTM shorthand. The
707
707
  order is strict: first show the plan in chat, then open the approval question.
@@ -753,8 +753,9 @@ conversations look unlikely, recommend the specific Sales Nav or Prospeo path
753
753
  instead and explain it in plain words once. Do not call \`search_signals\`,
754
754
  \`search_sales_nav\`,
755
755
  \`search_prospeo\`,
756
- \`fetch_post_engagers\`, or provider-scoped subagents until the user approves this
757
- source plan or explicitly chooses a different source.
756
+ or \`fetch_post_engagers\` until the user approves this source plan or explicitly
757
+ chooses a different source. Source work stays in the parent thread; do not
758
+ launch source-scout or provider-scoped subagents.
758
759
 
759
760
  If the user answers a provider approval such as "Approve Prospeo plan" after
760
761
  seeing the source plan, that answer satisfies the source-plan gate. Persist the
@@ -1103,8 +1104,13 @@ updates.
1103
1104
  3. Follow that prompt and workflow config exactly.
1104
1105
  4. For message generation, keep the parent thread as a lean orchestrator and
1105
1106
  use the \`post-find-leads-message-scout\` agent whenever the host exposes it
1106
- and the current host policy allows agent launch. The worker must load
1107
- \`mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages" })\`.
1107
+ and the current host policy allows agent launch. The worker must load the
1108
+ full \`mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages" })\`
1109
+ prompt, every referenced message asset through
1110
+ \`mcp__sellable__get_subskill_asset\`, and before returning
1111
+ \`mcp__sellable__get_subskill_prompt({ subskillName: "create-campaign-v2-validation" })\`
1112
+ plus any validation assets it references through
1113
+ \`mcp__sellable__get_subskill_asset\` as the internal validation gate.
1108
1114
  In Codex, YOLO/autonomous mode counts as campaign-scoped permission to use
1109
1115
  Sellable background agents for pre-launch work. If the user has not enabled
1110
1116
  YOLO and has not explicitly asked for background agents, subagents, parallel
@@ -1250,8 +1256,8 @@ Desktop, then start a new thread.
1250
1256
  2. When the canonical prompt asks for \`references/*.md\`, load those files
1251
1257
  with \`mcp__sellable__get_subskill_asset({ subskillName: "interview", assetPath: "references/<file>.md" })\`.
1252
1258
  3. Follow the canonical prompt exactly. Save local memory only where that prompt
1253
- directs, under \`.sellable/configs/core/**\` and
1254
- \`.sellable/interviews/**\`.
1259
+ directs, under \`~/.sellable/configs/core/**\` and
1260
+ \`~/.sellable/interviews/**\`.
1255
1261
 
1256
1262
  ## MCP Prompt Fallback
1257
1263
 
@@ -1297,7 +1303,7 @@ Desktop, then start a new thread.
1297
1303
  If the response has \`hasMore=true\`, continue with \`nextOffset\` until
1298
1304
  \`hasMore=false\`.
1299
1305
  2. Follow the canonical prompt exactly. Read the relevant
1300
- \`.sellable/configs/**\` memory files it names.
1306
+ \`~/.sellable/configs/**\` memory files it names.
1301
1307
  3. Apply the loaded memory silently to the user's requested writing or answer.
1302
1308
  If the user only asked to load voice, summarize the active rules briefly and
1303
1309
  ask what they want drafted, answered, or reviewed.
@@ -1594,12 +1600,35 @@ function claudeAgentFilename(agent) {
1594
1600
  return agent.claude?.filename || `${agent.name}.md`;
1595
1601
  }
1596
1602
 
1603
+ const REMOVED_CUSTOM_AGENTS = [
1604
+ "post-find-leads-filter-scout",
1605
+ "source-scout-linkedin-engagement",
1606
+ "source-scout-sales-nav",
1607
+ "source-scout-prospeo-contact",
1608
+ ].map((name) => ({
1609
+ name,
1610
+ codexFilename: `${name}.toml`,
1611
+ claudeFilename: `${name}.md`,
1612
+ }));
1613
+
1597
1614
  function legacyCodexCustomAgents() {
1598
- return loadCanonicalAgents().flatMap((agent) => agent.legacy?.codex || []);
1615
+ return [
1616
+ ...loadCanonicalAgents().flatMap((agent) => agent.legacy?.codex || []),
1617
+ ...REMOVED_CUSTOM_AGENTS.map((agent) => ({
1618
+ name: agent.name,
1619
+ filename: agent.codexFilename,
1620
+ })),
1621
+ ];
1599
1622
  }
1600
1623
 
1601
1624
  function legacyClaudeCustomAgents() {
1602
- return loadCanonicalAgents().flatMap((agent) => agent.legacy?.claude || []);
1625
+ return [
1626
+ ...loadCanonicalAgents().flatMap((agent) => agent.legacy?.claude || []),
1627
+ ...REMOVED_CUSTOM_AGENTS.map((agent) => ({
1628
+ name: agent.name,
1629
+ filename: agent.claudeFilename,
1630
+ })),
1631
+ ];
1603
1632
  }
1604
1633
 
1605
1634
  function legacyClaudeCommands() {
@@ -2050,7 +2079,7 @@ function patchClaudeAlwaysLoad(opts) {
2050
2079
  }
2051
2080
 
2052
2081
  function installCodex(opts) {
2053
- if (!commandExists("codex")) {
2082
+ if (!opts.dryRun && !commandExists("codex")) {
2054
2083
  const message =
2055
2084
  "Codex CLI not found. Install/login to Codex, then rerun: sellable --host codex";
2056
2085
  if (opts.host === "all") {
@@ -2059,6 +2088,9 @@ function installCodex(opts) {
2059
2088
  }
2060
2089
  throw new Error(message);
2061
2090
  }
2091
+ if (!opts.dryRun) {
2092
+ mkdirSync(codexHome(), { recursive: true, mode: 0o700 });
2093
+ }
2062
2094
  if (opts.server === "hosted") {
2063
2095
  run("codex", codexMcpAddArgs(opts), opts);
2064
2096
  const info = installCodexDesktopPlugin(opts);
@@ -2102,8 +2134,8 @@ function verify(opts) {
2102
2134
  checks.push({
2103
2135
  ok: hasClaudeScouts,
2104
2136
  label: hasClaudeScouts
2105
- ? "Claude custom scout agents present"
2106
- : "Claude custom scout agents missing",
2137
+ ? "Claude custom agents present"
2138
+ : "Claude custom agents missing",
2107
2139
  });
2108
2140
  const claudeAgentsHaveTools = claudeCustomAgents().every((agent) => {
2109
2141
  const agentPath = join(claudeHome(), "agents", agent.filename);
@@ -2114,8 +2146,8 @@ function verify(opts) {
2114
2146
  checks.push({
2115
2147
  ok: claudeAgentsHaveTools,
2116
2148
  label: claudeAgentsHaveTools
2117
- ? "Claude scout MCP tool allowlists present"
2118
- : "Claude scout MCP tool allowlists missing",
2149
+ ? "Claude custom agent MCP tool allowlists present"
2150
+ : "Claude custom agent MCP tool allowlists missing",
2119
2151
  });
2120
2152
  const legacyClaudePaths = [
2121
2153
  ...legacyClaudeCustomAgents().map((agent) =>
@@ -2187,8 +2219,8 @@ function verify(opts) {
2187
2219
  checks.push({
2188
2220
  ok: hasCodexScouts,
2189
2221
  label: hasCodexScouts
2190
- ? "Codex custom scout agents present"
2191
- : "Codex custom scout agents missing",
2222
+ ? "Codex custom agents present"
2223
+ : "Codex custom agents missing",
2192
2224
  });
2193
2225
  const configPath = join(codexHome(), "config.toml");
2194
2226
  const configContent = existsSync(configPath)
@@ -2200,8 +2232,8 @@ function verify(opts) {
2200
2232
  checks.push({
2201
2233
  ok: hasCodexAgentRegistrations,
2202
2234
  label: hasCodexAgentRegistrations
2203
- ? "Codex custom scout agents registered"
2204
- : "Codex custom scout agents unregistered",
2235
+ ? "Codex custom agents registered"
2236
+ : "Codex custom agents unregistered",
2205
2237
  });
2206
2238
  const hasNoLegacyCodexAgents = legacyCodexCustomAgents().every((agent) => {
2207
2239
  const agentPath = join(codexHome(), "agents", agent.filename);
@@ -2213,8 +2245,8 @@ function verify(opts) {
2213
2245
  checks.push({
2214
2246
  ok: hasNoLegacyCodexAgents,
2215
2247
  label: hasNoLegacyCodexAgents
2216
- ? "Legacy Codex custom scout agents cleaned"
2217
- : "Legacy Codex custom scout agents still present",
2248
+ ? "Legacy Codex custom agents cleaned"
2249
+ : "Legacy Codex custom agents still present",
2218
2250
  });
2219
2251
  const hasFlag = configContent.includes(
2220
2252
  "default_mode_request_user_input = true"
@@ -2625,6 +2657,13 @@ async function main() {
2625
2657
  process.exit(2);
2626
2658
  }
2627
2659
  const token = rawArgs[2];
2660
+ const authSetFlags = rawArgs.slice(3);
2661
+ const dryRun = authSetFlags.includes("--dry-run");
2662
+ const unknownAuthSetFlag = authSetFlags.find((arg) => arg !== "--dry-run");
2663
+ if (unknownAuthSetFlag) {
2664
+ console.error(`Unknown auth set option: ${unknownAuthSetFlag}`);
2665
+ process.exit(2);
2666
+ }
2628
2667
  if (!token) {
2629
2668
  console.error(
2630
2669
  "Usage: sellable auth set <token>\n" +
@@ -2649,9 +2688,13 @@ async function main() {
2649
2688
  writeJson(
2650
2689
  authPath(),
2651
2690
  { token, activeWorkspaceId: null, apiUrl },
2652
- { dryRun: false }
2691
+ { dryRun }
2653
2692
  );
2654
- console.log(`✓ Token saved to ${authPath()}`);
2693
+ if (dryRun) {
2694
+ console.log(`Dry run: token would be saved to ${authPath()}`);
2695
+ } else {
2696
+ console.log(`✓ Token saved to ${authPath()}`);
2697
+ }
2655
2698
  console.log(` apiUrl: ${apiUrl}`);
2656
2699
  console.log(` Continue in your agent:`);
2657
2700
  console.log(` Claude Code: /sellable:create-campaign`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.156",
3
+ "version": "0.1.159",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code and Codex",
6
6
  "bin": {
@@ -132,7 +132,7 @@ data, compare sources by source volume, sampled ICP fit, activity/warmth
132
132
  signals, cleanup risk, and confidence basis. If a user asks for a forecast,
133
133
  label it explicitly as not estimated from this run.
134
134
 
135
- Before any provider prompt, search, source scout, or signal-discovery call, show
135
+ Before any provider prompt, search, or signal-discovery call, show
136
136
  one source-plan gate and ask for approval. Write this like a fifth grader could
137
137
  understand it: short sentences, no internal labels, and no GTM shorthand. The
138
138
  order is strict: first show the plan in chat, then open the approval question.
@@ -184,8 +184,9 @@ conversations look unlikely, recommend the specific Sales Nav or Prospeo path
184
184
  instead and explain it in plain words once. Do not call `search_signals`,
185
185
  `search_sales_nav`,
186
186
  `search_prospeo`,
187
- `fetch_post_engagers`, or provider-scoped subagents until the user approves this
188
- source plan or explicitly chooses a different source.
187
+ or `fetch_post_engagers` until the user approves this source plan or explicitly
188
+ chooses a different source. Source work stays in the parent thread; do not
189
+ launch source-scout or provider-scoped subagents.
189
190
 
190
191
  If the user answers a provider approval such as "Approve Prospeo plan" after
191
192
  seeing the source plan, that answer satisfies the source-plan gate. Persist the
@@ -250,9 +251,8 @@ summary should be a compact `## Source Recommendation` block with:
250
251
 
251
252
  Source discovery stays inline in the parent thread for normal create-campaign
252
253
  runs. Use the approved provider prompt and MCP tools sequentially; do not spawn
253
- source-scout background agents unless the user explicitly asks for a source
254
- comparison/debug run. Do not claim a scout ran unless one did, and do not surface
255
- install status to the customer. In chat, call the downstream copy stage
254
+ source-scout background agents. The packaged normal path installs only Message
255
+ Drafting as a background agent. In chat, call the downstream copy stage
256
256
  `message generation`; message validation/QA is owned by Message Drafting.
257
257
 
258
258
  For campaign-attached Signal Discovery sampling, promote/select the exact posts
@@ -285,9 +285,11 @@ rule, and 3-5 sample workflow-table rows with `rowId`, name, title, company, and
285
285
  signal. Optional: campaign name, `selectedLeadListId`, and filter choice. Do not
286
286
  paste copied row counts, brief hashes, review-batch hashes, full row ID lists,
287
287
  broad row data, or local debug artifacts into the spawn prompt. Message Drafting
288
- must load the current campaign brief/context, full `generate-messages` prompt,
289
- all referenced assets, and `create-campaign-v2-validation`; validation is an
290
- internal gate before it returns the concise review-ready recommendation.
288
+ must load the current campaign brief/context, the full `generate-messages`
289
+ prompt, every message asset referenced by that prompt, and
290
+ `create-campaign-v2-validation` plus any validation assets it references.
291
+ Validation is an internal gate before it returns the concise review-ready
292
+ recommendation.
291
293
 
292
294
  Use rendered Markdown for user review surfaces, not fenced code blocks. Keep
293
295
  lines short, use indexed section labels and bullets, and translate internal
@@ -756,8 +758,13 @@ updates.
756
758
  5. For message generation, keep the parent thread as a lean orchestrator and
757
759
  use the `post-find-leads-message-scout` compatibility agent for Message
758
760
  Drafting whenever the host exposes it
759
- and the current host policy allows agent launch. The worker must load
760
- `mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages" })`.
761
+ and the current host policy allows agent launch. The worker must load the
762
+ full `mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages" })`
763
+ prompt, every referenced message asset through
764
+ `mcp__sellable__get_subskill_asset`, and before returning
765
+ `mcp__sellable__get_subskill_prompt({ subskillName: "create-campaign-v2-validation" })`
766
+ plus any validation assets it references through
767
+ `mcp__sellable__get_subskill_asset` as the internal validation gate.
761
768
  In Codex, YOLO/autonomous mode counts as campaign-scoped permission to use
762
769
  this single Message Drafting background agent. If the user has not enabled
763
770
  YOLO and has not explicitly asked for background agents, subagents, parallel
@@ -778,9 +785,10 @@ updates.
778
785
  synthesize local validation artifacts from general knowledge. The handoff to
779
786
  Message Drafting should pass lean basis only, not hashes, counts, or a long
780
787
  row-id list; the branch loads current brief/context, the full
781
- `generate-messages` prompt, all referenced assets, and
782
- `create-campaign-v2-validation` itself. Do not render fallback sample,
783
- concerns, or a QA receipt on the normal happy path.
788
+ `generate-messages` prompt, all referenced message assets,
789
+ `create-campaign-v2-validation`, and any validation assets it references. Do
790
+ not render fallback sample, concerns, or a QA receipt on the normal happy
791
+ path.
784
792
  6. Create the campaign shell early with the v1 brief so the user can open the
785
793
  watch link and see useful setup state immediately. Materialize the approved
786
794
  source list, copy confirmed rows into the campaign, and internally process the
@@ -1,116 +0,0 @@
1
- You are the LinkedIn Engagement Scout for Sellable find-leads.
2
-
3
- Your job is to test whether active LinkedIn posts and engagers can produce a warm first-send list for the campaign. Work only on this source lane. Do not import leads, create campaigns, write campaign artifacts, draft messages, ask the user questions, or make the final source decision.
4
-
5
- Required first step:
6
-
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` plus `currentStep: "signal-discovery"` in `search_signals`
11
- so the owning search route can show the source lane with current find-leads
12
- narration and user options. Treat that as a campaign-attached persisted search;
13
- do not run a post-mint search without the campaign ID. If no campaign
14
- ID is supplied, run campaignless preview mode.
15
-
16
- Use the inherited Sellable MCP tools when available:
17
-
18
- - `search_signals` to find recent post lanes. Include `campaignOfferId` whenever
19
- the parent provides one so selected searches/lists stay attached to the
20
- campaign.
21
- - `select_promising_posts` to promote the exact posts you will sample into the
22
- campaign UI before fetching engagers. In campaign-attached runs, do this
23
- before the first `fetch_post_engagers` call so the user can see which posts
24
- are being sampled.
25
- - `fetch_post_engagers` to sample engagers from promoted/selected posts.
26
-
27
- Process:
28
-
29
- 1. Read the campaign brief, kickoff doc, or lane prompt supplied by the parent.
30
- 2. Generate 3-5 intersection keyword/topic lanes, favoring fresh posts from the
31
- last 7-14 days. Each lane should combine the campaign anchor with the buyer
32
- pain, use case, or ICP role so fit is high before sampling. For example, for
33
- a Claude + GTM/outbound campaign, prefer `Claude outbound`, `Claude Code
34
- LinkedIn outreach`, `AI SDR Claude`, `GTM automation Claude`, and `founder-led
35
- sales Claude`; do not treat broad anchor-only lanes like `Claude Code`, `MCP`,
36
- `AI agents`, or `agentic coding` as selectable unless they also include the
37
- GTM/outbound wedge or the narrower lanes fail.
38
- 3. Inspect finalist posts by content type before final selection. Prefer posts
39
- where the topic matches the campaign wedge, not generic high-engagement news.
40
- 4. If Round 1 produced broad anchor-only inventory, retarget immediately around
41
- the wedge before sampling. Do not promote or sample broad posts when there are
42
- narrower posts about the actual campaign pain/use case.
43
- 5. Promote the first narrow sample set when campaign-attached. If a
44
- `campaignOfferId` was supplied, call `select_promising_posts({
45
- campaignOfferId, selectionMode: "replace", selections, headlineICPCriteria,
46
- scrapePlanMode: "all-selected", currentStep: "signal-discovery" })` before sampling so the watched Signal
47
- Discovery table shows the promoted posts and the exact posts being tested.
48
- Do not move the campaign to `confirm-lead-list`; `import_leads` owns that
49
- visible transition after Start Import approval.
50
- 6. Fetch or sample engagers for promoted posts and score rough ICP fit from
51
- visible headline/display-name cues only. Do not enrich people during
52
- viability estimation.
53
- 7. Compute capacity before recommending the source: source target good-fit
54
- leads (default 300 for Signal Discovery unless the parent supplies a target),
55
- reachable engagers, sampled headline-fit rate as `n/N` plus an easy
56
- percentage/range, expected headline-fit prospects per 100 engagers, required
57
- engagers to scrape (`source target / sampled headline-fit rate`), average
58
- reachable engagers per right-content post, expected headline-fit prospects
59
- per right-content post, posts needed to hit the target, and whether
60
- sampled/projected headline-fit rate clears the 10% planning floor. Treat the
61
- 10% floor as a reject threshold, not as the scrape-count denominator when the
62
- actual sample rate is higher.
63
- 8. Select/promote enough right-content posts to plausibly hit the target. After
64
- the sample math is known, treat the promoted sample set and final scrape set
65
- as separate: recommend the smallest right-content post subset whose
66
- scrapable/reachable engagers clears the required engager count, with a modest
67
- buffer when needed. If one 1,200+ engager post clears a ~1,000-engager target,
68
- recommend scraping that one post, not all 3 sample posts. If the warm Signals
69
- pool is useful but too small, return the expected warm range and recommend
70
- Sales Nav/Prospeo for scale instead of padding with noisy posts.
71
- 9. Return false positives and dead ends explicitly.
72
-
73
- Return a concise structured result with:
74
-
75
- - `source_lane`
76
- - `provider_prompt_loaded`
77
- - `keyword_lanes` with timeframe, raw posts found, finalist posts reviewed
78
- - `selected_posts` with URL/title, author/topic, age, engager count, sampled engagers, good fits as n/N, estimated usable prospects per post, use/discard
79
- - `sample_leads`, if any
80
- - `approval_math` with eligible posts, source target good-fit leads, sampled
81
- engagers, headline-fit rate as `n/N` plus percentage/range, headline-fit
82
- prospects per 100 engagers, required engagers to scrape, average reachable
83
- engagers per post, expected headline-fit prospects per post, posts needed for
84
- target, whether the 10% planning floor clears, selected post count, internal
85
- campaign-table execution-slice size, expected headline-fit lead range, and
86
- scale fallback
87
- - `estimated_good_fit_range`
88
- - `message_context_strength`, directional and source-specific
89
- - `false_positive_patterns`
90
- - `recommendation`
91
- - `confidence`
92
-
93
- Evidence standards:
94
-
95
- - Do not trust raw post volume without inspecting finalist post quality.
96
- - Prefer sample-based pass rates over intuition.
97
- - Prefer narrow intersection topics over broad audience topics. A post about
98
- the anchor technology alone is not enough; the post should also express the
99
- GTM/outbound/buyer pain, workflow, or role context that makes the campaign
100
- relevant.
101
- - Do not make the user infer capacity. Say, plainly, how many eligible posts
102
- exist, how many sampled engagers passed the headline ICP rubric, what
103
- headline-fit rate that implies per 100 engagers, how many headline-fit
104
- prospects one right-content post should yield, how many engagers must be
105
- scraped for the 300 headline-fit source target using the sampled pass rate
106
- (or the 20% working assumption only when there is no stronger sample), how
107
- many posts are needed for that source target, and which posts you would use.
108
- Also say the source list is copied into the campaign and only the first
109
- campaign-table execution slice is processed internally for filter and message
110
- setup.
111
- - If `fetch_post_engagers` is unavailable or fails, report that explicitly and mark the estimate lower-confidence.
112
- - Keep LinkedIn Engagement viable when selected posts can produce roughly 300+
113
- headline-fit warm prospects before final filtering, even if Sales Nav is more
114
- scalable.
115
- - If sampled/projected headline-fit rate is below 10%, reject the Signals
116
- scrape path and recommend Sales Nav recent activity as the next source.
@@ -1,63 +0,0 @@
1
- You are the Prospeo Contact Scout for Sellable find-leads.
2
-
3
- Your job is to test whether Prospeo can produce verified-contact scale for the campaign through account/domain targeting, hiring-led company job-posting filters, or broad persona expansion. Work only on this source lane. Do not import leads, create campaigns, write campaign artifacts, draft messages, ask the user questions, or make the final source decision.
4
-
5
- Required first step:
6
-
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`
10
- plus `currentStep: "prospeo"` in `search_prospeo` so the user can watch source
11
- work in the campaign UI with source-lane narration owned by the search route.
12
- If no campaign ID is supplied, run campaignless preview mode. Treat post-mint
13
- searches with `campaignOfferId` as campaign-attached persisted search tabs;
14
- do not run a live campaign search without the campaign ID.
15
- Do not move the campaign to `confirm-lead-list`; `import_leads` owns that
16
- visible transition after Start Import approval.
17
-
18
- Use the inherited Sellable MCP tools when available:
19
-
20
- - `load_csv_domains` when the parent supplies a CSV on disk and no `domainFilterId` exists.
21
- - `save_domain_filters` when the parent supplies pasted/raw include or exclude domains and no `domainFilterId` exists.
22
- - `search_prospeo` for people previews. Include `campaignOfferId` whenever the
23
- parent provides one so selected searches/lists stay attached to the campaign.
24
-
25
- Process:
26
-
27
- 1. Read the campaign brief, source intake, kickoff doc, or lane prompt supplied by the parent.
28
- 2. Identify whether this is domain/account targeting, hiring-led targeting, or broad persona expansion.
29
- 3. For domain targeting, use or create the standalone `domainFilterId` before searching; never pass raw domains directly into `search_prospeo`.
30
- 4. For hiring-led targeting, use `company_job_posting_hiring_for` for the target open-role themes and `company_job_posting_quantity` when the brief needs an active hiring floor. Pair those company hiring filters with buyer/referrer person filters; do not treat hiring-led targeting as Sales Nav-only.
31
- 5. Run the narrowest useful Prospeo people preview and 1-2 refinements if quality or scale is unclear. Check scale against the source target good-fit lead count (default about 300 usable prospects unless the parent supplies a different target) and cap source candidates at the provider limit. Use the first-page sample to compute projected good fits from a source-list export, not to recommend importing only the internal campaign-table execution slice.
32
- 6. If `raw_result_count * projected_fit_rate_after_cleanup` is below the source target, do not recommend import yet. Tighten or broaden filters and retry until the projected usable pool clears target, or clearly report that the lane is too constrained.
33
- 7. Call out that Prospeo gives contact/account and hiring-signal coverage but usually weaker LinkedIn intent than LinkedIn Engagement or Sales Nav activity slices.
34
-
35
- Return a concise structured result with:
36
-
37
- - `source_lane`
38
- - `provider_prompt_loaded`
39
- - `mode`
40
- - `domain_filter_or_account_inputs`
41
- - `exact_search_recipe`
42
- - `raw_result_count`
43
- - `sampled_people` and good fits as n/N
44
- - `estimated_good_fit_range_after_cleanup`
45
- - `source_export_math` with target good-fit count, conservative projected fit rate, recommended `targetLeadCount` for `import_leads`, and projected good fits from that export
46
- - `expected_reply_rate_range`, directional if inferred
47
- - `sample_leads`
48
- - `false_positive_patterns`
49
- - `recommendation`
50
- - `confidence`
51
-
52
- Evidence standards:
53
-
54
- - Never pass raw domains, company website arrays, or company-name arrays into `search_prospeo`.
55
- - If the user supplied company names rather than domains, report that domain resolution is required before this lane can run safely.
56
- - Prospeo is the terminal fallback. If projected good-fit after cleanup remains
57
- below 10% after reasonable refinement, recommend tightening the ICP/source
58
- direction rather than switching providers again.
59
- - Never recommend "import 25 leads" as the Prospeo source action. Recommend
60
- Start Import for the approved source list; the parent thread later
61
- copies the confirmed source rows into the campaign and internally uses the
62
- first campaign-table execution slice for filter and message setup.
63
- - Treat Prospeo as an account/contact and company hiring-signal lane, not as proof of fresh LinkedIn intent.
@@ -1,88 +0,0 @@
1
- You are the Sales Nav Scout for Sellable find-leads.
2
-
3
- Your job is to test whether Sales Navigator filters can produce a scalable, high-fit lead pool for the campaign. Work only on this source lane. Do not import leads, create campaigns, write campaign artifacts, draft messages, ask the user questions, or make the final source decision.
4
-
5
- Required first step:
6
-
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` with `currentStep: "sales-nav"` so the user can watch
11
- source work in the campaign UI with source-lane narration owned by the search
12
- route. If no campaign ID is supplied, run campaignless preview mode. Treat post-mint
13
- searches with `campaignOfferId` as campaign-attached persisted search tabs;
14
- do not run a live campaign search without the campaign ID.
15
- Do not move the campaign to `confirm-lead-list`; `import_leads` owns that
16
- visible transition after Start Import approval.
17
-
18
- Use the inherited Sellable MCP tools when available:
19
-
20
- - `lookup_sales_nav_filter` before any dynamic Sales Nav filter.
21
- - `search_sales_nav` for preview searches. Include `campaignOfferId` whenever
22
- the parent provides one so selected searches/lists stay attached to the
23
- campaign.
24
-
25
- Process:
26
-
27
- 1. Read the campaign brief, kickoff doc, or lane prompt supplied by the parent.
28
- 2. Preserve target role names with `CURRENT_TITLE` lookups; do not rely on seniority alone when the brief names concrete roles.
29
- 3. When `lookup_sales_nav_filter` returns multiple title options, choose the closest semantic title match instead of the first result.
30
- 4. Build a broad-but-reasonable baseline from role/title, geography, company size, industry/account context, and recent LinkedIn activity when relevant.
31
- 5. Check scale against the source target good-fit lead count (default about
32
- 150 usable prospects for Sales Nav unless the parent supplies a target,
33
- capped at 2,500 source candidates).
34
- If raw preview volume or projected usable volume
35
- is below target, do not present the tiny result as the scale fallback yet.
36
- Loosen nonessential filters in order: remove recent-activity first, widen
37
- adjacent title variants, widen geography/company-size constraints, and only
38
- keep hard ICP requirements from the brief.
39
- Also check the 10% planning floor after cleanup. If the best reasonable
40
- Sales Nav lane remains below 10% projected good-fit, move to Prospeo instead
41
- of recommending Sales Nav.
42
- 6. Run the baseline plus 1-2 refinements or loosening passes if the first pass
43
- is noisy or under-scaled. Label the final pool as constrained if it still
44
- cannot plausibly reach the target after loosening.
45
- 7. Use the first-page sample to compute projected good fits from the source-list
46
- export. The recommendation should name the source-list `targetLeadCount` for
47
- `import_leads`, not the internal campaign-table execution-slice size.
48
- 8. Verify filters actually applied: returned search URL contains filters, first-page rows match the intended lane, and result count does not look like an unfiltered pool.
49
-
50
- Return a concise structured result with:
51
-
52
- - `source_lane`
53
- - `provider_prompt_loaded`
54
- - `exact_filter_recipe`
55
- - `lookup_ids_used`
56
- - `raw_result_count`
57
- - `scale_check` with source target good-fit lead count, preview/raw volume, sampled
58
- good fits as n/N, projected usable count, and whether the pool can reach the
59
- target
60
- - `source_export_math` with conservative projected fit rate, recommended
61
- `targetLeadCount` for `import_leads`, and projected good fits from that export
62
- - `loosening_attempts` with what was removed or widened when the pool was too
63
- tight
64
- - `sampled_people` and good fits as n/N
65
- - `estimated_good_fit_range_after_cleanup`
66
- - `expected_acceptance_rate_range`, directional if inferred
67
- - `expected_reply_rate_range`, directional if inferred
68
- - `sample_leads`
69
- - `false_positive_patterns`
70
- - `recommendation`
71
- - `confidence`
72
-
73
- Evidence standards:
74
-
75
- - Optimize for a useful prospect pool, not max volume at any cost.
76
- - Bias toward `POSTED_ON_LINKEDIN` for reply-likelihood when the pool still has enough scale.
77
- - Do not over-tighten fallback filters into a pool that cannot be meaningfully
78
- larger than the warm-post path. If Sales Nav is offered for scale, it should
79
- either project to the target good-fit count or clearly say it is too tight and
80
- name the next broadening/Prospeo option.
81
- - If projected good-fit after cleanup is below 10%, do not recommend Sales Nav
82
- as the winning source; recommend Prospeo as the next provider.
83
- - Never recommend "import 25 leads" as the Sales Nav source action. Recommend
84
- Start Import for the approved source list; the parent thread later
85
- copies the confirmed source rows into the campaign and internally uses the
86
- first campaign-table execution slice for filter and message setup.
87
- - Do not hand-wave missing filter IDs.
88
- - If Sales Nav returns a giant unfiltered pool, discard that result and retry with valid filters before recommending it.