@sellable/mcp 0.1.551 → 0.1.553
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/tools/refill-executors.d.ts +30 -0
- package/dist/tools/refill-executors.js +126 -0
- package/dist/tools/refill-sends.d.ts +181 -39
- package/dist/tools/refill-sends.js +141 -13
- package/dist/tools/refill-target-plan.js +181 -3
- package/dist/tools/registry.d.ts +8 -0
- package/dist/tools/scheduler-fill-capacity.js +1 -1
- package/dist/tools/scheduler-run.d.ts +10 -0
- package/dist/tools/scheduler-run.js +20 -0
- package/package.json +1 -1
- package/skills/refill-sends/SKILL.md +42 -7
- package/skills/refill-sends-workflow/SKILL.md +44 -6
- package/skills/refill-sends-workflow/core/flow.v1.json +63 -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/skills/research/config.json +0 -9
|
@@ -83,6 +83,8 @@ Accepted invocation flags in the same user request:
|
|
|
83
83
|
- `senderIds: <id>, <id>` or `senderNames: <name>, <name>`: explicit selector
|
|
84
84
|
alternatives when the host preserves natural-language arguments better than
|
|
85
85
|
shell-style flags.
|
|
86
|
+
- `actionTypes: send_invite, send_inmail_closed`: optional lane selector. Omit
|
|
87
|
+
`actionTypes` to preserve the target planner's lane inference.
|
|
86
88
|
- `untilDate: YYYY-MM-DD`: explicit date selector alternative when the host
|
|
87
89
|
preserves natural-language arguments better than shell-style flags.
|
|
88
90
|
- `targetDate: YYYY-MM-DD`: exact-date selector alternative when the host
|
|
@@ -91,7 +93,7 @@ Accepted invocation flags in the same user request:
|
|
|
91
93
|
When the host can call typed MCP tools, start with:
|
|
92
94
|
|
|
93
95
|
```text
|
|
94
|
-
refill_sends({ yolo?: boolean, executionMode?: "manual" | "scheduled" | "yolo", requireWorkspace?: boolean, workspaceId?: string, senders?: string[], senderIds?: string[], senderNames?: string[], horizonSendDays?: number, untilDate?: "YYYY-MM-DD", targetDate?: "YYYY-MM-DD" })
|
|
96
|
+
refill_sends({ yolo?: boolean, executionMode?: "manual" | "scheduled" | "yolo", requireWorkspace?: boolean, workspaceId?: string, senders?: string[], senderIds?: string[], senderNames?: string[], actionTypes?: ("send_invite" | "send_inmail_closed")[], horizonSendDays?: number, untilDate?: "YYYY-MM-DD", targetDate?: "YYYY-MM-DD" })
|
|
95
97
|
```
|
|
96
98
|
|
|
97
99
|
That command helper normalizes arguments and returns the execution contract. In
|
|
@@ -99,14 +101,22 @@ non-yolo mode it does not mutate. In `--yolo`, it may execute exactly one safe
|
|
|
99
101
|
bounded primitive from the fresh `target.globalActionQueue[0]`, then reread and
|
|
100
102
|
return the new target plan; currently safe primitives are paid-credit refresh,
|
|
101
103
|
existing-row message preparation, generated-message approval, receipt-proven
|
|
102
|
-
same-source row copy,
|
|
104
|
+
same-source row copy, a request-scoped exact-date product scheduler sweep, and
|
|
105
|
+
read-only wait rereads. Same-source copy/source
|
|
103
106
|
fallback is safe only after receipt-proven exhaustion:
|
|
104
107
|
`hasMoreFrontierRows:false`, zero `approvalCandidates`, no `stuckActiveCells`,
|
|
105
|
-
and no non-terminal `approvedNotDispatched` work. It does not run unbounded
|
|
106
|
-
paid-InMail thresholds, switch source families, create
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
approval.
|
|
108
|
+
and no non-terminal `approvedNotDispatched` work. It does not run unbounded
|
|
109
|
+
approval, lower paid-InMail thresholds, switch source families, create
|
|
110
|
+
campaigns, launch, or send directly; it never raw-writes scheduler fields.
|
|
111
|
+
Continue with the workflow below for route selection, state rereads, approval
|
|
112
|
+
gating, source import, preparation, and bounded approval.
|
|
113
|
+
|
|
114
|
+
The approval scope is the exact `workspaceId`, `targetDate`, sender ids, action
|
|
115
|
+
types, campaign/table/source ids, `targetShapeRevision`, and `actionKey` in the
|
|
116
|
+
rendered packet. `stateRevision` and sent/scheduled/ready counts are mutable
|
|
117
|
+
progress, not approval-scope drift. Execute only
|
|
118
|
+
`target.globalActionQueue[0]`, then perform a full authoritative reread before
|
|
119
|
+
deciding any next action.
|
|
110
120
|
|
|
111
121
|
## Workspace Contract
|
|
112
122
|
|
|
@@ -276,10 +286,27 @@ need raw proof, call the read-only `get_scheduler_fill_capacity` query for the
|
|
|
276
286
|
same sender/action/date; it tells the MCP how many cells the product scheduler
|
|
277
287
|
will try to place and does not import, approve, schedule, refresh credits, or
|
|
278
288
|
mutate.
|
|
289
|
+
Treat `rollingWeeklyInvite.capacityFreedDuringWindow:true` as proof that
|
|
290
|
+
rolling-weekly capacity frees later in the send window and remains schedulable;
|
|
291
|
+
use its timing fields instead of treating the window-start gate as a permanent
|
|
292
|
+
full-day blocker.
|
|
279
293
|
When the refill loop has ready rows and needs scheduler pickup now, use
|
|
280
294
|
`run_scheduler_sweep` with the same explicit `workspaceId`; it can place cells
|
|
281
295
|
within existing scheduler gates and returns the receipt, but it never sends or
|
|
282
296
|
bypasses limits.
|
|
297
|
+
A scheduler sweep is a visible workspace-wide scheduling side effect. The
|
|
298
|
+
request packet limits the expected target changes to its exact date, senders,
|
|
299
|
+
action types, campaigns, tables, and stable sources, but the product scheduler
|
|
300
|
+
may also place unrelated eligible workspace work; disclose that possibility in
|
|
301
|
+
the approval packet and receipt. For an exact-date sweep action, execute it
|
|
302
|
+
once, then reread the full target plan before taking or presenting another
|
|
303
|
+
action.
|
|
304
|
+
The exact-date `run_scheduler_sweep` may schedule eligible workspace cells
|
|
305
|
+
through existing product gates; it never sends directly or raw-writes scheduler
|
|
306
|
+
fields. Only non-exact or no-sweep scheduler waits are read-only.
|
|
307
|
+
On resume, reconcile the current target plan and matching sweep status/receipt
|
|
308
|
+
before retrying. A matching same-key terminal receipt replays; an
|
|
309
|
+
`uncertain_outcome` stops for reconciliation and must not schedule again.
|
|
283
310
|
Scheduler-run receipt interpretation: `cellsConsidered is allocation-attempt
|
|
284
311
|
count`, not total ready supply, while `readyCellsFound` is ready inventory found
|
|
285
312
|
before prefilters. Inspect `campaignScopeSummary` before assuming the selected
|
|
@@ -392,6 +419,14 @@ scheduler wait loop. Do not finish the refill goal, mark it complete, or mark it
|
|
|
392
419
|
blocked only because it is still `awaiting_scheduler_after_ready_buffer`; keep
|
|
393
420
|
waiting unless Christian stops the run or the host cannot continue.
|
|
394
421
|
|
|
422
|
+
Future scheduled coverage and already sent actions are distinct; only
|
|
423
|
+
scheduler-owned future scheduled actions count as scheduled coverage. For
|
|
424
|
+
multiple target dates, finish D1 execution and the full D1 reread before
|
|
425
|
+
planning D2. Completion proof is product-native Sellable MCP evidence only:
|
|
426
|
+
target plan, campaign refill state, scheduler capacity, sweep/status, and
|
|
427
|
+
bounded receipts. Never use individual cell ids, Prisma, SQL, direct database
|
|
428
|
+
access, or production-environment scripts as completion proof.
|
|
429
|
+
|
|
395
430
|
In `--yolo`, the default fill target is the scheduler-forward 48-hour window
|
|
396
431
|
unless `--target-date`/`targetDate`, `--until`/`untilDate`, or an explicit
|
|
397
432
|
compatibility `horizonSendDays` is provided. If a target date is provided,
|
|
@@ -75,6 +75,20 @@ calls, preparation calls, approval calls, and campaign start calls. Missing
|
|
|
75
75
|
interactive workspace switching is diagnostic setup only and is not an
|
|
76
76
|
automation control path.
|
|
77
77
|
|
|
78
|
+
Typed command scope:
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
refill_sends({ yolo?: boolean, executionMode?: "manual" | "scheduled" | "yolo", requireWorkspace?: boolean, workspaceId?: string, senders?: string[], senderIds?: string[], senderNames?: string[], actionTypes?: ("send_invite" | "send_inmail_closed")[], horizonSendDays?: number, untilDate?: "YYYY-MM-DD", targetDate?: "YYYY-MM-DD" })
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Omit `actionTypes` to preserve the target planner's lane inference. The
|
|
85
|
+
approval scope is the exact `workspaceId`, `targetDate`, sender ids, action
|
|
86
|
+
types, campaign/table/source ids, `targetShapeRevision`, and `actionKey` in the
|
|
87
|
+
rendered packet. `stateRevision` and sent/scheduled/ready counts are mutable
|
|
88
|
+
progress, not approval-scope drift. Execute only
|
|
89
|
+
`target.globalActionQueue[0]`, then perform a full authoritative reread before
|
|
90
|
+
deciding any next action.
|
|
91
|
+
|
|
78
92
|
Goal-mode continuation: a skill cannot create or invoke `/goal` by itself. When
|
|
79
93
|
this workflow is already running inside an active Codex goal, keep that goal
|
|
80
94
|
open until every selected sender lane is horizon-filled by projected coverage
|
|
@@ -192,6 +206,19 @@ files or memory.
|
|
|
192
206
|
`run_scheduler_sweep` with the same explicit `workspaceId` to request the
|
|
193
207
|
product scheduler placement pass now and read its receipt. This may place
|
|
194
208
|
cells within existing gates, never sends messages, and never bypasses limits.
|
|
209
|
+
A scheduler sweep is a visible workspace-wide scheduling side effect. The
|
|
210
|
+
request packet limits expected target changes to the exact date, senders,
|
|
211
|
+
action types, campaigns, tables, and stable sources, but unrelated eligible
|
|
212
|
+
workspace work may also be scheduled by the product scheduler and must be
|
|
213
|
+
disclosed. For an exact-date sweep action, execute it once and perform the
|
|
214
|
+
full target-plan reread before taking or presenting another action.
|
|
215
|
+
The exact-date `run_scheduler_sweep` may schedule eligible workspace cells
|
|
216
|
+
through existing product gates; it never sends directly or raw-writes
|
|
217
|
+
scheduler fields. Only non-exact or no-sweep scheduler waits are read-only.
|
|
218
|
+
On resume, reconcile the current target plan and matching sweep
|
|
219
|
+
status/receipt before retrying. A matching same-key terminal receipt
|
|
220
|
+
replays; an `uncertain_outcome` stops for reconciliation and must not
|
|
221
|
+
schedule again.
|
|
195
222
|
Scheduler-run receipt interpretation: `cellsConsidered is
|
|
196
223
|
allocation-attempt count`, not total ready supply, while `readyCellsFound`
|
|
197
224
|
is ready inventory found before prefilters. Inspect `campaignScopeSummary`
|
|
@@ -210,8 +237,10 @@ files or memory.
|
|
|
210
237
|
scheduled count, projected count, campaign ids, and no-op proof without
|
|
211
238
|
asking for approval or mutating.
|
|
212
239
|
If `remainingReadyOrProjectedGap:0` but `remainingProjectedGap>0`, and paid
|
|
213
|
-
InMail credit freshness is clean for every selected paid-InMail lane,
|
|
214
|
-
|
|
240
|
+
InMail credit freshness is clean for every selected paid-InMail lane,
|
|
241
|
+
execute an exact-date `run_scheduler_sweep` action once when it is
|
|
242
|
+
`target.globalActionQueue[0]`, then fully reread. Otherwise, run only a
|
|
243
|
+
persistent read-only scheduler wait/reread loop; do not ask for
|
|
215
244
|
prep/import/approval. Poll `get_refill_target_plan` every 60-120 seconds, or
|
|
216
245
|
on the host's next continuation interval, until projected coverage fills,
|
|
217
246
|
a concrete non-scheduler blocker appears, or Christian explicitly asks to
|
|
@@ -384,10 +413,11 @@ exceeds the sender's capacity for the selected target window. Put another way:
|
|
|
384
413
|
complete when the same daily-limit/readback model would show no remaining
|
|
385
414
|
send-day capacity for that selected lane. Ready-to-schedule rows are only buffer
|
|
386
415
|
for the product scheduler. If ready plus projected coverage covers the target
|
|
387
|
-
window but scheduled cells do not yet,
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
416
|
+
window but scheduled cells do not yet, execute the exact-date sweep when the
|
|
417
|
+
planner emits it; otherwise run a persistent read-only scheduler wait loop:
|
|
418
|
+
wait, reread, recompute the ledger, and continue until projected coverage is
|
|
419
|
+
proved, a concrete non-scheduler blocker appears, or Christian explicitly stops
|
|
420
|
+
or asks for status only. `awaiting_scheduler_after_ready_buffer` is not
|
|
391
421
|
success and is not a terminal blocker for an active refill goal; it is the
|
|
392
422
|
loaded, awaiting scheduler poll state that keeps the thread waiting. It does not
|
|
393
423
|
need a prep/import/approval packet.
|
|
@@ -720,6 +750,14 @@ with non-null `scheduledFor`. Prepared, approved, and ready-to-schedule rows are
|
|
|
720
750
|
intermediate states; report them as awaiting scheduler unless scheduled cells
|
|
721
751
|
are present.
|
|
722
752
|
|
|
753
|
+
Future scheduled coverage and already sent actions are distinct; only
|
|
754
|
+
scheduler-owned future scheduled actions count as scheduled coverage. For
|
|
755
|
+
multiple target dates, finish D1 execution and the full D1 reread before
|
|
756
|
+
planning D2. Completion proof is product-native Sellable MCP evidence only:
|
|
757
|
+
target plan, campaign refill state, scheduler capacity, sweep/status, and
|
|
758
|
+
bounded receipts. Never use individual cell ids, Prisma, SQL, direct database
|
|
759
|
+
access, or production-environment scripts as completion proof.
|
|
760
|
+
|
|
723
761
|
preparedMessages can remain 0 while scheduler-ready state changes downstream.
|
|
724
762
|
After a prep job reaches a terminal state, run `wait_for_campaign_processing`
|
|
725
763
|
when generated/pass counts are still settling, then poll `get_campaign_refill_state` until `readyToSchedule` drops, scheduled counts increase, or the state clearly
|
|
@@ -61,6 +61,44 @@
|
|
|
61
61
|
"scheduled",
|
|
62
62
|
"projected"
|
|
63
63
|
],
|
|
64
|
+
"scopeContract": {
|
|
65
|
+
"immutableApprovalEnvelope": [
|
|
66
|
+
"workspaceId",
|
|
67
|
+
"targetDate",
|
|
68
|
+
"senderIds",
|
|
69
|
+
"actionTypes",
|
|
70
|
+
"campaignIds",
|
|
71
|
+
"tableIds",
|
|
72
|
+
"sourceIds",
|
|
73
|
+
"targetShapeRevision",
|
|
74
|
+
"actionKey"
|
|
75
|
+
],
|
|
76
|
+
"mutableProgress": [
|
|
77
|
+
"stateRevision",
|
|
78
|
+
"sent",
|
|
79
|
+
"scheduled",
|
|
80
|
+
"readyToSchedule",
|
|
81
|
+
"remainingProjectedGap"
|
|
82
|
+
],
|
|
83
|
+
"executionRules": [
|
|
84
|
+
"Execute only target.globalActionQueue[0].",
|
|
85
|
+
"Execute one action, then perform a full authoritative reread before selecting any next action.",
|
|
86
|
+
"Mutable progress must not be treated as approval-scope drift while targetShapeRevision remains stable."
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"interruptionRecovery": {
|
|
90
|
+
"rules": [
|
|
91
|
+
"On resume, reread the canonical target plan and reconcile scheduler status or receipt with the matching requestKey and actionKey before retrying.",
|
|
92
|
+
"A matching terminal receipt must replay without a second scheduler execution.",
|
|
93
|
+
"An uncertain_outcome requires reconciliation; do not schedule again with the same or a replacement key until a canonical read resolves it."
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
"multiDayOrdering": {
|
|
97
|
+
"rules": [
|
|
98
|
+
"For multiple target dates, finish D1 execution and the full D1 reread before planning D2.",
|
|
99
|
+
"Do not pre-plan or execute a later date from stale earlier-date state."
|
|
100
|
+
]
|
|
101
|
+
},
|
|
64
102
|
"states": [
|
|
65
103
|
{
|
|
66
104
|
"id": "target_plan_read",
|
|
@@ -188,7 +226,7 @@
|
|
|
188
226
|
},
|
|
189
227
|
{
|
|
190
228
|
"id": "scheduler_wait_readback",
|
|
191
|
-
"description": "
|
|
229
|
+
"description": "Exact-date scheduler sweep or read-only polling after rows are ready and paid-credit facts are fresh.",
|
|
192
230
|
"allowedTools": [
|
|
193
231
|
"get_refill_target_plan",
|
|
194
232
|
"get_scheduler_fill_capacity",
|
|
@@ -203,6 +241,9 @@
|
|
|
203
241
|
"rules": [
|
|
204
242
|
"Poll every 60-120 seconds or on the host continuation interval.",
|
|
205
243
|
"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.",
|
|
244
|
+
"A scheduler sweep is a visible workspace-wide scheduling side effect; the packet limits expected target changes but must disclose that unrelated eligible workspace work may also be scheduled.",
|
|
245
|
+
"For an explicit targetDate, execute only the request-scoped run_scheduler_sweep from target.globalActionQueue[0], then perform a full authoritative target-plan reread before any next action.",
|
|
246
|
+
"Reuse the matching requestKey and actionKey only for status or receipt reconciliation; replay a matching terminal receipt and stop on uncertain_outcome rather than scheduling again.",
|
|
206
247
|
"cellsConsidered is allocation-attempt count; inspect campaignScopeSummary before assuming the selected refill campaign/table was included or ready-but-blocked.",
|
|
207
248
|
"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
249
|
"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.",
|
|
@@ -211,6 +252,11 @@
|
|
|
211
252
|
"Do not mark complete or blocked only because awaiting_scheduler_after_ready_buffer persists."
|
|
212
253
|
],
|
|
213
254
|
"transitions": [
|
|
255
|
+
{
|
|
256
|
+
"when": "explicit targetDate and target.globalActionQueue[0] is request-scoped run_scheduler_sweep",
|
|
257
|
+
"action": "execute exactly one request-scoped run_scheduler_sweep, capture its complete receipt, and do not execute a returned next action",
|
|
258
|
+
"to": "target_plan_read"
|
|
259
|
+
},
|
|
214
260
|
{
|
|
215
261
|
"when": "projected coverage fills target",
|
|
216
262
|
"to": "completion_or_blocker"
|
|
@@ -243,6 +289,7 @@
|
|
|
243
289
|
"start_campaign_message_preparation",
|
|
244
290
|
"same_source_copy",
|
|
245
291
|
"bounded_generated_message_approval",
|
|
292
|
+
"run_scheduler_sweep",
|
|
246
293
|
"wait_for_scheduler"
|
|
247
294
|
],
|
|
248
295
|
"neverAutoExecute": [
|
|
@@ -271,11 +318,26 @@
|
|
|
271
318
|
"verification": {
|
|
272
319
|
"requiredProof": [
|
|
273
320
|
"final get_refill_target_plan",
|
|
321
|
+
"get_campaign_refill_state for selected campaign/table facts",
|
|
322
|
+
"get_scheduler_fill_capacity for the exact sender/action/date",
|
|
323
|
+
"run_scheduler_sweep status or receipt for any executed sweep",
|
|
274
324
|
"projected coverage sent + scheduled",
|
|
275
325
|
"scheduler-owned cells with non-null scheduledFor when claiming scheduled",
|
|
276
326
|
"paid-InMail freshness status clean before scheduler wait",
|
|
277
327
|
"refresh receipt for each refreshed sender"
|
|
278
328
|
],
|
|
329
|
+
"forbiddenProof": [
|
|
330
|
+
"individual cell ids",
|
|
331
|
+
"Prisma",
|
|
332
|
+
"SQL",
|
|
333
|
+
"direct database access",
|
|
334
|
+
"production-environment scripts"
|
|
335
|
+
],
|
|
336
|
+
"completionRules": [
|
|
337
|
+
"Report already sent actions separately from future scheduled actions.",
|
|
338
|
+
"Only scheduler-owned future scheduled actions count as scheduled coverage.",
|
|
339
|
+
"Prepared, approved, and ready rows remain intermediate evidence."
|
|
340
|
+
],
|
|
279
341
|
"notCompletionProof": [
|
|
280
342
|
"prepared rows alone",
|
|
281
343
|
"approved rows alone",
|
|
@@ -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
|
-
}
|