@sellable/mcp 0.1.642 → 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" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch";
969
- blocker: "workspace_exact_target_unavailable" | "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" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch";
1458
- blocker: "workspace_exact_target_unavailable" | "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,59 +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
528
  function workspaceExactTargetsFromAction(action) {
533
- const toolInput = recordValue(action.toolInput) ?? {};
534
- const expectedTargets = recordsWithExactTarget(toolInput.expectedTargets);
535
- const sourceScopes = recordsWithExactTarget(toolInput.sourceScopes);
536
- const exactEdges = expectedTargets.length > 0
537
- ? expectedTargets
538
- : sourceScopes.length > 0
539
- ? sourceScopes
540
- : [null];
541
- const campaignIds = normalizeStrings(toolInput.campaignIds);
542
- const tableIds = normalizeStrings(toolInput.tableIds);
543
- return exactEdges.flatMap((exactEdge) => {
544
- const campaignId = stringValue(exactEdge?.campaignId) ??
545
- actionCampaignId(action) ??
546
- campaignIds[0];
547
- const tableId = stringValue(exactEdge?.tableId) ?? actionTableId(action) ?? tableIds[0];
548
- if (!campaignId || !tableId)
529
+ if (!Array.isArray(action.exactTargets) || action.exactTargets.length === 0) {
530
+ return [];
531
+ }
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))) {
549
546
  return [];
550
- const exactEdgeSenderIds = normalizeStrings(exactEdge?.senderIds);
551
- const senderId = stringValue(exactEdge?.senderId) ?? actionSenderId(action) ?? null;
552
- const senderIds = senderId
553
- ? [senderId]
554
- : exactEdgeSenderIds.length > 0
555
- ? exactEdgeSenderIds
556
- : normalizeStrings(toolInput.senderIds);
557
- const directActionType = stringValue(exactEdge?.actionType) ??
558
- stringValue(exactEdge?.refillLaneKey) ??
559
- stringValue(action.actionType);
560
- const exactEdgeActionTypes = normalizeStrings(exactEdge?.branchActionTypes);
561
- const actionTypes = directActionType && REFILL_SEND_ACTION_TYPE_SET.has(directActionType)
562
- ? [directActionType]
563
- : (exactEdgeActionTypes.length > 0
564
- ? exactEdgeActionTypes
565
- : normalizeStrings(toolInput.actionTypes)).filter((actionType) => REFILL_SEND_ACTION_TYPE_SET.has(actionType));
547
+ }
566
548
  return [
567
549
  {
568
550
  campaignId,
569
551
  tableId,
570
552
  senderIds,
571
- actionTypes,
553
+ actionTypes: [refillLaneKey],
572
554
  actionKey: stringValue(action.actionKey) ?? null,
573
555
  },
574
556
  ];
575
557
  });
558
+ return targets.length === action.exactTargets.length ? targets : [];
576
559
  }
577
560
  function workspaceExactTargetKey(target) {
578
561
  return JSON.stringify([
@@ -582,55 +565,6 @@ function workspaceExactTargetKey(target) {
582
565
  [...new Set(target.actionTypes)].sort(),
583
566
  ]);
584
567
  }
585
- function evergreenExactTargetCandidates(workspacePlan, options) {
586
- const root = recordValue(workspacePlan) ?? {};
587
- const target = yoloTarget(workspacePlan) ?? {};
588
- const evergreen = recordValue(root.evergreen) ??
589
- recordValue(recordValue(root.packet)?.evergreen) ??
590
- {};
591
- const laneOrder = Array.isArray(evergreen.laneOrder)
592
- ? evergreen.laneOrder.map(recordValue).filter(Boolean)
593
- : [];
594
- const laneByKey = new Map(laneOrder
595
- .map((lane) => [stringValue(lane?.laneKey), lane])
596
- .filter((entry) => Boolean(entry[0] && entry[1])));
597
- const campaigns = Array.isArray(target.campaigns)
598
- ? target.campaigns.map(recordValue).filter(Boolean)
599
- : [];
600
- const campaignById = new Map(campaigns
601
- .map((campaign) => [stringValue(campaign?.campaignId), campaign])
602
- .filter((entry) => Boolean(entry[0] && entry[1])));
603
- const selections = Array.isArray(evergreen.senderLaneSelections)
604
- ? evergreen.senderLaneSelections
605
- .map(recordValue)
606
- .filter(Boolean)
607
- : [];
608
- const selection = selections.find((candidate) => stringValue(candidate?.senderId) === options.preferredSenderId) ?? selections[0];
609
- const senderId = stringValue(selection?.senderId);
610
- const laneChain = Array.isArray(selection?.laneChain)
611
- ? normalizeStrings(selection.laneChain)
612
- : laneOrder
613
- .map((lane) => stringValue(lane?.laneKey))
614
- .filter((laneKey) => Boolean(laneKey));
615
- return laneChain.flatMap((laneKey) => {
616
- const lane = laneByKey.get(laneKey);
617
- const campaignId = stringValue(lane?.campaignId) ??
618
- (campaignById.has(laneKey) ? laneKey : null);
619
- const campaign = campaignId ? campaignById.get(campaignId) : null;
620
- const tableId = stringValue(campaign?.tableId) ?? stringValue(lane?.tableId);
621
- if (!campaignId || !tableId)
622
- return [];
623
- return [
624
- {
625
- campaignId,
626
- tableId,
627
- senderIds: senderId ? [senderId] : [],
628
- actionTypes: options.actionTypes ?? [],
629
- actionKey: null,
630
- },
631
- ];
632
- });
633
- }
634
568
  async function selectWorkspaceExactTarget(input, workspacePlanOverride, options = {}) {
635
569
  const workspaceInput = workspaceCoordinatorInput(input);
636
570
  let workspacePlan = workspacePlanOverride ??
@@ -665,14 +599,18 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
665
599
  .filter((action) => Boolean(action))
666
600
  : [];
667
601
  const workspaceActions = [...globalActions, ...rankedCandidates];
668
- const actionTargets = workspaceActions.flatMap(workspaceExactTargetsFromAction);
669
- const evergreenTargets = evergreenExactTargetCandidates(workspacePlan, {
670
- preferredSenderId: options.preferredSenderId,
671
- actionTypes: input.actionTypes,
672
- }).filter((candidate) => !actionTargets.some((actionTarget) => actionTarget.campaignId === candidate.campaignId &&
673
- actionTarget.tableId === candidate.tableId));
602
+ const queueHead = globalActions[0] ?? rankedCandidates[0];
603
+ if (queueHead && workspaceExactTargetsFromAction(queueHead).length === 0) {
604
+ return {
605
+ status: "blocked",
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.",
608
+ workspacePlan,
609
+ };
610
+ }
611
+ const actionTargets = workspaceActions.flatMap((action) => workspaceExactTargetsFromAction(action));
674
612
  const seenTargetKeys = new Set();
675
- const targetCandidates = [...actionTargets, ...evergreenTargets].filter((candidate) => {
613
+ const targetCandidates = actionTargets.filter((candidate) => {
676
614
  const key = workspaceExactTargetKey(candidate);
677
615
  if (seenTargetKeys.has(key))
678
616
  return false;
@@ -1792,7 +1730,6 @@ export async function executeRefillSendsCommand(input = {}) {
1792
1730
  if (isWorkspaceCoordinator && result.status === "terminal") {
1793
1731
  const resultReport = recordValue(result.report) ?? {};
1794
1732
  const coordinatorDirective = recordValue(resultReport.coordinatorDirective);
1795
- const laneExhaustion = recordValue(resultReport.laneExhaustion);
1796
1733
  const excludedTargets = Array.isArray(coordinatorDirective?.excludeTargets)
1797
1734
  ? coordinatorDirective.excludeTargets
1798
1735
  .map(recordValue)
@@ -1804,7 +1741,6 @@ export async function executeRefillSendsCommand(input = {}) {
1804
1741
  : [];
1805
1742
  const nextSelection = await selectWorkspaceExactTarget(scopedInput, undefined, {
1806
1743
  excludeTargets: excludedTargets,
1807
- preferredSenderId: stringValue(laneExhaustion?.senderId) ?? undefined,
1808
1744
  });
1809
1745
  if (nextSelection.status === "complete") {
1810
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.642",
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",