@sellable/mcp 0.1.553 → 0.1.555

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.
@@ -662,6 +662,18 @@ export declare const allTools: ({
662
662
  enum: string[];
663
663
  description: string;
664
664
  };
665
+ targetDate: {
666
+ type: string;
667
+ description: string;
668
+ };
669
+ refillLaneKey: {
670
+ type: string;
671
+ description: string;
672
+ };
673
+ sourceEpoch: {
674
+ type: string;
675
+ description: string;
676
+ };
665
677
  disableLowPassRateStop: {
666
678
  type: string;
667
679
  description: string;
@@ -671,6 +683,7 @@ export declare const allTools: ({
671
683
  description: string;
672
684
  };
673
685
  jobId?: undefined;
686
+ expectedUpdatedAt?: undefined;
674
687
  };
675
688
  required: string[];
676
689
  additionalProperties: boolean;
@@ -705,11 +718,52 @@ export declare const allTools: ({
705
718
  autoContinue?: undefined;
706
719
  senderId?: undefined;
707
720
  actionType?: undefined;
721
+ targetDate?: undefined;
722
+ refillLaneKey?: undefined;
723
+ sourceEpoch?: undefined;
708
724
  disableLowPassRateStop?: undefined;
725
+ expectedUpdatedAt?: undefined;
709
726
  };
710
727
  additionalProperties: boolean;
711
728
  required?: undefined;
712
729
  };
730
+ } | {
731
+ name: string;
732
+ description: string;
733
+ inputSchema: {
734
+ type: string;
735
+ properties: {
736
+ jobId: {
737
+ type: string;
738
+ };
739
+ expectedUpdatedAt: {
740
+ type: string;
741
+ description: string;
742
+ };
743
+ workspaceId: {
744
+ type: string;
745
+ description?: undefined;
746
+ };
747
+ campaignId?: undefined;
748
+ tableId?: undefined;
749
+ targetPreparedMessages?: undefined;
750
+ maxRowsToCheck?: undefined;
751
+ batchSize?: undefined;
752
+ maxBatchRows?: undefined;
753
+ approvalMode?: undefined;
754
+ rowSelector?: undefined;
755
+ excludeRowIds?: undefined;
756
+ autoContinue?: undefined;
757
+ senderId?: undefined;
758
+ actionType?: undefined;
759
+ targetDate?: undefined;
760
+ refillLaneKey?: undefined;
761
+ sourceEpoch?: undefined;
762
+ disableLowPassRateStop?: undefined;
763
+ };
764
+ required: string[];
765
+ additionalProperties: boolean;
766
+ };
713
767
  } | {
714
768
  name: string;
715
769
  description: string;
@@ -7554,47 +7608,54 @@ export declare const allTools: ({
7554
7608
  type: string;
7555
7609
  description: string;
7556
7610
  };
7557
- };
7558
- required: string[];
7559
- additionalProperties: boolean;
7560
- };
7561
- } | {
7562
- name: string;
7563
- description: string;
7564
- inputSchema: {
7565
- type: string;
7566
- properties: {
7567
- workspaceId: {
7568
- type: string;
7569
- description: string;
7570
- };
7571
- runId: {
7611
+ expectedTargets: {
7572
7612
  type: string;
7573
- description: string;
7574
- };
7575
- fence: {
7576
- type: string;
7577
- description: string;
7613
+ maxItems: 25;
7614
+ items: {
7615
+ type: string;
7616
+ properties: {
7617
+ campaignId: {
7618
+ type: string;
7619
+ };
7620
+ tableId: {
7621
+ type: string;
7622
+ };
7623
+ refillLaneKey: {
7624
+ type: string;
7625
+ };
7626
+ branchActionTypes: {
7627
+ type: string;
7628
+ minItems: number;
7629
+ uniqueItems: boolean;
7630
+ items: {
7631
+ type: string;
7632
+ enum: string[];
7633
+ };
7634
+ };
7635
+ senderIds: {
7636
+ type: string;
7637
+ minItems: number;
7638
+ maxItems: 100;
7639
+ uniqueItems: boolean;
7640
+ items: {
7641
+ type: string;
7642
+ };
7643
+ };
7644
+ };
7645
+ required: string[];
7646
+ additionalProperties: boolean;
7647
+ };
7578
7648
  };
7579
- dryRun: {
7649
+ expectedTargetsHash: {
7580
7650
  type: string;
7581
7651
  description: string;
7582
7652
  };
7583
- approvalMode: {
7653
+ maxPlacements: {
7584
7654
  type: string;
7585
- enum: string[];
7655
+ minimum: number;
7656
+ maximum: 150;
7586
7657
  description: string;
7587
7658
  };
7588
- senderIds: {
7589
- type: string;
7590
- items: {
7591
- type: string;
7592
- };
7593
- };
7594
- intent: {
7595
- type: string;
7596
- enum: string[];
7597
- };
7598
7659
  };
7599
7660
  required: string[];
7600
7661
  additionalProperties: boolean;
@@ -34,10 +34,9 @@ import { onDemandToolDefinitions } from "./one-off.js";
34
34
  import { processingToolDefinitions } from "./processing.js";
35
35
  import { promptToolDefinitions } from "./prompts.js";
36
36
  import { readinessToolDefinitions } from "./readiness.js";
37
- import { refreshSenderEngagementToolDefinitions } from "./refresh-sender-engagement.js";
38
- import { refillSendsV2ToolDefinitions } from "./refill-sends-v2.js";
39
37
  import { refillSendsToolDefinitions } from "./refill-sends.js";
40
38
  import { refillTargetPlanToolDefinitions } from "./refill-target-plan.js";
39
+ import { refreshSenderEngagementToolDefinitions } from "./refresh-sender-engagement.js";
41
40
  import { rowToolDefinitions } from "./rows.js";
42
41
  import { rubricToolDefinitions } from "./rubrics.js";
43
42
  import { schedulerFillCapacityToolDefinitions } from "./scheduler-fill-capacity.js";
@@ -61,7 +60,6 @@ export const allTools = [
61
60
  ...schedulerFillCapacityToolDefinitions,
62
61
  ...schedulerRunToolDefinitions,
63
62
  ...refillSendsToolDefinitions,
64
- ...refillSendsV2ToolDefinitions,
65
63
  ...setupEvergreenCampaignsToolDefinitions,
66
64
  ...campaignHorizonFillToolDefinitions,
67
65
  ...campaignMessagePreparationToolDefinitions,
@@ -1,10 +1,23 @@
1
1
  type SchedulerRunAction = "run" | "status";
2
+ export declare const REFILL_SCHEDULER_EXPECTED_TARGET_LIMIT: 25;
3
+ export declare const REFILL_SCHEDULER_EXPECTED_TARGET_SENDER_LIMIT: 100;
4
+ export declare const REFILL_SCHEDULER_MAX_PLACEMENTS: 150;
5
+ export type SchedulerExpectedTargetInput = {
6
+ campaignId: string;
7
+ tableId: string;
8
+ refillLaneKey: string;
9
+ branchActionTypes: Array<"send_invite" | "send_inmail_open" | "send_inmail_closed">;
10
+ senderIds: string[];
11
+ };
2
12
  type RunSchedulerSweepInput = {
3
13
  workspaceId: string;
4
14
  action?: SchedulerRunAction;
5
15
  targetDate?: string;
6
16
  requestKey?: string;
7
17
  targetShapeRevision?: string;
18
+ expectedTargets?: SchedulerExpectedTargetInput[];
19
+ expectedTargetsHash?: string;
20
+ maxPlacements?: number;
8
21
  };
9
22
  export declare const schedulerRunToolDefinitions: {
10
23
  name: string;
@@ -33,6 +46,54 @@ export declare const schedulerRunToolDefinitions: {
33
46
  type: string;
34
47
  description: string;
35
48
  };
49
+ expectedTargets: {
50
+ type: string;
51
+ maxItems: 25;
52
+ items: {
53
+ type: string;
54
+ properties: {
55
+ campaignId: {
56
+ type: string;
57
+ };
58
+ tableId: {
59
+ type: string;
60
+ };
61
+ refillLaneKey: {
62
+ type: string;
63
+ };
64
+ branchActionTypes: {
65
+ type: string;
66
+ minItems: number;
67
+ uniqueItems: boolean;
68
+ items: {
69
+ type: string;
70
+ enum: string[];
71
+ };
72
+ };
73
+ senderIds: {
74
+ type: string;
75
+ minItems: number;
76
+ maxItems: 100;
77
+ uniqueItems: boolean;
78
+ items: {
79
+ type: string;
80
+ };
81
+ };
82
+ };
83
+ required: string[];
84
+ additionalProperties: boolean;
85
+ };
86
+ };
87
+ expectedTargetsHash: {
88
+ type: string;
89
+ description: string;
90
+ };
91
+ maxPlacements: {
92
+ type: string;
93
+ minimum: number;
94
+ maximum: 150;
95
+ description: string;
96
+ };
36
97
  };
37
98
  required: string[];
38
99
  additionalProperties: boolean;
@@ -1,5 +1,9 @@
1
+ import { createHash } from "crypto";
1
2
  import { getApi } from "../api.js";
2
3
  import { normalizeExplicitWorkspaceId, workspaceRequestOptions, } from "./workspace-context.js";
4
+ export const REFILL_SCHEDULER_EXPECTED_TARGET_LIMIT = 25;
5
+ export const REFILL_SCHEDULER_EXPECTED_TARGET_SENDER_LIMIT = 100;
6
+ export const REFILL_SCHEDULER_MAX_PLACEMENTS = 150;
3
7
  async function postSchedulerRun(body, workspaceId) {
4
8
  const api = getApi();
5
9
  const requestOptions = workspaceRequestOptions(workspaceId);
@@ -29,10 +33,89 @@ function normalizeOptionalScopeValue(value, field) {
29
33
  }
30
34
  return value.trim();
31
35
  }
36
+ function canonicalizeExpectedTargets(value) {
37
+ if (!Array.isArray(value) || value.length === 0) {
38
+ throw new Error("expectedTargets must be a non-empty array.");
39
+ }
40
+ if (value.length > REFILL_SCHEDULER_EXPECTED_TARGET_LIMIT) {
41
+ throw new Error(`expected_target_overflow: expectedTargets exceeds ${REFILL_SCHEDULER_EXPECTED_TARGET_LIMIT}.`);
42
+ }
43
+ const supportedActions = new Set([
44
+ "send_invite",
45
+ "send_inmail_open",
46
+ "send_inmail_closed",
47
+ ]);
48
+ const canonical = value.map((rawTarget, index) => {
49
+ if (!rawTarget ||
50
+ typeof rawTarget !== "object" ||
51
+ Array.isArray(rawTarget)) {
52
+ throw new Error(`expectedTargets[${index}] must be an object.`);
53
+ }
54
+ const target = rawTarget;
55
+ const allowedKeys = new Set([
56
+ "campaignId",
57
+ "tableId",
58
+ "refillLaneKey",
59
+ "branchActionTypes",
60
+ "senderIds",
61
+ ]);
62
+ if (Object.keys(target).some((key) => !allowedKeys.has(key))) {
63
+ throw new Error(`expectedTargets[${index}] contains unsupported fields.`);
64
+ }
65
+ const campaignId = normalizeOptionalScopeValue(target.campaignId, `expectedTargets[${index}].campaignId`);
66
+ const tableId = normalizeOptionalScopeValue(target.tableId, `expectedTargets[${index}].tableId`);
67
+ const refillLaneKey = normalizeOptionalScopeValue(target.refillLaneKey, `expectedTargets[${index}].refillLaneKey`);
68
+ if (!campaignId ||
69
+ !tableId ||
70
+ !refillLaneKey ||
71
+ !Array.isArray(target.branchActionTypes) ||
72
+ target.branchActionTypes.length === 0 ||
73
+ !Array.isArray(target.senderIds) ||
74
+ target.senderIds.length === 0) {
75
+ throw new Error(`expectedTargets[${index}] is incomplete.`);
76
+ }
77
+ if (target.senderIds.length > REFILL_SCHEDULER_EXPECTED_TARGET_SENDER_LIMIT) {
78
+ throw new Error(`expected_target_overflow: expectedTargets[${index}].senderIds exceeds ${REFILL_SCHEDULER_EXPECTED_TARGET_SENDER_LIMIT}.`);
79
+ }
80
+ const branchActionTypes = target.branchActionTypes.map((actionType) => {
81
+ if (typeof actionType !== "string" || !supportedActions.has(actionType)) {
82
+ throw new Error(`expectedTargets[${index}] contains an unsupported branch action.`);
83
+ }
84
+ return actionType;
85
+ });
86
+ const senderIds = target.senderIds.map((senderId) => {
87
+ const normalized = normalizeOptionalScopeValue(senderId, `expectedTargets[${index}].senderIds`);
88
+ if (!normalized) {
89
+ throw new Error(`expectedTargets[${index}].senderIds must be non-empty.`);
90
+ }
91
+ return normalized;
92
+ });
93
+ if (new Set(branchActionTypes).size !== branchActionTypes.length ||
94
+ new Set(senderIds).size !== senderIds.length) {
95
+ throw new Error(`expectedTargets[${index}] branchActionTypes and senderIds must be unique.`);
96
+ }
97
+ return {
98
+ campaignId,
99
+ tableId,
100
+ refillLaneKey,
101
+ branchActionTypes: branchActionTypes.sort(),
102
+ senderIds: senderIds.sort(),
103
+ };
104
+ });
105
+ canonical.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
106
+ const targetKeys = canonical.map((target) => JSON.stringify(target));
107
+ if (new Set(targetKeys).size !== targetKeys.length) {
108
+ throw new Error("expectedTargets contains duplicate canonical targets.");
109
+ }
110
+ return canonical;
111
+ }
112
+ function computeExpectedTargetsHash(targets) {
113
+ return createHash("sha256").update(JSON.stringify(targets)).digest("hex");
114
+ }
32
115
  export const schedulerRunToolDefinitions = [
33
116
  {
34
117
  name: "run_scheduler_sweep",
35
- description: 'Trigger the product scheduler placement sweep for one explicit workspace now, or read the last on-demand scheduler run status with action "status". A run is workspace-wide, not a scoped run for one campaign/table, and returns a synchronous envelope with status ran, attached, backoff, window_closed_noop, or failed; retryAfterMs for backoff replays; and a backward-compatible receipt. In v2 receipts, readyCellsFound is total scheduler-ready supply found before filters, cellsConsidered is the allocation-attempt count that survived prefilters, prefiltered means ready cells removed before allocation such as no capacity, stale paid InMail credit facts, or sender mismatch, skipped means considered cells rejected by hard scheduler gates, and deferred means considered cells waiting on windows/capacity/cooldown. campaignScopeSummary lists bounded tables/campaigns the workspace-wide run inspected; absence there is not a scoped-run guarantee that the target was ready. The receipt can include readyCellsByType, consideredCellsByType, prefilterReasons, skippedReasons, deferredReasons, summary, and nextAction. It places cells within existing scheduler gates only: it can move placement earlier, but it cannot bypass sending windows, daily limits, cooldowns, sender gates, billing, or credit thresholds, and it never sends messages directly. Repeated calls inside the backoff window replay the last receipt verbatim. Status is read-only and scoped to the last on-demand run only; cron sweeps are not recorded here.',
118
+ description: 'Trigger the product scheduler placement sweep for one explicit workspace now, or read the last on-demand scheduler run status with action "status". A run is workspace-wide, not a scoped run for one campaign/table, and returns a synchronous envelope with status ran, attached, backoff, window_closed_noop, or failed; retryAfterMs for backoff replays; and a backward-compatible receipt. In v3 receipts, readyCellsFound is total scheduler-ready supply found before filters, cellsConsidered is the allocation-attempt count that survived prefilters, prefiltered means ready cells removed before allocation such as no capacity, stale paid InMail credit facts, or sender mismatch, skipped means considered cells rejected by hard scheduler gates, and deferred means considered cells waiting on windows/capacity/cooldown. campaignScopeSummary remains bounded display evidence, while expectedTargetSummary explains every hash-bound planner target exactly once without filtering placement. Expected targets require a frozen numeric total placement cap; unrelated eligible campaigns may consume that cap through normal workspace-wide gates, and changedCounts audits every actual placement by campaign, table, action, and sender. The receipt can include readyCellsByType, consideredCellsByType, prefilterReasons, skippedReasons, deferredReasons, summary, and nextAction. It places cells within existing scheduler gates only: it can move placement earlier, but it cannot bypass sending windows, daily limits, cooldowns, sender gates, billing, or credit thresholds, and it never sends messages directly. Repeated calls inside the backoff window replay the last receipt verbatim. Status is read-only and scoped to the last on-demand run only; cron sweeps are not recorded here.',
36
119
  inputSchema: {
37
120
  type: "object",
38
121
  properties: {
@@ -57,6 +140,56 @@ export const schedulerRunToolDefinitions = [
57
140
  type: "string",
58
141
  description: "Optional stable approved refill target-shape revision associated with requestKey.",
59
142
  },
143
+ expectedTargets: {
144
+ type: "array",
145
+ maxItems: REFILL_SCHEDULER_EXPECTED_TARGET_LIMIT,
146
+ items: {
147
+ type: "object",
148
+ properties: {
149
+ campaignId: { type: "string" },
150
+ tableId: { type: "string" },
151
+ refillLaneKey: { type: "string" },
152
+ branchActionTypes: {
153
+ type: "array",
154
+ minItems: 1,
155
+ uniqueItems: true,
156
+ items: {
157
+ type: "string",
158
+ enum: [
159
+ "send_invite",
160
+ "send_inmail_open",
161
+ "send_inmail_closed",
162
+ ],
163
+ },
164
+ },
165
+ senderIds: {
166
+ type: "array",
167
+ minItems: 1,
168
+ maxItems: REFILL_SCHEDULER_EXPECTED_TARGET_SENDER_LIMIT,
169
+ uniqueItems: true,
170
+ items: { type: "string" },
171
+ },
172
+ },
173
+ required: [
174
+ "campaignId",
175
+ "tableId",
176
+ "refillLaneKey",
177
+ "branchActionTypes",
178
+ "senderIds",
179
+ ],
180
+ additionalProperties: false,
181
+ },
182
+ },
183
+ expectedTargetsHash: {
184
+ type: "string",
185
+ description: "Required SHA-256 of canonical expectedTargets for a target-aware run.",
186
+ },
187
+ maxPlacements: {
188
+ type: "integer",
189
+ minimum: 1,
190
+ maximum: REFILL_SCHEDULER_MAX_PLACEMENTS,
191
+ description: "Required frozen numeric total placement cap across the workspace-wide sweep when expectedTargets are present.",
192
+ },
60
193
  },
61
194
  required: ["workspaceId"],
62
195
  additionalProperties: false,
@@ -75,11 +208,60 @@ export async function runSchedulerSweep(input) {
75
208
  if (action !== "run" && action !== "status") {
76
209
  throw new Error('action must be "run" or "status" for run_scheduler_sweep.');
77
210
  }
211
+ const hasExpectedTargets = input.expectedTargets !== undefined;
212
+ const hasExpectedTargetScope = input.expectedTargetsHash !== undefined ||
213
+ input.maxPlacements !== undefined;
214
+ if (action === "status" && hasExpectedTargets) {
215
+ throw new Error("expectedTargets are only valid for action run.");
216
+ }
217
+ let expectedTargetRequest;
218
+ if (action === "run" && (hasExpectedTargets || hasExpectedTargetScope)) {
219
+ const expectedTargets = canonicalizeExpectedTargets(input.expectedTargets);
220
+ const expectedTargetsHash = normalizeOptionalScopeValue(input.expectedTargetsHash, "expectedTargetsHash");
221
+ if (!expectedTargetsHash ||
222
+ expectedTargetsHash !== computeExpectedTargetsHash(expectedTargets)) {
223
+ throw new Error("expectedTargetsHash must match the canonical expectedTargets payload.");
224
+ }
225
+ if (typeof input.maxPlacements !== "number" ||
226
+ !Number.isInteger(input.maxPlacements) ||
227
+ input.maxPlacements <= 0 ||
228
+ input.maxPlacements > REFILL_SCHEDULER_MAX_PLACEMENTS) {
229
+ throw new Error(`maxPlacements must be an integer from 1 to ${REFILL_SCHEDULER_MAX_PLACEMENTS}.`);
230
+ }
231
+ if (!targetDate || !requestKey || !targetShapeRevision) {
232
+ throw new Error("expectedTargets require targetDate, requestKey, and targetShapeRevision.");
233
+ }
234
+ expectedTargetRequest = {
235
+ expectedTargets,
236
+ expectedTargetsHash,
237
+ maxPlacements: input.maxPlacements,
238
+ };
239
+ }
240
+ let expectedStatusScope;
241
+ if (action === "status" && hasExpectedTargetScope) {
242
+ const expectedTargetsHash = normalizeOptionalScopeValue(input.expectedTargetsHash, "expectedTargetsHash");
243
+ if (!expectedTargetsHash ||
244
+ typeof input.maxPlacements !== "number" ||
245
+ !Number.isInteger(input.maxPlacements) ||
246
+ input.maxPlacements <= 0 ||
247
+ input.maxPlacements > REFILL_SCHEDULER_MAX_PLACEMENTS ||
248
+ !targetDate ||
249
+ !requestKey ||
250
+ !targetShapeRevision) {
251
+ throw new Error("hash-bound status requires expectedTargetsHash, maxPlacements, targetDate, requestKey, and targetShapeRevision.");
252
+ }
253
+ expectedStatusScope = {
254
+ expectedTargetsHash,
255
+ maxPlacements: input.maxPlacements,
256
+ };
257
+ }
78
258
  return postSchedulerRun({
79
259
  workspaceId,
80
260
  action,
81
261
  ...(targetDate ? { targetDate } : {}),
82
262
  ...(requestKey ? { requestKey } : {}),
83
263
  ...(targetShapeRevision ? { targetShapeRevision } : {}),
264
+ ...(expectedTargetRequest ?? {}),
265
+ ...(expectedStatusScope ?? {}),
84
266
  }, workspaceId);
85
267
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.553",
3
+ "version": "0.1.555",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code, Codex, and Hermes campaign workflows",
6
6
  "main": "dist/index.js",