@sellable/mcp 0.1.527 → 0.1.529
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/tools/auth.js
CHANGED
|
@@ -124,7 +124,7 @@ export async function getAuthStatus() {
|
|
|
124
124
|
" 3. Click it, come back here, and we'll keep going\\n\\n" +
|
|
125
125
|
"What email should I use?` " +
|
|
126
126
|
"2) Wait for the user to type their email in normal chat (do NOT use AskUserQuestion / request_user_input). " +
|
|
127
|
-
"3) Call `mcp__sellable__start_cli_login({ email })` with that email. " +
|
|
127
|
+
"3) Call `mcp__sellable__start_cli_login({ email })` with that email in Claude Code/Codex, or `mcp_sellable_start_cli_login({ email })` in Hermes. " +
|
|
128
128
|
"4) On `ok: true`, say verbatim (substituting the email exactly as typed):\\n" +
|
|
129
129
|
"`Magic link sent to {email}.\\n\\n" +
|
|
130
130
|
"─────────────────────────────────────────────\\n" +
|
|
@@ -135,8 +135,8 @@ export async function getAuthStatus() {
|
|
|
135
135
|
" 3. Come back here when you're done\\n\\n" +
|
|
136
136
|
"I'll be waiting right here.\\n\\n" +
|
|
137
137
|
" (If your team already uses Sellable, ask an admin to invite you into their shared workspace instead — that gets you straight in.)` " +
|
|
138
|
-
"5) Call `mcp__sellable__wait_for_cli_login({ sessionId })` using the sessionId returned by start_cli_login. " +
|
|
139
|
-
"6) If the result is `error.type === 'tool_timeout_guard'`, IMMEDIATELY re-call wait_for_cli_login with the SAME sessionId — do not narrate, do not call start_cli_login again. Loop until you get a different result. " +
|
|
138
|
+
"5) Call `mcp__sellable__wait_for_cli_login({ sessionId })` in Claude Code/Codex, or `mcp_sellable_wait_for_cli_login({ sessionId })` in Hermes, using the sessionId returned by start_cli_login. " +
|
|
139
|
+
"6) If the result is `error.type === 'tool_timeout_guard'`, IMMEDIATELY re-call wait_for_cli_login with the SAME sessionId — do not narrate, do not call start_cli_login again. In Hermes, re-call `mcp_sellable_wait_for_cli_login({ sessionId })`. Loop until you get a different result. " +
|
|
140
140
|
"7) On `ok: true`, the user is signed in and `~/.sellable/config.json` has been written. Branch on `isReturningUser` and use `activeWorkspaceName` when present, otherwise `activeWorkspaceId`, as `{workspaceLabel}`: " +
|
|
141
141
|
"if true, say `You're in {workspaceLabel}.\\n\\nExcited to help you launch your LinkedIn outbound campaign. We're at setup: first I'll use your LinkedIn profile to understand the company, then I'll draft the campaign brief, help choose where to find buyers, review messages, and wait for final launch approval.\\n\\nWhat's your LinkedIn profile URL or handle?`; " +
|
|
142
142
|
"if false, say `You're set up in {workspaceLabel}.\\n\\nExcited to help you launch your LinkedIn outbound campaign. We're at setup: first I'll use your LinkedIn profile to understand the company, then I'll draft the campaign brief, help choose where to find buyers, review messages, and wait for final launch approval.\\n\\nWhat's your LinkedIn profile URL or handle?`";
|
package/dist/tools/bootstrap.js
CHANGED
|
@@ -307,7 +307,7 @@ export async function bootstrapCreateCampaign(input = {}) {
|
|
|
307
307
|
? resumeDetected
|
|
308
308
|
? `Bootstrap complete.${workspaceNotice}${modelNotice} Resume from campaign state and navigation diagnostics first; treat local draft artifacts as debug-only evidence. 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.`
|
|
309
309
|
: flowVersion === "v2"
|
|
310
|
-
? `Bootstrap complete.${workspaceNotice}${modelNotice} Load the compact create-campaign-v2 entry prompt once with get_subskill_prompt({ subskillName: "create-campaign-v2" });
|
|
310
|
+
? `Bootstrap complete.${workspaceNotice}${modelNotice} Load the compact create-campaign-v2 entry prompt once with get_subskill_prompt({ subskillName: "create-campaign-v2" }); Hermes users should start this flow with /sellable-create-campaign. Load flow/reference assets lazily only when that stage needs them. Preserve the pre-intake sequence: confirm auth/workspace status, ask only for the LinkedIn profile URL or handle, normalize handles to a full profile URL, require that profile identity before continuing, run lightweight profile/company lookup, then ask the target, offer, credibility, and prospect-source setup questions. Do not call list_senders or sender discovery during setup; sender availability belongs only to Settings after message approval. Then write the campaign brief, call create_campaign once to mint the watchable shell, surface the returned watch link once before brief approval, and hand off to lead finding without repeating the link.`
|
|
311
311
|
: `Bootstrap complete.${workspaceNotice}${modelNotice} 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.`
|
|
312
312
|
: "Bootstrap incomplete. Resolve blockingErrors and rerun bootstrap_create_campaign before provider/search/import tools.";
|
|
313
313
|
// Strip prompt body from createCampaignSubskill — it's loaded via the host
|
|
@@ -9,9 +9,9 @@ declare const LEAD_SOURCE_PROVIDERS: {
|
|
|
9
9
|
};
|
|
10
10
|
type LeadSourceProvider = (typeof LEAD_SOURCE_PROVIDERS)[keyof typeof LEAD_SOURCE_PROVIDERS];
|
|
11
11
|
export declare function buildWatchUrl(config: Pick<ReturnType<typeof getConfig>, "apiUrl" | "token" | "activeWorkspaceId" | "workspaceId">, path: string): string;
|
|
12
|
-
export type CampaignBuilderWatchMode = "claude" | "codex";
|
|
12
|
+
export type CampaignBuilderWatchMode = "claude" | "codex" | "hermes";
|
|
13
13
|
export declare function getCampaignBuilderWatchModeParam(): CampaignBuilderWatchMode;
|
|
14
|
-
export declare function getCampaignBuilderWatchModeDriverLabel(mode?: CampaignBuilderWatchMode): "Claude Code" | "Codex";
|
|
14
|
+
export declare function getCampaignBuilderWatchModeDriverLabel(mode?: CampaignBuilderWatchMode): "Claude Code" | "Codex" | "Hermes";
|
|
15
15
|
export declare function buildCampaignWatchHandoffMarkdown(watchUrl: string, mode?: CampaignBuilderWatchMode): string;
|
|
16
16
|
export interface Campaign {
|
|
17
17
|
id: string;
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -124,23 +124,35 @@ export function buildWatchUrl(config, path) {
|
|
|
124
124
|
}
|
|
125
125
|
return url.toString();
|
|
126
126
|
}
|
|
127
|
+
const CAMPAIGN_BUILDER_AGENT_WATCH_MODES = new Set([
|
|
128
|
+
"claude",
|
|
129
|
+
"codex",
|
|
130
|
+
"hermes",
|
|
131
|
+
]);
|
|
127
132
|
export function getCampaignBuilderWatchModeParam() {
|
|
128
133
|
const explicit = process.env.SELLABLE_WATCH_MODE_DRIVER?.trim().toLowerCase();
|
|
129
|
-
if (explicit
|
|
134
|
+
if (CAMPAIGN_BUILDER_AGENT_WATCH_MODES.has(explicit)) {
|
|
130
135
|
return explicit;
|
|
136
|
+
}
|
|
131
137
|
return process.env.CODEX_HOME ? "codex" : "claude";
|
|
132
138
|
}
|
|
133
139
|
function getCampaignBuilderWatchModeFromUrl(watchUrl) {
|
|
134
140
|
try {
|
|
135
141
|
const mode = new URL(watchUrl).searchParams.get("mode");
|
|
136
|
-
return mode
|
|
142
|
+
return CAMPAIGN_BUILDER_AGENT_WATCH_MODES.has(mode)
|
|
143
|
+
? mode
|
|
144
|
+
: null;
|
|
137
145
|
}
|
|
138
146
|
catch {
|
|
139
147
|
return null;
|
|
140
148
|
}
|
|
141
149
|
}
|
|
142
150
|
export function getCampaignBuilderWatchModeDriverLabel(mode = getCampaignBuilderWatchModeParam()) {
|
|
143
|
-
|
|
151
|
+
if (mode === "codex")
|
|
152
|
+
return "Codex";
|
|
153
|
+
if (mode === "hermes")
|
|
154
|
+
return "Hermes";
|
|
155
|
+
return "Claude Code";
|
|
144
156
|
}
|
|
145
157
|
export function buildCampaignWatchHandoffMarkdown(watchUrl, mode = getCampaignBuilderWatchModeFromUrl(watchUrl) ?? getCampaignBuilderWatchModeParam()) {
|
|
146
158
|
const driverLabel = getCampaignBuilderWatchModeDriverLabel(mode);
|
|
@@ -161,7 +173,7 @@ function isValidBriefHandoffWatchUrl(watchUrl, campaignId) {
|
|
|
161
173
|
const url = new URL(watchUrl);
|
|
162
174
|
const mode = url.searchParams.get("mode");
|
|
163
175
|
return (url.pathname === `/campaign-builder/${campaignId}` &&
|
|
164
|
-
(mode
|
|
176
|
+
CAMPAIGN_BUILDER_AGENT_WATCH_MODES.has(mode) &&
|
|
165
177
|
Boolean(url.searchParams.get("workspaceId")) &&
|
|
166
178
|
Boolean(url.searchParams.get("token")));
|
|
167
179
|
}
|
|
@@ -173,7 +185,7 @@ function assertBriefHandoffWatchUrl(watchUrl, campaignId) {
|
|
|
173
185
|
if (isValidBriefHandoffWatchUrl(watchUrl, campaignId))
|
|
174
186
|
return;
|
|
175
187
|
throw new Error("create_campaign produced an invalid watchUrl for the brief approval handoff. " +
|
|
176
|
-
"Recover a fresh direct /campaign-builder/{campaignId}?mode={claude|codex}&workspaceId=...&token=... URL " +
|
|
188
|
+
"Recover a fresh direct /campaign-builder/{campaignId}?mode={claude|codex|hermes}&workspaceId=...&token=... URL " +
|
|
177
189
|
"with create_campaign({ campaignId }) or get_campaign before asking for approval.");
|
|
178
190
|
}
|
|
179
191
|
export const campaignToolDefinitions = [
|
package/dist/tools/leads.js
CHANGED
|
@@ -1316,6 +1316,14 @@ function normalizeImportProvider(provider) {
|
|
|
1316
1316
|
return "signal-discovery";
|
|
1317
1317
|
return undefined;
|
|
1318
1318
|
}
|
|
1319
|
+
function inferImportProviderFromLeadListConfig(config) {
|
|
1320
|
+
const provider = normalizeImportProvider(config?.importProvider);
|
|
1321
|
+
if (provider)
|
|
1322
|
+
return provider;
|
|
1323
|
+
if (config?.type === "signal_lead_list")
|
|
1324
|
+
return "signal-discovery";
|
|
1325
|
+
return undefined;
|
|
1326
|
+
}
|
|
1319
1327
|
function assertPhase126ImportProvider(provider, campaignOfferId) {
|
|
1320
1328
|
if (!provider) {
|
|
1321
1329
|
throw new Error(`import_leads requires a supported leadSourceProvider for campaign ${campaignOfferId}. Provide provider or set campaign.leadSourceProvider to sales-nav, prospeo, or signal-discovery, then call get_provider_prompt({ provider, campaignOfferId }).`);
|
|
@@ -4202,7 +4210,8 @@ export async function confirmLeadList(input) {
|
|
|
4202
4210
|
});
|
|
4203
4211
|
let resolvedLeadListId = sourceLeadListId;
|
|
4204
4212
|
let resolvedProvider;
|
|
4205
|
-
|
|
4213
|
+
const isSameSourceRefillCopy = currentStep === null && Array.isArray(sourceRowIds) && sourceRowIds.length > 0;
|
|
4214
|
+
if (!resolvedLeadListId || currentStep === undefined || !isSameSourceRefillCopy) {
|
|
4206
4215
|
const campaign = requestOptions
|
|
4207
4216
|
? await api.get(`/api/v2/campaign-offers/${campaignOfferId}`, requestOptions)
|
|
4208
4217
|
: await api.get(`/api/v2/campaign-offers/${campaignOfferId}`);
|
|
@@ -4258,7 +4267,7 @@ export async function confirmLeadList(input) {
|
|
|
4258
4267
|
: await api.get(`/api/v3/workflow-tables/${resolvedLeadListId}?mode=meta`);
|
|
4259
4268
|
const leadListConfig = leadListMeta.table?.config ?? null;
|
|
4260
4269
|
if (!resolvedProvider) {
|
|
4261
|
-
resolvedProvider =
|
|
4270
|
+
resolvedProvider = inferImportProviderFromLeadListConfig(leadListConfig);
|
|
4262
4271
|
}
|
|
4263
4272
|
const leadListRowCount = leadListMeta.rowCount ?? 0;
|
|
4264
4273
|
const importProgress = leadListConfig?.importProgress ?? null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type CampaignModelHost = "claude" | "codex" | "unknown";
|
|
1
|
+
export type CampaignModelHost = "claude" | "codex" | "hermes" | "unknown";
|
|
2
2
|
export type CampaignModelQualityInput = {
|
|
3
3
|
host?: string | null;
|
|
4
4
|
model?: string | null;
|
|
@@ -33,6 +33,7 @@ export type CampaignModelQualityConfig = {
|
|
|
33
33
|
hosts: {
|
|
34
34
|
claude: CampaignModelQualityHostConfig;
|
|
35
35
|
codex: CampaignModelQualityHostConfig;
|
|
36
|
+
hermes: CampaignModelQualityHostConfig;
|
|
36
37
|
};
|
|
37
38
|
warningCopy: {
|
|
38
39
|
ok: string;
|
|
@@ -30,6 +30,20 @@ const DEFAULT_MODEL_QUALITY_CONFIG = {
|
|
|
30
30
|
],
|
|
31
31
|
recommendedReasoningEffort: "xhigh",
|
|
32
32
|
},
|
|
33
|
+
hermes: {
|
|
34
|
+
label: "Hermes",
|
|
35
|
+
minimumModel: "GPT 5.5",
|
|
36
|
+
familyKeywords: ["gpt"],
|
|
37
|
+
minimumVersion: "5.5",
|
|
38
|
+
minimumReasoningEffort: "xhigh",
|
|
39
|
+
acceptedReasoningEfforts: [
|
|
40
|
+
"extra high",
|
|
41
|
+
"extra-high",
|
|
42
|
+
"xhigh",
|
|
43
|
+
"extra_high",
|
|
44
|
+
],
|
|
45
|
+
recommendedReasoningEffort: "xhigh",
|
|
46
|
+
},
|
|
33
47
|
},
|
|
34
48
|
warningCopy: {
|
|
35
49
|
ok: "Active host model metadata meets the configured campaign floor: {currentSettings}.",
|
|
@@ -41,6 +55,8 @@ const TRUSTED_METADATA_SOURCE_KEYWORDS = [
|
|
|
41
55
|
"codex_turn_metadata",
|
|
42
56
|
"claude_runtime_metadata",
|
|
43
57
|
"claude_session_context",
|
|
58
|
+
"hermes_runtime_metadata",
|
|
59
|
+
"hermes_session_context",
|
|
44
60
|
"active_turn_metadata",
|
|
45
61
|
"user_confirmed",
|
|
46
62
|
];
|
|
@@ -49,6 +65,9 @@ const normalize = (value) => String(value ?? "")
|
|
|
49
65
|
.toLowerCase();
|
|
50
66
|
const normalizeHost = (host) => {
|
|
51
67
|
const normalized = normalize(host);
|
|
68
|
+
if (normalized.includes("hermes")) {
|
|
69
|
+
return "hermes";
|
|
70
|
+
}
|
|
52
71
|
if (normalized.includes("claude") ||
|
|
53
72
|
normalized.includes("opus") ||
|
|
54
73
|
normalized.includes("sonnet") ||
|
|
@@ -114,6 +133,7 @@ function findHostConfig(host, model, config) {
|
|
|
114
133
|
? [
|
|
115
134
|
["claude", config.hosts.claude],
|
|
116
135
|
["codex", config.hosts.codex],
|
|
136
|
+
["hermes", config.hosts.hermes],
|
|
117
137
|
]
|
|
118
138
|
: [[host, config.hosts[host]]];
|
|
119
139
|
return candidates.find(([, hostConfig]) => modelMeetsMinimum(model, hostConfig, {
|
|
@@ -130,7 +150,7 @@ export function evaluateCampaignModelQuality(input = {}) {
|
|
|
130
150
|
const model = input.model?.trim() || null;
|
|
131
151
|
const reasoningEffort = input.reasoningEffort?.trim() || null;
|
|
132
152
|
const metadataSource = input.metadataSource?.trim() || null;
|
|
133
|
-
const recommendationHost = host === "claude"
|
|
153
|
+
const recommendationHost = host === "claude" || host === "hermes" ? host : "codex";
|
|
134
154
|
const recommendedHostConfig = config.hosts[recommendationHost];
|
|
135
155
|
const minimumSummary = getCampaignModelMinimumSummary(config);
|
|
136
156
|
const currentSettings = [
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sellable/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.529",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Sellable MCP server for Claude Code and
|
|
5
|
+
"description": "Sellable MCP server for Claude Code, Codex, and Hermes campaign workflows",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"mcp": "dist/index.js",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"mcp",
|
|
21
21
|
"claude-code",
|
|
22
22
|
"codex",
|
|
23
|
+
"hermes",
|
|
23
24
|
"sellable",
|
|
24
25
|
"linkedin",
|
|
25
26
|
"outreach"
|