@sellable/mcp 0.1.61 → 0.1.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server.js +4 -1
- package/dist/tools/prompts.d.ts +56 -0
- package/dist/tools/prompts.js +108 -25
- package/dist/tools/rubrics.d.ts +14 -3
- package/dist/tools/rubrics.js +11 -5
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +11 -5
- package/skills/create-campaign-v2/SKILL.md +38 -24
- package/skills/create-campaign-v2/core/flow.v2.json +44 -7
- package/skills/create-campaign-v2/references/sample-validation-loop.md +4 -9
- package/skills/create-campaign-v2-tail/SKILL.md +16 -4
- package/skills/generate-messages/SKILL.md +17 -9
- package/skills/research-prospect/SKILL.md +6 -3
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, getSubskillPrompt, listSubskillPrompts, promptToolDefinitions, searchSubskillPrompts, } from "./tools/prompts.js";
|
|
24
|
+
import { completeSenderResearch, getMessagePrompt, 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";
|
|
@@ -399,6 +399,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
399
399
|
case "get_subskill_prompt":
|
|
400
400
|
result = getSubskillPrompt(args?.subskillName, args?.offset, args?.limit);
|
|
401
401
|
break;
|
|
402
|
+
case "get_subskill_asset":
|
|
403
|
+
result = getSubskillAsset(args?.subskillName, args?.assetPath, args?.offset, args?.limit);
|
|
404
|
+
break;
|
|
402
405
|
case "list_subskill_prompts":
|
|
403
406
|
result = listSubskillPrompts(args?.limit, args?.includePublic, args?.includeInternal);
|
|
404
407
|
break;
|
package/dist/tools/prompts.d.ts
CHANGED
|
@@ -24,6 +24,19 @@ export interface SubskillPromptResponse {
|
|
|
24
24
|
chunkCount?: number;
|
|
25
25
|
chunkingInstructions?: string;
|
|
26
26
|
}
|
|
27
|
+
export interface SubskillAssetResponse {
|
|
28
|
+
subskillName: string;
|
|
29
|
+
assetPath: string;
|
|
30
|
+
content: string;
|
|
31
|
+
contentLength: number;
|
|
32
|
+
offset?: number;
|
|
33
|
+
limit?: number;
|
|
34
|
+
hasMore: boolean;
|
|
35
|
+
nextOffset: number | null;
|
|
36
|
+
chunkIndex: number;
|
|
37
|
+
chunkCount: number;
|
|
38
|
+
chunkingInstructions?: string;
|
|
39
|
+
}
|
|
27
40
|
export interface ListSubskillPromptsResponse {
|
|
28
41
|
total: number;
|
|
29
42
|
matches: Array<{
|
|
@@ -60,6 +73,7 @@ export declare const promptToolDefinitions: ({
|
|
|
60
73
|
includeInternal?: undefined;
|
|
61
74
|
subskillName?: undefined;
|
|
62
75
|
offset?: undefined;
|
|
76
|
+
assetPath?: undefined;
|
|
63
77
|
query?: undefined;
|
|
64
78
|
depth?: undefined;
|
|
65
79
|
proofItemsFound?: undefined;
|
|
@@ -91,6 +105,7 @@ export declare const promptToolDefinitions: ({
|
|
|
91
105
|
};
|
|
92
106
|
subskillName?: undefined;
|
|
93
107
|
offset?: undefined;
|
|
108
|
+
assetPath?: undefined;
|
|
94
109
|
query?: undefined;
|
|
95
110
|
depth?: undefined;
|
|
96
111
|
proofItemsFound?: undefined;
|
|
@@ -125,6 +140,44 @@ export declare const promptToolDefinitions: ({
|
|
|
125
140
|
};
|
|
126
141
|
includePublic?: undefined;
|
|
127
142
|
includeInternal?: undefined;
|
|
143
|
+
assetPath?: undefined;
|
|
144
|
+
query?: undefined;
|
|
145
|
+
depth?: undefined;
|
|
146
|
+
proofItemsFound?: undefined;
|
|
147
|
+
caseStudyItemsFound?: undefined;
|
|
148
|
+
credibilitySignalsFound?: undefined;
|
|
149
|
+
notes?: undefined;
|
|
150
|
+
};
|
|
151
|
+
required: string[];
|
|
152
|
+
additionalProperties: boolean;
|
|
153
|
+
};
|
|
154
|
+
} | {
|
|
155
|
+
name: string;
|
|
156
|
+
description: string;
|
|
157
|
+
_meta: {
|
|
158
|
+
"anthropic/maxResultSizeChars": number;
|
|
159
|
+
};
|
|
160
|
+
inputSchema: {
|
|
161
|
+
type: string;
|
|
162
|
+
properties: {
|
|
163
|
+
subskillName: {
|
|
164
|
+
type: string;
|
|
165
|
+
description: string;
|
|
166
|
+
};
|
|
167
|
+
assetPath: {
|
|
168
|
+
type: string;
|
|
169
|
+
description: string;
|
|
170
|
+
};
|
|
171
|
+
offset: {
|
|
172
|
+
type: string;
|
|
173
|
+
description: string;
|
|
174
|
+
};
|
|
175
|
+
limit: {
|
|
176
|
+
type: string;
|
|
177
|
+
description: string;
|
|
178
|
+
};
|
|
179
|
+
includePublic?: undefined;
|
|
180
|
+
includeInternal?: undefined;
|
|
128
181
|
query?: undefined;
|
|
129
182
|
depth?: undefined;
|
|
130
183
|
proofItemsFound?: undefined;
|
|
@@ -159,6 +212,7 @@ export declare const promptToolDefinitions: ({
|
|
|
159
212
|
};
|
|
160
213
|
subskillName?: undefined;
|
|
161
214
|
offset?: undefined;
|
|
215
|
+
assetPath?: undefined;
|
|
162
216
|
depth?: undefined;
|
|
163
217
|
proofItemsFound?: undefined;
|
|
164
218
|
caseStudyItemsFound?: undefined;
|
|
@@ -201,6 +255,7 @@ export declare const promptToolDefinitions: ({
|
|
|
201
255
|
includeInternal?: undefined;
|
|
202
256
|
subskillName?: undefined;
|
|
203
257
|
offset?: undefined;
|
|
258
|
+
assetPath?: undefined;
|
|
204
259
|
query?: undefined;
|
|
205
260
|
};
|
|
206
261
|
additionalProperties: boolean;
|
|
@@ -211,6 +266,7 @@ export declare const promptToolDefinitions: ({
|
|
|
211
266
|
export declare function getMessagePrompt(): Promise<PromptResponse>;
|
|
212
267
|
export declare function listSubskillPrompts(limit?: number, includePublic?: boolean, includeInternal?: boolean): ListSubskillPromptsResponse;
|
|
213
268
|
export declare function getSubskillPrompt(subskillName: string, offset?: number, limit?: number): SubskillPromptResponse;
|
|
269
|
+
export declare function getSubskillAsset(subskillName: string, assetPath: string, offset?: number, limit?: number): SubskillAssetResponse;
|
|
214
270
|
export declare function completeSenderResearch(input?: CompleteSenderResearchInput): {
|
|
215
271
|
readonly completedAt: string;
|
|
216
272
|
readonly depth: "minimal-verification" | "deep-proof" | "parallel-batch";
|
package/dist/tools/prompts.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
1
3
|
import { getApi } from "../api.js";
|
|
2
|
-
import { getSkillByName, listSkills, stripFrontmatter } from "../skills.js";
|
|
4
|
+
import { getSkillByName, listSkills, resolveSkillsDir, stripFrontmatter, } from "../skills.js";
|
|
3
5
|
import { markCreateCampaignPromptLoaded, markResearchPromptLoaded, markSenderResearchCompleted, } from "./flow-preflight.js";
|
|
4
6
|
// Chunk size sits below Claude Code's max-token cap on tool results
|
|
5
7
|
// (empirically around ~50k chars — fetch_linkedin_profile breached at
|
|
@@ -71,6 +73,36 @@ export const promptToolDefinitions = [
|
|
|
71
73
|
additionalProperties: false,
|
|
72
74
|
},
|
|
73
75
|
},
|
|
76
|
+
{
|
|
77
|
+
name: "get_subskill_asset",
|
|
78
|
+
description: "Load a packaged file that belongs to a Sellable subskill, such as core/flow.v2.json, core/auto-execute.yaml, or references/*.md. Use this instead of reading local repo files so Claude Code and Codex packaged runs share the same config.",
|
|
79
|
+
_meta: {
|
|
80
|
+
"anthropic/maxResultSizeChars": 200_000,
|
|
81
|
+
},
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties: {
|
|
85
|
+
subskillName: {
|
|
86
|
+
type: "string",
|
|
87
|
+
description: "Subskill name that owns the asset directory.",
|
|
88
|
+
},
|
|
89
|
+
assetPath: {
|
|
90
|
+
type: "string",
|
|
91
|
+
description: "Path relative to the subskill directory, for example core/flow.v2.json or references/sample-validation-loop.md.",
|
|
92
|
+
},
|
|
93
|
+
offset: {
|
|
94
|
+
type: "number",
|
|
95
|
+
description: "Optional character offset for chunked asset reads. Use for large reference files.",
|
|
96
|
+
},
|
|
97
|
+
limit: {
|
|
98
|
+
type: "number",
|
|
99
|
+
description: `Optional max characters to return for chunked asset reads. Defaults to ${DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS} for large assets and is capped at ${MAX_SUBSKILL_PROMPT_CHUNK_CHARS}.`,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
required: ["subskillName", "assetPath"],
|
|
103
|
+
additionalProperties: false,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
74
106
|
{
|
|
75
107
|
name: "search_subskill_prompts",
|
|
76
108
|
description: "Search Sellable subskills by name/description. Use this before get_subskill_prompt if you are unsure which prompt to load.",
|
|
@@ -169,53 +201,104 @@ function markSubskillPromptLoaded(subskillName) {
|
|
|
169
201
|
markResearchPromptLoaded("prospect", "research-prospect");
|
|
170
202
|
}
|
|
171
203
|
}
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
if (!skill) {
|
|
175
|
-
throw new Error(`Unknown subskill prompt: ${subskillName}`);
|
|
176
|
-
}
|
|
177
|
-
markSubskillPromptLoaded(subskillName);
|
|
178
|
-
const fullPrompt = stripFrontmatter(skill.content);
|
|
179
|
-
const safeOffset = Math.min(Math.max(Number.isFinite(offset) ? Math.floor(offset ?? 0) : 0, 0), fullPrompt.length);
|
|
204
|
+
function readChunkedContent(content, offset, limit, continuation) {
|
|
205
|
+
const safeOffset = Math.min(Math.max(Number.isFinite(offset) ? Math.floor(offset ?? 0) : 0, 0), content.length);
|
|
180
206
|
const requestedLimit = Number.isFinite(limit) && limit !== undefined
|
|
181
207
|
? Math.max(Math.floor(limit), 1)
|
|
182
208
|
: null;
|
|
183
|
-
const defaultChunkLimit =
|
|
209
|
+
const defaultChunkLimit = content.length > DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS
|
|
184
210
|
? DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS
|
|
185
211
|
: null;
|
|
186
212
|
const safeLimit = requestedLimit === null
|
|
187
213
|
? defaultChunkLimit
|
|
188
214
|
: Math.min(requestedLimit, MAX_SUBSKILL_PROMPT_CHUNK_CHARS);
|
|
189
|
-
const
|
|
190
|
-
?
|
|
191
|
-
:
|
|
215
|
+
const chunk = safeLimit === null
|
|
216
|
+
? content
|
|
217
|
+
: content.slice(safeOffset, safeOffset + safeLimit);
|
|
192
218
|
const nextOffset = safeLimit === null
|
|
193
219
|
? null
|
|
194
|
-
: Math.min(safeOffset + safeLimit,
|
|
195
|
-
const hasMore = nextOffset !== null && nextOffset <
|
|
196
|
-
const chunkCount = safeLimit === null
|
|
197
|
-
? 1
|
|
198
|
-
: Math.max(Math.ceil(fullPrompt.length / safeLimit), 1);
|
|
220
|
+
: Math.min(safeOffset + safeLimit, content.length);
|
|
221
|
+
const hasMore = nextOffset !== null && nextOffset < content.length;
|
|
222
|
+
const chunkCount = safeLimit === null ? 1 : Math.max(Math.ceil(content.length / safeLimit), 1);
|
|
199
223
|
const chunkIndex = safeLimit === null
|
|
200
224
|
? 1
|
|
201
225
|
: Math.min(Math.floor(safeOffset / safeLimit) + 1, chunkCount);
|
|
202
226
|
return {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
visibility: skill.visibility,
|
|
206
|
-
prompt,
|
|
207
|
-
promptLength: fullPrompt.length,
|
|
227
|
+
chunk,
|
|
228
|
+
contentLength: content.length,
|
|
208
229
|
offset: safeOffset,
|
|
209
230
|
limit: safeLimit ?? undefined,
|
|
210
231
|
hasMore,
|
|
211
232
|
nextOffset: hasMore ? nextOffset : null,
|
|
212
233
|
chunkIndex,
|
|
213
234
|
chunkCount,
|
|
214
|
-
chunkingInstructions: hasMore
|
|
215
|
-
?
|
|
235
|
+
chunkingInstructions: hasMore && nextOffset !== null && safeLimit !== null && continuation
|
|
236
|
+
? continuation(nextOffset, safeLimit)
|
|
216
237
|
: undefined,
|
|
217
238
|
};
|
|
218
239
|
}
|
|
240
|
+
export function getSubskillPrompt(subskillName, offset, limit) {
|
|
241
|
+
const skill = getSkillByName(subskillName);
|
|
242
|
+
if (!skill) {
|
|
243
|
+
throw new Error(`Unknown subskill prompt: ${subskillName}`);
|
|
244
|
+
}
|
|
245
|
+
markSubskillPromptLoaded(subskillName);
|
|
246
|
+
const fullPrompt = stripFrontmatter(skill.content);
|
|
247
|
+
const chunked = readChunkedContent(fullPrompt, offset, limit, (nextOffset, safeLimit) => `Continue this same prompt load with get_subskill_prompt({ subskillName: "${skill.name}", offset: ${nextOffset}, limit: ${safeLimit} }) until hasMore is false. Treat all chunks as one prompt load; do not use saved local tool-output files or machine-specific paths.`);
|
|
248
|
+
return {
|
|
249
|
+
name: skill.name,
|
|
250
|
+
description: skill.description,
|
|
251
|
+
visibility: skill.visibility,
|
|
252
|
+
prompt: chunked.chunk,
|
|
253
|
+
promptLength: chunked.contentLength,
|
|
254
|
+
offset: chunked.offset,
|
|
255
|
+
limit: chunked.limit,
|
|
256
|
+
hasMore: chunked.hasMore,
|
|
257
|
+
nextOffset: chunked.nextOffset,
|
|
258
|
+
chunkIndex: chunked.chunkIndex,
|
|
259
|
+
chunkCount: chunked.chunkCount,
|
|
260
|
+
chunkingInstructions: chunked.chunkingInstructions,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function resolveSubskillAssetPath(subskillName, assetPath) {
|
|
264
|
+
const cleanAssetPath = assetPath.replace(/\\/g, "/").trim();
|
|
265
|
+
if (cleanAssetPath === "" ||
|
|
266
|
+
cleanAssetPath.startsWith("/") ||
|
|
267
|
+
cleanAssetPath.split("/").includes("..")) {
|
|
268
|
+
throw new Error(`Invalid subskill asset path: ${assetPath}`);
|
|
269
|
+
}
|
|
270
|
+
const skillRoot = path.resolve(resolveSkillsDir(), subskillName);
|
|
271
|
+
const resolved = path.resolve(skillRoot, cleanAssetPath);
|
|
272
|
+
const relative = path.relative(skillRoot, resolved);
|
|
273
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
274
|
+
throw new Error(`Invalid subskill asset path: ${assetPath}`);
|
|
275
|
+
}
|
|
276
|
+
return { cleanAssetPath, resolved };
|
|
277
|
+
}
|
|
278
|
+
export function getSubskillAsset(subskillName, assetPath, offset, limit) {
|
|
279
|
+
if (!getSkillByName(subskillName)) {
|
|
280
|
+
throw new Error(`Unknown subskill prompt: ${subskillName}`);
|
|
281
|
+
}
|
|
282
|
+
const { cleanAssetPath, resolved } = resolveSubskillAssetPath(subskillName, assetPath);
|
|
283
|
+
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isFile()) {
|
|
284
|
+
throw new Error(`Unknown subskill asset: ${subskillName}/${cleanAssetPath}`);
|
|
285
|
+
}
|
|
286
|
+
const content = fs.readFileSync(resolved, "utf8");
|
|
287
|
+
const chunked = readChunkedContent(content, offset, limit, (nextOffset, safeLimit) => `Continue this same asset load with get_subskill_asset({ subskillName: "${subskillName}", assetPath: "${cleanAssetPath}", offset: ${nextOffset}, limit: ${safeLimit} }) until hasMore is false. Treat all chunks as one asset load; do not use saved local tool-output files or machine-specific paths.`);
|
|
288
|
+
return {
|
|
289
|
+
subskillName,
|
|
290
|
+
assetPath: cleanAssetPath,
|
|
291
|
+
content: chunked.chunk,
|
|
292
|
+
contentLength: chunked.contentLength,
|
|
293
|
+
offset: chunked.offset,
|
|
294
|
+
limit: chunked.limit,
|
|
295
|
+
hasMore: chunked.hasMore,
|
|
296
|
+
nextOffset: chunked.nextOffset,
|
|
297
|
+
chunkIndex: chunked.chunkIndex,
|
|
298
|
+
chunkCount: chunked.chunkCount,
|
|
299
|
+
chunkingInstructions: chunked.chunkingInstructions,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
219
302
|
export function completeSenderResearch(input) {
|
|
220
303
|
return markSenderResearchCompleted(input);
|
|
221
304
|
}
|
package/dist/tools/rubrics.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ type WaitForRubricResultsInput = {
|
|
|
34
34
|
targetCount?: number;
|
|
35
35
|
timeoutMs?: number;
|
|
36
36
|
intervalMs?: number;
|
|
37
|
+
includeRows?: boolean;
|
|
37
38
|
};
|
|
38
39
|
type WorkflowTableStats = {
|
|
39
40
|
tableId: string;
|
|
@@ -101,6 +102,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
101
102
|
targetCount?: undefined;
|
|
102
103
|
timeoutMs?: undefined;
|
|
103
104
|
intervalMs?: undefined;
|
|
105
|
+
includeRows?: undefined;
|
|
104
106
|
};
|
|
105
107
|
required: string[];
|
|
106
108
|
additionalProperties: boolean;
|
|
@@ -132,6 +134,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
132
134
|
targetCount?: undefined;
|
|
133
135
|
timeoutMs?: undefined;
|
|
134
136
|
intervalMs?: undefined;
|
|
137
|
+
includeRows?: undefined;
|
|
135
138
|
};
|
|
136
139
|
required: string[];
|
|
137
140
|
additionalProperties: boolean;
|
|
@@ -188,6 +191,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
188
191
|
targetCount?: undefined;
|
|
189
192
|
timeoutMs?: undefined;
|
|
190
193
|
intervalMs?: undefined;
|
|
194
|
+
includeRows?: undefined;
|
|
191
195
|
};
|
|
192
196
|
required: string[];
|
|
193
197
|
additionalProperties: boolean;
|
|
@@ -244,6 +248,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
244
248
|
targetCount?: undefined;
|
|
245
249
|
timeoutMs?: undefined;
|
|
246
250
|
intervalMs?: undefined;
|
|
251
|
+
includeRows?: undefined;
|
|
247
252
|
};
|
|
248
253
|
required: string[];
|
|
249
254
|
additionalProperties: boolean;
|
|
@@ -302,6 +307,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
302
307
|
targetCount?: undefined;
|
|
303
308
|
timeoutMs?: undefined;
|
|
304
309
|
intervalMs?: undefined;
|
|
310
|
+
includeRows?: undefined;
|
|
305
311
|
};
|
|
306
312
|
required: string[];
|
|
307
313
|
additionalProperties: boolean;
|
|
@@ -330,6 +336,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
330
336
|
targetCount?: undefined;
|
|
331
337
|
timeoutMs?: undefined;
|
|
332
338
|
intervalMs?: undefined;
|
|
339
|
+
includeRows?: undefined;
|
|
333
340
|
};
|
|
334
341
|
required: string[];
|
|
335
342
|
additionalProperties: boolean;
|
|
@@ -358,6 +365,7 @@ export declare const rubricToolDefinitions: ({
|
|
|
358
365
|
targetCount?: undefined;
|
|
359
366
|
timeoutMs?: undefined;
|
|
360
367
|
intervalMs?: undefined;
|
|
368
|
+
includeRows?: undefined;
|
|
361
369
|
};
|
|
362
370
|
required: string[];
|
|
363
371
|
additionalProperties: boolean;
|
|
@@ -388,6 +396,10 @@ export declare const rubricToolDefinitions: ({
|
|
|
388
396
|
type: string;
|
|
389
397
|
description: string;
|
|
390
398
|
};
|
|
399
|
+
includeRows: {
|
|
400
|
+
type: string;
|
|
401
|
+
description: string;
|
|
402
|
+
};
|
|
391
403
|
leadScoringRubrics?: undefined;
|
|
392
404
|
requiredCheckNames?: undefined;
|
|
393
405
|
rubric?: undefined;
|
|
@@ -466,6 +478,8 @@ export declare function checkRubric(input: CheckRubricInput): Promise<{
|
|
|
466
478
|
tableId: string;
|
|
467
479
|
}>;
|
|
468
480
|
export declare function waitForRubricResults(input: WaitForRubricResultsInput): Promise<{
|
|
481
|
+
stats: WorkflowTableStats;
|
|
482
|
+
rows?: import("./rows.js").LightweightRow[] | undefined;
|
|
469
483
|
ready: boolean;
|
|
470
484
|
attempts: number;
|
|
471
485
|
elapsedMs: number;
|
|
@@ -476,8 +490,6 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
476
490
|
percent: number;
|
|
477
491
|
targetCount: number;
|
|
478
492
|
};
|
|
479
|
-
rows: import("./rows.js").LightweightRow[];
|
|
480
|
-
stats: WorkflowTableStats;
|
|
481
493
|
reason?: undefined;
|
|
482
494
|
} | {
|
|
483
495
|
ready: boolean;
|
|
@@ -492,6 +504,5 @@ export declare function waitForRubricResults(input: WaitForRubricResultsInput):
|
|
|
492
504
|
targetCount: number;
|
|
493
505
|
};
|
|
494
506
|
stats: WorkflowTableStats | null;
|
|
495
|
-
rows?: undefined;
|
|
496
507
|
}>;
|
|
497
508
|
export {};
|
package/dist/tools/rubrics.js
CHANGED
|
@@ -356,6 +356,10 @@ export const rubricToolDefinitions = [
|
|
|
356
356
|
type: "number",
|
|
357
357
|
description: `Polling interval in ms (default ${DEFAULT_INTERVAL_MS}).`,
|
|
358
358
|
},
|
|
359
|
+
includeRows: {
|
|
360
|
+
type: "boolean",
|
|
361
|
+
description: "Whether to include minimal row snapshots in the response. Default true for backwards compatibility; pass false for stats-only polling in long autonomous flows.",
|
|
362
|
+
},
|
|
359
363
|
},
|
|
360
364
|
required: [],
|
|
361
365
|
additionalProperties: false,
|
|
@@ -619,6 +623,7 @@ export async function waitForRubricResults(input) {
|
|
|
619
623
|
const timeoutMs = Math.max(5000, input.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
620
624
|
const intervalMs = Math.max(500, input.intervalMs ?? DEFAULT_INTERVAL_MS);
|
|
621
625
|
const targetCount = resolveMaxProspects(input.targetCount);
|
|
626
|
+
const includeRows = input.includeRows !== false;
|
|
622
627
|
let tableId = input.tableId;
|
|
623
628
|
if (!tableId && input.campaignOfferId) {
|
|
624
629
|
const campaign = await fetchCampaignOffer(input.campaignOfferId);
|
|
@@ -640,10 +645,11 @@ export async function waitForRubricResults(input) {
|
|
|
640
645
|
if (completed >= effectiveTarget) {
|
|
641
646
|
const passed = stats.passRate?.passed ?? 0;
|
|
642
647
|
const percent = completed > 0 ? Math.round((passed / completed) * 100) : 0;
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
648
|
+
const rowSnapshot = includeRows
|
|
649
|
+
? await getTableRowsMinimal(tableId, {
|
|
650
|
+
limit: effectiveTarget,
|
|
651
|
+
})
|
|
652
|
+
: null;
|
|
647
653
|
return {
|
|
648
654
|
ready: true,
|
|
649
655
|
attempts,
|
|
@@ -655,7 +661,7 @@ export async function waitForRubricResults(input) {
|
|
|
655
661
|
percent,
|
|
656
662
|
targetCount: effectiveTarget,
|
|
657
663
|
},
|
|
658
|
-
rows: rowSnapshot.rows,
|
|
664
|
+
...(rowSnapshot ? { rows: rowSnapshot.rows } : {}),
|
|
659
665
|
stats,
|
|
660
666
|
};
|
|
661
667
|
}
|
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@ allowed-tools:
|
|
|
8
8
|
- mcp__sellable__wait_for_cli_login
|
|
9
9
|
- mcp__sellable__bootstrap_create_campaign
|
|
10
10
|
- mcp__sellable__get_subskill_prompt
|
|
11
|
+
- mcp__sellable__get_subskill_asset
|
|
11
12
|
- mcp__sellable__search_subskill_prompts
|
|
12
13
|
- mcp__sellable__get_provider_prompt
|
|
13
14
|
- mcp__sellable__get_message_prompt
|
|
@@ -449,19 +450,24 @@ updates.
|
|
|
449
450
|
|
|
450
451
|
1. Load canonical prompt via
|
|
451
452
|
`mcp__sellable__get_subskill_prompt({ subskillName: "create-campaign-v2" })`.
|
|
452
|
-
2.
|
|
453
|
-
|
|
453
|
+
2. Load the canonical workflow config via
|
|
454
|
+
`mcp__sellable__get_subskill_asset({ subskillName: "create-campaign-v2", assetPath: "core/flow.v2.json" })`.
|
|
455
|
+
Treat the returned JSON as the active state machine. Do not read repo-local
|
|
456
|
+
copies of this file; packaged Claude Code and Codex runs must use the MCP
|
|
457
|
+
asset loader so they share the same config.
|
|
458
|
+
3. Follow that prompt and workflow config exactly.
|
|
459
|
+
4. For message generation, load the full `generate-messages` prompt in the
|
|
454
460
|
same run with chunked
|
|
455
461
|
`mcp__sellable__get_subskill_prompt({ subskillName: "generate-messages", offset, limit })`
|
|
456
462
|
calls until `hasMore` is false. Do not synthesize
|
|
457
463
|
`message-validation.md` from the brief, lead review, or general knowledge.
|
|
458
|
-
|
|
464
|
+
5. Treat message quality as the gate before minting. Do not create a campaign,
|
|
459
465
|
show a commit gate, or mint anything until `message-validation.md` proves
|
|
460
466
|
the full generate-messages workflow ran and `message-review.md` recommends
|
|
461
467
|
`approve-message` against the gold-standard rules.
|
|
462
|
-
|
|
468
|
+
6. Do not create or mutate the live campaign until the approval gate returns
|
|
463
469
|
`approve`.
|
|
464
|
-
|
|
470
|
+
7. Do not ask the user to run another command.
|
|
465
471
|
|
|
466
472
|
## Fallback
|
|
467
473
|
|
|
@@ -74,6 +74,11 @@ Validated draft directory:
|
|
|
74
74
|
- Net-new runs start at `bootstrap` -> `brief-interview` in
|
|
75
75
|
`core/flow.v2.json`. Do not start at `validate-artifacts` unless resuming a
|
|
76
76
|
compatibility run where all upstream artifacts already exist.
|
|
77
|
+
- Load `core/flow.v2.json` through
|
|
78
|
+
`get_subskill_asset({ subskillName: "create-campaign-v2", assetPath: "core/flow.v2.json" })`
|
|
79
|
+
at the start of the run and treat the returned JSON as the active state
|
|
80
|
+
machine. Do not read repo-local config files; packaged Claude Code and Codex
|
|
81
|
+
runs must use the MCP asset loader so they execute the same workflow.
|
|
77
82
|
- Use the host-native structured question gate whenever it is exposed. In
|
|
78
83
|
Claude Code, this is `AskUserQuestion`. In Codex, this is
|
|
79
84
|
`request_user_input` (enabled in Default mode by
|
|
@@ -410,22 +415,23 @@ should test for this campaign. Those can run in parallel and usually take
|
|
|
410
415
|
`approval-packet.md`.
|
|
411
416
|
- Run the dependency chain as a DAG: `create-campaign-brief` -> `find leads`;
|
|
412
417
|
once `lead-review.md` and `lead-sample.json` exist, run `filter leads` and
|
|
413
|
-
`message generation`
|
|
414
|
-
`lead-
|
|
415
|
-
`
|
|
416
|
-
|
|
417
|
-
- Parallel means real parallel execution, not optimistic progress copy.
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
418
|
+
`message generation` from the same basis (`brief.md`, `lead-review.md`,
|
|
419
|
+
`lead-sample.json`). Approval waits for both `lead-filter.md` and
|
|
420
|
+
`message-validation.md`, then reconciles that the selected message basis rows
|
|
421
|
+
still pass the final filter.
|
|
422
|
+
- Parallel means real parallel execution, not optimistic progress copy. Prefer
|
|
423
|
+
product-native parallelism that works in both Claude Code and Codex:
|
|
424
|
+
independent MCP/tool calls in the same model turn, or dedicated Sellable MCP
|
|
425
|
+
tools that perform their own server-side `Promise.all` fan-out. Host subagents
|
|
426
|
+
are optional acceleration only: use Claude Task/subagents or Codex subagents
|
|
427
|
+
only when the host exposes them, policy allows them, and the user explicitly
|
|
428
|
+
asked for agent fan-out. A normal `$sellable:create-campaign` invocation is
|
|
429
|
+
not an explicit request for Codex subagents. If only parallel tool batching is
|
|
430
|
+
available, batch independent tool reads/lookups only. If real parallel
|
|
431
|
+
execution is not available or not allowed, run the same DAG sequentially and
|
|
432
|
+
use honest copy: `I’ll tighten the filter first, then draft the message from
|
|
433
|
+
the same sample.` Never say `kicking off two workstreams`, `in parallel`, or
|
|
434
|
+
`background` unless parallel branches were actually launched.
|
|
429
435
|
- Never run a downstream stage until the active `flow.v2.json` step's
|
|
430
436
|
`requiredArtifacts` exist.
|
|
431
437
|
- Never call a tool outside the active step's `allowedTools`, and never call a
|
|
@@ -523,18 +529,26 @@ batch, after buyer, offer/ask, and proof/safety are understood. Frame supplied
|
|
|
523
529
|
lists as optional, not required. The three visible options are exactly:
|
|
524
530
|
|
|
525
531
|
1. `Find people for me (recommended if you don't already have your own list)`
|
|
526
|
-
2. `I have a CSV of
|
|
527
|
-
3. `I have a CSV of
|
|
532
|
+
2. `I have a CSV of profiles to reach out to`
|
|
533
|
+
3. `I have a CSV of companies/domains I want to target`
|
|
534
|
+
|
|
535
|
+
Do not expose provider or implementation paths as the user's lead-source
|
|
536
|
+
choices. Never label the structured options as `engagers`, `titles`,
|
|
537
|
+
`Signals`, `Sales Nav`, `Prospeo`, `search LinkedIn posts`, or similar. Those
|
|
538
|
+
are internal scouting methods for the `Find people for me` path, not choices a
|
|
539
|
+
first-time user should have to understand.
|
|
528
540
|
|
|
529
541
|
Keep `Other / custom` available for freeform answers such as a pasted list,
|
|
530
542
|
an existing Sellable lead list, or another source idea. Do not put existing
|
|
531
543
|
Sellable lead lists in the main three-option first batch; support them through
|
|
532
|
-
custom/freeform input. If the user
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
544
|
+
custom/freeform input. If the user chooses the company/domain option, ask in
|
|
545
|
+
normal chat whether they have a CSV, pasted domains, or pasted company names.
|
|
546
|
+
If the user pastes up to 100 LinkedIn profile URLs or company domains,
|
|
547
|
+
normalize the paste into a temporary local CSV and continue through the
|
|
548
|
+
matching CSV preview path. Mixed, ambiguous, malformed, or oversized pastes
|
|
549
|
+
should ask for a real CSV file instead of guessing. Uploaded CSV support is
|
|
550
|
+
larger than paste support: LinkedIn profile CSVs can contain up to 7,500 rows;
|
|
551
|
+
domain CSVs can contain up to 7,500 rows but only 1,000 unique domains.
|
|
538
552
|
|
|
539
553
|
Avoid internal wording like `Which proof points should the message be allowed
|
|
540
554
|
to lean on?` because it describes the artifact, not the founder decision.
|
|
@@ -64,6 +64,13 @@
|
|
|
64
64
|
"requiredValues": {
|
|
65
65
|
"subskillName": "create-campaign-v2"
|
|
66
66
|
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"tool": "get_subskill_asset",
|
|
70
|
+
"requiredValues": {
|
|
71
|
+
"subskillName": "create-campaign-v2",
|
|
72
|
+
"assetPath": "core/flow.v2.json"
|
|
73
|
+
}
|
|
67
74
|
}
|
|
68
75
|
],
|
|
69
76
|
"requiredArtifacts": [],
|
|
@@ -73,6 +80,7 @@
|
|
|
73
80
|
"get_auth_status",
|
|
74
81
|
"get_active_workspace",
|
|
75
82
|
"get_subskill_prompt",
|
|
83
|
+
"get_subskill_asset",
|
|
76
84
|
"AskUserQuestion",
|
|
77
85
|
"request_user_input"
|
|
78
86
|
],
|
|
@@ -158,9 +166,18 @@
|
|
|
158
166
|
"question": "How should we get the people for this campaign?",
|
|
159
167
|
"options": [
|
|
160
168
|
"Find people for me (recommended if you don't already have your own list)",
|
|
161
|
-
"I have a CSV of
|
|
162
|
-
"I have a CSV of
|
|
169
|
+
"I have a CSV of profiles to reach out to",
|
|
170
|
+
"I have a CSV of companies/domains I want to target"
|
|
163
171
|
],
|
|
172
|
+
"forbiddenOptionLabels": [
|
|
173
|
+
"engagers",
|
|
174
|
+
"titles",
|
|
175
|
+
"Signals",
|
|
176
|
+
"Sales Nav",
|
|
177
|
+
"Prospeo",
|
|
178
|
+
"search LinkedIn posts"
|
|
179
|
+
],
|
|
180
|
+
"forbiddenOptionLabelReason": "Provider/source mechanics are internal to the 'Find people for me' path; first-time users should choose the job they need done, not the scouting provider.",
|
|
164
181
|
"customInput": true,
|
|
165
182
|
"lastQuestionInFirstStrategyBatch": true
|
|
166
183
|
}
|
|
@@ -169,6 +186,7 @@
|
|
|
169
186
|
"producesArtifacts": ["brief-v1.md", "brief.md"],
|
|
170
187
|
"allowedTools": [
|
|
171
188
|
"get_subskill_prompt",
|
|
189
|
+
"get_subskill_asset",
|
|
172
190
|
"get_auth_status",
|
|
173
191
|
"get_active_workspace",
|
|
174
192
|
"list_senders",
|
|
@@ -301,7 +319,7 @@
|
|
|
301
319
|
"action": "run_subskill",
|
|
302
320
|
"target": "find-leads",
|
|
303
321
|
"mode": "campaignless-preview",
|
|
304
|
-
"sourceScoutRule": "When source is not user-supplied and at least two viable source angles exist, scout independent source angles
|
|
322
|
+
"sourceScoutRule": "When source is not user-supplied and at least two viable source angles exist, scout independent source angles with product-native parallelism when host/tooling permits: independent MCP/tool calls in the same model turn or dedicated Sellable MCP tools that perform server-side fan-out. Signals active-post branch + Sales Nav title/company branch, Signals + Prospeo when domains/accounts are relevant, or all three when credible. Do not rely on Codex subagents for a normal $sellable:create-campaign invocation; use host subagents only when the user explicitly asked for agent fan-out. 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 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."
|
|
305
323
|
},
|
|
306
324
|
{
|
|
307
325
|
"action": "write_artifacts",
|
|
@@ -312,6 +330,7 @@
|
|
|
312
330
|
"producesArtifacts": ["lead-review.md", "lead-sample.json"],
|
|
313
331
|
"allowedTools": [
|
|
314
332
|
"get_subskill_prompt",
|
|
333
|
+
"get_subskill_asset",
|
|
315
334
|
"get_provider_prompt",
|
|
316
335
|
"lookup_sales_nav_filter",
|
|
317
336
|
"search_sales_nav",
|
|
@@ -415,7 +434,7 @@
|
|
|
415
434
|
"parallel only if real parallel branches were launched"
|
|
416
435
|
],
|
|
417
436
|
"timeEstimate": "~2-3 min",
|
|
418
|
-
"chatRenderRule": "If real parallel
|
|
437
|
+
"chatRenderRule": "If real parallel MCP/tool branches or explicitly requested host subagents were actually launched, say: 'I’m kicking off two workstreams now' and list 'Tighten the fit filter' and 'Message generation'. If not, do not mention parallel/background work; say: 'I’ll tighten the filter first, then run message generation from the same approved brief and sample leads.' Never claim parallelism unless parallel execution actually started. A normal $sellable:create-campaign invocation is not an explicit request for Codex subagents. User-facing stage name is message generation; message-validation.md is only the internal artifact."
|
|
419
438
|
},
|
|
420
439
|
{
|
|
421
440
|
"action": "ask_continue_revise_or_confirm_only_if_needed",
|
|
@@ -503,6 +522,7 @@
|
|
|
503
522
|
"optionalProducesArtifacts": ["rubric.json"],
|
|
504
523
|
"allowedTools": [
|
|
505
524
|
"get_subskill_prompt",
|
|
525
|
+
"get_subskill_asset",
|
|
506
526
|
"AskUserQuestion",
|
|
507
527
|
"request_user_input"
|
|
508
528
|
],
|
|
@@ -554,6 +574,7 @@
|
|
|
554
574
|
"producesArtifacts": ["message-validation.md"],
|
|
555
575
|
"allowedTools": [
|
|
556
576
|
"get_subskill_prompt",
|
|
577
|
+
"get_subskill_asset",
|
|
557
578
|
"AskUserQuestion",
|
|
558
579
|
"request_user_input"
|
|
559
580
|
],
|
|
@@ -1015,6 +1036,11 @@
|
|
|
1015
1036
|
{
|
|
1016
1037
|
"action": "load_auto_execute_config",
|
|
1017
1038
|
"source": "core/auto-execute.yaml",
|
|
1039
|
+
"tool": "get_subskill_asset",
|
|
1040
|
+
"requiredValues": {
|
|
1041
|
+
"subskillName": "create-campaign-v2",
|
|
1042
|
+
"assetPath": "core/auto-execute.yaml"
|
|
1043
|
+
},
|
|
1018
1044
|
"loadOnce": true
|
|
1019
1045
|
},
|
|
1020
1046
|
{
|
|
@@ -1022,7 +1048,11 @@
|
|
|
1022
1048
|
"target": "85-02"
|
|
1023
1049
|
}
|
|
1024
1050
|
],
|
|
1025
|
-
"allowedTools": [
|
|
1051
|
+
"allowedTools": [
|
|
1052
|
+
"get_subskill_asset",
|
|
1053
|
+
"AskUserQuestion",
|
|
1054
|
+
"request_user_input"
|
|
1055
|
+
],
|
|
1026
1056
|
"transitions": {
|
|
1027
1057
|
"autonomous_tail_started": "auto-execute-leads"
|
|
1028
1058
|
}
|
|
@@ -1105,6 +1135,7 @@
|
|
|
1105
1135
|
}
|
|
1106
1136
|
],
|
|
1107
1137
|
"allowedTools": [
|
|
1138
|
+
"get_subskill_asset",
|
|
1108
1139
|
"import_leads",
|
|
1109
1140
|
"wait_for_lead_list_ready",
|
|
1110
1141
|
"confirm_lead_list",
|
|
@@ -1153,11 +1184,14 @@
|
|
|
1153
1184
|
"tool": "wait_for_rubric_results",
|
|
1154
1185
|
"requiredFields": ["targetCount"],
|
|
1155
1186
|
"targetCountSource": "stats.totalRows_or_imported_batch_count",
|
|
1187
|
+
"requiredValues": {
|
|
1188
|
+
"includeRows": false
|
|
1189
|
+
},
|
|
1156
1190
|
"note": "default targetCount=25 returns ready=true after first 25 completions even when cohort is still pending; always pass cohortSize explicitly (see references/sample-validation-loop.md §Known Tool Behaviors #3)",
|
|
1157
|
-
"readVia": "
|
|
1191
|
+
"readVia": "stats_only_tool_result",
|
|
1158
1192
|
"extractFields": ["ready", "passRate.completed", "stats"],
|
|
1159
1193
|
"doNotRetain": "rows_payload",
|
|
1160
|
-
"contextBloatNote": "25 rows ≈ 67KB
|
|
1194
|
+
"contextBloatNote": "Pass includeRows=false so the MCP tool returns stats only; 25 rows ≈ 67KB and 100 rows ≈ 268KB when row payloads are retained (see references/sample-validation-loop.md §Known Tool Behaviors #2)."
|
|
1161
1195
|
},
|
|
1162
1196
|
{
|
|
1163
1197
|
"action": "compute_projected_pass",
|
|
@@ -1176,6 +1210,7 @@
|
|
|
1176
1210
|
}
|
|
1177
1211
|
],
|
|
1178
1212
|
"allowedTools": [
|
|
1213
|
+
"get_subskill_asset",
|
|
1179
1214
|
"get_rows_minimal",
|
|
1180
1215
|
"wait_for_campaign_table_ready",
|
|
1181
1216
|
"wait_for_rubric_results",
|
|
@@ -1287,6 +1322,7 @@
|
|
|
1287
1322
|
}
|
|
1288
1323
|
],
|
|
1289
1324
|
"allowedTools": [
|
|
1325
|
+
"get_subskill_asset",
|
|
1290
1326
|
"get_rows_minimal",
|
|
1291
1327
|
"queue_cells",
|
|
1292
1328
|
"wait_for_campaign_table_ready",
|
|
@@ -1345,6 +1381,7 @@
|
|
|
1345
1381
|
}
|
|
1346
1382
|
],
|
|
1347
1383
|
"allowedTools": [
|
|
1384
|
+
"get_subskill_asset",
|
|
1348
1385
|
"attach_recommended_sequence",
|
|
1349
1386
|
"AskUserQuestion",
|
|
1350
1387
|
"request_user_input"
|
|
@@ -56,7 +56,7 @@ auto-revise leads.
|
|
|
56
56
|
accidentally stop early
|
|
57
57
|
(see §Known Tool Behaviors #3)
|
|
58
58
|
|
|
59
|
-
7.
|
|
59
|
+
7. call `wait_for_rubric_results` with `includeRows=false`; extract ONLY:
|
|
60
60
|
- ready: boolean
|
|
61
61
|
- passRate.completed: number
|
|
62
62
|
- stats: object
|
|
@@ -110,16 +110,11 @@ Observed: default response includes `carryData.Post Content` with
|
|
|
110
110
|
whole LinkedIn post bodies. ~67KB per 25 rows. ~268KB per 100 rows.
|
|
111
111
|
Retaining the full payload across tail turns blows Opus context.
|
|
112
112
|
|
|
113
|
-
Workaround
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
context. Prefer `get_rows_minimal` (not `get_rows`) when row-level
|
|
113
|
+
Workaround: pass `includeRows=false` so `wait_for_rubric_results`
|
|
114
|
+
returns stats only. Never put raw rows payload back into the tail's
|
|
115
|
+
own context. Prefer `get_rows_minimal` (not `get_rows`) when row-level
|
|
117
116
|
inspection is required, and strip `carryData` before retention.
|
|
118
117
|
|
|
119
|
-
Workaround (long-term): add a `includeRows=false` / stats-only mode
|
|
120
|
-
to `wait_for_rubric_results` at the MCP tool level. Tracked as a
|
|
121
|
-
backlog item; until then the subagent-read pattern is the contract.
|
|
122
|
-
|
|
123
118
|
### 3. `wait_for_rubric_results.targetCount` defaults to 25
|
|
124
119
|
|
|
125
120
|
Observed: default `targetCount=25` returns `ready=true` after the first 25
|
|
@@ -104,8 +104,11 @@ Message` column's http_request writes those cells via the cascade.
|
|
|
104
104
|
generation is a credit-spend decision and must happen after the user
|
|
105
105
|
has reviewed the sample.
|
|
106
106
|
|
|
107
|
-
Load `core/auto-execute.yaml` exactly once at the start of Step 13
|
|
108
|
-
|
|
107
|
+
Load `core/auto-execute.yaml` exactly once at the start of Step 13 through
|
|
108
|
+
`get_subskill_asset({ subskillName: "create-campaign-v2", assetPath: "core/auto-execute.yaml" })`.
|
|
109
|
+
All subsequent steps read the already-parsed config. Do not re-load mid-run,
|
|
110
|
+
and do not read repo-local config files; packaged Claude Code and Codex runs
|
|
111
|
+
must use the MCP asset loader.
|
|
109
112
|
Load each subskill prompt (`create-campaign-v2`, `research-sender`,
|
|
110
113
|
`generate-messages`) at most once per run. A multi-call chunk sequence counts
|
|
111
114
|
as one load. If a tool result already told you to load a prompt, load all
|
|
@@ -155,8 +158,9 @@ atomic mint).
|
|
|
155
158
|
> for the lead-list table by name or skip the `mode: "add"` path; do
|
|
156
159
|
> not pass the campaign-table id as `sourceLeadListId`.
|
|
157
160
|
|
|
158
|
-
1. Load `core/auto-execute.yaml
|
|
159
|
-
`
|
|
161
|
+
1. Load `core/auto-execute.yaml` through
|
|
162
|
+
`get_subskill_asset({ subskillName: "create-campaign-v2", assetPath: "core/auto-execute.yaml" })`.
|
|
163
|
+
Capture `import.importLimit`, `sample.sampleSize`, `sample.minProjectedPass`,
|
|
160
164
|
`sample.maxRevisionRounds`, `messaging.tokenContract`,
|
|
161
165
|
`messaging.critique.enabled`, `handoff.autoStart`,
|
|
162
166
|
`handoff.orientation`, `retry.sameToolSameError`,
|
|
@@ -443,4 +447,12 @@ runs.
|
|
|
443
447
|
| `core/auto-execute.yaml` | Start of Step 13, load once; all tail steps read parsed values |
|
|
444
448
|
| `core/auto-execute.README.md` | When tuning `auto-execute.yaml` knobs or reviewing threshold-trip logs |
|
|
445
449
|
|
|
450
|
+
Load every file in this table with:
|
|
451
|
+
|
|
452
|
+
```text
|
|
453
|
+
get_subskill_asset({ subskillName: "create-campaign-v2", assetPath: "<file>" })
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Do not use local filesystem reads for these files in customer runs.
|
|
457
|
+
|
|
446
458
|
</references_index>
|
|
@@ -2263,21 +2263,27 @@ borrow the closest message shape from
|
|
|
2263
2263
|
|
|
2264
2264
|
When crafting messages for multiple rows (e.g., "craft rows 1-5"):
|
|
2265
2265
|
|
|
2266
|
-
**CRITICAL: Use
|
|
2266
|
+
**CRITICAL: Use real parallelism only when the host explicitly supports it for
|
|
2267
|
+
this run. Prefer product-native MCP/tool parallelism that works in both Claude
|
|
2268
|
+
Code and Codex; host subagents are optional acceleration only when the user
|
|
2269
|
+
explicitly asked for agent fan-out.**
|
|
2267
2270
|
|
|
2268
2271
|
1. Load campaign examples, calibration notes, and bounded copy from the brief first.
|
|
2269
2272
|
2. Call `get_message_prompt()` once to get the fallback REPLY framework and QA checks
|
|
2270
|
-
3.
|
|
2273
|
+
3. If the user explicitly asked for agent fan-out and the host exposes subagents,
|
|
2274
|
+
spawn parallel workers with explicit model/config. Otherwise batch independent
|
|
2275
|
+
MCP/tool reads where available or run rows sequentially with honest progress
|
|
2276
|
+
copy. Do not claim subagents or background work unless they actually started.
|
|
2271
2277
|
|
|
2272
2278
|
```
|
|
2273
|
-
For each row (1-5), launch a
|
|
2279
|
+
For each row (1-5), launch a worker only when allowed:
|
|
2274
2280
|
- subagent_type: "general-purpose"
|
|
2275
2281
|
- model: "opus"
|
|
2276
2282
|
- prompt: Include campaign context, sender info, row data, gold-standard message examples when present, and fallback REPLY framework
|
|
2277
2283
|
- Do NOT use run_in_background (avoids notification spam)
|
|
2278
2284
|
```
|
|
2279
2285
|
|
|
2280
|
-
4. Each
|
|
2286
|
+
4. Each worker or sequential row pass:
|
|
2281
2287
|
|
|
2282
2288
|
- Researches the prospect (WebSearch, LinkedIn tools)
|
|
2283
2289
|
- Crafts message by following the campaign's gold-standard pattern first
|
|
@@ -2285,7 +2291,7 @@ For each row (1-5), launch a Task subagent:
|
|
|
2285
2291
|
- Uses the fallback REPLY framework only as structure and QA support
|
|
2286
2292
|
- Returns the draft + research notes (do NOT save)
|
|
2287
2293
|
|
|
2288
|
-
5. Wait for all
|
|
2294
|
+
5. Wait for all workers to complete, or finish the sequential row pass
|
|
2289
2295
|
6. Auto-save drafts for rows where the message cell is empty (announce "Draft saved (not approved)")
|
|
2290
2296
|
7. Show summary:
|
|
2291
2297
|
|
|
@@ -2303,9 +2309,9 @@ Approve all for sending?
|
|
|
2303
2309
|
- Review individually first
|
|
2304
2310
|
```
|
|
2305
2311
|
|
|
2306
|
-
###
|
|
2312
|
+
### Optional Worker Prompt Template
|
|
2307
2313
|
|
|
2308
|
-
When spawning craft
|
|
2314
|
+
When spawning craft workers, include:
|
|
2309
2315
|
|
|
2310
2316
|
```
|
|
2311
2317
|
Craft a personalized LinkedIn message for this prospect using the campaign's
|
|
@@ -2371,7 +2377,9 @@ research → condense → 10 angle agents in parallel → finalizer combines bes
|
|
|
2371
2377
|
- use the strongest safe proof
|
|
2372
2378
|
- make the CTA a useful next step, not a vague meeting ask
|
|
2373
2379
|
5. Build a condensed angle brief (~1.5k tokens) from research + sender info
|
|
2374
|
-
6.
|
|
2380
|
+
6. If explicit host subagent fan-out is available, fan out 10 angle workers
|
|
2381
|
+
(model: "sonnet", no delayed/background notifications). Otherwise explore
|
|
2382
|
+
a smaller bounded set of angles sequentially and do not claim parallelism:
|
|
2375
2383
|
- Each gets the condensed brief + their angle instruction (~2k tokens per agent)
|
|
2376
2384
|
- DO NOT pass the full fallback REPLY framework to angle agents, only the brief
|
|
2377
2385
|
7. After all 10 return, act as the Finalizer:
|
|
@@ -2384,7 +2392,7 @@ research → condense → 10 angle agents in parallel → finalizer combines bes
|
|
|
2384
2392
|
8. Return: { success: true, message: "...", research: [...], anglesExplored: 10 }
|
|
2385
2393
|
```
|
|
2386
2394
|
|
|
2387
|
-
**IMPORTANT:** Load `.sellable/configs/writing/outbound.md`, `styleguide-core.md`, and `.sellable/insights/cross-skill.md` ONCE in the lead agent, then pass **condensed** versions to
|
|
2395
|
+
**IMPORTANT:** Load `.sellable/configs/writing/outbound.md`, `styleguide-core.md`, and `.sellable/insights/cross-skill.md` ONCE in the lead agent, then pass **condensed** versions to any workers. Don't pass full file contents to every worker — that wastes context and causes overflow.
|
|
2388
2396
|
|
|
2389
2397
|
## Batch Approval
|
|
2390
2398
|
|
|
@@ -33,9 +33,12 @@ Provide as many as available. Use `"Unknown"` when missing.
|
|
|
33
33
|
|
|
34
34
|
Choose backend once:
|
|
35
35
|
|
|
36
|
-
1. If
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
1. If product-native MCP/tool parallel calls are available: batch independent
|
|
37
|
+
retrieval units in one turn.
|
|
38
|
+
2. If the user explicitly asked for agent fan-out and `Task` is available: use
|
|
39
|
+
Task subagents in parallel.
|
|
40
|
+
3. Else if `multi_tool_use.parallel` is available: batch independent tool calls via `multi_tool_use.parallel`.
|
|
41
|
+
4. Else: run units sequentially.
|
|
39
42
|
|
|
40
43
|
Never claim Task subagents were used when they were not.
|
|
41
44
|
|