@sellable/mcp 0.1.540 → 0.1.542
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.
- package/README.md +2 -5
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/refill-run-loop.d.ts +1 -9
- package/dist/refill-run-loop.js +91 -156
- package/dist/server.js +5 -1
- package/dist/tools/campaigns.d.ts +50 -1
- package/dist/tools/campaigns.js +6 -2
- package/dist/tools/evergreen-refill-plan.d.ts +0 -19
- package/dist/tools/evergreen-refill-plan.js +0 -19
- package/dist/tools/refill-executors.d.ts +7 -0
- package/dist/tools/refill-executors.js +23 -1
- package/dist/tools/refill-sends-v2.d.ts +0 -19
- package/dist/tools/refill-sends-v2.js +2 -31
- package/dist/tools/refill-sends.d.ts +12 -0
- package/dist/tools/refill-sends.js +58 -36
- package/dist/tools/refill-target-plan.js +6 -19
- package/dist/tools/refresh-sender-engagement.d.ts +50 -0
- package/dist/tools/refresh-sender-engagement.js +60 -0
- package/dist/tools/registry.d.ts +88 -32
- package/dist/tools/registry.js +2 -0
- package/dist/tools/scheduler-fill-capacity.js +1 -1
- package/dist/tools/scheduler-run.d.ts +1 -1
- package/dist/tools/scheduler-run.js +17 -20
- package/dist/tools/senders.d.ts +14 -8
- package/dist/tools/senders.js +21 -10
- package/package.json +1 -1
- package/skills/refill-sends/SKILL.md +1 -9
- package/skills/refill-sends-v2/SKILL.md +5 -18
- package/skills/refill-sends-v2-workflow/SKILL.md +2 -16
- package/skills/refill-sends-v2-workflow/core/flow.v1.json +3 -19
- package/skills/refill-sends-workflow/SKILL.md +3 -5
- package/skills/refill-sends-workflow/core/flow.v1.json +1 -1
- package/skills/refresh-sender-engagement/SKILL.md +25 -29
- package/dist/refill-date-window.d.ts +0 -34
- package/dist/refill-date-window.js +0 -210
- package/dist/tools/refill-sends-evergreen.d.ts +0 -28
- package/dist/tools/refill-sends-evergreen.js +0 -47
- package/skills/research/config.json +0 -9
|
@@ -14,6 +14,12 @@ export type PaidInmailCreditRefreshOptions = {
|
|
|
14
14
|
maxStalenessSeconds?: number | null;
|
|
15
15
|
paidInmailCreditsMaxStalenessSeconds?: number | null;
|
|
16
16
|
};
|
|
17
|
+
export type PaidInmailRefreshReceiptClassification = {
|
|
18
|
+
usableCurrentFacts: boolean;
|
|
19
|
+
status: string;
|
|
20
|
+
error: string | null;
|
|
21
|
+
refreshed: boolean;
|
|
22
|
+
};
|
|
17
23
|
export type YoloPrimitiveExecution = {
|
|
18
24
|
enabled: true;
|
|
19
25
|
status: "no_action" | "read_only_reread" | "executed_and_reread" | "refused" | "skipped_after_paid_refresh";
|
|
@@ -135,6 +141,7 @@ export declare function userAddedRowsLimitPayloadFromError(error: unknown): User
|
|
|
135
141
|
export declare function sourceImportInProgressPayloadFromError(error: unknown): SourceImportInProgressPayload | null;
|
|
136
142
|
export declare function paidRefreshActionFrom(value: unknown): PaidInmailRefreshAction | null;
|
|
137
143
|
export declare function collectPaidInmailRefreshActions(plan: unknown): PaidInmailRefreshAction[];
|
|
144
|
+
export declare function classifyPaidInmailRefreshReceipt(value: unknown): PaidInmailRefreshReceiptClassification;
|
|
138
145
|
export declare function firstGlobalAction(plan: unknown): Record<string, unknown> | null;
|
|
139
146
|
export declare function actionIds(action: Record<string, unknown>): Record<string, unknown>;
|
|
140
147
|
export declare function actionToolInput(action: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -10,6 +10,11 @@ const PAID_INMAIL_REFRESH_RETRY_DELAY_MS = Number(process.env.SELLABLE_MCP_PAID_
|
|
|
10
10
|
const SIGNAL_DISCOVERY_MIN_REFILL_POSTS = 3;
|
|
11
11
|
const SIGNAL_DISCOVERY_MAX_REFILL_POSTS = 10;
|
|
12
12
|
const SIGNAL_DISCOVERY_TARGET_ROWS_PER_POST = 150;
|
|
13
|
+
const USABLE_PAID_INMAIL_REFRESH_RECEIPT_STATUSES = new Set([
|
|
14
|
+
"fresh",
|
|
15
|
+
"refreshed",
|
|
16
|
+
"below_threshold",
|
|
17
|
+
]);
|
|
13
18
|
export function normalizeStrings(values) {
|
|
14
19
|
if (!Array.isArray(values))
|
|
15
20
|
return [];
|
|
@@ -206,6 +211,23 @@ export function collectPaidInmailRefreshActions(plan) {
|
|
|
206
211
|
add(action);
|
|
207
212
|
return [...bySender.values()];
|
|
208
213
|
}
|
|
214
|
+
export function classifyPaidInmailRefreshReceipt(value) {
|
|
215
|
+
const response = recordValue(value);
|
|
216
|
+
const nestedReceipt = recordValue(response?.receipt);
|
|
217
|
+
const status = stringValue(nestedReceipt?.status) ??
|
|
218
|
+
stringValue(response?.status) ??
|
|
219
|
+
(response?.refreshed === true ? "refreshed" : "not_refreshed");
|
|
220
|
+
const refreshed = response?.refreshed === true || nestedReceipt?.refreshed === true;
|
|
221
|
+
const usableCurrentFacts = refreshed || USABLE_PAID_INMAIL_REFRESH_RECEIPT_STATUSES.has(status);
|
|
222
|
+
return {
|
|
223
|
+
usableCurrentFacts,
|
|
224
|
+
status,
|
|
225
|
+
refreshed,
|
|
226
|
+
error: stringValue(response?.error) ??
|
|
227
|
+
stringValue(nestedReceipt?.error) ??
|
|
228
|
+
(usableCurrentFacts ? null : "paid InMail credit refresh did not return usable current facts"),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
209
231
|
export function firstGlobalAction(plan) {
|
|
210
232
|
const root = recordValue(plan);
|
|
211
233
|
const target = recordValue(root?.target);
|
|
@@ -659,7 +681,7 @@ export async function executeStartCampaignPrimitive(params) {
|
|
|
659
681
|
(params.authority.laneChainCampaignIds.includes(campaignId) ||
|
|
660
682
|
authorizedByPinnedCampaign)) {
|
|
661
683
|
try {
|
|
662
|
-
const result = await startCampaign(campaignId);
|
|
684
|
+
const result = await startCampaign(campaignId, params.workspaceId);
|
|
663
685
|
return {
|
|
664
686
|
executed: true,
|
|
665
687
|
type: "start_paused_campaign",
|
|
@@ -6,9 +6,6 @@ type RefillSendsV2Input = {
|
|
|
6
6
|
approvalMode?: "approve" | "mark_ready";
|
|
7
7
|
senderIds?: string[];
|
|
8
8
|
intent?: "auto" | "evergreen" | "plain" | "active";
|
|
9
|
-
targetDate?: string | null;
|
|
10
|
-
untilDate?: string | null;
|
|
11
|
-
horizonSendDays?: number | null;
|
|
12
9
|
};
|
|
13
10
|
export declare const refillSendsV2ToolDefinitions: {
|
|
14
11
|
name: string;
|
|
@@ -47,22 +44,6 @@ export declare const refillSendsV2ToolDefinitions: {
|
|
|
47
44
|
type: string;
|
|
48
45
|
enum: string[];
|
|
49
46
|
};
|
|
50
|
-
targetDate: {
|
|
51
|
-
type: string;
|
|
52
|
-
pattern: string;
|
|
53
|
-
description: string;
|
|
54
|
-
};
|
|
55
|
-
untilDate: {
|
|
56
|
-
type: string;
|
|
57
|
-
pattern: string;
|
|
58
|
-
description: string;
|
|
59
|
-
};
|
|
60
|
-
horizonSendDays: {
|
|
61
|
-
type: string;
|
|
62
|
-
minimum: number;
|
|
63
|
-
maximum: number;
|
|
64
|
-
description: string;
|
|
65
|
-
};
|
|
66
47
|
};
|
|
67
48
|
required: string[];
|
|
68
49
|
additionalProperties: boolean;
|
|
@@ -18,7 +18,7 @@ const BOUNDED_AUTHORITY = "Within the refill run, bounded approvals, preparation
|
|
|
18
18
|
export const refillSendsV2ToolDefinitions = [
|
|
19
19
|
{
|
|
20
20
|
name: "refill_sends_v2",
|
|
21
|
-
description: "Execute the refill sends v2 loop with run-record fencing, packet re-planning, shared refill executors, and resumable dry-run/real-run modes.
|
|
21
|
+
description: "Execute the refill sends v2 loop with run-record fencing, packet re-planning, shared refill executors, and resumable dry-run/real-run modes.",
|
|
22
22
|
inputSchema: {
|
|
23
23
|
type: "object",
|
|
24
24
|
properties: {
|
|
@@ -51,22 +51,6 @@ export const refillSendsV2ToolDefinitions = [
|
|
|
51
51
|
type: "string",
|
|
52
52
|
enum: ["auto", "evergreen", "plain", "active"],
|
|
53
53
|
},
|
|
54
|
-
targetDate: {
|
|
55
|
-
type: "string",
|
|
56
|
-
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
57
|
-
description: "Optional one exact sender-local date (YYYY-MM-DD) to refill.",
|
|
58
|
-
},
|
|
59
|
-
untilDate: {
|
|
60
|
-
type: "string",
|
|
61
|
-
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
62
|
-
description: "Optional inclusive sender-local through-date (YYYY-MM-DD) to refill.",
|
|
63
|
-
},
|
|
64
|
-
horizonSendDays: {
|
|
65
|
-
type: "number",
|
|
66
|
-
minimum: 1,
|
|
67
|
-
maximum: 7,
|
|
68
|
-
description: "Optional explicit next N sender-local send days to refill.",
|
|
69
|
-
},
|
|
70
54
|
},
|
|
71
55
|
required: ["workspaceId"],
|
|
72
56
|
additionalProperties: false,
|
|
@@ -115,9 +99,6 @@ export async function refillSendsV2Command(input) {
|
|
|
115
99
|
workspaceId,
|
|
116
100
|
intent: input.intent,
|
|
117
101
|
senderIds: input.senderIds,
|
|
118
|
-
targetDate: input.targetDate,
|
|
119
|
-
untilDate: input.untilDate,
|
|
120
|
-
horizonSendDays: input.horizonSendDays,
|
|
121
102
|
journal: true,
|
|
122
103
|
});
|
|
123
104
|
return {
|
|
@@ -132,9 +113,6 @@ export async function refillSendsV2Command(input) {
|
|
|
132
113
|
workspaceId,
|
|
133
114
|
intent: input.intent,
|
|
134
115
|
senderIds: input.senderIds,
|
|
135
|
-
targetDate: input.targetDate,
|
|
136
|
-
untilDate: input.untilDate,
|
|
137
|
-
horizonSendDays: input.horizonSendDays,
|
|
138
116
|
approvalMode: input.approvalMode ?? "approve",
|
|
139
117
|
resume: resumeHandle(input),
|
|
140
118
|
}, {
|
|
@@ -151,9 +129,6 @@ export async function refillSendsV2Command(input) {
|
|
|
151
129
|
workspaceId: planInput.workspaceId,
|
|
152
130
|
intent: planInput.intent,
|
|
153
131
|
senderIds: planInput.senderIds,
|
|
154
|
-
targetDate: planInput.targetDate,
|
|
155
|
-
untilDate: planInput.untilDate,
|
|
156
|
-
horizonSendDays: planInput.horizonSendDays,
|
|
157
132
|
runState: planInput.runState,
|
|
158
133
|
journal: planInput.journal,
|
|
159
134
|
}),
|
|
@@ -178,11 +153,7 @@ export async function refillSendsV2Command(input) {
|
|
|
178
153
|
localState: {
|
|
179
154
|
writeRefillWorkspaceState,
|
|
180
155
|
},
|
|
181
|
-
requestSchedulerRun: (workspaceId
|
|
182
|
-
workspaceId,
|
|
183
|
-
action: "run",
|
|
184
|
-
targetDate: options?.targetDate,
|
|
185
|
-
}),
|
|
156
|
+
requestSchedulerRun: (workspaceId) => runSchedulerSweep({ workspaceId, action: "run" }),
|
|
186
157
|
});
|
|
187
158
|
return {
|
|
188
159
|
...(await maybeAddLostFenceGuidance(result, { ...input, workspaceId })),
|
|
@@ -292,6 +292,8 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
|
|
|
292
292
|
attempts?: number;
|
|
293
293
|
error: string;
|
|
294
294
|
errors?: string[];
|
|
295
|
+
receiptStatus?: string;
|
|
296
|
+
receipt?: unknown;
|
|
295
297
|
}[];
|
|
296
298
|
refreshReceipts: {
|
|
297
299
|
senderId: string;
|
|
@@ -301,6 +303,8 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
|
|
|
301
303
|
approvalSource: string;
|
|
302
304
|
approvalPacket: PaidInmailRefreshApprovalPacket;
|
|
303
305
|
receipt: import("./senders.js").RefreshPaidInmailCreditsResponse;
|
|
306
|
+
receiptStatus: string;
|
|
307
|
+
usableCurrentFacts: boolean;
|
|
304
308
|
}[];
|
|
305
309
|
attemptedSenderIds: string[];
|
|
306
310
|
targetPlanReread: boolean;
|
|
@@ -309,6 +313,14 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
|
|
|
309
313
|
note: string;
|
|
310
314
|
};
|
|
311
315
|
yoloExecution: {
|
|
316
|
+
enabled: false;
|
|
317
|
+
status: "not_run_without_yolo";
|
|
318
|
+
selectedAction: null;
|
|
319
|
+
targetPlanReread: false;
|
|
320
|
+
result?: undefined;
|
|
321
|
+
refusalReason?: undefined;
|
|
322
|
+
postActionFirstAction?: undefined;
|
|
323
|
+
} | {
|
|
312
324
|
enabled: true;
|
|
313
325
|
status: "skipped_after_paid_refresh";
|
|
314
326
|
selectedAction: null;
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import { actionIds, collectPaidInmailRefreshActions, executeOneYoloPrimitive, firstGlobalAction, normalizeStrings, numberValue, recordValue, refreshPaidInmailCreditsWithRetry, stringValue, } from "./refill-executors.js";
|
|
1
|
+
import { actionIds, classifyPaidInmailRefreshReceipt, collectPaidInmailRefreshActions, executeOneYoloPrimitive, firstGlobalAction, normalizeStrings, numberValue, recordValue, refreshPaidInmailCreditsWithRetry, stringValue, } from "./refill-executors.js";
|
|
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
5
|
const MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS = 4 * 60 * 60;
|
|
6
6
|
function normalizeHorizonSendDays(value) {
|
|
7
|
-
if (value === undefined)
|
|
7
|
+
if (value === undefined || value === null)
|
|
8
8
|
return null;
|
|
9
|
-
if (typeof value !== "number" ||
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
value > 7) {
|
|
13
|
-
throw new Error("horizonSendDays must be an integer between 1 and 7.");
|
|
14
|
-
}
|
|
15
|
-
return value;
|
|
9
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
10
|
+
return null;
|
|
11
|
+
return Math.max(1, Math.min(7, Math.floor(value)));
|
|
16
12
|
}
|
|
17
13
|
function normalizeUntilDate(value) {
|
|
18
14
|
if (value === undefined || value === null || value === "")
|
|
@@ -95,7 +91,7 @@ export const refillSendsToolDefinitions = [
|
|
|
95
91
|
type: "number",
|
|
96
92
|
minimum: 1,
|
|
97
93
|
maximum: 7,
|
|
98
|
-
description: "Compatibility override for explicit
|
|
94
|
+
description: "Compatibility override for an explicit sender-local send-day horizon. Omit for the default scheduler-forward 48-hour target window.",
|
|
99
95
|
},
|
|
100
96
|
untilDate: {
|
|
101
97
|
type: "string",
|
|
@@ -188,7 +184,7 @@ export function refillSendsCommand(input = {}) {
|
|
|
188
184
|
horizonHours: horizonSendDays === null ? DEFAULT_SCHEDULER_FORWARD_HOURS : null,
|
|
189
185
|
description: horizonSendDays === null
|
|
190
186
|
? "Fill the default scheduler-forward 48-hour target window."
|
|
191
|
-
:
|
|
187
|
+
: "Fill the explicit compatibility horizon by sender-local send days.",
|
|
192
188
|
},
|
|
193
189
|
approvalMode,
|
|
194
190
|
campaignId: input.campaignId ?? null,
|
|
@@ -235,7 +231,7 @@ export function refillSendsCommand(input = {}) {
|
|
|
235
231
|
"Fresh reread get_campaign_refill_state immediately before any import, prep, approval, or horizon-fill mutation.",
|
|
236
232
|
"Maintain a target-window saturation ledger per selected sender from get_refill_target_plan: selected days, gross capacity, actual sent counts, future scheduler-owned scheduled counts, projected counts, ready-to-schedule buffer, remaining projected gap, paid InMail threshold feasibility, targetShapeRevision, stateRevision, and next MCP primitive.",
|
|
237
233
|
"Source/fallback primitives require receipt-proven exhaustion: existingRowFrontier.hasMoreFrontierRows:false, approvalCandidates:0, no fresh active prep, no stuckActiveCells, and no non-terminal approvedNotDispatched rows. Treat anomalies, stuckActiveCells, and non-terminal approvedNotDispatched as diagnose-and-report gates, not exhaustion. Terminal approvedNotDispatched blockers may be reported and then the ladder can proceed.",
|
|
238
|
-
"In --yolo, this tool automatically
|
|
234
|
+
"In manual/non-yolo, scheduled, and --yolo modes, this tool may automatically refresh stale or missing paid-InMail credit facts once per selected sender when the first target plan returns refresh_paid_inmail_credits candidates. This is the only newly allowed automatic write in manual/non-yolo mode; it reruns get_refill_target_plan and returns autoPaidInmailRefresh with attemptedSenderIds, refreshedPaidInmailSenderIds, failedPaidInmailRefreshes, and the post-refresh targetPlan before any prep/source-copy/bounded-approval/read-only wait action is chosen.",
|
|
239
235
|
"Freshness gate precedes scheduler wait: if any selected target.senderRefillPlans[].paidInmail.status is missing_credit_facts or stale_credit_facts, or the target plan contains refresh_paid_inmail_credits, do not enter wait_for_scheduler even when remainingReadyOrProjectedGap is 0; refresh exact sender credit facts once, reread get_refill_target_plan, then choose scheduler wait only if freshness is clean.",
|
|
240
236
|
"Scheduler-run receipt interpretation: cellsConsidered is allocation-attempt count, readyCellsFound is ready inventory before prefilters, and campaignScopeSummary must include the selected campaign/table before inferring it was ready-but-blocked. Read prefiltered, skipped, and deferred separately. For ready closed-InMail cells with stale paid-credit facts, route to refresh_paid_inmail_credits_then_rerun once, then rerun/status. wait_for_capacity_or_window means report loaded/capped/waiting and do not source or prep more rows. no_ready_cells_continue_refill_prep means return to the refill/prep ladder.",
|
|
241
237
|
"For wait_for_active_work and wait_for_scheduler, honor the receipt's absolute wait.deadlineAt when present. If the deadline is expired on this call, escalate to diagnostics with the receipt evidence instead of issuing another blind wait.",
|
|
@@ -379,7 +375,11 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
379
375
|
? { ...input, workspaceId: workspaceContext.context.workspaceId }
|
|
380
376
|
: input;
|
|
381
377
|
const command = refillSendsCommand(scopedInput);
|
|
382
|
-
|
|
378
|
+
const workspaceId = workspaceContext && workspaceContext.ok
|
|
379
|
+
? workspaceContext.context.workspaceId
|
|
380
|
+
: normalizeExplicitWorkspaceId(scopedInput.workspaceId);
|
|
381
|
+
const canRefreshPaidInmailFacts = yolo || executionMode === "scheduled" || Boolean(workspaceId);
|
|
382
|
+
if (!canRefreshPaidInmailFacts) {
|
|
383
383
|
return {
|
|
384
384
|
...command,
|
|
385
385
|
autoPaidInmailRefresh: {
|
|
@@ -387,7 +387,7 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
387
387
|
status: "not_run_without_yolo",
|
|
388
388
|
refreshedPaidInmailSenderIds: [],
|
|
389
389
|
failedPaidInmailRefreshes: [],
|
|
390
|
-
note: "Automatic paid InMail credit refresh
|
|
390
|
+
note: "Automatic paid InMail credit refresh requires --yolo, scheduled mode, or an explicit workspaceId so the request can refresh exact sender facts safely.",
|
|
391
391
|
},
|
|
392
392
|
yoloExecution: {
|
|
393
393
|
enabled: false,
|
|
@@ -397,9 +397,6 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
397
397
|
},
|
|
398
398
|
};
|
|
399
399
|
}
|
|
400
|
-
const workspaceId = workspaceContext && workspaceContext.ok
|
|
401
|
-
? workspaceContext.context.workspaceId
|
|
402
|
-
: normalizeExplicitWorkspaceId(scopedInput.workspaceId);
|
|
403
400
|
const targetPlanInput = targetPlanInputFor(scopedInput);
|
|
404
401
|
const targetPlanBeforePaidRefresh = await getRefillTargetPlan(targetPlanInput);
|
|
405
402
|
const refreshActions = collectPaidInmailRefreshActions(targetPlanBeforePaidRefresh);
|
|
@@ -425,7 +422,7 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
425
422
|
}
|
|
426
423
|
const refreshResult = await refreshPaidInmailCreditsWithRetry(action.senderId, workspaceId, paidRefreshOptionsFromApprovalPacket(approvalPacket));
|
|
427
424
|
if (refreshResult.receipt) {
|
|
428
|
-
|
|
425
|
+
const classification = classifyPaidInmailRefreshReceipt(refreshResult.receipt);
|
|
429
426
|
refreshReceipts.push({
|
|
430
427
|
senderId: action.senderId,
|
|
431
428
|
actionKey: action.actionKey ?? null,
|
|
@@ -434,7 +431,23 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
434
431
|
approvalSource: "explicit_yolo_flag",
|
|
435
432
|
approvalPacket,
|
|
436
433
|
receipt: refreshResult.receipt,
|
|
434
|
+
receiptStatus: classification.status,
|
|
435
|
+
usableCurrentFacts: classification.usableCurrentFacts,
|
|
437
436
|
});
|
|
437
|
+
if (classification.usableCurrentFacts) {
|
|
438
|
+
refreshedPaidInmailSenderIds.push(action.senderId);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
failedPaidInmailRefreshes.push({
|
|
442
|
+
senderId: action.senderId,
|
|
443
|
+
attempts: refreshResult.attempts,
|
|
444
|
+
error: classification.error ??
|
|
445
|
+
"paid InMail credit refresh did not return usable current facts",
|
|
446
|
+
errors: refreshResult.errors,
|
|
447
|
+
receiptStatus: classification.status,
|
|
448
|
+
receipt: refreshResult.receipt,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
438
451
|
}
|
|
439
452
|
else {
|
|
440
453
|
failedPaidInmailRefreshes.push({
|
|
@@ -449,8 +462,8 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
449
462
|
const targetPlan = refreshActions.length > 0
|
|
450
463
|
? await getRefillTargetPlan(targetPlanInput)
|
|
451
464
|
: targetPlanBeforePaidRefresh;
|
|
452
|
-
const selectedAction = refreshActions.length
|
|
453
|
-
const primitiveAttempt = refreshActions.length > 0
|
|
465
|
+
const selectedAction = yolo && refreshActions.length === 0 ? firstGlobalAction(targetPlan) : null;
|
|
466
|
+
const primitiveAttempt = !yolo || refreshActions.length > 0
|
|
454
467
|
? null
|
|
455
468
|
: await executeOneYoloPrimitive(selectedAction, workspaceId ?? undefined);
|
|
456
469
|
const shouldRereadAfterPrimitive = primitiveAttempt?.status === "executed_and_reread" ||
|
|
@@ -463,7 +476,9 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
463
476
|
...command,
|
|
464
477
|
targetPlan: finalTargetPlan,
|
|
465
478
|
targetPlanBeforePaidRefresh: refreshActions.length > 0 ? targetPlanBeforePaidRefresh : null,
|
|
466
|
-
targetPlanBeforeYoloPrimitive: refreshActions.length === 0 && postActionTargetPlan
|
|
479
|
+
targetPlanBeforeYoloPrimitive: yolo && refreshActions.length === 0 && postActionTargetPlan
|
|
480
|
+
? targetPlan
|
|
481
|
+
: null,
|
|
467
482
|
autoPaidInmailRefresh: {
|
|
468
483
|
enabled: true,
|
|
469
484
|
status: refreshActions.length === 0
|
|
@@ -482,23 +497,30 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
482
497
|
? "refill_sends refreshed stale paid InMail credit facts internally with bounded retries, once per sender, then returned the post-refresh targetPlan."
|
|
483
498
|
: "No stale or missing paid InMail credit refresh candidates were present in the initial target plan.",
|
|
484
499
|
},
|
|
485
|
-
yoloExecution:
|
|
500
|
+
yoloExecution: !yolo
|
|
486
501
|
? {
|
|
487
|
-
enabled:
|
|
488
|
-
status: "
|
|
502
|
+
enabled: false,
|
|
503
|
+
status: "not_run_without_yolo",
|
|
489
504
|
selectedAction: null,
|
|
490
|
-
targetPlanReread:
|
|
505
|
+
targetPlanReread: false,
|
|
491
506
|
}
|
|
492
|
-
:
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
:
|
|
502
|
-
|
|
507
|
+
: refreshActions.length > 0
|
|
508
|
+
? {
|
|
509
|
+
enabled: true,
|
|
510
|
+
status: "skipped_after_paid_refresh",
|
|
511
|
+
selectedAction: null,
|
|
512
|
+
targetPlanReread: true,
|
|
513
|
+
}
|
|
514
|
+
: {
|
|
515
|
+
enabled: true,
|
|
516
|
+
status: primitiveAttempt?.status ?? "no_action",
|
|
517
|
+
selectedAction,
|
|
518
|
+
result: primitiveAttempt?.result,
|
|
519
|
+
targetPlanReread: shouldRereadAfterPrimitive,
|
|
520
|
+
refusalReason: primitiveAttempt?.refusalReason,
|
|
521
|
+
postActionFirstAction: postActionTargetPlan
|
|
522
|
+
? firstGlobalAction(postActionTargetPlan)
|
|
523
|
+
: null,
|
|
524
|
+
},
|
|
503
525
|
};
|
|
504
526
|
}
|
|
@@ -1026,16 +1026,6 @@ function sanitizeRefillTargetPlanResult(result) {
|
|
|
1026
1026
|
mcpSanitizedRefillLanes: true,
|
|
1027
1027
|
};
|
|
1028
1028
|
}
|
|
1029
|
-
function stripInternalTargetDateKeys(value) {
|
|
1030
|
-
if (Array.isArray(value)) {
|
|
1031
|
-
return value.map((item) => stripInternalTargetDateKeys(item));
|
|
1032
|
-
}
|
|
1033
|
-
if (!isRecord(value))
|
|
1034
|
-
return value;
|
|
1035
|
-
return Object.fromEntries(Object.entries(value)
|
|
1036
|
-
.filter(([key]) => key !== "targetDateKey")
|
|
1037
|
-
.map(([key, nested]) => [key, stripInternalTargetDateKeys(nested)]));
|
|
1038
|
-
}
|
|
1039
1029
|
export const refillTargetPlanToolDefinitions = [
|
|
1040
1030
|
{
|
|
1041
1031
|
name: "get_refill_target_plan",
|
|
@@ -1052,7 +1042,7 @@ export const refillTargetPlanToolDefinitions = [
|
|
|
1052
1042
|
type: "number",
|
|
1053
1043
|
minimum: 1,
|
|
1054
1044
|
maximum: 7,
|
|
1055
|
-
description: "Compatibility override for explicit
|
|
1045
|
+
description: "Compatibility override for explicit sender-local send-day horizon planning. Omit for the default scheduler-forward 48-hour target window; no-send days contribute zero target.",
|
|
1056
1046
|
},
|
|
1057
1047
|
untilDate: {
|
|
1058
1048
|
type: "string",
|
|
@@ -1062,7 +1052,7 @@ export const refillTargetPlanToolDefinitions = [
|
|
|
1062
1052
|
targetDate: {
|
|
1063
1053
|
type: "string",
|
|
1064
1054
|
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
1065
|
-
description: "Optional
|
|
1055
|
+
description: "Optional exact sender-local YYYY-MM-DD date to fill. Distinct from untilDate: targetDate asks how many scheduler-fillable slots exist on only this date.",
|
|
1066
1056
|
},
|
|
1067
1057
|
campaignId: {
|
|
1068
1058
|
type: "string",
|
|
@@ -1112,14 +1102,11 @@ export const refillTargetPlanToolDefinitions = [
|
|
|
1112
1102
|
];
|
|
1113
1103
|
export async function getRefillTargetPlan(input = {}) {
|
|
1114
1104
|
const workspaceId = normalizeExplicitWorkspaceId(input.workspaceId);
|
|
1115
|
-
const targetDate = input.targetDate;
|
|
1116
|
-
const untilDate = targetDate ? undefined : input.untilDate;
|
|
1117
|
-
const horizonSendDays = targetDate || untilDate ? undefined : input.horizonSendDays;
|
|
1118
1105
|
const result = await postRefillTargetPlan({
|
|
1119
1106
|
intent: input.intent,
|
|
1120
|
-
horizonSendDays,
|
|
1121
|
-
untilDate,
|
|
1122
|
-
targetDate,
|
|
1107
|
+
horizonSendDays: input.horizonSendDays,
|
|
1108
|
+
untilDate: input.untilDate,
|
|
1109
|
+
targetDate: input.targetDate,
|
|
1123
1110
|
campaignId: input.campaignId,
|
|
1124
1111
|
tableId: input.tableId,
|
|
1125
1112
|
senderIds: input.senderIds,
|
|
@@ -1130,7 +1117,7 @@ export async function getRefillTargetPlan(input = {}) {
|
|
|
1130
1117
|
paidInmailCreditsMaxStalenessSeconds: MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS,
|
|
1131
1118
|
...(workspaceId ? { workspaceId } : {}),
|
|
1132
1119
|
}, workspaceId ?? undefined);
|
|
1133
|
-
const sanitized =
|
|
1120
|
+
const sanitized = sanitizeRefillTargetPlanResult(result);
|
|
1134
1121
|
if (!workspaceId || !isRecord(sanitized))
|
|
1135
1122
|
return sanitized;
|
|
1136
1123
|
return {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type RefreshSenderEngagementInput = {
|
|
2
|
+
workspaceId?: string;
|
|
3
|
+
mode?: "dry_run" | "apply";
|
|
4
|
+
senderId?: string;
|
|
5
|
+
tableId?: string;
|
|
6
|
+
maxPosts?: number;
|
|
7
|
+
maxEngagerPages?: number;
|
|
8
|
+
dryRunFingerprint?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const refreshSenderEngagementToolDefinitions: {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: string;
|
|
15
|
+
properties: {
|
|
16
|
+
workspaceId: {
|
|
17
|
+
type: string;
|
|
18
|
+
description: string;
|
|
19
|
+
};
|
|
20
|
+
mode: {
|
|
21
|
+
type: string;
|
|
22
|
+
enum: string[];
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
senderId: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
tableId: {
|
|
30
|
+
type: string;
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
maxPosts: {
|
|
34
|
+
type: string;
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
maxEngagerPages: {
|
|
38
|
+
type: string;
|
|
39
|
+
description: string;
|
|
40
|
+
};
|
|
41
|
+
dryRunFingerprint: {
|
|
42
|
+
type: string;
|
|
43
|
+
description: string;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
required: string[];
|
|
47
|
+
additionalProperties: boolean;
|
|
48
|
+
};
|
|
49
|
+
}[];
|
|
50
|
+
export declare function refreshSenderEngagementCommand(input: RefreshSenderEngagementInput): Promise<unknown>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { getApi } from "../api.js";
|
|
2
|
+
import { createWorkspaceContext } from "./workspace-context.js";
|
|
3
|
+
export const refreshSenderEngagementToolDefinitions = [
|
|
4
|
+
{
|
|
5
|
+
name: "refresh_sender_engagement",
|
|
6
|
+
description: "Refresh one sender-owned Post Engagers campaign through the product-native CampaignTrackedPost path. Dry-run first returns a human-reviewable receipt; apply requires that dry-run fingerprint and never sends, schedules, approves, prepares messages, launches campaigns, or mutates shared Signal Discovery lanes.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
workspaceId: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Explicit request-scoped workspace id.",
|
|
13
|
+
},
|
|
14
|
+
mode: {
|
|
15
|
+
type: "string",
|
|
16
|
+
enum: ["dry_run", "apply"],
|
|
17
|
+
description: "dry_run is read-only and returns an approval packet; apply requires dryRunFingerprint.",
|
|
18
|
+
},
|
|
19
|
+
senderId: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Exactly one Sellable sender id to refresh.",
|
|
22
|
+
},
|
|
23
|
+
tableId: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Optional campaign table id for disambiguating the sender-owned Post Engagers lane.",
|
|
26
|
+
},
|
|
27
|
+
maxPosts: {
|
|
28
|
+
type: "number",
|
|
29
|
+
description: "Maximum tracked/sender-authored posts to refresh.",
|
|
30
|
+
},
|
|
31
|
+
maxEngagerPages: {
|
|
32
|
+
type: "number",
|
|
33
|
+
description: "Maximum provider pages per engager source.",
|
|
34
|
+
},
|
|
35
|
+
dryRunFingerprint: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "Required for apply; returned by the matching dry_run receipt.",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ["workspaceId", "mode", "senderId"],
|
|
41
|
+
additionalProperties: false,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
function compactBody(input) {
|
|
46
|
+
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
|
|
47
|
+
}
|
|
48
|
+
export async function refreshSenderEngagementCommand(input) {
|
|
49
|
+
const workspace = createWorkspaceContext({
|
|
50
|
+
workspaceId: input.workspaceId,
|
|
51
|
+
executionMode: input.mode === "apply" ? "yolo" : "manual",
|
|
52
|
+
toolName: "refresh_sender_engagement",
|
|
53
|
+
});
|
|
54
|
+
if (!workspace.ok)
|
|
55
|
+
return workspace;
|
|
56
|
+
return getApi().post("/api/v3/mcp/refresh-sender-engagement", compactBody({
|
|
57
|
+
...input,
|
|
58
|
+
workspaceId: workspace.context.workspaceId,
|
|
59
|
+
}), workspace.context.requestOptions);
|
|
60
|
+
}
|