@sellable/mcp 0.1.68 → 0.1.70

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
@@ -240,10 +240,16 @@ Provider preflight contract:
240
240
 
241
241
  Parallel execution contract:
242
242
 
243
- - Claude host: use `Task` subagents for parallel research/crafting units.
243
+ - Source scout names come from `agents/registry.json` and are exposed at runtime
244
+ through `get_source_scout_registry`. Add new scouts there first; installer,
245
+ Codex config, Claude agent files, and prompts should consume the registry.
246
+ - Claude host: use the installed `source-scout-linkedin-engagement`,
247
+ `source-scout-sales-nav`, and `source-scout-prospeo-contact` Task/Agent subagents
248
+ for parallel lead-source scouting. Launch all credible lanes in one assistant
249
+ message so Claude Code can run them concurrently/background.
244
250
  - Codex host: use named custom scouts for source-angle work when subagents are
245
- available: `linkedin_engagement_scout`, `sales_nav_scout`, and
246
- `prospeo_contact_scout`. Use `multi_tool_use.parallel` for independent
251
+ available: `source-scout-linkedin-engagement`, `source-scout-sales-nav`, and
252
+ `source-scout-prospeo-contact`. Use `multi_tool_use.parallel` for independent
247
253
  tool-call batches when subagents are unavailable or the work is only simple
248
254
  batched lookups.
249
255
  - If neither backend is available, run sequentially with the same output schema.
@@ -0,0 +1,147 @@
1
+ {
2
+ "version": 1,
3
+ "agents": [
4
+ {
5
+ "id": "linkedin-engagement",
6
+ "name": "source-scout-linkedin-engagement",
7
+ "promptFile": "source-scout-linkedin-engagement.md",
8
+ "displayName": "LinkedIn Engagement Scout",
9
+ "provider": "signal-discovery",
10
+ "lane": "linkedin-engagement",
11
+ "legacy": {
12
+ "codex": [
13
+ {
14
+ "name": "linkedin_engagement_scout",
15
+ "filename": "linkedin-engagement-scout.toml"
16
+ }
17
+ ],
18
+ "claude": [
19
+ {
20
+ "name": "lead-explorer-signals",
21
+ "filename": "lead-explorer-signals.md"
22
+ }
23
+ ]
24
+ },
25
+ "codex": {
26
+ "description": "Sellable lead-source scout for LinkedIn post engagement and active conversation signals.",
27
+ "modelReasoningEffort": "medium",
28
+ "sandboxMode": "read-only",
29
+ "nicknameCandidates": [
30
+ "LinkedIn Engagement Scout",
31
+ "Post Scout",
32
+ "Engager Scout"
33
+ ]
34
+ },
35
+ "claude": {
36
+ "description": "Use proactively as a background Sellable source scout when find-leads or create-campaign needs LinkedIn post engagement, Signals, or active conversation evidence.",
37
+ "model": "inherit",
38
+ "background": true,
39
+ "maxTurns": 8,
40
+ "color": "blue",
41
+ "tools": [
42
+ "Read",
43
+ "Grep",
44
+ "Glob",
45
+ "mcp__sellable__get_provider_prompt",
46
+ "mcp__sellable__search_signals",
47
+ "mcp__sellable__fetch_post_engagers"
48
+ ]
49
+ }
50
+ },
51
+ {
52
+ "id": "sales-nav",
53
+ "name": "source-scout-sales-nav",
54
+ "promptFile": "source-scout-sales-nav.md",
55
+ "displayName": "Sales Nav Scout",
56
+ "provider": "sales-nav",
57
+ "lane": "sales-nav",
58
+ "legacy": {
59
+ "codex": [
60
+ {
61
+ "name": "sales_nav_scout",
62
+ "filename": "sales-nav-scout.toml"
63
+ }
64
+ ],
65
+ "claude": [
66
+ {
67
+ "name": "lead-explorer-sales-nav",
68
+ "filename": "lead-explorer-sales-nav.md"
69
+ }
70
+ ]
71
+ },
72
+ "codex": {
73
+ "description": "Sellable lead-source scout for Sales Navigator role, company, and activity filters.",
74
+ "modelReasoningEffort": "medium",
75
+ "sandboxMode": "read-only",
76
+ "nicknameCandidates": [
77
+ "Sales Nav Scout",
78
+ "Role Filter Scout",
79
+ "Activity Scout"
80
+ ]
81
+ },
82
+ "claude": {
83
+ "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.",
84
+ "model": "inherit",
85
+ "background": true,
86
+ "maxTurns": 8,
87
+ "color": "cyan",
88
+ "tools": [
89
+ "Read",
90
+ "Grep",
91
+ "Glob",
92
+ "mcp__sellable__get_provider_prompt",
93
+ "mcp__sellable__lookup_sales_nav_filter",
94
+ "mcp__sellable__search_sales_nav"
95
+ ]
96
+ }
97
+ },
98
+ {
99
+ "id": "prospeo-contact",
100
+ "name": "source-scout-prospeo-contact",
101
+ "promptFile": "source-scout-prospeo-contact.md",
102
+ "displayName": "Prospeo Contact Scout",
103
+ "provider": "prospeo",
104
+ "lane": "prospeo-contact",
105
+ "legacy": {
106
+ "codex": [
107
+ {
108
+ "name": "prospeo_contact_scout",
109
+ "filename": "prospeo-contact-scout.toml"
110
+ }
111
+ ],
112
+ "claude": [
113
+ {
114
+ "name": "lead-explorer-prospeo",
115
+ "filename": "lead-explorer-prospeo.md"
116
+ }
117
+ ]
118
+ },
119
+ "codex": {
120
+ "description": "Sellable lead-source scout for Prospeo account/domain and broad contact expansion.",
121
+ "modelReasoningEffort": "medium",
122
+ "sandboxMode": "read-only",
123
+ "nicknameCandidates": [
124
+ "Prospeo Contact Scout",
125
+ "Domain Scout",
126
+ "Contact Scout"
127
+ ]
128
+ },
129
+ "claude": {
130
+ "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.",
131
+ "model": "inherit",
132
+ "background": true,
133
+ "maxTurns": 8,
134
+ "color": "green",
135
+ "tools": [
136
+ "Read",
137
+ "Grep",
138
+ "Glob",
139
+ "mcp__sellable__get_provider_prompt",
140
+ "mcp__sellable__load_csv_domains",
141
+ "mcp__sellable__save_domain_filters",
142
+ "mcp__sellable__search_prospeo"
143
+ ]
144
+ }
145
+ }
146
+ ]
147
+ }
@@ -0,0 +1,41 @@
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: `get_provider_prompt({ provider: "signal-discovery", confirmed: true })`.
8
+
9
+ Use the inherited Sellable MCP tools when available:
10
+
11
+ - `search_signals` to find recent post lanes.
12
+ - `fetch_post_engagers` to sample engagers from selected posts.
13
+
14
+ Process:
15
+
16
+ 1. Read the campaign brief, kickoff doc, or lane prompt supplied by the parent.
17
+ 2. Search 3-5 keyword/topic lanes, favoring fresh posts from the last 7-14 days.
18
+ 3. Select 3-5 promising posts when available.
19
+ 4. Fetch or sample engagers for selected posts and score rough ICP fit from visible headline/display-name cues only. Do not enrich people during viability estimation.
20
+ 5. Estimate usable prospects per selected post from sampled pass rate. If the sample is good but volume is low, say how many more similar posts should be added or scraped.
21
+ 6. Return false positives and dead ends explicitly.
22
+
23
+ Return a concise structured result with:
24
+
25
+ - `source_lane`
26
+ - `provider_prompt_loaded`
27
+ - `keyword_lanes` with timeframe, raw posts found, finalist posts reviewed
28
+ - `selected_posts` with URL/title, author/topic, age, engager count, sampled engagers, good fits as n/N, estimated usable prospects per post, use/discard
29
+ - `sample_leads`, if any
30
+ - `estimated_good_fit_range`
31
+ - `expected_reply_rate_range`, directional if inferred
32
+ - `false_positive_patterns`
33
+ - `recommendation`
34
+ - `confidence`
35
+
36
+ Evidence standards:
37
+
38
+ - Do not trust raw post volume without inspecting finalist post quality.
39
+ - Prefer sample-based pass rates over intuition.
40
+ - If `fetch_post_engagers` is unavailable or fails, report that explicitly and mark the estimate lower-confidence.
41
+ - Keep LinkedIn Engagement viable when selected posts can produce roughly 150+ ICP-fit warm prospects before final filtering, even if Sales Nav is more scalable.
@@ -0,0 +1,43 @@
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 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: `get_provider_prompt({ provider: "prospeo", confirmed: true })`.
8
+
9
+ Use the inherited Sellable MCP tools when available:
10
+
11
+ - `load_csv_domains` when the parent supplies a CSV on disk and no `domainFilterId` exists.
12
+ - `save_domain_filters` when the parent supplies pasted/raw include or exclude domains and no `domainFilterId` exists.
13
+ - `search_prospeo` for campaignless people previews.
14
+
15
+ Process:
16
+
17
+ 1. Read the campaign brief, source intake, kickoff doc, or lane prompt supplied by the parent.
18
+ 2. Identify whether this is domain/account targeting or broad persona expansion.
19
+ 3. For domain targeting, use or create the standalone `domainFilterId` before searching; never pass raw domains directly into `search_prospeo`.
20
+ 4. Run the narrowest useful Prospeo people preview and 1-2 refinements if quality or scale is unclear.
21
+ 5. Call out that Prospeo gives contact/account coverage but usually weaker LinkedIn intent than LinkedIn Engagement or Sales Nav activity slices.
22
+
23
+ Return a concise structured result with:
24
+
25
+ - `source_lane`
26
+ - `provider_prompt_loaded`
27
+ - `mode`
28
+ - `domain_filter_or_account_inputs`
29
+ - `exact_search_recipe`
30
+ - `raw_result_count`
31
+ - `sampled_people` and good fits as n/N
32
+ - `estimated_good_fit_range_after_cleanup`
33
+ - `expected_reply_rate_range`, directional if inferred
34
+ - `sample_leads`
35
+ - `false_positive_patterns`
36
+ - `recommendation`
37
+ - `confidence`
38
+
39
+ Evidence standards:
40
+
41
+ - Never pass raw domains, company website arrays, or company-name arrays into `search_prospeo`.
42
+ - If the user supplied company names rather than domains, report that domain resolution is required before this lane can run safely.
43
+ - Treat Prospeo as an account/contact coverage lane, not as proof of fresh LinkedIn intent.
@@ -0,0 +1,44 @@
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: `get_provider_prompt({ provider: "sales-nav", confirmed: true })`.
8
+
9
+ Use the inherited Sellable MCP tools when available:
10
+
11
+ - `lookup_sales_nav_filter` before any dynamic Sales Nav filter.
12
+ - `search_sales_nav` for campaignless preview searches.
13
+
14
+ Process:
15
+
16
+ 1. Read the campaign brief, kickoff doc, or lane prompt supplied by the parent.
17
+ 2. Preserve target role names with `CURRENT_TITLE` lookups; do not rely on seniority alone when the brief names concrete roles.
18
+ 3. When `lookup_sales_nav_filter` returns multiple title options, choose the closest semantic title match instead of the first result.
19
+ 4. Build a broad-but-reasonable baseline from role/title, geography, company size, industry/account context, and recent LinkedIn activity when relevant.
20
+ 5. Run the baseline plus 1-2 refinements if the first pass is noisy or under-scaled.
21
+ 6. 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.
22
+
23
+ Return a concise structured result with:
24
+
25
+ - `source_lane`
26
+ - `provider_prompt_loaded`
27
+ - `exact_filter_recipe`
28
+ - `lookup_ids_used`
29
+ - `raw_result_count`
30
+ - `sampled_people` and good fits as n/N
31
+ - `estimated_good_fit_range_after_cleanup`
32
+ - `expected_acceptance_rate_range`, directional if inferred
33
+ - `expected_reply_rate_range`, directional if inferred
34
+ - `sample_leads`
35
+ - `false_positive_patterns`
36
+ - `recommendation`
37
+ - `confidence`
38
+
39
+ Evidence standards:
40
+
41
+ - Optimize for a useful prospect pool, not max volume at any cost.
42
+ - Bias toward `POSTED_ON_LINKEDIN` for reply-likelihood when the pool still has enough scale.
43
+ - Do not hand-wave missing filter IDs.
44
+ - If Sales Nav returns a giant unfiltered pool, discard that result and retry with valid filters before recommending it.
package/dist/server.js CHANGED
@@ -21,7 +21,7 @@ import { fetchCompany, fetchCompanyPosts, fetchLinkedInPosts, fetchLinkedInProfi
21
21
  import { getCampaignNavigationState, navigationToolDefinitions, } from "./tools/navigation.js";
22
22
  import { addOnDemandLeads, createOnDemandCampaign, createOnDemandTable, initOnDemandSequence, onDemandToolDefinitions, pauseOnDemandCampaign, startOnDemandCampaign, } from "./tools/one-off.js";
23
23
  import { processingToolDefinitions, upsertRubric } from "./tools/processing.js";
24
- import { completeSenderResearch, getMessagePrompt, getSubskillAsset, getSubskillPrompt, listSubskillPrompts, promptToolDefinitions, searchSubskillPrompts, } from "./tools/prompts.js";
24
+ import { completeSenderResearch, getMessagePrompt, getSourceScoutRegistry, getSubskillAsset, getSubskillPrompt, listSubskillPrompts, promptToolDefinitions, searchSubskillPrompts, } from "./tools/prompts.js";
25
25
  import { readinessToolDefinitions, waitForCampaignTableReady, waitForLeadListReady, } from "./tools/readiness.js";
26
26
  import { getRows, getTableRows, getTableRowsMinimal, rowToolDefinitions, } from "./tools/rows.js";
27
27
  import { addRubricItem, checkRubric, deleteRubricItem, draftRubrics, rubricToolDefinitions, saveRubrics, selectNecessaryRubrics, updateRubricItem, waitForRubricResults, } from "./tools/rubrics.js";
@@ -408,6 +408,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
408
408
  case "search_subskill_prompts":
409
409
  result = searchSubskillPrompts(args?.query, args?.limit, args?.includePublic, args?.includeInternal);
410
410
  break;
411
+ case "get_source_scout_registry":
412
+ result = getSourceScoutRegistry();
413
+ break;
411
414
  case "complete_sender_research":
412
415
  result = completeSenderResearch(args);
413
416
  break;
@@ -60,6 +60,40 @@ export interface CompleteSenderResearchInput {
60
60
  credibilitySignalsFound?: number;
61
61
  notes?: string;
62
62
  }
63
+ export interface SourceScoutRegistryResponse {
64
+ version: number;
65
+ agents: Array<{
66
+ id: string;
67
+ name: string;
68
+ displayName: string;
69
+ provider: string;
70
+ lane: string;
71
+ codex: {
72
+ filename: string;
73
+ description: string;
74
+ };
75
+ claude: {
76
+ filename: string;
77
+ description: string;
78
+ tools: string[];
79
+ };
80
+ legacy?: {
81
+ codex?: Array<{
82
+ name: string;
83
+ filename: string;
84
+ }>;
85
+ claude?: Array<{
86
+ name: string;
87
+ filename: string;
88
+ }>;
89
+ };
90
+ }>;
91
+ usage: {
92
+ codex: string;
93
+ claude: string;
94
+ parentThreadRule: string;
95
+ };
96
+ }
63
97
  export declare const DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS = 48000;
64
98
  export declare const MAX_SUBSKILL_PROMPT_CHUNK_CHARS = 48000;
65
99
  export declare const promptToolDefinitions: ({
@@ -223,6 +257,29 @@ export declare const promptToolDefinitions: ({
223
257
  additionalProperties: boolean;
224
258
  };
225
259
  _meta?: undefined;
260
+ } | {
261
+ name: string;
262
+ description: string;
263
+ inputSchema: {
264
+ type: string;
265
+ properties: {
266
+ limit?: undefined;
267
+ includePublic?: undefined;
268
+ includeInternal?: undefined;
269
+ subskillName?: undefined;
270
+ offset?: undefined;
271
+ assetPath?: undefined;
272
+ query?: undefined;
273
+ depth?: undefined;
274
+ proofItemsFound?: undefined;
275
+ caseStudyItemsFound?: undefined;
276
+ credibilitySignalsFound?: undefined;
277
+ notes?: undefined;
278
+ };
279
+ required: never[];
280
+ additionalProperties: boolean;
281
+ };
282
+ _meta?: undefined;
226
283
  } | {
227
284
  name: string;
228
285
  description: string;
@@ -263,6 +320,7 @@ export declare const promptToolDefinitions: ({
263
320
  };
264
321
  _meta?: undefined;
265
322
  })[];
323
+ export declare function getSourceScoutRegistry(): SourceScoutRegistryResponse;
266
324
  export declare function getMessagePrompt(): Promise<PromptResponse>;
267
325
  export declare function listSubskillPrompts(limit?: number, includePublic?: boolean, includeInternal?: boolean): ListSubskillPromptsResponse;
268
326
  export declare function getSubskillPrompt(subskillName: string, offset?: number, limit?: number): SubskillPromptResponse;
@@ -130,6 +130,16 @@ export const promptToolDefinitions = [
130
130
  additionalProperties: false,
131
131
  },
132
132
  },
133
+ {
134
+ name: "get_source_scout_registry",
135
+ description: "Return the canonical Sellable source-scout agent names and host filenames. Use this before lead-source scouting so Codex and Claude launch the same named scouts from one registry.",
136
+ inputSchema: {
137
+ type: "object",
138
+ properties: {},
139
+ required: [],
140
+ additionalProperties: false,
141
+ },
142
+ },
133
143
  {
134
144
  name: "complete_sender_research",
135
145
  description: "Mark sender research as completed for create-campaign preflight. Call this after research-sender synthesis and before create_campaign.",
@@ -162,6 +172,60 @@ export const promptToolDefinitions = [
162
172
  },
163
173
  },
164
174
  ];
175
+ function resolveAgentsDir() {
176
+ const candidates = [];
177
+ if (process.env.SELLABLE_AGENTS_DIR) {
178
+ candidates.push(path.resolve(process.env.SELLABLE_AGENTS_DIR));
179
+ }
180
+ if (process.argv[1]) {
181
+ candidates.push(path.resolve(path.dirname(process.argv[1]), "../agents"));
182
+ try {
183
+ const realEntryPath = fs.realpathSync(process.argv[1]);
184
+ candidates.push(path.resolve(path.dirname(realEntryPath), "../agents"));
185
+ }
186
+ catch {
187
+ // Module-relative and cwd fallbacks remain.
188
+ }
189
+ }
190
+ candidates.push(path.resolve(process.cwd(), "mcp/sellable/agents"));
191
+ candidates.push(path.resolve(process.cwd(), "agents"));
192
+ for (const candidate of candidates) {
193
+ if (fs.existsSync(path.join(candidate, "registry.json"))) {
194
+ return candidate;
195
+ }
196
+ }
197
+ return candidates[0] ?? path.resolve(process.cwd(), "mcp/sellable/agents");
198
+ }
199
+ export function getSourceScoutRegistry() {
200
+ const registryPath = path.join(resolveAgentsDir(), "registry.json");
201
+ const registry = JSON.parse(fs.readFileSync(registryPath, "utf8"));
202
+ const agents = Array.isArray(registry.agents) ? registry.agents : [];
203
+ return {
204
+ version: Number(registry.version || 1),
205
+ agents: agents.map((agent) => ({
206
+ id: String(agent.id || ""),
207
+ name: String(agent.name || ""),
208
+ displayName: String(agent.displayName || agent.name || ""),
209
+ provider: String(agent.provider || ""),
210
+ lane: String(agent.lane || agent.id || ""),
211
+ codex: {
212
+ filename: String(agent.codex?.filename || `${agent.name}.toml`),
213
+ description: String(agent.codex?.description || ""),
214
+ },
215
+ claude: {
216
+ filename: String(agent.claude?.filename || `${agent.name}.md`),
217
+ description: String(agent.claude?.description || ""),
218
+ tools: Array.isArray(agent.claude?.tools) ? agent.claude.tools : [],
219
+ },
220
+ legacy: agent.legacy,
221
+ })),
222
+ usage: {
223
+ codex: "Spawn credible scouts by the returned `name` values in one assistant turn when the user approved background source scouts.",
224
+ claude: "Invoke Claude Code Task/Agent subagents with subagent_type equal to the returned `name` values.",
225
+ parentThreadRule: "Do not preload every provider prompt in the parent; each scout loads only the provider prompt for its lane.",
226
+ },
227
+ };
228
+ }
165
229
  export async function getMessagePrompt() {
166
230
  const api = getApi();
167
231
  return api.get("/api/v3/prompts/edit-message");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.68",
3
+ "version": "0.1.70",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -36,6 +36,7 @@
36
36
  },
37
37
  "files": [
38
38
  "dist",
39
+ "agents",
39
40
  "skills",
40
41
  ".claude-plugin",
41
42
  ".mcp.json",
@@ -11,6 +11,7 @@ allowed-tools:
11
11
  - mcp__sellable__get_subskill_asset
12
12
  - mcp__sellable__search_subskill_prompts
13
13
  - mcp__sellable__get_provider_prompt
14
+ - mcp__sellable__get_source_scout_registry
14
15
  - mcp__sellable__get_message_prompt
15
16
  - mcp__sellable__get_active_workspace
16
17
  - mcp__sellable__list_senders
@@ -102,11 +103,15 @@ scout those angles as independent branches when the host can actually do it:
102
103
  LinkedIn Engagement / active post engagers (internal `signal-discovery`
103
104
  provider prompt), Sales Nav / title + company filters, and Prospeo Contact /
104
105
  domains only when relevant. In Codex, explicitly spawn the named custom scouts
105
- `linkedin_engagement_scout`, `sales_nav_scout`, and `prospeo_contact_scout` for
106
+ `source-scout-linkedin-engagement`, `source-scout-sales-nav`, and `source-scout-prospeo-contact` for
106
107
  the credible lanes; Codex does not infer subagent fan-out from generic source
107
- comparison wording. In Claude Code, keep using the existing Task agents under
108
- `.claude/agents/`. If the host runs them sequentially, do not claim they ran in
109
- parallel. In chat, call the downstream copy stage `message generation`;
108
+ comparison wording. In Claude Code, invoke the generated `source-scout-*`
109
+ Task/Agent subagents for all credible lanes in one assistant message; the
110
+ installer writes them from the same canonical Sellable agent registry with
111
+ explicit Sellable MCP tool allowlists. The create-campaign-v2 subskill calls
112
+ `get_source_scout_registry` before dispatch so the current registry, not this
113
+ copy, is the runtime source of truth. If the host runs them sequentially, do not
114
+ claim they ran in parallel. In chat, call the downstream copy stage `message generation`;
110
115
  `message-validation.md` is only an internal proof artifact.
111
116
 
112
117
  Use rendered Markdown for user review surfaces, not fenced code blocks. Keep
@@ -360,6 +360,11 @@ me`, `I’ll paste a different sender profile`, and `Other / custom`.
360
360
  user-facing choice should be approve/revise language, not "looks good".
361
361
  Approval options should refer to what the user just read, e.g. `Approve this
362
362
  brief`, `Revise target`, `Revise offer/proof`, and `Other / custom`.
363
+ When the user chose `Find people for me` and the host supports subagents,
364
+ make the recommended approval label explicit: `Approve brief + use background
365
+ source scouts`. That gives Codex/Claude a user-visible request to launch
366
+ source-scout subagents after approval instead of silently falling back to
367
+ parent-thread provider probes.
363
368
  Include an `Open artifact:` link to `brief.md` before the approval question.
364
369
  The visible brief must come before local persistence chrome. After the brief
365
370
  is synthesized, render the approval-ready brief in chat before running visible
@@ -433,18 +438,23 @@ should test for this campaign. Those can run in parallel and usually take
433
438
  `message-validation.md`, then reconciles that the selected message basis rows
434
439
  still pass the final filter.
435
440
  - Parallel means real parallel execution, not optimistic progress copy. For the
436
- lead-source scout in Codex, explicitly spawn one named custom scout per
437
- credible source lane when subagents are available: `linkedin_engagement_scout`
441
+ lead-source scout, first call `get_source_scout_registry` and use the
442
+ returned canonical `name` values. In Codex, explicitly spawn one named custom scout per
443
+ credible source lane when subagents are available: `source-scout-linkedin-engagement`
438
444
  (display: LinkedIn Engagement Scout, powered by the `signal-discovery`
439
- provider prompt), `sales_nav_scout` (Sales Nav Scout), and
440
- `prospeo_contact_scout` (Prospeo Contact Scout). For Claude Code, keep using
441
- the existing `.claude/agents/lead-explorer-*.md` Task agents. If host
442
- subagents are unavailable, use independent MCP/tool calls in the same model
443
- turn or dedicated Sellable MCP tools that perform server-side `Promise.all`
444
- fan-out. If real parallel execution is not available or not allowed, run the
445
- same DAG sequentially and use honest copy: `I’ll tighten the filter first,
446
- then draft the message from the same sample.` Never say `kicking off two
447
- workstreams`, `in parallel`, or `background` unless parallel branches were
445
+ provider prompt), `source-scout-sales-nav` (Sales Nav Scout), and
446
+ `source-scout-prospeo-contact` (Prospeo Contact Scout). For Claude Code, explicitly
447
+ invoke the generated `.claude/agents/source-scout-*.md` Task/Agent subagents
448
+ for all credible lanes in one assistant message; they are installed from the
449
+ same canonical Sellable agent registry and carry explicit Sellable MCP tool
450
+ allowlists. The parent thread should not preload every provider prompt before
451
+ spawning scouts; each scout loads only its own provider prompt. If host
452
+ subagents are unavailable, use independent MCP/tool calls
453
+ in the same model turn or dedicated Sellable MCP tools that perform
454
+ server-side `Promise.all` fan-out. If real parallel execution is not available
455
+ or not allowed, run the same DAG sequentially and use honest copy: `I’ll tighten the filter first,
456
+ then draft the message from the same sample.` Never say `kicking off two
457
+ workstreams`, `in parallel`, or `background` unless parallel branches were
448
458
  actually launched.
449
459
  - Never run a downstream stage until the active `flow.v2.json` step's
450
460
  `requiredArtifacts` exist.
@@ -601,13 +611,18 @@ Required behavior:
601
611
  - do not mutate DB-backed campaign state
602
612
  - run a real parallel source-angle scout when the source is not supplied and at
603
613
  least two viable source angles exist. Treat source scouting as independent
604
- branches, then compare the outputs in `lead-review.md`. In Codex, explicitly
605
- spawn named custom subagents in the same turn: `linkedin_engagement_scout`,
606
- `sales_nav_scout`, and `prospeo_contact_scout` for the credible lanes. Codex
614
+ branches, then compare the outputs in `lead-review.md`. Call
615
+ `get_source_scout_registry` first so new scouts can be added without prompt
616
+ rewrites. In Codex, explicitly
617
+ spawn named custom subagents in the same turn: `source-scout-linkedin-engagement`,
618
+ `source-scout-sales-nav`, and `source-scout-prospeo-contact` for the credible lanes. Codex
607
619
  does not infer this from generic "compare paths" wording. If the realistic
608
620
  source set is LinkedIn Engagement + Sales Nav (Signals + Sales Nav), run both.
609
621
  If it is LinkedIn Engagement + Prospeo Contact (Signals + Prospeo), run both.
610
622
  If all three are credible, run all three when the host/runtime supports it.
623
+ In Claude Code, launch the matching `source-scout-linkedin-engagement`,
624
+ `source-scout-sales-nav`, and/or `source-scout-prospeo-contact` Task/Agent subagents
625
+ in the same assistant message, not as sequential scout turns.
611
626
  - Branch A: LinkedIn Engagement / active LinkedIn posts (internal provider:
612
627
  Signals / `signal-discovery`). Search relevant keyword lanes, review
613
628
  finalist posts, fetch top-post engagers, and estimate warm-fit volume.
@@ -224,14 +224,24 @@ setting: `~/.codex/config.toml` with
224
224
  Source-angle comparison should be real, not implied. When the source is not
225
225
  supplied and multiple source angles are viable, scout Signals, Sales Nav, and
226
226
  relevant domain/contact paths as independent branches when the host can do so.
227
- In Codex, explicitly spawn named custom scouts in the same turn:
228
- `linkedin_engagement_scout` (LinkedIn Engagement Scout, backed by the
229
- `signal-discovery` provider prompt), `sales_nav_scout` (Sales Nav Scout), and
230
- `prospeo_contact_scout` (Prospeo Contact Scout) for the credible lanes. Codex
227
+ Call `get_source_scout_registry` first and use the returned canonical `name`
228
+ values; the names below are the current registry entries. In Codex, explicitly
229
+ spawn named custom scouts in the same turn:
230
+ `source-scout-linkedin-engagement` (LinkedIn Engagement Scout, backed by the
231
+ `signal-discovery` provider prompt), `source-scout-sales-nav` (Sales Nav Scout), and
232
+ `source-scout-prospeo-contact` (Prospeo Contact Scout) for the credible lanes. Codex
231
233
  does not infer subagent fan-out from generic "compare paths" wording. In Claude
232
- Code, keep using the existing Task agents under `.claude/agents/`. If the host
233
- runs them sequentially, keep the output numeric but do not claim the source
234
- scout was parallel.
234
+ Code, invoke the generated `source-scout-linkedin-engagement`, `source-scout-sales-nav`,
235
+ and/or `source-scout-prospeo-contact` Task/Agent subagents in one assistant message so
236
+ Claude can run the source lanes concurrently/background. These agents come from
237
+ the same canonical Sellable registry and must load their matching provider
238
+ prompt before searching. The parent thread should not fetch every provider
239
+ prompt first; that burns time and often degenerates into parallel tool calls
240
+ instead of true subagents. When asking the user to approve a brief with
241
+ Sellable finding leads, use an explicit label like `Approve brief + use
242
+ background source scouts` so the next turn has a user-visible request to spawn
243
+ scouts. If the host runs them sequentially, keep the output numeric but do not
244
+ claim the source scout was parallel.
235
245
 
236
246
  Only promise parallel post-lead work when parallel work actually started. If the
237
247
  host cannot or should not launch background branches, say the real sequence:
@@ -341,7 +341,7 @@
341
341
  "action": "run_subskill",
342
342
  "target": "find-leads",
343
343
  "mode": "campaignless-preview",
344
- "sourceScoutRule": "When source is not user-supplied and at least two viable source angles exist, scout independent source angles with real parallelism when host/tooling permits. In Codex, explicitly spawn one named custom scout per credible lane in the same turn: linkedin_engagement_scout for LinkedIn Engagement / active post engagers (internal signal-discovery provider prompt), sales_nav_scout for Sales Nav title/company/activity filters, and prospeo_contact_scout for Prospeo account/domain/contact expansion. In Claude Code, use the existing .claude/agents/lead-explorer-*.md Task agents. Fallback to independent MCP/tool calls in the same model turn or dedicated Sellable MCP tools that perform server-side fan-out. Run LinkedIn Engagement + Sales Nav, LinkedIn Engagement + Prospeo Contact, or all three when credible. If real parallel execution is unavailable, run the same scouts sequentially and do not claim parallel execution. Compare outputs by raw volume, n/N sampled fit, estimated good-fit range, expected reply range, and tradeoff. Keep Signals/LinkedIn Engagement as viable when selected posts can produce ~150+ ICP-fit warm prospects before final filtering, even if Sales Nav is more scalable; when both are viable, present the choice and recommend the stronger default."
344
+ "sourceScoutRule": "When source is not user-supplied and at least two viable source angles exist, call get_source_scout_registry and use the returned canonical agent names, then scout independent source angles with real parallelism when host/tooling permits. In Codex, explicitly spawn one named custom scout per credible lane in the same turn: source-scout-linkedin-engagement for LinkedIn Engagement / active post engagers (internal signal-discovery provider prompt), source-scout-sales-nav for Sales Nav title/company/activity filters, and source-scout-prospeo-contact for Prospeo account/domain/contact expansion. In Claude Code, explicitly invoke the generated source-scout-linkedin-engagement, source-scout-sales-nav, and/or source-scout-prospeo-contact Task agents / Agent subagents in one assistant message so the lanes can run concurrently/background; those agents are installed from the canonical Sellable agent registry and carry explicit Sellable MCP tool allowlists. Fallback to independent MCP/tool calls in the same model turn or dedicated Sellable MCP tools that perform server-side fan-out. Run LinkedIn Engagement + Sales Nav, LinkedIn Engagement + Prospeo Contact, or all three when credible. If real parallel execution is unavailable, run the same scouts sequentially and do not claim parallel execution. Compare outputs by raw volume, n/N sampled fit, estimated good-fit range, expected reply range, and tradeoff. Keep Signals/LinkedIn Engagement as viable when selected posts can produce ~150+ ICP-fit warm prospects before final filtering, even if Sales Nav is more scalable; when both are viable, present the choice and recommend the stronger default."
345
345
  },
346
346
  {
347
347
  "action": "write_artifacts",
@@ -65,22 +65,34 @@ The kickoff doc is the resume surface. Re-open it before repeating discovery wor
65
65
 
66
66
  ## Execution Backend Routing
67
67
 
68
+ - Before source-scout dispatch, call `get_source_scout_registry` and use the
69
+ returned `name` values. The current canonical names are listed below for
70
+ readability, but the registry is the source of truth for install/runtime.
68
71
  - If Codex subagents are available, run the scout lanes with named custom
69
72
  agents. Spawn one agent per credible lane in the same turn, then wait for all
70
73
  lane results before synthesizing `lead-review.md`:
71
- - `linkedin_engagement_scout` (display: LinkedIn Engagement Scout) for
74
+ - `source-scout-linkedin-engagement` (display: LinkedIn Engagement Scout) for
72
75
  active LinkedIn posts and engagers; internally this uses the
73
76
  `signal-discovery` provider prompt plus `search_signals` /
74
77
  `fetch_post_engagers`.
75
- - `sales_nav_scout` (display: Sales Nav Scout) for Sales Navigator title,
78
+ - `source-scout-sales-nav` (display: Sales Nav Scout) for Sales Navigator title,
76
79
  company, geography, and activity filters.
77
- - `prospeo_contact_scout` (display: Prospeo Contact Scout) for Prospeo
80
+ - `source-scout-prospeo-contact` (display: Prospeo Contact Scout) for Prospeo
78
81
  account/domain and verified-contact expansion.
79
- - If Claude `Task` is available, deep exploration uses the existing file-backed
80
- Claude explorer agents:
81
- - `./.claude/agents/lead-explorer-signals.md`
82
- - `./.claude/agents/lead-explorer-sales-nav.md`
83
- - `./.claude/agents/lead-explorer-prospeo.md`
82
+ - Common parallel comparisons are LinkedIn Engagement + Sales Nav, LinkedIn
83
+ Engagement + Prospeo Contact, or all three when each lane is credible.
84
+ - If Claude `Task`/`Agent` is available, deep exploration uses the file-backed
85
+ Claude explorer agents installed from the same canonical Sellable agent
86
+ registry. Launch every credible lane in the same assistant message so Claude
87
+ Code can run them concurrently/background:
88
+ - `./.claude/agents/source-scout-linkedin-engagement.md`
89
+ - `./.claude/agents/source-scout-sales-nav.md`
90
+ - `./.claude/agents/source-scout-prospeo-contact.md`
91
+ - These Claude agents must have explicit Sellable MCP tool allowlists and must
92
+ load the matching provider prompt before searching.
93
+ - The parent thread should not preload every provider prompt before launching
94
+ scouts. Spawn the credible scouts first; each scout loads only the provider
95
+ prompt for its lane and returns structured evidence.
84
96
  - Else if `multi_tool_use.parallel` is available, run independent discovery calls in parallel and synthesize locally.
85
97
  - Else run sequentially.
86
98
  - Never claim agents were used when they were not.
@@ -250,10 +262,13 @@ If the first probe has good quality but insufficient scale, iterate 1-2 times to
250
262
  When two or more source angles are viable, run the provider probes in real
251
263
  parallel when the host/runtime allows it. For Codex, explicitly spawn the
252
264
  named source scout agents above; Codex will not infer subagent fan-out from
253
- general "compare sources" wording. Examples: LinkedIn Engagement + Sales Nav,
254
- LinkedIn Engagement + Prospeo Contact, or Sales Nav + Prospeo Contact. If the
255
- host cannot run them in parallel, run the same probes sequentially and do not
256
- claim they ran in parallel.
265
+ general "compare sources" wording. For Claude Code, explicitly invoke the
266
+ matching `source-scout-*` Task/Agent subagents in one message instead of
267
+ probing one lane, waiting, then probing the next. Examples: LinkedIn Engagement
268
+
269
+ - Sales Nav, LinkedIn Engagement + Prospeo Contact, or Sales Nav + Prospeo
270
+ Contact. If the host cannot run them in parallel, run the same probes
271
+ sequentially and do not claim they ran in parallel.
257
272
 
258
273
  Treat refinement as a measured loop:
259
274
 
@@ -414,11 +429,13 @@ Show the light-touch results and ask whether to deep-dispatch or iterate.
414
429
 
415
430
  ## Layer 3: Selective deep exploration
416
431
 
417
- Spawn only the validated paths from Gate 1.
432
+ Spawn only the validated paths from Gate 1. In Claude Code, invoke all selected
433
+ `source-scout-*` agents in one turn so source scouting is concurrent when the
434
+ runtime supports it.
418
435
 
419
- - `lead-explorer-signals`
420
- - `lead-explorer-sales-nav`
421
- - `lead-explorer-prospeo`
436
+ - `source-scout-linkedin-engagement`
437
+ - `source-scout-sales-nav`
438
+ - `source-scout-prospeo-contact`
422
439
 
423
440
  Each explorer must return:
424
441