@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.
@@ -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, and read-only wait rereads. Same-source copy/source
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 approval, lower
106
- paid-InMail thresholds, switch source families, create campaigns, launch, send,
107
- or write scheduler rows. Continue with the workflow below for route selection,
108
- state rereads, approval gating, source import, preparation, and bounded
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, run
214
- only a persistent read-only scheduler wait/reread loop; do not ask for
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, run a persistent read-only scheduler wait
388
- loop: wait, reread, recompute the ledger, and continue until projected coverage
389
- is proved, a concrete non-scheduler blocker appears, or Christian explicitly
390
- stops or asks for status only. `awaiting_scheduler_after_ready_buffer` is not
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": "Read-only scheduler polling after rows are ready and paid-credit facts are fresh.",
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
- }
@@ -1,9 +0,0 @@
1
- {
2
- "parallelMode": "wide",
3
- "agentCount": 6,
4
- "maxToolCallsPerAgent": 2,
5
- "senderMaxAgents": 2,
6
- "senderMaxToolCallsPerAgent": 3,
7
- "progressMode": true,
8
- "debugMode": true
9
- }