@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.
- package/dist/tools/refill-sends.d.ts +4 -4
- package/dist/tools/refill-sends.js +32 -182
- package/dist/tools/refill-target-plan.js +138 -12
- package/package.json +1 -1
|
@@ -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" | "
|
|
969
|
-
blocker: "workspace_exact_target_unavailable" | "
|
|
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" | "
|
|
1458
|
-
blocker: "workspace_exact_target_unavailable" | "
|
|
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,
|
|
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
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
-
|
|
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
|
|
728
|
-
|
|
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: "
|
|
750
|
-
note: "The workspace planner
|
|
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
|
|
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 =
|
|
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
|
|
412
|
-
campaignId
|
|
438
|
+
actionType,
|
|
439
|
+
campaignId,
|
|
413
440
|
campaignName: stringValue(candidate.campaignName),
|
|
414
|
-
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
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
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),
|