@sellable/mcp 0.1.643 → 0.1.644

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.
@@ -965,8 +965,8 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
965
965
  } | {
966
966
  ok: boolean;
967
967
  status: "blocked";
968
- code: "workspace_exact_target_unavailable" | "workspace_exact_target_ambiguous" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch";
969
- blocker: "workspace_exact_target_unavailable" | "workspace_exact_target_ambiguous" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch";
968
+ code: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch";
969
+ blocker: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch";
970
970
  note: string;
971
971
  targetPlan: unknown;
972
972
  exactTargetPlan: {} | null;
@@ -1454,8 +1454,8 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
1454
1454
  } | {
1455
1455
  ok: boolean;
1456
1456
  status: "blocked";
1457
- code: "workspace_exact_target_unavailable" | "workspace_exact_target_ambiguous" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch";
1458
- blocker: "workspace_exact_target_unavailable" | "workspace_exact_target_ambiguous" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch";
1457
+ code: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch";
1458
+ blocker: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch";
1459
1459
  note: string;
1460
1460
  targetPlan: unknown;
1461
1461
  exactTargetPlan: {} | null;
@@ -1,12 +1,17 @@
1
1
  import { decodeRefillReadinessAuthority, refillReadinessFingerprint, } from "../refill-readiness.js";
2
2
  import { MESSAGE_TEMPLATE_REVISION_INPUT_SCHEMA, REFILL_REPORTING_CONTEXT_INPUT_SCHEMA, REFILL_RUN_HANDLE_INPUT_SCHEMA, REFILL_TARGET_CONFIG_INPUT_SCHEMA, normalizeMessageTemplateRevision, normalizeRefillRunHandle, normalizeRefillTargetConfig, } from "./campaign-processing.js";
3
- import { actionCampaignId, actionIds, actionSenderId, actionTableId, classifyPaidInmailRefreshReceipt, collectPaidInmailRefreshActions, executeOneYoloPrimitive, executeStartCampaignPrimitive, firstGlobalAction, normalizeStrings, numberValue, recordValue, refreshPaidInmailCreditsWithRetry, stringValue, } from "./refill-executors.js";
3
+ import { actionCampaignId, actionIds, actionSenderId, classifyPaidInmailRefreshReceipt, collectPaidInmailRefreshActions, executeOneYoloPrimitive, executeStartCampaignPrimitive, firstGlobalAction, normalizeStrings, numberValue, recordValue, refreshPaidInmailCreditsWithRetry, stringValue, } from "./refill-executors.js";
4
4
  import { refillSendsV2Command } from "./refill-sends-v2.js";
5
5
  import { getRefillTargetPlan } from "./refill-target-plan.js";
6
6
  import { listSenders } from "./senders.js";
7
7
  import { createWorkspaceContext, normalizeExplicitWorkspaceId, } from "./workspace-context.js";
8
8
  const REFILL_SEND_ACTION_TYPES = ["send_invite", "send_inmail_closed"];
9
9
  const REFILL_SEND_ACTION_TYPE_SET = new Set(REFILL_SEND_ACTION_TYPES);
10
+ const WORKSPACE_EXACT_BRANCH_ACTION_TYPE_SET = new Set([
11
+ "send_invite",
12
+ "send_inmail_open",
13
+ "send_inmail_closed",
14
+ ]);
10
15
  const REFILL_SEND_INTENT_SET = new Set([
11
16
  "plain",
12
17
  "active",
@@ -520,118 +525,37 @@ function workspaceCoordinatorInput(input) {
520
525
  delete coordinatorInput.agentKeywords;
521
526
  return coordinatorInput;
522
527
  }
523
- function recordsWithExactTarget(value) {
524
- return Array.isArray(value)
525
- ? value
526
- .map(recordValue)
527
- .filter((candidate) => Boolean(candidate &&
528
- stringValue(candidate.campaignId) &&
529
- stringValue(candidate.tableId)))
530
- : [];
531
- }
532
- function workspaceExactActionSignature(action) {
533
- const campaignId = actionCampaignId(action);
534
- const tableId = actionTableId(action);
535
- const actionKey = stringValue(action.actionKey);
536
- const actionType = stringValue(action.actionType);
537
- if (!campaignId || !tableId || !actionKey || !actionType)
538
- return null;
539
- return JSON.stringify([campaignId, tableId, actionKey, actionType]);
540
- }
541
- function workspaceAggregateActionSenderIds(workspacePlan) {
542
- const target = yoloTarget(workspacePlan);
543
- const senderPlans = Array.isArray(target?.senderRefillPlans)
544
- ? target.senderRefillPlans
545
- .map(recordValue)
546
- .filter((plan) => Boolean(plan))
547
- : [];
548
- const senderIdsByAction = new Map();
549
- for (const senderPlan of senderPlans) {
550
- const senderId = stringValue(senderPlan.senderId);
551
- const nextActions = Array.isArray(senderPlan.nextActions)
552
- ? senderPlan.nextActions
553
- .map(recordValue)
554
- .filter((action) => Boolean(action))
555
- : [];
556
- if (!senderId)
557
- continue;
558
- for (const action of nextActions) {
559
- const signature = workspaceExactActionSignature(action);
560
- if (!signature)
561
- continue;
562
- const senderIds = senderIdsByAction.get(signature) ?? new Set();
563
- senderIds.add(senderId);
564
- senderIdsByAction.set(signature, senderIds);
565
- }
528
+ function workspaceExactTargetsFromAction(action) {
529
+ if (!Array.isArray(action.exactTargets) || action.exactTargets.length === 0) {
530
+ return [];
566
531
  }
567
- return new Map([...senderIdsByAction.entries()]
568
- .filter(([, senderIds]) => senderIds.size > 1)
569
- .map(([signature, senderIds]) => [
570
- signature,
571
- [...senderIds],
572
- ]));
573
- }
574
- function workspaceExactTargetsFromAction(action, aggregateSenderIdsByAction = new Map()) {
575
- const toolInput = recordValue(action.toolInput) ?? {};
576
- const exactTargets = recordsWithExactTarget(toolInput.exactTargets);
577
- const expectedTargets = recordsWithExactTarget(toolInput.expectedTargets);
578
- const sourceScopes = recordsWithExactTarget(toolInput.sourceScopes);
579
- const exactEdges = exactTargets.length > 0
580
- ? exactTargets
581
- : expectedTargets.length > 0
582
- ? expectedTargets
583
- : sourceScopes.length > 0
584
- ? sourceScopes
585
- : [null];
586
- const campaignIds = normalizeStrings(toolInput.campaignIds);
587
- const tableIds = normalizeStrings(toolInput.tableIds);
588
- return exactEdges.flatMap((exactEdge) => {
589
- const campaignId = stringValue(exactEdge?.campaignId) ??
590
- actionCampaignId(action) ??
591
- campaignIds[0];
592
- const tableId = stringValue(exactEdge?.tableId) ?? actionTableId(action) ?? tableIds[0];
593
- if (!campaignId || !tableId)
532
+ const targets = action.exactTargets.flatMap((value) => {
533
+ const exactTarget = recordValue(value);
534
+ const campaignId = stringValue(exactTarget?.campaignId);
535
+ const tableId = stringValue(exactTarget?.tableId);
536
+ const senderIds = normalizeStrings(exactTarget?.senderIds);
537
+ const refillLaneKey = stringValue(exactTarget?.refillLaneKey);
538
+ const branchActionTypes = normalizeStrings(exactTarget?.branchActionTypes);
539
+ if (!campaignId ||
540
+ !tableId ||
541
+ senderIds.length === 0 ||
542
+ !refillLaneKey ||
543
+ !REFILL_SEND_ACTION_TYPE_SET.has(refillLaneKey) ||
544
+ branchActionTypes.length === 0 ||
545
+ branchActionTypes.some((actionType) => !WORKSPACE_EXACT_BRANCH_ACTION_TYPE_SET.has(actionType))) {
594
546
  return [];
595
- const exactEdgeSenderIds = normalizeStrings(exactEdge?.senderIds);
596
- const exactEdgeSenderId = stringValue(exactEdge?.senderId);
597
- const toolInputSenderIds = normalizeStrings(toolInput.senderIds);
598
- const aggregateSenderIds = aggregateSenderIdsByAction.get(workspaceExactActionSignature(action) ?? "") ?? [];
599
- const actionSenderIdValue = actionSenderId(action);
600
- // Explicit edge/list scope is authoritative. Legacy shared campaign
601
- // actions predate expectedTargets and can repeat the same representative
602
- // sender action across several senderRefillPlans. Preserve that recovered
603
- // member set instead of narrowing the exact coordinator to the complete
604
- // representative sender and hiding another member's actionable gap.
605
- const senderIds = exactEdgeSenderIds.length > 0
606
- ? exactEdgeSenderIds
607
- : exactEdgeSenderId
608
- ? [exactEdgeSenderId]
609
- : toolInputSenderIds.length > 0
610
- ? toolInputSenderIds
611
- : aggregateSenderIds.length > 0
612
- ? aggregateSenderIds
613
- : actionSenderIdValue
614
- ? [actionSenderIdValue]
615
- : [];
616
- const directActionType = stringValue(exactEdge?.actionType) ??
617
- stringValue(exactEdge?.refillLaneKey) ??
618
- stringValue(action.actionType);
619
- const exactEdgeActionTypes = normalizeStrings(exactEdge?.branchActionTypes);
620
- const actionTypes = directActionType && REFILL_SEND_ACTION_TYPE_SET.has(directActionType)
621
- ? [directActionType]
622
- : (exactEdgeActionTypes.length > 0
623
- ? exactEdgeActionTypes
624
- : normalizeStrings(toolInput.actionTypes)).filter((actionType) => REFILL_SEND_ACTION_TYPE_SET.has(actionType));
547
+ }
625
548
  return [
626
549
  {
627
550
  campaignId,
628
551
  tableId,
629
552
  senderIds,
630
- actionTypes,
553
+ actionTypes: [refillLaneKey],
631
554
  actionKey: stringValue(action.actionKey) ?? null,
632
555
  },
633
556
  ];
634
557
  });
558
+ return targets.length === action.exactTargets.length ? targets : [];
635
559
  }
636
560
  function workspaceExactTargetKey(target) {
637
561
  return JSON.stringify([
@@ -641,55 +565,6 @@ function workspaceExactTargetKey(target) {
641
565
  [...new Set(target.actionTypes)].sort(),
642
566
  ]);
643
567
  }
644
- function evergreenExactTargetCandidates(workspacePlan, options) {
645
- const root = recordValue(workspacePlan) ?? {};
646
- const target = yoloTarget(workspacePlan) ?? {};
647
- const evergreen = recordValue(root.evergreen) ??
648
- recordValue(recordValue(root.packet)?.evergreen) ??
649
- {};
650
- const laneOrder = Array.isArray(evergreen.laneOrder)
651
- ? evergreen.laneOrder.map(recordValue).filter(Boolean)
652
- : [];
653
- const laneByKey = new Map(laneOrder
654
- .map((lane) => [stringValue(lane?.laneKey), lane])
655
- .filter((entry) => Boolean(entry[0] && entry[1])));
656
- const campaigns = Array.isArray(target.campaigns)
657
- ? target.campaigns.map(recordValue).filter(Boolean)
658
- : [];
659
- const campaignById = new Map(campaigns
660
- .map((campaign) => [stringValue(campaign?.campaignId), campaign])
661
- .filter((entry) => Boolean(entry[0] && entry[1])));
662
- const selections = Array.isArray(evergreen.senderLaneSelections)
663
- ? evergreen.senderLaneSelections
664
- .map(recordValue)
665
- .filter(Boolean)
666
- : [];
667
- const selection = selections.find((candidate) => stringValue(candidate?.senderId) === options.preferredSenderId) ?? selections[0];
668
- const senderId = stringValue(selection?.senderId);
669
- const laneChain = Array.isArray(selection?.laneChain)
670
- ? normalizeStrings(selection.laneChain)
671
- : laneOrder
672
- .map((lane) => stringValue(lane?.laneKey))
673
- .filter((laneKey) => Boolean(laneKey));
674
- return laneChain.flatMap((laneKey) => {
675
- const lane = laneByKey.get(laneKey);
676
- const campaignId = stringValue(lane?.campaignId) ??
677
- (campaignById.has(laneKey) ? laneKey : null);
678
- const campaign = campaignId ? campaignById.get(campaignId) : null;
679
- const tableId = stringValue(campaign?.tableId) ?? stringValue(lane?.tableId);
680
- if (!campaignId || !tableId)
681
- return [];
682
- return [
683
- {
684
- campaignId,
685
- tableId,
686
- senderIds: senderId ? [senderId] : [],
687
- actionTypes: options.actionTypes ?? [],
688
- actionKey: null,
689
- },
690
- ];
691
- });
692
- }
693
568
  async function selectWorkspaceExactTarget(input, workspacePlanOverride, options = {}) {
694
569
  const workspaceInput = workspaceCoordinatorInput(input);
695
570
  let workspacePlan = workspacePlanOverride ??
@@ -724,41 +599,18 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
724
599
  .filter((action) => Boolean(action))
725
600
  : [];
726
601
  const workspaceActions = [...globalActions, ...rankedCandidates];
727
- const aggregateSenderIdsByAction = workspaceAggregateActionSenderIds(workspacePlan);
728
- const legacyGlobalActionCounts = new Map();
729
- for (const action of globalActions) {
730
- const signature = workspaceExactActionSignature(action);
731
- if (!signature)
732
- continue;
733
- legacyGlobalActionCounts.set(signature, (legacyGlobalActionCounts.get(signature) ?? 0) + 1);
734
- }
735
- const ambiguousLegacyAction = globalActions.find((action) => {
736
- const signature = workspaceExactActionSignature(action);
737
- if (!signature || (legacyGlobalActionCounts.get(signature) ?? 0) < 2) {
738
- return false;
739
- }
740
- const toolInput = recordValue(action.toolInput) ?? {};
741
- const hasExplicitTargets = recordsWithExactTarget(toolInput.exactTargets).length > 0 ||
742
- recordsWithExactTarget(toolInput.expectedTargets).length > 0 ||
743
- recordsWithExactTarget(toolInput.sourceScopes).length > 0;
744
- return !hasExplicitTargets && !aggregateSenderIdsByAction.has(signature);
745
- });
746
- if (ambiguousLegacyAction) {
602
+ const queueHead = globalActions[0] ?? rankedCandidates[0];
603
+ if (queueHead && workspaceExactTargetsFromAction(queueHead).length === 0) {
747
604
  return {
748
605
  status: "blocked",
749
- code: "workspace_exact_target_ambiguous",
750
- note: "The workspace planner repeated one legacy aggregate action without explicit exact targets or recoverable sender membership. No refill run or mutation was started.",
606
+ code: "workspace_exact_target_contract_missing",
607
+ note: "The workspace planner's current action did not include a complete exactTargets contract. No refill run or mutation was started.",
751
608
  workspacePlan,
752
609
  };
753
610
  }
754
- const actionTargets = workspaceActions.flatMap((action) => workspaceExactTargetsFromAction(action, aggregateSenderIdsByAction));
755
- const evergreenTargets = evergreenExactTargetCandidates(workspacePlan, {
756
- preferredSenderId: options.preferredSenderId,
757
- actionTypes: input.actionTypes,
758
- }).filter((candidate) => !actionTargets.some((actionTarget) => actionTarget.campaignId === candidate.campaignId &&
759
- actionTarget.tableId === candidate.tableId));
611
+ const actionTargets = workspaceActions.flatMap((action) => workspaceExactTargetsFromAction(action));
760
612
  const seenTargetKeys = new Set();
761
- const targetCandidates = [...actionTargets, ...evergreenTargets].filter((candidate) => {
613
+ const targetCandidates = actionTargets.filter((candidate) => {
762
614
  const key = workspaceExactTargetKey(candidate);
763
615
  if (seenTargetKeys.has(key))
764
616
  return false;
@@ -1878,7 +1730,6 @@ export async function executeRefillSendsCommand(input = {}) {
1878
1730
  if (isWorkspaceCoordinator && result.status === "terminal") {
1879
1731
  const resultReport = recordValue(result.report) ?? {};
1880
1732
  const coordinatorDirective = recordValue(resultReport.coordinatorDirective);
1881
- const laneExhaustion = recordValue(resultReport.laneExhaustion);
1882
1733
  const excludedTargets = Array.isArray(coordinatorDirective?.excludeTargets)
1883
1734
  ? coordinatorDirective.excludeTargets
1884
1735
  .map(recordValue)
@@ -1890,7 +1741,6 @@ export async function executeRefillSendsCommand(input = {}) {
1890
1741
  : [];
1891
1742
  const nextSelection = await selectWorkspaceExactTarget(scopedInput, undefined, {
1892
1743
  excludeTargets: excludedTargets,
1893
- preferredSenderId: stringValue(laneExhaustion?.senderId) ?? undefined,
1894
1744
  });
1895
1745
  if (nextSelection.status === "complete") {
1896
1746
  return {
@@ -383,7 +383,7 @@ function sanitizeActionCandidates(candidates, selectedKeys, paidRefreshNeededSen
383
383
  const key = senderActionKey(item.senderId, allowed);
384
384
  return Boolean(key && selectedKeys.has(key));
385
385
  })
386
- .map(sanitizeActionCandidate)
386
+ .map((candidate) => sanitizeActionCandidate(candidate, selectedKeys))
387
387
  : [];
388
388
  if (status === "awaiting_scheduler_after_ready_buffer" &&
389
389
  remainingProjectedGap > 0 &&
@@ -396,7 +396,7 @@ function sanitizeActionCandidates(candidates, selectedKeys, paidRefreshNeededSen
396
396
  }
397
397
  return filtered;
398
398
  }
399
- function sanitizeActionCandidate(candidate) {
399
+ function sanitizeActionCandidate(candidate, selectedKeys) {
400
400
  const primary = decodeRefillReadinessIdentity(candidate.readinessIdentity);
401
401
  const readinessIdentities = Array.isArray(candidate.readinessIdentities)
402
402
  ? candidate.readinessIdentities.flatMap((identity) => {
@@ -404,14 +404,41 @@ function sanitizeActionCandidate(candidate) {
404
404
  return decoded.ok ? [decoded.value] : [];
405
405
  })
406
406
  : [];
407
+ const actionType = allowedActionType(candidate.actionType);
408
+ const campaignId = stringValue(candidate.campaignId);
409
+ const tableId = stringValue(candidate.tableId);
410
+ const sharedLane = isRecord(candidate.sharedLane)
411
+ ? candidate.sharedLane
412
+ : null;
413
+ const candidateSenderIds = [
414
+ ...new Set([
415
+ ...stringArray(sharedLane?.memberSenderIds),
416
+ ...(stringValue(candidate.senderId)
417
+ ? [stringValue(candidate.senderId)]
418
+ : []),
419
+ ]),
420
+ ]
421
+ .filter((senderId) => actionType && selectedKeys.has(`${senderId}:${actionType}`))
422
+ .sort();
423
+ const exactTargets = campaignId && tableId && actionType && candidateSenderIds.length > 0
424
+ ? [
425
+ {
426
+ campaignId,
427
+ tableId,
428
+ senderIds: candidateSenderIds,
429
+ refillLaneKey: actionType,
430
+ branchActionTypes: [actionType],
431
+ },
432
+ ]
433
+ : [];
407
434
  return {
408
435
  type: candidate.type,
409
436
  senderId: stringValue(candidate.senderId),
410
437
  senderName: stringValue(candidate.senderName),
411
- actionType: stringValue(candidate.actionType),
412
- campaignId: stringValue(candidate.campaignId),
438
+ actionType,
439
+ campaignId,
413
440
  campaignName: stringValue(candidate.campaignName),
414
- tableId: stringValue(candidate.tableId),
441
+ tableId,
415
442
  tableName: stringValue(candidate.tableName) ?? null,
416
443
  columnId: stringValue(candidate.columnId) ?? null,
417
444
  sourceLeadListId: stringValue(candidate.sourceLeadListId) ?? null,
@@ -444,8 +471,40 @@ function sanitizeActionCandidate(candidate) {
444
471
  effectId: stringValue(candidate.effectId),
445
472
  targetShapeRevision: stringValue(candidate.targetShapeRevision),
446
473
  stateRevision: stringValue(candidate.stateRevision),
474
+ exactTargets,
447
475
  };
448
476
  }
477
+ function sanitizeExactTargets(value) {
478
+ if (!Array.isArray(value))
479
+ return [];
480
+ return value
481
+ .slice(0, REFILL_SCHEDULER_EXPECTED_TARGET_LIMIT)
482
+ .flatMap((raw) => {
483
+ if (!isRecord(raw))
484
+ return [];
485
+ const campaignId = stringValue(raw.campaignId)?.trim();
486
+ const tableId = stringValue(raw.tableId)?.trim();
487
+ const refillLaneKey = allowedActionType(raw.refillLaneKey ?? raw.actionType);
488
+ const senderIds = sortedUniqueStrings(raw.senderIds);
489
+ const branchActionTypes = sortedUniqueStrings(raw.branchActionTypes).filter((actionType) => SCHEDULER_EXPECTED_BRANCH_ACTION_TYPES.has(actionType));
490
+ if (!campaignId ||
491
+ !tableId ||
492
+ !refillLaneKey ||
493
+ senderIds.length === 0 ||
494
+ branchActionTypes.length === 0) {
495
+ return [];
496
+ }
497
+ return [
498
+ {
499
+ campaignId,
500
+ tableId,
501
+ senderIds,
502
+ refillLaneKey,
503
+ branchActionTypes,
504
+ },
505
+ ];
506
+ });
507
+ }
449
508
  function sanitizeRowSelector(value) {
450
509
  if (!isRecord(value) || typeof value.type !== "string")
451
510
  return undefined;
@@ -840,6 +899,7 @@ function sanitizeStructuredAction(action, selectedBySender, mode, rank) {
840
899
  ...sanitizeToolInput(action.toolInput, authority),
841
900
  ...(providerSearchReference ? { providerSearchReference } : {}),
842
901
  },
902
+ exactTargets: sanitizeExactTargets(action.exactTargets),
843
903
  inputSummary: stringValue(action.inputSummary) ?? "",
844
904
  reason: stringValue(action.reason) ?? "",
845
905
  refillReceipt: sanitizeRefillReceipt(action.refillReceipt),
@@ -1182,13 +1242,78 @@ function sanitizeStructuredSenderPlans(value, selectedBySender) {
1182
1242
  });
1183
1243
  }
1184
1244
  function buildSanitizedGlobalActionQueue(senderRefillPlans) {
1185
- return senderRefillPlans
1186
- .flatMap((plan) => Array.isArray(plan.nextActions) ? plan.nextActions.slice(0, 1) : [])
1187
- .filter((action) => isRecord(action))
1188
- .map((action, index) => ({
1189
- ...action,
1190
- rank: index + 1,
1191
- }));
1245
+ const entries = senderRefillPlans.flatMap((plan) => Array.isArray(plan.nextActions)
1246
+ ? plan.nextActions
1247
+ .slice(0, 1)
1248
+ .filter((action) => isRecord(action))
1249
+ .map((action) => ({ plan, action }))
1250
+ : []);
1251
+ const signatureFor = (action) => {
1252
+ const ids = isRecord(action.ids) ? action.ids : {};
1253
+ const campaignId = stringValue(ids.campaignId);
1254
+ const tableId = stringValue(ids.tableId);
1255
+ const actionType = allowedActionType(action.actionType);
1256
+ const actionKey = stringValue(action.actionKey);
1257
+ if (!campaignId || !tableId || !actionType || !actionKey)
1258
+ return null;
1259
+ return JSON.stringify([
1260
+ action.type,
1261
+ actionKey,
1262
+ campaignId,
1263
+ tableId,
1264
+ actionType,
1265
+ ]);
1266
+ };
1267
+ const senderIdsBySignature = new Map();
1268
+ for (const { plan, action } of entries) {
1269
+ const signature = signatureFor(action);
1270
+ const senderId = stringValue(plan.senderId);
1271
+ if (!signature || !senderId)
1272
+ continue;
1273
+ const senderIds = senderIdsBySignature.get(signature) ?? new Set();
1274
+ senderIds.add(senderId);
1275
+ senderIdsBySignature.set(signature, senderIds);
1276
+ }
1277
+ const seen = new Set();
1278
+ return entries
1279
+ .filter(({ action }) => {
1280
+ const signature = signatureFor(action);
1281
+ if (!signature)
1282
+ return true;
1283
+ if (seen.has(signature))
1284
+ return false;
1285
+ seen.add(signature);
1286
+ return true;
1287
+ })
1288
+ .map(({ action }, index) => {
1289
+ const signature = signatureFor(action);
1290
+ const ids = isRecord(action.ids) ? action.ids : {};
1291
+ const actionType = allowedActionType(action.actionType);
1292
+ const senderIds = signature
1293
+ ? [...(senderIdsBySignature.get(signature) ?? [])]
1294
+ : [];
1295
+ senderIds.sort();
1296
+ const exactTargets = signature &&
1297
+ actionType &&
1298
+ stringValue(ids.campaignId) &&
1299
+ stringValue(ids.tableId) &&
1300
+ senderIds.length > 0
1301
+ ? [
1302
+ {
1303
+ campaignId: stringValue(ids.campaignId),
1304
+ tableId: stringValue(ids.tableId),
1305
+ senderIds,
1306
+ refillLaneKey: actionType,
1307
+ branchActionTypes: [actionType],
1308
+ },
1309
+ ]
1310
+ : sanitizeExactTargets(action.exactTargets);
1311
+ return {
1312
+ ...action,
1313
+ exactTargets,
1314
+ rank: index + 1,
1315
+ };
1316
+ });
1192
1317
  }
1193
1318
  function sortedUniqueStrings(value) {
1194
1319
  return [...new Set(stringArray(value))].sort();
@@ -1387,6 +1512,7 @@ function sanitizeTargetSchedulerSweep(params) {
1387
1512
  ? refillReadinessExpectationInput(authority)
1388
1513
  : {}),
1389
1514
  },
1515
+ exactTargets: expectedTargets,
1390
1516
  inputSummary: stringValue(rawAction.inputSummary) ?? "",
1391
1517
  reason: stringValue(rawAction.reason) ?? "",
1392
1518
  prerequisites: stringArray(rawAction.prerequisites),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.643",
3
+ "version": "0.1.644",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code, Codex, and Hermes campaign workflows",
6
6
  "main": "dist/index.js",