@sellable/mcp 0.1.497 → 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}"`
@@ -707,7 +719,9 @@ async function executeOneYoloPrimitive(action) {
707
719
  const toolInput = actionToolInput(action);
708
720
  const targetPreparedMessages = numberValue(toolInput.targetPreparedMessages);
709
721
  const approvalMode = toolInput.approvalMode === "approve" ? "approve" : "mark_ready";
710
- if (!campaignId || !targetPreparedMessages || targetPreparedMessages <= 0) {
722
+ if (!campaignId ||
723
+ !targetPreparedMessages ||
724
+ targetPreparedMessages <= 0) {
711
725
  return {
712
726
  status: "refused",
713
727
  refusalReason: "prepare_messages action is missing campaignId or bounded targetPreparedMessages",
@@ -747,21 +761,85 @@ async function executeOneYoloPrimitive(action) {
747
761
  refusalReason: "copy_selected_source_rows action is missing campaignOfferId, sourceLeadListId, or sourceFingerprint",
748
762
  };
749
763
  }
750
- if (sourceRowIds.length === 0 && (!sourceRowLimit || sourceRowLimit <= 0)) {
764
+ if (sourceRowIds.length === 0 &&
765
+ (!sourceRowLimit || sourceRowLimit <= 0)) {
751
766
  return {
752
767
  status: "refused",
753
768
  refusalReason: "copy_selected_source_rows action is missing exact sourceRowIds or a bounded sourceRowLimit",
754
769
  };
755
770
  }
756
- const result = await confirmLeadList({
771
+ const copyInput = {
757
772
  campaignOfferId,
758
773
  sourceLeadListId,
759
774
  currentStep: null,
760
775
  confirmed: true,
761
776
  sourceRowIds: sourceRowIds.length > 0 ? sourceRowIds : undefined,
762
- sourceRowLimit: sourceRowIds.length > 0 ? undefined : sourceRowLimit ?? undefined,
763
- reviewBatchLimit: sourceRowIds.length > 0 ? sourceRowIds.length : sourceRowLimit ?? undefined,
764
- });
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
+ }
765
843
  return { status: "executed_and_reread", result };
766
844
  }
767
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.497",
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",