@sellable/mcp 0.1.539 → 0.1.541

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.
Files changed (34) hide show
  1. package/README.md +2 -5
  2. package/dist/index-dev.js +0 -0
  3. package/dist/index.js +0 -0
  4. package/dist/refill-run-loop.d.ts +1 -9
  5. package/dist/refill-run-loop.js +90 -151
  6. package/dist/server.js +1 -1
  7. package/dist/tools/campaigns.d.ts +50 -1
  8. package/dist/tools/campaigns.js +6 -2
  9. package/dist/tools/evergreen-refill-plan.d.ts +0 -19
  10. package/dist/tools/evergreen-refill-plan.js +0 -19
  11. package/dist/tools/refill-executors.d.ts +7 -0
  12. package/dist/tools/refill-executors.js +23 -1
  13. package/dist/tools/refill-sends-v2.d.ts +0 -19
  14. package/dist/tools/refill-sends-v2.js +2 -31
  15. package/dist/tools/refill-sends.d.ts +12 -0
  16. package/dist/tools/refill-sends.js +58 -36
  17. package/dist/tools/refill-target-plan.js +6 -19
  18. package/dist/tools/registry.d.ts +49 -32
  19. package/dist/tools/scheduler-fill-capacity.js +1 -1
  20. package/dist/tools/scheduler-run.d.ts +1 -1
  21. package/dist/tools/scheduler-run.js +17 -20
  22. package/dist/tools/senders.d.ts +14 -8
  23. package/dist/tools/senders.js +21 -10
  24. package/package.json +1 -1
  25. package/skills/refill-sends/SKILL.md +1 -9
  26. package/skills/refill-sends-v2/SKILL.md +5 -18
  27. package/skills/refill-sends-v2-workflow/SKILL.md +2 -16
  28. package/skills/refill-sends-v2-workflow/core/flow.v1.json +3 -19
  29. package/skills/refill-sends-workflow/SKILL.md +3 -5
  30. package/skills/refill-sends-workflow/core/flow.v1.json +1 -1
  31. package/dist/refill-date-window.d.ts +0 -34
  32. package/dist/refill-date-window.js +0 -210
  33. package/dist/tools/refill-sends-evergreen.d.ts +0 -28
  34. package/dist/tools/refill-sends-evergreen.js +0 -47
@@ -10,6 +10,11 @@ const PAID_INMAIL_REFRESH_RETRY_DELAY_MS = Number(process.env.SELLABLE_MCP_PAID_
10
10
  const SIGNAL_DISCOVERY_MIN_REFILL_POSTS = 3;
11
11
  const SIGNAL_DISCOVERY_MAX_REFILL_POSTS = 10;
12
12
  const SIGNAL_DISCOVERY_TARGET_ROWS_PER_POST = 150;
13
+ const USABLE_PAID_INMAIL_REFRESH_RECEIPT_STATUSES = new Set([
14
+ "fresh",
15
+ "refreshed",
16
+ "below_threshold",
17
+ ]);
13
18
  export function normalizeStrings(values) {
14
19
  if (!Array.isArray(values))
15
20
  return [];
@@ -206,6 +211,23 @@ export function collectPaidInmailRefreshActions(plan) {
206
211
  add(action);
207
212
  return [...bySender.values()];
208
213
  }
214
+ export function classifyPaidInmailRefreshReceipt(value) {
215
+ const response = recordValue(value);
216
+ const nestedReceipt = recordValue(response?.receipt);
217
+ const status = stringValue(nestedReceipt?.status) ??
218
+ stringValue(response?.status) ??
219
+ (response?.refreshed === true ? "refreshed" : "not_refreshed");
220
+ const refreshed = response?.refreshed === true || nestedReceipt?.refreshed === true;
221
+ const usableCurrentFacts = refreshed || USABLE_PAID_INMAIL_REFRESH_RECEIPT_STATUSES.has(status);
222
+ return {
223
+ usableCurrentFacts,
224
+ status,
225
+ refreshed,
226
+ error: stringValue(response?.error) ??
227
+ stringValue(nestedReceipt?.error) ??
228
+ (usableCurrentFacts ? null : "paid InMail credit refresh did not return usable current facts"),
229
+ };
230
+ }
209
231
  export function firstGlobalAction(plan) {
210
232
  const root = recordValue(plan);
211
233
  const target = recordValue(root?.target);
@@ -659,7 +681,7 @@ export async function executeStartCampaignPrimitive(params) {
659
681
  (params.authority.laneChainCampaignIds.includes(campaignId) ||
660
682
  authorizedByPinnedCampaign)) {
661
683
  try {
662
- const result = await startCampaign(campaignId);
684
+ const result = await startCampaign(campaignId, params.workspaceId);
663
685
  return {
664
686
  executed: true,
665
687
  type: "start_paused_campaign",
@@ -6,9 +6,6 @@ type RefillSendsV2Input = {
6
6
  approvalMode?: "approve" | "mark_ready";
7
7
  senderIds?: string[];
8
8
  intent?: "auto" | "evergreen" | "plain" | "active";
9
- targetDate?: string | null;
10
- untilDate?: string | null;
11
- horizonSendDays?: number | null;
12
9
  };
13
10
  export declare const refillSendsV2ToolDefinitions: {
14
11
  name: string;
@@ -47,22 +44,6 @@ export declare const refillSendsV2ToolDefinitions: {
47
44
  type: string;
48
45
  enum: string[];
49
46
  };
50
- targetDate: {
51
- type: string;
52
- pattern: string;
53
- description: string;
54
- };
55
- untilDate: {
56
- type: string;
57
- pattern: string;
58
- description: string;
59
- };
60
- horizonSendDays: {
61
- type: string;
62
- minimum: number;
63
- maximum: number;
64
- description: string;
65
- };
66
47
  };
67
48
  required: string[];
68
49
  additionalProperties: boolean;
@@ -18,7 +18,7 @@ const BOUNDED_AUTHORITY = "Within the refill run, bounded approvals, preparation
18
18
  export const refillSendsV2ToolDefinitions = [
19
19
  {
20
20
  name: "refill_sends_v2",
21
- description: "Execute the refill sends v2 loop with run-record fencing, packet re-planning, shared refill executors, and resumable dry-run/real-run modes. Date selectors match refill_sends: targetDate is one exact sender-local date, untilDate is inclusive, horizonSendDays is the next N sender-local send days, with precedence targetDate > untilDate > horizonSendDays > default scheduler-forward planning.",
21
+ description: "Execute the refill sends v2 loop with run-record fencing, packet re-planning, shared refill executors, and resumable dry-run/real-run modes.",
22
22
  inputSchema: {
23
23
  type: "object",
24
24
  properties: {
@@ -51,22 +51,6 @@ export const refillSendsV2ToolDefinitions = [
51
51
  type: "string",
52
52
  enum: ["auto", "evergreen", "plain", "active"],
53
53
  },
54
- targetDate: {
55
- type: "string",
56
- pattern: "^\\d{4}-\\d{2}-\\d{2}$",
57
- description: "Optional one exact sender-local date (YYYY-MM-DD) to refill.",
58
- },
59
- untilDate: {
60
- type: "string",
61
- pattern: "^\\d{4}-\\d{2}-\\d{2}$",
62
- description: "Optional inclusive sender-local through-date (YYYY-MM-DD) to refill.",
63
- },
64
- horizonSendDays: {
65
- type: "number",
66
- minimum: 1,
67
- maximum: 7,
68
- description: "Optional explicit next N sender-local send days to refill.",
69
- },
70
54
  },
71
55
  required: ["workspaceId"],
72
56
  additionalProperties: false,
@@ -115,9 +99,6 @@ export async function refillSendsV2Command(input) {
115
99
  workspaceId,
116
100
  intent: input.intent,
117
101
  senderIds: input.senderIds,
118
- targetDate: input.targetDate,
119
- untilDate: input.untilDate,
120
- horizonSendDays: input.horizonSendDays,
121
102
  journal: true,
122
103
  });
123
104
  return {
@@ -132,9 +113,6 @@ export async function refillSendsV2Command(input) {
132
113
  workspaceId,
133
114
  intent: input.intent,
134
115
  senderIds: input.senderIds,
135
- targetDate: input.targetDate,
136
- untilDate: input.untilDate,
137
- horizonSendDays: input.horizonSendDays,
138
116
  approvalMode: input.approvalMode ?? "approve",
139
117
  resume: resumeHandle(input),
140
118
  }, {
@@ -151,9 +129,6 @@ export async function refillSendsV2Command(input) {
151
129
  workspaceId: planInput.workspaceId,
152
130
  intent: planInput.intent,
153
131
  senderIds: planInput.senderIds,
154
- targetDate: planInput.targetDate,
155
- untilDate: planInput.untilDate,
156
- horizonSendDays: planInput.horizonSendDays,
157
132
  runState: planInput.runState,
158
133
  journal: planInput.journal,
159
134
  }),
@@ -178,11 +153,7 @@ export async function refillSendsV2Command(input) {
178
153
  localState: {
179
154
  writeRefillWorkspaceState,
180
155
  },
181
- requestSchedulerRun: (workspaceId, options) => runSchedulerSweep({
182
- workspaceId,
183
- action: "run",
184
- targetDate: options?.targetDate,
185
- }),
156
+ requestSchedulerRun: (workspaceId) => runSchedulerSweep({ workspaceId, action: "run" }),
186
157
  });
187
158
  return {
188
159
  ...(await maybeAddLostFenceGuidance(result, { ...input, workspaceId })),
@@ -292,6 +292,8 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
292
292
  attempts?: number;
293
293
  error: string;
294
294
  errors?: string[];
295
+ receiptStatus?: string;
296
+ receipt?: unknown;
295
297
  }[];
296
298
  refreshReceipts: {
297
299
  senderId: string;
@@ -301,6 +303,8 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
301
303
  approvalSource: string;
302
304
  approvalPacket: PaidInmailRefreshApprovalPacket;
303
305
  receipt: import("./senders.js").RefreshPaidInmailCreditsResponse;
306
+ receiptStatus: string;
307
+ usableCurrentFacts: boolean;
304
308
  }[];
305
309
  attemptedSenderIds: string[];
306
310
  targetPlanReread: boolean;
@@ -309,6 +313,14 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
309
313
  note: string;
310
314
  };
311
315
  yoloExecution: {
316
+ enabled: false;
317
+ status: "not_run_without_yolo";
318
+ selectedAction: null;
319
+ targetPlanReread: false;
320
+ result?: undefined;
321
+ refusalReason?: undefined;
322
+ postActionFirstAction?: undefined;
323
+ } | {
312
324
  enabled: true;
313
325
  status: "skipped_after_paid_refresh";
314
326
  selectedAction: null;
@@ -1,18 +1,14 @@
1
- import { actionIds, collectPaidInmailRefreshActions, executeOneYoloPrimitive, firstGlobalAction, normalizeStrings, numberValue, recordValue, refreshPaidInmailCreditsWithRetry, stringValue, } from "./refill-executors.js";
1
+ import { actionIds, classifyPaidInmailRefreshReceipt, collectPaidInmailRefreshActions, executeOneYoloPrimitive, firstGlobalAction, normalizeStrings, numberValue, recordValue, refreshPaidInmailCreditsWithRetry, stringValue, } from "./refill-executors.js";
2
2
  import { getRefillTargetPlan } from "./refill-target-plan.js";
3
3
  import { createWorkspaceContext, normalizeExplicitWorkspaceId, } from "./workspace-context.js";
4
4
  const DEFAULT_SCHEDULER_FORWARD_HOURS = 48;
5
5
  const MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS = 4 * 60 * 60;
6
6
  function normalizeHorizonSendDays(value) {
7
- if (value === undefined)
7
+ if (value === undefined || value === null)
8
8
  return null;
9
- if (typeof value !== "number" ||
10
- !Number.isInteger(value) ||
11
- value < 1 ||
12
- value > 7) {
13
- throw new Error("horizonSendDays must be an integer between 1 and 7.");
14
- }
15
- return value;
9
+ if (typeof value !== "number" || !Number.isFinite(value))
10
+ return null;
11
+ return Math.max(1, Math.min(7, Math.floor(value)));
16
12
  }
17
13
  function normalizeUntilDate(value) {
18
14
  if (value === undefined || value === null || value === "")
@@ -95,7 +91,7 @@ export const refillSendsToolDefinitions = [
95
91
  type: "number",
96
92
  minimum: 1,
97
93
  maximum: 7,
98
- description: "Compatibility override for explicit next N sender-local send days. Omit for the default scheduler-forward 48-hour target window.",
94
+ description: "Compatibility override for an explicit sender-local send-day horizon. Omit for the default scheduler-forward 48-hour target window.",
99
95
  },
100
96
  untilDate: {
101
97
  type: "string",
@@ -188,7 +184,7 @@ export function refillSendsCommand(input = {}) {
188
184
  horizonHours: horizonSendDays === null ? DEFAULT_SCHEDULER_FORWARD_HOURS : null,
189
185
  description: horizonSendDays === null
190
186
  ? "Fill the default scheduler-forward 48-hour target window."
191
- : `Fill the next ${horizonSendDays} sender-local send days.`,
187
+ : "Fill the explicit compatibility horizon by sender-local send days.",
192
188
  },
193
189
  approvalMode,
194
190
  campaignId: input.campaignId ?? null,
@@ -235,7 +231,7 @@ export function refillSendsCommand(input = {}) {
235
231
  "Fresh reread get_campaign_refill_state immediately before any import, prep, approval, or horizon-fill mutation.",
236
232
  "Maintain a target-window saturation ledger per selected sender from get_refill_target_plan: selected days, gross capacity, actual sent counts, future scheduler-owned scheduled counts, projected counts, ready-to-schedule buffer, remaining projected gap, paid InMail threshold feasibility, targetShapeRevision, stateRevision, and next MCP primitive.",
237
233
  "Source/fallback primitives require receipt-proven exhaustion: existingRowFrontier.hasMoreFrontierRows:false, approvalCandidates:0, no fresh active prep, no stuckActiveCells, and no non-terminal approvedNotDispatched rows. Treat anomalies, stuckActiveCells, and non-terminal approvedNotDispatched as diagnose-and-report gates, not exhaustion. Terminal approvedNotDispatched blockers may be reported and then the ladder can proceed.",
238
- "In --yolo, this tool automatically maintains a run-local refreshedPaidInmailSenderIds set: when the first target plan returns refresh_paid_inmail_credits candidates for selected paid-InMail lanes, it refreshes each exact sender at most once, reruns get_refill_target_plan, and returns the post-refresh targetPlan before any prep/source-copy/bounded-approval/read-only wait action is chosen.",
234
+ "In manual/non-yolo, scheduled, and --yolo modes, this tool may automatically refresh stale or missing paid-InMail credit facts once per selected sender when the first target plan returns refresh_paid_inmail_credits candidates. This is the only newly allowed automatic write in manual/non-yolo mode; it reruns get_refill_target_plan and returns autoPaidInmailRefresh with attemptedSenderIds, refreshedPaidInmailSenderIds, failedPaidInmailRefreshes, and the post-refresh targetPlan before any prep/source-copy/bounded-approval/read-only wait action is chosen.",
239
235
  "Freshness gate precedes scheduler wait: if any selected target.senderRefillPlans[].paidInmail.status is missing_credit_facts or stale_credit_facts, or the target plan contains refresh_paid_inmail_credits, do not enter wait_for_scheduler even when remainingReadyOrProjectedGap is 0; refresh exact sender credit facts once, reread get_refill_target_plan, then choose scheduler wait only if freshness is clean.",
240
236
  "Scheduler-run receipt interpretation: cellsConsidered is allocation-attempt count, readyCellsFound is ready inventory before prefilters, and campaignScopeSummary must include the selected campaign/table before inferring it was ready-but-blocked. Read prefiltered, skipped, and deferred separately. For ready closed-InMail cells with stale paid-credit facts, route to refresh_paid_inmail_credits_then_rerun once, then rerun/status. wait_for_capacity_or_window means report loaded/capped/waiting and do not source or prep more rows. no_ready_cells_continue_refill_prep means return to the refill/prep ladder.",
241
237
  "For wait_for_active_work and wait_for_scheduler, honor the receipt's absolute wait.deadlineAt when present. If the deadline is expired on this call, escalate to diagnostics with the receipt evidence instead of issuing another blind wait.",
@@ -379,7 +375,11 @@ export async function executeRefillSendsCommand(input = {}) {
379
375
  ? { ...input, workspaceId: workspaceContext.context.workspaceId }
380
376
  : input;
381
377
  const command = refillSendsCommand(scopedInput);
382
- if (!yolo) {
378
+ const workspaceId = workspaceContext && workspaceContext.ok
379
+ ? workspaceContext.context.workspaceId
380
+ : normalizeExplicitWorkspaceId(scopedInput.workspaceId);
381
+ const canRefreshPaidInmailFacts = yolo || executionMode === "scheduled" || Boolean(workspaceId);
382
+ if (!canRefreshPaidInmailFacts) {
383
383
  return {
384
384
  ...command,
385
385
  autoPaidInmailRefresh: {
@@ -387,7 +387,7 @@ export async function executeRefillSendsCommand(input = {}) {
387
387
  status: "not_run_without_yolo",
388
388
  refreshedPaidInmailSenderIds: [],
389
389
  failedPaidInmailRefreshes: [],
390
- note: "Automatic paid InMail credit refresh only runs for --yolo refill_sends calls.",
390
+ note: "Automatic paid InMail credit refresh requires --yolo, scheduled mode, or an explicit workspaceId so the request can refresh exact sender facts safely.",
391
391
  },
392
392
  yoloExecution: {
393
393
  enabled: false,
@@ -397,9 +397,6 @@ export async function executeRefillSendsCommand(input = {}) {
397
397
  },
398
398
  };
399
399
  }
400
- const workspaceId = workspaceContext && workspaceContext.ok
401
- ? workspaceContext.context.workspaceId
402
- : normalizeExplicitWorkspaceId(scopedInput.workspaceId);
403
400
  const targetPlanInput = targetPlanInputFor(scopedInput);
404
401
  const targetPlanBeforePaidRefresh = await getRefillTargetPlan(targetPlanInput);
405
402
  const refreshActions = collectPaidInmailRefreshActions(targetPlanBeforePaidRefresh);
@@ -425,7 +422,7 @@ export async function executeRefillSendsCommand(input = {}) {
425
422
  }
426
423
  const refreshResult = await refreshPaidInmailCreditsWithRetry(action.senderId, workspaceId, paidRefreshOptionsFromApprovalPacket(approvalPacket));
427
424
  if (refreshResult.receipt) {
428
- refreshedPaidInmailSenderIds.push(action.senderId);
425
+ const classification = classifyPaidInmailRefreshReceipt(refreshResult.receipt);
429
426
  refreshReceipts.push({
430
427
  senderId: action.senderId,
431
428
  actionKey: action.actionKey ?? null,
@@ -434,7 +431,23 @@ export async function executeRefillSendsCommand(input = {}) {
434
431
  approvalSource: "explicit_yolo_flag",
435
432
  approvalPacket,
436
433
  receipt: refreshResult.receipt,
434
+ receiptStatus: classification.status,
435
+ usableCurrentFacts: classification.usableCurrentFacts,
437
436
  });
437
+ if (classification.usableCurrentFacts) {
438
+ refreshedPaidInmailSenderIds.push(action.senderId);
439
+ }
440
+ else {
441
+ failedPaidInmailRefreshes.push({
442
+ senderId: action.senderId,
443
+ attempts: refreshResult.attempts,
444
+ error: classification.error ??
445
+ "paid InMail credit refresh did not return usable current facts",
446
+ errors: refreshResult.errors,
447
+ receiptStatus: classification.status,
448
+ receipt: refreshResult.receipt,
449
+ });
450
+ }
438
451
  }
439
452
  else {
440
453
  failedPaidInmailRefreshes.push({
@@ -449,8 +462,8 @@ export async function executeRefillSendsCommand(input = {}) {
449
462
  const targetPlan = refreshActions.length > 0
450
463
  ? await getRefillTargetPlan(targetPlanInput)
451
464
  : targetPlanBeforePaidRefresh;
452
- const selectedAction = refreshActions.length > 0 ? null : firstGlobalAction(targetPlan);
453
- const primitiveAttempt = refreshActions.length > 0
465
+ const selectedAction = yolo && refreshActions.length === 0 ? firstGlobalAction(targetPlan) : null;
466
+ const primitiveAttempt = !yolo || refreshActions.length > 0
454
467
  ? null
455
468
  : await executeOneYoloPrimitive(selectedAction, workspaceId ?? undefined);
456
469
  const shouldRereadAfterPrimitive = primitiveAttempt?.status === "executed_and_reread" ||
@@ -463,7 +476,9 @@ export async function executeRefillSendsCommand(input = {}) {
463
476
  ...command,
464
477
  targetPlan: finalTargetPlan,
465
478
  targetPlanBeforePaidRefresh: refreshActions.length > 0 ? targetPlanBeforePaidRefresh : null,
466
- targetPlanBeforeYoloPrimitive: refreshActions.length === 0 && postActionTargetPlan ? targetPlan : null,
479
+ targetPlanBeforeYoloPrimitive: yolo && refreshActions.length === 0 && postActionTargetPlan
480
+ ? targetPlan
481
+ : null,
467
482
  autoPaidInmailRefresh: {
468
483
  enabled: true,
469
484
  status: refreshActions.length === 0
@@ -482,23 +497,30 @@ export async function executeRefillSendsCommand(input = {}) {
482
497
  ? "refill_sends refreshed stale paid InMail credit facts internally with bounded retries, once per sender, then returned the post-refresh targetPlan."
483
498
  : "No stale or missing paid InMail credit refresh candidates were present in the initial target plan.",
484
499
  },
485
- yoloExecution: refreshActions.length > 0
500
+ yoloExecution: !yolo
486
501
  ? {
487
- enabled: true,
488
- status: "skipped_after_paid_refresh",
502
+ enabled: false,
503
+ status: "not_run_without_yolo",
489
504
  selectedAction: null,
490
- targetPlanReread: true,
505
+ targetPlanReread: false,
491
506
  }
492
- : {
493
- enabled: true,
494
- status: primitiveAttempt?.status ?? "no_action",
495
- selectedAction,
496
- result: primitiveAttempt?.result,
497
- targetPlanReread: shouldRereadAfterPrimitive,
498
- refusalReason: primitiveAttempt?.refusalReason,
499
- postActionFirstAction: postActionTargetPlan
500
- ? firstGlobalAction(postActionTargetPlan)
501
- : null,
502
- },
507
+ : refreshActions.length > 0
508
+ ? {
509
+ enabled: true,
510
+ status: "skipped_after_paid_refresh",
511
+ selectedAction: null,
512
+ targetPlanReread: true,
513
+ }
514
+ : {
515
+ enabled: true,
516
+ status: primitiveAttempt?.status ?? "no_action",
517
+ selectedAction,
518
+ result: primitiveAttempt?.result,
519
+ targetPlanReread: shouldRereadAfterPrimitive,
520
+ refusalReason: primitiveAttempt?.refusalReason,
521
+ postActionFirstAction: postActionTargetPlan
522
+ ? firstGlobalAction(postActionTargetPlan)
523
+ : null,
524
+ },
503
525
  };
504
526
  }
@@ -1026,16 +1026,6 @@ function sanitizeRefillTargetPlanResult(result) {
1026
1026
  mcpSanitizedRefillLanes: true,
1027
1027
  };
1028
1028
  }
1029
- function stripInternalTargetDateKeys(value) {
1030
- if (Array.isArray(value)) {
1031
- return value.map((item) => stripInternalTargetDateKeys(item));
1032
- }
1033
- if (!isRecord(value))
1034
- return value;
1035
- return Object.fromEntries(Object.entries(value)
1036
- .filter(([key]) => key !== "targetDateKey")
1037
- .map(([key, nested]) => [key, stripInternalTargetDateKeys(nested)]));
1038
- }
1039
1029
  export const refillTargetPlanToolDefinitions = [
1040
1030
  {
1041
1031
  name: "get_refill_target_plan",
@@ -1052,7 +1042,7 @@ export const refillTargetPlanToolDefinitions = [
1052
1042
  type: "number",
1053
1043
  minimum: 1,
1054
1044
  maximum: 7,
1055
- description: "Compatibility override for explicit next N sender-local send days. Omit for the default scheduler-forward 48-hour target window; no-send days are skipped while choosing the requested send days.",
1045
+ description: "Compatibility override for explicit sender-local send-day horizon planning. Omit for the default scheduler-forward 48-hour target window; no-send days contribute zero target.",
1056
1046
  },
1057
1047
  untilDate: {
1058
1048
  type: "string",
@@ -1062,7 +1052,7 @@ export const refillTargetPlanToolDefinitions = [
1062
1052
  targetDate: {
1063
1053
  type: "string",
1064
1054
  pattern: "^\\d{4}-\\d{2}-\\d{2}$",
1065
- description: "Optional one exact sender-local date (YYYY-MM-DD) to fill. Distinct from untilDate: targetDate asks how many scheduler-fillable slots exist on only this date.",
1055
+ description: "Optional exact sender-local YYYY-MM-DD date to fill. Distinct from untilDate: targetDate asks how many scheduler-fillable slots exist on only this date.",
1066
1056
  },
1067
1057
  campaignId: {
1068
1058
  type: "string",
@@ -1112,14 +1102,11 @@ export const refillTargetPlanToolDefinitions = [
1112
1102
  ];
1113
1103
  export async function getRefillTargetPlan(input = {}) {
1114
1104
  const workspaceId = normalizeExplicitWorkspaceId(input.workspaceId);
1115
- const targetDate = input.targetDate;
1116
- const untilDate = targetDate ? undefined : input.untilDate;
1117
- const horizonSendDays = targetDate || untilDate ? undefined : input.horizonSendDays;
1118
1105
  const result = await postRefillTargetPlan({
1119
1106
  intent: input.intent,
1120
- horizonSendDays,
1121
- untilDate,
1122
- targetDate,
1107
+ horizonSendDays: input.horizonSendDays,
1108
+ untilDate: input.untilDate,
1109
+ targetDate: input.targetDate,
1123
1110
  campaignId: input.campaignId,
1124
1111
  tableId: input.tableId,
1125
1112
  senderIds: input.senderIds,
@@ -1130,7 +1117,7 @@ export async function getRefillTargetPlan(input = {}) {
1130
1117
  paidInmailCreditsMaxStalenessSeconds: MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS,
1131
1118
  ...(workspaceId ? { workspaceId } : {}),
1132
1119
  }, workspaceId ?? undefined);
1133
- const sanitized = stripInternalTargetDateKeys(sanitizeRefillTargetPlanResult(result));
1120
+ const sanitized = sanitizeRefillTargetPlanResult(result);
1134
1121
  if (!workspaceId || !isRecord(sanitized))
1135
1122
  return sanitized;
1136
1123
  return {
@@ -1118,6 +1118,7 @@ export declare const allTools: ({
1118
1118
  useMessagingTemplate?: undefined;
1119
1119
  rubric?: undefined;
1120
1120
  flowVersion?: undefined;
1121
+ workspaceId?: undefined;
1121
1122
  };
1122
1123
  required: string[];
1123
1124
  additionalProperties: boolean;
@@ -1156,6 +1157,7 @@ export declare const allTools: ({
1156
1157
  useMessagingTemplate?: undefined;
1157
1158
  rubric?: undefined;
1158
1159
  flowVersion?: undefined;
1160
+ workspaceId?: undefined;
1159
1161
  };
1160
1162
  required: never[];
1161
1163
  additionalProperties?: undefined;
@@ -1194,6 +1196,7 @@ export declare const allTools: ({
1194
1196
  useMessagingTemplate?: undefined;
1195
1197
  rubric?: undefined;
1196
1198
  flowVersion?: undefined;
1199
+ workspaceId?: undefined;
1197
1200
  };
1198
1201
  required: string[];
1199
1202
  additionalProperties?: undefined;
@@ -1275,6 +1278,7 @@ export declare const allTools: ({
1275
1278
  useMessagingTemplate?: undefined;
1276
1279
  rubric?: undefined;
1277
1280
  flowVersion?: undefined;
1281
+ workspaceId?: undefined;
1278
1282
  };
1279
1283
  required: string[];
1280
1284
  additionalProperties: boolean;
@@ -1469,6 +1473,7 @@ export declare const allTools: ({
1469
1473
  useMessagingTemplate?: undefined;
1470
1474
  rubric?: undefined;
1471
1475
  flowVersion?: undefined;
1476
+ workspaceId?: undefined;
1472
1477
  };
1473
1478
  required: never[];
1474
1479
  additionalProperties?: undefined;
@@ -1674,6 +1679,49 @@ export declare const allTools: ({
1674
1679
  clientProspectId?: undefined;
1675
1680
  senderLinkedinUrl?: undefined;
1676
1681
  messageGenerationMode?: undefined;
1682
+ workspaceId?: undefined;
1683
+ };
1684
+ required: string[];
1685
+ additionalProperties?: undefined;
1686
+ };
1687
+ } | {
1688
+ name: string;
1689
+ description: string;
1690
+ inputSchema: {
1691
+ type: string;
1692
+ properties: {
1693
+ campaignId: {
1694
+ type: string;
1695
+ description: string;
1696
+ };
1697
+ workspaceId: {
1698
+ type: string;
1699
+ description: string;
1700
+ };
1701
+ limit?: undefined;
1702
+ tableId?: undefined;
1703
+ leadLimit?: undefined;
1704
+ page?: undefined;
1705
+ filters?: undefined;
1706
+ name?: undefined;
1707
+ clientProspectId?: undefined;
1708
+ senderLinkedinUrl?: undefined;
1709
+ offerPositioning?: undefined;
1710
+ campaignBrief?: undefined;
1711
+ messageGenerationMode?: undefined;
1712
+ currentStep?: undefined;
1713
+ watchNarration?: undefined;
1714
+ leadSourceType?: undefined;
1715
+ leadSourceProvider?: undefined;
1716
+ selectedLeadListId?: undefined;
1717
+ senderIds?: undefined;
1718
+ currentStepTransition?: undefined;
1719
+ clearCurrentStepIfMatches?: undefined;
1720
+ interactionMode?: undefined;
1721
+ enableICPFilters?: undefined;
1722
+ useMessagingTemplate?: undefined;
1723
+ rubric?: undefined;
1724
+ flowVersion?: undefined;
1677
1725
  };
1678
1726
  required: string[];
1679
1727
  additionalProperties?: undefined;
@@ -1715,6 +1763,7 @@ export declare const allTools: ({
1715
1763
  useMessagingTemplate?: undefined;
1716
1764
  rubric?: undefined;
1717
1765
  flowVersion?: undefined;
1766
+ workspaceId?: undefined;
1718
1767
  };
1719
1768
  required: string[];
1720
1769
  additionalProperties?: undefined;
@@ -2758,22 +2807,6 @@ export declare const allTools: ({
2758
2807
  enum: string[];
2759
2808
  description: string;
2760
2809
  };
2761
- targetDate: {
2762
- type: string;
2763
- pattern: string;
2764
- description: string;
2765
- };
2766
- untilDate: {
2767
- type: string;
2768
- pattern: string;
2769
- description: string;
2770
- };
2771
- horizonSendDays: {
2772
- type: string;
2773
- minimum: number;
2774
- maximum: number;
2775
- description: string;
2776
- };
2777
2810
  journal: {
2778
2811
  type: string;
2779
2812
  description: string;
@@ -7446,22 +7479,6 @@ export declare const allTools: ({
7446
7479
  type: string;
7447
7480
  enum: string[];
7448
7481
  };
7449
- targetDate: {
7450
- type: string;
7451
- pattern: string;
7452
- description: string;
7453
- };
7454
- untilDate: {
7455
- type: string;
7456
- pattern: string;
7457
- description: string;
7458
- };
7459
- horizonSendDays: {
7460
- type: string;
7461
- minimum: number;
7462
- maximum: number;
7463
- description: string;
7464
- };
7465
7482
  };
7466
7483
  required: string[];
7467
7484
  additionalProperties: boolean;
@@ -10,7 +10,7 @@ async function postSchedulerFillCapacity(body, workspaceId) {
10
10
  export const schedulerFillCapacityToolDefinitions = [
11
11
  {
12
12
  name: "get_scheduler_fill_capacity",
13
- description: "read-only scheduler capacity query for refill-sends before any refill mutation. It asks the product scheduler model how many additional cells the scheduler will try to place for exact sender/action requests, using the default scheduler-forward horizon or one exact targetDate. Use get_refill_target_plan for multi-day untilDate or horizonSendDays planning. It returns scheduler-fillable slots, occupied scheduled/processing slots, sender sendability gates, cooldowns, Sales Navigator status for paid InMail, cached paid-InMail credit feasibility, threshold source, blockers, warnings, and explicit sideEffects false. This tool does not create rows, import leads, prepare messages, approve messages, does not schedule sends, does not refresh paid InMail credits, mutate thresholds, start campaigns, launch, or send. Use exact senderId values only; never pass sender names.",
13
+ description: "read-only scheduler capacity query for refill-sends before any refill mutation. It asks the product scheduler model how many additional cells the scheduler will try to place for exact sender/action requests, using the default scheduler-forward horizon or one exact targetDate. It returns scheduler-fillable slots, occupied scheduled/processing slots, sender sendability gates, cooldowns, Sales Navigator status for paid InMail, cached paid-InMail credit feasibility, threshold source, blockers, warnings, and explicit sideEffects false. This tool does not create rows, import leads, prepare messages, approve messages, does not schedule sends, does not refresh paid InMail credits, mutate thresholds, start campaigns, launch, or send. Use exact senderId values only; never pass sender names.",
14
14
  inputSchema: {
15
15
  type: "object",
16
16
  properties: {
@@ -2,7 +2,7 @@ type SchedulerRunAction = "run" | "status";
2
2
  type RunSchedulerSweepInput = {
3
3
  workspaceId: string;
4
4
  action?: SchedulerRunAction;
5
- targetDate?: string | null;
5
+ targetDate?: string;
6
6
  };
7
7
  export declare const schedulerRunToolDefinitions: {
8
8
  name: string;
@@ -7,10 +7,24 @@ async function postSchedulerRun(body, workspaceId) {
7
7
  ? api.post("/api/v3/mcp/scheduler-run", body, requestOptions)
8
8
  : api.post("/api/v3/mcp/scheduler-run", body);
9
9
  }
10
+ function normalizeTargetDate(value) {
11
+ if (typeof value !== "string" || !value.trim())
12
+ return null;
13
+ const targetDate = value.trim();
14
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(targetDate)) {
15
+ throw new Error("targetDate must use YYYY-MM-DD format.");
16
+ }
17
+ const parsed = new Date(`${targetDate}T00:00:00.000Z`);
18
+ if (Number.isNaN(parsed.getTime()) ||
19
+ parsed.toISOString().slice(0, 10) !== targetDate) {
20
+ throw new Error("targetDate must be a valid calendar date.");
21
+ }
22
+ return targetDate;
23
+ }
10
24
  export const schedulerRunToolDefinitions = [
11
25
  {
12
26
  name: "run_scheduler_sweep",
13
- description: 'Trigger the product scheduler placement sweep for one explicit workspace now, or read the last on-demand scheduler run status with action "status". By default a run is workspace-wide, not a scoped run for one campaign/table. Pass targetDate to scope placement to one exact sender-local date while still remaining workspace-wide inside that date. Multi-day untilDate or horizonSendDays planning should come from get_refill_target_plan, then use exact per-date run_scheduler_sweep calls only for dates that still need scheduler placement. The synchronous envelope includes status ran, attached, backoff, window_closed_noop, or failed; retryAfterMs for matching-scope backoff replays; and a backward-compatible receipt with public scope metadata. 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 only when the recorded public scope matches this request. Status is read-only and scoped to the last on-demand run only; cron sweeps are not recorded here.',
27
+ 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.',
14
28
  inputSchema: {
15
29
  type: "object",
16
30
  properties: {
@@ -33,36 +47,19 @@ export const schedulerRunToolDefinitions = [
33
47
  },
34
48
  },
35
49
  ];
36
- function isValidDateKey(value) {
37
- if (!/^\d{4}-\d{2}-\d{2}$/.test(value))
38
- return false;
39
- const [year, month, day] = value.split("-").map(Number);
40
- const date = new Date(Date.UTC(year, month - 1, day));
41
- return (date.getUTCFullYear() === year &&
42
- date.getUTCMonth() === month - 1 &&
43
- date.getUTCDate() === day);
44
- }
45
- function normalizeTargetDate(value) {
46
- if (value === undefined)
47
- return undefined;
48
- if (typeof value !== "string" || !isValidDateKey(value)) {
49
- throw new Error("targetDate must be a valid YYYY-MM-DD calendar date.");
50
- }
51
- return value;
52
- }
53
50
  export async function runSchedulerSweep(input) {
54
51
  const workspaceId = normalizeExplicitWorkspaceId(input.workspaceId);
55
52
  if (!workspaceId) {
56
53
  throw new Error("workspaceId is required for run_scheduler_sweep.");
57
54
  }
58
55
  const action = input.action ?? "run";
56
+ const targetDate = normalizeTargetDate(input.targetDate);
59
57
  if (action !== "run" && action !== "status") {
60
58
  throw new Error('action must be "run" or "status" for run_scheduler_sweep.');
61
59
  }
62
- const targetDate = normalizeTargetDate(input.targetDate);
63
60
  return postSchedulerRun({
64
61
  workspaceId,
65
62
  action,
66
- targetDate,
63
+ ...(targetDate ? { targetDate } : {}),
67
64
  }, workspaceId);
68
65
  }