@sellable/mcp 0.1.521 → 0.1.523

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.
@@ -5,6 +5,15 @@ export type PaidInmailRefreshAction = {
5
5
  actionKey?: string | null;
6
6
  action?: Record<string, unknown>;
7
7
  };
8
+ export type PaidInmailCreditRefreshOptions = {
9
+ actionType?: string | null;
10
+ campaignId?: string | null;
11
+ tableId?: string | null;
12
+ columnId?: string | null;
13
+ threshold?: number | null;
14
+ maxStalenessSeconds?: number | null;
15
+ paidInmailCreditsMaxStalenessSeconds?: number | null;
16
+ };
8
17
  export type YoloPrimitiveExecution = {
9
18
  enabled: true;
10
19
  status: "no_action" | "read_only_reread" | "executed_and_reread" | "refused" | "skipped_after_paid_refresh";
@@ -161,7 +170,7 @@ export declare function approveGeneratedMessagesBatch(action: Record<string, unk
161
170
  refusalReason?: undefined;
162
171
  }>;
163
172
  export declare function continueSignalDiscoverySource(action: Record<string, unknown>, workspaceId?: string): Promise<YoloPrimitiveAttempt>;
164
- export declare function refreshPaidInmailCreditsWithRetry(senderId: string, workspaceId: string): Promise<{
173
+ export declare function refreshPaidInmailCreditsWithRetry(senderId: string, workspaceId: string, options?: PaidInmailCreditRefreshOptions): Promise<{
165
174
  receipt: import("./senders.js").RefreshPaidInmailCreditsResponse;
166
175
  attempts: number;
167
176
  errors: string[];
@@ -618,13 +618,14 @@ export async function continueSignalDiscoverySource(action, workspaceId) {
618
618
  },
619
619
  };
620
620
  }
621
- export async function refreshPaidInmailCreditsWithRetry(senderId, workspaceId) {
621
+ export async function refreshPaidInmailCreditsWithRetry(senderId, workspaceId, options = {}) {
622
622
  const errors = [];
623
623
  for (let attempt = 1; attempt <= PAID_INMAIL_REFRESH_MAX_ATTEMPTS; attempt += 1) {
624
624
  try {
625
625
  const receipt = await refreshPaidInmailCredits({
626
626
  senderId,
627
627
  workspaceId,
628
+ ...options,
628
629
  });
629
630
  return { receipt, attempts: attempt, errors };
630
631
  }
@@ -2,6 +2,7 @@ import { actionIds, collectPaidInmailRefreshActions, executeOneYoloPrimitive, fi
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
+ const MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS = 4 * 60 * 60;
5
6
  function normalizeHorizonSendDays(value) {
6
7
  if (value === undefined || value === null)
7
8
  return null;
@@ -329,6 +330,9 @@ function paidRefreshApprovalPacket(params) {
329
330
  const action = params.action.action ?? {};
330
331
  const ids = actionIds(action);
331
332
  const threshold = numberValue(action.oldThreshold) ?? numberValue(action.threshold);
333
+ const maxStalenessSeconds = Math.max(numberValue(action.maxStalenessSeconds) ??
334
+ numberValue(action.paidInmailCreditsMaxStalenessSeconds) ??
335
+ MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS, MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS);
332
336
  return {
333
337
  approvalSource: "explicit_yolo_flag",
334
338
  workspaceId: params.workspaceId ?? null,
@@ -339,12 +343,22 @@ function paidRefreshApprovalPacket(params) {
339
343
  tableId: stringValue(ids.tableId) ?? stringValue(action.tableId) ?? null,
340
344
  columnId: stringValue(ids.columnId) ?? stringValue(action.columnId) ?? null,
341
345
  threshold: threshold ?? null,
342
- maxStalenessSeconds: numberValue(action.maxStalenessSeconds) ??
343
- numberValue(action.paidInmailCreditsMaxStalenessSeconds) ??
344
- null,
346
+ maxStalenessSeconds,
345
347
  targetPlanFingerprint: targetPlanFingerprint(params.targetPlan),
346
348
  };
347
349
  }
350
+ function paidRefreshOptionsFromApprovalPacket(packet) {
351
+ return {
352
+ ...(packet.actionType ? { actionType: packet.actionType } : {}),
353
+ ...(packet.campaignId ? { campaignId: packet.campaignId } : {}),
354
+ ...(packet.tableId ? { tableId: packet.tableId } : {}),
355
+ ...(packet.columnId ? { columnId: packet.columnId } : {}),
356
+ ...(typeof packet.threshold === "number"
357
+ ? { threshold: packet.threshold }
358
+ : {}),
359
+ maxStalenessSeconds: packet.maxStalenessSeconds,
360
+ };
361
+ }
348
362
  export async function executeRefillSendsCommand(input = {}) {
349
363
  const yolo = input.yolo === true;
350
364
  const executionMode = input.executionMode ?? (yolo ? "yolo" : "manual");
@@ -407,7 +421,7 @@ export async function executeRefillSendsCommand(input = {}) {
407
421
  });
408
422
  continue;
409
423
  }
410
- const refreshResult = await refreshPaidInmailCreditsWithRetry(action.senderId, workspaceId);
424
+ const refreshResult = await refreshPaidInmailCreditsWithRetry(action.senderId, workspaceId, paidRefreshOptionsFromApprovalPacket(approvalPacket));
411
425
  if (refreshResult.receipt) {
412
426
  refreshedPaidInmailSenderIds.push(action.senderId);
413
427
  refreshReceipts.push({
@@ -36,6 +36,11 @@ const TERMINAL_NO_ACTION_BLOCKER_CODES = new Set([
36
36
  "paid_inmail_credit_missing",
37
37
  "paid_inmail_credit_stale",
38
38
  ]);
39
+ const MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS = 4 * 60 * 60;
40
+ const PAID_INMAIL_REFRESH_STATUSES = new Set([
41
+ "missing_credit_facts",
42
+ "stale_credit_facts",
43
+ ]);
39
44
  const SOURCE_PLAN_STATES = new Set([
40
45
  "not_needed",
41
46
  "use_existing_rows",
@@ -73,6 +78,9 @@ function actionTypesFrom(value) {
73
78
  function numberValue(value) {
74
79
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
75
80
  }
81
+ function optionalNumberValue(value) {
82
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
83
+ }
76
84
  function stringValue(value) {
77
85
  return typeof value === "string" ? value : undefined;
78
86
  }
@@ -87,6 +95,20 @@ function numericRecord(value) {
87
95
  function campaignClassificationValue(value) {
88
96
  return value === "sales_nav_cascade" ? "sales_nav_cascade" : undefined;
89
97
  }
98
+ function normalizePaidInmailMaxStalenessSeconds(value) {
99
+ const numeric = optionalNumberValue(value);
100
+ return numeric !== null && numeric > 0
101
+ ? Math.max(Math.trunc(numeric), MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS)
102
+ : MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS;
103
+ }
104
+ function isFreshWithinMcpPaidInmailWindow(checkedAt, maxStalenessSeconds) {
105
+ if (!checkedAt)
106
+ return false;
107
+ const checkedAtMs = new Date(checkedAt).getTime();
108
+ if (!Number.isFinite(checkedAtMs))
109
+ return false;
110
+ return Date.now() - checkedAtMs <= maxStalenessSeconds * 1000;
111
+ }
90
112
  function stringArray(value) {
91
113
  if (!Array.isArray(value))
92
114
  return [];
@@ -138,7 +160,7 @@ function sanitizeCounts(counts, selectedKeys) {
138
160
  return Boolean(key && selectedKeys.has(key));
139
161
  });
140
162
  }
141
- function sanitizeActionCandidates(candidates, selectedKeys, status, remainingReadyOrProjectedGap, remainingProjectedGap) {
163
+ function sanitizeActionCandidates(candidates, selectedKeys, paidRefreshNeededSenderIds, status, remainingReadyOrProjectedGap, remainingProjectedGap) {
142
164
  const filtered = Array.isArray(candidates)
143
165
  ? candidates
144
166
  .filter((item) => {
@@ -149,6 +171,13 @@ function sanitizeActionCandidates(candidates, selectedKeys, status, remainingRea
149
171
  return false;
150
172
  }
151
173
  const actionType = item.actionType;
174
+ if (item.type === "refresh_paid_inmail_credits") {
175
+ const ids = isRecord(item.ids) ? item.ids : {};
176
+ const senderId = stringValue(item.senderId) ?? stringValue(ids.senderId);
177
+ return Boolean(senderId &&
178
+ paidRefreshNeededSenderIds.has(senderId) &&
179
+ selectedKeys.has(`${senderId}:send_inmail_closed`));
180
+ }
152
181
  if (actionType === undefined)
153
182
  return item.type === "wait_for_scheduler";
154
183
  const allowed = allowedActionType(actionType);
@@ -375,6 +404,7 @@ function sanitizeToolInput(value) {
375
404
  : undefined,
376
405
  columnRole: stringValue(value.columnRole),
377
406
  rowSelector: sanitizeRowSelector(value.rowSelector),
407
+ excludeRowIds: stringArray(value.excludeRowIds),
378
408
  oldThreshold: typeof value.oldThreshold === "number" ? value.oldThreshold : undefined,
379
409
  suggestedThreshold: typeof value.suggestedThreshold === "number"
380
410
  ? value.suggestedThreshold
@@ -532,20 +562,41 @@ function sanitizeSourcePlan(value) {
532
562
  function sanitizePaidInmail(value) {
533
563
  if (!isRecord(value))
534
564
  return null;
565
+ const originalMaxStalenessSeconds = optionalNumberValue(value.maxStalenessSeconds);
566
+ const maxStalenessSeconds = normalizePaidInmailMaxStalenessSeconds(value.maxStalenessSeconds);
567
+ const checkedAt = stringValue(value.checkedAt) ?? null;
568
+ const availableCredits = numberValue(value.availableCredits);
569
+ const threshold = numberValue(value.threshold);
570
+ let status = stringValue(value.status) ?? "missing_credit_facts";
571
+ let reason = stringValue(value.reason) ?? "";
572
+ let freshnessPolicy;
573
+ if (status === "stale_credit_facts" &&
574
+ isFreshWithinMcpPaidInmailWindow(checkedAt, maxStalenessSeconds)) {
575
+ status = availableCredits < threshold ? "below_threshold" : "ok";
576
+ reason =
577
+ status === "below_threshold"
578
+ ? "MCP normalized paid InMail freshness to 4h; cached credits are fresh enough but below threshold."
579
+ : "MCP normalized paid InMail freshness to 4h; cached credits are fresh enough.";
580
+ freshnessPolicy = {
581
+ normalizedBy: "mcp",
582
+ minMaxStalenessSeconds: MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS,
583
+ originalMaxStalenessSeconds,
584
+ };
585
+ }
535
586
  return {
536
- status: stringValue(value.status) ?? "missing_credit_facts",
537
- reason: stringValue(value.reason) ?? "",
538
- checkedAt: stringValue(value.checkedAt) ?? null,
587
+ status,
588
+ reason,
589
+ checkedAt,
539
590
  balance: typeof value.balance === "number" ? value.balance : null,
540
591
  sentSince: numberValue(value.sentSince),
541
- availableCredits: numberValue(value.availableCredits),
542
- threshold: numberValue(value.threshold),
592
+ availableCredits,
593
+ threshold,
543
594
  campaignId: stringValue(value.campaignId) ?? null,
544
595
  campaignName: stringValue(value.campaignName) ?? null,
545
596
  tableId: stringValue(value.tableId) ?? null,
546
597
  tableName: stringValue(value.tableName) ?? null,
547
598
  columnId: stringValue(value.columnId) ?? null,
548
- maxStalenessSeconds: numberValue(value.maxStalenessSeconds),
599
+ maxStalenessSeconds,
549
600
  campaignClassification: campaignClassificationValue(value.campaignClassification) ?? null,
550
601
  connectionFallbackAvailable: booleanValue(value.connectionFallbackAvailable) ?? false,
551
602
  connectionFallbackCampaignId: stringValue(value.connectionFallbackCampaignId) ?? null,
@@ -553,8 +604,19 @@ function sanitizePaidInmail(value) {
553
604
  suggestedThreshold: typeof value.suggestedThreshold === "number"
554
605
  ? value.suggestedThreshold
555
606
  : null,
607
+ ...(freshnessPolicy ? { freshnessPolicy } : {}),
556
608
  };
557
609
  }
610
+ function paidInmailNeedsRefresh(value) {
611
+ if (!isRecord(value))
612
+ return false;
613
+ const status = stringValue(value.status);
614
+ return Boolean(status && PAID_INMAIL_REFRESH_STATUSES.has(status));
615
+ }
616
+ function filterFreshPaidInmailRefreshAction(action, paidInmail) {
617
+ return (action.type !== "refresh_paid_inmail_credits" ||
618
+ paidInmailNeedsRefresh(paidInmail));
619
+ }
558
620
  function sanitizeStructuredSenderPlans(value, selectedBySender) {
559
621
  if (!Array.isArray(value))
560
622
  return [];
@@ -566,10 +628,12 @@ function sanitizeStructuredSenderPlans(value, selectedBySender) {
566
628
  return Boolean(selectedLane && selectedBySender.get(plan.senderId) === selectedLane);
567
629
  })
568
630
  .map((plan) => {
631
+ const paidInmail = sanitizePaidInmail(plan.paidInmail);
569
632
  const nextActions = Array.isArray(plan.nextActions)
570
633
  ? plan.nextActions
571
634
  .map((action, index) => sanitizeStructuredAction(action, selectedBySender, "next", index + 1))
572
635
  .filter((action) => Boolean(action))
636
+ .filter((action) => filterFreshPaidInmailRefreshAction(action, paidInmail))
573
637
  : [];
574
638
  const manualAlternates = Array.isArray(plan.manualAlternates)
575
639
  ? plan.manualAlternates
@@ -614,7 +678,7 @@ function sanitizeStructuredSenderPlans(value, selectedBySender) {
614
678
  : {},
615
679
  coverageDisplay: stringArray(plan.coverageDisplay),
616
680
  campaignRanking: sanitizeCampaignRanking(plan.campaignRanking),
617
- paidInmail: sanitizePaidInmail(plan.paidInmail),
681
+ paidInmail,
618
682
  sourcePlan: sanitizeSourcePlan(plan.sourcePlan),
619
683
  refillReceipt: sanitizeRefillReceipt(plan.refillReceipt),
620
684
  nextActions,
@@ -640,6 +704,17 @@ function buildSanitizedGlobalActionQueue(senderRefillPlans) {
640
704
  rank: index + 1,
641
705
  }));
642
706
  }
707
+ function paidRefreshNeededSenderIds(senderRefillPlans) {
708
+ const senderIds = new Set();
709
+ for (const plan of senderRefillPlans) {
710
+ if (!paidInmailNeedsRefresh(plan.paidInmail))
711
+ continue;
712
+ const senderId = stringValue(plan.senderId);
713
+ if (senderId)
714
+ senderIds.add(senderId);
715
+ }
716
+ return senderIds;
717
+ }
643
718
  function emptyUnsupportedResult(result) {
644
719
  if (!isRecord(result) || !isRecord(result.target))
645
720
  return result;
@@ -682,7 +757,7 @@ function emptyUnsupportedResult(result) {
682
757
  mcpSanitizedRefillLanes: true,
683
758
  };
684
759
  }
685
- function sanitizeBlockers(blockers, selectedKeys) {
760
+ function sanitizeBlockers(blockers, selectedKeys, paidRefreshNeededSenderIds) {
686
761
  if (!Array.isArray(blockers))
687
762
  return [];
688
763
  return blockers.filter((item) => {
@@ -694,6 +769,10 @@ function sanitizeBlockers(blockers, selectedKeys) {
694
769
  return true;
695
770
  const detailSenderId = detail.split(":")[0];
696
771
  if (code.startsWith("paid_inmail_")) {
772
+ if (code === "paid_inmail_credit_stale" &&
773
+ !paidRefreshNeededSenderIds.has(detailSenderId)) {
774
+ return false;
775
+ }
697
776
  return selectedKeys.has(`${detailSenderId}:send_inmail_closed`);
698
777
  }
699
778
  if (detail.includes(":send_dm") || detail.includes(":send_inmail_open")) {
@@ -782,9 +861,10 @@ function sanitizeRefillTargetPlanResult(result) {
782
861
  const remainingScheduledGap = senderPlans.reduce((sum, plan) => sum + numberValue(plan.remainingScheduledGap), 0);
783
862
  const remainingProjectedGap = senderPlans.reduce((sum, plan) => sum + numberValue(plan.remainingProjectedGap), 0);
784
863
  const remainingReadyOrProjectedGap = senderPlans.reduce((sum, plan) => sum + numberValue(plan.remainingReadyOrProjectedGap), 0);
785
- const blockers = sanitizeBlockers(result.blockers, selectedKeys);
786
864
  const eligibleSenderLedger = sanitizeEligibleSenderLedger(target.eligibleSenderLedger, selectedBySender);
787
865
  const senderRefillPlans = sanitizeStructuredSenderPlans(target.senderRefillPlans, selectedBySender);
866
+ const paidRefreshNeededSenderIdSet = paidRefreshNeededSenderIds(senderRefillPlans);
867
+ const blockers = sanitizeBlockers(result.blockers, selectedKeys, paidRefreshNeededSenderIdSet);
788
868
  const globalActionQueue = buildSanitizedGlobalActionQueue(senderRefillPlans);
789
869
  const hasTerminalNoActionBlocker = globalActionQueue.length === 0 &&
790
870
  blockers.some((blocker) => TERMINAL_NO_ACTION_BLOCKER_CODES.has(String(blocker.code)));
@@ -831,7 +911,7 @@ function sanitizeRefillTargetPlanResult(result) {
831
911
  readyCounts: sanitizeCounts(result.coverage.readyCounts, selectedKeys),
832
912
  }
833
913
  : result.coverage,
834
- actionCandidates: sanitizeActionCandidates(result.actionCandidates, selectedKeys, status, remainingReadyOrProjectedGap, remainingProjectedGap),
914
+ actionCandidates: sanitizeActionCandidates(result.actionCandidates, selectedKeys, paidRefreshNeededSenderIdSet, status, remainingReadyOrProjectedGap, remainingProjectedGap),
835
915
  blockers,
836
916
  mcpSanitizedRefillLanes: true,
837
917
  };
@@ -584,6 +584,97 @@ export declare const allTools: ({
584
584
  required: string[];
585
585
  additionalProperties: boolean;
586
586
  };
587
+ } | {
588
+ name: string;
589
+ description: string;
590
+ inputSchema: {
591
+ type: string;
592
+ properties: {
593
+ campaignId: {
594
+ type: string;
595
+ description: string;
596
+ };
597
+ tableId: {
598
+ type: string;
599
+ };
600
+ targetPreparedMessages: {
601
+ type: string;
602
+ minimum: number;
603
+ maximum: number;
604
+ };
605
+ maxRowsToCheck: {
606
+ type: string;
607
+ minimum: number;
608
+ maximum: number;
609
+ description: string;
610
+ };
611
+ batchSize: {
612
+ type: string;
613
+ minimum: number;
614
+ maximum: number;
615
+ description: string;
616
+ };
617
+ maxBatchRows: {
618
+ type: string;
619
+ minimum: number;
620
+ maximum: number;
621
+ description: string;
622
+ };
623
+ approvalMode: {
624
+ type: string;
625
+ enum: string[];
626
+ description: string;
627
+ };
628
+ rowSelector: {
629
+ type: string;
630
+ description: string;
631
+ properties: {
632
+ type: {
633
+ type: string;
634
+ enum: string[];
635
+ };
636
+ limit: {
637
+ type: string;
638
+ minimum: number;
639
+ maximum: number;
640
+ };
641
+ };
642
+ required: string[];
643
+ additionalProperties: boolean;
644
+ };
645
+ excludeRowIds: {
646
+ type: string;
647
+ items: {
648
+ type: string;
649
+ };
650
+ maxItems: number;
651
+ description: string;
652
+ };
653
+ autoContinue: {
654
+ type: string;
655
+ };
656
+ senderId: {
657
+ type: string;
658
+ description: string;
659
+ };
660
+ actionType: {
661
+ type: string;
662
+ enum: string[];
663
+ description: string;
664
+ };
665
+ disableLowPassRateStop: {
666
+ type: string;
667
+ description: string;
668
+ };
669
+ workspaceId: {
670
+ type: string;
671
+ description: string;
672
+ };
673
+ jobId?: undefined;
674
+ };
675
+ required: string[];
676
+ additionalProperties: boolean;
677
+ };
587
678
  } | {
588
679
  name: string;
589
680
  description: string;
@@ -2343,6 +2434,13 @@ export declare const allTools: ({
2343
2434
  type: string;
2344
2435
  description: string;
2345
2436
  };
2437
+ actionType?: undefined;
2438
+ campaignId?: undefined;
2439
+ tableId?: undefined;
2440
+ columnId?: undefined;
2441
+ threshold?: undefined;
2442
+ maxStalenessSeconds?: undefined;
2443
+ paidInmailCreditsMaxStalenessSeconds?: undefined;
2346
2444
  };
2347
2445
  required: string[];
2348
2446
  additionalProperties: boolean;
@@ -35,6 +35,13 @@ export type SenderDetailResponse = {
35
35
  export type RefreshPaidInmailCreditsInput = {
36
36
  senderId: string;
37
37
  workspaceId: string;
38
+ actionType?: string | null;
39
+ campaignId?: string | null;
40
+ tableId?: string | null;
41
+ columnId?: string | null;
42
+ threshold?: number | null;
43
+ maxStalenessSeconds?: number | null;
44
+ paidInmailCreditsMaxStalenessSeconds?: number | null;
38
45
  };
39
46
  export type PaidInmailCreditStatus = {
40
47
  available: number;
@@ -68,6 +75,13 @@ export declare const senderToolDefinitions: ({
68
75
  description: string;
69
76
  };
70
77
  senderId?: undefined;
78
+ actionType?: undefined;
79
+ campaignId?: undefined;
80
+ tableId?: undefined;
81
+ columnId?: undefined;
82
+ threshold?: undefined;
83
+ maxStalenessSeconds?: undefined;
84
+ paidInmailCreditsMaxStalenessSeconds?: undefined;
71
85
  };
72
86
  required: never[];
73
87
  additionalProperties: boolean;
@@ -86,6 +100,59 @@ export declare const senderToolDefinitions: ({
86
100
  type: string;
87
101
  description: string;
88
102
  };
103
+ actionType?: undefined;
104
+ campaignId?: undefined;
105
+ tableId?: undefined;
106
+ columnId?: undefined;
107
+ threshold?: undefined;
108
+ maxStalenessSeconds?: undefined;
109
+ paidInmailCreditsMaxStalenessSeconds?: undefined;
110
+ };
111
+ required: string[];
112
+ additionalProperties: boolean;
113
+ };
114
+ } | {
115
+ name: string;
116
+ description: string;
117
+ inputSchema: {
118
+ type: string;
119
+ properties: {
120
+ senderId: {
121
+ type: string;
122
+ description: string;
123
+ };
124
+ workspaceId: {
125
+ type: string;
126
+ description: string;
127
+ };
128
+ actionType: {
129
+ type: string;
130
+ description: string;
131
+ };
132
+ campaignId: {
133
+ type: string;
134
+ description: string;
135
+ };
136
+ tableId: {
137
+ type: string;
138
+ description: string;
139
+ };
140
+ columnId: {
141
+ type: string;
142
+ description: string;
143
+ };
144
+ threshold: {
145
+ type: string;
146
+ description: string;
147
+ };
148
+ maxStalenessSeconds: {
149
+ type: string;
150
+ description: string;
151
+ };
152
+ paidInmailCreditsMaxStalenessSeconds: {
153
+ type: string;
154
+ description: string;
155
+ };
89
156
  };
90
157
  required: string[];
91
158
  additionalProperties: boolean;
@@ -11,6 +11,19 @@ function pickString(...values) {
11
11
  }
12
12
  return null;
13
13
  }
14
+ const DEFAULT_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS = 4 * 60 * 60;
15
+ function numberOrNull(value) {
16
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
17
+ }
18
+ function normalizePaidInmailCreditsMaxStalenessSeconds(...values) {
19
+ for (const value of values) {
20
+ const numeric = numberOrNull(value);
21
+ if (numeric === null || numeric <= 0)
22
+ continue;
23
+ return Math.max(Math.trunc(numeric), DEFAULT_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS);
24
+ }
25
+ return DEFAULT_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS;
26
+ }
14
27
  function computeDisplayName(sender) {
15
28
  const profile = sender?.unipileAccount?.profileData;
16
29
  const name = pickString(profile?.name);
@@ -148,6 +161,34 @@ export const senderToolDefinitions = [
148
161
  type: "string",
149
162
  description: "Explicit request-scoped workspace id for scheduled/yolo refill automation. Pass this instead of switching the shared active workspace.",
150
163
  },
164
+ actionType: {
165
+ type: "string",
166
+ description: "Optional action type context for the credit freshness check. Defaults to send_inmail_closed.",
167
+ },
168
+ campaignId: {
169
+ type: "string",
170
+ description: "Optional campaign context for the refresh receipt.",
171
+ },
172
+ tableId: {
173
+ type: "string",
174
+ description: "Optional workflow table context for the refresh receipt.",
175
+ },
176
+ columnId: {
177
+ type: "string",
178
+ description: "Optional workflow column context for the refresh receipt.",
179
+ },
180
+ threshold: {
181
+ type: "number",
182
+ description: "Optional paid InMail threshold used by the planner for this sender.",
183
+ },
184
+ maxStalenessSeconds: {
185
+ type: "number",
186
+ description: "Optional cache freshness window in seconds. Values below 4h are clamped to 4h.",
187
+ },
188
+ paidInmailCreditsMaxStalenessSeconds: {
189
+ type: "number",
190
+ description: "Optional legacy alias for maxStalenessSeconds. Values below 4h are clamped to 4h.",
191
+ },
151
192
  },
152
193
  required: ["senderId", "workspaceId"],
153
194
  additionalProperties: false,
@@ -213,7 +254,25 @@ export async function refreshPaidInmailCredits(input) {
213
254
  throw new Error("workspaceId is required.");
214
255
  }
215
256
  const requestOptions = workspaceRequestOptions(workspaceId);
216
- const body = { workspaceId };
257
+ const threshold = numberOrNull(input.threshold);
258
+ const body = {
259
+ workspaceId,
260
+ maxStalenessSeconds: normalizePaidInmailCreditsMaxStalenessSeconds(input.maxStalenessSeconds, input.paidInmailCreditsMaxStalenessSeconds),
261
+ };
262
+ const actionType = pickString(input.actionType);
263
+ const campaignId = pickString(input.campaignId);
264
+ const tableId = pickString(input.tableId);
265
+ const columnId = pickString(input.columnId);
266
+ if (actionType)
267
+ body.actionType = actionType;
268
+ if (campaignId)
269
+ body.campaignId = campaignId;
270
+ if (tableId)
271
+ body.tableId = tableId;
272
+ if (columnId)
273
+ body.columnId = columnId;
274
+ if (threshold !== null)
275
+ body.threshold = threshold;
217
276
  const result = await api.post(`/api/v3/mcp/senders/${encodeURIComponent(senderId)}/refresh-inmail-credits`, body, requestOptions);
218
277
  return {
219
278
  senderId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.521",
3
+ "version": "0.1.523",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",