@sellable/mcp 0.1.540 → 0.1.541
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 +1 -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/registry.d.ts +49 -32
- 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/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/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
|
@@ -87,12 +87,6 @@ Accepted invocation flags in the same user request:
|
|
|
87
87
|
preserves natural-language arguments better than shell-style flags.
|
|
88
88
|
- `targetDate: YYYY-MM-DD`: exact-date selector alternative when the host
|
|
89
89
|
preserves natural-language arguments better than shell-style flags.
|
|
90
|
-
- `horizonSendDays: N`: compatibility selector for the next N sender-local send
|
|
91
|
-
days when no exact or through-date selector is present.
|
|
92
|
-
|
|
93
|
-
Date selector precedence is `targetDate > untilDate > horizonSendDays`. Use
|
|
94
|
-
`targetDate` for one exact sender-local date, `untilDate` for an inclusive
|
|
95
|
-
through-date, and `horizonSendDays` for the next N sender-local send days.
|
|
96
90
|
|
|
97
91
|
When the host can call typed MCP tools, start with:
|
|
98
92
|
|
|
@@ -278,9 +272,7 @@ same sender/action/date; it tells the MCP how many cells the product scheduler
|
|
|
278
272
|
will try to place and does not import, approve, schedule, refresh credits, or
|
|
279
273
|
mutate.
|
|
280
274
|
When the refill loop has ready rows and needs scheduler pickup now, use
|
|
281
|
-
`run_scheduler_sweep
|
|
282
|
-
`untilDate` or `horizonSendDays` requests, sweep each planner-selected
|
|
283
|
-
`targetDate` with the same explicit `workspaceId`. The sweep can place cells
|
|
275
|
+
`run_scheduler_sweep` with the same explicit `workspaceId`; it can place cells
|
|
284
276
|
within existing scheduler gates and returns the receipt, but it never sends or
|
|
285
277
|
bypasses limits.
|
|
286
278
|
Scheduler-run receipt interpretation: `cellsConsidered is allocation-attempt
|
|
@@ -37,21 +37,16 @@ active workspace readback. Scheduled or autonomous usage must carry
|
|
|
37
37
|
`workspaceId` on every tool call. If the workspace is missing or ambiguous, stop
|
|
38
38
|
with `WORKSPACE_REQUIRED`; do not switch the shared active workspace.
|
|
39
39
|
|
|
40
|
-
Date selectors are sender-local and mutually exclusive by precedence:
|
|
41
|
-
`targetDate > untilDate > horizonSendDays`. Use `targetDate` for one exact sender-local date, `untilDate` for an inclusive through-date, and `horizonSendDays` for the next N sender-local send days. If none is provided, the planner uses the default scheduler-forward 48-hour behavior.
|
|
42
|
-
|
|
43
40
|
For a real refill run:
|
|
44
41
|
|
|
45
42
|
```text
|
|
46
|
-
refill_sends_v2({ workspaceId, intent:"auto", senderIds?, approvalMode
|
|
47
|
-
refill_sends_v2({ workspaceId, targetDate:"2026-07-07" })
|
|
48
|
-
refill_sends_v2({ workspaceId, untilDate:"2026-07-09" })
|
|
43
|
+
refill_sends_v2({ workspaceId, intent:"auto", senderIds?, approvalMode? })
|
|
49
44
|
```
|
|
50
45
|
|
|
51
46
|
For read-only inspection:
|
|
52
47
|
|
|
53
48
|
```text
|
|
54
|
-
refill_sends_v2({ workspaceId, dryRun:true, intent:"auto", senderIds
|
|
49
|
+
refill_sends_v2({ workspaceId, dryRun:true, intent:"auto", senderIds? })
|
|
55
50
|
```
|
|
56
51
|
|
|
57
52
|
To resume an in-progress run, pass the handle back exactly:
|
|
@@ -60,11 +55,6 @@ To resume an in-progress run, pass the handle back exactly:
|
|
|
60
55
|
refill_sends_v2({ workspaceId, runId, fence })
|
|
61
56
|
```
|
|
62
57
|
|
|
63
|
-
The original run's date window is stored in the run record. On resume, the
|
|
64
|
-
stored window is reused and conflicting resume input is blocked; do not add,
|
|
65
|
-
change, or remove `targetDate`, `untilDate`, or `horizonSendDays` while passing
|
|
66
|
-
an existing `{ runId, fence }`.
|
|
67
|
-
|
|
68
58
|
If a stale handle loses the lease, the tool reports the holder status and the
|
|
69
59
|
approximately 10 minute lockout window. Reinvoke with the current handle or wait
|
|
70
60
|
for lease expiry; never guess a fence.
|
|
@@ -135,12 +125,9 @@ host guard, heartbeats the run lease, and may return:
|
|
|
135
125
|
```
|
|
136
126
|
|
|
137
127
|
Reinvoke with that handle. The approximately five minute scheduler budget is at
|
|
138
|
-
the run level, not one tool call.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
window-closed or loaded-awaiting scheduler report must include remaining-ready
|
|
142
|
-
count, exact expected pickup time, and the resume handle; never treat bare
|
|
143
|
-
"awaiting scheduler" copy as a final answer.
|
|
128
|
+
the run level, not one tool call. A window-closed or loaded-awaiting scheduler
|
|
129
|
+
report must include remaining-ready count, exact expected pickup time, and the
|
|
130
|
+
resume handle; never treat bare "awaiting scheduler" copy as a final answer.
|
|
144
131
|
Scheduler-run receipt interpretation still applies to terminal progress:
|
|
145
132
|
`cellsConsidered is allocation-attempt count`, not total ready supply, while
|
|
146
133
|
`readyCellsFound` is ready inventory found before prefilters. Inspect
|
|
@@ -33,16 +33,10 @@ local files or memory.
|
|
|
33
33
|
Resolve request-scoped `workspaceId` using read-only auth and workspace tools.
|
|
34
34
|
Scheduled or autonomous runs must not rely on implicit workspace state.
|
|
35
35
|
|
|
36
|
-
Carry the request date selector through bootstrap, planning, execution, and
|
|
37
|
-
resume. Date selector precedence is `targetDate > untilDate > horizonSendDays`:
|
|
38
|
-
`targetDate` is one exact sender-local date, `untilDate` is an inclusive
|
|
39
|
-
through-date, and `horizonSendDays` is the next N sender-local send days. If no
|
|
40
|
-
selector is present, use the default scheduler-forward 48-hour behavior.
|
|
41
|
-
|
|
42
36
|
Start or resume the execution loop:
|
|
43
37
|
|
|
44
38
|
```text
|
|
45
|
-
refill_sends_v2({ workspaceId, intent:"auto", senderIds?, approvalMode?,
|
|
39
|
+
refill_sends_v2({ workspaceId, intent:"auto", senderIds?, approvalMode?, runId?, fence? })
|
|
46
40
|
```
|
|
47
41
|
|
|
48
42
|
Use `dryRun:true` only when the user asked for read-only inspection. A dry run
|
|
@@ -50,11 +44,6 @@ writes a journal only. A real run creates or resumes a run record, refreshes
|
|
|
50
44
|
paid-credit trust only when packet facts require it, and returns a resume handle
|
|
51
45
|
when the current invocation must pause.
|
|
52
46
|
|
|
53
|
-
The run record stores the selected date window. On resume, reuse that stored
|
|
54
|
-
window and block conflicting resume input; do not accept a changed
|
|
55
|
-
`targetDate`, `untilDate`, or `horizonSendDays` with an existing `{ runId,
|
|
56
|
-
fence }`.
|
|
57
|
-
|
|
58
47
|
## G1 Plan
|
|
59
48
|
|
|
60
49
|
The loop reads `get_refill_plan_v2` with runState fed back from the run record.
|
|
@@ -127,10 +116,7 @@ outcome, then verifies before replanning.
|
|
|
127
116
|
Scheduler wait is cross-invocation. If the tool returns `in_progress` with
|
|
128
117
|
sweep guidance, re-invoke `refill_sends_v2` with `{runId, fence}`. The scheduler
|
|
129
118
|
budget is cumulative at the run level. Zero pickup after a confirmed sweep is
|
|
130
|
-
debugged from `pipelineDiagnosis`; it is not waited out forever.
|
|
131
|
-
runs request pickup with `run_scheduler_sweep({ workspaceId, targetDate })`;
|
|
132
|
-
`untilDate` and `horizonSendDays` runs sweep each planner-selected exact target
|
|
133
|
-
date.
|
|
119
|
+
debugged from `pipelineDiagnosis`; it is not waited out forever.
|
|
134
120
|
Scheduler-run receipt interpretation still applies to terminal progress:
|
|
135
121
|
`cellsConsidered is allocation-attempt count`, not total ready supply, while
|
|
136
122
|
`readyCellsFound` is ready inventory found before prefilters. Inspect
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "v1.0",
|
|
3
3
|
"workflow": "refill-sends-v2-workflow",
|
|
4
|
-
"principle": "Execute refill sends v2 through a fenced run record: bootstrap, plan, execute, verify, and report. Dry runs stay read-only; real runs execute only packet-named bounded work.
|
|
4
|
+
"principle": "Execute refill sends v2 through a fenced run record: bootstrap, plan, execute, verify, and report. Dry runs stay read-only; real runs execute only packet-named bounded work.",
|
|
5
5
|
"laneSources": [
|
|
6
6
|
"managed_waterfall",
|
|
7
7
|
"dashboard_evergreen",
|
|
@@ -50,9 +50,6 @@
|
|
|
50
50
|
"repair rungs come from the plan packet; the loop bounds and refuses, never self-emits repair actions",
|
|
51
51
|
"capped_by_scheduler is complete, never poll",
|
|
52
52
|
"awaiting scheduler is not an end state; window-closed reports name remaining-ready, expected pickup time, and resume handle",
|
|
53
|
-
"date selectors resolve by targetDate > untilDate > horizonSendDays; targetDate is one exact sender-local date, untilDate is an inclusive through-date, and horizonSendDays means next N sender-local send days",
|
|
54
|
-
"dateWindow is stored on the run record and conflicting resume input is blocked",
|
|
55
|
-
"exact-date scheduler pickup uses run_scheduler_sweep({ workspaceId, targetDate }); untilDate and horizonSendDays runs sweep each planner-selected exact targetDate",
|
|
56
53
|
"honest-rubric-fail means a human updates the rubric or changes lead source",
|
|
57
54
|
"source replenishment beyond same-source-automatic is a scoped create-campaign handoff, never refill-owned provider search",
|
|
58
55
|
"no campaign creation, provider-family switch, threshold lowering, scheduler writes, sends, archives, deletes, or brief/filter/message/sequence/sender mutation without a separate approval packet"
|
|
@@ -146,16 +143,7 @@
|
|
|
146
143
|
},
|
|
147
144
|
{
|
|
148
145
|
"tool": "refill_sends_v2",
|
|
149
|
-
"requiredFields": ["workspaceId"]
|
|
150
|
-
"optionalFields": [
|
|
151
|
-
"senderIds",
|
|
152
|
-
"approvalMode",
|
|
153
|
-
"targetDate",
|
|
154
|
-
"untilDate",
|
|
155
|
-
"horizonSendDays",
|
|
156
|
-
"runId",
|
|
157
|
-
"fence"
|
|
158
|
-
]
|
|
146
|
+
"requiredFields": ["workspaceId"]
|
|
159
147
|
},
|
|
160
148
|
{
|
|
161
149
|
"tool": "get_subskill_prompt",
|
|
@@ -188,9 +176,6 @@
|
|
|
188
176
|
"optionalFields": [
|
|
189
177
|
"intent",
|
|
190
178
|
"senderIds",
|
|
191
|
-
"targetDate",
|
|
192
|
-
"untilDate",
|
|
193
|
-
"horizonSendDays",
|
|
194
179
|
"runState",
|
|
195
180
|
"journal",
|
|
196
181
|
"journalNote"
|
|
@@ -326,7 +311,7 @@
|
|
|
326
311
|
},
|
|
327
312
|
{
|
|
328
313
|
"id": "verify",
|
|
329
|
-
"description": "Verify whole-row outcomes before another plan read. Reattach to an own active prep job by requestSource/requestHash; never re-dispatch prep while a matching job is active. Foreign prep waits until clear or blocks foreign_prep_active. wait_for_active_work and wait_for_source_import reread the plan on bounded cadence. wait_for_scheduler is cross-invocation: each tool call polls briefly under the host guard, heartbeats the lease, and returns in_progress sweep guidance so the agent re-invokes refill_sends_v2 with {runId,fence}.
|
|
314
|
+
"description": "Verify whole-row outcomes before another plan read. Reattach to an own active prep job by requestSource/requestHash; never re-dispatch prep while a matching job is active. Foreign prep waits until clear or blocks foreign_prep_active. wait_for_active_work and wait_for_source_import reread the plan on bounded cadence. wait_for_scheduler is cross-invocation: each tool call polls briefly under the host guard, heartbeats the lease, and returns in_progress sweep guidance so the agent re-invokes refill_sends_v2 with {runId,fence}. The approximately five minute scheduler budget is cumulative at the run level.",
|
|
330
315
|
"allowedTools": ["refill_sends_v2", "get_refill_plan_v2"],
|
|
331
316
|
"onEnter": [
|
|
332
317
|
{
|
|
@@ -337,7 +322,6 @@
|
|
|
337
322
|
"hardRules": [
|
|
338
323
|
"do not re-dispatch prep when an own active job exists; reattach and poll status",
|
|
339
324
|
"capped_by_scheduler is complete",
|
|
340
|
-
"resume uses the stored dateWindow; conflicting resume input is blocked",
|
|
341
325
|
"replan-driven repair: the planner names rerun_errored_cells, approve_messages, start_campaign, or source work; the loop bounds and refuses",
|
|
342
326
|
"conversion verdict is journaled from packet facts after the first prep batch; the plan gate withholds a second unproven prep batch",
|
|
343
327
|
"zero pickup after a confirmed sweep is debugged from pipelineDiagnosis, not waited out forever",
|
|
@@ -189,11 +189,9 @@ files or memory.
|
|
|
189
189
|
does not create rows, import, approve, schedule, refresh paid-InMail credits,
|
|
190
190
|
or mutate thresholds.
|
|
191
191
|
When ready rows exist and the wait is for scheduler pickup, call
|
|
192
|
-
`run_scheduler_sweep
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
`targetDate` with the same explicit `workspaceId`. This may place cells
|
|
196
|
-
within existing gates, never sends messages, and never bypasses limits.
|
|
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.
|
|
197
195
|
Scheduler-run receipt interpretation: `cellsConsidered is
|
|
198
196
|
allocation-attempt count`, not total ready supply, while `readyCellsFound`
|
|
199
197
|
is ready inventory found before prefilters. Inspect `campaignScopeSummary`
|
|
@@ -202,7 +202,7 @@
|
|
|
202
202
|
],
|
|
203
203
|
"rules": [
|
|
204
204
|
"Poll every 60-120 seconds or on the host continuation interval.",
|
|
205
|
-
"Use run_scheduler_sweep
|
|
205
|
+
"Use run_scheduler_sweep with the explicit workspaceId when ready rows need scheduler pickup now; it places cells only inside existing gates and never sends.",
|
|
206
206
|
"cellsConsidered is allocation-attempt count; inspect campaignScopeSummary before assuming the selected refill campaign/table was included or ready-but-blocked.",
|
|
207
207
|
"Read prefiltered, skipped, and deferred separately: prefiltered ready closed-InMail cells with stale paid-credit facts require refresh_paid_inmail_credits_then_rerun once, then rerun/status.",
|
|
208
208
|
"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.",
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export type RefillDateWindowSource = "default_scheduler_forward" | "horizon_send_days" | "until_date" | "target_date";
|
|
2
|
-
export type NormalizedRefillDateSelector = {
|
|
3
|
-
source: RefillDateWindowSource;
|
|
4
|
-
targetDate: string | null;
|
|
5
|
-
untilDate: string | null;
|
|
6
|
-
horizonSendDays: number | null;
|
|
7
|
-
};
|
|
8
|
-
export type RefillRunDateWindow = NormalizedRefillDateSelector & {
|
|
9
|
-
version: 1;
|
|
10
|
-
selectedDates: string[];
|
|
11
|
-
senderWindows?: Array<{
|
|
12
|
-
senderId: string | null;
|
|
13
|
-
timeZone: string | null;
|
|
14
|
-
selectedDates: string[];
|
|
15
|
-
}>;
|
|
16
|
-
};
|
|
17
|
-
type DateSelectorInput = {
|
|
18
|
-
targetDate?: unknown;
|
|
19
|
-
untilDate?: unknown;
|
|
20
|
-
horizonSendDays?: unknown;
|
|
21
|
-
};
|
|
22
|
-
export declare function normalizeRefillDateSelector(input: DateSelectorInput): NormalizedRefillDateSelector;
|
|
23
|
-
export declare function refillDateSelectorBody(selector: NormalizedRefillDateSelector): {
|
|
24
|
-
targetDate?: string;
|
|
25
|
-
untilDate?: string;
|
|
26
|
-
horizonSendDays?: number;
|
|
27
|
-
};
|
|
28
|
-
export declare function dateWindowFromSelector(selector: NormalizedRefillDateSelector): RefillRunDateWindow;
|
|
29
|
-
export declare function dateWindowFromPlan(plan: Record<string, unknown>, selector: NormalizedRefillDateSelector): RefillRunDateWindow;
|
|
30
|
-
export declare function dateWindowFromRunState(runState: unknown): RefillRunDateWindow | null;
|
|
31
|
-
export declare function selectorFromDateWindow(window: RefillRunDateWindow): NormalizedRefillDateSelector;
|
|
32
|
-
export declare function dateSelectorMatchesWindow(selector: NormalizedRefillDateSelector, window: RefillRunDateWindow): boolean;
|
|
33
|
-
export declare function schedulerSweepTargetDates(window: RefillRunDateWindow): string[];
|
|
34
|
-
export {};
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
function isRecord(value) {
|
|
2
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3
|
-
}
|
|
4
|
-
function stringValue(value) {
|
|
5
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
6
|
-
}
|
|
7
|
-
function numberValue(value) {
|
|
8
|
-
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
9
|
-
}
|
|
10
|
-
function arrayValue(value) {
|
|
11
|
-
return Array.isArray(value) ? value : [];
|
|
12
|
-
}
|
|
13
|
-
function isValidDateKey(value) {
|
|
14
|
-
if (!/^\d{4}-\d{2}-\d{2}$/.test(value))
|
|
15
|
-
return false;
|
|
16
|
-
const [year, month, day] = value.split("-").map(Number);
|
|
17
|
-
const date = new Date(Date.UTC(year, month - 1, day));
|
|
18
|
-
return (date.getUTCFullYear() === year &&
|
|
19
|
-
date.getUTCMonth() === month - 1 &&
|
|
20
|
-
date.getUTCDate() === day);
|
|
21
|
-
}
|
|
22
|
-
function normalizeDateKey(value, field) {
|
|
23
|
-
if (value === undefined || value === null || value === "")
|
|
24
|
-
return null;
|
|
25
|
-
if (typeof value !== "string") {
|
|
26
|
-
throw new Error(`${field} must be a string in YYYY-MM-DD format.`);
|
|
27
|
-
}
|
|
28
|
-
const trimmed = value.trim();
|
|
29
|
-
if (!isValidDateKey(trimmed)) {
|
|
30
|
-
throw new Error(`${field} must be a valid YYYY-MM-DD calendar date.`);
|
|
31
|
-
}
|
|
32
|
-
return trimmed;
|
|
33
|
-
}
|
|
34
|
-
function normalizeHorizonSendDays(value) {
|
|
35
|
-
if (value === undefined)
|
|
36
|
-
return null;
|
|
37
|
-
if (typeof value !== "number" ||
|
|
38
|
-
!Number.isInteger(value) ||
|
|
39
|
-
value < 1 ||
|
|
40
|
-
value > 7) {
|
|
41
|
-
throw new Error("horizonSendDays must be an integer between 1 and 7.");
|
|
42
|
-
}
|
|
43
|
-
return value;
|
|
44
|
-
}
|
|
45
|
-
export function normalizeRefillDateSelector(input) {
|
|
46
|
-
const targetDate = normalizeDateKey(input.targetDate, "targetDate");
|
|
47
|
-
const untilDate = normalizeDateKey(input.untilDate, "untilDate");
|
|
48
|
-
const horizonSendDays = normalizeHorizonSendDays(input.horizonSendDays);
|
|
49
|
-
if (targetDate) {
|
|
50
|
-
return {
|
|
51
|
-
source: "target_date",
|
|
52
|
-
targetDate,
|
|
53
|
-
untilDate: null,
|
|
54
|
-
horizonSendDays: null,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
if (untilDate) {
|
|
58
|
-
return {
|
|
59
|
-
source: "until_date",
|
|
60
|
-
targetDate: null,
|
|
61
|
-
untilDate,
|
|
62
|
-
horizonSendDays: null,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
if (horizonSendDays !== null) {
|
|
66
|
-
return {
|
|
67
|
-
source: "horizon_send_days",
|
|
68
|
-
targetDate: null,
|
|
69
|
-
untilDate: null,
|
|
70
|
-
horizonSendDays,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
return {
|
|
74
|
-
source: "default_scheduler_forward",
|
|
75
|
-
targetDate: null,
|
|
76
|
-
untilDate: null,
|
|
77
|
-
horizonSendDays: null,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
export function refillDateSelectorBody(selector) {
|
|
81
|
-
if (selector.source === "target_date" && selector.targetDate) {
|
|
82
|
-
return { targetDate: selector.targetDate };
|
|
83
|
-
}
|
|
84
|
-
if (selector.source === "until_date" && selector.untilDate) {
|
|
85
|
-
return { untilDate: selector.untilDate };
|
|
86
|
-
}
|
|
87
|
-
if (selector.source === "horizon_send_days" &&
|
|
88
|
-
selector.horizonSendDays !== null) {
|
|
89
|
-
return { horizonSendDays: selector.horizonSendDays };
|
|
90
|
-
}
|
|
91
|
-
return {};
|
|
92
|
-
}
|
|
93
|
-
export function dateWindowFromSelector(selector) {
|
|
94
|
-
return {
|
|
95
|
-
version: 1,
|
|
96
|
-
...selector,
|
|
97
|
-
selectedDates: selector.targetDate ? [selector.targetDate] : [],
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
function selectedDatesFromPlan(plan) {
|
|
101
|
-
const packet = isRecord(plan.packet) ? plan.packet : {};
|
|
102
|
-
const target = isRecord(packet.target) ? packet.target : {};
|
|
103
|
-
const window = isRecord(target.window) ? target.window : {};
|
|
104
|
-
const senderWindows = arrayValue(window.senderWindows).filter(isRecord);
|
|
105
|
-
const fromWindows = senderWindows.flatMap((senderWindow) => arrayValue(senderWindow.selectedDates)
|
|
106
|
-
.map((entry) => stringValue(entry))
|
|
107
|
-
.filter((entry) => Boolean(entry)));
|
|
108
|
-
if (fromWindows.length > 0)
|
|
109
|
-
return Array.from(new Set(fromWindows)).sort();
|
|
110
|
-
const senderRefillPlans = arrayValue(target.senderRefillPlans).filter(isRecord);
|
|
111
|
-
return Array.from(new Set(senderRefillPlans.flatMap((senderPlan) => {
|
|
112
|
-
const horizon = isRecord(senderPlan.horizon) ? senderPlan.horizon : {};
|
|
113
|
-
return arrayValue(horizon.selectedDays)
|
|
114
|
-
.filter(isRecord)
|
|
115
|
-
.map((day) => stringValue(day.date))
|
|
116
|
-
.filter((entry) => Boolean(entry));
|
|
117
|
-
}))).sort();
|
|
118
|
-
}
|
|
119
|
-
function senderWindowsFromPlan(plan) {
|
|
120
|
-
const packet = isRecord(plan.packet) ? plan.packet : {};
|
|
121
|
-
const target = isRecord(packet.target) ? packet.target : {};
|
|
122
|
-
const window = isRecord(target.window) ? target.window : {};
|
|
123
|
-
return arrayValue(window.senderWindows)
|
|
124
|
-
.filter(isRecord)
|
|
125
|
-
.map((senderWindow) => ({
|
|
126
|
-
senderId: stringValue(senderWindow.senderId),
|
|
127
|
-
timeZone: stringValue(senderWindow.timeZone),
|
|
128
|
-
selectedDates: arrayValue(senderWindow.selectedDates)
|
|
129
|
-
.map((entry) => stringValue(entry))
|
|
130
|
-
.filter((entry) => Boolean(entry)),
|
|
131
|
-
}));
|
|
132
|
-
}
|
|
133
|
-
export function dateWindowFromPlan(plan, selector) {
|
|
134
|
-
const packet = isRecord(plan.packet) ? plan.packet : {};
|
|
135
|
-
const request = isRecord(packet.request) ? packet.request : {};
|
|
136
|
-
const targetDate = stringValue(request.targetDate) ?? selector.targetDate ?? null;
|
|
137
|
-
const untilDate = stringValue(request.untilDate) ?? selector.untilDate ?? null;
|
|
138
|
-
const horizonSendDays = numberValue(request.horizonSendDays) ?? selector.horizonSendDays ?? null;
|
|
139
|
-
const source = targetDate
|
|
140
|
-
? "target_date"
|
|
141
|
-
: untilDate
|
|
142
|
-
? "until_date"
|
|
143
|
-
: horizonSendDays !== null
|
|
144
|
-
? "horizon_send_days"
|
|
145
|
-
: selector.source;
|
|
146
|
-
const selectedDates = selectedDatesFromPlan(plan);
|
|
147
|
-
return {
|
|
148
|
-
version: 1,
|
|
149
|
-
source,
|
|
150
|
-
targetDate: source === "target_date" ? targetDate : null,
|
|
151
|
-
untilDate: source === "until_date" ? untilDate : null,
|
|
152
|
-
horizonSendDays: source === "horizon_send_days" ? horizonSendDays : null,
|
|
153
|
-
selectedDates: selectedDates.length > 0
|
|
154
|
-
? selectedDates
|
|
155
|
-
: targetDate
|
|
156
|
-
? [targetDate]
|
|
157
|
-
: [],
|
|
158
|
-
senderWindows: senderWindowsFromPlan(plan),
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
export function dateWindowFromRunState(runState) {
|
|
162
|
-
const state = isRecord(runState) ? runState : {};
|
|
163
|
-
const raw = isRecord(state.dateWindow) ? state.dateWindow : null;
|
|
164
|
-
if (!raw || raw.version !== 1)
|
|
165
|
-
return null;
|
|
166
|
-
const source = raw.source === "target_date" ||
|
|
167
|
-
raw.source === "until_date" ||
|
|
168
|
-
raw.source === "horizon_send_days" ||
|
|
169
|
-
raw.source === "default_scheduler_forward"
|
|
170
|
-
? raw.source
|
|
171
|
-
: null;
|
|
172
|
-
if (!source)
|
|
173
|
-
return null;
|
|
174
|
-
return {
|
|
175
|
-
version: 1,
|
|
176
|
-
source,
|
|
177
|
-
targetDate: source === "target_date" ? stringValue(raw.targetDate) : null,
|
|
178
|
-
untilDate: source === "until_date" ? stringValue(raw.untilDate) : null,
|
|
179
|
-
horizonSendDays: source === "horizon_send_days"
|
|
180
|
-
? numberValue(raw.horizonSendDays)
|
|
181
|
-
: null,
|
|
182
|
-
selectedDates: arrayValue(raw.selectedDates)
|
|
183
|
-
.map((entry) => stringValue(entry))
|
|
184
|
-
.filter((entry) => Boolean(entry)),
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
export function selectorFromDateWindow(window) {
|
|
188
|
-
return {
|
|
189
|
-
source: window.source,
|
|
190
|
-
targetDate: window.targetDate,
|
|
191
|
-
untilDate: window.untilDate,
|
|
192
|
-
horizonSendDays: window.horizonSendDays,
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
export function dateSelectorMatchesWindow(selector, window) {
|
|
196
|
-
return (selector.source === window.source &&
|
|
197
|
-
selector.targetDate === window.targetDate &&
|
|
198
|
-
selector.untilDate === window.untilDate &&
|
|
199
|
-
selector.horizonSendDays === window.horizonSendDays);
|
|
200
|
-
}
|
|
201
|
-
export function schedulerSweepTargetDates(window) {
|
|
202
|
-
if (window.source === "target_date" && window.targetDate) {
|
|
203
|
-
return [window.targetDate];
|
|
204
|
-
}
|
|
205
|
-
if (window.source === "until_date" ||
|
|
206
|
-
window.source === "horizon_send_days") {
|
|
207
|
-
return window.selectedDates;
|
|
208
|
-
}
|
|
209
|
-
return [];
|
|
210
|
-
}
|
|
@@ -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
|
-
}
|