@sellable/mcp 0.1.557 → 0.1.558

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 (63) hide show
  1. package/README.md +2 -13
  2. package/agents/registry.json +2 -2
  3. package/dist/api.js +6 -3
  4. package/dist/auth.d.ts +6 -0
  5. package/dist/auth.js +44 -2
  6. package/dist/refill-contract.d.ts +157 -0
  7. package/dist/refill-contract.js +487 -0
  8. package/dist/refill-run-client.d.ts +10 -0
  9. package/dist/refill-run-client.js +22 -0
  10. package/dist/refill-run-loop.d.ts +14 -2
  11. package/dist/refill-run-loop.js +160 -15
  12. package/dist/server.js +0 -23
  13. package/dist/tools/auth.d.ts +5 -0
  14. package/dist/tools/auth.js +49 -12
  15. package/dist/tools/campaign-message-preparation.d.ts +62 -0
  16. package/dist/tools/campaign-message-preparation.js +41 -0
  17. package/dist/tools/campaigns.js +2 -2
  18. package/dist/tools/csv-dnc.js +2 -2
  19. package/dist/tools/evergreen-refill-plan.d.ts +3 -0
  20. package/dist/tools/evergreen-refill-plan.js +29 -7
  21. package/dist/tools/leads.d.ts +32 -317
  22. package/dist/tools/leads.js +10 -171
  23. package/dist/tools/model-quality.js +6 -4
  24. package/dist/tools/prompts.d.ts +3 -3
  25. package/dist/tools/prompts.js +15 -7
  26. package/dist/tools/provider-preflight.d.ts +2 -65
  27. package/dist/tools/provider-preflight.js +10 -97
  28. package/dist/tools/readiness.d.ts +5 -89
  29. package/dist/tools/readiness.js +0 -66
  30. package/dist/tools/refill-executors.d.ts +38 -0
  31. package/dist/tools/refill-executors.js +222 -3
  32. package/dist/tools/refill-sends-v2.d.ts +118 -1
  33. package/dist/tools/refill-sends-v2.js +312 -3
  34. package/dist/tools/refill-sends.d.ts +678 -32
  35. package/dist/tools/refill-sends.js +274 -13
  36. package/dist/tools/refill-target-plan.js +486 -14
  37. package/dist/tools/registry.d.ts +115 -330
  38. package/dist/tools/registry.js +1 -7
  39. package/dist/tools/scheduler-fill-capacity.js +1 -1
  40. package/dist/tools/scheduler-run.d.ts +71 -0
  41. package/dist/tools/scheduler-run.js +203 -1
  42. package/dist/tools/setup-evergreen-campaigns.js +1 -1
  43. package/dist/tools/workspace-context.d.ts +1 -1
  44. package/dist/tools/workspace-context.js +8 -3
  45. package/dist/tools/workspace-export.js +2 -2
  46. package/dist/tools/workspaces.d.ts +48 -2
  47. package/dist/tools/workspaces.js +48 -5
  48. package/package.json +1 -1
  49. package/skills/create-campaign/SKILL.md +3 -3
  50. package/skills/create-campaign-v2/SKILL.md +1 -1
  51. package/skills/create-evergreen-campaigns/SKILL.md +16 -16
  52. package/skills/find-leads/SKILL.md +630 -48
  53. package/skills/refill-sends/SKILL.md +91 -353
  54. package/skills/refill-sends-v2/SKILL.md +6 -6
  55. package/skills/refill-sends-v2-workflow/SKILL.md +5 -5
  56. package/skills/refill-sends-v2-workflow/core/flow.v1.json +8 -8
  57. package/skills/refill-sends-workflow/SKILL.md +100 -743
  58. package/skills/refill-sends-workflow/core/contract.v2.json +543 -0
  59. package/skills/refill-sends-workflow/core/flow.v1.json +185 -1
  60. package/dist/tools/find-leads-runs.d.ts +0 -151
  61. package/dist/tools/find-leads-runs.js +0 -98
  62. package/skills/find-leads-v2/SKILL.md +0 -70
  63. package/skills/find-leads-v2/core/flow.v1.json +0 -31
@@ -3,6 +3,7 @@ import { startPrepareCampaignMessages } from "./campaign-message-preparation.js"
3
3
  import { startCampaign } from "./campaigns.js";
4
4
  import { confirmLeadList, importLeads, searchSignals, selectPromisingPosts, } from "./leads.js";
5
5
  import { markProviderPromptLoaded } from "./provider-preflight.js";
6
+ import { runSchedulerSweep, } from "./scheduler-run.js";
6
7
  import { refreshPaidInmailCredits } from "./senders.js";
7
8
  import { workspaceRequestOptions } from "./workspace-context.js";
8
9
  const PAID_INMAIL_REFRESH_MAX_ATTEMPTS = 3;
@@ -40,6 +41,160 @@ export function stringValue(value) {
40
41
  export function numberValue(value) {
41
42
  return typeof value === "number" && Number.isFinite(value) ? value : null;
42
43
  }
44
+ export function normalizeSchedulerChangedCounts(value) {
45
+ const changedCounts = recordValue(value);
46
+ if (!changedCounts ||
47
+ changedCounts.complete !== true ||
48
+ changedCounts.truncated !== false) {
49
+ return null;
50
+ }
51
+ const total = numberValue(changedCounts.total);
52
+ if (total === null || total < 0 || !Number.isInteger(total))
53
+ return null;
54
+ if (!Array.isArray(changedCounts.byCampaignTableActionType))
55
+ return null;
56
+ const groups = [];
57
+ const seen = new Set();
58
+ for (const rawGroup of changedCounts.byCampaignTableActionType) {
59
+ const group = recordValue(rawGroup);
60
+ const campaignId = stringValue(group?.campaignId);
61
+ const tableId = stringValue(group?.tableId);
62
+ const actionType = stringValue(group?.actionType);
63
+ const count = numberValue(group?.count);
64
+ if (!campaignId ||
65
+ !tableId ||
66
+ !actionType ||
67
+ count === null ||
68
+ count <= 0 ||
69
+ !Number.isInteger(count)) {
70
+ return null;
71
+ }
72
+ const key = `${campaignId}:${tableId}:${actionType}`;
73
+ if (seen.has(key))
74
+ return null;
75
+ seen.add(key);
76
+ groups.push({ campaignId, tableId, actionType, count });
77
+ }
78
+ groups.sort((a, b) => {
79
+ if (a.campaignId !== b.campaignId) {
80
+ return a.campaignId.localeCompare(b.campaignId);
81
+ }
82
+ if (a.tableId !== b.tableId)
83
+ return a.tableId.localeCompare(b.tableId);
84
+ return a.actionType.localeCompare(b.actionType);
85
+ });
86
+ if (groups.reduce((sum, group) => sum + group.count, 0) !== total) {
87
+ return null;
88
+ }
89
+ if (!Array.isArray(changedCounts.byCampaignTableActionTypeSender)) {
90
+ return null;
91
+ }
92
+ const senderGroups = [];
93
+ const senderSeen = new Set();
94
+ for (const rawGroup of changedCounts.byCampaignTableActionTypeSender) {
95
+ const group = recordValue(rawGroup);
96
+ const campaignId = stringValue(group?.campaignId);
97
+ const tableId = stringValue(group?.tableId);
98
+ const actionType = stringValue(group?.actionType);
99
+ const senderId = stringValue(group?.senderId);
100
+ const count = numberValue(group?.count);
101
+ if (!campaignId ||
102
+ !tableId ||
103
+ !actionType ||
104
+ !senderId ||
105
+ count === null ||
106
+ count <= 0 ||
107
+ !Number.isInteger(count)) {
108
+ return null;
109
+ }
110
+ const key = `${campaignId}:${tableId}:${actionType}:${senderId}`;
111
+ if (senderSeen.has(key))
112
+ return null;
113
+ senderSeen.add(key);
114
+ senderGroups.push({ campaignId, tableId, actionType, senderId, count });
115
+ }
116
+ if (senderGroups.reduce((sum, group) => sum + group.count, 0) !== total) {
117
+ return null;
118
+ }
119
+ return {
120
+ complete: true,
121
+ truncated: false,
122
+ total,
123
+ byCampaignTableActionType: groups,
124
+ byCampaignTableActionTypeSender: senderGroups,
125
+ };
126
+ }
127
+ function completeExpectedTargetSummary(value, expectedTargets, maxPlacements) {
128
+ const summary = recordValue(value);
129
+ const items = Array.isArray(summary?.items) ? summary.items : [];
130
+ if (summary?.complete !== true ||
131
+ summary.truncated !== false ||
132
+ numberValue(summary.requested) !== expectedTargets.length ||
133
+ numberValue(summary.included) == null ||
134
+ numberValue(summary.omitted) == null ||
135
+ numberValue(summary.included) + numberValue(summary.omitted) !==
136
+ expectedTargets.length ||
137
+ numberValue(summary.maxPlacements) !== maxPlacements ||
138
+ items.length !== expectedTargets.length) {
139
+ return false;
140
+ }
141
+ return items.every((rawItem, index) => {
142
+ const item = recordValue(rawItem);
143
+ const target = expectedTargets[index];
144
+ const senderIds = stringArray(item?.senderIds).sort();
145
+ const bySender = Array.isArray(item?.bySender) ? item.bySender : [];
146
+ const accounted = bySender
147
+ .map((entry) => stringValue(recordValue(entry)?.senderId))
148
+ .filter((senderId) => Boolean(senderId))
149
+ .sort();
150
+ return (Boolean(target) &&
151
+ item?.campaignId === target?.campaignId &&
152
+ item?.tableId === target?.tableId &&
153
+ item?.refillLaneKey === target?.refillLaneKey &&
154
+ (item?.outcome === "included" || item?.outcome === "omitted") &&
155
+ JSON.stringify(senderIds) === JSON.stringify(target?.senderIds) &&
156
+ JSON.stringify(accounted) === JSON.stringify(target?.senderIds));
157
+ });
158
+ }
159
+ export function normalizeSchedulerPrimitiveResult(value, expected) {
160
+ const response = recordValue(value);
161
+ const receipt = recordValue(response?.receipt);
162
+ const changedCounts = normalizeSchedulerChangedCounts(receipt?.changedCounts ?? response?.changedCounts);
163
+ const targetAuditComplete = !expected ||
164
+ (receipt?.receiptVersion === 3 &&
165
+ receipt.expectedTargetsHash === expected.expectedTargetsHash &&
166
+ receipt.maxPlacements === expected.maxPlacements &&
167
+ completeExpectedTargetSummary(receipt.expectedTargetSummary, expected.expectedTargets, expected.maxPlacements));
168
+ const schedulerStatus = stringValue(response?.status) ?? stringValue(receipt?.status) ?? "unknown";
169
+ if (!changedCounts || !targetAuditComplete) {
170
+ return {
171
+ status: "scheduler_receipt_incomplete",
172
+ schedulerStatus,
173
+ receipt: response?.receipt ?? null,
174
+ retryAfterMs: numberValue(response?.retryAfterMs),
175
+ changedCounts: null,
176
+ auditComplete: false,
177
+ blocker: "scheduler_receipt_incomplete",
178
+ };
179
+ }
180
+ return {
181
+ status: schedulerStatus,
182
+ receipt: response?.receipt ?? null,
183
+ retryAfterMs: numberValue(response?.retryAfterMs),
184
+ changedCounts,
185
+ auditComplete: true,
186
+ };
187
+ }
188
+ function exactDateValue(value) {
189
+ const targetDate = stringValue(value);
190
+ if (!targetDate || !/^\d{4}-\d{2}-\d{2}$/.test(targetDate))
191
+ return null;
192
+ const parsed = new Date(`${targetDate}T00:00:00.000Z`);
193
+ return !Number.isNaN(parsed.getTime()) &&
194
+ parsed.toISOString().slice(0, 10) === targetDate
195
+ ? targetDate
196
+ : null;
197
+ }
43
198
  export function stringArray(value) {
44
199
  if (!Array.isArray(value))
45
200
  return [];
@@ -225,7 +380,9 @@ export function classifyPaidInmailRefreshReceipt(value) {
225
380
  refreshed,
226
381
  error: stringValue(response?.error) ??
227
382
  stringValue(nestedReceipt?.error) ??
228
- (usableCurrentFacts ? null : "paid InMail credit refresh did not return usable current facts"),
383
+ (usableCurrentFacts
384
+ ? null
385
+ : "paid InMail credit refresh did not return usable current facts"),
229
386
  };
230
387
  }
231
388
  export function firstGlobalAction(plan) {
@@ -769,7 +926,67 @@ export async function executeOneYoloPrimitive(action, workspaceId) {
769
926
  case "wait_for_scheduler":
770
927
  case "wait_for_active_work":
771
928
  case "wait_for_source_import":
772
- return { status: "read_only_reread", result: waitResultForAction(action) };
929
+ return {
930
+ status: "read_only_reread",
931
+ result: waitResultForAction(action),
932
+ };
933
+ case "run_scheduler_sweep": {
934
+ const toolInput = actionToolInput(action);
935
+ const actionWorkspaceId = stringValue(toolInput.workspaceId);
936
+ const targetDate = exactDateValue(toolInput.targetDate);
937
+ const targetDateKey = exactDateValue(toolInput.targetDateKey);
938
+ const actionKey = stringValue(action.actionKey);
939
+ const requestKey = stringValue(toolInput.requestKey);
940
+ const targetShapeRevision = stringValue(toolInput.targetShapeRevision);
941
+ const receiptRequirements = recordValue(action.receiptRequirements);
942
+ const receiptGroups = stringArray(receiptRequirements?.groupBy);
943
+ const expectedTargets = Array.isArray(toolInput.expectedTargets)
944
+ ? toolInput.expectedTargets
945
+ : [];
946
+ const expectedTargetsHash = stringValue(toolInput.expectedTargetsHash);
947
+ const maxPlacements = numberValue(toolInput.maxPlacements);
948
+ if (!workspaceId ||
949
+ !actionWorkspaceId ||
950
+ actionWorkspaceId !== workspaceId ||
951
+ toolInput.action !== "run" ||
952
+ !targetDate ||
953
+ targetDateKey !== targetDate ||
954
+ !actionKey ||
955
+ requestKey !== actionKey ||
956
+ !targetShapeRevision ||
957
+ action.workspaceWide !== true ||
958
+ receiptRequirements?.nonTruncated !== true ||
959
+ receiptGroups.join(":") !== "campaignId:tableId:actionType:senderId" ||
960
+ receiptRequirements?.expectedTargetSummary !== true ||
961
+ expectedTargets.length === 0 ||
962
+ !expectedTargetsHash ||
963
+ maxPlacements === null ||
964
+ !Number.isInteger(maxPlacements) ||
965
+ maxPlacements <= 0) {
966
+ return {
967
+ status: "refused",
968
+ refusalReason: "run_scheduler_sweep action is missing exact workspace/date/revision scope or complete receipt requirements",
969
+ };
970
+ }
971
+ const result = await runSchedulerSweep({
972
+ workspaceId,
973
+ action: "run",
974
+ targetDate,
975
+ requestKey,
976
+ targetShapeRevision,
977
+ expectedTargets,
978
+ expectedTargetsHash,
979
+ maxPlacements,
980
+ });
981
+ return {
982
+ status: "executed_and_reread",
983
+ result: normalizeSchedulerPrimitiveResult(result, {
984
+ expectedTargets,
985
+ expectedTargetsHash,
986
+ maxPlacements,
987
+ }),
988
+ };
989
+ }
773
990
  case "prepare_messages": {
774
991
  const campaignId = actionCampaignId(action);
775
992
  const tableId = actionTableId(action) ?? undefined;
@@ -791,7 +1008,9 @@ export async function executeOneYoloPrimitive(action, workspaceId) {
791
1008
  tableId,
792
1009
  ...(workspaceId ? { workspaceId } : {}),
793
1010
  targetPreparedMessages,
794
- maxRowsToCheck: numberValue(toolInput.maxRowsToCheck) ?? 300,
1011
+ ...(numberValue(toolInput.maxRowsToCheck) != null
1012
+ ? { maxRowsToCheck: numberValue(toolInput.maxRowsToCheck) }
1013
+ : {}),
795
1014
  approvalMode,
796
1015
  rowSelector,
797
1016
  ...(excludeRowIds.length > 0 ? { excludeRowIds } : {}),
@@ -1,4 +1,4 @@
1
- type RefillSendsV2Input = {
1
+ export type RefillSendsV2Input = {
2
2
  workspaceId?: string;
3
3
  runId?: string;
4
4
  fence?: number;
@@ -6,6 +6,45 @@ type RefillSendsV2Input = {
6
6
  approvalMode?: "approve" | "mark_ready";
7
7
  senderIds?: string[];
8
8
  intent?: "auto" | "evergreen" | "plain" | "active";
9
+ targetDate?: string;
10
+ untilDate?: string;
11
+ horizonSendDays?: number;
12
+ scope?: Record<string, unknown>;
13
+ scopeHash?: string;
14
+ approval?: Record<string, unknown>;
15
+ targetPlan?: Record<string, unknown>;
16
+ approvalEcho?: {
17
+ scopeHash: string;
18
+ targetShapeRevision: string;
19
+ actionFingerprint: string;
20
+ expectedTargetsHash: string;
21
+ approvalExpiresAt: string;
22
+ approvalFingerprint: string;
23
+ };
24
+ };
25
+ type RefillV2ApprovalPreparationInput = Pick<RefillSendsV2Input, "workspaceId" | "intent" | "senderIds" | "approvalMode" | "targetDate" | "untilDate" | "horizonSendDays"> & {
26
+ targetPlan: Record<string, unknown>;
27
+ now?: Date;
28
+ approvalExpiresAt?: string;
29
+ };
30
+ export declare function resolveRefillV2InstalledIdentity(env?: Record<string, string | undefined>): {
31
+ readonly mcpVersion: string;
32
+ readonly installVersion: string;
33
+ readonly pluginVersion: string;
34
+ readonly cacheIdentity: string;
35
+ };
36
+ export declare const REFILL_V2_RUNTIME_IDENTITY: {
37
+ readonly contract: {
38
+ readonly version: "2.0.0";
39
+ readonly hash: string;
40
+ readonly cacheKey: "refill-contract-v1";
41
+ };
42
+ readonly installed: {
43
+ readonly mcpVersion: string;
44
+ readonly installVersion: string;
45
+ readonly pluginVersion: string;
46
+ readonly cacheIdentity: string;
47
+ };
9
48
  };
10
49
  export declare const refillSendsV2ToolDefinitions: {
11
50
  name: string;
@@ -44,11 +83,89 @@ export declare const refillSendsV2ToolDefinitions: {
44
83
  type: string;
45
84
  enum: string[];
46
85
  };
86
+ targetDate: {
87
+ type: string;
88
+ description: string;
89
+ };
90
+ untilDate: {
91
+ type: string;
92
+ description: string;
93
+ };
94
+ horizonSendDays: {
95
+ type: string;
96
+ description: string;
97
+ };
47
98
  };
48
99
  required: string[];
49
100
  additionalProperties: boolean;
50
101
  };
51
102
  }[];
103
+ export declare function prepareRefillSendsV2Approval(input: RefillV2ApprovalPreparationInput): Promise<{
104
+ status: "blocked";
105
+ blocker: string;
106
+ response: unknown;
107
+ } | {
108
+ status: "awaiting_approval";
109
+ runId: string;
110
+ fence: number;
111
+ scope: Record<string, unknown> | {
112
+ version: 2;
113
+ workspaceId: string;
114
+ intent: "active" | "plain" | "evergreen" | "auto";
115
+ senderIds: string[];
116
+ campaignIds: string[];
117
+ tableIds: string[];
118
+ actionIds: string[];
119
+ approvalMode: "mark_ready" | "approve";
120
+ dateSelector: {
121
+ kind: "target_date";
122
+ targetDate: string;
123
+ untilDate?: undefined;
124
+ horizonSendDays?: undefined;
125
+ } | {
126
+ kind: "until_date";
127
+ untilDate: string;
128
+ targetDate?: undefined;
129
+ horizonSendDays?: undefined;
130
+ } | {
131
+ kind: "horizon_send_days";
132
+ horizonSendDays: number;
133
+ targetDate?: undefined;
134
+ untilDate?: undefined;
135
+ } | {
136
+ kind: "scheduler_default";
137
+ targetDate?: undefined;
138
+ untilDate?: undefined;
139
+ horizonSendDays?: undefined;
140
+ };
141
+ senderTimezones: {
142
+ [k: string]: string;
143
+ };
144
+ expectedTargetsHash: string;
145
+ maxPlacements: number;
146
+ contract: {
147
+ version: "2.0.0";
148
+ hash: string;
149
+ cacheKey: "refill-contract-v1";
150
+ };
151
+ installed: {
152
+ mcpVersion: string;
153
+ installVersion: string;
154
+ pluginVersion: string;
155
+ cacheIdentity: string;
156
+ };
157
+ };
158
+ approvalFingerprint: string;
159
+ scopeHash: string;
160
+ targetShapeRevision: string;
161
+ actionFingerprint: string;
162
+ expectedTargetsHash: string;
163
+ sideEffectEnvelope: string[];
164
+ maxPlacements: number;
165
+ approvalExpiresAt: string;
166
+ blocker?: undefined;
167
+ response?: undefined;
168
+ }>;
52
169
  export declare function refillSendsV2Command(input: RefillSendsV2Input): Promise<{
53
170
  readOnly: boolean;
54
171
  mode: string;