@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 +2 -5
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/refill-run-loop.d.ts +1 -9
- package/dist/refill-run-loop.js +84 -138
- package/dist/tools/evergreen-refill-plan.d.ts +0 -19
- package/dist/tools/evergreen-refill-plan.js +0 -19
- package/dist/tools/refill-executors.d.ts +7 -0
- package/dist/tools/refill-executors.js +22 -0
- package/dist/tools/refill-sends-v2.d.ts +0 -19
- package/dist/tools/refill-sends-v2.js +2 -31
- package/dist/tools/refill-sends.d.ts +12 -0
- package/dist/tools/refill-sends.js +58 -36
- package/dist/tools/refill-target-plan.js +6 -19
- package/dist/tools/registry.d.ts +0 -36
- package/dist/tools/scheduler-fill-capacity.js +1 -1
- package/dist/tools/scheduler-run.d.ts +0 -5
- package/dist/tools/scheduler-run.js +1 -24
- package/dist/tools/senders.d.ts +14 -8
- package/dist/tools/senders.js +21 -10
- package/package.json +1 -1
- package/skills/refill-sends/SKILL.md +1 -9
- package/skills/refill-sends-v2/SKILL.md +5 -18
- package/skills/refill-sends-v2-workflow/SKILL.md +2 -16
- package/skills/refill-sends-v2-workflow/core/flow.v1.json +3 -19
- package/skills/refill-sends-workflow/SKILL.md +3 -5
- package/skills/refill-sends-workflow/core/flow.v1.json +1 -1
- package/dist/refill-date-window.d.ts +0 -34
- package/dist/refill-date-window.js +0 -210
- package/dist/tools/refill-sends-evergreen.d.ts +0 -28
- package/dist/tools/refill-sends-evergreen.js +0 -47
package/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"`,
|
|
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
|
-
|
|
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
|
|
78
|
-
targetDate?: string;
|
|
79
|
-
}) => Promise<unknown>;
|
|
71
|
+
requestSchedulerRun?: (workspaceId: string) => Promise<unknown>;
|
|
80
72
|
}
|
|
81
73
|
export type RefillV2LoopResult = {
|
|
82
74
|
status: "dry_run";
|
package/dist/refill-run-loop.js
CHANGED
|
@@ -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
|
|
710
|
-
const
|
|
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
|
|
842
|
-
|
|
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
|
-
|
|
857
|
-
|
|
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 =
|
|
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
|
-
})
|
|
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
|
|
974
|
-
|
|
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
|
|
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
|
|
1340
|
-
|
|
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
|
-
|
|
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
|
|
1416
|
-
|
|
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
|
|
1473
|
-
|
|
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
|
|
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
|
-
|
|
1553
|
-
}));
|
|
1503
|
+
});
|
|
1554
1504
|
return { status: "dry_run", plan };
|
|
1555
1505
|
}
|
|
1556
|
-
const
|
|
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.
|
|
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
|
|
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;
|