@sellable/mcp 0.1.337 → 0.1.339

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 CHANGED
@@ -9,7 +9,7 @@ import { prepareCampaignAbTest } from "./tools/campaign-ab-test.js";
9
9
  import { resolveCampaignFillRoute } from "./tools/campaign-fill-routing.js";
10
10
  import { fillCampaignHorizon } from "./tools/campaign-horizon-fill.js";
11
11
  import { cancelPrepareCampaignMessages, getPrepareCampaignMessagesStatus, startPrepareCampaignMessages, } from "./tools/campaign-message-preparation.js";
12
- import { refillCampaignSends } from "./tools/refill-campaign-sends.js";
12
+ import { getCampaignRefillState } from "./tools/campaign-refill-state.js";
13
13
  import { getCampaignTableSchema, queueCampaignCells, recordCampaignReviewBatch, reviseMessageTemplateAndRerun, selectCampaignCells, waitForCampaignProcessing, } from "./tools/campaign-processing.js";
14
14
  import { createCampaign, duplicateCampaign, getCampaign, getCampaignMessagesPreview, getCampaigns, pauseCampaign, startCampaign, updateCampaign, updateCampaignBrief, } from "./tools/campaigns.js";
15
15
  import { queueCells, updateCell } from "./tools/cells.js";
@@ -184,13 +184,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
184
184
  case "resolve_campaign_fill_route":
185
185
  result = await resolveCampaignFillRoute(args);
186
186
  break;
187
- case "refill_campaign_sends":
188
- result = await refillCampaignSends(args);
189
- if (args?.mode === "apply" && Array.isArray(result?.appliedCampaignIds)) {
190
- for (const campaignId of result.appliedCampaignIds) {
191
- markCampaignContextDirty(campaignId, "refill_campaign_sends");
192
- }
193
- }
187
+ case "get_campaign_refill_state":
188
+ result = await getCampaignRefillState(args);
194
189
  break;
195
190
  case "fill_campaign_horizon":
196
191
  result = await fillCampaignHorizon(args);
@@ -0,0 +1,25 @@
1
+ type GetCampaignRefillStateInput = {
2
+ campaignId?: string;
3
+ tableId?: string;
4
+ };
5
+ export declare const campaignRefillStateToolDefinitions: {
6
+ name: string;
7
+ description: string;
8
+ inputSchema: {
9
+ type: string;
10
+ properties: {
11
+ campaignId: {
12
+ type: string;
13
+ description: string;
14
+ };
15
+ tableId: {
16
+ type: string;
17
+ description: string;
18
+ };
19
+ };
20
+ required: never[];
21
+ additionalProperties: boolean;
22
+ };
23
+ }[];
24
+ export declare function getCampaignRefillState(input: GetCampaignRefillStateInput): Promise<unknown>;
25
+ export {};
@@ -0,0 +1,32 @@
1
+ import { getApi } from "../api.js";
2
+ async function postCampaignRefillState(body) {
3
+ const api = getApi();
4
+ return api.post("/api/v3/mcp/campaign-refill-state", body);
5
+ }
6
+ export const campaignRefillStateToolDefinitions = [
7
+ {
8
+ name: "get_campaign_refill_state",
9
+ description: "read-only refill research primitive to call after resolve_campaign_fill_route and before any source import, message preparation, approval, scheduling, or horizon fill decision. It returns current campaign/table/source/sender/funnel/scheduler diagnostics plus freshness state for one exact campaignId or tableId only. This tool does not create rows, does not import leads, does not prepare messages, does not approve messages, does not schedule sends, does not launch campaigns, and does not expose direct campaign types as refillable targets. Exact targeting uses campaignId or tableId only; never pass campaign names or table names.",
10
+ inputSchema: {
11
+ type: "object",
12
+ properties: {
13
+ campaignId: {
14
+ type: "string",
15
+ description: "Optional exact CampaignOffer.id from resolve_campaign_fill_route. Do not pass campaign names.",
16
+ },
17
+ tableId: {
18
+ type: "string",
19
+ description: "Optional exact WorkflowTable.id from resolve_campaign_fill_route. Do not pass table names.",
20
+ },
21
+ },
22
+ required: [],
23
+ additionalProperties: false,
24
+ },
25
+ },
26
+ ];
27
+ export function getCampaignRefillState(input) {
28
+ return postCampaignRefillState({
29
+ campaignId: input.campaignId,
30
+ tableId: input.tableId,
31
+ });
32
+ }
@@ -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-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"];
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", "refill-sends-workflow", "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-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"];
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", "refill-sends-workflow", "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-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"];
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", "refill-sends-workflow", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
220
220
  description: string;
221
221
  };
222
222
  assetPath: {
@@ -37,6 +37,7 @@ export const ALLOWED_SUBSKILL_PROMPT_NAMES = [
37
37
  "interview",
38
38
  "load-voice",
39
39
  "refresh-sender-engagement",
40
+ "refill-sends-workflow",
40
41
  "research",
41
42
  "research-prospect",
42
43
  "research-sender",
@@ -371,7 +372,7 @@ export function getPostFindLeadsScoutRegistry() {
371
372
  codex: 'After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not spawn anything before that question. After the answer, launch only Message Drafting. The filter-choice answer is the post-import user gate for this single worker; do not ask another question about starting it in step-wise or YOLO mode. The registry lookup is not a launch: after get_post_find_leads_scout_registry, immediately invoke Task/spawn_agent or the host background-agent mechanism before loading filter-leads.md, before saving rubrics, and before treating skip-filters as ready for message review. Both choices route through this kickoff; do not let filters_skipped jump straight from filter-choice to message-generation. If filters are chosen, the parent stays on Filter Rules and drafts/saves rubrics with MCP tools while Message Drafting runs in the background. If filters are skipped, move to Messages/message review only after Message Drafting has started or is ready; update_campaign(currentStep=messages) is not proof of launch. If the named Message Drafting custom agent is unavailable, spawn a generic gpt-5.5 xhigh Message Drafting background agent with the same lean campaign/table basis. When the background worker starts, persist workerDetails.messageDraftBuilder with statusSource "branch", status "branch-running", runId, startedAt, updatedAt, basisToken when known, and basis containing campaignId, selectedLeadListId, workflowTableId, filterChoice, and reviewBatchRowHash or reviewBatchRowIds; workerStatuses.messageDraftBuilder may be "running" as a simple badge only. Never put rich proof under workerStatuses and never use workerStatuses.messageDrafting. If no background-agent tool is callable, start the same full message branch inline before filter drafting or before skip-filter message review, record workerDetails.messageDraftBuilder with statusSource "parent-thread-fallback" and status "fallback-active", and require the same live context, prompt, assets, and validation gate before message review; do not wait until filters are saved and then call the registry.',
372
373
  claude: "After confirm_lead_list copies source rows and the initial campaign-table execution slice exists, ask the filter-choice question immediately. Do not invoke any Task/Agent before that question. After the answer, invoke only Message Drafting. If filters are chosen, parent drafts/saves rubrics with MCP tools while Message Drafting runs, asks filter approval, then joins Message Drafting. If filters are skipped, invoke only Message Drafting and move to Messages/message review.",
373
374
  parentThreadRule: 'Named agents are optional acceleration, but message drafting is not optional. The only normal background worker is Message Drafting. The filter-choice answer is the campaign-scoped go-ahead for this single post-import worker; do not ask another question to start it in step-wise or YOLO mode. If a named agent is unavailable, use a generic gpt-5.5 xhigh Message Drafting background agent. source work and filter work stay in the parent thread with MCP tools. If post-find-leads-message-scout is available, run it as the background Message Draft Builder after the filter-choice answer. The registry lookup is not a launch: get_post_find_leads_scout_registry only identifies the worker, and Message Drafting counts as started only after Task/spawn_agent or the host background-agent tool is invoked, or after the parent begins the same full message branch inline because no background-agent tool is callable. This launch must happen before loading filter-leads.md, save_rubrics, filter approval, or skip-filter message review; currentStep=messages is not proof of launch. If post-find-leads-message-scout is absent, do not customer-surface install status. Do not silently treat message drafting as started; the main thread must either launch the background worker or execute the same message branch from CampaignOffer state, selected source state, workflowTableId, and initial campaign-table execution slice rows. For a spawned worker, record workerDetails.messageDraftBuilder with statusSource branch / status branch-running, runId, startedAt, updatedAt, and basis containing campaignId, selectedLeadListId, workflowTableId, filterChoice, and reviewBatchRowHash or reviewBatchRowIds. workerStatuses.messageDraftBuilder is optional simple badge text only ("running", "ready", "blocked", "idle"); never put runId/statusSource/basis under workerStatuses and never use workerStatuses.messageDrafting. If no background-agent tool is callable, start that same full message branch inline before filter drafting or before skip-filter message review, record workerDetails.messageDraftBuilder with statusSource parent-thread-fallback / status fallback-active then ready, and require the same live context, prompt, assets, and validation gate before message review; do not report that as a background worker failure. If neither branch nor inline fallback can run, return blocked/retry-needed; do not wait until filters are saved and then call the registry. The Message Drafting handoff must be lean. Do not paste copied row counts, brief hashes, review-batch hashes, full reviewBatchRowIds, broad row data, or local debug artifacts into the spawn prompt. Local markdown/json files are not normal-path inputs. The filter-choice question is the first post-import user gate; do not load post-lead registries or filter references before it. Message drafting starts after the filter-choice answer, must load get_subskill_prompt({ subskillName: "generate-messages" }), and must load every required message asset named by generate-messages Mode 0 through get_subskill_asset before drafting. Reference Asset Loading means loading the required pre-draft reference pack before drafting; return blocked/retry-needed if required assets cannot be loaded; load ai-tells.md because it is never optional. The branch or parent-thread fallback loads the full generate-messages prompt and every referenced asset through get_subskill_asset. After generating/revising the candidate and before returning ready, must load get_subskill_prompt({ subskillName: "create-campaign-v2-validation" }) as the final internal validation gate, must read live campaign table state through scoped MCP/product tools, and must reject mismatched selectedLeadListId/workflowTableId/campaign/workspace input. Do not block when filters were chosen but leadScoringRubrics are not yet visible in the branch read; the parent owns save_rubrics and filter approval in parallel, so Message Drafting should return status ready with basisStatus usable_initial when campaign/list/table identity and the non-empty execution slice match. Do not use any alternate, local-artifact, or examples-only message prompt. User copy feedback, message QA, or rewrite requests before approve-message must be routed back to Message Drafting with the current recommendation, lean campaign/table basis, and latest user text; the parent must not rewrite or QA the template from memory and must not call update_campaign_brief before approve-message. The worker validates internally and returns only templateRecommendation, tokenFillRules, renderedGoodSample, status, approveOrReviseRecommendation, validationStatus, outputAt, outputHash, and blocked/retry detail. Do not render renderedFallbackSample, risk notes, or a qaReceipt on the normal happy path. On the filter path, save_rubrics keeps the browser on Filter Rules after save_rubrics so the user can approve the saved criteria; after saved-filter approval, move to Filter Leads with currentStep=apply-icp-rubric whether Message Drafting is ready or still running. Wait there for message approval. Enrichment, filtering, Generate Message cells, sender setup, sequence attach, and launch wait for template approval on the Use Template path. On the skip path, move to Messages/message review after Message Drafting has started or is ready and wait for message approval before enrichment or Settings. Do not render message review from checklist or shortcut instructions; message review requires a messageDraftRecommendation whose basis proves the generate-messages prompt, required message assets, and validation gate ran for the current campaign/table execution slice. Do not automatically rerun Message Drafting after filters/enrichment finish; show the initial draft by default and offer an enriched rewrite only with explicit user opt-in. Handoff and recommendation output are Markdown with labeled fields, not raw JSON.',
374
- prepareMessagesRule: `Default create-campaign stays on the existing reviewBatchLimit:15 first campaign-table execution slice. For plain post-mint fill/load/refill requests, first call refill_campaign_sends({ mode:"plan", intent:"plain" }). Plain fill is not an alias for fill_campaign_horizon or campaign creation. If no --yolo is present, report the plan and stop. If --yolo is present, call plan first, inspect blockers, then call refill_campaign_sends({ mode:"apply", yolo:true, planRevision, actionIds }) using only selected immutable actionIds from the fresh plan. Use resolve_campaign_fill_route, fill_campaign_horizon, and start_campaign_message_preparation only as fallback/lower-level diagnostics when refill_campaign_sends is unavailable or when the refill command returns an evergreen subplan. fill_campaign_horizon is evergreen-only and is not the generic regular-campaign fill path. Treat "fill up/load sends" as capacity-fill preparation: calculate the bounded target from sender capacity when needed, then let the command plan source, enrichment, ICP/rubric, Generate Message propagation, approval/ready state, scheduler reread, and blockers. Do not interpret checkedRows as enriched rows; it is only the table cursor. Prepared, approved, and ready_to_schedule rows are intermediate states; never call them scheduled unless a re-read proves scheduler-owned scheduled cells with non-null scheduledFor. For "approve X messages", use approvalMode:approve only when explicitly requested, but still do not launch. For "schedule X sends" or "fill sender sends", approve only when explicitly requested, then re-read campaign/table scheduled counts; if scheduler-owned scheduledFor cells are not present, report prepared/approved/ready - awaiting scheduler instead of success. Do not call start_campaign as part of refill sends. Launch/start is a separate explicit human action after the operator intentionally wants sends to go out, and it must still verify that the bounded cohort is the only approved cohort and must not broad approve-all. campaignId is CampaignOffer.id. If the user asks to stop preparation, the target is wrong, or status shows the wrong campaign/table, use cancel_campaign_message_preparation only for the exact active job. Low-level selectors are diagnostics and recovery only for this lane. start_campaign remains forbidden until explicit launch/start approval outside refill sends.`,
375
+ prepareMessagesRule: `Default create-campaign stays on the existing reviewBatchLimit:15 first campaign-table execution slice. For plain post-mint fill/load/refill requests, load get_subskill_prompt({ subskillName: "refill-sends-workflow" }), then call resolve_campaign_fill_route and get_campaign_refill_state before any mutation. Plain fill is not an alias for fill_campaign_horizon or campaign creation. fill_campaign_horizon is evergreen-only and is not the generic regular-campaign fill path. Treat "fill up/load sends" as capacity-fill preparation: calculate the bounded target from sender capacity when needed, then use the refill workflow to decide same-campaign source replenishment, enrichment/prep, approval/ready state, scheduler reread, and blockers. Do not interpret checkedRows as enriched rows; it is only the table cursor. Prepared, approved, and ready_to_schedule rows are intermediate states; never call them scheduled unless a re-read proves scheduler-owned scheduled cells with non-null scheduledFor. Before source import, prep, or approval, require exact visible approval and a fresh get_campaign_refill_state reread; stop if freshness.stateHash or exact ids changed. For "approve X messages", use approvalMode:approve only when explicitly requested, but still do not launch. For "schedule X sends" or "fill sender sends", approve only when explicitly requested, then re-read campaign/table scheduled counts; if scheduler-owned scheduledFor cells are not present, report prepared/approved/ready - awaiting scheduler instead of success. Do not call start_campaign as part of refill sends. Launch/start is a separate explicit human action after the operator intentionally wants sends to go out, and it must still verify that the bounded cohort is the only approved cohort and must not broad approve-all. campaignId is CampaignOffer.id. If the user asks to stop preparation, the target is wrong, or status shows the wrong campaign/table, use cancel_campaign_message_preparation only for the exact active job. Low-level selectors are diagnostics and recovery only for this lane. start_campaign remains forbidden until explicit launch/start approval outside refill sends.`,
375
376
  },
376
377
  };
377
378
  }
@@ -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-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"];
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", "refill-sends-workflow", "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-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"];
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", "refill-sends-workflow", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
275
275
  description: string;
276
276
  };
277
277
  assetPath: {
@@ -660,82 +660,6 @@ export declare const allTools: ({
660
660
  additionalProperties: boolean;
661
661
  required?: undefined;
662
662
  };
663
- } | {
664
- name: string;
665
- description: string;
666
- inputSchema: {
667
- type: string;
668
- properties: {
669
- mode: {
670
- type: string;
671
- enum: string[];
672
- default: string;
673
- description: string;
674
- };
675
- intent: {
676
- type: string;
677
- enum: string[];
678
- description: string;
679
- };
680
- campaignIds: {
681
- type: string;
682
- items: {
683
- type: string;
684
- };
685
- maxItems: number;
686
- description: string;
687
- };
688
- tableIds: {
689
- type: string;
690
- items: {
691
- type: string;
692
- };
693
- maxItems: number;
694
- description: string;
695
- };
696
- sendWindowDays: {
697
- type: string;
698
- minimum: number;
699
- maximum: number;
700
- description: string;
701
- };
702
- targetDates: {
703
- type: string;
704
- items: {
705
- type: string;
706
- };
707
- maxItems: number;
708
- description: string;
709
- };
710
- targetScheduledSends: {
711
- type: string;
712
- minimum: number;
713
- maximum: number;
714
- description: string;
715
- };
716
- approvalMode: {
717
- type: string;
718
- enum: string[];
719
- description: string;
720
- };
721
- yolo: {
722
- type: string;
723
- description: string;
724
- };
725
- planRevision: {
726
- type: string;
727
- description: string;
728
- };
729
- actionIds: {
730
- type: string;
731
- items: {
732
- type: string;
733
- };
734
- description: string;
735
- };
736
- };
737
- additionalProperties: boolean;
738
- };
739
663
  } | {
740
664
  name: string;
741
665
  description: string;
@@ -5,8 +5,8 @@ import { campaignAbTestToolDefinitions } from "./campaign-ab-test.js";
5
5
  import { campaignFillRoutingToolDefinitions } from "./campaign-fill-routing.js";
6
6
  import { campaignHorizonFillToolDefinitions } from "./campaign-horizon-fill.js";
7
7
  import { campaignMessagePreparationToolDefinitions } from "./campaign-message-preparation.js";
8
+ import { campaignRefillStateToolDefinitions } from "./campaign-refill-state.js";
8
9
  import { campaignProcessingToolDefinitions } from "./campaign-processing.js";
9
- import { refillCampaignSendsToolDefinitions } from "./refill-campaign-sends.js";
10
10
  import { campaignToolDefinitions } from "./campaigns.js";
11
11
  import { cellToolDefinitions } from "./cells.js";
12
12
  import { startCliLoginToolDef, waitForCliLoginToolDef } from "./cli-login.js";
@@ -46,7 +46,7 @@ export const allTools = [
46
46
  ...campaignToolDefinitions,
47
47
  ...campaignAbTestToolDefinitions,
48
48
  ...campaignFillRoutingToolDefinitions,
49
- ...refillCampaignSendsToolDefinitions,
49
+ ...campaignRefillStateToolDefinitions,
50
50
  ...campaignHorizonFillToolDefinitions,
51
51
  ...campaignMessagePreparationToolDefinitions,
52
52
  ...campaignProcessingToolDefinitions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.337",
3
+ "version": "0.1.339",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -47,8 +47,8 @@ allowed-tools:
47
47
  - mcp__sellable__record_campaign_review_batch
48
48
  - mcp__sellable__queue_campaign_cells
49
49
  - mcp__sellable__wait_for_campaign_processing
50
- - mcp__sellable__refill_campaign_sends
51
50
  - mcp__sellable__resolve_campaign_fill_route
51
+ - mcp__sellable__get_campaign_refill_state
52
52
  - mcp__sellable__fill_campaign_horizon
53
53
  - mcp__sellable__start_campaign_message_preparation
54
54
  - mcp__sellable__get_campaign_message_preparation_status
@@ -118,44 +118,35 @@ The default path stays the existing first campaign-table execution slice:
118
118
  review the normal `reviewBatchLimit:15`, approve reviewed draft rows, then move
119
119
  to Settings/sequence/final greenlight. For any plain post-mint fill request
120
120
  such as "fill campaigns", "fill up", "refill sends", "max out sends", or
121
- "load sends", first call `refill_campaign_sends({ mode:"plan", intent:"plain" })`.
122
- If the user did not include `--yolo`, report the plan and stop. If the user did
123
- include `--yolo`, call plan first, inspect blockers, then call
124
- `refill_campaign_sends({ mode:"apply", yolo:true, planRevision, actionIds })`
125
- using only the selected immutable action IDs from the fresh plan. Plain fill is
126
- not an alias for `fill_campaign_horizon` or campaign creation. Keep
127
- `resolve_campaign_fill_route`, `fill_campaign_horizon`, and
128
- `start_campaign_message_preparation` as fallback/lower-level diagnostics only
129
- when `refill_campaign_sends` is unavailable or when the refill command itself
130
- returns an evergreen subplan. `fill_campaign_horizon` is evergreen-only and
131
- must not be used for regular campaign refill. When more leads are needed, the
132
- plan must recommend same-campaign source-ladder replenishment; do not create
133
- warm-post-engager side campaigns, on-demand campaigns, or unrelated campaigns.
134
- If fallback `resolve_campaign_fill_route` returns `route:"ask_create"`, ask
135
- whether to create a normal campaign or evergreen campaigns; campaign creation is
136
- never the default response to plain fill.
121
+ "load sends", hand off to the skill-led refill workflow:
122
+
123
+ ```text
124
+ get_subskill_prompt({ subskillName: "refill-sends-workflow" })
125
+ resolve_campaign_fill_route
126
+ get_campaign_refill_state
127
+ ```
128
+
129
+ Plain fill is not an alias for `fill_campaign_horizon` or campaign creation.
130
+ `fill_campaign_horizon` is evergreen-only and must not be used for regular
131
+ campaign refill. If route resolution returns `route:"ask_create"`, ask whether
132
+ to create a normal campaign or evergreen campaigns; campaign creation is never
133
+ the default response to plain fill. When more leads are needed, the refill
134
+ workflow must recommend same-campaign source-ladder replenishment; do not
135
+ create warm-post-engager side campaigns, on-demand campaigns, or unrelated
136
+ campaigns.
137
+
137
138
  Treat active fills as capacity-fill preparation: calculate the bounded target
138
- from sender capacity when needed, then let the preparation job queue pending
139
- `Enrich Prospect` cells, wait for ICP/rubric and Generate Message cells to
140
- cascade, and mark ready or approve only the target cohort. Do not count
141
- `checkedRows` as enriched rows; it is only the table cursor. Use
142
- `progress.enrichedRows`, `progress.needsEnrichRows`, `activeCellCount`,
143
- `preparedMessages`, `approvedRows`, active preparation jobs, sender-health
144
- blockers, target, estimated row budget remaining, and `stopReason` to explain
145
- progress. If the user says "prepare/generate X messages", set
146
- `targetPreparedMessages:X`, omit `maxRowsToCheck`, and keep
147
- `approvalMode:"mark_ready"`. The backend calibrates on at least 100 actually
148
- enriched rows, estimates the row budget from observed rubric/pass yield, caps
149
- `maxRowsToCheck` at 300, then continues in batches capped at 100 newly checked
150
- rows. It will not pull another row batch while the current checked batch still
151
- has queueable or active cells. If the user says "approve X messages", use
152
- `approvalMode:"approve"` but still do not launch. If the user says "schedule X
153
- sends" or asks to fill sender sends, use `approvalMode:"approve"` only when
154
- the user explicitly asked for approval, approve exactly the bounded X-message
155
- cohort during preparation, then re-read scheduled counts; if scheduler-owned
156
- cells are not present, report prepared/approved/ready awaiting scheduler
157
- instead of success. Final launch remains a separate explicit user greenlight
158
- and must verify that bounded cohort and must not broad approve-all.
139
+ from sender capacity when needed, then use the refill workflow to decide source
140
+ replenishment, enrichment/prep, approval policy, and scheduler proof. Mutation
141
+ requires exact visible approval and a fresh `get_campaign_refill_state` reread.
142
+ If the user says "prepare/generate X messages", use message-prep primitives with
143
+ `targetPreparedMessages:X` and default `approvalMode:"mark_ready"`. If the user
144
+ says "approve X messages", use `approvalMode:"approve"` only for the bounded
145
+ cohort and still do not launch. If the user says "schedule X sends", approve
146
+ only when explicitly requested, then reread scheduled counts; if
147
+ scheduler-owned cells are not present, report prepared/approved/ready awaiting
148
+ scheduler instead of success. Final launch remains a separate explicit user
149
+ greenlight and must not broad approve-all.
159
150
  When approving reviewed draft rows in the campaign table, resolve the actual
160
151
  visible `Approved` cells with `select_campaign_cells({ columnRole: "approved",
161
152
  rowSelector: { type: "rowIds", rowIds } })` and `update_cell` those returned
@@ -1,94 +1,33 @@
1
1
  ---
2
2
  name: fill-send-horizon
3
- description: Compatibility alias for refill-sends. Plain fill, refill sends, max out sends, load everyone up, and fill horizon sends now route to refill_campaign_sends plan mode first. Legacy fill_campaign_horizon remains evergreen-only lower-level audit/apply.
3
+ description: Compatibility alias for refill-sends. Fill horizon requests load the skill-led refill workflow; fill_campaign_horizon remains evergreen-only lower-level execution.
4
4
  visibility: internal
5
5
  allowed-tools:
6
- - mcp__sellable__refill_campaign_sends
6
+ - mcp__sellable__get_subskill_prompt
7
7
  - mcp__sellable__resolve_campaign_fill_route
8
+ - mcp__sellable__get_campaign_refill_state
8
9
  - mcp__sellable__fill_campaign_horizon
9
10
  ---
10
11
 
11
12
  # Fill Send Horizon
12
13
 
13
- <role>
14
- This is a compatibility alias. For plain operator language, use the new refill
15
- command first: `refill_campaign_sends({ mode: "plan", intent: "plain" })`.
16
- The word "horizon" means send window. Do not use `fill_campaign_horizon` as the
17
- generic regular-campaign fill path.
18
- </role>
19
-
20
- <inputs>
21
- The invoking prompt (often a scheduled automation) names the targets or gives a
22
- plain fill request. Route before acting:
23
-
24
- - Plain fill/load requests ("fill campaigns", "load everyone up", "fill send horizon") — first call `refill_campaign_sends({ mode: "plan", intent: "plain" })`.
25
- - If no `--yolo`, report the plan and stop.
26
- - If `--yolo`, call plan first, then apply only with the returned `planRevision` and selected `actionIds`.
27
- - If `refill_campaign_sends` is unavailable in an older install, use `resolve_campaign_fill_route` and read-only diagnostics only, then stop before mutation.
28
-
29
- Optional inputs: `targetPreparedMessages` (default: leave unset for the adaptive default), an explicit "approve" instruction (see safety rules), and a **waterfall priority order** (see below).
30
- </inputs>
31
-
32
- <objective>
33
- Route result handling:
34
-
35
- 1. If `refill_campaign_sends` returns regular campaigns, report the ranked plan.
36
- 2. If it returns evergreen campaigns, report the current-source audit and any legacy evergreen send-window subplan.
37
- 3. If it returns blockers, report them and stop.
38
- 4. If `--yolo` is approved, apply only selected immutable actions from the plan.
39
-
40
- For each active campaign target:
41
-
42
- 1. Confirm workspace, `workflowTableId`, `campaignStatus:"ACTIVE"`, sender IDs, current stats, and scheduled/ready/approved/prepared counts where available. Skip and report PAUSED/DRAFT/ARCHIVED targets separately.
43
- 2. Check `get_campaign_message_preparation_status({ campaignId, tableId })` first. If an active prep job exists for the same target, report it and do not start an overlapping job.
44
- 3. If rows already exist, let `refill_campaign_sends` plan/apply the bounded preparation action. Do not call lower-level preparation manually unless the command is unavailable.
45
- 4. If source is thin and the user asked to add/fill more leads, source fresh Signal Discovery/post engagement leads only into the same `campaignOfferId`/`campaignId` context. Do not create warm-post-engager side campaigns, on-demand campaigns, or unrelated campaigns.
46
- 5. Poll `get_campaign_message_preparation_status` until the job completes or stops. Read `progress.enrichedRows`, `preparedMessages`, `approvedMessages`, `activeCellCount`, active prep blockers, and `stopReason`; never treat `checkedRows` as enriched rows.
47
- 6. After any prep/approval work, re-read campaign/table state. Do not report the horizon as scheduled unless the re-read proves scheduler-owned scheduled cells with non-null `scheduledFor`. If only prepared/approved/ready cells exist, report the exact state as `prepared/approved/ready - awaiting scheduler`, not complete.
48
- 7. Report appended rows, enriched rows, prepared messages, approved messages, ready-to-schedule rows, scheduler-owned scheduled cells, active preparation jobs, and sender-health blockers such as disconnected Sales Nav, deleted LinkedIn accounts, missing sequence state, or exhausted source rows as separate lines.
49
- </objective>
50
-
51
- <waterfall>
52
- ## Campaign hierarchy (waterfall order)
53
-
54
- Evergreen/horizon fill order is **stored workspace state** returned by
55
- `resolve_campaign_fill_route`, not something each automation restates. Resolve
56
- it in this precedence:
57
-
58
- 1. **Stored hierarchy (default)**: the resolver reads the stored managed waterfall and returns slots in priority order with campaign offer IDs included. Fill in that order.
59
- 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.
60
- 3. **No stored hierarchy**: resolver returns active targets or ask-create. Do not invent a conventional evergreen waterfall. Ask whether to create normal campaigns or evergreen campaigns only after `ask_create`.
61
-
62
- Fill mechanics, whatever the source of the order:
63
-
64
- - **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.
65
- - **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.
66
- - **Report per lane** so the operator can see the waterfall working:
14
+ This is a compatibility alias. For plain operator language such as "fill",
15
+ "refill sends", "max out sends", "load everyone up", or "fill horizon sends",
16
+ load the canonical workflow first:
67
17
 
18
+ ```text
19
+ get_subskill_prompt({ subskillName: "refill-sends-workflow" })
68
20
  ```
69
- csreyes92 waterfall (stored order): Post Engagers filled 4/4 (horizon met — lower lanes skipped)
70
- thomas waterfall (stored order): Post Engagers 1/4 (source thin) → Shared Signal Discovery 3/3 remaining
71
- ```
72
- </waterfall>
73
21
 
74
- <safety>
75
- - **Never call `start_campaign`, `start_on_demand_campaign`, or `start_direct_campaign`.** Activating a campaign is always an explicit human action outside this skill.
76
- - 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.
77
- - Auto-approval is not scheduling. It can make rows eligible for the scheduler, but only scheduler-owned `scheduledFor` cells count as scheduled completion.
78
- - Never use a sender from one workspace for another workspace's campaign. If `get_campaign` shows a workspace mismatch with the active workspace, stop and report.
79
- - Paid InMail: never opt a campaign into paid-InMail templates or spend credits. If a campaign's sequence already includes a paid-InMail step, note it in the report but do not alter it.
80
- - Do not write `WorkflowTableCell.scheduledFor` directly. Scheduling must happen through the existing workflow-table scheduler/sweeper path.
81
- - This skill fills readiness and verifies scheduling; the product's sweeper sends only from ACTIVE campaigns, only approved rows, only inside sending hours. Say so in the report so the operator knows nothing went out.
82
- </safety>
22
+ Then follow the workflow:
83
23
 
84
- <output>
85
- One compact report:
24
+ 1. Call `resolve_campaign_fill_route`.
25
+ 2. Call `get_campaign_refill_state` for the exact selected target.
26
+ 3. Treat public targets as regular campaigns or evergreen campaigns only.
27
+ 4. Stop on `unsupported_campaign_type` for internal direct campaign tables.
28
+ 5. Use `fill_campaign_horizon` only after route and refill-state evidence prove
29
+ the target is an evergreen campaign.
86
30
 
87
- ```
88
- Fill Send Horizon {date}
89
- • {Campaign A}: scheduled {s}/{target} for the horizon ({r} ready, {p} prepared, {a} approved) — {state}
90
- • {Campaign B}: source thin after {k} prepared — refill via refresh-sender-engagement
91
- Next operator action: {approve messages | wait for scheduler | refresh source | launch separately if you intentionally want sends to go out}
92
- Nothing was sent or launched; campaigns remain in their current status. Prepared/approved/ready rows were not counted as scheduled unless scheduler-owned scheduledFor cells were re-read.
93
- ```
94
- </output>
31
+ Do not start or launch campaigns. Do not raw-write scheduler fields. Do not
32
+ claim scheduled success without rereading scheduler-owned cells with non-null
33
+ `scheduledFor`.
@@ -1,57 +1,54 @@
1
1
  ---
2
2
  name: refill-sends
3
- description: Plan and optionally apply bounded campaign send refill work. Plain fill, refill sends, max out sends, load everyone up, and fill send window requests route to refill_campaign_sends plan mode first. Apply requires --yolo, a fresh planRevision, and selected actionIds.
4
- visibility: internal
3
+ description: Plan regular campaign and evergreen campaign send refill work through the skill-led refill workflow.
4
+ visibility: public
5
5
  allowed-tools:
6
- - mcp__sellable__refill_campaign_sends
6
+ - mcp__sellable__get_subskill_prompt
7
+ - mcp__sellable__search_subskill_prompts
7
8
  - mcp__sellable__resolve_campaign_fill_route
9
+ - mcp__sellable__get_campaign_refill_state
8
10
  - mcp__sellable__fill_campaign_horizon
9
11
  - mcp__sellable__get_campaign
10
12
  - mcp__sellable__get_campaign_message_preparation_status
13
+ - mcp__sellable__start_campaign_message_preparation
14
+ - mcp__sellable__cancel_campaign_message_preparation
15
+ - mcp__sellable__import_leads
16
+ - mcp__sellable__wait_for_lead_list_ready
17
+ - mcp__sellable__confirm_lead_list
18
+ - mcp__sellable__search_signals
19
+ - mcp__sellable__fetch_post_engagers
20
+ - mcp__sellable__search_sales_nav
21
+ - mcp__sellable__lookup_sales_nav_filter
22
+ - mcp__sellable__search_prospeo
23
+ - mcp__sellable__search_prospeo_companies
24
+ - mcp__sellable__confirm_prospeo_company_accounts
25
+ - mcp__sellable__load_csv_linkedin_leads
26
+ - mcp__sellable__load_csv_domains
27
+ - mcp__sellable__list_dnc_entries
28
+ - mcp__sellable__load_csv_dnc_entries
29
+ - mcp__sellable__get_rows
30
+ - mcp__sellable__get_rows_minimal
31
+ - mcp__sellable__get_table_rows
11
32
  ---
12
33
 
13
34
  # Refill Sends
14
35
 
15
- Use this skill for plain operator requests such as "fill", "refill sends",
36
+ Use this public wrapper for plain operator requests such as "fill", "refill sends",
16
37
  "max out sends", "load everyone up", or "fill horizon sends".
17
38
 
18
- ## Default Flow
39
+ Load the internal workflow prompt before taking any operational step:
19
40
 
20
- 1. Call `refill_campaign_sends({ mode: "plan", intent: "plain" })`.
21
- 2. Report the returned plan and stop when the user did not include `--yolo`.
22
- 3. If the user included `--yolo`, still call plan first. Inspect blockers.
23
- 4. Apply only by calling `refill_campaign_sends({ mode: "apply", yolo: true, planRevision, actionIds })` with action IDs copied from the fresh plan.
41
+ ```text
42
+ get_subskill_prompt({ subskillName: "refill-sends-workflow" })
43
+ ```
24
44
 
25
- Do not manually chain lower-level tools unless `refill_campaign_sends` is
26
- unavailable. In that fallback case, run read-only diagnostics only, then stop
27
- before mutation.
28
-
29
- ## Public Concepts
30
-
31
- - Regular campaign: a campaign with a brief, source, sequence, senders, and a
32
- campaign-backed workflow table.
33
- - Evergreen campaign: a managed always-on send-window lane.
34
- - Send window: the dates/capacity the operator wants kept full.
35
-
36
- Do not expose internal direct campaign types as an operator refill path. If the
37
- plan reports `unsupported_campaign_type`, report it and stop.
38
-
39
- ## Safety Rules
40
-
41
- - Default mode is read-only plan.
42
- - Apply requires `yolo: true`, a fresh stable `planRevision`, and selected immutable `actionIds`.
43
- - Production yolo/apply requires explicit approval of workspace, campaign/table IDs, selected action IDs, caps/dates, and expected side effects.
44
- - Never create side campaigns.
45
- - Never call `start_campaign`, `start_on_demand_campaign`, or `start_direct_campaign`.
46
- - Never raw-write scheduled fields or sender assignment fields.
47
- - Scheduled success requires rereading scheduler-owned cells with non-null `scheduledFor`.
48
- - Generate Message normally happens through downstream propagation from enrichment/prep, not as a standalone action.
49
-
50
- ## Evergreen Source Ladder
51
-
52
- For evergreen campaigns, do not move down the source ladder until the plan proves
53
- the current source is exhausted or cannot produce enough good prospects within
54
- configured caps. Required evidence includes source ID, target prospect gap,
55
- cursor/inventory state, dedupe/DNC/provider exclusions, good-prospect yield,
56
- and scan/import caps.
45
+ Then follow that workflow exactly. The default path is read-only research:
46
+ resolve the route, read target refill state, report the next safe step, and stop
47
+ before mutation unless the user has explicitly approved the exact workspace,
48
+ campaign/table/source ids, caps/dates, approval mode, expected side effects,
49
+ and stop/rollback condition.
57
50
 
51
+ Public concepts are regular campaign and evergreen campaign. Internal direct
52
+ campaign types are unsupported refill targets. `fill_campaign_horizon` is only a
53
+ legacy evergreen-only lower-level primitive after route and refill-state
54
+ evidence proves an evergreen target.
@@ -0,0 +1,172 @@
1
+ ---
2
+ name: refill-sends-workflow
3
+ description: Internal skill-led refill sends workflow for regular campaigns and evergreen campaigns.
4
+ visibility: internal
5
+ allowed-tools:
6
+ - mcp__sellable__resolve_campaign_fill_route
7
+ - mcp__sellable__get_campaign_refill_state
8
+ - mcp__sellable__fill_campaign_horizon
9
+ - mcp__sellable__get_campaign
10
+ - mcp__sellable__get_campaign_message_preparation_status
11
+ - mcp__sellable__start_campaign_message_preparation
12
+ - mcp__sellable__cancel_campaign_message_preparation
13
+ - mcp__sellable__import_leads
14
+ - mcp__sellable__wait_for_lead_list_ready
15
+ - mcp__sellable__confirm_lead_list
16
+ - mcp__sellable__search_signals
17
+ - mcp__sellable__fetch_post_engagers
18
+ - mcp__sellable__search_sales_nav
19
+ - mcp__sellable__lookup_sales_nav_filter
20
+ - mcp__sellable__search_prospeo
21
+ - mcp__sellable__search_prospeo_companies
22
+ - mcp__sellable__confirm_prospeo_company_accounts
23
+ - mcp__sellable__load_csv_linkedin_leads
24
+ - mcp__sellable__load_csv_domains
25
+ - mcp__sellable__list_dnc_entries
26
+ - mcp__sellable__load_csv_dnc_entries
27
+ - mcp__sellable__get_rows
28
+ - mcp__sellable__get_rows_minimal
29
+ - mcp__sellable__get_table_rows
30
+ ---
31
+
32
+ # Refill Sends Workflow
33
+
34
+ Default mode is read-only research. This workflow decides the next safe refill
35
+ step in the parent thread; backend tools remain narrow route, state, source,
36
+ prep, readiness, and evergreen primitives.
37
+
38
+ ## Entry
39
+
40
+ Plain phrases that enter here include "fill", "refill sends", "max out sends",
41
+ "load everyone up", and "fill horizon sends".
42
+
43
+ 1. Call `resolve_campaign_fill_route` first. Use `intent:"plain"` for generic
44
+ fill/load language, `intent:"active"` only when the user explicitly narrowed
45
+ to active regular campaigns, and `intent:"evergreen"` only when the user
46
+ explicitly asked for evergreen or horizon work.
47
+ 2. Pick exact ids from the resolver result. Do not use campaign names or table
48
+ names as target identifiers.
49
+ 3. Call `get_campaign_refill_state` for the selected `campaignId` or `tableId`.
50
+ This must be the next target-specific research primitive.
51
+ 4. Classify the target from refill state:
52
+ - regular campaign: campaign-backed workflow table with normal source,
53
+ sequence, senders, and send/refill stages.
54
+ - evergreen campaign: managed always-on lane with current source evidence.
55
+ - `unsupported_campaign_type`: internal direct campaign table; report the
56
+ blocker and stop without mutation.
57
+
58
+ ## Research Checklist
59
+
60
+ Use the refill-state response as the current facts receipt:
61
+
62
+ - campaign status/current step, workflow table id, table config type, and public
63
+ type;
64
+ - selected source list/provider identity, source id, target prospect gap,
65
+ cursor/inventory state, dedupe/DNC/provider exclusions, good-prospect yield,
66
+ scan/import caps, and proof;
67
+ - sender ids, healthy senders, disconnected senders, and daily capacity;
68
+ - row/stage counts: source available, rows, enriched, passed, generated,
69
+ approved, ready to schedule, scheduled;
70
+ - active message prep job;
71
+ - scheduler-owned scheduled counts by date/action from cells with non-null
72
+ `scheduledFor`;
73
+ - `freshness.stateHash` and exact campaign/table/workspace ids.
74
+
75
+ Empty or unsafe states stop with the blocker: no fillable target, missing or deleted campaign,
76
+ missing table, paused/archive campaign, zero sender capacity, zero source candidates,
77
+ zero viable rows, no approved/preparable rows, active prep job, awaiting
78
+ scheduler, or unsupported direct campaign type.
79
+
80
+ ## Regular Campaign Decision
81
+
82
+ For regular campaign workspaces, rank regular campaigns from route and
83
+ `get_campaign_refill_state` evidence. Choose the best existing refill target.
84
+ Do not create side campaigns, warm-post-engager side campaigns, on-demand campaigns,
85
+ or unrelated campaigns as the default answer to a fill request.
86
+
87
+ When the best regular campaign is source-exhausted, recommend same-campaign source-ladder replenishment.
88
+ Keep `selectedLeadListId` source identity distinct
89
+ from the campaign `workflowTableId`.
90
+
91
+ Source replenishment choices:
92
+
93
+ - existing source list reuse: append/confirm the existing source list when it
94
+ has enough eligible rows;
95
+ - CSV path: load CSV profiles/domains, then import into the same campaign/source
96
+ path;
97
+ - Sales Nav path: use Sales Nav lookup/search, then import selected prospects;
98
+ - Prospeo path: use account/person search as appropriate, then import selected
99
+ prospects;
100
+ - Signal Discovery path: search/select posts or engagers, import selected
101
+ source prospects into the same campaign/source-list path, then confirm
102
+ readiness.
103
+
104
+ Before any source mutation, show exact approval evidence: workspace id,
105
+ campaign id, workflow table id, source/provider id, source-list id when known,
106
+ import caps, target dates/caps, dedupe/DNC/provider exclusion expectations, and
107
+ expected row side effects. Then reread `get_campaign_refill_state`; if
108
+ `freshness.stateHash` or exact ids changed, stop and ask for a fresh approval.
109
+
110
+ After source import, call `wait_for_lead_list_ready` and `confirm_lead_list`
111
+ with the source-list id and campaign id. Imports append to the existing
112
+ campaign/source path; they must not reset campaign state, sequence, sender
113
+ assignment, current step, or watch state. If provider auth, rate limit, timeout,
114
+ partial import, duplicate-only import, DNC-only result, CSV parse failure,
115
+ readiness timeout, or `confirm_lead_list` failure occurs, stop before prep and
116
+ report the blocker/retry path.
117
+
118
+ ## Prep, Approval, And Scheduler Proof
119
+
120
+ Existing rows normally move through enrichment/prep/approval using
121
+ `start_campaign_message_preparation` and
122
+ `get_campaign_message_preparation_status`. Standalone Generate Message is not the normal refill action;
123
+ it is only for a manual/user-authored message path
124
+ that requires it.
125
+
126
+ Approval mode:
127
+
128
+ - default is `approvalMode:"mark_ready"`, not approve;
129
+ - use `approvalMode:"approve"` only when the user explicitly asks to approve
130
+ messages or schedule sends and has approved the exact bounded cohort;
131
+ - never launch/start a campaign from refill.
132
+
133
+ Before prep or approval mutation, reread `get_campaign_refill_state` and compare
134
+ `freshness.stateHash`, campaign id, table id, workspace id, source id, and caps.
135
+ If they changed, stop. A second concurrent refill run must see the first run's
136
+ import/prep state on reread and avoid duplicate work.
137
+
138
+ Scheduled success requires a post-action reread proving scheduler-owned cells
139
+ with non-null `scheduledFor`. Prepared, approved, and ready-to-schedule rows are
140
+ intermediate states; report them as awaiting scheduler unless scheduled cells
141
+ are present.
142
+
143
+ ## Evergreen Campaign Discipline
144
+
145
+ `fill_campaign_horizon` is a legacy evergreen-only lower-level primitive. Call it
146
+ only after `resolve_campaign_fill_route` and `get_campaign_refill_state` prove an
147
+ evergreen campaign target.
148
+
149
+ For evergreen campaigns, do not advance down the source ladder while the current source can produce enough good prospects.
150
+ The current source is exhausted, or
151
+ unable to produce enough good prospects, only when evidence includes source id,
152
+ target prospect gap, cursor/inventory state, dedupe/DNC/provider exclusions,
153
+ good-prospect yield, and scan/import caps. If that proof is missing, continue
154
+ the current source or ask for more research instead of walking the ladder.
155
+
156
+ ## Hard Safety Rules
157
+
158
+ - Never call start or launch tools.
159
+ - Never raw-write `scheduledFor`, `scheduledAt`, scheduled status, or sender
160
+ assignment fields.
161
+ - Never expose internal direct campaign types as an operator refill path.
162
+ - Never claim scheduled success without rereading scheduler-owned cells with
163
+ non-null `scheduledFor`.
164
+ - No mutation in production unless Christian explicitly approves exact
165
+ workspace, campaign/table ids, source/action ids, caps/dates, approval mode,
166
+ expected side effects, and rollback/stop condition.
167
+
168
+ ## Report
169
+
170
+ Report the selected target, target type, source state, blockers, exact next
171
+ primitive, approval required, and the freshness hash used. Say explicitly that
172
+ nothing was launched and nothing was sent.
@@ -1,92 +0,0 @@
1
- type RefillCampaignSendsInput = {
2
- mode?: "plan" | "apply";
3
- intent?: "plain" | "evergreen" | "active";
4
- campaignIds?: string[];
5
- tableIds?: string[];
6
- sendWindowDays?: number;
7
- targetDates?: string[];
8
- targetScheduledSends?: number;
9
- approvalMode?: "mark_ready" | "approve";
10
- yolo?: boolean;
11
- planRevision?: string;
12
- actionIds?: string[];
13
- };
14
- export declare const refillCampaignSendsToolDefinitions: {
15
- name: string;
16
- description: string;
17
- inputSchema: {
18
- type: string;
19
- properties: {
20
- mode: {
21
- type: string;
22
- enum: string[];
23
- default: string;
24
- description: string;
25
- };
26
- intent: {
27
- type: string;
28
- enum: string[];
29
- description: string;
30
- };
31
- campaignIds: {
32
- type: string;
33
- items: {
34
- type: string;
35
- };
36
- maxItems: number;
37
- description: string;
38
- };
39
- tableIds: {
40
- type: string;
41
- items: {
42
- type: string;
43
- };
44
- maxItems: number;
45
- description: string;
46
- };
47
- sendWindowDays: {
48
- type: string;
49
- minimum: number;
50
- maximum: number;
51
- description: string;
52
- };
53
- targetDates: {
54
- type: string;
55
- items: {
56
- type: string;
57
- };
58
- maxItems: number;
59
- description: string;
60
- };
61
- targetScheduledSends: {
62
- type: string;
63
- minimum: number;
64
- maximum: number;
65
- description: string;
66
- };
67
- approvalMode: {
68
- type: string;
69
- enum: string[];
70
- description: string;
71
- };
72
- yolo: {
73
- type: string;
74
- description: string;
75
- };
76
- planRevision: {
77
- type: string;
78
- description: string;
79
- };
80
- actionIds: {
81
- type: string;
82
- items: {
83
- type: string;
84
- };
85
- description: string;
86
- };
87
- };
88
- additionalProperties: boolean;
89
- };
90
- }[];
91
- export declare function refillCampaignSends(input: RefillCampaignSendsInput): Promise<unknown>;
92
- export {};
@@ -1,91 +0,0 @@
1
- import { getApi } from "../api.js";
2
- async function postRefillCampaignSends(body) {
3
- const api = getApi();
4
- return api.post("/api/v3/mcp/refill-campaign-sends", body);
5
- }
6
- export const refillCampaignSendsToolDefinitions = [
7
- {
8
- name: "refill_campaign_sends",
9
- description: "Default mode is plan and read-only. Use this as the canonical tool for plain fill, refill sends, max out sends, load everyone up, and fill send window requests. It returns a campaign send refill plan for regular campaigns and evergreen campaigns, including campaign/table ids, senders, source state, current evergreen source audit evidence, funnel counts, blockers, stable planRevision, and immutable actionIds. It does not create campaigns, create side campaigns, use unsupported_campaign_type internal targets, does not launch or start campaigns, spend paid InMail opt-in, and does not raw-write scheduler fields. mode apply requires yolo: true, the fresh planRevision from the immediately preceding plan, and selected actionIds only; apply rejects changed caps, source ids, target dates, approval mode, or selectors. Evergreen source fallback is evidence-gated: audit the current evergreen source first and recommend the next source-ladder option only after current-source exhaustion or insufficient good-prospect yield is proven. Scheduled completion is not claimed until scheduler-owned cells are reread with non-null scheduledFor.",
10
- inputSchema: {
11
- type: "object",
12
- properties: {
13
- mode: {
14
- type: "string",
15
- enum: ["plan", "apply"],
16
- default: "plan",
17
- description: 'Defaults to "plan"; plan mode is read-only and returns the plan of attack.',
18
- },
19
- intent: {
20
- type: "string",
21
- enum: ["plain", "evergreen", "active"],
22
- description: 'Use "plain" for generic fill/refill sends, "evergreen" for explicit evergreen, and "active" for exact active campaign refill.',
23
- },
24
- campaignIds: {
25
- type: "array",
26
- items: { type: "string" },
27
- maxItems: 25,
28
- description: "Optional exact CampaignOffer ids. Do not pass names.",
29
- },
30
- tableIds: {
31
- type: "array",
32
- items: { type: "string" },
33
- maxItems: 25,
34
- description: "Optional exact WorkflowTable ids. Do not pass names.",
35
- },
36
- sendWindowDays: {
37
- type: "number",
38
- minimum: 1,
39
- maximum: 30,
40
- description: "Optional send window size in days.",
41
- },
42
- targetDates: {
43
- type: "array",
44
- items: { type: "string" },
45
- maxItems: 30,
46
- description: "Optional ISO date keys for the send window.",
47
- },
48
- targetScheduledSends: {
49
- type: "number",
50
- minimum: 1,
51
- maximum: 500,
52
- description: "Optional bounded send target. This is not a launch request.",
53
- },
54
- approvalMode: {
55
- type: "string",
56
- enum: ["mark_ready", "approve"],
57
- description: "Planning preference. Use approve only when the user explicitly asked for approval; launch remains separate.",
58
- },
59
- yolo: {
60
- type: "boolean",
61
- description: "Required true for apply. Never use in production without explicit operator approval of exact workspace, campaign/table ids, action ids, caps/dates, and expected side effects.",
62
- },
63
- planRevision: {
64
- type: "string",
65
- description: "Required for apply. Copy from the fresh read-only plan response.",
66
- },
67
- actionIds: {
68
- type: "array",
69
- items: { type: "string" },
70
- description: "Required for apply. Select immutable actionIds from the fresh plan only.",
71
- },
72
- },
73
- additionalProperties: false,
74
- },
75
- },
76
- ];
77
- export function refillCampaignSends(input) {
78
- return postRefillCampaignSends({
79
- mode: input.mode ?? "plan",
80
- intent: input.intent,
81
- campaignIds: input.campaignIds,
82
- tableIds: input.tableIds,
83
- sendWindowDays: input.sendWindowDays,
84
- targetScheduledSends: input.targetScheduledSends,
85
- targetDates: input.targetDates,
86
- approvalMode: input.approvalMode,
87
- yolo: input.yolo,
88
- planRevision: input.planRevision,
89
- actionIds: input.actionIds,
90
- });
91
- }