@sellable/mcp 0.1.536 → 0.1.537

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 CHANGED
@@ -74,12 +74,9 @@ sending from:
74
74
 
75
75
  The refill-sends public command wrapper plans and executes approval-gated send
76
76
  refills. It supports `--yolo` and optional sender selectors such as
77
- `--sender "Christian Reyes"`, exact-date selectors such as
78
- `--target-date 2026-06-30`, optional through-date selectors such as
77
+ `--sender "Christian Reyes"`, optional through-date selectors such as
79
78
  `--until 2026-06-30`, or typed MCP calls to `refill_sends({ yolo, senders,
80
- horizonSendDays, targetDate, untilDate })`. Date selectors resolve by
81
- `targetDate > untilDate > horizonSendDays`, where `targetDate` means one exact
82
- sender-local date and `untilDate` means an inclusive through-date, from:
79
+ untilDate })` from:
83
80
 
84
81
  - `mcp/sellable/skills/refill-sends/SKILL.md`
85
82
 
package/dist/index-dev.js CHANGED
File without changes
package/dist/index.js CHANGED
File without changes
@@ -13,9 +13,6 @@ export interface RefillV2LoopInput {
13
13
  };
14
14
  approvalMode?: "approve" | "mark_ready";
15
15
  dryRun?: boolean;
16
- targetDate?: string | null;
17
- untilDate?: string | null;
18
- horizonSendDays?: number | null;
19
16
  }
20
17
  export interface RefillV2LoopBudgets {
21
18
  maxGateCyclesPerInvocation: number;
@@ -63,9 +60,6 @@ export interface RefillV2LoopDeps {
63
60
  intent?: string;
64
61
  senderIds?: string[];
65
62
  runState?: Record<string, unknown>;
66
- targetDate?: string;
67
- untilDate?: string;
68
- horizonSendDays?: number;
69
63
  journal?: boolean;
70
64
  }) => Promise<Record<string, unknown>>;
71
65
  executors: RefillV2Executors;
@@ -74,9 +68,7 @@ export interface RefillV2LoopDeps {
74
68
  budgets?: Partial<RefillV2LoopBudgets>;
75
69
  now?: () => Date;
76
70
  sleep?: (ms: number) => Promise<void>;
77
- requestSchedulerRun?: (workspaceId: string, options?: {
78
- targetDate?: string;
79
- }) => Promise<unknown>;
71
+ requestSchedulerRun?: (workspaceId: string) => Promise<unknown>;
80
72
  }
81
73
  export type RefillV2LoopResult = {
82
74
  status: "dry_run";
@@ -1,7 +1,6 @@
1
1
  import { SellableApiError } from "./api.js";
2
- import { dateSelectorMatchesWindow, dateWindowFromPlan, dateWindowFromRunState, dateWindowFromSelector, normalizeRefillDateSelector, refillDateSelectorBody, schedulerSweepTargetDates, selectorFromDateWindow, } from "./refill-date-window.js";
3
2
  import { buildRunStateFromLocalHints } from "./tools/evergreen-refill-plan.js";
4
- import { prepareRowSelectorValue as defaultPrepareRowSelectorValue, refillPrepareRequestHash as defaultRefillPrepareRequestHash, } from "./tools/refill-executors.js";
3
+ import { classifyPaidInmailRefreshReceipt, prepareRowSelectorValue as defaultPrepareRowSelectorValue, refillPrepareRequestHash as defaultRefillPrepareRequestHash, } from "./tools/refill-executors.js";
5
4
  const DEFAULT_BUDGETS = {
6
5
  maxGateCyclesPerInvocation: 8,
7
6
  pollIntervalMs: 15_000,
@@ -418,41 +417,6 @@ function mergeRunState(current, updates) {
418
417
  ...updates,
419
418
  };
420
419
  }
421
- function dateSelectorFromInput(input) {
422
- return normalizeRefillDateSelector({
423
- targetDate: input.targetDate,
424
- untilDate: input.untilDate,
425
- horizonSendDays: input.horizonSendDays,
426
- });
427
- }
428
- function runStateWithDateWindow(runState, dateWindow) {
429
- return mergeRunState(runState, { dateWindow });
430
- }
431
- function readPlanRequest(input) {
432
- return {
433
- workspaceId: input.workspaceId,
434
- intent: input.intent,
435
- senderIds: input.senderIds,
436
- runState: input.runState,
437
- journal: input.journal,
438
- ...refillDateSelectorBody(input.dateSelector),
439
- };
440
- }
441
- async function readLoopPlan(input, deps, ctx, journal) {
442
- return deps.readPlan(readPlanRequest({
443
- workspaceId: input.workspaceId,
444
- intent: input.intent,
445
- senderIds: input.senderIds,
446
- runState: ctx.runState,
447
- journal,
448
- dateSelector: ctx.dateSelector,
449
- }));
450
- }
451
- function applyPlanDateWindow(ctx, plan) {
452
- ctx.dateWindow = dateWindowFromPlan(plan, ctx.dateSelector);
453
- ctx.dateSelector = selectorFromDateWindow(ctx.dateWindow);
454
- ctx.runState = runStateWithDateWindow(ctx.runState, ctx.dateWindow);
455
- }
456
420
  function normalizeDoneReason(value) {
457
421
  const raw = stringValue(value);
458
422
  if (!raw)
@@ -706,63 +670,14 @@ async function createJournal(input, deps, runId) {
706
670
  return null;
707
671
  }
708
672
  }
709
- async function preflightResumeDateWindow(input, deps, requestedSelector) {
710
- const requestedWindow = dateWindowFromSelector(requestedSelector);
711
- if (!input.resume) {
712
- return { ok: true, selector: requestedSelector, window: requestedWindow };
713
- }
714
- const status = await deps.runClient.refillRunStatus({
715
- workspaceId: input.workspaceId,
716
- runId: input.resume.runId,
717
- });
718
- if (hasBlocker(status, "lease_lost")) {
719
- return {
720
- status: "blocked",
721
- blocker: "lease_lost",
722
- runId: input.resume.runId,
723
- fence: input.resume.fence,
724
- guidance: "Refill run lease was lost before resume date-window preflight.",
725
- };
726
- }
727
- const storedWindow = dateWindowFromRunState(recordValue(status)?.runState);
728
- const explicitSelector = requestedSelector.source !== "default_scheduler_forward";
729
- if (!storedWindow) {
730
- if (explicitSelector) {
731
- return {
732
- status: "blocked",
733
- blocker: "date_window_conflict",
734
- runId: input.resume.runId,
735
- fence: input.resume.fence,
736
- guidance: "This legacy refill_sends_v2 run has no stored date window. Resume it without a new date selector or start a new run.",
737
- report: { requestedDateWindow: requestedWindow },
738
- };
739
- }
740
- return { ok: true, selector: requestedSelector, window: requestedWindow };
741
- }
742
- if (explicitSelector && !dateSelectorMatchesWindow(requestedSelector, storedWindow)) {
743
- return {
744
- status: "blocked",
745
- blocker: "date_window_conflict",
746
- runId: input.resume.runId,
747
- fence: input.resume.fence,
748
- guidance: "Resume date selector conflicts with the stored refill run date window.",
749
- report: { requestedDateWindow: requestedWindow, storedDateWindow: storedWindow },
750
- };
751
- }
752
- const selector = explicitSelector
753
- ? requestedSelector
754
- : selectorFromDateWindow(storedWindow);
755
- return { ok: true, selector, window: storedWindow };
756
- }
757
- async function startOrResume(input, deps, dateWindow) {
758
- const initialRunState = runStateWithDateWindow(buildRunStateFromLocalHints(input.workspaceId), dateWindow);
673
+ async function startOrResume(input, deps) {
674
+ const initialRunState = buildRunStateFromLocalHints(input.workspaceId);
759
675
  const started = await deps.runClient.startRefillRunRemote({
760
676
  workspaceId: input.workspaceId,
761
677
  config: {
762
678
  intent: input.intent ?? "auto",
763
679
  senderIds: input.senderIds,
764
680
  approvalMode: input.approvalMode ?? "approve",
765
- dateWindow,
766
681
  },
767
682
  runState: initialRunState,
768
683
  resume: input.resume,
@@ -804,8 +719,6 @@ async function startOrResume(input, deps, dateWindow) {
804
719
  laneCooldowns: [],
805
720
  passRates: [],
806
721
  };
807
- const storedDateWindow = dateWindowFromRunState(runState) ?? dateWindow;
808
- const storedDateSelector = selectorFromDateWindow(storedDateWindow);
809
722
  const journalPath = await createJournal(input, deps, runId);
810
723
  const ctx = {
811
724
  workspaceId: input.workspaceId,
@@ -818,8 +731,6 @@ async function startOrResume(input, deps, dateWindow) {
818
731
  plan: null,
819
732
  headAction: null,
820
733
  lastFingerprint: null,
821
- dateSelector: storedDateSelector,
822
- dateWindow: storedDateWindow,
823
734
  recoveryPoint: recordValue(result.recoveryPoint),
824
735
  gateCycles: 0,
825
736
  sleptMs: 0,
@@ -838,31 +749,45 @@ async function startOrResume(input, deps, dateWindow) {
838
749
  return ctx;
839
750
  }
840
751
  async function gateBootstrap(input, deps, ctx) {
841
- const plan = await readLoopPlan(input, deps, ctx, false);
842
- applyPlanDateWindow(ctx, plan);
752
+ const plan = await deps.readPlan({
753
+ workspaceId: input.workspaceId,
754
+ intent: input.intent,
755
+ senderIds: input.senderIds,
756
+ runState: ctx.runState,
757
+ journal: false,
758
+ });
843
759
  const paidCredit = arrayValue(recordValue(plan.bootstrap)?.paidCredit).filter(isRecord);
844
760
  const refreshedSenderIds = new Set(arrayValue(recordValue(ctx.runState.creditTrust)?.senderIds)
845
761
  .map((entry) => stringValue(entry))
846
762
  .filter((entry) => Boolean(entry)));
763
+ const attemptedSenderIds = new Set(arrayValue(recordValue(ctx.runState.creditTrust)?.attemptedSenderIds)
764
+ .map((entry) => stringValue(entry))
765
+ .filter((entry) => Boolean(entry)));
847
766
  const receipts = [];
848
767
  for (const entry of paidCredit) {
849
768
  const senderId = stringValue(entry.senderId);
850
769
  if (!senderId ||
851
770
  !entry.plannedJitRefresh ||
852
- refreshedSenderIds.has(senderId)) {
771
+ refreshedSenderIds.has(senderId) ||
772
+ attemptedSenderIds.has(senderId)) {
853
773
  continue;
854
774
  }
855
775
  const receipt = await deps.executors.refreshPaidInmailCreditsWithRetry(senderId, input.workspaceId);
856
- receipts.push({ senderId, receipt });
857
- refreshedSenderIds.add(senderId);
776
+ attemptedSenderIds.add(senderId);
777
+ const classification = classifyPaidInmailRefreshReceipt(receipt.receipt);
778
+ receipts.push({ senderId, receipt, classification });
779
+ if (classification.usableCurrentFacts) {
780
+ refreshedSenderIds.add(senderId);
781
+ }
858
782
  }
859
- const nextRunState = runStateWithDateWindow(mergeRunState(ctx.runState, {
783
+ const nextRunState = mergeRunState(ctx.runState, {
860
784
  creditTrust: {
861
785
  refreshedAt: (deps.now?.() ?? new Date()).toISOString(),
862
786
  senderIds: [...refreshedSenderIds],
787
+ attemptedSenderIds: [...attemptedSenderIds],
863
788
  receipts,
864
789
  },
865
- }), ctx.dateWindow);
790
+ });
866
791
  await safeJournalAppend(ctx, deps, deps.journal.renderBootstrapSection({
867
792
  summary: "Refill v2 execution bootstrap",
868
793
  senderSummary: safeJson(recordValue(plan.bootstrap)?.senders ?? []),
@@ -970,8 +895,13 @@ async function completeTerminal(input, deps, ctx, doneReasonInput, reportInput =
970
895
  };
971
896
  }
972
897
  async function gatePlan(input, deps, ctx) {
973
- const plan = await readLoopPlan(input, deps, ctx, false);
974
- applyPlanDateWindow(ctx, plan);
898
+ const plan = await deps.readPlan({
899
+ workspaceId: input.workspaceId,
900
+ intent: input.intent,
901
+ senderIds: input.senderIds,
902
+ runState: ctx.runState,
903
+ journal: false,
904
+ });
975
905
  if (plan.blocker === "workspace_access") {
976
906
  return {
977
907
  status: "blocked",
@@ -1074,7 +1004,13 @@ async function verifyRecoveryPoint(input, deps, ctx) {
1074
1004
  stateRevision: stringValue(planned.stateRevision),
1075
1005
  targetShapeRevision: stringValue(planned.targetShapeRevision),
1076
1006
  };
1077
- const fresh = await readLoopPlan(input, deps, ctx, false);
1007
+ const fresh = await deps.readPlan({
1008
+ workspaceId: input.workspaceId,
1009
+ intent: input.intent,
1010
+ senderIds: input.senderIds,
1011
+ runState: ctx.runState,
1012
+ journal: false,
1013
+ });
1078
1014
  const changed = fingerprintChanged(expected, fingerprintFromPlan(fresh));
1079
1015
  if (changed.length > 0) {
1080
1016
  await deps.runClient.recordRunOutcome({
@@ -1336,8 +1272,13 @@ async function verifyPrepareAction(input, deps, ctx, action, budgets) {
1336
1272
  }
1337
1273
  async function verifyReadOnlyWait(input, deps, ctx, budgets, waitKind) {
1338
1274
  for (let poll = 0; poll < budgets.maxPollsPerWait; poll += 1) {
1339
- const fresh = await readLoopPlan(input, deps, ctx, false);
1340
- applyPlanDateWindow(ctx, fresh);
1275
+ const fresh = await deps.readPlan({
1276
+ workspaceId: input.workspaceId,
1277
+ intent: input.intent,
1278
+ senderIds: input.senderIds,
1279
+ runState: ctx.runState,
1280
+ journal: false,
1281
+ });
1341
1282
  ctx.plan = fresh;
1342
1283
  const doneReason = doneReasonFromPlan(fresh);
1343
1284
  if (doneReason) {
@@ -1369,30 +1310,31 @@ async function verifySchedulerWait(input, deps, ctx, action, budgets) {
1369
1310
  if (!jitFired) {
1370
1311
  const senderId = actionSenderId(action);
1371
1312
  if (senderId) {
1372
- await deps.executors.refreshPaidInmailCreditsWithRetry(senderId, input.workspaceId);
1313
+ const refresh = await deps.executors.refreshPaidInmailCreditsWithRetry(senderId, input.workspaceId);
1314
+ const classification = classifyPaidInmailRefreshReceipt(refresh.receipt);
1315
+ if (!classification.usableCurrentFacts) {
1316
+ return {
1317
+ status: "blocked",
1318
+ blocker: "paid_inmail_refresh_failed",
1319
+ runId: ctx.runId,
1320
+ fence: ctx.fence,
1321
+ gate: ctx.gate,
1322
+ guidance: "Paid InMail credit refresh did not return usable current facts, so scheduler JIT was not requested.",
1323
+ report: {
1324
+ senderId,
1325
+ receiptStatus: classification.status,
1326
+ error: classification.error,
1327
+ receipt: refresh.receipt,
1328
+ attempts: refresh.attempts,
1329
+ errors: refresh.errors,
1330
+ },
1331
+ journalPath: ctx.journalPath,
1332
+ };
1333
+ }
1373
1334
  }
1374
1335
  if (deps.requestSchedulerRun) {
1375
1336
  try {
1376
- const targetDates = schedulerSweepTargetDates(ctx.dateWindow);
1377
- const sweepReceipts = [];
1378
- if (targetDates.length === 0) {
1379
- schedulerRunReceipt = sanitizeSchedulerRunReceipt(await deps.requestSchedulerRun(input.workspaceId));
1380
- }
1381
- else {
1382
- for (const targetDate of targetDates) {
1383
- const receipt = sanitizeSchedulerRunReceipt(await deps.requestSchedulerRun(input.workspaceId, { targetDate }));
1384
- if (receipt)
1385
- sweepReceipts.push({ targetDate, ...receipt });
1386
- }
1387
- schedulerRunReceipt = sweepReceipts[0] ?? null;
1388
- }
1389
- if (sweepReceipts.length > 1) {
1390
- ctx.runState = mergeRunState(ctx.runState, {
1391
- progress: mergeProgress(ctx, {
1392
- schedulerRunReceipts: sweepReceipts,
1393
- }),
1394
- });
1395
- }
1337
+ schedulerRunReceipt = sanitizeSchedulerRunReceipt(await deps.requestSchedulerRun(input.workspaceId));
1396
1338
  }
1397
1339
  catch {
1398
1340
  schedulerRunReceipt = null;
@@ -1412,8 +1354,13 @@ async function verifySchedulerWait(input, deps, ctx, action, budgets) {
1412
1354
  // full wait budget when the fresh receipt says nothing was placeable now.
1413
1355
  const readbackBudget = zeroScheduledFresh ? 1 : budgets.maxSchedulerReadbacks;
1414
1356
  for (let poll = 0; poll < readbackBudget; poll += 1) {
1415
- const fresh = await readLoopPlan(input, deps, ctx, false);
1416
- applyPlanDateWindow(ctx, fresh);
1357
+ const fresh = await deps.readPlan({
1358
+ workspaceId: input.workspaceId,
1359
+ intent: input.intent,
1360
+ senderIds: input.senderIds,
1361
+ runState: ctx.runState,
1362
+ journal: false,
1363
+ });
1417
1364
  ctx.plan = fresh;
1418
1365
  const doneReason = normalizeDoneReason(doneReasonFromPlan(fresh));
1419
1366
  if (doneReason === "complete" || doneReason === "capped_by_scheduler") {
@@ -1469,8 +1416,13 @@ async function gateVerify(input, deps, ctx, budgets) {
1469
1416
  actionType === "wait_for_source_import") {
1470
1417
  return verifyReadOnlyWait(input, deps, ctx, budgets, actionType);
1471
1418
  }
1472
- const fresh = await readLoopPlan(input, deps, ctx, false);
1473
- applyPlanDateWindow(ctx, fresh);
1419
+ const fresh = await deps.readPlan({
1420
+ workspaceId: input.workspaceId,
1421
+ intent: input.intent,
1422
+ senderIds: input.senderIds,
1423
+ runState: ctx.runState,
1424
+ journal: false,
1425
+ });
1474
1426
  ctx.plan = fresh;
1475
1427
  const doneReason = doneReasonFromPlan(fresh);
1476
1428
  if (doneReason) {
@@ -1543,21 +1495,15 @@ export async function runRefillV2Loop(input, deps) {
1543
1495
  let ctx = null;
1544
1496
  try {
1545
1497
  if (input.dryRun) {
1546
- const dateSelector = dateSelectorFromInput(input);
1547
- const plan = await deps.readPlan(readPlanRequest({
1498
+ const plan = await deps.readPlan({
1548
1499
  workspaceId: input.workspaceId,
1549
1500
  intent: input.intent,
1550
1501
  senderIds: input.senderIds,
1551
1502
  journal: true,
1552
- dateSelector,
1553
- }));
1503
+ });
1554
1504
  return { status: "dry_run", plan };
1555
1505
  }
1556
- const requestedSelector = dateSelectorFromInput(input);
1557
- const preflight = await preflightResumeDateWindow(input, deps, requestedSelector);
1558
- if ("status" in preflight)
1559
- return preflight;
1560
- const started = await startOrResume(input, deps, preflight.window);
1506
+ const started = await startOrResume(input, deps);
1561
1507
  if ("status" in started)
1562
1508
  return started;
1563
1509
  ctx = started;
@@ -3,9 +3,6 @@ type GetRefillPlanV2Input = {
3
3
  intent?: "auto" | "evergreen" | "plain" | "active";
4
4
  senderIds?: string[];
5
5
  runState?: Record<string, unknown>;
6
- targetDate?: string | null;
7
- untilDate?: string | null;
8
- horizonSendDays?: number | null;
9
6
  journal?: boolean;
10
7
  journalNote?: string;
11
8
  };
@@ -41,22 +38,6 @@ export declare const refillPlanV2ToolDefinitions: {
41
38
  enum: string[];
42
39
  description: string;
43
40
  };
44
- targetDate: {
45
- type: string;
46
- pattern: string;
47
- description: string;
48
- };
49
- untilDate: {
50
- type: string;
51
- pattern: string;
52
- description: string;
53
- };
54
- horizonSendDays: {
55
- type: string;
56
- minimum: number;
57
- maximum: number;
58
- description: string;
59
- };
60
41
  journal: {
61
42
  type: string;
62
43
  description: string;
@@ -1,6 +1,5 @@
1
1
  import * as path from "node:path";
2
2
  import { getApi, SellableApiError } from "../api.js";
3
- import { normalizeRefillDateSelector, refillDateSelectorBody, } from "../refill-date-window.js";
4
3
  import { appendIndexLine, appendJournalEvent, createRunJournal, renderBootstrapSection, renderPlanSection, renderTerminalSection, } from "../refill-journal.js";
5
4
  import { readRefillWorkspaceState } from "../refill-local-state.js";
6
5
  import { normalizeExplicitWorkspaceId, workspaceRequestOptions, } from "./workspace-context.js";
@@ -176,22 +175,6 @@ export const refillPlanV2ToolDefinitions = [
176
175
  enum: ["auto", "evergreen", "plain", "active"],
177
176
  description: 'Planner intent. Defaults to "auto" for refill-sends-v2.',
178
177
  },
179
- targetDate: {
180
- type: "string",
181
- pattern: "^\\d{4}-\\d{2}-\\d{2}$",
182
- description: "Optional one exact sender-local date (YYYY-MM-DD) to plan. Takes precedence over untilDate and horizonSendDays.",
183
- },
184
- untilDate: {
185
- type: "string",
186
- pattern: "^\\d{4}-\\d{2}-\\d{2}$",
187
- description: "Optional inclusive sender-local through-date (YYYY-MM-DD) to plan.",
188
- },
189
- horizonSendDays: {
190
- type: "number",
191
- minimum: 1,
192
- maximum: 7,
193
- description: "Optional explicit next N sender-local send days to plan.",
194
- },
195
178
  journal: {
196
179
  type: "boolean",
197
180
  description: "Set false to skip the local dry-run journal write.",
@@ -214,7 +197,6 @@ export async function getRefillPlanV2(input) {
214
197
  const runState = input.runState !== undefined
215
198
  ? input.runState
216
199
  : buildRunStateFromLocalHints(workspaceId);
217
- const dateSelector = normalizeRefillDateSelector(input);
218
200
  let raw;
219
201
  try {
220
202
  raw = await postRefillPlanV2({
@@ -222,7 +204,6 @@ export async function getRefillPlanV2(input) {
222
204
  intent: input.intent ?? "auto",
223
205
  senderIds: input.senderIds,
224
206
  runState,
225
- ...refillDateSelectorBody(dateSelector),
226
207
  }, workspaceId);
227
208
  }
228
209
  catch (error) {
@@ -14,6 +14,12 @@ export type PaidInmailCreditRefreshOptions = {
14
14
  maxStalenessSeconds?: number | null;
15
15
  paidInmailCreditsMaxStalenessSeconds?: number | null;
16
16
  };
17
+ export type PaidInmailRefreshReceiptClassification = {
18
+ usableCurrentFacts: boolean;
19
+ status: string;
20
+ error: string | null;
21
+ refreshed: boolean;
22
+ };
17
23
  export type YoloPrimitiveExecution = {
18
24
  enabled: true;
19
25
  status: "no_action" | "read_only_reread" | "executed_and_reread" | "refused" | "skipped_after_paid_refresh";
@@ -135,6 +141,7 @@ export declare function userAddedRowsLimitPayloadFromError(error: unknown): User
135
141
  export declare function sourceImportInProgressPayloadFromError(error: unknown): SourceImportInProgressPayload | null;
136
142
  export declare function paidRefreshActionFrom(value: unknown): PaidInmailRefreshAction | null;
137
143
  export declare function collectPaidInmailRefreshActions(plan: unknown): PaidInmailRefreshAction[];
144
+ export declare function classifyPaidInmailRefreshReceipt(value: unknown): PaidInmailRefreshReceiptClassification;
138
145
  export declare function firstGlobalAction(plan: unknown): Record<string, unknown> | null;
139
146
  export declare function actionIds(action: Record<string, unknown>): Record<string, unknown>;
140
147
  export declare function actionToolInput(action: Record<string, unknown>): Record<string, unknown>;
@@ -10,6 +10,11 @@ const PAID_INMAIL_REFRESH_RETRY_DELAY_MS = Number(process.env.SELLABLE_MCP_PAID_
10
10
  const SIGNAL_DISCOVERY_MIN_REFILL_POSTS = 3;
11
11
  const SIGNAL_DISCOVERY_MAX_REFILL_POSTS = 10;
12
12
  const SIGNAL_DISCOVERY_TARGET_ROWS_PER_POST = 150;
13
+ const USABLE_PAID_INMAIL_REFRESH_RECEIPT_STATUSES = new Set([
14
+ "fresh",
15
+ "refreshed",
16
+ "below_threshold",
17
+ ]);
13
18
  export function normalizeStrings(values) {
14
19
  if (!Array.isArray(values))
15
20
  return [];
@@ -206,6 +211,23 @@ export function collectPaidInmailRefreshActions(plan) {
206
211
  add(action);
207
212
  return [...bySender.values()];
208
213
  }
214
+ export function classifyPaidInmailRefreshReceipt(value) {
215
+ const response = recordValue(value);
216
+ const nestedReceipt = recordValue(response?.receipt);
217
+ const status = stringValue(nestedReceipt?.status) ??
218
+ stringValue(response?.status) ??
219
+ (response?.refreshed === true ? "refreshed" : "not_refreshed");
220
+ const refreshed = response?.refreshed === true || nestedReceipt?.refreshed === true;
221
+ const usableCurrentFacts = refreshed || USABLE_PAID_INMAIL_REFRESH_RECEIPT_STATUSES.has(status);
222
+ return {
223
+ usableCurrentFacts,
224
+ status,
225
+ refreshed,
226
+ error: stringValue(response?.error) ??
227
+ stringValue(nestedReceipt?.error) ??
228
+ (usableCurrentFacts ? null : "paid InMail credit refresh did not return usable current facts"),
229
+ };
230
+ }
209
231
  export function firstGlobalAction(plan) {
210
232
  const root = recordValue(plan);
211
233
  const target = recordValue(root?.target);
@@ -6,9 +6,6 @@ type RefillSendsV2Input = {
6
6
  approvalMode?: "approve" | "mark_ready";
7
7
  senderIds?: string[];
8
8
  intent?: "auto" | "evergreen" | "plain" | "active";
9
- targetDate?: string | null;
10
- untilDate?: string | null;
11
- horizonSendDays?: number | null;
12
9
  };
13
10
  export declare const refillSendsV2ToolDefinitions: {
14
11
  name: string;
@@ -47,22 +44,6 @@ export declare const refillSendsV2ToolDefinitions: {
47
44
  type: string;
48
45
  enum: string[];
49
46
  };
50
- targetDate: {
51
- type: string;
52
- pattern: string;
53
- description: string;
54
- };
55
- untilDate: {
56
- type: string;
57
- pattern: string;
58
- description: string;
59
- };
60
- horizonSendDays: {
61
- type: string;
62
- minimum: number;
63
- maximum: number;
64
- description: string;
65
- };
66
47
  };
67
48
  required: string[];
68
49
  additionalProperties: boolean;
@@ -18,7 +18,7 @@ const BOUNDED_AUTHORITY = "Within the refill run, bounded approvals, preparation
18
18
  export const refillSendsV2ToolDefinitions = [
19
19
  {
20
20
  name: "refill_sends_v2",
21
- description: "Execute the refill sends v2 loop with run-record fencing, packet re-planning, shared refill executors, and resumable dry-run/real-run modes. Date selectors match refill_sends: targetDate is one exact sender-local date, untilDate is inclusive, horizonSendDays is the next N sender-local send days, with precedence targetDate > untilDate > horizonSendDays > default scheduler-forward planning.",
21
+ description: "Execute the refill sends v2 loop with run-record fencing, packet re-planning, shared refill executors, and resumable dry-run/real-run modes.",
22
22
  inputSchema: {
23
23
  type: "object",
24
24
  properties: {
@@ -51,22 +51,6 @@ export const refillSendsV2ToolDefinitions = [
51
51
  type: "string",
52
52
  enum: ["auto", "evergreen", "plain", "active"],
53
53
  },
54
- targetDate: {
55
- type: "string",
56
- pattern: "^\\d{4}-\\d{2}-\\d{2}$",
57
- description: "Optional one exact sender-local date (YYYY-MM-DD) to refill.",
58
- },
59
- untilDate: {
60
- type: "string",
61
- pattern: "^\\d{4}-\\d{2}-\\d{2}$",
62
- description: "Optional inclusive sender-local through-date (YYYY-MM-DD) to refill.",
63
- },
64
- horizonSendDays: {
65
- type: "number",
66
- minimum: 1,
67
- maximum: 7,
68
- description: "Optional explicit next N sender-local send days to refill.",
69
- },
70
54
  },
71
55
  required: ["workspaceId"],
72
56
  additionalProperties: false,
@@ -115,9 +99,6 @@ export async function refillSendsV2Command(input) {
115
99
  workspaceId,
116
100
  intent: input.intent,
117
101
  senderIds: input.senderIds,
118
- targetDate: input.targetDate,
119
- untilDate: input.untilDate,
120
- horizonSendDays: input.horizonSendDays,
121
102
  journal: true,
122
103
  });
123
104
  return {
@@ -132,9 +113,6 @@ export async function refillSendsV2Command(input) {
132
113
  workspaceId,
133
114
  intent: input.intent,
134
115
  senderIds: input.senderIds,
135
- targetDate: input.targetDate,
136
- untilDate: input.untilDate,
137
- horizonSendDays: input.horizonSendDays,
138
116
  approvalMode: input.approvalMode ?? "approve",
139
117
  resume: resumeHandle(input),
140
118
  }, {
@@ -151,9 +129,6 @@ export async function refillSendsV2Command(input) {
151
129
  workspaceId: planInput.workspaceId,
152
130
  intent: planInput.intent,
153
131
  senderIds: planInput.senderIds,
154
- targetDate: planInput.targetDate,
155
- untilDate: planInput.untilDate,
156
- horizonSendDays: planInput.horizonSendDays,
157
132
  runState: planInput.runState,
158
133
  journal: planInput.journal,
159
134
  }),
@@ -178,11 +153,7 @@ export async function refillSendsV2Command(input) {
178
153
  localState: {
179
154
  writeRefillWorkspaceState,
180
155
  },
181
- requestSchedulerRun: (workspaceId, options) => runSchedulerSweep({
182
- workspaceId,
183
- action: "run",
184
- targetDate: options?.targetDate,
185
- }),
156
+ requestSchedulerRun: (workspaceId) => runSchedulerSweep({ workspaceId, action: "run" }),
186
157
  });
187
158
  return {
188
159
  ...(await maybeAddLostFenceGuidance(result, { ...input, workspaceId })),
@@ -292,6 +292,8 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
292
292
  attempts?: number;
293
293
  error: string;
294
294
  errors?: string[];
295
+ receiptStatus?: string;
296
+ receipt?: unknown;
295
297
  }[];
296
298
  refreshReceipts: {
297
299
  senderId: string;
@@ -301,6 +303,8 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
301
303
  approvalSource: string;
302
304
  approvalPacket: PaidInmailRefreshApprovalPacket;
303
305
  receipt: import("./senders.js").RefreshPaidInmailCreditsResponse;
306
+ receiptStatus: string;
307
+ usableCurrentFacts: boolean;
304
308
  }[];
305
309
  attemptedSenderIds: string[];
306
310
  targetPlanReread: boolean;
@@ -309,6 +313,14 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
309
313
  note: string;
310
314
  };
311
315
  yoloExecution: {
316
+ enabled: false;
317
+ status: "not_run_without_yolo";
318
+ selectedAction: null;
319
+ targetPlanReread: false;
320
+ result?: undefined;
321
+ refusalReason?: undefined;
322
+ postActionFirstAction?: undefined;
323
+ } | {
312
324
  enabled: true;
313
325
  status: "skipped_after_paid_refresh";
314
326
  selectedAction: null;