@sellable/mcp 0.1.26 → 0.1.27
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/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/server.js +45 -3
- package/dist/tools/bootstrap.js +3 -3
- package/dist/tools/prompts.d.ts +12 -0
- package/dist/tools/prompts.js +36 -10
- package/package.json +1 -1
- package/skills/create-campaign-brief/references/examples/briefs/clover.md +1 -1
- package/skills/create-campaign-brief/references/examples/briefs/galley.md +1 -1
- package/skills/create-campaign-brief/references/examples/briefs/gelee.md +1 -1
- package/skills/create-campaign-brief/references/examples/briefs/hey-digital.md +1 -1
- package/skills/create-campaign-brief/references/examples/briefs/persona.md +1 -1
- package/skills/create-campaign-brief/references/examples/briefs/revvix.md +1 -1
- package/skills/create-campaign-brief/references/examples/briefs/sellable-dev.md +1 -1
- package/skills/create-campaign-brief/references/examples/briefs/superposition.md +1 -1
- package/skills/create-campaign-brief/references/examples/briefs/superpower.md +1 -1
- package/skills/create-campaign-brief/references/examples/briefs/westpark-villas.md +1 -1
- package/skills/create-campaign-brief/references/phase75-canonical-brief-template.md +1 -1
- package/skills/create-campaign-brief/references/phase75-good-brief-and-messaging-examples.md +4 -4
- package/skills/create-campaign-v2/SKILL.md +23 -14
- package/skills/create-campaign-v2/core/flow.v2.json +2 -2
- package/skills/create-campaign-v2/references/step-13-import-leads.md +6 -2
package/dist/index-dev.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
-
import { getSkillByName, listSkills
|
|
4
|
+
import { getSkillByName, listSkills } from "./skills.js";
|
|
5
5
|
import { authToolDefinitions, getAuthStatus } from "./tools/auth.js";
|
|
6
6
|
import { blueprintCommitToolDefinitions, handleAddColumn, handleCommitBlueprint, } from "./tools/blueprint-commit.js";
|
|
7
7
|
import { bootstrapCreateCampaign, bootstrapToolDefinitions, } from "./tools/bootstrap.js";
|
|
@@ -67,6 +67,35 @@ const allTools = [
|
|
|
67
67
|
...blueprintCommitToolDefinitions,
|
|
68
68
|
...verifyRowToolDefinitions,
|
|
69
69
|
];
|
|
70
|
+
function parseOptionalNumber(value) {
|
|
71
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
72
|
+
return value;
|
|
73
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
74
|
+
const parsed = Number(value);
|
|
75
|
+
if (Number.isFinite(parsed))
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
function formatSubskillPromptText(result) {
|
|
81
|
+
const header = result.chunkCount && result.chunkCount > 1
|
|
82
|
+
? [
|
|
83
|
+
`# ${result.name} prompt chunk ${result.chunkIndex} of ${result.chunkCount}`,
|
|
84
|
+
"",
|
|
85
|
+
`Prompt length: ${result.promptLength} characters.`,
|
|
86
|
+
result.chunkingInstructions ?? "",
|
|
87
|
+
"",
|
|
88
|
+
].join("\n")
|
|
89
|
+
: "";
|
|
90
|
+
const footer = result.hasMore && result.nextOffset !== null
|
|
91
|
+
? [
|
|
92
|
+
"",
|
|
93
|
+
"---",
|
|
94
|
+
`Continue with offset ${result.nextOffset} and limit ${result.limit}.`,
|
|
95
|
+
].join("\n")
|
|
96
|
+
: "";
|
|
97
|
+
return `${header}${result.prompt}${footer}`;
|
|
98
|
+
}
|
|
70
99
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
71
100
|
tools: allTools,
|
|
72
101
|
}));
|
|
@@ -447,16 +476,29 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
|
447
476
|
prompts: listSkills("public").map((skill) => ({
|
|
448
477
|
name: skill.name,
|
|
449
478
|
description: skill.description,
|
|
479
|
+
arguments: [
|
|
480
|
+
{
|
|
481
|
+
name: "offset",
|
|
482
|
+
description: "Optional character offset for chunked prompt reads.",
|
|
483
|
+
required: false,
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
name: "limit",
|
|
487
|
+
description: "Optional max characters to return for chunked prompt reads.",
|
|
488
|
+
required: false,
|
|
489
|
+
},
|
|
490
|
+
],
|
|
450
491
|
})),
|
|
451
492
|
}));
|
|
452
493
|
// Get prompt content
|
|
453
494
|
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
454
495
|
const { name } = request.params;
|
|
496
|
+
const args = request.params.arguments ?? {};
|
|
455
497
|
const skill = getSkillByName(name);
|
|
456
498
|
if (!skill) {
|
|
457
499
|
throw new Error(`Unknown prompt: ${name}`);
|
|
458
500
|
}
|
|
459
|
-
const content =
|
|
501
|
+
const content = getSubskillPrompt(name, parseOptionalNumber(args.offset), parseOptionalNumber(args.limit));
|
|
460
502
|
return {
|
|
461
503
|
description: skill.description,
|
|
462
504
|
messages: [
|
|
@@ -464,7 +506,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
464
506
|
role: "user",
|
|
465
507
|
content: {
|
|
466
508
|
type: "text",
|
|
467
|
-
text: content,
|
|
509
|
+
text: formatSubskillPromptText(content),
|
|
468
510
|
},
|
|
469
511
|
},
|
|
470
512
|
],
|
package/dist/tools/bootstrap.js
CHANGED
|
@@ -215,10 +215,10 @@ export async function bootstrapCreateCampaign(input = {}) {
|
|
|
215
215
|
: "";
|
|
216
216
|
const nextStep = safeToProceed
|
|
217
217
|
? resumeDetected
|
|
218
|
-
? `Bootstrap complete.${workspaceNotice} Resume using campaign context, then load ${createCampaignSubskill?.name ?? "create-campaign"} instructions with get_subskill_prompt({ subskillName: "${createCampaignSubskill?.name ?? "create-campaign"}" }).`
|
|
218
|
+
? `Bootstrap complete.${workspaceNotice} Resume using campaign context, then load ${createCampaignSubskill?.name ?? "create-campaign"} instructions with get_subskill_prompt({ subskillName: "${createCampaignSubskill?.name ?? "create-campaign"}" }); if the response has hasMore=true, continue with nextOffset until hasMore=false.`
|
|
219
219
|
: flowVersion === "v2"
|
|
220
|
-
? `Bootstrap complete.${workspaceNotice} Load create-campaign-v2 instructions with get_subskill_prompt({ subskillName: "create-campaign-v2" }),
|
|
221
|
-
: `Bootstrap complete.${workspaceNotice} Load ${createCampaignSubskill?.name ?? "create-campaign"} instructions with get_subskill_prompt({ subskillName: "${createCampaignSubskill?.name ?? "create-campaign"}" })
|
|
220
|
+
? `Bootstrap complete.${workspaceNotice} Load create-campaign-v2 instructions with get_subskill_prompt({ subskillName: "create-campaign-v2" }); if the response has hasMore=true, continue with nextOffset until hasMore=false. Then start with the create-campaign-brief interview stage. Do not call create_campaign until the v2 approval gate returns approve.`
|
|
221
|
+
: `Bootstrap complete.${workspaceNotice} Load ${createCampaignSubskill?.name ?? "create-campaign"} instructions with get_subskill_prompt({ subskillName: "${createCampaignSubskill?.name ?? "create-campaign"}" }); if the response has hasMore=true, continue with nextOffset until hasMore=false. Follow that flow before calling create_campaign.`
|
|
222
222
|
: "Bootstrap incomplete. Resolve blockingErrors and rerun bootstrap_create_campaign before provider/search/import tools.";
|
|
223
223
|
// Strip prompt body from createCampaignSubskill — it's loaded via the host
|
|
224
224
|
// skill prompt. Only return metadata to save context tokens.
|
package/dist/tools/prompts.d.ts
CHANGED
|
@@ -20,6 +20,9 @@ export interface SubskillPromptResponse {
|
|
|
20
20
|
limit?: number;
|
|
21
21
|
hasMore?: boolean;
|
|
22
22
|
nextOffset?: number | null;
|
|
23
|
+
chunkIndex?: number;
|
|
24
|
+
chunkCount?: number;
|
|
25
|
+
chunkingInstructions?: string;
|
|
23
26
|
}
|
|
24
27
|
export interface ListSubskillPromptsResponse {
|
|
25
28
|
total: number;
|
|
@@ -44,6 +47,8 @@ export interface CompleteSenderResearchInput {
|
|
|
44
47
|
credibilitySignalsFound?: number;
|
|
45
48
|
notes?: string;
|
|
46
49
|
}
|
|
50
|
+
export declare const DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS = 24000;
|
|
51
|
+
export declare const MAX_SUBSKILL_PROMPT_CHUNK_CHARS = 24000;
|
|
47
52
|
export declare const promptToolDefinitions: ({
|
|
48
53
|
name: string;
|
|
49
54
|
description: string;
|
|
@@ -65,6 +70,7 @@ export declare const promptToolDefinitions: ({
|
|
|
65
70
|
required: never[];
|
|
66
71
|
additionalProperties?: undefined;
|
|
67
72
|
};
|
|
73
|
+
_meta?: undefined;
|
|
68
74
|
} | {
|
|
69
75
|
name: string;
|
|
70
76
|
description: string;
|
|
@@ -95,9 +101,13 @@ export declare const promptToolDefinitions: ({
|
|
|
95
101
|
required: never[];
|
|
96
102
|
additionalProperties: boolean;
|
|
97
103
|
};
|
|
104
|
+
_meta?: undefined;
|
|
98
105
|
} | {
|
|
99
106
|
name: string;
|
|
100
107
|
description: string;
|
|
108
|
+
_meta: {
|
|
109
|
+
"anthropic/maxResultSizeChars": number;
|
|
110
|
+
};
|
|
101
111
|
inputSchema: {
|
|
102
112
|
type: string;
|
|
103
113
|
properties: {
|
|
@@ -158,6 +168,7 @@ export declare const promptToolDefinitions: ({
|
|
|
158
168
|
required: string[];
|
|
159
169
|
additionalProperties: boolean;
|
|
160
170
|
};
|
|
171
|
+
_meta?: undefined;
|
|
161
172
|
} | {
|
|
162
173
|
name: string;
|
|
163
174
|
description: string;
|
|
@@ -195,6 +206,7 @@ export declare const promptToolDefinitions: ({
|
|
|
195
206
|
additionalProperties: boolean;
|
|
196
207
|
required?: undefined;
|
|
197
208
|
};
|
|
209
|
+
_meta?: undefined;
|
|
198
210
|
})[];
|
|
199
211
|
export declare function getMessagePrompt(): Promise<PromptResponse>;
|
|
200
212
|
export declare function listSubskillPrompts(limit?: number, includePublic?: boolean, includeInternal?: boolean): ListSubskillPromptsResponse;
|
package/dist/tools/prompts.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { getApi } from "../api.js";
|
|
2
2
|
import { getSkillByName, listSkills, stripFrontmatter } from "../skills.js";
|
|
3
3
|
import { markCreateCampaignPromptLoaded, markResearchPromptLoaded, markSenderResearchCompleted, } from "./flow-preflight.js";
|
|
4
|
+
export const DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS = 24_000;
|
|
5
|
+
export const MAX_SUBSKILL_PROMPT_CHUNK_CHARS = 24_000;
|
|
4
6
|
export const promptToolDefinitions = [
|
|
5
7
|
{
|
|
6
8
|
name: "get_message_prompt",
|
|
@@ -36,7 +38,10 @@ export const promptToolDefinitions = [
|
|
|
36
38
|
},
|
|
37
39
|
{
|
|
38
40
|
name: "get_subskill_prompt",
|
|
39
|
-
description: "Load a Sellable subskill prompt by name. Internal subskills are not listed in prompts/list; use search_subskill_prompts when you are unsure of the exact name.",
|
|
41
|
+
description: "Load a Sellable subskill prompt by name. Large prompts are returned in portable chunks by default; continue with nextOffset until hasMore is false. Internal subskills are not listed in prompts/list; use search_subskill_prompts when you are unsure of the exact name.",
|
|
42
|
+
_meta: {
|
|
43
|
+
"anthropic/maxResultSizeChars": 200_000,
|
|
44
|
+
},
|
|
40
45
|
inputSchema: {
|
|
41
46
|
type: "object",
|
|
42
47
|
properties: {
|
|
@@ -50,7 +55,7 @@ export const promptToolDefinitions = [
|
|
|
50
55
|
},
|
|
51
56
|
limit: {
|
|
52
57
|
type: "number",
|
|
53
|
-
description:
|
|
58
|
+
description: `Optional max characters to return for chunked prompt reads. Defaults to ${DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS} for large prompts and is capped at ${MAX_SUBSKILL_PROMPT_CHUNK_CHARS}.`,
|
|
54
59
|
},
|
|
55
60
|
},
|
|
56
61
|
required: ["subskillName"],
|
|
@@ -141,11 +146,7 @@ export function listSubskillPrompts(limit, includePublic, includeInternal) {
|
|
|
141
146
|
})),
|
|
142
147
|
};
|
|
143
148
|
}
|
|
144
|
-
|
|
145
|
-
const skill = getSkillByName(subskillName);
|
|
146
|
-
if (!skill) {
|
|
147
|
-
throw new Error(`Unknown subskill prompt: ${subskillName}`);
|
|
148
|
-
}
|
|
149
|
+
function markSubskillPromptLoaded(subskillName) {
|
|
149
150
|
if (subskillName === "create-campaign") {
|
|
150
151
|
markCreateCampaignPromptLoaded();
|
|
151
152
|
}
|
|
@@ -158,17 +159,37 @@ export function getSubskillPrompt(subskillName, offset, limit) {
|
|
|
158
159
|
if (subskillName === "research-prospect") {
|
|
159
160
|
markResearchPromptLoaded("prospect", "research-prospect");
|
|
160
161
|
}
|
|
162
|
+
}
|
|
163
|
+
export function getSubskillPrompt(subskillName, offset, limit) {
|
|
164
|
+
const skill = getSkillByName(subskillName);
|
|
165
|
+
if (!skill) {
|
|
166
|
+
throw new Error(`Unknown subskill prompt: ${subskillName}`);
|
|
167
|
+
}
|
|
168
|
+
markSubskillPromptLoaded(subskillName);
|
|
161
169
|
const fullPrompt = stripFrontmatter(skill.content);
|
|
162
170
|
const safeOffset = Math.min(Math.max(Number.isFinite(offset) ? Math.floor(offset ?? 0) : 0, 0), fullPrompt.length);
|
|
163
|
-
const
|
|
171
|
+
const requestedLimit = Number.isFinite(limit) && limit !== undefined
|
|
164
172
|
? Math.max(Math.floor(limit), 1)
|
|
165
173
|
: null;
|
|
174
|
+
const defaultChunkLimit = fullPrompt.length > DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS
|
|
175
|
+
? DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS
|
|
176
|
+
: null;
|
|
177
|
+
const safeLimit = requestedLimit === null
|
|
178
|
+
? defaultChunkLimit
|
|
179
|
+
: Math.min(requestedLimit, MAX_SUBSKILL_PROMPT_CHUNK_CHARS);
|
|
166
180
|
const prompt = safeLimit === null
|
|
167
181
|
? fullPrompt
|
|
168
182
|
: fullPrompt.slice(safeOffset, safeOffset + safeLimit);
|
|
169
183
|
const nextOffset = safeLimit === null
|
|
170
184
|
? null
|
|
171
185
|
: Math.min(safeOffset + safeLimit, fullPrompt.length);
|
|
186
|
+
const hasMore = nextOffset !== null && nextOffset < fullPrompt.length;
|
|
187
|
+
const chunkCount = safeLimit === null
|
|
188
|
+
? 1
|
|
189
|
+
: Math.max(Math.ceil(fullPrompt.length / safeLimit), 1);
|
|
190
|
+
const chunkIndex = safeLimit === null
|
|
191
|
+
? 1
|
|
192
|
+
: Math.min(Math.floor(safeOffset / safeLimit) + 1, chunkCount);
|
|
172
193
|
return {
|
|
173
194
|
name: skill.name,
|
|
174
195
|
description: skill.description,
|
|
@@ -177,8 +198,13 @@ export function getSubskillPrompt(subskillName, offset, limit) {
|
|
|
177
198
|
promptLength: fullPrompt.length,
|
|
178
199
|
offset: safeOffset,
|
|
179
200
|
limit: safeLimit ?? undefined,
|
|
180
|
-
hasMore
|
|
181
|
-
nextOffset:
|
|
201
|
+
hasMore,
|
|
202
|
+
nextOffset: hasMore ? nextOffset : null,
|
|
203
|
+
chunkIndex,
|
|
204
|
+
chunkCount,
|
|
205
|
+
chunkingInstructions: hasMore
|
|
206
|
+
? `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.`
|
|
207
|
+
: undefined,
|
|
182
208
|
};
|
|
183
209
|
}
|
|
184
210
|
export function completeSenderResearch(input) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Clover Campaign Brief + Rubric Archive
|
|
2
2
|
|
|
3
|
-
Source:
|
|
3
|
+
Source: Phase 75 all-client campaign corpus, copied into this prompt reference archive.
|
|
4
4
|
|
|
5
5
|
Campaigns captured: 4
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Galley Campaign Brief + Rubric Archive
|
|
2
2
|
|
|
3
|
-
Source:
|
|
3
|
+
Source: Phase 75 all-client campaign corpus, copied into this prompt reference archive.
|
|
4
4
|
|
|
5
5
|
Campaigns captured: 5
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Gelee Campaign Brief + Rubric Archive
|
|
2
2
|
|
|
3
|
-
Source:
|
|
3
|
+
Source: Phase 75 all-client campaign corpus, copied into this prompt reference archive.
|
|
4
4
|
|
|
5
5
|
Campaigns captured: 1
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Hey Digital Campaign Brief + Rubric Archive
|
|
2
2
|
|
|
3
|
-
Source:
|
|
3
|
+
Source: Phase 75 all-client campaign corpus, copied into this prompt reference archive.
|
|
4
4
|
|
|
5
5
|
Campaigns captured: 4
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Persona Campaign Brief + Rubric Archive
|
|
2
2
|
|
|
3
|
-
Source:
|
|
3
|
+
Source: Phase 75 all-client campaign corpus, copied into this prompt reference archive.
|
|
4
4
|
|
|
5
5
|
Campaigns captured: 3
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Revvix Campaign Brief + Rubric Archive
|
|
2
2
|
|
|
3
|
-
Source:
|
|
3
|
+
Source: Phase 75 all-client campaign corpus, copied into this prompt reference archive.
|
|
4
4
|
|
|
5
5
|
Campaigns captured: 8
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# sellable.dev Campaign Brief + Rubric Archive
|
|
2
2
|
|
|
3
|
-
Source:
|
|
3
|
+
Source: Phase 75 all-client campaign corpus, copied into this prompt reference archive.
|
|
4
4
|
|
|
5
5
|
Campaigns captured: 304
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# superposition Campaign Brief + Rubric Archive
|
|
2
2
|
|
|
3
|
-
Source:
|
|
3
|
+
Source: Phase 75 all-client campaign corpus, copied into this prompt reference archive.
|
|
4
4
|
|
|
5
5
|
Campaigns captured: 5
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Superpower Campaign Brief + Rubric Archive
|
|
2
2
|
|
|
3
|
-
Source:
|
|
3
|
+
Source: Phase 75 all-client campaign corpus, copied into this prompt reference archive.
|
|
4
4
|
|
|
5
5
|
Campaigns captured: 3
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# westpark villas Campaign Brief + Rubric Archive
|
|
2
2
|
|
|
3
|
-
Source:
|
|
3
|
+
Source: Phase 75 all-client campaign corpus, copied into this prompt reference archive.
|
|
4
4
|
|
|
5
5
|
Campaigns captured: 26
|
|
6
6
|
|
|
@@ -167,7 +167,7 @@ Add only when the campaign truly needs them:
|
|
|
167
167
|
|
|
168
168
|
Reference:
|
|
169
169
|
|
|
170
|
-
-
|
|
170
|
+
- `75-MESSAGE-VARIANT-GENERATION-FLOW.md` in the Phase 75 planning archive.
|
|
171
171
|
|
|
172
172
|
## Section Rules
|
|
173
173
|
|
package/skills/create-campaign-brief/references/phase75-good-brief-and-messaging-examples.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Phase 75 Live Brief + Messaging Example Bank
|
|
2
2
|
|
|
3
|
-
Source of truth:
|
|
3
|
+
Source of truth: Phase 75 live campaign corpus used to generate this prompt reference.
|
|
4
4
|
|
|
5
|
-
Full archive of client campaign briefs:
|
|
5
|
+
Full archive of client campaign briefs: Phase 75 all-client campaign corpus, copied into this prompt reference archive.
|
|
6
6
|
|
|
7
|
-
Direct copied archive of full campaign brief content + rubrics:
|
|
7
|
+
Direct copied archive of full campaign brief content + rubrics: `brief-rubric-archive/INDEX.md` in the Phase 75 planning archive.
|
|
8
8
|
|
|
9
9
|
This file distills the strongest live campaign briefs currently stored in production. The runtime gold set should stay narrow: active clients only, plus the one Hey Digital campaign that clearly performed and still teaches useful structure. The goal is not to preserve every brief shape forever. The goal is to preserve the patterns that consistently show up in the briefs that are actually usable, persuasive, and close to what Sellable has been shipping manually for clients.
|
|
10
10
|
|
|
@@ -20,7 +20,7 @@ Important note:
|
|
|
20
20
|
|
|
21
21
|
Runtime gold pack:
|
|
22
22
|
|
|
23
|
-
-
|
|
23
|
+
- `75-ACTIVE-RUNTIME-MESSAGE-PACK.md` in the Phase 75 planning archive.
|
|
24
24
|
|
|
25
25
|
## Winning Campaigns In Scope
|
|
26
26
|
|
|
@@ -482,7 +482,9 @@ Sellable lead lists in the main three-option first batch; support them through
|
|
|
482
482
|
custom/freeform input. If the user pastes up to 100 LinkedIn profile URLs or
|
|
483
483
|
company domains, normalize the paste into a temporary local CSV and continue
|
|
484
484
|
through the matching CSV preview path. Mixed, ambiguous, malformed, or oversized
|
|
485
|
-
pastes should ask for a real CSV file instead of guessing.
|
|
485
|
+
pastes should ask for a real CSV file instead of guessing. Uploaded CSV support
|
|
486
|
+
is larger than paste support: LinkedIn profile CSVs can contain up to 7,500
|
|
487
|
+
rows; domain CSVs can contain up to 7,500 rows but only 1,000 unique domains.
|
|
486
488
|
|
|
487
489
|
Avoid internal wording like `Which proof points should the message be allowed
|
|
488
490
|
to lean on?` because it describes the artifact, not the founder decision.
|
|
@@ -555,6 +557,10 @@ usable leads`.
|
|
|
555
557
|
- supplied LinkedIn profile CSVs call `load_csv_linkedin_leads` in preview mode
|
|
556
558
|
only before approval; do not pass `confirmed: true`, `campaignOfferId`,
|
|
557
559
|
`currentStep`, `leadListId`, `sourceLeadListId`, or provider-import params
|
|
560
|
+
during the preview. After approval, this branch is a two-stage batch path:
|
|
561
|
+
materialize the full supplied CSV into a Sellable lead-list table first, then
|
|
562
|
+
use the returned `leadListId` as `sourceLeadListId` for the bounded campaign
|
|
563
|
+
review import
|
|
558
564
|
- supplied company/domain CSVs call `load_csv_domains`; pre-approval
|
|
559
565
|
confirmation is allowed only without `campaignOfferId` or `currentStep` and
|
|
560
566
|
only to produce a standalone `domainFilterId` for campaignless
|
|
@@ -766,12 +772,13 @@ Run the `generate-messages` skill in caller-declared `DRY MODE`. Its SKILL.md
|
|
|
766
772
|
holds the full drafting contract (retrieval, proof inventory, candidates,
|
|
767
773
|
finalizer pass, voice rules, safety). This step only covers orchestration.
|
|
768
774
|
This is not optional: before any write to `message-validation.md`,
|
|
769
|
-
`message-review.md`, `approval-packet.md`, or a commit-gate question, the
|
|
770
|
-
full `generate-messages` prompt. In hosted/from-
|
|
771
|
-
chunked `get_subskill_prompt({ subskillName:
|
|
772
|
-
limit })` calls so every tool result stays small
|
|
773
|
-
harness. Start with `offset: 0
|
|
774
|
-
`offset: nextOffset`
|
|
775
|
+
`message-review.md`, `approval-packet.md`, or a commit-gate question, the
|
|
776
|
+
current run must load the full `generate-messages` prompt. In hosted/from-
|
|
777
|
+
scratch runs, load it with chunked `get_subskill_prompt({ subskillName:
|
|
778
|
+
"generate-messages", offset, limit })` calls so every tool result stays small
|
|
779
|
+
enough for the streamed harness. Start with `offset: 0`; the MCP will apply a
|
|
780
|
+
portable default limit when omitted. Keep calling with `offset: nextOffset`
|
|
781
|
+
until `hasMore` is false.
|
|
775
782
|
In Codex-hosted runs, this is a quality gate, not just provenance: if the
|
|
776
783
|
model cannot retrieve the complete prompt or cannot follow the required
|
|
777
784
|
gold-standard deliberation flow, stop at `message-review` and ask for
|
|
@@ -1648,9 +1655,9 @@ Message` column's http_request writes those cells via the cascade.
|
|
|
1648
1655
|
Load `core/auto-execute.yaml` exactly once at the start of Step 13. All
|
|
1649
1656
|
subsequent steps read the already-parsed config. Do not re-load mid-run.
|
|
1650
1657
|
Load each subskill prompt (`create-campaign-v2`, `research-sender`,
|
|
1651
|
-
`generate-messages`) at most once per run
|
|
1652
|
-
told you to load a prompt, load
|
|
1653
|
-
same prompt later.
|
|
1658
|
+
`generate-messages`) at most once per run. A multi-call chunk sequence counts
|
|
1659
|
+
as one load. If a tool result already told you to load a prompt, load all
|
|
1660
|
+
chunks once and remember; do not restart the same prompt from offset 0 later.
|
|
1654
1661
|
|
|
1655
1662
|
After every `update_campaign({ currentStep: ... })` in the tail, narrate
|
|
1656
1663
|
what changed and orient the user to what the watch link will show next —
|
|
@@ -1707,10 +1714,12 @@ atomic mint).
|
|
|
1707
1714
|
`import_leads({ campaignOfferId, targetLeadCount: importLimit })`.
|
|
1708
1715
|
If a manifest exists, branch by `sourceType`:
|
|
1709
1716
|
- `supplied-linkedin-profiles`: revalidate file metadata and confirmation
|
|
1710
|
-
token, confirm `load_csv_linkedin_leads` only after approval
|
|
1711
|
-
returned `leadListId`,
|
|
1712
|
-
`confirm_lead_list({ sourceLeadListId: leadListId,
|
|
1713
|
-
importLimit })`.
|
|
1717
|
+
token, confirm `load_csv_linkedin_leads` only after approval to batch the
|
|
1718
|
+
supplied CSV into a Sellable lead list, persist the returned `leadListId`,
|
|
1719
|
+
then call `confirm_lead_list({ sourceLeadListId: leadListId,
|
|
1720
|
+
targetLeadCount: importLimit })`. Do not call `import_leads` for this
|
|
1721
|
+
branch; the lead list is the source, and `confirm_lead_list` imports the
|
|
1722
|
+
bounded review batch into the campaign table.
|
|
1714
1723
|
- `existing-lead-list`: revalidate that the lead list still exists in the
|
|
1715
1724
|
same workspace, reuse it as `sourceLeadListId`, then call
|
|
1716
1725
|
`confirm_lead_list({ sourceLeadListId, targetLeadCount: importLimit })`.
|
|
@@ -345,7 +345,7 @@
|
|
|
345
345
|
},
|
|
346
346
|
"optionalRequiredArtifacts": ["lead-source-intake.json"],
|
|
347
347
|
"toolRules": {
|
|
348
|
-
"suppliedLinkedinProfiles": "Preview only before approval: call load_csv_linkedin_leads without confirmed, campaignOfferId, currentStep, leadListId, sourceLeadListId, or provider-import parameters; skip provider discovery.",
|
|
348
|
+
"suppliedLinkedinProfiles": "Preview only before approval: call load_csv_linkedin_leads without confirmed, campaignOfferId, currentStep, leadListId, sourceLeadListId, or provider-import parameters; skip provider discovery. After approval, batch/materialize the supplied CSV into a Sellable lead list first, then use the returned leadListId as sourceLeadListId for confirm_lead_list.",
|
|
349
349
|
"suppliedDomains": "May confirm load_csv_domains before approval only without campaignOfferId/currentStep to produce a standalone domainFilterId, then run a campaignless Prospeo sample constrained by domainFilterId.",
|
|
350
350
|
"existingLeadList": "Skip provider discovery and sample existing rows before approval; do not clone/import until after approval.",
|
|
351
351
|
"forbiddenPreApproval": [
|
|
@@ -1022,7 +1022,7 @@
|
|
|
1022
1022
|
"defaultWhenMissing": "discovered-provider-import",
|
|
1023
1023
|
"branches": {
|
|
1024
1024
|
"normal-discovery": "import_leads({ campaignOfferId, targetLeadCount: import.importLimit })",
|
|
1025
|
-
"supplied-linkedin-profiles": "confirm load_csv_linkedin_leads after approval, then confirm_lead_list({ sourceLeadListId, targetLeadCount: import.importLimit })",
|
|
1025
|
+
"supplied-linkedin-profiles": "confirm load_csv_linkedin_leads after approval to batch/materialize the CSV into a lead list, persist returned leadListId, then confirm_lead_list({ sourceLeadListId: leadListId, targetLeadCount: import.importLimit }); do not call import_leads for this branch",
|
|
1026
1026
|
"existing-lead-list": "confirm_lead_list({ sourceLeadListId: existingLeadListId, targetLeadCount: import.importLimit })",
|
|
1027
1027
|
"supplied-domains": "reuse or confirm load_csv_domains, run campaign-associated search_prospeo({ campaignOfferId, domainFilterId }), then import_leads({ campaignOfferId, targetLeadCount: import.importLimit })"
|
|
1028
1028
|
},
|
|
@@ -23,8 +23,12 @@ Supported branches:
|
|
|
23
23
|
|
|
24
24
|
- **No manifest / normal discovery** — use the existing provider import path.
|
|
25
25
|
- **Supplied LinkedIn profile CSV** — confirm `load_csv_linkedin_leads` only
|
|
26
|
-
after approval
|
|
27
|
-
`
|
|
26
|
+
after approval to batch/materialize the full uploaded CSV into a Sellable
|
|
27
|
+
lead-list table. Persist the returned `leadListId`, then import the bounded
|
|
28
|
+
review batch into the campaign table with
|
|
29
|
+
`confirm_lead_list({ sourceLeadListId: leadListId, targetLeadCount: importLimit })`.
|
|
30
|
+
Do not call `import_leads` for this branch; the materialized lead list is the
|
|
31
|
+
source.
|
|
28
32
|
Do **not** call `wait_for_lead_list_ready` for this branch; there is no
|
|
29
33
|
provider import job to wait on, and probing readiness can leave the campaign
|
|
30
34
|
in an import-failed state before `confirm_lead_list`.
|