@sellable/mcp 0.1.315 → 0.1.318
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/tools/bootstrap.d.ts +5 -0
- package/dist/tools/bootstrap.js +10 -5
- package/dist/tools/model-quality.d.ts +3 -2
- package/dist/tools/model-quality.js +49 -130
- package/dist/tools/registry.d.ts +4 -0
- package/package.json +1 -1
- 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/README.md
CHANGED
|
@@ -302,11 +302,32 @@ Use the same MCP runtime preflight in both hosts:
|
|
|
302
302
|
|
|
303
303
|
Canonical shortcut:
|
|
304
304
|
|
|
305
|
-
- `bootstrap_create_campaign({ flowVersion: "v2", campaignId?, host?, model?, reasoningEffort? })`
|
|
305
|
+
- `bootstrap_create_campaign({ flowVersion: "v2", campaignId?, host?, model?, reasoningEffort?, modelMetadataSource? })`
|
|
306
|
+
|
|
307
|
+
Pass model/reasoning metadata only when it comes from active turn/runtime
|
|
308
|
+
metadata or explicit user confirmation. For Codex, use
|
|
309
|
+
`nodeRepl.requestMeta["x-codex-turn-metadata"]` and pass
|
|
310
|
+
`modelMetadataSource: "codex_turn_metadata"`. Config files such as
|
|
311
|
+
`~/.codex/config.toml` are fallback/cross-check evidence only and must not
|
|
312
|
+
trigger model-switch warnings.
|
|
313
|
+
|
|
314
|
+
For Claude Code, pass model metadata only when the current host exposes active
|
|
315
|
+
runtime model and effort for the same session, or when the user explicitly
|
|
316
|
+
confirms current `/status` or `/model` output that includes both model and effort.
|
|
317
|
+
If the current Claude session context explicitly states both values, use
|
|
318
|
+
`modelMetadataSource: "claude_session_context"`. Use
|
|
319
|
+
`modelMetadataSource: "user_confirmed"` for explicit user confirmation. Do not
|
|
320
|
+
use a nested `claude -p` run, `~/.claude/settings.json`, or CLI defaults as
|
|
321
|
+
active-session evidence.
|
|
322
|
+
|
|
323
|
+
Claude session-context self-report shape:
|
|
306
324
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
325
|
+
```text
|
|
326
|
+
- Model (name): ...
|
|
327
|
+
- Model (ID): ...
|
|
328
|
+
- Reasoning effort: ...
|
|
329
|
+
- Source: active Claude Code session context
|
|
330
|
+
```
|
|
310
331
|
|
|
311
332
|
If bootstrap returns blocking errors, fail fast and do not continue into provider
|
|
312
333
|
search/import tools.
|
|
@@ -11,6 +11,7 @@ type BootstrapCreateCampaignInput = {
|
|
|
11
11
|
host?: string;
|
|
12
12
|
model?: string;
|
|
13
13
|
reasoningEffort?: string;
|
|
14
|
+
modelMetadataSource?: string;
|
|
14
15
|
};
|
|
15
16
|
type BootstrapCheck = {
|
|
16
17
|
key: "auth" | "update" | "framework" | "model_quality" | "resume_context" | "subskill_catalog" | "create_campaign_subskill";
|
|
@@ -87,6 +88,10 @@ export declare const bootstrapToolDefinitions: {
|
|
|
87
88
|
type: string;
|
|
88
89
|
description: string;
|
|
89
90
|
};
|
|
91
|
+
modelMetadataSource: {
|
|
92
|
+
type: string;
|
|
93
|
+
description: string;
|
|
94
|
+
};
|
|
90
95
|
};
|
|
91
96
|
additionalProperties: boolean;
|
|
92
97
|
};
|
package/dist/tools/bootstrap.js
CHANGED
|
@@ -18,7 +18,7 @@ function toGuidance(check, message) {
|
|
|
18
18
|
return "Fix framework files and rerun bootstrap_create_campaign.";
|
|
19
19
|
}
|
|
20
20
|
if (check === "model_quality") {
|
|
21
|
-
return `Use
|
|
21
|
+
return `Use trusted active-turn metadata before enforcing the campaign model floor: ${getCampaignModelMinimumSummary()}.`;
|
|
22
22
|
}
|
|
23
23
|
if (check === "resume_context") {
|
|
24
24
|
return "Confirm the campaignId exists and is accessible, then retry bootstrap_create_campaign.";
|
|
@@ -98,15 +98,19 @@ export const bootstrapToolDefinitions = [
|
|
|
98
98
|
},
|
|
99
99
|
host: {
|
|
100
100
|
type: "string",
|
|
101
|
-
description: "Optional host label for model-quality preflight, for example Claude Code or Codex.",
|
|
101
|
+
description: "Optional host label for active model-quality preflight, for example Claude Code or Codex.",
|
|
102
102
|
},
|
|
103
103
|
model: {
|
|
104
104
|
type: "string",
|
|
105
|
-
description:
|
|
105
|
+
description: "Optional active host model name from runtime metadata. Do not pass config defaults.",
|
|
106
106
|
},
|
|
107
107
|
reasoningEffort: {
|
|
108
108
|
type: "string",
|
|
109
|
-
description:
|
|
109
|
+
description: "Optional active reasoning setting from runtime metadata. Do not pass config defaults.",
|
|
110
|
+
},
|
|
111
|
+
modelMetadataSource: {
|
|
112
|
+
type: "string",
|
|
113
|
+
description: "Where active model metadata came from, for example codex_turn_metadata, claude_runtime_metadata, claude_session_context, active_turn_metadata, or user_confirmed.",
|
|
110
114
|
},
|
|
111
115
|
},
|
|
112
116
|
additionalProperties: false,
|
|
@@ -129,6 +133,7 @@ export async function bootstrapCreateCampaign(input = {}) {
|
|
|
129
133
|
host: input.host,
|
|
130
134
|
model: input.model,
|
|
131
135
|
reasoningEffort: input.reasoningEffort,
|
|
136
|
+
metadataSource: input.modelMetadataSource,
|
|
132
137
|
});
|
|
133
138
|
const runAuthCheck = async () => {
|
|
134
139
|
let status = await getAuthStatus();
|
|
@@ -188,7 +193,7 @@ export async function bootstrapCreateCampaign(input = {}) {
|
|
|
188
193
|
}
|
|
189
194
|
requiredChecks.push({
|
|
190
195
|
key: "model_quality",
|
|
191
|
-
ok: modelQuality.status
|
|
196
|
+
ok: modelQuality.status !== "warn",
|
|
192
197
|
blocking: false,
|
|
193
198
|
detail: modelQuality.message,
|
|
194
199
|
});
|
|
@@ -3,6 +3,7 @@ export type CampaignModelQualityInput = {
|
|
|
3
3
|
host?: string | null;
|
|
4
4
|
model?: string | null;
|
|
5
5
|
reasoningEffort?: string | null;
|
|
6
|
+
metadataSource?: string | null;
|
|
6
7
|
};
|
|
7
8
|
export type CampaignModelQualityStatus = "ok" | "warn" | "unknown";
|
|
8
9
|
export type CampaignModelQualityResult = {
|
|
@@ -10,6 +11,7 @@ export type CampaignModelQualityResult = {
|
|
|
10
11
|
host: CampaignModelHost;
|
|
11
12
|
model: string | null;
|
|
12
13
|
reasoningEffort: string | null;
|
|
14
|
+
metadataSource: string | null;
|
|
13
15
|
recommendedModel: string;
|
|
14
16
|
recommendedReasoningEffort: string;
|
|
15
17
|
minimumSummary: string;
|
|
@@ -34,8 +36,7 @@ export type CampaignModelQualityConfig = {
|
|
|
34
36
|
};
|
|
35
37
|
warningCopy: {
|
|
36
38
|
ok: string;
|
|
37
|
-
|
|
38
|
-
unknownSettings: string;
|
|
39
|
+
skipped: string;
|
|
39
40
|
belowMinimum: string;
|
|
40
41
|
};
|
|
41
42
|
};
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
import { resolveSkillsDir } from "../skills.js";
|
|
4
1
|
const DEFAULT_MODEL_QUALITY_CONFIG = {
|
|
5
|
-
version:
|
|
2
|
+
version: 2,
|
|
6
3
|
hosts: {
|
|
7
4
|
claude: {
|
|
8
5
|
label: "Claude Code",
|
|
@@ -36,20 +33,24 @@ const DEFAULT_MODEL_QUALITY_CONFIG = {
|
|
|
36
33
|
},
|
|
37
34
|
},
|
|
38
35
|
warningCopy: {
|
|
39
|
-
ok: "
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
belowMinimum: "Best campaigns need at least {minimumSummary}. Current settings look below that: {currentSettings}. Please switch before continuing, or explicitly say to continue anyway.",
|
|
36
|
+
ok: "Active host model metadata meets the configured campaign floor: {currentSettings}.",
|
|
37
|
+
skipped: "Active host model metadata was not available from a trusted runtime source. Continue without asking the user to switch models.",
|
|
38
|
+
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.",
|
|
43
39
|
},
|
|
44
40
|
};
|
|
45
|
-
|
|
41
|
+
const TRUSTED_METADATA_SOURCE_KEYWORDS = [
|
|
42
|
+
"codex_turn_metadata",
|
|
43
|
+
"claude_runtime_metadata",
|
|
44
|
+
"claude_session_context",
|
|
45
|
+
"active_turn_metadata",
|
|
46
|
+
"user_confirmed",
|
|
47
|
+
];
|
|
46
48
|
const normalize = (value) => String(value ?? "")
|
|
47
49
|
.trim()
|
|
48
50
|
.toLowerCase();
|
|
49
51
|
const normalizeHost = (host) => {
|
|
50
52
|
const normalized = normalize(host);
|
|
51
53
|
if (normalized.includes("claude") ||
|
|
52
|
-
normalized.includes("clod") ||
|
|
53
54
|
normalized.includes("opus") ||
|
|
54
55
|
normalized.includes("sonnet") ||
|
|
55
56
|
normalized.includes("haiku")) {
|
|
@@ -62,12 +63,6 @@ const normalizeHost = (host) => {
|
|
|
62
63
|
}
|
|
63
64
|
return "unknown";
|
|
64
65
|
};
|
|
65
|
-
const normalizeHostFromInput = (host, model) => {
|
|
66
|
-
const explicitHost = normalizeHost(host);
|
|
67
|
-
if (explicitHost !== "unknown")
|
|
68
|
-
return explicitHost;
|
|
69
|
-
return normalizeHost(model);
|
|
70
|
-
};
|
|
71
66
|
const normalizeReasoning = (reasoning) => normalize(reasoning).replace(/[_\s-]+/g, "");
|
|
72
67
|
const compareVersion = (candidate, minimum) => {
|
|
73
68
|
const candidateParts = candidate.split(".").map((part) => Number(part));
|
|
@@ -83,50 +78,12 @@ const compareVersion = (candidate, minimum) => {
|
|
|
83
78
|
}
|
|
84
79
|
return 0;
|
|
85
80
|
};
|
|
86
|
-
const extractModelVersions = (model) =>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return false;
|
|
91
|
-
return Object.values(config.hosts)
|
|
92
|
-
.filter((hostConfig) => hostConfig !== currentHostConfig)
|
|
93
|
-
.some((hostConfig) => hostConfig.familyKeywords.some((keyword) => normalizedModel.includes(normalize(keyword))));
|
|
94
|
-
}
|
|
95
|
-
function mergeConfig(rawConfig) {
|
|
96
|
-
return {
|
|
97
|
-
...DEFAULT_MODEL_QUALITY_CONFIG,
|
|
98
|
-
...rawConfig,
|
|
99
|
-
hosts: {
|
|
100
|
-
claude: {
|
|
101
|
-
...DEFAULT_MODEL_QUALITY_CONFIG.hosts.claude,
|
|
102
|
-
...(rawConfig.hosts?.claude || {}),
|
|
103
|
-
},
|
|
104
|
-
codex: {
|
|
105
|
-
...DEFAULT_MODEL_QUALITY_CONFIG.hosts.codex,
|
|
106
|
-
...(rawConfig.hosts?.codex || {}),
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
warningCopy: {
|
|
110
|
-
...DEFAULT_MODEL_QUALITY_CONFIG.warningCopy,
|
|
111
|
-
...(rawConfig.warningCopy || {}),
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
}
|
|
81
|
+
const extractModelVersions = (model) => {
|
|
82
|
+
const normalized = normalize(model).replace(/(?<=\d)[_-](?=\d)/g, ".");
|
|
83
|
+
return Array.from(normalized.matchAll(/\b(\d+(?:\.\d+){0,2})\b/g)).map((match) => match[1]);
|
|
84
|
+
};
|
|
115
85
|
export function getCampaignModelQualityConfig() {
|
|
116
|
-
|
|
117
|
-
return cachedConfig;
|
|
118
|
-
const configPath = join(resolveSkillsDir(), "create-campaign", "core", "model-quality.json");
|
|
119
|
-
if (!existsSync(configPath)) {
|
|
120
|
-
cachedConfig = DEFAULT_MODEL_QUALITY_CONFIG;
|
|
121
|
-
return cachedConfig;
|
|
122
|
-
}
|
|
123
|
-
try {
|
|
124
|
-
cachedConfig = mergeConfig(JSON.parse(readFileSync(configPath, "utf-8")));
|
|
125
|
-
}
|
|
126
|
-
catch {
|
|
127
|
-
cachedConfig = DEFAULT_MODEL_QUALITY_CONFIG;
|
|
128
|
-
}
|
|
129
|
-
return cachedConfig;
|
|
86
|
+
return DEFAULT_MODEL_QUALITY_CONFIG;
|
|
130
87
|
}
|
|
131
88
|
export function getCampaignModelMinimumSummary(config = getCampaignModelQualityConfig()) {
|
|
132
89
|
return `${config.hosts.claude.minimumModel} with ${config.hosts.claude.minimumReasoningEffort} reasoning or ${config.hosts.codex.minimumModel} with ${config.hosts.codex.minimumReasoningEffort} reasoning`;
|
|
@@ -148,12 +105,12 @@ function modelMeetsMinimum(model, hostConfig, options = {}) {
|
|
|
148
105
|
hostConfig.familyKeywords.every((keyword) => normalizedModel.includes(normalize(keyword)));
|
|
149
106
|
if (!familyMatches)
|
|
150
107
|
return false;
|
|
151
|
-
const versions = extractModelVersions(
|
|
108
|
+
const versions = extractModelVersions(model);
|
|
152
109
|
if (!hostConfig.minimumVersion)
|
|
153
110
|
return true;
|
|
154
111
|
return versions.some((version) => compareVersion(version, hostConfig.minimumVersion) >= 0);
|
|
155
112
|
}
|
|
156
|
-
function
|
|
113
|
+
function findHostConfig(host, model, config) {
|
|
157
114
|
const candidates = host === "unknown"
|
|
158
115
|
? [
|
|
159
116
|
["claude", config.hosts.claude],
|
|
@@ -161,104 +118,66 @@ function findAcceptedHostConfig(host, model, config) {
|
|
|
161
118
|
]
|
|
162
119
|
: [[host, config.hosts[host]]];
|
|
163
120
|
return candidates.find(([, hostConfig]) => modelMeetsMinimum(model, hostConfig, {
|
|
164
|
-
familyKnownFromHost: host !== "unknown"
|
|
165
|
-
!modelContainsOtherHostFamily(model, hostConfig, config),
|
|
121
|
+
familyKnownFromHost: host !== "unknown",
|
|
166
122
|
}));
|
|
167
123
|
}
|
|
168
|
-
function
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (!model || !reasoningEffort)
|
|
172
|
-
return false;
|
|
173
|
-
const normalizedModel = normalize(model).replace(/[_-]+/g, " ");
|
|
174
|
-
const normalizedReasoning = normalizeReasoning(reasoningEffort);
|
|
175
|
-
if (!normalizedModel.includes("gpt"))
|
|
176
|
-
return false;
|
|
177
|
-
const versions = extractModelVersions(normalizedModel);
|
|
178
|
-
const looksLikeBaseGpt5 = versions.some((version) => version === "5" || version === "5.0");
|
|
179
|
-
if (!looksLikeBaseGpt5)
|
|
180
|
-
return false;
|
|
181
|
-
return (["default", "auto", "standard"].includes(normalizedReasoning) ||
|
|
182
|
-
acceptsReasoning(reasoningEffort, config.hosts.codex));
|
|
124
|
+
function isTrustedMetadataSource(source) {
|
|
125
|
+
const normalized = normalize(source).replace(/[\s-]+/g, "_");
|
|
126
|
+
return TRUSTED_METADATA_SOURCE_KEYWORDS.some((keyword) => normalized.includes(keyword));
|
|
183
127
|
}
|
|
184
128
|
export function evaluateCampaignModelQuality(input = {}) {
|
|
185
129
|
const config = getCampaignModelQualityConfig();
|
|
186
|
-
const host =
|
|
130
|
+
const host = normalizeHost([input.host, input.model].filter(Boolean).join(" "));
|
|
187
131
|
const model = input.model?.trim() || null;
|
|
188
132
|
const reasoningEffort = input.reasoningEffort?.trim() || null;
|
|
133
|
+
const metadataSource = input.metadataSource?.trim() || null;
|
|
189
134
|
const recommendationHost = host === "claude" ? "claude" : "codex";
|
|
190
135
|
const recommendedHostConfig = config.hosts[recommendationHost];
|
|
191
136
|
const minimumSummary = getCampaignModelMinimumSummary(config);
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
137
|
+
const currentSettings = [
|
|
138
|
+
model ? `model "${model}"` : "unknown model",
|
|
139
|
+
reasoningEffort ? `reasoning "${reasoningEffort}"` : "unknown reasoning",
|
|
140
|
+
metadataSource ? `source "${metadataSource}"` : "unknown source",
|
|
141
|
+
].join(", ");
|
|
142
|
+
const base = {
|
|
143
|
+
host,
|
|
144
|
+
model,
|
|
145
|
+
reasoningEffort,
|
|
146
|
+
metadataSource,
|
|
147
|
+
recommendedModel: recommendedHostConfig.minimumModel,
|
|
148
|
+
recommendedReasoningEffort: recommendedHostConfig.recommendedReasoningEffort,
|
|
149
|
+
minimumSummary,
|
|
150
|
+
metadataStale: false,
|
|
151
|
+
};
|
|
152
|
+
if (!isTrustedMetadataSource(metadataSource) || !model || !reasoningEffort) {
|
|
195
153
|
return {
|
|
154
|
+
...base,
|
|
196
155
|
status: "unknown",
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
reasoningEffort,
|
|
200
|
-
recommendedModel,
|
|
201
|
-
recommendedReasoningEffort,
|
|
202
|
-
minimumSummary,
|
|
203
|
-
confirmationRequired: true,
|
|
204
|
-
metadataStale: false,
|
|
205
|
-
message: formatCopy(config.warningCopy.unknownSettings, {
|
|
206
|
-
minimumSummary,
|
|
207
|
-
currentSettings: "unknown",
|
|
208
|
-
}),
|
|
156
|
+
confirmationRequired: false,
|
|
157
|
+
message: config.warningCopy.skipped,
|
|
209
158
|
};
|
|
210
159
|
}
|
|
211
|
-
const acceptedHostConfig =
|
|
160
|
+
const acceptedHostConfig = findHostConfig(host, model, config);
|
|
212
161
|
const ok = Boolean(acceptedHostConfig && acceptsReasoning(reasoningEffort, acceptedHostConfig[1]));
|
|
213
162
|
if (ok) {
|
|
214
163
|
return {
|
|
164
|
+
...base,
|
|
215
165
|
status: "ok",
|
|
216
166
|
host: acceptedHostConfig?.[0] ?? host,
|
|
217
|
-
|
|
218
|
-
reasoningEffort,
|
|
219
|
-
recommendedModel: acceptedHostConfig?.[1].minimumModel ?? recommendedModel,
|
|
167
|
+
recommendedModel: acceptedHostConfig?.[1].minimumModel ?? base.recommendedModel,
|
|
220
168
|
recommendedReasoningEffort: acceptedHostConfig?.[1].recommendedReasoningEffort ??
|
|
221
|
-
recommendedReasoningEffort,
|
|
222
|
-
minimumSummary,
|
|
169
|
+
base.recommendedReasoningEffort,
|
|
223
170
|
confirmationRequired: false,
|
|
224
|
-
metadataStale: false,
|
|
225
171
|
message: formatCopy(config.warningCopy.ok, {
|
|
226
172
|
minimumSummary,
|
|
227
|
-
currentSettings
|
|
173
|
+
currentSettings,
|
|
228
174
|
}),
|
|
229
175
|
};
|
|
230
176
|
}
|
|
231
|
-
if (looksLikeCodexStaleMetadata(host, model, reasoningEffort, config)) {
|
|
232
|
-
return {
|
|
233
|
-
status: "ok",
|
|
234
|
-
host,
|
|
235
|
-
model,
|
|
236
|
-
reasoningEffort,
|
|
237
|
-
recommendedModel,
|
|
238
|
-
recommendedReasoningEffort,
|
|
239
|
-
minimumSummary,
|
|
240
|
-
confirmationRequired: false,
|
|
241
|
-
metadataStale: true,
|
|
242
|
-
message: formatCopy(config.warningCopy.staleCodexMetadata, {
|
|
243
|
-
minimumSummary,
|
|
244
|
-
currentSettings: "stale Codex host metadata",
|
|
245
|
-
}),
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
const currentSettings = [
|
|
249
|
-
model ? `model "${model}"` : "unknown model",
|
|
250
|
-
reasoningEffort ? `reasoning "${reasoningEffort}"` : "unknown reasoning",
|
|
251
|
-
].join(", ");
|
|
252
177
|
return {
|
|
178
|
+
...base,
|
|
253
179
|
status: "warn",
|
|
254
|
-
host,
|
|
255
|
-
model,
|
|
256
|
-
reasoningEffort,
|
|
257
|
-
recommendedModel,
|
|
258
|
-
recommendedReasoningEffort,
|
|
259
|
-
minimumSummary,
|
|
260
180
|
confirmationRequired: true,
|
|
261
|
-
metadataStale: false,
|
|
262
181
|
message: formatCopy(config.warningCopy.belowMinimum, {
|
|
263
182
|
minimumSummary,
|
|
264
183
|
currentSettings,
|
package/dist/tools/registry.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -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,
|