@sellable/mcp 0.1.496 → 0.1.498

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,9 +1,9 @@
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");
@@ -66,6 +66,20 @@ function normalizeTargetDate(value) {
66
66
  }
67
67
  return trimmed;
68
68
  }
69
+ function userAddedRowsLimitPayloadFromError(error) {
70
+ if (!(error instanceof SellableApiError) || error.status !== 400) {
71
+ return null;
72
+ }
73
+ try {
74
+ const parsed = JSON.parse(error.body);
75
+ if (parsed?.code !== "USER_ADDED_ROWS_LIMIT_EXCEEDED")
76
+ return null;
77
+ return parsed;
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
69
83
  export const refillSendsToolDefinitions = [
70
84
  {
71
85
  name: "refill_sends",
@@ -194,9 +208,7 @@ export function refillSendsCommand(input = {}) {
194
208
  firstOperationalSteps: [
195
209
  'Load get_subskill_prompt({ subskillName: "refill-sends-workflow" }) before any product operation.',
196
210
  "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
211
+ `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
212
  ? `, senderNames: ${JSON.stringify(senderNames)}`
201
213
  : ""}${senders.length > 0 ? `, senders: ${JSON.stringify(senders)}` : ""}${targetDate
202
214
  ? `, targetDate: "${targetDate}"`
@@ -498,7 +510,6 @@ function refillPrepareRequestHash(params) {
498
510
  params.tableId ?? "no-table",
499
511
  actionSenderId(params.action) ?? "all-senders",
500
512
  actionSourceLeadListId(params.action) ?? "no-source-list",
501
- actionSourceFingerprint(params.action) ?? "no-source-fingerprint",
502
513
  actionActionType(params.action) ?? "no-action-type",
503
514
  params.approvalMode,
504
515
  ].join(":");
@@ -708,7 +719,9 @@ async function executeOneYoloPrimitive(action) {
708
719
  const toolInput = actionToolInput(action);
709
720
  const targetPreparedMessages = numberValue(toolInput.targetPreparedMessages);
710
721
  const approvalMode = toolInput.approvalMode === "approve" ? "approve" : "mark_ready";
711
- if (!campaignId || !targetPreparedMessages || targetPreparedMessages <= 0) {
722
+ if (!campaignId ||
723
+ !targetPreparedMessages ||
724
+ targetPreparedMessages <= 0) {
712
725
  return {
713
726
  status: "refused",
714
727
  refusalReason: "prepare_messages action is missing campaignId or bounded targetPreparedMessages",
@@ -748,21 +761,85 @@ async function executeOneYoloPrimitive(action) {
748
761
  refusalReason: "copy_selected_source_rows action is missing campaignOfferId, sourceLeadListId, or sourceFingerprint",
749
762
  };
750
763
  }
751
- if (sourceRowIds.length === 0 && (!sourceRowLimit || sourceRowLimit <= 0)) {
764
+ if (sourceRowIds.length === 0 &&
765
+ (!sourceRowLimit || sourceRowLimit <= 0)) {
752
766
  return {
753
767
  status: "refused",
754
768
  refusalReason: "copy_selected_source_rows action is missing exact sourceRowIds or a bounded sourceRowLimit",
755
769
  };
756
770
  }
757
- const result = await confirmLeadList({
771
+ const copyInput = {
758
772
  campaignOfferId,
759
773
  sourceLeadListId,
760
774
  currentStep: null,
761
775
  confirmed: true,
762
776
  sourceRowIds: sourceRowIds.length > 0 ? sourceRowIds : undefined,
763
- sourceRowLimit: sourceRowIds.length > 0 ? undefined : sourceRowLimit ?? undefined,
764
- reviewBatchLimit: sourceRowIds.length > 0 ? sourceRowIds.length : sourceRowLimit ?? undefined,
765
- });
777
+ sourceRowLimit: sourceRowIds.length > 0 ? undefined : (sourceRowLimit ?? undefined),
778
+ reviewBatchLimit: sourceRowIds.length > 0
779
+ ? sourceRowIds.length
780
+ : (sourceRowLimit ?? undefined),
781
+ };
782
+ let result;
783
+ try {
784
+ result = await confirmLeadList(copyInput);
785
+ }
786
+ catch (error) {
787
+ const rowLimitPayload = userAddedRowsLimitPayloadFromError(error);
788
+ if (!rowLimitPayload)
789
+ throw error;
790
+ const remainingRows = typeof rowLimitPayload.remainingRows === "number" &&
791
+ Number.isFinite(rowLimitPayload.remainingRows)
792
+ ? Math.floor(rowLimitPayload.remainingRows)
793
+ : 0;
794
+ if (remainingRows <= 0) {
795
+ return {
796
+ status: "refused",
797
+ refusalReason: rowLimitPayload.error ??
798
+ "selected campaign table is at the workflow row limit",
799
+ };
800
+ }
801
+ const retrySourceRowIds = sourceRowIds.length > 0 ? sourceRowIds.slice(0, remainingRows) : [];
802
+ const retrySourceRowLimit = retrySourceRowIds.length > 0
803
+ ? undefined
804
+ : Math.min(sourceRowLimit ?? remainingRows, remainingRows);
805
+ const retryReviewBatchLimit = retrySourceRowIds.length > 0
806
+ ? retrySourceRowIds.length
807
+ : retrySourceRowLimit;
808
+ if (!retryReviewBatchLimit || retryReviewBatchLimit <= 0) {
809
+ return {
810
+ status: "refused",
811
+ refusalReason: rowLimitPayload.error ??
812
+ "selected campaign table cannot accept more workflow rows",
813
+ };
814
+ }
815
+ const retryResult = await confirmLeadList({
816
+ ...copyInput,
817
+ sourceRowIds: retrySourceRowIds.length > 0 ? retrySourceRowIds : undefined,
818
+ sourceRowLimit: retrySourceRowIds.length > 0 ? undefined : retrySourceRowLimit,
819
+ reviewBatchLimit: retryReviewBatchLimit,
820
+ });
821
+ result =
822
+ retryResult && typeof retryResult === "object"
823
+ ? {
824
+ ...retryResult,
825
+ rowLimitRetry: {
826
+ code: rowLimitPayload.code,
827
+ maxRows: rowLimitPayload.maxRows,
828
+ currentRows: rowLimitPayload.currentRows,
829
+ requestedRows: rowLimitPayload.requestedRows,
830
+ remainingRows,
831
+ retriedRows: retryReviewBatchLimit,
832
+ },
833
+ }
834
+ : {
835
+ result: retryResult,
836
+ rowLimitRetry: {
837
+ code: rowLimitPayload.code,
838
+ remainingRows,
839
+ retriedRows: retryReviewBatchLimit,
840
+ },
841
+ };
842
+ }
766
843
  return { status: "executed_and_reread", result };
767
844
  }
768
845
  case "continue_signal_discovery_source":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.496",
3
+ "version": "0.1.498",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",