@sellable/mcp 0.1.322 → 0.1.324
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/server.js +7 -0
- package/dist/tools/prompts.d.ts +3 -3
- package/dist/tools/prompts.js +1 -0
- package/dist/tools/registry.d.ts +32 -2
- package/dist/tools/registry.js +2 -0
- package/dist/tools/waterfalls.d.ts +67 -0
- package/dist/tools/waterfalls.js +51 -0
- package/package.json +1 -1
- package/skills/create-evergreen-campaigns/SKILL.md +59 -0
- package/skills/fill-send-horizon/SKILL.md +22 -1
package/dist/server.js
CHANGED
|
@@ -41,6 +41,7 @@ import { attachRecommendedSequence, attachSequence, createWorkflowTable, } from
|
|
|
41
41
|
import { exportTableCsv, listTables } from "./tools/tables.js";
|
|
42
42
|
import { handleVerifyTableRow } from "./tools/verify-row.js";
|
|
43
43
|
import { sanitizeWatchUrlsForMcpResult } from "./tools/watch-url-security.js";
|
|
44
|
+
import { getCampaignWaterfall, setCampaignWaterfallOrder, } from "./tools/waterfalls.js";
|
|
44
45
|
import { addTeammate, createWorkspace, getActiveWorkspace, listWorkspaces, setActiveWorkspace, } from "./tools/workspaces.js";
|
|
45
46
|
import { checkForUpdates, logUpdateNotice } from "./update-check.js";
|
|
46
47
|
const server = new Server({
|
|
@@ -378,6 +379,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
378
379
|
case "verify_table_row":
|
|
379
380
|
result = await handleVerifyTableRow(args);
|
|
380
381
|
break;
|
|
382
|
+
case "get_campaign_waterfall":
|
|
383
|
+
result = await getCampaignWaterfall();
|
|
384
|
+
break;
|
|
385
|
+
case "set_campaign_waterfall_order":
|
|
386
|
+
result = await setCampaignWaterfallOrder(args);
|
|
387
|
+
break;
|
|
381
388
|
case "get_rows":
|
|
382
389
|
result = await getRows(args?.tableId, args?.rowIds);
|
|
383
390
|
break;
|
package/dist/tools/prompts.d.ts
CHANGED
|
@@ -137,7 +137,7 @@ export interface PostFindLeadsScoutRegistryResponse {
|
|
|
137
137
|
}
|
|
138
138
|
export declare const DEFAULT_SUBSKILL_PROMPT_CHUNK_CHARS = 48000;
|
|
139
139
|
export declare const MAX_SUBSKILL_PROMPT_CHUNK_CHARS = 48000;
|
|
140
|
-
export declare const ALLOWED_SUBSKILL_PROMPT_NAMES: readonly ["building-gtm-tables", "content", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
140
|
+
export declare const ALLOWED_SUBSKILL_PROMPT_NAMES: readonly ["building-gtm-tables", "content", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-evergreen-campaigns", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
141
141
|
export declare const promptToolDefinitions: ({
|
|
142
142
|
name: string;
|
|
143
143
|
description: string;
|
|
@@ -181,7 +181,7 @@ export declare const promptToolDefinitions: ({
|
|
|
181
181
|
properties: {
|
|
182
182
|
subskillName: {
|
|
183
183
|
type: string;
|
|
184
|
-
enum: readonly ["building-gtm-tables", "content", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
184
|
+
enum: readonly ["building-gtm-tables", "content", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-evergreen-campaigns", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
185
185
|
description: string;
|
|
186
186
|
};
|
|
187
187
|
offset: {
|
|
@@ -216,7 +216,7 @@ export declare const promptToolDefinitions: ({
|
|
|
216
216
|
properties: {
|
|
217
217
|
subskillName: {
|
|
218
218
|
type: string;
|
|
219
|
-
enum: readonly ["building-gtm-tables", "content", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
219
|
+
enum: readonly ["building-gtm-tables", "content", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-evergreen-campaigns", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
220
220
|
description: string;
|
|
221
221
|
};
|
|
222
222
|
assetPath: {
|
package/dist/tools/prompts.js
CHANGED
package/dist/tools/registry.d.ts
CHANGED
|
@@ -236,7 +236,7 @@ export declare const allTools: ({
|
|
|
236
236
|
properties: {
|
|
237
237
|
subskillName: {
|
|
238
238
|
type: string;
|
|
239
|
-
enum: readonly ["building-gtm-tables", "content", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
239
|
+
enum: readonly ["building-gtm-tables", "content", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-evergreen-campaigns", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
240
240
|
description: string;
|
|
241
241
|
};
|
|
242
242
|
offset: {
|
|
@@ -271,7 +271,7 @@ export declare const allTools: ({
|
|
|
271
271
|
properties: {
|
|
272
272
|
subskillName: {
|
|
273
273
|
type: string;
|
|
274
|
-
enum: readonly ["building-gtm-tables", "content", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
274
|
+
enum: readonly ["building-gtm-tables", "content", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-evergreen-campaigns", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
275
275
|
description: string;
|
|
276
276
|
};
|
|
277
277
|
assetPath: {
|
|
@@ -7421,6 +7421,36 @@ export declare const allTools: ({
|
|
|
7421
7421
|
};
|
|
7422
7422
|
required: string[];
|
|
7423
7423
|
};
|
|
7424
|
+
} | {
|
|
7425
|
+
readonly name: "get_campaign_waterfall";
|
|
7426
|
+
readonly description: "Read the active workspace's managed-campaign waterfall hierarchy: each waterfall (per sender or shared) with its campaign slots in stored priority order. Use this before fill-send-horizon runs to follow the configured lane order, or when the user asks what order their campaigns fill in. Returns empty when the workspace has no managed-campaign program (the hierarchy is created by evergreen reconcile).";
|
|
7427
|
+
readonly inputSchema: {
|
|
7428
|
+
readonly type: "object";
|
|
7429
|
+
readonly properties: {};
|
|
7430
|
+
readonly required: readonly [];
|
|
7431
|
+
readonly additionalProperties: false;
|
|
7432
|
+
};
|
|
7433
|
+
} | {
|
|
7434
|
+
readonly name: "set_campaign_waterfall_order";
|
|
7435
|
+
readonly description: "Reorder one waterfall's campaign slots — the stored hierarchy that decides which lane fills first (e.g. move Shared Signal Discovery above Post Engagers). Pass the waterfallKey from get_campaign_waterfall and orderedSlotKeys containing EXACTLY that waterfall's slot keys in the desired order; priorities are renumbered 10/20/30. Confirm the new order with the user before calling unless they stated it explicitly.";
|
|
7436
|
+
readonly inputSchema: {
|
|
7437
|
+
readonly type: "object";
|
|
7438
|
+
readonly properties: {
|
|
7439
|
+
readonly waterfallKey: {
|
|
7440
|
+
readonly type: "string";
|
|
7441
|
+
readonly description: "Waterfall key from get_campaign_waterfall.";
|
|
7442
|
+
};
|
|
7443
|
+
readonly orderedSlotKeys: {
|
|
7444
|
+
readonly type: "array";
|
|
7445
|
+
readonly items: {
|
|
7446
|
+
readonly type: "string";
|
|
7447
|
+
};
|
|
7448
|
+
readonly description: "All of the waterfall's slot keys in the desired fill order (highest priority first). Must match the existing slot set exactly — no additions or omissions.";
|
|
7449
|
+
};
|
|
7450
|
+
};
|
|
7451
|
+
readonly required: readonly ["waterfallKey", "orderedSlotKeys"];
|
|
7452
|
+
readonly additionalProperties: false;
|
|
7453
|
+
};
|
|
7424
7454
|
} | {
|
|
7425
7455
|
name: string;
|
|
7426
7456
|
description: string;
|
package/dist/tools/registry.js
CHANGED
|
@@ -35,6 +35,7 @@ import { senderToolDefinitions } from "./senders.js";
|
|
|
35
35
|
import { sequencerToolDefinitions } from "./sequencer.js";
|
|
36
36
|
import { tableToolDefinitions } from "./tables.js";
|
|
37
37
|
import { verifyRowToolDefinitions } from "./verify-row.js";
|
|
38
|
+
import { waterfallToolDefinitions } from "./waterfalls.js";
|
|
38
39
|
import { workspaceToolDefinitions } from "./workspaces.js";
|
|
39
40
|
export const allTools = [
|
|
40
41
|
...campaignToolDefinitions,
|
|
@@ -76,4 +77,5 @@ export const allTools = [
|
|
|
76
77
|
...columnDeleteToolDefinitions,
|
|
77
78
|
...columnReorderToolDefinitions,
|
|
78
79
|
...verifyRowToolDefinitions,
|
|
80
|
+
...waterfallToolDefinitions,
|
|
79
81
|
];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export interface WaterfallSlotEntry {
|
|
2
|
+
slotKey: string;
|
|
3
|
+
priority: number;
|
|
4
|
+
displayName: string;
|
|
5
|
+
campaignOfferId: string | null;
|
|
6
|
+
workflowTableId: string | null;
|
|
7
|
+
sourceType: string;
|
|
8
|
+
sourceScope: string;
|
|
9
|
+
slotStatus: string;
|
|
10
|
+
}
|
|
11
|
+
export interface WaterfallEntry {
|
|
12
|
+
programKey: string;
|
|
13
|
+
waterfallKey: string;
|
|
14
|
+
displayName: string;
|
|
15
|
+
senderRefs: string[];
|
|
16
|
+
slots: WaterfallSlotEntry[];
|
|
17
|
+
}
|
|
18
|
+
export interface GetCampaignWaterfallResponse {
|
|
19
|
+
waterfalls: WaterfallEntry[];
|
|
20
|
+
}
|
|
21
|
+
export interface SetWaterfallOrderInput {
|
|
22
|
+
waterfallKey: string;
|
|
23
|
+
orderedSlotKeys: string[];
|
|
24
|
+
}
|
|
25
|
+
export declare const waterfallToolDefinitions: readonly [{
|
|
26
|
+
readonly name: "get_campaign_waterfall";
|
|
27
|
+
readonly description: "Read the active workspace's managed-campaign waterfall hierarchy: each waterfall (per sender or shared) with its campaign slots in stored priority order. Use this before fill-send-horizon runs to follow the configured lane order, or when the user asks what order their campaigns fill in. Returns empty when the workspace has no managed-campaign program (the hierarchy is created by evergreen reconcile).";
|
|
28
|
+
readonly inputSchema: {
|
|
29
|
+
readonly type: "object";
|
|
30
|
+
readonly properties: {};
|
|
31
|
+
readonly required: readonly [];
|
|
32
|
+
readonly additionalProperties: false;
|
|
33
|
+
};
|
|
34
|
+
}, {
|
|
35
|
+
readonly name: "set_campaign_waterfall_order";
|
|
36
|
+
readonly description: "Reorder one waterfall's campaign slots — the stored hierarchy that decides which lane fills first (e.g. move Shared Signal Discovery above Post Engagers). Pass the waterfallKey from get_campaign_waterfall and orderedSlotKeys containing EXACTLY that waterfall's slot keys in the desired order; priorities are renumbered 10/20/30. Confirm the new order with the user before calling unless they stated it explicitly.";
|
|
37
|
+
readonly inputSchema: {
|
|
38
|
+
readonly type: "object";
|
|
39
|
+
readonly properties: {
|
|
40
|
+
readonly waterfallKey: {
|
|
41
|
+
readonly type: "string";
|
|
42
|
+
readonly description: "Waterfall key from get_campaign_waterfall.";
|
|
43
|
+
};
|
|
44
|
+
readonly orderedSlotKeys: {
|
|
45
|
+
readonly type: "array";
|
|
46
|
+
readonly items: {
|
|
47
|
+
readonly type: "string";
|
|
48
|
+
};
|
|
49
|
+
readonly description: "All of the waterfall's slot keys in the desired fill order (highest priority first). Must match the existing slot set exactly — no additions or omissions.";
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
readonly required: readonly ["waterfallKey", "orderedSlotKeys"];
|
|
53
|
+
readonly additionalProperties: false;
|
|
54
|
+
};
|
|
55
|
+
}];
|
|
56
|
+
export declare function getCampaignWaterfall(): Promise<GetCampaignWaterfallResponse | {
|
|
57
|
+
waterfalls: never[];
|
|
58
|
+
_notice: string;
|
|
59
|
+
}>;
|
|
60
|
+
export declare function setCampaignWaterfallOrder(input: SetWaterfallOrderInput): Promise<{
|
|
61
|
+
waterfallKey: string;
|
|
62
|
+
slots: Array<{
|
|
63
|
+
slotKey: string;
|
|
64
|
+
priority: number;
|
|
65
|
+
}>;
|
|
66
|
+
previousOrder: string[];
|
|
67
|
+
}>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { getApi } from "../api.js";
|
|
2
|
+
export const waterfallToolDefinitions = [
|
|
3
|
+
{
|
|
4
|
+
name: "get_campaign_waterfall",
|
|
5
|
+
description: "Read the active workspace's managed-campaign waterfall hierarchy: each waterfall (per sender or shared) with its campaign slots in stored priority order. Use this before fill-send-horizon runs to follow the configured lane order, or when the user asks what order their campaigns fill in. Returns empty when the workspace has no managed-campaign program (the hierarchy is created by evergreen reconcile).",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: {},
|
|
9
|
+
required: [],
|
|
10
|
+
additionalProperties: false,
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: "set_campaign_waterfall_order",
|
|
15
|
+
description: "Reorder one waterfall's campaign slots — the stored hierarchy that decides which lane fills first (e.g. move Shared Signal Discovery above Post Engagers). Pass the waterfallKey from get_campaign_waterfall and orderedSlotKeys containing EXACTLY that waterfall's slot keys in the desired order; priorities are renumbered 10/20/30. Confirm the new order with the user before calling unless they stated it explicitly.",
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
waterfallKey: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Waterfall key from get_campaign_waterfall.",
|
|
22
|
+
},
|
|
23
|
+
orderedSlotKeys: {
|
|
24
|
+
type: "array",
|
|
25
|
+
items: { type: "string" },
|
|
26
|
+
description: "All of the waterfall's slot keys in the desired fill order (highest priority first). Must match the existing slot set exactly — no additions or omissions.",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
required: ["waterfallKey", "orderedSlotKeys"],
|
|
30
|
+
additionalProperties: false,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
export async function getCampaignWaterfall() {
|
|
35
|
+
const api = getApi();
|
|
36
|
+
const response = await api.get("/api/v3/managed-campaigns/waterfalls");
|
|
37
|
+
if (!response.waterfalls || response.waterfalls.length === 0) {
|
|
38
|
+
return {
|
|
39
|
+
waterfalls: [],
|
|
40
|
+
_notice: "No managed-campaign waterfalls exist in this workspace yet. Run the evergreen reconcile (create-evergreen-campaigns) to materialize the hierarchy, or fall back to the per-invocation order.",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return response;
|
|
44
|
+
}
|
|
45
|
+
export async function setCampaignWaterfallOrder(input) {
|
|
46
|
+
const api = getApi();
|
|
47
|
+
return api.put("/api/v3/managed-campaigns/waterfalls", {
|
|
48
|
+
waterfallKey: input.waterfallKey,
|
|
49
|
+
orderedSlotKeys: input.orderedSlotKeys,
|
|
50
|
+
});
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-evergreen-campaigns
|
|
3
|
+
description: Reconcile a sender team's evergreen campaign structure — create-or-reuse the standing campaigns (per-sender post engagers + shared fallback lanes) idempotently, with sequences attached and prospect-safe message briefs. Never duplicates, never launches. Schedule-automation friendly ("make sure evergreen campaigns exist for these users").
|
|
4
|
+
visibility: internal
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Create Evergreen Campaigns
|
|
8
|
+
|
|
9
|
+
<role>
|
|
10
|
+
You are a campaign structure reconciler. Evergreen campaigns are the standing
|
|
11
|
+
always-on lanes every sender should have. Your job is to make reality match the
|
|
12
|
+
plan — creating only what is missing, reusing everything that exists, and never
|
|
13
|
+
producing duplicates. Reconcile/create-reuse only; this skill never launches campaigns.
|
|
14
|
+
</role>
|
|
15
|
+
|
|
16
|
+
<inputs>
|
|
17
|
+
The invoking prompt names the senders ("create evergreen campaigns for csreyes92 and thomas"). Resolve each via `list_senders`.
|
|
18
|
+
|
|
19
|
+
Default evergreen plan per workspace (override only if the prompt specifies different lanes):
|
|
20
|
+
|
|
21
|
+
1. **`<Sender Name> - Post Engagers`** — one per sender (warm lane, highest priority)
|
|
22
|
+
2. **`<Workspace/Team> - Shared Signal Discovery`** — one shared across senders
|
|
23
|
+
3. **`<Workspace/Team> - Shared Cold Fallback`** — one shared across senders
|
|
24
|
+
</inputs>
|
|
25
|
+
|
|
26
|
+
<objective>
|
|
27
|
+
1. **Inventory first**: `get_campaigns` + `list_tables` in the active workspace. Match existing campaigns to the plan by name (case-insensitive, ignore suffixes like "(Copy)"). A matching non-archived campaign = REUSE; record it and move on. Never create a second campaign for a slot that already has one.
|
|
28
|
+
2. **Create only the missing slots** with `create_on_demand_campaign({ name, senderIds, campaignBrief })`:
|
|
29
|
+
- Post Engagers lanes: that sender's ID only. Shared lanes: all the senders' IDs.
|
|
30
|
+
- The brief must include the warm post-engager first-message template style — short, casual, references the post they engaged with, closed question, **no internal vocabulary, no pitch, no meeting ask** in message one:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
{{first_name}}
|
|
34
|
+
|
|
35
|
+
Hey there
|
|
36
|
+
|
|
37
|
+
Saw you pop up on my post about {{post_topic_line}}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- The sequence is auto-selected by sender tier; do not hand-author sequence templates here. Never select a paid-InMail template.
|
|
41
|
+
3. **Verify each slot** after create/reuse: `get_campaign` shows the table exists and a sequence is attached (`list_tables({ hasSequence: true })` as a cross-check).
|
|
42
|
+
4. **Report the reconcile plan and result** — every slot tagged `reused` or `created`, nothing else:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Evergreen Reconcile — {date}
|
|
46
|
+
• Christian Reyes - Post Engagers: reused (18 rows)
|
|
47
|
+
• Thomas Nobbs - Post Engagers: reused (8 rows)
|
|
48
|
+
• Sellable.dev - Shared Signal Discovery: reused
|
|
49
|
+
• Sellable.dev - Shared Cold Fallback: created (empty — needs leads)
|
|
50
|
+
All campaigns remain unlaunched. Next: refresh-sender-engagement to supply the warm lanes, then fill-send-horizon.
|
|
51
|
+
```
|
|
52
|
+
</objective>
|
|
53
|
+
|
|
54
|
+
<safety>
|
|
55
|
+
- **Idempotent by construction**: rerunning on a schedule must produce all-`reused` and zero new campaigns. If a name matches ambiguously (two candidates), reuse the most recently updated and flag the duplicate for the operator instead of creating a third.
|
|
56
|
+
- Never call `start_campaign` / `start_on_demand_campaign`. New campaigns stay in their default unlaunched state.
|
|
57
|
+
- Never archive, rename, or delete existing campaigns — reconcile is additive; structural removals are operator actions.
|
|
58
|
+
- Respect workspace boundaries: senders and campaigns must belong to the active workspace.
|
|
59
|
+
</safety>
|
|
@@ -17,7 +17,7 @@ The invoking prompt (often a scheduled automation) names the targets. Accept any
|
|
|
17
17
|
- Sender names ("…for csreyes92 and thomas") — resolve via `list_senders`, then `get_campaigns` and match campaigns attached to those senders
|
|
18
18
|
- Nothing specific ("fill send horizon") — operate on all non-archived campaign tables in the active workspace that have a sequence attached (`list_tables({ hasSequence: true })`), skipping ARCHIVED ones
|
|
19
19
|
|
|
20
|
-
Optional inputs: `targetPreparedMessages` (default: leave unset for the adaptive default),
|
|
20
|
+
Optional inputs: `targetPreparedMessages` (default: leave unset for the adaptive default), an explicit "approve" instruction (see safety rules), and a **waterfall priority order** (see below).
|
|
21
21
|
</inputs>
|
|
22
22
|
|
|
23
23
|
<objective>
|
|
@@ -30,6 +30,27 @@ For each target campaign:
|
|
|
30
30
|
5. Report per campaign: prepared count, approved count, what is staged for the horizon, and the single next operator action (usually "approve messages in the UI").
|
|
31
31
|
</objective>
|
|
32
32
|
|
|
33
|
+
<waterfall>
|
|
34
|
+
## Campaign hierarchy (waterfall order)
|
|
35
|
+
|
|
36
|
+
The fill order is **stored workspace state**, not something each automation restates. Resolve it in this precedence:
|
|
37
|
+
|
|
38
|
+
1. **Stored hierarchy (default)**: call `get_campaign_waterfall` first. It returns each waterfall (per sender or shared) with slots in stored priority order — campaign offer IDs included. Fill in that order.
|
|
39
|
+
2. **Per-invocation override**: if the invoking prompt states an explicit order ("fill in this order: Signal Discovery, Post Engagers"), use it for this run only and say in the report that the stored order was overridden. To change the order durably, use `set_campaign_waterfall_order` — but only when the user/automation explicitly asks to change the hierarchy, never as a side effect of a fill run.
|
|
40
|
+
3. **No stored hierarchy** (tool returns empty — evergreen reconcile hasn't run): fall back to the conventional order `<Sender> - Post Engagers` (warmest) → `Shared Signal Discovery` → `Shared Cold Fallback` (coldest), and note that running `create-evergreen-campaigns` will materialize a stored hierarchy.
|
|
41
|
+
|
|
42
|
+
Fill mechanics, whatever the source of the order:
|
|
43
|
+
|
|
44
|
+
- **Fill the top lane first.** Move to the next lane ONLY when the current one stops with source exhaustion / no more eligible rows — never because it is merely slow.
|
|
45
|
+
- **Stop descending once the horizon target is met.** If the warm lane alone fills the horizon, the cold lanes get nothing this run — that is correct, not a gap.
|
|
46
|
+
- **Report per lane** so the operator can see the waterfall working:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
csreyes92 waterfall (stored order): Post Engagers filled 4/4 (horizon met — lower lanes skipped)
|
|
50
|
+
thomas waterfall (stored order): Post Engagers 1/4 (source thin) → Shared Signal Discovery 3/3 remaining
|
|
51
|
+
```
|
|
52
|
+
</waterfall>
|
|
53
|
+
|
|
33
54
|
<safety>
|
|
34
55
|
- **Never call `start_campaign`, `start_on_demand_campaign`, or `start_direct_campaign`.** Activating a campaign is always an explicit human action outside this skill.
|
|
35
56
|
- Use `approvalMode: "approve"` ONLY when the invoking prompt explicitly says to auto-approve (e.g. "fill and approve"). Default is `mark_ready` — a human approves in the UI.
|