@sellable/mcp 0.1.555 → 0.1.557

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.
Files changed (63) hide show
  1. package/README.md +13 -2
  2. package/agents/registry.json +2 -2
  3. package/dist/api.js +3 -6
  4. package/dist/auth.d.ts +0 -6
  5. package/dist/auth.js +2 -44
  6. package/dist/refill-run-client.d.ts +0 -5
  7. package/dist/refill-run-client.js +0 -15
  8. package/dist/refill-run-loop.d.ts +1 -12
  9. package/dist/refill-run-loop.js +13 -158
  10. package/dist/server.js +23 -0
  11. package/dist/tools/auth.d.ts +0 -5
  12. package/dist/tools/auth.js +12 -49
  13. package/dist/tools/campaign-message-preparation.d.ts +0 -62
  14. package/dist/tools/campaign-message-preparation.js +0 -41
  15. package/dist/tools/campaigns.js +2 -2
  16. package/dist/tools/csv-dnc.js +2 -2
  17. package/dist/tools/evergreen-refill-plan.d.ts +0 -3
  18. package/dist/tools/evergreen-refill-plan.js +7 -29
  19. package/dist/tools/find-leads-runs.d.ts +151 -0
  20. package/dist/tools/find-leads-runs.js +98 -0
  21. package/dist/tools/leads.d.ts +317 -32
  22. package/dist/tools/leads.js +171 -10
  23. package/dist/tools/model-quality.js +4 -6
  24. package/dist/tools/prompts.d.ts +3 -3
  25. package/dist/tools/prompts.js +7 -15
  26. package/dist/tools/provider-preflight.d.ts +65 -2
  27. package/dist/tools/provider-preflight.js +97 -10
  28. package/dist/tools/readiness.d.ts +89 -5
  29. package/dist/tools/readiness.js +66 -0
  30. package/dist/tools/refill-executors.d.ts +0 -38
  31. package/dist/tools/refill-executors.js +3 -222
  32. package/dist/tools/refill-sends-v2.d.ts +1 -112
  33. package/dist/tools/refill-sends-v2.js +2 -302
  34. package/dist/tools/refill-sends.d.ts +32 -678
  35. package/dist/tools/refill-sends.js +13 -274
  36. package/dist/tools/refill-target-plan.js +14 -486
  37. package/dist/tools/registry.d.ts +330 -115
  38. package/dist/tools/registry.js +7 -1
  39. package/dist/tools/scheduler-fill-capacity.js +1 -1
  40. package/dist/tools/scheduler-run.d.ts +0 -71
  41. package/dist/tools/scheduler-run.js +1 -203
  42. package/dist/tools/setup-evergreen-campaigns.js +1 -1
  43. package/dist/tools/workspace-context.d.ts +1 -1
  44. package/dist/tools/workspace-context.js +3 -8
  45. package/dist/tools/workspace-export.js +2 -2
  46. package/dist/tools/workspaces.d.ts +2 -48
  47. package/dist/tools/workspaces.js +5 -48
  48. package/package.json +1 -1
  49. package/skills/create-campaign/SKILL.md +3 -3
  50. package/skills/create-campaign-v2/SKILL.md +1 -1
  51. package/skills/create-evergreen-campaigns/SKILL.md +16 -16
  52. package/skills/find-leads/SKILL.md +48 -630
  53. package/skills/find-leads-v2/SKILL.md +70 -0
  54. package/skills/find-leads-v2/core/flow.v1.json +31 -0
  55. package/skills/refill-sends/SKILL.md +353 -91
  56. package/skills/refill-sends-v2/SKILL.md +6 -6
  57. package/skills/refill-sends-v2-workflow/SKILL.md +5 -5
  58. package/skills/refill-sends-v2-workflow/core/flow.v1.json +8 -8
  59. package/skills/refill-sends-workflow/SKILL.md +743 -100
  60. package/skills/refill-sends-workflow/core/flow.v1.json +1 -185
  61. package/dist/refill-contract.d.ts +0 -157
  62. package/dist/refill-contract.js +0 -487
  63. package/skills/refill-sends-workflow/core/contract.v2.json +0 -543
@@ -10,8 +10,6 @@ const SAFE_PACKET_ACTION_TYPES = new Set([
10
10
  "approve_messages",
11
11
  "start_paused_campaign",
12
12
  "rerun_errored_cells",
13
- "run_scheduler_sweep",
14
- "scheduler_first_settle",
15
13
  "wait_for_scheduler",
16
14
  "wait_for_active_work",
17
15
  "wait_for_source_import",
@@ -75,17 +73,6 @@ const PREP_FAILURE_RECOMMENDED_ACTIONS = new Set([
75
73
  "approve_or_repair_approval_cells",
76
74
  "inspect_rows",
77
75
  ]);
78
- const SCHEDULER_SWEEP_ALLOWED_SIDE_EFFECTS = [
79
- "schedule any eligible workspace cells on the approved date within scheduler gates",
80
- ];
81
- const SCHEDULER_SWEEP_FORBIDDEN_SIDE_EFFECTS = [
82
- "send directly",
83
- "write scheduler fields directly",
84
- "change campaign state",
85
- "change sender limits",
86
- "change source selection",
87
- ];
88
- const SCHEDULER_SWEEP_RECEIPT_GROUPS = ["campaignId", "tableId", "actionType"];
89
76
  async function postRefillTargetPlan(body, workspaceId) {
90
77
  const api = getApi();
91
78
  const requestOptions = workspaceRequestOptions(workspaceId);
@@ -212,289 +199,14 @@ function senderActionKey(senderId, actionType) {
212
199
  function sanitizeCounts(counts, selectedKeys) {
213
200
  if (!Array.isArray(counts))
214
201
  return [];
215
- return counts
216
- .filter((item) => {
202
+ return counts.filter((item) => {
217
203
  if (!isRecord(item))
218
204
  return false;
219
205
  const actionType = allowedActionType(item.actionType);
220
206
  const key = senderActionKey(item.senderId, actionType);
221
207
  return Boolean(key && selectedKeys.has(key));
222
- })
223
- .map((item) => ({
224
- senderId: stringValue(item.senderId),
225
- actionType: allowedActionType(item.actionType),
226
- refillLaneKey: refillLaneKeyValue(item.refillLaneKey),
227
- branchActionType: branchActionTypeValue(item.branchActionType),
228
- date: stringValue(item.date),
229
- count: nonNegativeNumber(item.count),
230
- campaignId: stringValue(item.campaignId),
231
- tableId: stringValue(item.tableId),
232
- }));
233
- }
234
- const REFILL_BRANCH_ACTION_TYPES = new Set([
235
- "send_invite",
236
- "send_dm",
237
- "send_inmail_open",
238
- "send_inmail_closed",
239
- "react_and_comment",
240
- ]);
241
- function nonNegativeNumber(value) {
242
- return typeof value === "number" && Number.isFinite(value)
243
- ? Math.max(0, value)
244
- : 0;
245
- }
246
- function refillLaneKeyValue(value) {
247
- if (value === "sales_nav_cascade")
248
- return value;
249
- return allowedActionType(value) ?? undefined;
250
- }
251
- function branchActionTypeValue(value) {
252
- return typeof value === "string" && REFILL_BRANCH_ACTION_TYPES.has(value)
253
- ? value
254
- : undefined;
255
- }
256
- function sanitizeBranchMetrics(value) {
257
- if (!Array.isArray(value))
258
- return [];
259
- return value
260
- .filter((metric) => isRecord(metric))
261
- .map((metric) => ({
262
- actionType: branchActionTypeValue(metric.actionType),
263
- count: nonNegativeNumber(metric.count),
264
- }))
265
- .filter((metric) => Boolean(metric.actionType));
266
- }
267
- function sanitizeBranchMetricGroups(value) {
268
- if (!isRecord(value)) {
269
- return { sentScheduled: [], assignedReady: [], sharedReady: [] };
270
- }
271
- return {
272
- sentScheduled: sanitizeBranchMetrics(value.sentScheduled),
273
- assignedReady: sanitizeBranchMetrics(value.assignedReady),
274
- sharedReady: sanitizeBranchMetrics(value.sharedReady),
275
- };
276
- }
277
- function sanitizeLedgerViews(value) {
278
- const views = isRecord(value) ? value : {};
279
- const raw = isRecord(views.raw) ? views.raw : {};
280
- const goalCapped = isRecord(views.goalCapped) ? views.goalCapped : {};
281
- const allocatable = isRecord(views.allocatable) ? views.allocatable : {};
282
- const unallocatable = isRecord(views.unallocatable)
283
- ? views.unallocatable
284
- : {};
285
- return {
286
- raw: {
287
- sentScheduled: nonNegativeNumber(raw.sentScheduled),
288
- assignedReady: nonNegativeNumber(raw.assignedReady),
289
- sharedReady: nonNegativeNumber(raw.sharedReady),
290
- },
291
- goalCapped: {
292
- projected: nonNegativeNumber(goalCapped.projected),
293
- ready: nonNegativeNumber(goalCapped.ready),
294
- remaining: nonNegativeNumber(goalCapped.remaining),
295
- },
296
- allocatable: {
297
- demand: nonNegativeNumber(allocatable.demand),
298
- ready: nonNegativeNumber(allocatable.ready),
299
- prepDeficit: nonNegativeNumber(allocatable.prepDeficit),
300
- },
301
- unallocatable: {
302
- assignedReady: nonNegativeNumber(unallocatable.assignedReady),
303
- sharedReady: nonNegativeNumber(unallocatable.sharedReady),
304
- goalDeficit: nonNegativeNumber(unallocatable.goalDeficit),
305
- },
306
- };
307
- }
308
- function sanitizeIdentityChecks(value) {
309
- const checks = isRecord(value) ? value : {};
310
- return {
311
- assignedConserved: checks.assignedConserved === true,
312
- sharedConserved: checks.sharedConserved === true,
313
- readyCapped: checks.readyCapped === true,
314
- deficitConserved: checks.deficitConserved === true,
315
- nonNegative: checks.nonNegative === true,
316
- };
317
- }
318
- const LEDGER_NUMBER_FIELDS = [
319
- "goal",
320
- "sentScheduledRawUnique",
321
- "goalAfterSentScheduled",
322
- "schedulerFillableSlotsFromCandidateSimulation",
323
- "preReadyAllocatableDemand",
324
- "assignedRawReady",
325
- "assignedCreditedReady",
326
- "assignedUnallocatableReady",
327
- "sharedRawReady",
328
- "sharedAllocatedReady",
329
- "sharedUnallocatableReady",
330
- "readyGoalCredited",
331
- "goalCappedProjected",
332
- "remainingGoalGap",
333
- "initialAllocatablePrepDeficit",
334
- "unallocatableGoalDeficit",
335
- "matchingCardinality",
336
- ];
337
- function sanitizeLaneLedger(value, requireScope) {
338
- if (!isRecord(value))
339
- return null;
340
- const senderId = stringValue(value.senderId);
341
- const refillLaneKey = refillLaneKeyValue(value.refillLaneKey);
342
- const targetDate = stringValue(value.targetDate);
343
- if (requireScope && (!senderId || !refillLaneKey || !targetDate))
344
- return null;
345
- const numbers = Object.fromEntries(LEDGER_NUMBER_FIELDS.map((field) => [
346
- field,
347
- nonNegativeNumber(value[field]),
348
- ]));
349
- return {
350
- ...(senderId ? { senderId } : {}),
351
- ...(refillLaneKey ? { refillLaneKey } : {}),
352
- ...(targetDate ? { targetDate } : {}),
353
- ...numbers,
354
- unmatchedReadyReasons: numericRecord(value.unmatchedReadyReasons),
355
- branchMetrics: sanitizeBranchMetricGroups(value.branchMetrics),
356
- views: sanitizeLedgerViews(value.views),
357
- identityChecks: sanitizeIdentityChecks(value.identityChecks),
358
- };
359
- }
360
- function sanitizeLaneLedgers(value) {
361
- if (!Array.isArray(value))
362
- return [];
363
- return value.flatMap((ledger) => {
364
- const sanitized = sanitizeLaneLedger(ledger, true);
365
- return sanitized ? [sanitized] : [];
366
208
  });
367
209
  }
368
- function sanitizePhase98RefillContract(value) {
369
- if (!isRecord(value))
370
- return {};
371
- const refillLaneKey = refillLaneKeyValue(value.refillLaneKey);
372
- const laneLedgers = sanitizeLaneLedgers(value.laneLedgers);
373
- const laneLedgerTotals = sanitizeLaneLedger(value.laneLedgerTotals, false);
374
- const settleKey = stringValue(value.settleKey);
375
- const settleDeadline = stringValue(value.settleDeadline);
376
- const settleBasisRevision = stringValue(value.settleBasisRevision);
377
- return {
378
- ...(refillLaneKey ? { refillLaneKey } : {}),
379
- ...(isRecord(value.branchMetrics)
380
- ? { branchMetrics: sanitizeBranchMetricGroups(value.branchMetrics) }
381
- : {}),
382
- ...(Array.isArray(value.laneLedgers) ? { laneLedgers } : {}),
383
- ...(laneLedgerTotals ? { laneLedgerTotals } : {}),
384
- ...(settleKey?.startsWith("scheduler_first_settle:") ? { settleKey } : {}),
385
- ...(settleDeadline ? { settleDeadline } : {}),
386
- ...(settleBasisRevision ? { settleBasisRevision } : {}),
387
- };
388
- }
389
- function sanitizeSelectedDay(value) {
390
- if (!isRecord(value))
391
- return null;
392
- const senderId = stringValue(value.senderId);
393
- const actionType = allowedActionType(value.actionType);
394
- if (!senderId || !actionType)
395
- return null;
396
- return {
397
- senderId,
398
- senderName: stringValue(value.senderName) ?? senderId,
399
- actionType,
400
- refillLaneKey: refillLaneKeyValue(value.refillLaneKey),
401
- date: stringValue(value.date),
402
- timeZone: stringValue(value.timeZone),
403
- startUtc: stringValue(value.startUtc),
404
- endUtc: stringValue(value.endUtc),
405
- limit: nonNegativeNumber(value.limit),
406
- };
407
- }
408
- function sanitizeSenderPlan(value) {
409
- if (!isRecord(value))
410
- return null;
411
- const senderId = stringValue(value.senderId);
412
- const actionType = allowedActionType(value.actionType);
413
- if (!senderId || !actionType)
414
- return null;
415
- const numericFields = [
416
- "requestedTarget",
417
- "grossTarget",
418
- "effectiveTarget",
419
- "sent",
420
- "scheduled",
421
- "projected",
422
- "readyBuffer",
423
- "remainingScheduledGap",
424
- "remainingProjectedGap",
425
- "remainingReadyOrProjectedGap",
426
- "schedulerFillableSlots",
427
- "schedulerCapacityTarget",
428
- "remainingSchedulerFillableGap",
429
- ];
430
- const numbers = Object.fromEntries(numericFields.map((field) => [field, nonNegativeNumber(value[field])]));
431
- const campaigns = Array.isArray(value.campaigns)
432
- ? value.campaigns
433
- .filter((campaign) => isRecord(campaign))
434
- .map((campaign) => ({
435
- campaignId: stringValue(campaign.campaignId),
436
- campaignName: stringValue(campaign.campaignName) ?? "",
437
- tableId: stringValue(campaign.tableId),
438
- tableName: stringValue(campaign.tableName) ?? null,
439
- campaignStatus: stringValue(campaign.campaignStatus) ?? null,
440
- classification: campaignClassificationValue(campaign.classification),
441
- selectedLeadListId: stringValue(campaign.selectedLeadListId) ?? null,
442
- leadSourceProvider: stringValue(campaign.leadSourceProvider) ?? null,
443
- sourceFingerprint: stringValue(campaign.sourceFingerprint) ?? null,
444
- }))
445
- : [];
446
- return {
447
- senderId,
448
- senderName: stringValue(value.senderName) ?? senderId,
449
- actionType,
450
- ...sanitizePhase98RefillContract(value),
451
- selectedDays: Array.isArray(value.selectedDays)
452
- ? value.selectedDays
453
- .map(sanitizeSelectedDay)
454
- .filter((day) => Boolean(day))
455
- : [],
456
- campaigns,
457
- ...numbers,
458
- schedulerFillableSlotsKnown: booleanValue(value.schedulerFillableSlotsKnown) ?? undefined,
459
- schedulerCapacityMode: stringValue(value.schedulerCapacityMode),
460
- allocationScope: stringValue(value.allocationScope),
461
- schedulerWarnings: stringArray(value.schedulerWarnings),
462
- paidInmail: sanitizePaidInmail(value.paidInmail),
463
- refillReceipt: sanitizeRefillReceipt(value.refillReceipt),
464
- };
465
- }
466
- function sanitizeActionSelection(value) {
467
- if (!isRecord(value))
468
- return null;
469
- const senderId = stringValue(value.senderId);
470
- if (!senderId)
471
- return null;
472
- const evidence = Array.isArray(value.evidence)
473
- ? value.evidence
474
- .filter((item) => isRecord(item))
475
- .map((item) => ({
476
- senderId: stringValue(item.senderId),
477
- actionType: allowedActionType(item.actionType),
478
- refillLaneKey: refillLaneKeyValue(item.refillLaneKey),
479
- source: stringValue(item.source),
480
- count: nonNegativeNumber(item.count),
481
- latestAt: stringValue(item.latestAt),
482
- campaignId: stringValue(item.campaignId),
483
- tableId: stringValue(item.tableId),
484
- }))
485
- : [];
486
- const refillLaneKeys = Array.isArray(value.refillLaneKeys)
487
- ? value.refillLaneKeys.map(refillLaneKeyValue).filter(Boolean)
488
- : [];
489
- return {
490
- senderId,
491
- senderName: stringValue(value.senderName) ?? senderId,
492
- actionTypes: actionTypesFrom(value.actionTypes),
493
- ...(refillLaneKeys.length > 0 ? { refillLaneKeys } : {}),
494
- source: stringValue(value.source),
495
- evidence,
496
- };
497
- }
498
210
  function sanitizeActionCandidates(candidates, selectedKeys, paidRefreshNeededSenderIds, status, remainingReadyOrProjectedGap, remainingProjectedGap) {
499
211
  const filtered = Array.isArray(candidates)
500
212
  ? candidates
@@ -565,7 +277,6 @@ function sanitizeActionCandidate(candidate) {
565
277
  ? "get_refill_target_plan"
566
278
  : undefined,
567
279
  reason: stringValue(candidate.reason),
568
- ...sanitizePhase98RefillContract(candidate),
569
280
  };
570
281
  }
571
282
  function sanitizeRowSelector(value) {
@@ -754,16 +465,6 @@ function sanitizeToolInput(value) {
754
465
  wait: sanitizeWait(value.wait),
755
466
  refillReceipt: sanitizeRefillReceipt(value.refillReceipt),
756
467
  capSaturated: booleanValue(value.capSaturated) === true ? true : undefined,
757
- settleKey: stringValue(value.settleKey),
758
- settleDeadline: stringValue(value.settleDeadline),
759
- settleBasisRevision: stringValue(value.settleBasisRevision),
760
- calibration: isRecord(value.calibration)
761
- ? {
762
- trusted: value.calibration.trusted === true,
763
- passRate: optionalNumberValue(value.calibration.passRate),
764
- minimumRows: nonNegativeNumber(value.calibration.minimumRows),
765
- }
766
- : undefined,
767
468
  };
768
469
  }
769
470
  function sanitizePacketIds(value) {
@@ -814,10 +515,8 @@ function sanitizeStructuredAction(action, selectedBySender, mode, rank) {
814
515
  : null,
815
516
  stopCondition: stringValue(action.stopCondition) ?? "",
816
517
  yoloEligible,
817
- actionKey: stringValue(action.actionKey),
818
518
  capSaturated: booleanValue(action.capSaturated) === true ? true : undefined,
819
519
  prepFailureDiagnosis: sanitizePrepFailureDiagnosis(action.prepFailureDiagnosis),
820
- ...sanitizePhase98RefillContract(action),
821
520
  };
822
521
  }
823
522
  function sanitizeLedgerBlockers(value) {
@@ -1010,12 +709,7 @@ function paidInmailRefreshActionForSenderPlan(plan, paidInmail) {
1010
709
  availableCredits: numberValue(paidInmail?.availableCredits),
1011
710
  oldThreshold: numberValue(paidInmail?.threshold),
1012
711
  maxStalenessSeconds: MCP_PAID_INMAIL_CREDITS_MAX_STALENESS_SECONDS,
1013
- actionKey: [
1014
- "refresh_paid_inmail_credits",
1015
- senderId,
1016
- campaignId,
1017
- columnId,
1018
- ].join(":"),
712
+ actionKey: ["refresh_paid_inmail_credits", senderId, campaignId, columnId].join(":"),
1019
713
  rereadAfter: "get_refill_target_plan",
1020
714
  reason: stringValue(paidInmail?.reason) ??
1021
715
  "MCP paid InMail sender-credit facts require refresh.",
@@ -1061,7 +755,6 @@ function sanitizeStructuredSenderPlans(value, selectedBySender) {
1061
755
  senderName: stringValue(plan.senderName) ?? "",
1062
756
  status: stringValue(plan.status) ?? "needs_refill",
1063
757
  selectedLane: allowedActionType(plan.selectedLane),
1064
- ...sanitizePhase98RefillContract(plan),
1065
758
  horizon: isRecord(plan.horizon)
1066
759
  ? {
1067
760
  selectedDays: Array.isArray(plan.horizon.selectedDays)
@@ -1121,149 +814,6 @@ function buildSanitizedGlobalActionQueue(senderRefillPlans) {
1121
814
  rank: index + 1,
1122
815
  }));
1123
816
  }
1124
- function sortedUniqueStrings(value) {
1125
- return [...new Set(stringArray(value))].sort();
1126
- }
1127
- function sameStrings(left, right) {
1128
- return (left.length === right.length &&
1129
- left.every((value, index) => value === right[index]));
1130
- }
1131
- function sanitizeSchedulerSweepSourceScopes(value) {
1132
- if (!Array.isArray(value))
1133
- return [];
1134
- const scopes = value.flatMap((entry) => {
1135
- if (!isRecord(entry))
1136
- return [];
1137
- const campaignId = stringValue(entry.campaignId)?.trim();
1138
- const tableId = stringValue(entry.tableId)?.trim();
1139
- const sourceLeadListId = stringValue(entry.sourceLeadListId)?.trim();
1140
- const leadSourceProvider = stringValue(entry.leadSourceProvider)?.trim();
1141
- if (!campaignId || !tableId || !sourceLeadListId || !leadSourceProvider) {
1142
- return [];
1143
- }
1144
- return [{ campaignId, tableId, sourceLeadListId, leadSourceProvider }];
1145
- });
1146
- const unique = new Map(scopes.map((scope) => [
1147
- [
1148
- scope.campaignId,
1149
- scope.tableId,
1150
- scope.sourceLeadListId,
1151
- scope.leadSourceProvider,
1152
- ].join(":"),
1153
- scope,
1154
- ]));
1155
- return [...unique.values()].sort((a, b) => {
1156
- if (a.campaignId !== b.campaignId) {
1157
- return a.campaignId.localeCompare(b.campaignId);
1158
- }
1159
- return a.tableId.localeCompare(b.tableId);
1160
- });
1161
- }
1162
- function sanitizeTargetSchedulerSweep(params) {
1163
- if (!Array.isArray(params.value) ||
1164
- !params.expectedWorkspaceId ||
1165
- !params.expectedTargetDate ||
1166
- !/^\d{4}-\d{2}-\d{2}$/.test(params.expectedTargetDate) ||
1167
- !params.expectedTargetShapeRevision ||
1168
- !params.expectedStateRevision ||
1169
- params.remainingProjectedGap <= 0 ||
1170
- params.remainingReadyOrProjectedGap !== 0) {
1171
- return null;
1172
- }
1173
- const expectedSenderIds = [...params.selectedBySender.keys()].sort();
1174
- const expectedActionTypes = [
1175
- ...new Set(params.selectedBySender.values()),
1176
- ].sort();
1177
- if (expectedSenderIds.length === 0 || expectedActionTypes.length === 0) {
1178
- return null;
1179
- }
1180
- for (const rawAction of params.value) {
1181
- if (!isRecord(rawAction) || rawAction.type !== "run_scheduler_sweep") {
1182
- continue;
1183
- }
1184
- const input = isRecord(rawAction.toolInput) ? rawAction.toolInput : {};
1185
- const actionKey = stringValue(rawAction.actionKey)?.trim();
1186
- const requestKey = stringValue(input.requestKey)?.trim();
1187
- const senderIds = sortedUniqueStrings(input.senderIds);
1188
- const actionTypes = sortedUniqueStrings(input.actionTypes).filter((actionType) => allowedActionType(actionType) === actionType);
1189
- const campaignIds = sortedUniqueStrings(input.campaignIds);
1190
- const tableIds = sortedUniqueStrings(input.tableIds);
1191
- const sourceScopes = sanitizeSchedulerSweepSourceScopes(input.sourceScopes);
1192
- const receiptRequirements = isRecord(rawAction.receiptRequirements)
1193
- ? rawAction.receiptRequirements
1194
- : {};
1195
- const groupBy = stringArray(receiptRequirements.groupBy);
1196
- const allowedSideEffects = stringArray(rawAction.allowedSideEffects);
1197
- const forbiddenSideEffects = stringArray(rawAction.forbiddenSideEffects);
1198
- const rawActionType = allowedActionType(rawAction.actionType);
1199
- if (input.action !== "run" ||
1200
- input.workspaceId !== params.expectedWorkspaceId ||
1201
- input.targetDate !== params.expectedTargetDate ||
1202
- input.targetDateKey !== params.expectedTargetDate ||
1203
- input.targetShapeRevision !== params.expectedTargetShapeRevision ||
1204
- input.stateRevision !== params.expectedStateRevision ||
1205
- !actionKey ||
1206
- requestKey !== actionKey ||
1207
- !sameStrings(senderIds, expectedSenderIds) ||
1208
- !sameStrings(actionTypes, expectedActionTypes) ||
1209
- (expectedActionTypes.length === 1 &&
1210
- rawActionType !== expectedActionTypes[0]) ||
1211
- campaignIds.length === 0 ||
1212
- tableIds.length === 0 ||
1213
- sourceScopes.length === 0 ||
1214
- sourceScopes.some((scope) => !campaignIds.includes(scope.campaignId) ||
1215
- !tableIds.includes(scope.tableId)) ||
1216
- rawAction.toolName !== "run_scheduler_sweep" ||
1217
- rawAction.sideEffectClass !== "scheduler_placement" ||
1218
- rawAction.workspaceWide !== true ||
1219
- rawAction.yoloEligible !== true ||
1220
- receiptRequirements.nonTruncated !== true ||
1221
- !sameStrings(groupBy, SCHEDULER_SWEEP_RECEIPT_GROUPS) ||
1222
- !sameStrings(allowedSideEffects, SCHEDULER_SWEEP_ALLOWED_SIDE_EFFECTS) ||
1223
- !sameStrings(forbiddenSideEffects, SCHEDULER_SWEEP_FORBIDDEN_SIDE_EFFECTS)) {
1224
- continue;
1225
- }
1226
- return {
1227
- rank: 1,
1228
- type: "run_scheduler_sweep",
1229
- actionType: rawActionType,
1230
- toolName: "run_scheduler_sweep",
1231
- sideEffectClass: "scheduler_placement",
1232
- ids: {},
1233
- toolInput: {
1234
- action: "run",
1235
- workspaceId: params.expectedWorkspaceId,
1236
- targetDate: params.expectedTargetDate,
1237
- targetDateKey: params.expectedTargetDate,
1238
- senderIds,
1239
- actionTypes,
1240
- campaignIds,
1241
- tableIds,
1242
- sourceScopes,
1243
- targetShapeRevision: params.expectedTargetShapeRevision,
1244
- stateRevision: params.expectedStateRevision,
1245
- requestKey,
1246
- },
1247
- inputSummary: stringValue(rawAction.inputSummary) ?? "",
1248
- reason: stringValue(rawAction.reason) ?? "",
1249
- prerequisites: stringArray(rawAction.prerequisites),
1250
- rereadAfter: rawAction.rereadAfter === "get_refill_target_plan"
1251
- ? "get_refill_target_plan"
1252
- : null,
1253
- stopCondition: stringValue(rawAction.stopCondition) ?? "",
1254
- yoloEligible: true,
1255
- workspaceWide: true,
1256
- allowedSideEffects: [...SCHEDULER_SWEEP_ALLOWED_SIDE_EFFECTS],
1257
- forbiddenSideEffects: [...SCHEDULER_SWEEP_FORBIDDEN_SIDE_EFFECTS],
1258
- receiptRequirements: {
1259
- nonTruncated: true,
1260
- groupBy: [...SCHEDULER_SWEEP_RECEIPT_GROUPS],
1261
- },
1262
- actionKey,
1263
- };
1264
- }
1265
- return null;
1266
- }
1267
817
  function paidRefreshNeededSenderIds(senderRefillPlans) {
1268
818
  const senderIds = new Set();
1269
819
  for (const plan of senderRefillPlans) {
@@ -1385,17 +935,11 @@ function sanitizeRefillTargetPlanResult(result) {
1385
935
  const actionTypes = [...new Set(selectedBySender.values())];
1386
936
  if (selectedKeys.size === 0)
1387
937
  return emptyUnsupportedResult(result);
1388
- const senderPlans = rawSenderPlans
1389
- .filter((plan) => isRecord(plan) &&
1390
- selectedKeys.has(`${String(plan.senderId)}:${String(plan.actionType)}`))
1391
- .map(sanitizeSenderPlan)
1392
- .filter((plan) => Boolean(plan));
938
+ const senderPlans = rawSenderPlans.filter((plan) => isRecord(plan) &&
939
+ selectedKeys.has(`${String(plan.senderId)}:${String(plan.actionType)}`));
1393
940
  const selectedDays = Array.isArray(target.selectedDays)
1394
- ? target.selectedDays
1395
- .filter((day) => isRecord(day) &&
941
+ ? target.selectedDays.filter((day) => isRecord(day) &&
1396
942
  selectedKeys.has(`${String(day.senderId)}:${String(day.actionType)}`))
1397
- .map(sanitizeSelectedDay)
1398
- .filter((day) => Boolean(day))
1399
943
  : [];
1400
944
  const actionSelections = rawSelections
1401
945
  .filter((selection) => isRecord(selection) &&
@@ -1403,22 +947,20 @@ function sanitizeRefillTargetPlanResult(result) {
1403
947
  selectedBySender.has(selection.senderId))
1404
948
  .map((selection) => {
1405
949
  const actionType = selectedBySender.get(selection.senderId);
1406
- const sanitized = sanitizeActionSelection(selection);
1407
- if (!sanitized)
1408
- return null;
1409
- const evidence = sanitized.evidence.filter((item) => item.actionType === actionType);
950
+ const evidence = Array.isArray(selection.evidence)
951
+ ? selection.evidence.filter((item) => isRecord(item) && item.actionType === actionType)
952
+ : [];
1410
953
  return {
1411
- ...sanitized,
954
+ ...selection,
1412
955
  actionTypes: actionType ? [actionType] : [],
1413
956
  evidence,
1414
957
  };
1415
- })
1416
- .filter((selection) => Boolean(selection));
958
+ });
1417
959
  const grossTarget = senderPlans.reduce((sum, plan) => sum + numberValue(plan.grossTarget), 0);
1418
960
  const requestedTarget = senderPlans.reduce((sum, plan) => sum + numberValue(plan.requestedTarget), 0);
1419
961
  const effectiveTarget = senderPlans.reduce((sum, plan) => {
1420
- const target = typeof plan.effectiveTarget === "number"
1421
- ? plan.effectiveTarget
962
+ const target = typeof plan.schedulerCapacityTarget === "number"
963
+ ? plan.schedulerCapacityTarget
1422
964
  : numberValue(plan.grossTarget);
1423
965
  return sum + target;
1424
966
  }, 0);
@@ -1433,20 +975,7 @@ function sanitizeRefillTargetPlanResult(result) {
1433
975
  const senderRefillPlans = sanitizeStructuredSenderPlans(target.senderRefillPlans, selectedBySender);
1434
976
  const paidRefreshNeededSenderIdSet = paidRefreshNeededSenderIds(senderRefillPlans);
1435
977
  const blockers = sanitizeBlockers(result.blockers, selectedKeys, paidRefreshNeededSenderIdSet);
1436
- const request = isRecord(result.request) ? result.request : {};
1437
- const schedulerSweepAction = sanitizeTargetSchedulerSweep({
1438
- value: target.globalActionQueue,
1439
- selectedBySender,
1440
- expectedWorkspaceId: stringValue(target.workspaceId),
1441
- expectedTargetDate: stringValue(request.targetDate),
1442
- expectedTargetShapeRevision: stringValue(result.targetShapeRevision),
1443
- expectedStateRevision: stringValue(result.stateRevision),
1444
- remainingProjectedGap,
1445
- remainingReadyOrProjectedGap,
1446
- });
1447
- const globalActionQueue = schedulerSweepAction
1448
- ? [schedulerSweepAction]
1449
- : buildSanitizedGlobalActionQueue(senderRefillPlans);
978
+ const globalActionQueue = buildSanitizedGlobalActionQueue(senderRefillPlans);
1450
979
  const hasTerminalNoActionBlocker = globalActionQueue.length === 0 &&
1451
980
  blockers.some((blocker) => TERMINAL_NO_ACTION_BLOCKER_CODES.has(String(blocker.code)));
1452
981
  const status = remainingProjectedGap === 0
@@ -1483,7 +1012,6 @@ function sanitizeRefillTargetPlanResult(result) {
1483
1012
  remainingProjectedGap,
1484
1013
  remainingReadyOrProjectedGap,
1485
1014
  remainingReadyOrScheduledGap: remainingReadyOrProjectedGap,
1486
- ...sanitizePhase98RefillContract(target),
1487
1015
  },
1488
1016
  coverage: isRecord(result.coverage)
1489
1017
  ? {
@@ -1501,7 +1029,7 @@ function sanitizeRefillTargetPlanResult(result) {
1501
1029
  export const refillTargetPlanToolDefinitions = [
1502
1030
  {
1503
1031
  name: "get_refill_target_plan",
1504
- description: "read-only refill target planner to call before any refill mutation. It identifies eligible senders and infers one implicit refill lane per sender. For unified Sales Nav cascade campaigns with campaign classification sales_nav_cascade, Open InMail, paid InMail, and connection fallback satisfy one sales_nav_cascade goal while branch metrics remain visible; row routing still uses fresh paid credits >= 5 and reports same-campaign connection fallback availability. Campaign and sender names stay primary labels and stable IDs remain execution proof. Non-cascade lanes remain connection invites or paid InMails, chosen from future scheduled, recent scheduled, and ready-to-schedule evidence in active campaign-backed sequence campaigns before falling back to campaign sequences. DMs remain follow-up sequence actions, not refill target lanes. By default the planner computes the scheduler-forward 48-hour target window, selected sender-local days whose sending windows overlap that window, raw coverage, projected coverage (sent + scheduled), goal-capped projected coverage, ready buffer, remaining projected gap, allocatable preparation deficit, unallocatable supply/deficit, paid InMail credit/threshold/freshness feasibility, bounded action candidates, and targetRevision drift proof. A calibrated small gap may return one scheduler_first_settle read-only receipt with a stable key/deadline before bounded preparation. This tool does not create rows, import leads, prepare messages, approve messages, does not schedule sends, start campaigns, lower paid InMail thresholds, create campaigns, launch, spend InMail credits, or write scheduler fields. Complete projected targets no-op without approval; loaded, awaiting scheduler targets use read-only wait/reread until projected coverage fills.",
1032
+ description: "read-only refill target planner to call before any refill mutation. It identifies eligible senders and infers one implicit refill lane per sender. For unified Sales Nav cascade campaigns, the public refill lane is the paid-InMail cascade target (send_inmail_closed) with campaign classification sales_nav_cascade; the same campaign can still route prospects to Open InMail or connection-request fallback based on row eligibility and fresh paid credits >= 5. Non-cascade lanes remain connection invites or paid InMails, chosen from future scheduled, recent scheduled, and ready-to-schedule evidence in active campaign-backed sequence campaigns before falling back to campaign sequences. DMs remain follow-up sequence actions, not refill target lanes. By default the planner computes the scheduler-forward 48-hour target window, selected sender-local days whose sending windows overlap that window, gross target, actual sent coverage, scheduler-owned scheduled coverage, projected coverage (sent + scheduled), ready buffer, remaining projected gap, paid InMail credit/threshold/freshness feasibility, same-campaign connection fallback availability, bounded action candidates, and targetRevision drift proof. This tool does not create rows, import leads, prepare messages, approve messages, does not schedule sends, start campaigns, lower paid InMail thresholds, create campaigns, launch, spend InMail credits, or write scheduler fields. Complete projected targets no-op without approval; loaded, awaiting scheduler targets use read-only wait/reread until projected coverage fills.",
1505
1033
  inputSchema: {
1506
1034
  type: "object",
1507
1035
  properties: {