@sellable/mcp 0.1.309 → 0.1.310
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 +1 -5
- package/agents/post-find-leads-message-scout.md +5 -0
- package/dist/generated/column-schema-manifest.js +1 -1
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/tools/bootstrap.js +5 -7
- package/dist/tools/campaigns.d.ts +169 -1
- package/dist/tools/campaigns.js +95 -62
- package/dist/tools/model-quality.d.ts +0 -24
- package/dist/tools/model-quality.js +25 -165
- package/dist/tools/prompts.js +5 -4
- package/dist/tools/registry.d.ts +168 -0
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +8 -16
- package/skills/create-campaign-v2/SKILL.md +1 -1
- package/skills/create-campaign-v2/core/flow.v2.json +1 -1
- package/skills/create-campaign-v2/references/approval-gate-framing.md +1 -1
- package/skills/create-campaign-v2/references/watch-guide-narration.md +20 -0
- package/skills/create-campaign-v2-tail/SKILL.md +18 -32
- package/skills/generate-messages/SKILL.md +4 -9
- package/skills/create-campaign/core/model-quality.json +0 -28
|
@@ -11,7 +11,7 @@ type LeadSourceProvider = (typeof LEAD_SOURCE_PROVIDERS)[keyof typeof LEAD_SOURC
|
|
|
11
11
|
export declare function buildWatchUrl(config: Pick<ReturnType<typeof getConfig>, "apiUrl" | "token" | "activeWorkspaceId" | "workspaceId">, path: string): string;
|
|
12
12
|
export type CampaignBuilderWatchMode = "claude" | "codex";
|
|
13
13
|
export declare function getCampaignBuilderWatchModeParam(): CampaignBuilderWatchMode;
|
|
14
|
-
export declare function getCampaignBuilderWatchModeDriverLabel(mode?: CampaignBuilderWatchMode): "
|
|
14
|
+
export declare function getCampaignBuilderWatchModeDriverLabel(mode?: CampaignBuilderWatchMode): "Codex" | "Claude Code";
|
|
15
15
|
export declare function buildCampaignWatchHandoffMarkdown(watchUrl: string, mode?: CampaignBuilderWatchMode): string;
|
|
16
16
|
export interface Campaign {
|
|
17
17
|
id: string;
|
|
@@ -416,6 +416,90 @@ export declare const campaignToolDefinitions: ({
|
|
|
416
416
|
};
|
|
417
417
|
workerStatuses: {
|
|
418
418
|
type: string;
|
|
419
|
+
description: string;
|
|
420
|
+
properties: {
|
|
421
|
+
leadFitBuilder: {
|
|
422
|
+
type: string[];
|
|
423
|
+
enum: (string | null)[];
|
|
424
|
+
};
|
|
425
|
+
messageDraftBuilder: {
|
|
426
|
+
type: string[];
|
|
427
|
+
enum: (string | null)[];
|
|
428
|
+
};
|
|
429
|
+
};
|
|
430
|
+
additionalProperties: boolean;
|
|
431
|
+
};
|
|
432
|
+
workerDetails: {
|
|
433
|
+
type: string;
|
|
434
|
+
description: string;
|
|
435
|
+
properties: {
|
|
436
|
+
messageDraftBuilder: {
|
|
437
|
+
type: string[];
|
|
438
|
+
description: string;
|
|
439
|
+
properties: {
|
|
440
|
+
statusSource: {
|
|
441
|
+
type: string;
|
|
442
|
+
enum: string[];
|
|
443
|
+
};
|
|
444
|
+
status: {
|
|
445
|
+
type: string;
|
|
446
|
+
enum: string[];
|
|
447
|
+
};
|
|
448
|
+
runId: {
|
|
449
|
+
type: string[];
|
|
450
|
+
};
|
|
451
|
+
fallbackId: {
|
|
452
|
+
type: string[];
|
|
453
|
+
};
|
|
454
|
+
startedAt: {
|
|
455
|
+
type: string;
|
|
456
|
+
};
|
|
457
|
+
updatedAt: {
|
|
458
|
+
type: string;
|
|
459
|
+
};
|
|
460
|
+
basisToken: {
|
|
461
|
+
type: string[];
|
|
462
|
+
};
|
|
463
|
+
basis: {
|
|
464
|
+
type: string;
|
|
465
|
+
properties: {
|
|
466
|
+
campaignId: {
|
|
467
|
+
type: string[];
|
|
468
|
+
};
|
|
469
|
+
selectedLeadListId: {
|
|
470
|
+
type: string[];
|
|
471
|
+
};
|
|
472
|
+
workflowTableId: {
|
|
473
|
+
type: string[];
|
|
474
|
+
};
|
|
475
|
+
reviewBatchRowHash: {
|
|
476
|
+
type: string[];
|
|
477
|
+
};
|
|
478
|
+
reviewBatchRowIds: {
|
|
479
|
+
type: string[];
|
|
480
|
+
items: {
|
|
481
|
+
type: string;
|
|
482
|
+
};
|
|
483
|
+
};
|
|
484
|
+
filterChoice: {
|
|
485
|
+
type: string[];
|
|
486
|
+
};
|
|
487
|
+
};
|
|
488
|
+
};
|
|
489
|
+
messageDraftOutputRef: {
|
|
490
|
+
type: string[];
|
|
491
|
+
};
|
|
492
|
+
messageDraftOutput: {
|
|
493
|
+
type: string[];
|
|
494
|
+
};
|
|
495
|
+
error: {
|
|
496
|
+
type: string[];
|
|
497
|
+
};
|
|
498
|
+
};
|
|
499
|
+
required: string[];
|
|
500
|
+
};
|
|
501
|
+
};
|
|
502
|
+
additionalProperties: boolean;
|
|
419
503
|
};
|
|
420
504
|
};
|
|
421
505
|
required: string[];
|
|
@@ -531,6 +615,90 @@ export declare const campaignToolDefinitions: ({
|
|
|
531
615
|
};
|
|
532
616
|
workerStatuses: {
|
|
533
617
|
type: string;
|
|
618
|
+
description: string;
|
|
619
|
+
properties: {
|
|
620
|
+
leadFitBuilder: {
|
|
621
|
+
type: string[];
|
|
622
|
+
enum: (string | null)[];
|
|
623
|
+
};
|
|
624
|
+
messageDraftBuilder: {
|
|
625
|
+
type: string[];
|
|
626
|
+
enum: (string | null)[];
|
|
627
|
+
};
|
|
628
|
+
};
|
|
629
|
+
additionalProperties: boolean;
|
|
630
|
+
};
|
|
631
|
+
workerDetails: {
|
|
632
|
+
type: string;
|
|
633
|
+
description: string;
|
|
634
|
+
properties: {
|
|
635
|
+
messageDraftBuilder: {
|
|
636
|
+
type: string[];
|
|
637
|
+
description: string;
|
|
638
|
+
properties: {
|
|
639
|
+
statusSource: {
|
|
640
|
+
type: string;
|
|
641
|
+
enum: string[];
|
|
642
|
+
};
|
|
643
|
+
status: {
|
|
644
|
+
type: string;
|
|
645
|
+
enum: string[];
|
|
646
|
+
};
|
|
647
|
+
runId: {
|
|
648
|
+
type: string[];
|
|
649
|
+
};
|
|
650
|
+
fallbackId: {
|
|
651
|
+
type: string[];
|
|
652
|
+
};
|
|
653
|
+
startedAt: {
|
|
654
|
+
type: string;
|
|
655
|
+
};
|
|
656
|
+
updatedAt: {
|
|
657
|
+
type: string;
|
|
658
|
+
};
|
|
659
|
+
basisToken: {
|
|
660
|
+
type: string[];
|
|
661
|
+
};
|
|
662
|
+
basis: {
|
|
663
|
+
type: string;
|
|
664
|
+
properties: {
|
|
665
|
+
campaignId: {
|
|
666
|
+
type: string[];
|
|
667
|
+
};
|
|
668
|
+
selectedLeadListId: {
|
|
669
|
+
type: string[];
|
|
670
|
+
};
|
|
671
|
+
workflowTableId: {
|
|
672
|
+
type: string[];
|
|
673
|
+
};
|
|
674
|
+
reviewBatchRowHash: {
|
|
675
|
+
type: string[];
|
|
676
|
+
};
|
|
677
|
+
reviewBatchRowIds: {
|
|
678
|
+
type: string[];
|
|
679
|
+
items: {
|
|
680
|
+
type: string;
|
|
681
|
+
};
|
|
682
|
+
};
|
|
683
|
+
filterChoice: {
|
|
684
|
+
type: string[];
|
|
685
|
+
};
|
|
686
|
+
};
|
|
687
|
+
};
|
|
688
|
+
messageDraftOutputRef: {
|
|
689
|
+
type: string[];
|
|
690
|
+
};
|
|
691
|
+
messageDraftOutput: {
|
|
692
|
+
type: string[];
|
|
693
|
+
};
|
|
694
|
+
error: {
|
|
695
|
+
type: string[];
|
|
696
|
+
};
|
|
697
|
+
};
|
|
698
|
+
required: string[];
|
|
699
|
+
};
|
|
700
|
+
};
|
|
701
|
+
additionalProperties: boolean;
|
|
534
702
|
};
|
|
535
703
|
};
|
|
536
704
|
required: string[];
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -10,6 +10,99 @@ const LEAD_SOURCE_PROVIDERS = {
|
|
|
10
10
|
PROSPEO: "prospeo",
|
|
11
11
|
SIGNAL_DISCOVERY: "signal-discovery",
|
|
12
12
|
};
|
|
13
|
+
const WATCH_NARRATION_TOOL_SCHEMA = {
|
|
14
|
+
type: "object",
|
|
15
|
+
description: "Single structured watch-guide narration object to send with currentStep changes. Include what just happened, what the browser is showing now, what Codex is doing next, and nextAction so the browser guide mirrors Codex progress. Do not send separate guideHeadline/guideBody fields. Use workerStatuses only for simple string badges; put Message Drafting runtime proof under workerDetails.messageDraftBuilder.",
|
|
16
|
+
properties: {
|
|
17
|
+
stage: {
|
|
18
|
+
type: "string",
|
|
19
|
+
enum: [
|
|
20
|
+
"brief-review",
|
|
21
|
+
"find-leads",
|
|
22
|
+
"review-batch",
|
|
23
|
+
"fit-message",
|
|
24
|
+
"review-ready",
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
headline: { type: "string" },
|
|
28
|
+
visibleState: { type: "string" },
|
|
29
|
+
agentIntent: { type: "string" },
|
|
30
|
+
nextAction: { type: ["string", "null"] },
|
|
31
|
+
safety: { type: ["string", "null"] },
|
|
32
|
+
progressLabel: { type: ["string", "null"] },
|
|
33
|
+
blockedReason: { type: ["string", "null"] },
|
|
34
|
+
workerStatuses: {
|
|
35
|
+
type: "object",
|
|
36
|
+
description: 'Simple display badges only. Allowed keys are leadFitBuilder and messageDraftBuilder; values are "idle", "running", "ready", "blocked", or null. Do not put runId, statusSource, basis, or rich worker proof here.',
|
|
37
|
+
properties: {
|
|
38
|
+
leadFitBuilder: {
|
|
39
|
+
type: ["string", "null"],
|
|
40
|
+
enum: ["idle", "running", "ready", "blocked", null],
|
|
41
|
+
},
|
|
42
|
+
messageDraftBuilder: {
|
|
43
|
+
type: ["string", "null"],
|
|
44
|
+
enum: ["idle", "running", "ready", "blocked", null],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
additionalProperties: false,
|
|
48
|
+
},
|
|
49
|
+
workerDetails: {
|
|
50
|
+
type: "object",
|
|
51
|
+
description: "Rich runtime proof for background workers. Message Drafting proof must use workerDetails.messageDraftBuilder, not workerStatuses.messageDrafting.",
|
|
52
|
+
properties: {
|
|
53
|
+
messageDraftBuilder: {
|
|
54
|
+
type: ["object", "null"],
|
|
55
|
+
description: 'Runtime proof that Message Drafting actually started or fell back inline. For a spawned background worker use statusSource "branch" and status "branch-running"; for inline fallback use statusSource "parent-thread-fallback" and status "fallback-active". The basis must include selectedLeadListId, workflowTableId, and either reviewBatchRowHash or reviewBatchRowIds so the watched UI can trust the current execution slice.',
|
|
56
|
+
properties: {
|
|
57
|
+
statusSource: {
|
|
58
|
+
type: "string",
|
|
59
|
+
enum: ["branch", "parent-thread-fallback"],
|
|
60
|
+
},
|
|
61
|
+
status: {
|
|
62
|
+
type: "string",
|
|
63
|
+
enum: [
|
|
64
|
+
"branch-running",
|
|
65
|
+
"fallback-active",
|
|
66
|
+
"spawn-failed",
|
|
67
|
+
"fallback-superseded",
|
|
68
|
+
"branch-superseded",
|
|
69
|
+
"ready",
|
|
70
|
+
"blocked",
|
|
71
|
+
"retry-needed",
|
|
72
|
+
"stale",
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
runId: { type: ["string", "null"] },
|
|
76
|
+
fallbackId: { type: ["string", "null"] },
|
|
77
|
+
startedAt: { type: "string" },
|
|
78
|
+
updatedAt: { type: "string" },
|
|
79
|
+
basisToken: { type: ["string", "null"] },
|
|
80
|
+
basis: {
|
|
81
|
+
type: "object",
|
|
82
|
+
properties: {
|
|
83
|
+
campaignId: { type: ["string", "null"] },
|
|
84
|
+
selectedLeadListId: { type: ["string", "null"] },
|
|
85
|
+
workflowTableId: { type: ["string", "null"] },
|
|
86
|
+
reviewBatchRowHash: { type: ["string", "null"] },
|
|
87
|
+
reviewBatchRowIds: {
|
|
88
|
+
type: ["array", "null"],
|
|
89
|
+
items: { type: "string" },
|
|
90
|
+
},
|
|
91
|
+
filterChoice: { type: ["string", "null"] },
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
messageDraftOutputRef: { type: ["string", "null"] },
|
|
95
|
+
messageDraftOutput: { type: ["object", "null"] },
|
|
96
|
+
error: { type: ["string", "null"] },
|
|
97
|
+
},
|
|
98
|
+
required: ["statusSource", "status", "startedAt", "updatedAt", "basis"],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
additionalProperties: false,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
required: ["stage", "headline", "visibleState", "agentIntent", "nextAction"],
|
|
105
|
+
};
|
|
13
106
|
function normalizeLeadSourceProvider(input) {
|
|
14
107
|
if (!input)
|
|
15
108
|
return null;
|
|
@@ -236,37 +329,7 @@ export const campaignToolDefinitions = [
|
|
|
236
329
|
type: ["string", "null"],
|
|
237
330
|
description: "Workflow step ID (headless or UI step ID such as filter-rules)",
|
|
238
331
|
},
|
|
239
|
-
watchNarration:
|
|
240
|
-
type: "object",
|
|
241
|
-
description: "Single structured watch-guide narration object to send with currentStep changes. Include what just happened, what the browser is showing now, what Codex is doing next, and nextAction so the browser guide mirrors Codex progress.",
|
|
242
|
-
properties: {
|
|
243
|
-
stage: {
|
|
244
|
-
type: "string",
|
|
245
|
-
enum: [
|
|
246
|
-
"brief-review",
|
|
247
|
-
"find-leads",
|
|
248
|
-
"review-batch",
|
|
249
|
-
"fit-message",
|
|
250
|
-
"review-ready",
|
|
251
|
-
],
|
|
252
|
-
},
|
|
253
|
-
headline: { type: "string" },
|
|
254
|
-
visibleState: { type: "string" },
|
|
255
|
-
agentIntent: { type: "string" },
|
|
256
|
-
nextAction: { type: ["string", "null"] },
|
|
257
|
-
safety: { type: ["string", "null"] },
|
|
258
|
-
progressLabel: { type: ["string", "null"] },
|
|
259
|
-
blockedReason: { type: ["string", "null"] },
|
|
260
|
-
workerStatuses: { type: "object" },
|
|
261
|
-
},
|
|
262
|
-
required: [
|
|
263
|
-
"stage",
|
|
264
|
-
"headline",
|
|
265
|
-
"visibleState",
|
|
266
|
-
"agentIntent",
|
|
267
|
-
"nextAction",
|
|
268
|
-
],
|
|
269
|
-
},
|
|
332
|
+
watchNarration: WATCH_NARRATION_TOOL_SCHEMA,
|
|
270
333
|
leadSourceType: {
|
|
271
334
|
type: ["string", "null"],
|
|
272
335
|
description: "Lead source type (existing | new)",
|
|
@@ -344,37 +407,7 @@ export const campaignToolDefinitions = [
|
|
|
344
407
|
type: "string",
|
|
345
408
|
description: "Guarded manual recovery clear. currentStep:null is accepted only when this matches the durable step being cleared.",
|
|
346
409
|
},
|
|
347
|
-
watchNarration:
|
|
348
|
-
type: "object",
|
|
349
|
-
description: "Single structured watch-guide narration object to send with currentStep changes. Include what just happened, what the browser is showing now, what Codex is doing next, and nextAction so the browser guide mirrors Codex progress. Do not send separate guideHeadline/guideBody fields.",
|
|
350
|
-
properties: {
|
|
351
|
-
stage: {
|
|
352
|
-
type: "string",
|
|
353
|
-
enum: [
|
|
354
|
-
"brief-review",
|
|
355
|
-
"find-leads",
|
|
356
|
-
"review-batch",
|
|
357
|
-
"fit-message",
|
|
358
|
-
"review-ready",
|
|
359
|
-
],
|
|
360
|
-
},
|
|
361
|
-
headline: { type: "string" },
|
|
362
|
-
visibleState: { type: "string" },
|
|
363
|
-
agentIntent: { type: "string" },
|
|
364
|
-
nextAction: { type: ["string", "null"] },
|
|
365
|
-
safety: { type: ["string", "null"] },
|
|
366
|
-
progressLabel: { type: ["string", "null"] },
|
|
367
|
-
blockedReason: { type: ["string", "null"] },
|
|
368
|
-
workerStatuses: { type: "object" },
|
|
369
|
-
},
|
|
370
|
-
required: [
|
|
371
|
-
"stage",
|
|
372
|
-
"headline",
|
|
373
|
-
"visibleState",
|
|
374
|
-
"agentIntent",
|
|
375
|
-
"nextAction",
|
|
376
|
-
],
|
|
377
|
-
},
|
|
410
|
+
watchNarration: WATCH_NARRATION_TOOL_SCHEMA,
|
|
378
411
|
interactionMode: {
|
|
379
412
|
type: "string",
|
|
380
413
|
enum: ["step-by-step", "autonomous", "ask-when-needed"],
|
|
@@ -12,31 +12,7 @@ export type CampaignModelQualityResult = {
|
|
|
12
12
|
reasoningEffort: string | null;
|
|
13
13
|
recommendedModel: string;
|
|
14
14
|
recommendedReasoningEffort: string;
|
|
15
|
-
minimumSummary: string;
|
|
16
15
|
confirmationRequired: boolean;
|
|
17
16
|
message: string;
|
|
18
17
|
};
|
|
19
|
-
export type CampaignModelQualityHostConfig = {
|
|
20
|
-
label: string;
|
|
21
|
-
minimumModel: string;
|
|
22
|
-
familyKeywords: string[];
|
|
23
|
-
minimumVersion: string;
|
|
24
|
-
minimumReasoningEffort: string;
|
|
25
|
-
acceptedReasoningEfforts: string[];
|
|
26
|
-
recommendedReasoningEffort: string;
|
|
27
|
-
};
|
|
28
|
-
export type CampaignModelQualityConfig = {
|
|
29
|
-
version: number;
|
|
30
|
-
hosts: {
|
|
31
|
-
claude: CampaignModelQualityHostConfig;
|
|
32
|
-
codex: CampaignModelQualityHostConfig;
|
|
33
|
-
};
|
|
34
|
-
warningCopy: {
|
|
35
|
-
ok: string;
|
|
36
|
-
unknownSettings: string;
|
|
37
|
-
belowMinimum: string;
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
export declare function getCampaignModelQualityConfig(): CampaignModelQualityConfig;
|
|
41
|
-
export declare function getCampaignModelMinimumSummary(config?: CampaignModelQualityConfig): string;
|
|
42
18
|
export declare function evaluateCampaignModelQuality(input?: CampaignModelQualityInput): CampaignModelQualityResult;
|
|
@@ -1,57 +1,9 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
import { resolveSkillsDir } from "../skills.js";
|
|
4
|
-
const DEFAULT_MODEL_QUALITY_CONFIG = {
|
|
5
|
-
version: 1,
|
|
6
|
-
hosts: {
|
|
7
|
-
claude: {
|
|
8
|
-
label: "Claude Code",
|
|
9
|
-
minimumModel: "Claude Opus 4.8",
|
|
10
|
-
familyKeywords: ["opus"],
|
|
11
|
-
minimumVersion: "4.8",
|
|
12
|
-
minimumReasoningEffort: "high",
|
|
13
|
-
acceptedReasoningEfforts: [
|
|
14
|
-
"high",
|
|
15
|
-
"extra high",
|
|
16
|
-
"extra-high",
|
|
17
|
-
"xhigh",
|
|
18
|
-
"extra_high",
|
|
19
|
-
],
|
|
20
|
-
recommendedReasoningEffort: "high or better",
|
|
21
|
-
},
|
|
22
|
-
codex: {
|
|
23
|
-
label: "Codex",
|
|
24
|
-
minimumModel: "GPT 5.5",
|
|
25
|
-
familyKeywords: ["gpt"],
|
|
26
|
-
minimumVersion: "5.5",
|
|
27
|
-
minimumReasoningEffort: "high",
|
|
28
|
-
acceptedReasoningEfforts: [
|
|
29
|
-
"high",
|
|
30
|
-
"extra high",
|
|
31
|
-
"extra-high",
|
|
32
|
-
"xhigh",
|
|
33
|
-
"extra_high",
|
|
34
|
-
],
|
|
35
|
-
recommendedReasoningEffort: "high or better",
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
warningCopy: {
|
|
39
|
-
ok: "Campaign model settings meet the configured minimum: {minimumSummary}.",
|
|
40
|
-
unknownSettings: "Model settings were not provided by the host. Best campaigns need at least {minimumSummary}. Confirm the user is on one of those settings, or ask them to switch before continuing.",
|
|
41
|
-
belowMinimum: "Best campaigns need at least {minimumSummary}. Current settings look below that: {currentSettings}. Please switch before continuing, or explicitly say to continue anyway.",
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
let cachedConfig = null;
|
|
45
1
|
const normalize = (value) => String(value ?? "")
|
|
46
2
|
.trim()
|
|
47
3
|
.toLowerCase();
|
|
48
4
|
const normalizeHost = (host) => {
|
|
49
5
|
const normalized = normalize(host);
|
|
50
|
-
if (normalized.includes("claude") ||
|
|
51
|
-
normalized.includes("clod") ||
|
|
52
|
-
normalized.includes("opus") ||
|
|
53
|
-
normalized.includes("sonnet") ||
|
|
54
|
-
normalized.includes("haiku")) {
|
|
6
|
+
if (normalized.includes("claude") || normalized.includes("clod")) {
|
|
55
7
|
return "claude";
|
|
56
8
|
}
|
|
57
9
|
if (normalized.includes("codex") ||
|
|
@@ -61,101 +13,20 @@ const normalizeHost = (host) => {
|
|
|
61
13
|
}
|
|
62
14
|
return "unknown";
|
|
63
15
|
};
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const minimumPart = minimumParts[index] ?? 0;
|
|
72
|
-
if (candidatePart > minimumPart)
|
|
73
|
-
return 1;
|
|
74
|
-
if (candidatePart < minimumPart)
|
|
75
|
-
return -1;
|
|
76
|
-
}
|
|
77
|
-
return 0;
|
|
16
|
+
const isHighClaudeReasoning = (reasoning) => {
|
|
17
|
+
const normalized = normalize(reasoning).replace(/[_\s-]+/g, "");
|
|
18
|
+
return ["high", "xhigh", "extrahigh"].includes(normalized);
|
|
19
|
+
};
|
|
20
|
+
const isExtraHighCodexReasoning = (reasoning) => {
|
|
21
|
+
const normalized = normalize(reasoning).replace(/[_\s-]+/g, "");
|
|
22
|
+
return ["xhigh", "extrahigh"].includes(normalized);
|
|
78
23
|
};
|
|
79
|
-
const extractModelVersions = (model) => Array.from(normalize(model).matchAll(/\b(\d+(?:\.\d+){0,2})\b/g)).map((match) => match[1]);
|
|
80
|
-
function mergeConfig(rawConfig) {
|
|
81
|
-
return {
|
|
82
|
-
...DEFAULT_MODEL_QUALITY_CONFIG,
|
|
83
|
-
...rawConfig,
|
|
84
|
-
hosts: {
|
|
85
|
-
claude: {
|
|
86
|
-
...DEFAULT_MODEL_QUALITY_CONFIG.hosts.claude,
|
|
87
|
-
...(rawConfig.hosts?.claude || {}),
|
|
88
|
-
},
|
|
89
|
-
codex: {
|
|
90
|
-
...DEFAULT_MODEL_QUALITY_CONFIG.hosts.codex,
|
|
91
|
-
...(rawConfig.hosts?.codex || {}),
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
warningCopy: {
|
|
95
|
-
...DEFAULT_MODEL_QUALITY_CONFIG.warningCopy,
|
|
96
|
-
...(rawConfig.warningCopy || {}),
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
export function getCampaignModelQualityConfig() {
|
|
101
|
-
if (cachedConfig)
|
|
102
|
-
return cachedConfig;
|
|
103
|
-
const configPath = join(resolveSkillsDir(), "create-campaign", "core", "model-quality.json");
|
|
104
|
-
if (!existsSync(configPath)) {
|
|
105
|
-
cachedConfig = DEFAULT_MODEL_QUALITY_CONFIG;
|
|
106
|
-
return cachedConfig;
|
|
107
|
-
}
|
|
108
|
-
try {
|
|
109
|
-
cachedConfig = mergeConfig(JSON.parse(readFileSync(configPath, "utf-8")));
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
cachedConfig = DEFAULT_MODEL_QUALITY_CONFIG;
|
|
113
|
-
}
|
|
114
|
-
return cachedConfig;
|
|
115
|
-
}
|
|
116
|
-
export function getCampaignModelMinimumSummary(config = getCampaignModelQualityConfig()) {
|
|
117
|
-
return `${config.hosts.claude.minimumModel} with ${config.hosts.claude.minimumReasoningEffort} reasoning or ${config.hosts.codex.minimumModel} with ${config.hosts.codex.minimumReasoningEffort} reasoning`;
|
|
118
|
-
}
|
|
119
|
-
function formatCopy(template, values) {
|
|
120
|
-
return Object.entries(values).reduce((text, [key, value]) => text.replaceAll(`{${key}}`, value), template);
|
|
121
|
-
}
|
|
122
|
-
function acceptsReasoning(reasoning, hostConfig) {
|
|
123
|
-
const normalized = normalizeReasoning(reasoning);
|
|
124
|
-
return hostConfig.acceptedReasoningEfforts
|
|
125
|
-
.map((value) => normalizeReasoning(value))
|
|
126
|
-
.includes(normalized);
|
|
127
|
-
}
|
|
128
|
-
function modelMeetsMinimum(model, hostConfig) {
|
|
129
|
-
const normalizedModel = normalize(model).replace(/[_-]+/g, " ");
|
|
130
|
-
if (!normalizedModel)
|
|
131
|
-
return false;
|
|
132
|
-
const familyMatches = hostConfig.familyKeywords.every((keyword) => normalizedModel.includes(normalize(keyword)));
|
|
133
|
-
if (!familyMatches)
|
|
134
|
-
return false;
|
|
135
|
-
const versions = extractModelVersions(normalizedModel);
|
|
136
|
-
if (!hostConfig.minimumVersion)
|
|
137
|
-
return true;
|
|
138
|
-
return versions.some((version) => compareVersion(version, hostConfig.minimumVersion) >= 0);
|
|
139
|
-
}
|
|
140
|
-
function findAcceptedHostConfig(host, model, config) {
|
|
141
|
-
const candidates = host === "unknown"
|
|
142
|
-
? [
|
|
143
|
-
["claude", config.hosts.claude],
|
|
144
|
-
["codex", config.hosts.codex],
|
|
145
|
-
]
|
|
146
|
-
: [[host, config.hosts[host]]];
|
|
147
|
-
return candidates.find(([, hostConfig]) => modelMeetsMinimum(model, hostConfig));
|
|
148
|
-
}
|
|
149
24
|
export function evaluateCampaignModelQuality(input = {}) {
|
|
150
|
-
const
|
|
151
|
-
const host = normalizeHost([input.host, input.model].filter(Boolean).join(" "));
|
|
25
|
+
const host = normalizeHost(input.host);
|
|
152
26
|
const model = input.model?.trim() || null;
|
|
153
27
|
const reasoningEffort = input.reasoningEffort?.trim() || null;
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
const minimumSummary = getCampaignModelMinimumSummary(config);
|
|
157
|
-
const recommendedModel = recommendedHostConfig.minimumModel;
|
|
158
|
-
const recommendedReasoningEffort = recommendedHostConfig.recommendedReasoningEffort;
|
|
28
|
+
const recommendedModel = host === "claude" ? "Opus 4.8" : "GPT 5.5";
|
|
29
|
+
const recommendedReasoningEffort = host === "claude" ? "high or extra high" : "extra high";
|
|
159
30
|
if (!model && !reasoningEffort) {
|
|
160
31
|
return {
|
|
161
32
|
status: "unknown",
|
|
@@ -164,37 +35,30 @@ export function evaluateCampaignModelQuality(input = {}) {
|
|
|
164
35
|
reasoningEffort,
|
|
165
36
|
recommendedModel,
|
|
166
37
|
recommendedReasoningEffort,
|
|
167
|
-
minimumSummary,
|
|
168
38
|
confirmationRequired: true,
|
|
169
|
-
message:
|
|
170
|
-
minimumSummary,
|
|
171
|
-
currentSettings: "unknown",
|
|
172
|
-
}),
|
|
39
|
+
message: "Model settings were not provided by the host. Before campaign-critical generation, confirm the user is using Claude Opus 4.8 at high/extra high or Codex GPT 5.5 at extra high.",
|
|
173
40
|
};
|
|
174
41
|
}
|
|
175
|
-
const
|
|
176
|
-
const ok =
|
|
42
|
+
const normalizedModel = normalize(model);
|
|
43
|
+
const ok = host === "claude"
|
|
44
|
+
? normalizedModel.includes("opus") &&
|
|
45
|
+
normalizedModel.includes("4.8") &&
|
|
46
|
+
isHighClaudeReasoning(reasoningEffort)
|
|
47
|
+
: normalizedModel.includes("gpt") &&
|
|
48
|
+
normalizedModel.includes("5.5") &&
|
|
49
|
+
isExtraHighCodexReasoning(reasoningEffort);
|
|
177
50
|
if (ok) {
|
|
178
51
|
return {
|
|
179
52
|
status: "ok",
|
|
180
|
-
host
|
|
53
|
+
host,
|
|
181
54
|
model,
|
|
182
55
|
reasoningEffort,
|
|
183
|
-
recommendedModel
|
|
184
|
-
recommendedReasoningEffort
|
|
185
|
-
recommendedReasoningEffort,
|
|
186
|
-
minimumSummary,
|
|
56
|
+
recommendedModel,
|
|
57
|
+
recommendedReasoningEffort,
|
|
187
58
|
confirmationRequired: false,
|
|
188
|
-
message:
|
|
189
|
-
minimumSummary,
|
|
190
|
-
currentSettings: "current settings",
|
|
191
|
-
}),
|
|
59
|
+
message: "Recommended campaign-quality model settings are active.",
|
|
192
60
|
};
|
|
193
61
|
}
|
|
194
|
-
const currentSettings = [
|
|
195
|
-
model ? `model "${model}"` : "unknown model",
|
|
196
|
-
reasoningEffort ? `reasoning "${reasoningEffort}"` : "unknown reasoning",
|
|
197
|
-
].join(", ");
|
|
198
62
|
return {
|
|
199
63
|
status: "warn",
|
|
200
64
|
host,
|
|
@@ -202,11 +66,7 @@ export function evaluateCampaignModelQuality(input = {}) {
|
|
|
202
66
|
reasoningEffort,
|
|
203
67
|
recommendedModel,
|
|
204
68
|
recommendedReasoningEffort,
|
|
205
|
-
minimumSummary,
|
|
206
69
|
confirmationRequired: true,
|
|
207
|
-
message:
|
|
208
|
-
minimumSummary,
|
|
209
|
-
currentSettings,
|
|
210
|
-
}),
|
|
70
|
+
message: `For best campaign quality, switch to ${recommendedModel} with ${recommendedReasoningEffort} reasoning before lead filtering, message generation, or launch review.`,
|
|
211
71
|
};
|
|
212
72
|
}
|