@sellable/mcp 0.1.533 → 0.1.535
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/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/refill-run-loop.js +36 -130
- package/dist/server.js +0 -4
- package/dist/tools/refill-executors.d.ts +7 -0
- package/dist/tools/refill-executors.js +22 -0
- package/dist/tools/refill-sends-v2.js +0 -2
- package/dist/tools/refill-sends.d.ts +12 -0
- package/dist/tools/refill-sends.js +52 -26
- package/dist/tools/registry.d.ts +0 -19
- package/dist/tools/registry.js +0 -2
- 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 +4 -10
- package/skills/refill-sends-workflow/SKILL.md +5 -11
- package/dist/tools/refill-sends-evergreen.d.ts +0 -28
- package/dist/tools/refill-sends-evergreen.js +0 -47
- package/dist/tools/scheduler-run.d.ts +0 -27
- package/dist/tools/scheduler-run.js +0 -45
package/dist/index-dev.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/refill-run-loop.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SellableApiError } from "./api.js";
|
|
2
2
|
import { buildRunStateFromLocalHints } from "./tools/evergreen-refill-plan.js";
|
|
3
|
-
import { prepareRowSelectorValue as defaultPrepareRowSelectorValue, refillPrepareRequestHash as defaultRefillPrepareRequestHash, } from "./tools/refill-executors.js";
|
|
3
|
+
import { classifyPaidInmailRefreshReceipt, prepareRowSelectorValue as defaultPrepareRowSelectorValue, refillPrepareRequestHash as defaultRefillPrepareRequestHash, } from "./tools/refill-executors.js";
|
|
4
4
|
const DEFAULT_BUDGETS = {
|
|
5
5
|
maxGateCyclesPerInvocation: 8,
|
|
6
6
|
pollIntervalMs: 15_000,
|
|
@@ -19,34 +19,6 @@ const REFILL_DONE_REASONS = new Set([
|
|
|
19
19
|
"not_an_evergreen_workspace",
|
|
20
20
|
"no_refillable_campaigns",
|
|
21
21
|
]);
|
|
22
|
-
const SCHEDULER_RUN_ENVELOPE_STATUSES = new Set([
|
|
23
|
-
"ran",
|
|
24
|
-
"attached",
|
|
25
|
-
"backoff",
|
|
26
|
-
"window_closed_noop",
|
|
27
|
-
"failed",
|
|
28
|
-
]);
|
|
29
|
-
const SCHEDULER_RUN_RECEIPT_STATUSES = new Set([
|
|
30
|
-
"ran",
|
|
31
|
-
"window_closed_noop",
|
|
32
|
-
"failed",
|
|
33
|
-
]);
|
|
34
|
-
const SCHEDULER_RUN_SKIP_REASONS = [
|
|
35
|
-
"window_closed",
|
|
36
|
-
"daily_limit",
|
|
37
|
-
"cooldown",
|
|
38
|
-
"sender_gate",
|
|
39
|
-
"credit_threshold",
|
|
40
|
-
"billing_blocked",
|
|
41
|
-
"duplicate_lead",
|
|
42
|
-
"no_senders",
|
|
43
|
-
"other",
|
|
44
|
-
];
|
|
45
|
-
const SCHEDULER_RUN_HARD_BLOCK_REASONS = new Set([
|
|
46
|
-
"billing_blocked",
|
|
47
|
-
"credit_threshold",
|
|
48
|
-
"cooldown",
|
|
49
|
-
]);
|
|
50
22
|
function isRecord(value) {
|
|
51
23
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
52
24
|
}
|
|
@@ -68,89 +40,6 @@ function arrayValue(value) {
|
|
|
68
40
|
function hasBlocker(value, blocker) {
|
|
69
41
|
return isRecord(value) && value.blocker === blocker;
|
|
70
42
|
}
|
|
71
|
-
function schedulerRunSkipReasons(value) {
|
|
72
|
-
const raw = recordValue(value) ?? {};
|
|
73
|
-
const normalized = {};
|
|
74
|
-
for (const reason of SCHEDULER_RUN_SKIP_REASONS) {
|
|
75
|
-
normalized[reason] = numberValue(raw[reason]) ?? 0;
|
|
76
|
-
}
|
|
77
|
-
return normalized;
|
|
78
|
-
}
|
|
79
|
-
function dominantSchedulerRunSkipReason(skipReasons) {
|
|
80
|
-
let dominant = null;
|
|
81
|
-
let dominantCount = 0;
|
|
82
|
-
for (const reason of SCHEDULER_RUN_SKIP_REASONS) {
|
|
83
|
-
const count = skipReasons[reason] ?? 0;
|
|
84
|
-
if (count > dominantCount) {
|
|
85
|
-
dominant = reason;
|
|
86
|
-
dominantCount = count;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return dominantCount > 0 ? dominant : null;
|
|
90
|
-
}
|
|
91
|
-
function sanitizeSchedulerRunReceipt(raw) {
|
|
92
|
-
const envelope = recordValue(raw);
|
|
93
|
-
if (!envelope)
|
|
94
|
-
return null;
|
|
95
|
-
const status = stringValue(envelope.status);
|
|
96
|
-
if (!status || !SCHEDULER_RUN_ENVELOPE_STATUSES.has(status))
|
|
97
|
-
return null;
|
|
98
|
-
const retryAfterMs = envelope.retryAfterMs == null ? null : numberValue(envelope.retryAfterMs);
|
|
99
|
-
if (envelope.retryAfterMs != null && retryAfterMs == null)
|
|
100
|
-
return null;
|
|
101
|
-
const receiptInput = envelope.receipt;
|
|
102
|
-
let receipt = null;
|
|
103
|
-
let dominantSkipReason = null;
|
|
104
|
-
if (receiptInput != null) {
|
|
105
|
-
const rawReceipt = recordValue(receiptInput);
|
|
106
|
-
if (!rawReceipt)
|
|
107
|
-
return null;
|
|
108
|
-
const receiptStatus = stringValue(rawReceipt.status);
|
|
109
|
-
if (!receiptStatus || !SCHEDULER_RUN_RECEIPT_STATUSES.has(receiptStatus)) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
const cellsConsidered = numberValue(rawReceipt.cellsConsidered);
|
|
113
|
-
const cellsScheduled = numberValue(rawReceipt.cellsScheduled);
|
|
114
|
-
const cellsSkipped = numberValue(rawReceipt.cellsSkipped);
|
|
115
|
-
const cellsDeferred = numberValue(rawReceipt.cellsDeferred);
|
|
116
|
-
const tablesFilteredForNoCapacity = numberValue(rawReceipt.tablesFilteredForNoCapacity);
|
|
117
|
-
if (cellsConsidered == null ||
|
|
118
|
-
cellsScheduled == null ||
|
|
119
|
-
cellsSkipped == null ||
|
|
120
|
-
cellsDeferred == null ||
|
|
121
|
-
tablesFilteredForNoCapacity == null) {
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
const skipReasons = schedulerRunSkipReasons(rawReceipt.skipReasons);
|
|
125
|
-
dominantSkipReason = dominantSchedulerRunSkipReason(skipReasons);
|
|
126
|
-
receipt = {
|
|
127
|
-
status: receiptStatus,
|
|
128
|
-
cellsConsidered,
|
|
129
|
-
cellsScheduled,
|
|
130
|
-
cellsSkipped,
|
|
131
|
-
cellsDeferred,
|
|
132
|
-
tablesFilteredForNoCapacity,
|
|
133
|
-
skipReasons,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
return {
|
|
137
|
-
status,
|
|
138
|
-
retryAfterMs,
|
|
139
|
-
receipt,
|
|
140
|
-
dominantSkipReason,
|
|
141
|
-
hardBlocked: dominantSkipReason != null &&
|
|
142
|
-
SCHEDULER_RUN_HARD_BLOCK_REASONS.has(dominantSkipReason),
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
function schedulerRunReceiptIsFreshZeroScheduled(schedulerRunReceipt) {
|
|
146
|
-
const status = stringValue(schedulerRunReceipt?.status);
|
|
147
|
-
const fresh = status === "ran" || status === "window_closed_noop";
|
|
148
|
-
if (!fresh)
|
|
149
|
-
return false;
|
|
150
|
-
const receipt = recordValue(schedulerRunReceipt?.receipt);
|
|
151
|
-
return (status === "window_closed_noop" ||
|
|
152
|
-
numberValue(receipt?.cellsScheduled) === 0);
|
|
153
|
-
}
|
|
154
43
|
function isLeaseLost(value) {
|
|
155
44
|
return hasBlocker(value, "lease_lost");
|
|
156
45
|
}
|
|
@@ -587,22 +476,31 @@ async function gateBootstrap(input, deps, ctx) {
|
|
|
587
476
|
const refreshedSenderIds = new Set(arrayValue(recordValue(ctx.runState.creditTrust)?.senderIds)
|
|
588
477
|
.map((entry) => stringValue(entry))
|
|
589
478
|
.filter((entry) => Boolean(entry)));
|
|
479
|
+
const attemptedSenderIds = new Set(arrayValue(recordValue(ctx.runState.creditTrust)?.attemptedSenderIds)
|
|
480
|
+
.map((entry) => stringValue(entry))
|
|
481
|
+
.filter((entry) => Boolean(entry)));
|
|
590
482
|
const receipts = [];
|
|
591
483
|
for (const entry of paidCredit) {
|
|
592
484
|
const senderId = stringValue(entry.senderId);
|
|
593
485
|
if (!senderId ||
|
|
594
486
|
!entry.plannedJitRefresh ||
|
|
595
|
-
refreshedSenderIds.has(senderId)
|
|
487
|
+
refreshedSenderIds.has(senderId) ||
|
|
488
|
+
attemptedSenderIds.has(senderId)) {
|
|
596
489
|
continue;
|
|
597
490
|
}
|
|
598
491
|
const receipt = await deps.executors.refreshPaidInmailCreditsWithRetry(senderId, input.workspaceId);
|
|
599
|
-
|
|
600
|
-
|
|
492
|
+
attemptedSenderIds.add(senderId);
|
|
493
|
+
const classification = classifyPaidInmailRefreshReceipt(receipt.receipt);
|
|
494
|
+
receipts.push({ senderId, receipt, classification });
|
|
495
|
+
if (classification.usableCurrentFacts) {
|
|
496
|
+
refreshedSenderIds.add(senderId);
|
|
497
|
+
}
|
|
601
498
|
}
|
|
602
499
|
const nextRunState = mergeRunState(ctx.runState, {
|
|
603
500
|
creditTrust: {
|
|
604
501
|
refreshedAt: (deps.now?.() ?? new Date()).toISOString(),
|
|
605
502
|
senderIds: [...refreshedSenderIds],
|
|
503
|
+
attemptedSenderIds: [...attemptedSenderIds],
|
|
606
504
|
receipts,
|
|
607
505
|
},
|
|
608
506
|
});
|
|
@@ -1124,34 +1022,42 @@ async function verifySchedulerWait(input, deps, ctx, action, budgets) {
|
|
|
1124
1022
|
const enteredAt = stringValue(progress.schedulerWaitEnteredAt) ??
|
|
1125
1023
|
(deps.now?.() ?? new Date()).toISOString();
|
|
1126
1024
|
const jitFired = booleanValue(progress.schedulerJitFired) ?? false;
|
|
1127
|
-
let schedulerRunReceipt = recordValue(progress.schedulerRunReceipt) ?? null;
|
|
1128
1025
|
if (!jitFired) {
|
|
1129
1026
|
const senderId = actionSenderId(action);
|
|
1130
1027
|
if (senderId) {
|
|
1131
|
-
await deps.executors.refreshPaidInmailCreditsWithRetry(senderId, input.workspaceId);
|
|
1028
|
+
const refresh = await deps.executors.refreshPaidInmailCreditsWithRetry(senderId, input.workspaceId);
|
|
1029
|
+
const classification = classifyPaidInmailRefreshReceipt(refresh.receipt);
|
|
1030
|
+
if (!classification.usableCurrentFacts) {
|
|
1031
|
+
return {
|
|
1032
|
+
status: "blocked",
|
|
1033
|
+
blocker: "paid_inmail_refresh_failed",
|
|
1034
|
+
runId: ctx.runId,
|
|
1035
|
+
fence: ctx.fence,
|
|
1036
|
+
gate: ctx.gate,
|
|
1037
|
+
guidance: "Paid InMail credit refresh did not return usable current facts, so scheduler JIT was not requested.",
|
|
1038
|
+
report: {
|
|
1039
|
+
senderId,
|
|
1040
|
+
receiptStatus: classification.status,
|
|
1041
|
+
error: classification.error,
|
|
1042
|
+
receipt: refresh.receipt,
|
|
1043
|
+
attempts: refresh.attempts,
|
|
1044
|
+
errors: refresh.errors,
|
|
1045
|
+
},
|
|
1046
|
+
journalPath: ctx.journalPath,
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1132
1049
|
}
|
|
1133
1050
|
if (deps.requestSchedulerRun) {
|
|
1134
|
-
|
|
1135
|
-
schedulerRunReceipt = sanitizeSchedulerRunReceipt(await deps.requestSchedulerRun(input.workspaceId));
|
|
1136
|
-
}
|
|
1137
|
-
catch {
|
|
1138
|
-
schedulerRunReceipt = null;
|
|
1139
|
-
}
|
|
1051
|
+
await deps.requestSchedulerRun(input.workspaceId);
|
|
1140
1052
|
}
|
|
1141
1053
|
ctx.runState = mergeRunState(ctx.runState, {
|
|
1142
1054
|
progress: mergeProgress(ctx, {
|
|
1143
1055
|
schedulerWaitEnteredAt: enteredAt,
|
|
1144
1056
|
schedulerJitFired: true,
|
|
1145
|
-
...(schedulerRunReceipt ? { schedulerRunReceipt } : {}),
|
|
1146
1057
|
}),
|
|
1147
1058
|
});
|
|
1148
1059
|
}
|
|
1149
|
-
|
|
1150
|
-
// EDGE-2: the on-demand run only executes placement; cron can still process
|
|
1151
|
-
// due/timed-out cells later. We still do one readback, then avoid burning the
|
|
1152
|
-
// full wait budget when the fresh receipt says nothing was placeable now.
|
|
1153
|
-
const readbackBudget = zeroScheduledFresh ? 1 : budgets.maxSchedulerReadbacks;
|
|
1154
|
-
for (let poll = 0; poll < readbackBudget; poll += 1) {
|
|
1060
|
+
for (let poll = 0; poll < budgets.maxSchedulerReadbacks; poll += 1) {
|
|
1155
1061
|
const fresh = await deps.readPlan({
|
|
1156
1062
|
workspaceId: input.workspaceId,
|
|
1157
1063
|
intent: input.intent,
|
package/dist/server.js
CHANGED
|
@@ -45,7 +45,6 @@ import { allTools } from "./tools/registry.js";
|
|
|
45
45
|
import { getRows, getTableRows, getTableRowsMinimal } from "./tools/rows.js";
|
|
46
46
|
import { addRubricItem, checkRubric, deleteRubricItem, draftRubrics, saveRubrics, selectNecessaryRubrics, updateRubricItem, waitForRubricResults, } from "./tools/rubrics.js";
|
|
47
47
|
import { getSchedulerFillCapacity } from "./tools/scheduler-fill-capacity.js";
|
|
48
|
-
import { runSchedulerSweep } from "./tools/scheduler-run.js";
|
|
49
48
|
import { getSenderRoutingTool, setSenderRoutingTool, } from "./tools/sender-routing.js";
|
|
50
49
|
import { getSender, listSenders, refreshPaidInmailCredits, } from "./tools/senders.js";
|
|
51
50
|
import { attachRecommendedSequence, attachSequence, createWorkflowTable, } from "./tools/sequencer.js";
|
|
@@ -236,9 +235,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
236
235
|
case "get_scheduler_fill_capacity":
|
|
237
236
|
result = await getSchedulerFillCapacity(args);
|
|
238
237
|
break;
|
|
239
|
-
case "run_scheduler_sweep":
|
|
240
|
-
result = await runSchedulerSweep(args);
|
|
241
|
-
break;
|
|
242
238
|
case "refill_sends":
|
|
243
239
|
result = await executeRefillSendsCommand(args);
|
|
244
240
|
break;
|
|
@@ -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);
|
|
@@ -5,7 +5,6 @@ import { runRefillV2Loop } from "../refill-run-loop.js";
|
|
|
5
5
|
import { getPrepareCampaignMessagesStatus } from "./campaign-message-preparation.js";
|
|
6
6
|
import { getRefillPlanV2 } from "./evergreen-refill-plan.js";
|
|
7
7
|
import { executeOneYoloPrimitive, executeStartCampaignPrimitive, prepareRowSelectorValue, refillPrepareRequestHash, refreshPaidInmailCreditsWithRetry, } from "./refill-executors.js";
|
|
8
|
-
import { runSchedulerSweep } from "./scheduler-run.js";
|
|
9
8
|
const FORBIDDEN_ACTIONS = [
|
|
10
9
|
"Do not schedule sends.",
|
|
11
10
|
"Do not send messages.",
|
|
@@ -153,7 +152,6 @@ export async function refillSendsV2Command(input) {
|
|
|
153
152
|
localState: {
|
|
154
153
|
writeRefillWorkspaceState,
|
|
155
154
|
},
|
|
156
|
-
requestSchedulerRun: (workspaceId) => runSchedulerSweep({ workspaceId, action: "run" }),
|
|
157
155
|
});
|
|
158
156
|
return {
|
|
159
157
|
...(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,4 +1,4 @@
|
|
|
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;
|
|
@@ -231,7 +231,7 @@ export function refillSendsCommand(input = {}) {
|
|
|
231
231
|
"Fresh reread get_campaign_refill_state immediately before any import, prep, approval, or horizon-fill mutation.",
|
|
232
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.",
|
|
233
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.",
|
|
234
|
-
"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.",
|
|
235
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.",
|
|
236
236
|
"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.",
|
|
237
237
|
"Do not present paid-credit refresh as the next operator action after refill_sends returns autoPaidInmailRefresh and the post-refresh targetPlan.",
|
|
@@ -374,7 +374,11 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
374
374
|
? { ...input, workspaceId: workspaceContext.context.workspaceId }
|
|
375
375
|
: input;
|
|
376
376
|
const command = refillSendsCommand(scopedInput);
|
|
377
|
-
|
|
377
|
+
const workspaceId = workspaceContext && workspaceContext.ok
|
|
378
|
+
? workspaceContext.context.workspaceId
|
|
379
|
+
: normalizeExplicitWorkspaceId(scopedInput.workspaceId);
|
|
380
|
+
const canRefreshPaidInmailFacts = yolo || executionMode === "scheduled" || Boolean(workspaceId);
|
|
381
|
+
if (!canRefreshPaidInmailFacts) {
|
|
378
382
|
return {
|
|
379
383
|
...command,
|
|
380
384
|
autoPaidInmailRefresh: {
|
|
@@ -382,7 +386,7 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
382
386
|
status: "not_run_without_yolo",
|
|
383
387
|
refreshedPaidInmailSenderIds: [],
|
|
384
388
|
failedPaidInmailRefreshes: [],
|
|
385
|
-
note: "Automatic paid InMail credit refresh
|
|
389
|
+
note: "Automatic paid InMail credit refresh requires --yolo, scheduled mode, or an explicit workspaceId so the request can refresh exact sender facts safely.",
|
|
386
390
|
},
|
|
387
391
|
yoloExecution: {
|
|
388
392
|
enabled: false,
|
|
@@ -392,9 +396,6 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
392
396
|
},
|
|
393
397
|
};
|
|
394
398
|
}
|
|
395
|
-
const workspaceId = workspaceContext && workspaceContext.ok
|
|
396
|
-
? workspaceContext.context.workspaceId
|
|
397
|
-
: normalizeExplicitWorkspaceId(scopedInput.workspaceId);
|
|
398
399
|
const targetPlanInput = targetPlanInputFor(scopedInput);
|
|
399
400
|
const targetPlanBeforePaidRefresh = await getRefillTargetPlan(targetPlanInput);
|
|
400
401
|
const refreshActions = collectPaidInmailRefreshActions(targetPlanBeforePaidRefresh);
|
|
@@ -420,7 +421,7 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
420
421
|
}
|
|
421
422
|
const refreshResult = await refreshPaidInmailCreditsWithRetry(action.senderId, workspaceId, paidRefreshOptionsFromApprovalPacket(approvalPacket));
|
|
422
423
|
if (refreshResult.receipt) {
|
|
423
|
-
|
|
424
|
+
const classification = classifyPaidInmailRefreshReceipt(refreshResult.receipt);
|
|
424
425
|
refreshReceipts.push({
|
|
425
426
|
senderId: action.senderId,
|
|
426
427
|
actionKey: action.actionKey ?? null,
|
|
@@ -429,7 +430,23 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
429
430
|
approvalSource: "explicit_yolo_flag",
|
|
430
431
|
approvalPacket,
|
|
431
432
|
receipt: refreshResult.receipt,
|
|
433
|
+
receiptStatus: classification.status,
|
|
434
|
+
usableCurrentFacts: classification.usableCurrentFacts,
|
|
432
435
|
});
|
|
436
|
+
if (classification.usableCurrentFacts) {
|
|
437
|
+
refreshedPaidInmailSenderIds.push(action.senderId);
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
failedPaidInmailRefreshes.push({
|
|
441
|
+
senderId: action.senderId,
|
|
442
|
+
attempts: refreshResult.attempts,
|
|
443
|
+
error: classification.error ??
|
|
444
|
+
"paid InMail credit refresh did not return usable current facts",
|
|
445
|
+
errors: refreshResult.errors,
|
|
446
|
+
receiptStatus: classification.status,
|
|
447
|
+
receipt: refreshResult.receipt,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
433
450
|
}
|
|
434
451
|
else {
|
|
435
452
|
failedPaidInmailRefreshes.push({
|
|
@@ -444,8 +461,8 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
444
461
|
const targetPlan = refreshActions.length > 0
|
|
445
462
|
? await getRefillTargetPlan(targetPlanInput)
|
|
446
463
|
: targetPlanBeforePaidRefresh;
|
|
447
|
-
const selectedAction = refreshActions.length
|
|
448
|
-
const primitiveAttempt = refreshActions.length > 0
|
|
464
|
+
const selectedAction = yolo && refreshActions.length === 0 ? firstGlobalAction(targetPlan) : null;
|
|
465
|
+
const primitiveAttempt = !yolo || refreshActions.length > 0
|
|
449
466
|
? null
|
|
450
467
|
: await executeOneYoloPrimitive(selectedAction, workspaceId ?? undefined);
|
|
451
468
|
const shouldRereadAfterPrimitive = primitiveAttempt?.status === "executed_and_reread" ||
|
|
@@ -458,7 +475,9 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
458
475
|
...command,
|
|
459
476
|
targetPlan: finalTargetPlan,
|
|
460
477
|
targetPlanBeforePaidRefresh: refreshActions.length > 0 ? targetPlanBeforePaidRefresh : null,
|
|
461
|
-
targetPlanBeforeYoloPrimitive: refreshActions.length === 0 && postActionTargetPlan
|
|
478
|
+
targetPlanBeforeYoloPrimitive: yolo && refreshActions.length === 0 && postActionTargetPlan
|
|
479
|
+
? targetPlan
|
|
480
|
+
: null,
|
|
462
481
|
autoPaidInmailRefresh: {
|
|
463
482
|
enabled: true,
|
|
464
483
|
status: refreshActions.length === 0
|
|
@@ -477,23 +496,30 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
477
496
|
? "refill_sends refreshed stale paid InMail credit facts internally with bounded retries, once per sender, then returned the post-refresh targetPlan."
|
|
478
497
|
: "No stale or missing paid InMail credit refresh candidates were present in the initial target plan.",
|
|
479
498
|
},
|
|
480
|
-
yoloExecution:
|
|
499
|
+
yoloExecution: !yolo
|
|
481
500
|
? {
|
|
482
|
-
enabled:
|
|
483
|
-
status: "
|
|
501
|
+
enabled: false,
|
|
502
|
+
status: "not_run_without_yolo",
|
|
484
503
|
selectedAction: null,
|
|
485
|
-
targetPlanReread:
|
|
504
|
+
targetPlanReread: false,
|
|
486
505
|
}
|
|
487
|
-
:
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
:
|
|
497
|
-
|
|
506
|
+
: refreshActions.length > 0
|
|
507
|
+
? {
|
|
508
|
+
enabled: true,
|
|
509
|
+
status: "skipped_after_paid_refresh",
|
|
510
|
+
selectedAction: null,
|
|
511
|
+
targetPlanReread: true,
|
|
512
|
+
}
|
|
513
|
+
: {
|
|
514
|
+
enabled: true,
|
|
515
|
+
status: primitiveAttempt?.status ?? "no_action",
|
|
516
|
+
selectedAction,
|
|
517
|
+
result: primitiveAttempt?.result,
|
|
518
|
+
targetPlanReread: shouldRereadAfterPrimitive,
|
|
519
|
+
refusalReason: primitiveAttempt?.refusalReason,
|
|
520
|
+
postActionFirstAction: postActionTargetPlan
|
|
521
|
+
? firstGlobalAction(postActionTargetPlan)
|
|
522
|
+
: null,
|
|
523
|
+
},
|
|
498
524
|
};
|
|
499
525
|
}
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -7370,25 +7370,6 @@ export declare const allTools: ({
|
|
|
7370
7370
|
};
|
|
7371
7371
|
required: string[];
|
|
7372
7372
|
};
|
|
7373
|
-
} | {
|
|
7374
|
-
name: string;
|
|
7375
|
-
description: string;
|
|
7376
|
-
inputSchema: {
|
|
7377
|
-
type: string;
|
|
7378
|
-
properties: {
|
|
7379
|
-
workspaceId: {
|
|
7380
|
-
type: string;
|
|
7381
|
-
description: string;
|
|
7382
|
-
};
|
|
7383
|
-
action: {
|
|
7384
|
-
type: string;
|
|
7385
|
-
enum: string[];
|
|
7386
|
-
description: string;
|
|
7387
|
-
};
|
|
7388
|
-
};
|
|
7389
|
-
required: string[];
|
|
7390
|
-
additionalProperties: boolean;
|
|
7391
|
-
};
|
|
7392
7373
|
} | {
|
|
7393
7374
|
name: string;
|
|
7394
7375
|
description: string;
|
package/dist/tools/registry.js
CHANGED
|
@@ -40,7 +40,6 @@ import { refillTargetPlanToolDefinitions } from "./refill-target-plan.js";
|
|
|
40
40
|
import { rowToolDefinitions } from "./rows.js";
|
|
41
41
|
import { rubricToolDefinitions } from "./rubrics.js";
|
|
42
42
|
import { schedulerFillCapacityToolDefinitions } from "./scheduler-fill-capacity.js";
|
|
43
|
-
import { schedulerRunToolDefinitions } from "./scheduler-run.js";
|
|
44
43
|
import { senderRoutingToolDefinitions } from "./sender-routing.js";
|
|
45
44
|
import { senderToolDefinitions } from "./senders.js";
|
|
46
45
|
import { sequencerToolDefinitions } from "./sequencer.js";
|
|
@@ -58,7 +57,6 @@ export const allTools = [
|
|
|
58
57
|
...refillPlanV2ToolDefinitions,
|
|
59
58
|
...refillTargetPlanToolDefinitions,
|
|
60
59
|
...schedulerFillCapacityToolDefinitions,
|
|
61
|
-
...schedulerRunToolDefinitions,
|
|
62
60
|
...refillSendsToolDefinitions,
|
|
63
61
|
...refillSendsV2ToolDefinitions,
|
|
64
62
|
...setupEvergreenCampaignsToolDefinitions,
|
package/dist/tools/senders.d.ts
CHANGED
|
@@ -52,16 +52,22 @@ export type PaidInmailCreditStatus = {
|
|
|
52
52
|
};
|
|
53
53
|
export type RefreshPaidInmailCreditsResponse = {
|
|
54
54
|
senderId: string;
|
|
55
|
-
refreshed:
|
|
55
|
+
refreshed: boolean;
|
|
56
|
+
error?: string | null;
|
|
56
57
|
credits: PaidInmailCreditStatus;
|
|
57
|
-
receipt?:
|
|
58
|
+
receipt?: {
|
|
59
|
+
status?: string;
|
|
60
|
+
refreshed?: boolean;
|
|
61
|
+
error?: string;
|
|
62
|
+
[key: string]: unknown;
|
|
63
|
+
} | null;
|
|
58
64
|
sideEffects: {
|
|
59
|
-
refreshedLinkedInDerivedCreditFacts:
|
|
60
|
-
updatedSenderCreditCache:
|
|
61
|
-
campaignMutation:
|
|
62
|
-
schedulerMutation:
|
|
63
|
-
thresholdMutation:
|
|
64
|
-
sendMutation:
|
|
65
|
+
refreshedLinkedInDerivedCreditFacts: boolean;
|
|
66
|
+
updatedSenderCreditCache: boolean;
|
|
67
|
+
campaignMutation: boolean;
|
|
68
|
+
schedulerMutation: boolean;
|
|
69
|
+
thresholdMutation: boolean;
|
|
70
|
+
sendMutation: boolean;
|
|
65
71
|
};
|
|
66
72
|
};
|
|
67
73
|
export declare const senderToolDefinitions: ({
|
package/dist/tools/senders.js
CHANGED
|
@@ -237,6 +237,21 @@ function normalizeCreditStatus(raw) {
|
|
|
237
237
|
needsRefresh: raw?.needsRefresh === true,
|
|
238
238
|
};
|
|
239
239
|
}
|
|
240
|
+
function normalizeRefreshReceipt(raw) {
|
|
241
|
+
if (!raw || typeof raw !== "object")
|
|
242
|
+
return null;
|
|
243
|
+
return raw;
|
|
244
|
+
}
|
|
245
|
+
function normalizeCreditRefreshSideEffects(raw, receiptRefreshed) {
|
|
246
|
+
return {
|
|
247
|
+
refreshedLinkedInDerivedCreditFacts: raw?.refreshedLinkedInDerivedCreditFacts === true || receiptRefreshed,
|
|
248
|
+
updatedSenderCreditCache: raw?.updatedSenderCreditCache === true || receiptRefreshed,
|
|
249
|
+
campaignMutation: raw?.campaignMutation === true,
|
|
250
|
+
schedulerMutation: raw?.schedulerMutation === true,
|
|
251
|
+
thresholdMutation: raw?.thresholdMutation === true,
|
|
252
|
+
sendMutation: raw?.sendMutation === true,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
240
255
|
export async function refreshPaidInmailCredits(input) {
|
|
241
256
|
const senderId = input.senderId?.trim();
|
|
242
257
|
if (!senderId) {
|
|
@@ -268,18 +283,14 @@ export async function refreshPaidInmailCredits(input) {
|
|
|
268
283
|
if (threshold !== null)
|
|
269
284
|
body.threshold = threshold;
|
|
270
285
|
const result = await api.post(`/api/v3/mcp/senders/${encodeURIComponent(senderId)}/refresh-inmail-credits`, body, requestOptions);
|
|
286
|
+
const receipt = normalizeRefreshReceipt(result?.receipt);
|
|
287
|
+
const receiptRefreshed = result?.refreshed === true || receipt?.refreshed === true;
|
|
271
288
|
return {
|
|
272
289
|
senderId,
|
|
273
|
-
refreshed:
|
|
290
|
+
refreshed: receiptRefreshed,
|
|
291
|
+
error: pickString(result?.error, receipt?.error),
|
|
274
292
|
credits: normalizeCreditStatus(result?.credits),
|
|
275
|
-
receipt
|
|
276
|
-
sideEffects:
|
|
277
|
-
refreshedLinkedInDerivedCreditFacts: true,
|
|
278
|
-
updatedSenderCreditCache: true,
|
|
279
|
-
campaignMutation: false,
|
|
280
|
-
schedulerMutation: false,
|
|
281
|
-
thresholdMutation: false,
|
|
282
|
-
sendMutation: false,
|
|
283
|
-
},
|
|
293
|
+
receipt,
|
|
294
|
+
sideEffects: normalizeCreditRefreshSideEffects(result?.sideEffects, receiptRefreshed),
|
|
284
295
|
};
|
|
285
296
|
}
|
package/package.json
CHANGED
|
@@ -6,7 +6,6 @@ allowed-tools:
|
|
|
6
6
|
- mcp__sellable__refill_sends
|
|
7
7
|
- mcp__sellable__get_refill_target_plan
|
|
8
8
|
- mcp__sellable__get_scheduler_fill_capacity
|
|
9
|
-
- mcp__sellable__run_scheduler_sweep
|
|
10
9
|
- mcp__sellable__refresh_paid_inmail_credits
|
|
11
10
|
- mcp__sellable__get_subskill_asset
|
|
12
11
|
- mcp__sellable__get_auth_status
|
|
@@ -115,11 +114,10 @@ or install-time workspace mapping. Pass `workspaceId` on every scheduled or
|
|
|
115
114
|
`--yolo` refill tool call, including setup/read calls such as
|
|
116
115
|
`refill_sends`, `get_refill_target_plan`, `list_senders`,
|
|
117
116
|
`get_sender_routing`, `resolve_campaign_fill_route`,
|
|
118
|
-
`get_campaign_refill_state`, `get_scheduler_fill_capacity`,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
workspace.
|
|
117
|
+
`get_campaign_refill_state`, `get_scheduler_fill_capacity`, and any later
|
|
118
|
+
refill mutation covered by the packet. Missing `workspaceId` in scheduled or
|
|
119
|
+
`--yolo` mode is a blocker; stop with `WORKSPACE_REQUIRED` instead of running
|
|
120
|
+
against an implicit or guessed workspace.
|
|
123
121
|
|
|
124
122
|
Do not solve scheduled or `--yolo` workspace uncertainty by changing the shared
|
|
125
123
|
active workspace. Manual interactive workspace switching remains a separate
|
|
@@ -271,10 +269,6 @@ need raw proof, call the read-only `get_scheduler_fill_capacity` query for the
|
|
|
271
269
|
same sender/action/date; it tells the MCP how many cells the product scheduler
|
|
272
270
|
will try to place and does not import, approve, schedule, refresh credits, or
|
|
273
271
|
mutate.
|
|
274
|
-
When the refill loop has ready rows and needs scheduler pickup now, use
|
|
275
|
-
`run_scheduler_sweep` with the same explicit `workspaceId`; it can place cells
|
|
276
|
-
within existing scheduler gates and returns the receipt, but it never sends or
|
|
277
|
-
bypasses limits.
|
|
278
272
|
If the target plan is complete by projected coverage, report that the selected
|
|
279
273
|
target is already filled and no-op without asking for approval. If the ready
|
|
280
274
|
buffer covers the projected gap, paid InMail credit facts are fresh for every
|
|
@@ -6,7 +6,6 @@ allowed-tools:
|
|
|
6
6
|
- mcp__sellable__get_subskill_asset
|
|
7
7
|
- mcp__sellable__get_refill_target_plan
|
|
8
8
|
- mcp__sellable__get_scheduler_fill_capacity
|
|
9
|
-
- mcp__sellable__run_scheduler_sweep
|
|
10
9
|
- mcp__sellable__refresh_paid_inmail_credits
|
|
11
10
|
- mcp__sellable__list_senders
|
|
12
11
|
- mcp__sellable__get_sender_routing
|
|
@@ -68,12 +67,11 @@ request-scoped `workspaceId`. Pass that same `workspaceId` on every refill tool
|
|
|
68
67
|
call in this workflow: `get_refill_target_plan`, `list_senders`,
|
|
69
68
|
`get_sender_routing`, `resolve_campaign_fill_route`,
|
|
70
69
|
`get_campaign_refill_state`, `get_scheduler_fill_capacity`,
|
|
71
|
-
`
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
automation control path.
|
|
70
|
+
`refresh_paid_inmail_credits`, source import/readiness calls, preparation calls,
|
|
71
|
+
approval calls, and campaign start calls. Missing `workspaceId` in scheduled or
|
|
72
|
+
`--yolo` mode is a blocker; return or report `WORKSPACE_REQUIRED` instead of
|
|
73
|
+
falling back to shared config state. Manual interactive workspace switching is
|
|
74
|
+
diagnostic setup only and is not an automation control path.
|
|
77
75
|
|
|
78
76
|
Goal-mode continuation: a skill cannot create or invoke `/goal` by itself. When
|
|
79
77
|
this workflow is already running inside an active Codex goal, keep that goal
|
|
@@ -188,10 +186,6 @@ files or memory.
|
|
|
188
186
|
how many cells the product scheduler will try to place for that sender; it
|
|
189
187
|
does not create rows, import, approve, schedule, refresh paid-InMail credits,
|
|
190
188
|
or mutate thresholds.
|
|
191
|
-
When ready rows exist and the wait is for scheduler pickup, call
|
|
192
|
-
`run_scheduler_sweep` with the same explicit `workspaceId` to request the
|
|
193
|
-
product scheduler placement pass now and read its receipt. This may place
|
|
194
|
-
cells within existing gates, never sends messages, and never bypasses limits.
|
|
195
189
|
If `status:"complete"`, report the target, selected dates, sent count,
|
|
196
190
|
scheduled count, projected count, campaign ids, and no-op proof without
|
|
197
191
|
asking for approval or mutating.
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
type RefillSendsEvergreenInput = {
|
|
2
|
-
workspaceId?: string;
|
|
3
|
-
};
|
|
4
|
-
export declare const refillSendsEvergreenToolDefinitions: {
|
|
5
|
-
name: string;
|
|
6
|
-
description: string;
|
|
7
|
-
inputSchema: {
|
|
8
|
-
type: string;
|
|
9
|
-
properties: {
|
|
10
|
-
workspaceId: {
|
|
11
|
-
type: string;
|
|
12
|
-
description: string;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
required: string[];
|
|
16
|
-
additionalProperties: boolean;
|
|
17
|
-
};
|
|
18
|
-
}[];
|
|
19
|
-
export declare function refillSendsEvergreenCommand(input: RefillSendsEvergreenInput): {
|
|
20
|
-
readOnly: boolean;
|
|
21
|
-
workspaceId: string | null;
|
|
22
|
-
firstOperationalSteps: string[];
|
|
23
|
-
approvalContract: string;
|
|
24
|
-
forbiddenActions: string[];
|
|
25
|
-
fillWindow: string;
|
|
26
|
-
hostExamples: string[];
|
|
27
|
-
};
|
|
28
|
-
export {};
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export const refillSendsEvergreenToolDefinitions = [
|
|
2
|
-
{
|
|
3
|
-
name: "refill_sends_evergreen",
|
|
4
|
-
description: "Read-only Phase 85 evergreen refill command contract. It performs no mutations and only tells the operator to call get_evergreen_refill_plan for a dry-run packet and journal.",
|
|
5
|
-
inputSchema: {
|
|
6
|
-
type: "object",
|
|
7
|
-
properties: {
|
|
8
|
-
workspaceId: {
|
|
9
|
-
type: "string",
|
|
10
|
-
description: "Explicit request-scoped workspace id.",
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
required: ["workspaceId"],
|
|
14
|
-
additionalProperties: false,
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
];
|
|
18
|
-
export function refillSendsEvergreenCommand(input) {
|
|
19
|
-
return {
|
|
20
|
-
readOnly: true,
|
|
21
|
-
workspaceId: input.workspaceId ?? null,
|
|
22
|
-
firstOperationalSteps: [
|
|
23
|
-
"Call get_evergreen_refill_plan with the explicit workspaceId.",
|
|
24
|
-
"Read the returned packet, globalActionQueue, per-sender plans, and itinerary before taking any action.",
|
|
25
|
-
"Review the dry-run journal file path returned by get_evergreen_refill_plan.",
|
|
26
|
-
"Phase 85 is PLAN-ONLY; execution arrives in Phase 86.",
|
|
27
|
-
],
|
|
28
|
-
approvalContract: "Nothing is approved or executable in Phase 85. The evergreen command is read-only; Phase 86 introduces execution approval.",
|
|
29
|
-
forbiddenActions: [
|
|
30
|
-
"Do not schedule sends.",
|
|
31
|
-
"Do not send messages.",
|
|
32
|
-
"Do not approve messages.",
|
|
33
|
-
"Do not prepare messages.",
|
|
34
|
-
"Do not start or launch campaigns.",
|
|
35
|
-
"Do not create campaigns.",
|
|
36
|
-
"Do not switch providers or source families.",
|
|
37
|
-
"Do not lower paid InMail thresholds.",
|
|
38
|
-
"Do not refresh paid InMail credits.",
|
|
39
|
-
"Do not write scheduler fields.",
|
|
40
|
-
],
|
|
41
|
-
fillWindow: "Use only the target window and caps returned by get_evergreen_refill_plan.",
|
|
42
|
-
hostExamples: [
|
|
43
|
-
"refill_sends_evergreen({ workspaceId })",
|
|
44
|
-
"get_evergreen_refill_plan({ workspaceId })",
|
|
45
|
-
],
|
|
46
|
-
};
|
|
47
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
type SchedulerRunAction = "run" | "status";
|
|
2
|
-
type RunSchedulerSweepInput = {
|
|
3
|
-
workspaceId: string;
|
|
4
|
-
action?: SchedulerRunAction;
|
|
5
|
-
};
|
|
6
|
-
export declare const schedulerRunToolDefinitions: {
|
|
7
|
-
name: string;
|
|
8
|
-
description: string;
|
|
9
|
-
inputSchema: {
|
|
10
|
-
type: string;
|
|
11
|
-
properties: {
|
|
12
|
-
workspaceId: {
|
|
13
|
-
type: string;
|
|
14
|
-
description: string;
|
|
15
|
-
};
|
|
16
|
-
action: {
|
|
17
|
-
type: string;
|
|
18
|
-
enum: string[];
|
|
19
|
-
description: string;
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
required: string[];
|
|
23
|
-
additionalProperties: boolean;
|
|
24
|
-
};
|
|
25
|
-
}[];
|
|
26
|
-
export declare function runSchedulerSweep(input: RunSchedulerSweepInput): Promise<unknown>;
|
|
27
|
-
export {};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { getApi } from "../api.js";
|
|
2
|
-
import { normalizeExplicitWorkspaceId, workspaceRequestOptions, } from "./workspace-context.js";
|
|
3
|
-
async function postSchedulerRun(body, workspaceId) {
|
|
4
|
-
const api = getApi();
|
|
5
|
-
const requestOptions = workspaceRequestOptions(workspaceId);
|
|
6
|
-
return requestOptions
|
|
7
|
-
? api.post("/api/v3/mcp/scheduler-run", body, requestOptions)
|
|
8
|
-
: api.post("/api/v3/mcp/scheduler-run", body);
|
|
9
|
-
}
|
|
10
|
-
export const schedulerRunToolDefinitions = [
|
|
11
|
-
{
|
|
12
|
-
name: "run_scheduler_sweep",
|
|
13
|
-
description: 'Trigger the product scheduler placement sweep for one explicit workspace now, or read the last on-demand scheduler run status with action "status". A run returns a synchronous envelope with status ran, attached, backoff, window_closed_noop, or failed; retryAfterMs for backoff replays; and a receipt with cellsConsidered, cellsScheduled, cellsSkipped, cellsDeferred, deterministic skipReasons, and tablesFilteredForNoCapacity. It places cells within existing scheduler gates only: it can move placement earlier, but it cannot bypass sending windows, daily limits, cooldowns, sender gates, billing, or credit thresholds, and it never sends messages directly. Repeated calls inside the backoff window replay the last receipt verbatim. Status is read-only and scoped to the last on-demand run only; cron sweeps are not recorded here.',
|
|
14
|
-
inputSchema: {
|
|
15
|
-
type: "object",
|
|
16
|
-
properties: {
|
|
17
|
-
workspaceId: {
|
|
18
|
-
type: "string",
|
|
19
|
-
description: "Explicit request-scoped workspace id for scheduled/yolo refill automation. Pass this instead of switching the shared active workspace.",
|
|
20
|
-
},
|
|
21
|
-
action: {
|
|
22
|
-
type: "string",
|
|
23
|
-
enum: ["run", "status"],
|
|
24
|
-
description: 'Use "run" to trigger a placement sweep now. Use "status" for a read-only view of the last on-demand run. Defaults to "run".',
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
required: ["workspaceId"],
|
|
28
|
-
additionalProperties: false,
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
];
|
|
32
|
-
export async function runSchedulerSweep(input) {
|
|
33
|
-
const workspaceId = normalizeExplicitWorkspaceId(input.workspaceId);
|
|
34
|
-
if (!workspaceId) {
|
|
35
|
-
throw new Error("workspaceId is required for run_scheduler_sweep.");
|
|
36
|
-
}
|
|
37
|
-
const action = input.action ?? "run";
|
|
38
|
-
if (action !== "run" && action !== "status") {
|
|
39
|
-
throw new Error('action must be "run" or "status" for run_scheduler_sweep.');
|
|
40
|
-
}
|
|
41
|
-
return postSchedulerRun({
|
|
42
|
-
workspaceId,
|
|
43
|
-
action,
|
|
44
|
-
}, workspaceId);
|
|
45
|
-
}
|