@sellable/mcp 0.1.315 → 0.1.319
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 +25 -4
- package/dist/generated/column-schema-manifest.js +1 -1
- package/dist/server.js +1 -0
- package/dist/tools/blueprint-commit.js +1 -1
- package/dist/tools/bootstrap.d.ts +5 -0
- package/dist/tools/bootstrap.js +10 -5
- package/dist/tools/inbox.d.ts +308 -0
- package/dist/tools/inbox.js +544 -0
- package/dist/tools/linkedin.d.ts +76 -0
- package/dist/tools/linkedin.js +351 -18
- package/dist/tools/model-quality.d.ts +3 -2
- package/dist/tools/model-quality.js +49 -130
- package/dist/tools/readiness.js +44 -13
- package/dist/tools/registry.d.ts +15 -0
- package/package.json +1 -1
- package/skills/building-gtm-tables/SKILL.md +26 -0
- package/skills/building-gtm-tables/references/column-type-catalog.md +15 -1
- package/skills/building-gtm-tables/references/common-blueprints.fixtures.ts +54 -5
- package/skills/building-gtm-tables/references/common-blueprints.md +9 -0
- package/skills/create-campaign/SKILL.md +77 -32
- package/skills/create-campaign/core/model-quality.json +4 -5
- package/skills/create-campaign-v2/references/approval-gate-framing.md +6 -1
- package/skills/research/config.json +9 -0
package/dist/tools/readiness.js
CHANGED
|
@@ -20,6 +20,23 @@ function normalizeLeadSourceProvider(provider) {
|
|
|
20
20
|
return "csv-linkedin";
|
|
21
21
|
return undefined;
|
|
22
22
|
}
|
|
23
|
+
function isPositiveNumber(value) {
|
|
24
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
25
|
+
}
|
|
26
|
+
function hasSignalDiscoveryProgressEvidence(rowCount, importProgress) {
|
|
27
|
+
const phase = importProgress?.phase ?? null;
|
|
28
|
+
return (rowCount > 0 ||
|
|
29
|
+
isPositiveNumber(importProgress?.processed) ||
|
|
30
|
+
isPositiveNumber(importProgress?.leadsImported) ||
|
|
31
|
+
isPositiveNumber(importProgress?.scrapedEngagers) ||
|
|
32
|
+
isPositiveNumber(importProgress?.evaluatedEngagers) ||
|
|
33
|
+
isPositiveNumber(importProgress?.evaluationTotal) ||
|
|
34
|
+
isPositiveNumber(importProgress?.activeEvaluationBatches) ||
|
|
35
|
+
isPositiveNumber(importProgress?.queuedForEvaluation) ||
|
|
36
|
+
isPositiveNumber(importProgress?.postsScraped) ||
|
|
37
|
+
phase === "scraping" ||
|
|
38
|
+
phase === "processing");
|
|
39
|
+
}
|
|
23
40
|
function sleep(ms) {
|
|
24
41
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
25
42
|
}
|
|
@@ -233,6 +250,8 @@ export async function waitForLeadListReady(input) {
|
|
|
233
250
|
if (typeof configTarget === "number" && !targetLeadCount) {
|
|
234
251
|
targetLeadCount = configTarget;
|
|
235
252
|
}
|
|
253
|
+
const signalDiscoveryHasProgressEvidence = provider === "signal-discovery" &&
|
|
254
|
+
hasSignalDiscoveryProgressEvidence(rowCount, importProgress);
|
|
236
255
|
let jobId = input.jobId;
|
|
237
256
|
if (!jobId && typeof config?.importJobId === "string") {
|
|
238
257
|
jobId = config.importJobId;
|
|
@@ -253,7 +272,9 @@ export async function waitForLeadListReady(input) {
|
|
|
253
272
|
targetLeadCount,
|
|
254
273
|
};
|
|
255
274
|
}
|
|
256
|
-
if (configError &&
|
|
275
|
+
if (configError &&
|
|
276
|
+
configStatus !== "complete" &&
|
|
277
|
+
!signalDiscoveryHasProgressEvidence) {
|
|
257
278
|
return {
|
|
258
279
|
ready: false,
|
|
259
280
|
reason: "import_failed",
|
|
@@ -267,21 +288,31 @@ export async function waitForLeadListReady(input) {
|
|
|
267
288
|
error: configError,
|
|
268
289
|
};
|
|
269
290
|
}
|
|
291
|
+
if (configError &&
|
|
292
|
+
configStatus !== "complete" &&
|
|
293
|
+
signalDiscoveryHasProgressEvidence) {
|
|
294
|
+
lastError = configError;
|
|
295
|
+
}
|
|
270
296
|
if (configStatus) {
|
|
271
297
|
lastStatus = configStatus;
|
|
272
298
|
if (configStatus === "error") {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
299
|
+
if (signalDiscoveryHasProgressEvidence) {
|
|
300
|
+
lastError = configError ?? lastError;
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
return {
|
|
304
|
+
ready: false,
|
|
305
|
+
reason: "import_failed",
|
|
306
|
+
leadListId,
|
|
307
|
+
provider: provider ?? null,
|
|
308
|
+
attempts,
|
|
309
|
+
elapsedMs: Date.now() - start,
|
|
310
|
+
rowCount,
|
|
311
|
+
status: configStatus,
|
|
312
|
+
targetLeadCount,
|
|
313
|
+
error: configError,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
285
316
|
}
|
|
286
317
|
if (configStatus === "complete") {
|
|
287
318
|
importComplete = true;
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -407,6 +407,10 @@ export declare const allTools: ({
|
|
|
407
407
|
type: string;
|
|
408
408
|
description: string;
|
|
409
409
|
};
|
|
410
|
+
modelMetadataSource: {
|
|
411
|
+
type: string;
|
|
412
|
+
description: string;
|
|
413
|
+
};
|
|
410
414
|
};
|
|
411
415
|
additionalProperties: boolean;
|
|
412
416
|
};
|
|
@@ -6309,6 +6313,7 @@ export declare const allTools: ({
|
|
|
6309
6313
|
postUrl?: undefined;
|
|
6310
6314
|
sources?: undefined;
|
|
6311
6315
|
full?: undefined;
|
|
6316
|
+
fetchMinimal?: undefined;
|
|
6312
6317
|
companyUrl?: undefined;
|
|
6313
6318
|
sortBy?: undefined;
|
|
6314
6319
|
linkedin_url?: undefined;
|
|
@@ -6339,6 +6344,7 @@ export declare const allTools: ({
|
|
|
6339
6344
|
};
|
|
6340
6345
|
linkedinUrl?: undefined;
|
|
6341
6346
|
full?: undefined;
|
|
6347
|
+
fetchMinimal?: undefined;
|
|
6342
6348
|
companyUrl?: undefined;
|
|
6343
6349
|
sortBy?: undefined;
|
|
6344
6350
|
linkedin_url?: undefined;
|
|
@@ -6361,6 +6367,11 @@ export declare const allTools: ({
|
|
|
6361
6367
|
description: string;
|
|
6362
6368
|
default: boolean;
|
|
6363
6369
|
};
|
|
6370
|
+
fetchMinimal: {
|
|
6371
|
+
type: string;
|
|
6372
|
+
description: string;
|
|
6373
|
+
default: boolean;
|
|
6374
|
+
};
|
|
6364
6375
|
limit?: undefined;
|
|
6365
6376
|
postUrl?: undefined;
|
|
6366
6377
|
sources?: undefined;
|
|
@@ -6386,6 +6397,7 @@ export declare const allTools: ({
|
|
|
6386
6397
|
postUrl?: undefined;
|
|
6387
6398
|
sources?: undefined;
|
|
6388
6399
|
full?: undefined;
|
|
6400
|
+
fetchMinimal?: undefined;
|
|
6389
6401
|
sortBy?: undefined;
|
|
6390
6402
|
linkedin_url?: undefined;
|
|
6391
6403
|
max_posts?: undefined;
|
|
@@ -6417,6 +6429,7 @@ export declare const allTools: ({
|
|
|
6417
6429
|
postUrl?: undefined;
|
|
6418
6430
|
sources?: undefined;
|
|
6419
6431
|
full?: undefined;
|
|
6432
|
+
fetchMinimal?: undefined;
|
|
6420
6433
|
linkedin_url?: undefined;
|
|
6421
6434
|
max_posts?: undefined;
|
|
6422
6435
|
};
|
|
@@ -6442,6 +6455,7 @@ export declare const allTools: ({
|
|
|
6442
6455
|
postUrl?: undefined;
|
|
6443
6456
|
sources?: undefined;
|
|
6444
6457
|
full?: undefined;
|
|
6458
|
+
fetchMinimal?: undefined;
|
|
6445
6459
|
companyUrl?: undefined;
|
|
6446
6460
|
sortBy?: undefined;
|
|
6447
6461
|
};
|
|
@@ -6462,6 +6476,7 @@ export declare const allTools: ({
|
|
|
6462
6476
|
postUrl?: undefined;
|
|
6463
6477
|
sources?: undefined;
|
|
6464
6478
|
full?: undefined;
|
|
6479
|
+
fetchMinimal?: undefined;
|
|
6465
6480
|
companyUrl?: undefined;
|
|
6466
6481
|
sortBy?: undefined;
|
|
6467
6482
|
max_posts?: undefined;
|
package/package.json
CHANGED
|
@@ -111,6 +111,10 @@ Blueprint rules:
|
|
|
111
111
|
- Use stable, readable column ids such as `linkedin_url`, `enrich_prospect`, and
|
|
112
112
|
`score_icp_mcp`.
|
|
113
113
|
- Use registry type keys exactly, not display names.
|
|
114
|
+
- Treat the API column registry as the source of truth for type validation.
|
|
115
|
+
MCP code must not maintain its own hardcoded column-type allowlist; if the API
|
|
116
|
+
returns `phantom_type`, `deprecated_type`, or `not_createable_type`, fix the
|
|
117
|
+
blueprint to match the current registry response.
|
|
114
118
|
- Treat "Enrich Prospect" as a `http_request` preset, not its own type.
|
|
115
119
|
- Use `score_icp_mcp` for new AI ICP scoring. Do not use legacy
|
|
116
120
|
`score_icp` or the old `score_icp_rubric` alias; they are reserved for older
|
|
@@ -145,6 +149,28 @@ Blueprint rules:
|
|
|
145
149
|
synced sender connections, a positive `check_connection`, or accepted invite
|
|
146
150
|
proof from the current flow.
|
|
147
151
|
|
|
152
|
+
Config rules:
|
|
153
|
+
|
|
154
|
+
- Put native column settings in `config`. The commit path preserves full config
|
|
155
|
+
objects and validates them server-side before mutation.
|
|
156
|
+
- Use blueprint-level `inputMapping` for references to existing producer
|
|
157
|
+
columns. The commit path materializes those references into production
|
|
158
|
+
template mappings such as `{{<actual column id>}}` or
|
|
159
|
+
`{{<actual Enrich Prospect column id>.id}}`.
|
|
160
|
+
- Use `runCondition` for branch gates that should exist in production. Template
|
|
161
|
+
tokens in `config`, AI prompts, formula expressions, and `runCondition` are
|
|
162
|
+
dependency-bearing and must point to real producer columns.
|
|
163
|
+
- For outbound HTTP throttling, set `http_request.config.rateLimit` as either
|
|
164
|
+
`{ "mode": "window", "maxRequests": N, "windowSeconds": S }` or
|
|
165
|
+
`{ "mode": "concurrency", "maxConcurrent": N }`.
|
|
166
|
+
- For public webhook intake throttling, set
|
|
167
|
+
`inbound_webhook.config.rateLimit` as
|
|
168
|
+
`{ "maxRequests": N, "windowSeconds": S }`. Omit it to use the server default.
|
|
169
|
+
- Do not invent per-column `rateLimit` for LinkedIn action columns. Those are
|
|
170
|
+
governed by sender/account limits and scheduling windows; configure action
|
|
171
|
+
behavior with `actionConfig`, `waitForEvent`, `runCondition`, and producer
|
|
172
|
+
mappings.
|
|
173
|
+
|
|
148
174
|
## Commit phase
|
|
149
175
|
|
|
150
176
|
Use exactly one tool for the initial commit:
|
|
@@ -55,10 +55,11 @@ read/edit/delete safety, but they are not valid `add_column` or
|
|
|
55
55
|
|
|
56
56
|
- type: `datetime`
|
|
57
57
|
- displayName: `Date/Time`
|
|
58
|
+
- lifecycle: hidden active, load-only; not a valid create target.
|
|
58
59
|
- category: Lead Data
|
|
59
60
|
- inputs: none.
|
|
60
61
|
- outputs: ISO-like date/time value.
|
|
61
|
-
- when to use:
|
|
62
|
+
- when to use: Existing tables may contain system-owned scheduling timestamps.
|
|
62
63
|
- when NOT to use: Do not use to wait; use `wait` for sequence timing.
|
|
63
64
|
|
|
64
65
|
### Next Action
|
|
@@ -117,6 +118,8 @@ read/edit/delete safety, but they are not valid `add_column` or
|
|
|
117
118
|
- displayName: `Inbound Webhook`
|
|
118
119
|
- category: Lead Data
|
|
119
120
|
- inputs: none.
|
|
121
|
+
- config: optional `rateLimit` as `{ "maxRequests": N, "windowSeconds": S }`;
|
|
122
|
+
server defaults to 100 requests per 60 seconds when omitted.
|
|
120
123
|
- outputs: received JSON payload and webhook metadata.
|
|
121
124
|
- when to use: Use when rows are populated by external webhook events.
|
|
122
125
|
- when NOT to use: Do not use for polling APIs; use `http_request`.
|
|
@@ -163,6 +166,8 @@ read/edit/delete safety, but they are not valid `add_column` or
|
|
|
163
166
|
- displayName: `Check Open Profile`
|
|
164
167
|
- category: LinkedIn Actions
|
|
165
168
|
- inputs: required `linkedin_url`.
|
|
169
|
+
- config: may use the same `rateLimit` shape as `http_request` for provider
|
|
170
|
+
lookup throttling.
|
|
166
171
|
- outputs: boolean open-profile decision and lookup metadata.
|
|
167
172
|
- when to use: Use before selecting open-profile InMail behavior.
|
|
168
173
|
- when NOT to use: Do not use to check first-degree connection; use `check_connection`.
|
|
@@ -296,6 +301,11 @@ read/edit/delete safety, but they are not valid `add_column` or
|
|
|
296
301
|
|
|
297
302
|
## Data & Logic
|
|
298
303
|
|
|
304
|
+
LinkedIn action columns do not accept a generic per-column `rateLimit`. They
|
|
305
|
+
use sender/account daily limits, sending hours, and action scheduling outside
|
|
306
|
+
the blueprint column config. Use `actionConfig.waitForEvent`, `runCondition`,
|
|
307
|
+
and explicit producer mappings for action behavior.
|
|
308
|
+
|
|
299
309
|
### AI Column
|
|
300
310
|
|
|
301
311
|
- type: `ai_column`
|
|
@@ -332,6 +342,10 @@ read/edit/delete safety, but they are not valid `add_column` or
|
|
|
332
342
|
- displayName: `HTTP Request`
|
|
333
343
|
- category: Data & Logic
|
|
334
344
|
- inputs: required `method`, required `endpoint`, optional `headers`, optional `body`.
|
|
345
|
+
- config: optional `rateLimit` supports rolling windows
|
|
346
|
+
`{ "mode": "window", "maxRequests": N, "windowSeconds": S }` and concurrency
|
|
347
|
+
lanes `{ "mode": "concurrency", "maxConcurrent": N }`. Omit it to use the
|
|
348
|
+
server's default I/O concurrency lane.
|
|
335
349
|
- outputs: HTTP status, response body, and metadata; Enrich Prospect also returns root `id` as the EnrichedProspect id.
|
|
336
350
|
- when to use: Use for REST API calls, including the canonical "Enrich Prospect" preset at `/api/v4/enrich-prospect`.
|
|
337
351
|
- when NOT to use: Do not use when a first-party typed column already handles the action.
|
|
@@ -31,6 +31,7 @@ const httpEnrichConfig = {
|
|
|
31
31
|
endpoint: { value: "/api/v4/enrich-prospect" },
|
|
32
32
|
body: { value: '{"linkedinUrl":"{{LinkedIn URL}}"}' },
|
|
33
33
|
},
|
|
34
|
+
runCondition: "{{LinkedIn URL}} !== ''",
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
export const COMMON_BLUEPRINTS: CommonBlueprintEntry[] = [
|
|
@@ -51,7 +52,10 @@ export const COMMON_BLUEPRINTS: CommonBlueprintEntry[] = [
|
|
|
51
52
|
id: "c3",
|
|
52
53
|
type: "score_icp_mcp",
|
|
53
54
|
name: "ICP Score",
|
|
54
|
-
config: {
|
|
55
|
+
config: {
|
|
56
|
+
rubric: enrichmentRubric,
|
|
57
|
+
runCondition: "{{Enrich Prospect.id}} !== ''",
|
|
58
|
+
},
|
|
55
59
|
inputMapping: { enrichedProspectId: "c2" },
|
|
56
60
|
},
|
|
57
61
|
],
|
|
@@ -78,7 +82,10 @@ export const COMMON_BLUEPRINTS: CommonBlueprintEntry[] = [
|
|
|
78
82
|
id: "c3",
|
|
79
83
|
type: "score_icp_mcp",
|
|
80
84
|
name: "ICP Score",
|
|
81
|
-
config: {
|
|
85
|
+
config: {
|
|
86
|
+
rubric: enrichmentRubric,
|
|
87
|
+
runCondition: "{{Enrich Prospect.id}} !== ''",
|
|
88
|
+
},
|
|
82
89
|
inputMapping: { enrichedProspectId: "c2" },
|
|
83
90
|
},
|
|
84
91
|
{
|
|
@@ -131,7 +138,10 @@ export const COMMON_BLUEPRINTS: CommonBlueprintEntry[] = [
|
|
|
131
138
|
id: "c4",
|
|
132
139
|
type: "score_icp_mcp",
|
|
133
140
|
name: "ICP Score",
|
|
134
|
-
config: {
|
|
141
|
+
config: {
|
|
142
|
+
rubric: enrichmentRubric,
|
|
143
|
+
runCondition: "{{Enrich Prospect.id}} !== ''",
|
|
144
|
+
},
|
|
135
145
|
inputMapping: { enrichedProspectId: "c3" },
|
|
136
146
|
},
|
|
137
147
|
{
|
|
@@ -139,13 +149,15 @@ export const COMMON_BLUEPRINTS: CommonBlueprintEntry[] = [
|
|
|
139
149
|
type: "generate_message",
|
|
140
150
|
name: "Generate Message",
|
|
141
151
|
config: {
|
|
142
|
-
runCondition:
|
|
152
|
+
runCondition:
|
|
153
|
+
"{{Enrich Prospect.id}} !== '' && {{ICP Score.score}} >= 70",
|
|
143
154
|
},
|
|
144
155
|
inputMapping: {
|
|
145
156
|
campaignOfferId: "c2",
|
|
146
157
|
enrichedProspectId: "c3",
|
|
147
158
|
},
|
|
148
|
-
runCondition:
|
|
159
|
+
runCondition:
|
|
160
|
+
"{{Enrich Prospect.id}} !== '' && {{ICP Score.score}} >= 70",
|
|
149
161
|
},
|
|
150
162
|
],
|
|
151
163
|
edges: [
|
|
@@ -196,4 +208,41 @@ export const COMMON_BLUEPRINTS: CommonBlueprintEntry[] = [
|
|
|
196
208
|
],
|
|
197
209
|
},
|
|
198
210
|
},
|
|
211
|
+
{
|
|
212
|
+
slug: "rate-limited-http-and-webhook",
|
|
213
|
+
description:
|
|
214
|
+
"Receive webhook payloads and call a rate-limited HTTP enrichment API.",
|
|
215
|
+
blueprint: {
|
|
216
|
+
columns: [
|
|
217
|
+
{ id: "c1", type: "text", name: "Company Domain", config: {} },
|
|
218
|
+
{
|
|
219
|
+
id: "c2",
|
|
220
|
+
type: "inbound_webhook",
|
|
221
|
+
name: "Inbound Payload",
|
|
222
|
+
config: {
|
|
223
|
+
rateLimit: { maxRequests: 100, windowSeconds: 60 },
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: "c3",
|
|
228
|
+
type: "http_request",
|
|
229
|
+
name: "Fetch Account Context",
|
|
230
|
+
config: {
|
|
231
|
+
inputMapping: {
|
|
232
|
+
method: { value: "POST" },
|
|
233
|
+
endpoint: { value: "https://example.test/account-context" },
|
|
234
|
+
body: { value: '{"domain":"{{Company Domain}}"}' },
|
|
235
|
+
},
|
|
236
|
+
rateLimit: {
|
|
237
|
+
mode: "window",
|
|
238
|
+
maxRequests: 10,
|
|
239
|
+
windowSeconds: 60,
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
inputMapping: { domain: "c1" },
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
edges: [{ from: "c1", to: "c3", via: "domain" }],
|
|
246
|
+
},
|
|
247
|
+
},
|
|
199
248
|
];
|
|
@@ -42,3 +42,12 @@ Use `ai_column` only when the user needs a bespoke prompt that is not covered by
|
|
|
42
42
|
fixed enrichment, scoring, or LinkedIn action columns.
|
|
43
43
|
|
|
44
44
|
See `COMMON_BLUEPRINTS[3].blueprint` in the fixture.
|
|
45
|
+
|
|
46
|
+
## 5. Rate-Limited HTTP + Webhook (`rate-limited-http-and-webhook`)
|
|
47
|
+
|
|
48
|
+
Use `inbound_webhook.config.rateLimit` for public webhook intake and
|
|
49
|
+
`http_request.config.rateLimit` for outbound HTTP calls. HTTP supports rolling
|
|
50
|
+
window limits with `mode: "window"`, `maxRequests`, and `windowSeconds`, or
|
|
51
|
+
concurrency limits with `mode: "concurrency"` and `maxConcurrent`.
|
|
52
|
+
|
|
53
|
+
See `COMMON_BLUEPRINTS[4].blueprint` in the fixture.
|
|
@@ -179,17 +179,11 @@ person/company this campaign is for, then I’ll turn that into a campaign brief
|
|
|
179
179
|
before we move into lead sourcing.
|
|
180
180
|
```
|
|
181
181
|
|
|
182
|
-
Exception: if `bootstrap_create_campaign.modelQuality.status === "warn"
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
message generation, or launch review. If `metadataStale === true`, continue
|
|
188
|
-
normally and do not ask the user to switch.
|
|
189
|
-
|
|
190
|
-
If `bootstrap_create_campaign.modelQuality.metadataStale === true`, continue
|
|
191
|
-
normally. Do not ask the user to switch models; this is an accepted Codex host
|
|
192
|
-
metadata mismatch.
|
|
182
|
+
Exception: if `bootstrap_create_campaign.modelQuality.status === "warn"`, the
|
|
183
|
+
first visible campaign message must be the model-quality warning from
|
|
184
|
+
`modelQuality.message`. Only trust that warning when bootstrap received active
|
|
185
|
+
turn/runtime metadata or an explicit user-confirmed model. Do not warn from
|
|
186
|
+
config defaults, stale host labels, or inferred model names.
|
|
193
187
|
|
|
194
188
|
If a linked/local skill file is stale or missing, silently use the installed
|
|
195
189
|
`sellable@sellable` plugin copy. Do not tell the user about the stale link,
|
|
@@ -207,6 +201,55 @@ clear business decisions, tradeoffs, and approval gates. Use product language:
|
|
|
207
201
|
- "I can create a draft shell for you to watch with approval gates before
|
|
208
202
|
sourcing", not mutation jargon
|
|
209
203
|
|
|
204
|
+
## Active Model Metadata
|
|
205
|
+
|
|
206
|
+
Before calling `bootstrap_create_campaign`, collect the active host model data
|
|
207
|
+
only from a source that can describe this turn:
|
|
208
|
+
|
|
209
|
+
- Codex: if `mcp__node_repl__js` is available, inspect only
|
|
210
|
+
`nodeRepl.requestMeta["x-codex-turn-metadata"].model` and
|
|
211
|
+
`nodeRepl.requestMeta["x-codex-turn-metadata"].reasoning_effort`. Pass those
|
|
212
|
+
values as `model` and `reasoningEffort`, with
|
|
213
|
+
`modelMetadataSource: "codex_turn_metadata"`.
|
|
214
|
+
- Codex fallback/cross-check: if active turn metadata is unavailable, you may
|
|
215
|
+
inspect `~/.codex/config.toml` for `model` and `model_reasoning_effort`, but
|
|
216
|
+
pass `modelMetadataSource: "codex_config_fallback"` and do not treat it as a
|
|
217
|
+
reason to ask the user to switch models. Active turn metadata wins if it
|
|
218
|
+
differs from config.
|
|
219
|
+
- Claude Code / Opus: use active Claude runtime metadata only if the current
|
|
220
|
+
host exposes model and effort for this same session, with
|
|
221
|
+
`modelMetadataSource: "claude_runtime_metadata"`. Current Claude Code MCP
|
|
222
|
+
tool calls may not include model or effort metadata; when they do not, omit
|
|
223
|
+
`model`, `reasoningEffort`, and `modelMetadataSource`.
|
|
224
|
+
- Claude Code session-context self-report: if your current Claude session
|
|
225
|
+
context explicitly states both the exact model ID and effort/thinking level,
|
|
226
|
+
report it internally in this shape and pass it to bootstrap with
|
|
227
|
+
`modelMetadataSource: "claude_session_context"`:
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
- Model (name): ...
|
|
231
|
+
- Model (ID): ...
|
|
232
|
+
- Reasoning effort: ...
|
|
233
|
+
- Source: active Claude Code session context
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Use this only when the values are explicitly present in the current session
|
|
237
|
+
context. Do not infer an ID from the friendly name, do not infer effort from
|
|
238
|
+
`alwaysThinkingEnabled`, and do not show this self-report to the user during
|
|
239
|
+
normal campaign setup.
|
|
240
|
+
- Do not run a nested `claude -p`, inspect `~/.claude/settings.json`, or read
|
|
241
|
+
Claude CLI defaults as proof of the user's active Claude Code session. Those
|
|
242
|
+
checks can validate a new child session or saved defaults, but not this
|
|
243
|
+
session's actual model and effort.
|
|
244
|
+
- If the user explicitly provides active Claude `/status` or `/model` output
|
|
245
|
+
that includes both model and effort, pass it with
|
|
246
|
+
`modelMetadataSource: "user_confirmed"`. If it is missing either model or
|
|
247
|
+
effort, treat the metadata as unknown and continue.
|
|
248
|
+
|
|
249
|
+
Never invent the model or reasoning effort. Never pass config defaults as active
|
|
250
|
+
metadata. If bootstrap returns `modelQuality.status === "unknown"`, continue
|
|
251
|
+
without asking the user to switch models.
|
|
252
|
+
|
|
210
253
|
Approval and safety copy should be tasteful. State what the current approval
|
|
211
254
|
covers once, in one short sentence, then move on. Do not append repeated
|
|
212
255
|
"nothing starts / no leads import / no sending" disclaimers to routine progress
|
|
@@ -775,10 +818,9 @@ messages, and wait for final launch approval.
|
|
|
775
818
|
What's your LinkedIn profile URL or handle?
|
|
776
819
|
```
|
|
777
820
|
|
|
778
|
-
|
|
779
|
-
`request_user_input` is unavailable in an interactive
|
|
780
|
-
|
|
781
|
-
In Codex, stop and tell the user:
|
|
821
|
+
Do not silently ask Codex intake or approval questions as plain chat when
|
|
822
|
+
`request_user_input` is unavailable in an interactive session. Stop and tell
|
|
823
|
+
the user:
|
|
782
824
|
|
|
783
825
|
```text
|
|
784
826
|
I need Codex’s quick question panel to collect campaign inputs and approvals cleanly.
|
|
@@ -817,18 +859,17 @@ there.
|
|
|
817
859
|
## Bootstrap
|
|
818
860
|
|
|
819
861
|
MCP tool access is required. First call `mcp__sellable__get_auth_status({})`
|
|
820
|
-
directly. If that tool is unavailable, stop and say this is a
|
|
821
|
-
install/reload problem
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
want an agent-readable checklist, tell them:
|
|
862
|
+
directly. If that tool is unavailable, stop and say this is a Codex
|
|
863
|
+
install/reload problem, not a campaign problem. Tell the user to
|
|
864
|
+
run `curl -fsSL "https://app.sellable.dev/api/v2/cli/install" | sh` so the
|
|
865
|
+
packaged MCP server, Codex Desktop plugin, and Sellable skill bundle are
|
|
866
|
+
installed. If they want an agent-readable checklist, tell them:
|
|
825
867
|
`Install Sellable CLI and skills using https://app.sellable.dev/agent-install.txt`.
|
|
826
868
|
For CLI verification, tell them to run
|
|
827
869
|
`sellable --verify-only --host all --json --artifact "$HOME/.local/sellable/app-sellable-dev/installer/.last-verify.json"`.
|
|
828
|
-
After that, they must fully quit and reopen
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
harness as a fallback for this interactive skill.
|
|
870
|
+
After that, they must fully quit and reopen Codex Desktop before starting a new
|
|
871
|
+
thread. Do not use `scripts/mcp/sellable-tool-call.mjs`, `npm run`, `node`, or
|
|
872
|
+
any local harness as a fallback for this interactive skill.
|
|
832
873
|
Do not mention prompt loading, local skill files, missing linked versions,
|
|
833
874
|
plugin cache paths, MCP namespaces, or runbooks in customer-facing progress
|
|
834
875
|
updates.
|
|
@@ -940,15 +981,19 @@ updates.
|
|
|
940
981
|
- Do not call `mcp__sellable__get_campaigns`.
|
|
941
982
|
- Do not call `mcp__sellable__get_campaign` to hunt for IDs.
|
|
942
983
|
- Do not call `mcp__sellable__create_campaign({ campaignId: ... })` unless the user supplied that id.
|
|
943
|
-
6. Call `mcp__sellable__bootstrap_create_campaign({ flowVersion: "v2", campaignId?, host
|
|
944
|
-
Pass
|
|
945
|
-
|
|
946
|
-
|
|
984
|
+
6. Call `mcp__sellable__bootstrap_create_campaign({ flowVersion: "v2", campaignId?, host?, model?, reasoningEffort?, modelMetadataSource? })`.
|
|
985
|
+
Pass model metadata only when collected by the Active Model Metadata rules
|
|
986
|
+
above. For Codex active turn metadata, pass
|
|
987
|
+
`modelMetadataSource: "codex_turn_metadata"`. For explicit Claude session
|
|
988
|
+
context, pass `modelMetadataSource: "claude_session_context"`. For explicit
|
|
989
|
+
user-confirmed Claude `/status` or `/model` output, pass
|
|
990
|
+
`modelMetadataSource: "user_confirmed"` only when it includes both model and
|
|
991
|
+
effort.
|
|
947
992
|
7. If `safeToProceed !== true`, stop and show `blockingErrors` + `nextStep`.
|
|
948
|
-
8. If `modelQuality.status === "warn"
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
993
|
+
8. If `modelQuality.status === "warn"`, show `modelQuality.message` before any
|
|
994
|
+
setup/research and wait for the user to switch or explicitly continue. If
|
|
995
|
+
`modelQuality.status === "unknown"`, continue; do not tell the user to
|
|
996
|
+
switch models.
|
|
952
997
|
|
|
953
998
|
## Execute Workflow
|
|
954
999
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 2,
|
|
3
3
|
"hosts": {
|
|
4
4
|
"claude": {
|
|
5
5
|
"label": "Claude Code",
|
|
@@ -21,9 +21,8 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"warningCopy": {
|
|
24
|
-
"ok": "
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"belowMinimum": "Best campaigns need at least {minimumSummary}. Current settings look below that: {currentSettings}. Please switch before continuing, or explicitly say to continue anyway."
|
|
24
|
+
"ok": "Active host model metadata meets the configured campaign floor: {currentSettings}.",
|
|
25
|
+
"skipped": "Active host model metadata was not available from a trusted runtime source. Continue without asking the user to switch models.",
|
|
26
|
+
"belowMinimum": "Active host metadata reports {currentSettings}, which is below the configured campaign floor: {minimumSummary}. Ask the user to switch before continuing, or explicitly say to continue anyway."
|
|
28
27
|
}
|
|
29
28
|
}
|
|
@@ -296,7 +296,12 @@ atomic-mint step:
|
|
|
296
296
|
|
|
297
297
|
0. **verify workspace/sender invariant** (see §Preconditions) — if the
|
|
298
298
|
invariant fails, abort BEFORE `bootstrap_create_campaign`
|
|
299
|
-
1. `bootstrap_create_campaign({ flowVersion: "v2", host?, model?, reasoningEffort? })`
|
|
299
|
+
1. `bootstrap_create_campaign({ flowVersion: "v2", host?, model?, reasoningEffort?, modelMetadataSource? })`
|
|
300
|
+
Pass model metadata only when it comes from active turn/runtime metadata or
|
|
301
|
+
explicit user confirmation that includes both model and effort. Use
|
|
302
|
+
`modelMetadataSource: "claude_session_context"` only when the current Claude
|
|
303
|
+
session context explicitly states both values. Never use inferred config
|
|
304
|
+
defaults, nested Claude sessions, or saved settings files.
|
|
300
305
|
2. If `campaign-shell.json` exists, call `create_campaign({ campaignId })` to
|
|
301
306
|
resume/recover `{ campaignId, watchUrl }`, then
|
|
302
307
|
`update_campaign({ campaignId, campaignBrief, leadSourceType,
|