@sellable/mcp 0.1.497 → 0.1.499

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.
@@ -1,12 +1,15 @@
1
- import { getRefillTargetPlan } from "./refill-target-plan.js";
2
- import { refreshPaidInmailCredits } from "./senders.js";
1
+ import { getApi, SellableApiError } from "../api.js";
3
2
  import { startPrepareCampaignMessages } from "./campaign-message-preparation.js";
4
3
  import { confirmLeadList, importLeads, searchSignals, selectPromisingPosts, } from "./leads.js";
5
4
  import { markProviderPromptLoaded } from "./provider-preflight.js";
6
- import { getApi } from "../api.js";
5
+ import { getRefillTargetPlan } from "./refill-target-plan.js";
6
+ import { refreshPaidInmailCredits } from "./senders.js";
7
7
  const DEFAULT_SCHEDULER_FORWARD_HOURS = 48;
8
8
  const PAID_INMAIL_REFRESH_MAX_ATTEMPTS = 3;
9
9
  const PAID_INMAIL_REFRESH_RETRY_DELAY_MS = Number(process.env.SELLABLE_MCP_PAID_REFRESH_RETRY_DELAY_MS ?? "1000");
10
+ const SIGNAL_DISCOVERY_MIN_REFILL_POSTS = 3;
11
+ const SIGNAL_DISCOVERY_MAX_REFILL_POSTS = 10;
12
+ const SIGNAL_DISCOVERY_TARGET_ROWS_PER_POST = 150;
10
13
  function normalizeStrings(values) {
11
14
  if (!Array.isArray(values))
12
15
  return [];
@@ -66,6 +69,20 @@ function normalizeTargetDate(value) {
66
69
  }
67
70
  return trimmed;
68
71
  }
72
+ function userAddedRowsLimitPayloadFromError(error) {
73
+ if (!(error instanceof SellableApiError) || error.status !== 400) {
74
+ return null;
75
+ }
76
+ try {
77
+ const parsed = JSON.parse(error.body);
78
+ if (parsed?.code !== "USER_ADDED_ROWS_LIMIT_EXCEEDED")
79
+ return null;
80
+ return parsed;
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ }
69
86
  export const refillSendsToolDefinitions = [
70
87
  {
71
88
  name: "refill_sends",
@@ -194,9 +211,7 @@ export function refillSendsCommand(input = {}) {
194
211
  firstOperationalSteps: [
195
212
  'Load get_subskill_prompt({ subskillName: "refill-sends-workflow" }) before any product operation.',
196
213
  "A skill cannot create or invoke /goal by itself. If this refill is already running inside an active Codex goal, keep that goal open until every selected sender lane is horizon-filled by projected coverage (sent + scheduled), Christian explicitly stops/statuses the run, or a concrete non-scheduler blocker appears.",
197
- `Call get_refill_target_plan({ intent: "${intent}"${input.campaignId ? `, campaignId: "${input.campaignId}"` : ""}${input.tableId ? `, tableId: "${input.tableId}"` : ""}${senderIds.length > 0
198
- ? `, senderIds: ${JSON.stringify(senderIds)}`
199
- : ""}${senderNames.length > 0
214
+ `Call get_refill_target_plan({ intent: "${intent}"${input.campaignId ? `, campaignId: "${input.campaignId}"` : ""}${input.tableId ? `, tableId: "${input.tableId}"` : ""}${senderIds.length > 0 ? `, senderIds: ${JSON.stringify(senderIds)}` : ""}${senderNames.length > 0
200
215
  ? `, senderNames: ${JSON.stringify(senderNames)}`
201
216
  : ""}${senders.length > 0 ? `, senders: ${JSON.stringify(senders)}` : ""}${targetDate
202
217
  ? `, targetDate: "${targetDate}"`
@@ -490,6 +505,9 @@ function postIdsFromSignalSearch(summary, maxPosts) {
490
505
  : [];
491
506
  return uniqueStrings([...recommendedPostIds, ...topPostIds]).slice(0, Math.max(1, maxPosts));
492
507
  }
508
+ function maxSignalDiscoveryPostsForSourceLimit(sourceRowLimit) {
509
+ return Math.min(SIGNAL_DISCOVERY_MAX_REFILL_POSTS, Math.max(SIGNAL_DISCOVERY_MIN_REFILL_POSTS, Math.ceil(sourceRowLimit / SIGNAL_DISCOVERY_TARGET_ROWS_PER_POST)));
510
+ }
493
511
  function refillPrepareRequestHash(params) {
494
512
  return [
495
513
  "refill_sends",
@@ -541,7 +559,7 @@ async function continueSignalDiscoverySource(action) {
541
559
  numberValue(toolInput.targetRows) ??
542
560
  numberValue(action.targetRows) ??
543
561
  100)));
544
- const maxPostsToScrape = Math.min(5, Math.max(3, Math.ceil(sourceRowLimit / 500)));
562
+ const maxPostsToScrape = maxSignalDiscoveryPostsForSourceLimit(sourceRowLimit);
545
563
  if (!campaignOfferId || !sourceLeadListId || !sourceFingerprint) {
546
564
  return {
547
565
  status: "refused",
@@ -707,7 +725,9 @@ async function executeOneYoloPrimitive(action) {
707
725
  const toolInput = actionToolInput(action);
708
726
  const targetPreparedMessages = numberValue(toolInput.targetPreparedMessages);
709
727
  const approvalMode = toolInput.approvalMode === "approve" ? "approve" : "mark_ready";
710
- if (!campaignId || !targetPreparedMessages || targetPreparedMessages <= 0) {
728
+ if (!campaignId ||
729
+ !targetPreparedMessages ||
730
+ targetPreparedMessages <= 0) {
711
731
  return {
712
732
  status: "refused",
713
733
  refusalReason: "prepare_messages action is missing campaignId or bounded targetPreparedMessages",
@@ -747,21 +767,85 @@ async function executeOneYoloPrimitive(action) {
747
767
  refusalReason: "copy_selected_source_rows action is missing campaignOfferId, sourceLeadListId, or sourceFingerprint",
748
768
  };
749
769
  }
750
- if (sourceRowIds.length === 0 && (!sourceRowLimit || sourceRowLimit <= 0)) {
770
+ if (sourceRowIds.length === 0 &&
771
+ (!sourceRowLimit || sourceRowLimit <= 0)) {
751
772
  return {
752
773
  status: "refused",
753
774
  refusalReason: "copy_selected_source_rows action is missing exact sourceRowIds or a bounded sourceRowLimit",
754
775
  };
755
776
  }
756
- const result = await confirmLeadList({
777
+ const copyInput = {
757
778
  campaignOfferId,
758
779
  sourceLeadListId,
759
780
  currentStep: null,
760
781
  confirmed: true,
761
782
  sourceRowIds: sourceRowIds.length > 0 ? sourceRowIds : undefined,
762
- sourceRowLimit: sourceRowIds.length > 0 ? undefined : sourceRowLimit ?? undefined,
763
- reviewBatchLimit: sourceRowIds.length > 0 ? sourceRowIds.length : sourceRowLimit ?? undefined,
764
- });
783
+ sourceRowLimit: sourceRowIds.length > 0 ? undefined : (sourceRowLimit ?? undefined),
784
+ reviewBatchLimit: sourceRowIds.length > 0
785
+ ? sourceRowIds.length
786
+ : (sourceRowLimit ?? undefined),
787
+ };
788
+ let result;
789
+ try {
790
+ result = await confirmLeadList(copyInput);
791
+ }
792
+ catch (error) {
793
+ const rowLimitPayload = userAddedRowsLimitPayloadFromError(error);
794
+ if (!rowLimitPayload)
795
+ throw error;
796
+ const remainingRows = typeof rowLimitPayload.remainingRows === "number" &&
797
+ Number.isFinite(rowLimitPayload.remainingRows)
798
+ ? Math.floor(rowLimitPayload.remainingRows)
799
+ : 0;
800
+ if (remainingRows <= 0) {
801
+ return {
802
+ status: "refused",
803
+ refusalReason: rowLimitPayload.error ??
804
+ "selected campaign table is at the workflow row limit",
805
+ };
806
+ }
807
+ const retrySourceRowIds = sourceRowIds.length > 0 ? sourceRowIds.slice(0, remainingRows) : [];
808
+ const retrySourceRowLimit = retrySourceRowIds.length > 0
809
+ ? undefined
810
+ : Math.min(sourceRowLimit ?? remainingRows, remainingRows);
811
+ const retryReviewBatchLimit = retrySourceRowIds.length > 0
812
+ ? retrySourceRowIds.length
813
+ : retrySourceRowLimit;
814
+ if (!retryReviewBatchLimit || retryReviewBatchLimit <= 0) {
815
+ return {
816
+ status: "refused",
817
+ refusalReason: rowLimitPayload.error ??
818
+ "selected campaign table cannot accept more workflow rows",
819
+ };
820
+ }
821
+ const retryResult = await confirmLeadList({
822
+ ...copyInput,
823
+ sourceRowIds: retrySourceRowIds.length > 0 ? retrySourceRowIds : undefined,
824
+ sourceRowLimit: retrySourceRowIds.length > 0 ? undefined : retrySourceRowLimit,
825
+ reviewBatchLimit: retryReviewBatchLimit,
826
+ });
827
+ result =
828
+ retryResult && typeof retryResult === "object"
829
+ ? {
830
+ ...retryResult,
831
+ rowLimitRetry: {
832
+ code: rowLimitPayload.code,
833
+ maxRows: rowLimitPayload.maxRows,
834
+ currentRows: rowLimitPayload.currentRows,
835
+ requestedRows: rowLimitPayload.requestedRows,
836
+ remainingRows,
837
+ retriedRows: retryReviewBatchLimit,
838
+ },
839
+ }
840
+ : {
841
+ result: retryResult,
842
+ rowLimitRetry: {
843
+ code: rowLimitPayload.code,
844
+ remainingRows,
845
+ retriedRows: retryReviewBatchLimit,
846
+ },
847
+ };
848
+ }
765
849
  return { status: "executed_and_reread", result };
766
850
  }
767
851
  case "continue_signal_discovery_source":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.497",
3
+ "version": "0.1.499",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",