@sellable/mcp 0.1.643 → 0.1.645-wip.refill-zero-shot.20260721153930
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 +133 -4
- package/dist/tools/refill-sends.js +79 -185
- 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;
|
|
@@ -1722,6 +1722,135 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
|
|
|
1722
1722
|
workspaceId: string | null;
|
|
1723
1723
|
workspaceResolution: string;
|
|
1724
1724
|
intent: RefillSendsIntent;
|
|
1725
|
+
} | {
|
|
1726
|
+
ok: boolean;
|
|
1727
|
+
status: "blocked";
|
|
1728
|
+
code: string;
|
|
1729
|
+
blocker: string;
|
|
1730
|
+
note: string;
|
|
1731
|
+
targetPlan: unknown;
|
|
1732
|
+
exactTargetPlan: unknown;
|
|
1733
|
+
coordinator: {
|
|
1734
|
+
scope: string;
|
|
1735
|
+
status: string;
|
|
1736
|
+
completedTarget: WorkspaceExactTarget | null;
|
|
1737
|
+
selectedTarget: WorkspaceExactTarget;
|
|
1738
|
+
exactRunResult: {
|
|
1739
|
+
runSnapshot: unknown;
|
|
1740
|
+
readOnly: boolean;
|
|
1741
|
+
forbiddenActions: string[];
|
|
1742
|
+
boundedAuthority: string;
|
|
1743
|
+
status: "terminal";
|
|
1744
|
+
doneReason: string;
|
|
1745
|
+
report: Record<string, unknown>;
|
|
1746
|
+
runId: string;
|
|
1747
|
+
guidance?: string;
|
|
1748
|
+
journalPath?: string | null;
|
|
1749
|
+
targetConfig?: RefillTargetConfigV1;
|
|
1750
|
+
runHandle?: RefillRunHandleV1;
|
|
1751
|
+
refillReporting?: Record<string, unknown>;
|
|
1752
|
+
blocker?: undefined;
|
|
1753
|
+
conflicts?: undefined;
|
|
1754
|
+
};
|
|
1755
|
+
workspaceTargetPlan?: undefined;
|
|
1756
|
+
exactTargetPlan?: undefined;
|
|
1757
|
+
note?: undefined;
|
|
1758
|
+
};
|
|
1759
|
+
continuation: {
|
|
1760
|
+
required: boolean;
|
|
1761
|
+
userActionRequired: boolean;
|
|
1762
|
+
kind?: undefined;
|
|
1763
|
+
owner?: undefined;
|
|
1764
|
+
tool?: undefined;
|
|
1765
|
+
arguments?: undefined;
|
|
1766
|
+
stopCondition?: undefined;
|
|
1767
|
+
};
|
|
1768
|
+
targetDate: string | null;
|
|
1769
|
+
untilDate: string | null;
|
|
1770
|
+
horizonSendDays: number | null;
|
|
1771
|
+
fillWindow: {
|
|
1772
|
+
mode: string;
|
|
1773
|
+
targetDate: string;
|
|
1774
|
+
description: string;
|
|
1775
|
+
untilDate?: undefined;
|
|
1776
|
+
horizonSendDays?: undefined;
|
|
1777
|
+
horizonHours?: undefined;
|
|
1778
|
+
} | {
|
|
1779
|
+
mode: string;
|
|
1780
|
+
untilDate: string;
|
|
1781
|
+
description: string;
|
|
1782
|
+
targetDate?: undefined;
|
|
1783
|
+
horizonSendDays?: undefined;
|
|
1784
|
+
horizonHours?: undefined;
|
|
1785
|
+
} | {
|
|
1786
|
+
mode: string;
|
|
1787
|
+
horizonSendDays: number | null;
|
|
1788
|
+
horizonHours: number | null;
|
|
1789
|
+
description: string;
|
|
1790
|
+
targetDate?: undefined;
|
|
1791
|
+
untilDate?: undefined;
|
|
1792
|
+
};
|
|
1793
|
+
approvalMode: RefillSendsApprovalMode;
|
|
1794
|
+
campaignId: string | null;
|
|
1795
|
+
tableId: string | null;
|
|
1796
|
+
targetConfig: RefillTargetConfigV1 | null;
|
|
1797
|
+
runHandle: RefillRunHandleV1 | null;
|
|
1798
|
+
reportingContext: RefillReportingContextV2 | null;
|
|
1799
|
+
messageTemplateRevision: MessageTemplateRevisionV1 | null;
|
|
1800
|
+
senderScope: string;
|
|
1801
|
+
senderSelectors: {
|
|
1802
|
+
senders: string[];
|
|
1803
|
+
senderIds: string[];
|
|
1804
|
+
senderNames: string[];
|
|
1805
|
+
};
|
|
1806
|
+
firstOperationalSteps: string[];
|
|
1807
|
+
approvalContract: string;
|
|
1808
|
+
forbiddenActions: string[];
|
|
1809
|
+
hostExamples: {
|
|
1810
|
+
claude: string[];
|
|
1811
|
+
codex: string[];
|
|
1812
|
+
mcpTool: {
|
|
1813
|
+
name: string;
|
|
1814
|
+
arguments: {
|
|
1815
|
+
workspaceCoordinator?: boolean | undefined;
|
|
1816
|
+
messageTemplateRevision?: MessageTemplateRevisionV1 | undefined;
|
|
1817
|
+
reportingContext?: RefillReportingContextV2 | undefined;
|
|
1818
|
+
targetConfig?: RefillTargetConfigV1 | undefined;
|
|
1819
|
+
runHandle?: RefillRunHandleV1 | undefined;
|
|
1820
|
+
fence?: number | undefined;
|
|
1821
|
+
runId?: string | undefined;
|
|
1822
|
+
expectedActionKey?: string | undefined;
|
|
1823
|
+
expectedTargetShapeRevision?: string | undefined;
|
|
1824
|
+
targetDate: string | null;
|
|
1825
|
+
untilDate: string | null;
|
|
1826
|
+
horizonSendDays: number | null;
|
|
1827
|
+
campaignId: string | undefined;
|
|
1828
|
+
tableId: string | undefined;
|
|
1829
|
+
intent: RefillSendsIntent;
|
|
1830
|
+
approvalMode: RefillSendsApprovalMode;
|
|
1831
|
+
workspaceId: string | null;
|
|
1832
|
+
actionTypes?: ("send_invite" | "send_inmail_closed")[] | undefined;
|
|
1833
|
+
yolo: boolean;
|
|
1834
|
+
senders: string[];
|
|
1835
|
+
senderIds: string[];
|
|
1836
|
+
senderNames: string[];
|
|
1837
|
+
};
|
|
1838
|
+
};
|
|
1839
|
+
};
|
|
1840
|
+
actionTypes?: ("send_invite" | "send_inmail_closed")[] | undefined;
|
|
1841
|
+
command: string;
|
|
1842
|
+
tool: string;
|
|
1843
|
+
promptName: string;
|
|
1844
|
+
workflowPromptName: string;
|
|
1845
|
+
workflowAsset: {
|
|
1846
|
+
subskillName: string;
|
|
1847
|
+
assetPath: string;
|
|
1848
|
+
};
|
|
1849
|
+
yolo: boolean;
|
|
1850
|
+
executionMode: RefillSendsExecutionMode;
|
|
1851
|
+
workspaceId: string | null;
|
|
1852
|
+
workspaceResolution: string;
|
|
1853
|
+
intent: RefillSendsIntent;
|
|
1725
1854
|
} | {
|
|
1726
1855
|
status: "in_progress";
|
|
1727
1856
|
targetPlan: unknown;
|
|
@@ -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",
|
|
@@ -133,7 +138,7 @@ function normalizeRefillSendsInput(input) {
|
|
|
133
138
|
export const refillSendsToolDefinitions = [
|
|
134
139
|
{
|
|
135
140
|
name: "refill_sends",
|
|
136
|
-
description: "Typed command entrypoint for Sellable refill sends. Workspace-wide --yolo reads globalActionQueue[0], preflights its exact campaign/table/sender/lane targetConfig, and executes one fenced checkpointed run slice before rereading workspace scope for the next exact target. An active_exact_run continuation carries targetConfig plus exact runId/fence; a next_exact_target continuation carries the next preflighted targetConfig and opens a new fence. When an exact run terminalizes as loaded_awaiting_scheduler and the fresh workspace queue reselects the same campaign/table/sender/lane, the coordinator returns that scheduler-owned workspace terminal instead of opening another identical fence. The packaged workflow must immediately invoke the returned continuation arguments until workspace projected coverage fills, the same-target scheduler terminal is returned, or a concrete blocker appears, without asking the user to continue. Every primitive records actionKey/requestId/effectId before mutation and rereads the fresh target plan afterward. It may refresh stale paid InMail facts once, prepare/approve bounded existing rows, reuse/import receipt-proven same-source rows, add more leads to an existing campaign from its SAME provider source once that campaign's eligible/prepared rows are receipt-proven exhausted (cold-outbound campaigns may source Sales Nav or Prospeo), start a route-resolver start-eligible PAUSED campaign-backed sequence campaign selected by the packet when starting is needed to fill the send window, run a request-scoped product scheduler sweep, and wait/reread. Each source add and campaign start is one bounded primitive, then a full replan. It never sends directly, lowers paid InMail thresholds/floors, creates NEW campaigns, archives/deletes, or switches an existing campaign's source family (same provider source only). Unselected PAUSED campaigns that are not start-eligible remain paused. Scheduler placement stays product-native; the command never raw-writes scheduler fields.",
|
|
141
|
+
description: "Typed command entrypoint for Sellable refill sends. Workspace-wide --yolo reads globalActionQueue[0], preflights its exact campaign/table/sender/lane targetConfig, and executes one fenced checkpointed run slice before rereading workspace scope for the next exact target. An active_exact_run continuation carries targetConfig plus exact runId/fence; a next_exact_target continuation carries the next preflighted targetConfig and opens a new fence. Exact target configs bind the issued semantic action and revisions. A terminal exact run may advance only when a fresh exact preflight selects a different semantic action; two fresh reads of the same terminal action fail closed instead of opening another identical fence. When an exact run terminalizes as loaded_awaiting_scheduler and the fresh workspace queue reselects the same campaign/table/sender/lane, the coordinator returns that scheduler-owned workspace terminal instead of opening another identical fence. The packaged workflow must immediately invoke the returned continuation arguments until workspace projected coverage fills, the same-target scheduler terminal is returned, or a concrete blocker appears, without asking the user to continue. Every primitive records actionKey/requestId/effectId before mutation and rereads the fresh target plan afterward. It may refresh stale paid InMail facts once, prepare/approve bounded existing rows, reuse/import receipt-proven same-source rows, add more leads to an existing campaign from its SAME provider source once that campaign's eligible/prepared rows are receipt-proven exhausted (cold-outbound campaigns may source Sales Nav or Prospeo), start a route-resolver start-eligible PAUSED campaign-backed sequence campaign selected by the packet when starting is needed to fill the send window, run a request-scoped product scheduler sweep, and wait/reread. Each source add and campaign start is one bounded primitive, then a full replan. It never sends directly, lowers paid InMail thresholds/floors, creates NEW campaigns, archives/deletes, or switches an existing campaign's source family (same provider source only). Unselected PAUSED campaigns that are not start-eligible remain paused. Scheduler placement stays product-native; the command never raw-writes scheduler fields.",
|
|
137
142
|
inputSchema: {
|
|
138
143
|
type: "object",
|
|
139
144
|
properties: {
|
|
@@ -507,6 +512,11 @@ function sameWorkspaceExactTarget(left, right) {
|
|
|
507
512
|
sameStringSet(left.senderIds, right.senderIds) &&
|
|
508
513
|
sameStringSet(left.actionTypes, right.actionTypes));
|
|
509
514
|
}
|
|
515
|
+
function sameWorkspaceExactAction(left, right) {
|
|
516
|
+
return (sameWorkspaceExactTarget(left, right) &&
|
|
517
|
+
Boolean(left?.actionKey) &&
|
|
518
|
+
left?.actionKey === right.actionKey);
|
|
519
|
+
}
|
|
510
520
|
function workspaceCoordinatorInput(input) {
|
|
511
521
|
const coordinatorInput = { ...input };
|
|
512
522
|
delete coordinatorInput.campaignId;
|
|
@@ -520,118 +530,37 @@ function workspaceCoordinatorInput(input) {
|
|
|
520
530
|
delete coordinatorInput.agentKeywords;
|
|
521
531
|
return coordinatorInput;
|
|
522
532
|
}
|
|
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
|
-
}
|
|
533
|
+
function workspaceExactTargetsFromAction(action) {
|
|
534
|
+
if (!Array.isArray(action.exactTargets) || action.exactTargets.length === 0) {
|
|
535
|
+
return [];
|
|
566
536
|
}
|
|
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)
|
|
537
|
+
const targets = action.exactTargets.flatMap((value) => {
|
|
538
|
+
const exactTarget = recordValue(value);
|
|
539
|
+
const campaignId = stringValue(exactTarget?.campaignId);
|
|
540
|
+
const tableId = stringValue(exactTarget?.tableId);
|
|
541
|
+
const senderIds = normalizeStrings(exactTarget?.senderIds);
|
|
542
|
+
const refillLaneKey = stringValue(exactTarget?.refillLaneKey);
|
|
543
|
+
const branchActionTypes = normalizeStrings(exactTarget?.branchActionTypes);
|
|
544
|
+
if (!campaignId ||
|
|
545
|
+
!tableId ||
|
|
546
|
+
senderIds.length === 0 ||
|
|
547
|
+
!refillLaneKey ||
|
|
548
|
+
!REFILL_SEND_ACTION_TYPE_SET.has(refillLaneKey) ||
|
|
549
|
+
branchActionTypes.length === 0 ||
|
|
550
|
+
branchActionTypes.some((actionType) => !WORKSPACE_EXACT_BRANCH_ACTION_TYPE_SET.has(actionType))) {
|
|
594
551
|
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));
|
|
552
|
+
}
|
|
625
553
|
return [
|
|
626
554
|
{
|
|
627
555
|
campaignId,
|
|
628
556
|
tableId,
|
|
629
557
|
senderIds,
|
|
630
|
-
actionTypes,
|
|
558
|
+
actionTypes: [refillLaneKey],
|
|
631
559
|
actionKey: stringValue(action.actionKey) ?? null,
|
|
632
560
|
},
|
|
633
561
|
];
|
|
634
562
|
});
|
|
563
|
+
return targets.length === action.exactTargets.length ? targets : [];
|
|
635
564
|
}
|
|
636
565
|
function workspaceExactTargetKey(target) {
|
|
637
566
|
return JSON.stringify([
|
|
@@ -641,55 +570,6 @@ function workspaceExactTargetKey(target) {
|
|
|
641
570
|
[...new Set(target.actionTypes)].sort(),
|
|
642
571
|
]);
|
|
643
572
|
}
|
|
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
573
|
async function selectWorkspaceExactTarget(input, workspacePlanOverride, options = {}) {
|
|
694
574
|
const workspaceInput = workspaceCoordinatorInput(input);
|
|
695
575
|
let workspacePlan = workspacePlanOverride ??
|
|
@@ -724,41 +604,18 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
|
|
|
724
604
|
.filter((action) => Boolean(action))
|
|
725
605
|
: [];
|
|
726
606
|
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) {
|
|
607
|
+
const queueHead = globalActions[0] ?? rankedCandidates[0];
|
|
608
|
+
if (queueHead && workspaceExactTargetsFromAction(queueHead).length === 0) {
|
|
747
609
|
return {
|
|
748
610
|
status: "blocked",
|
|
749
|
-
code: "
|
|
750
|
-
note: "The workspace planner
|
|
611
|
+
code: "workspace_exact_target_contract_missing",
|
|
612
|
+
note: "The workspace planner's current action did not include a complete exactTargets contract. No refill run or mutation was started.",
|
|
751
613
|
workspacePlan,
|
|
752
614
|
};
|
|
753
615
|
}
|
|
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));
|
|
616
|
+
const actionTargets = workspaceActions.flatMap((action) => workspaceExactTargetsFromAction(action));
|
|
760
617
|
const seenTargetKeys = new Set();
|
|
761
|
-
const targetCandidates =
|
|
618
|
+
const targetCandidates = actionTargets.filter((candidate) => {
|
|
762
619
|
const key = workspaceExactTargetKey(candidate);
|
|
763
620
|
if (seenTargetKeys.has(key))
|
|
764
621
|
return false;
|
|
@@ -844,7 +701,13 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
|
|
|
844
701
|
status: "selected",
|
|
845
702
|
workspacePlan,
|
|
846
703
|
exactTargetPlan,
|
|
847
|
-
selectedTarget
|
|
704
|
+
selectedTarget: {
|
|
705
|
+
...selectedTarget,
|
|
706
|
+
// The workspace action chooses the structural target. The exact
|
|
707
|
+
// preflight owns the semantic action that the new fence is allowed to
|
|
708
|
+
// execute, so convergence comparisons must use its immutable binding.
|
|
709
|
+
actionKey: targetConfig.approvalInputs.expectedActionKey,
|
|
710
|
+
},
|
|
848
711
|
targetConfig,
|
|
849
712
|
};
|
|
850
713
|
}
|
|
@@ -1878,7 +1741,6 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
1878
1741
|
if (isWorkspaceCoordinator && result.status === "terminal") {
|
|
1879
1742
|
const resultReport = recordValue(result.report) ?? {};
|
|
1880
1743
|
const coordinatorDirective = recordValue(resultReport.coordinatorDirective);
|
|
1881
|
-
const laneExhaustion = recordValue(resultReport.laneExhaustion);
|
|
1882
1744
|
const excludedTargets = Array.isArray(coordinatorDirective?.excludeTargets)
|
|
1883
1745
|
? coordinatorDirective.excludeTargets
|
|
1884
1746
|
.map(recordValue)
|
|
@@ -1888,10 +1750,19 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
1888
1750
|
}))
|
|
1889
1751
|
.filter((target) => Boolean(target.campaignId && target.tableId))
|
|
1890
1752
|
: [];
|
|
1891
|
-
|
|
1753
|
+
let nextSelection = await selectWorkspaceExactTarget(scopedInput, undefined, {
|
|
1892
1754
|
excludeTargets: excludedTargets,
|
|
1893
|
-
preferredSenderId: stringValue(laneExhaustion?.senderId) ?? undefined,
|
|
1894
1755
|
});
|
|
1756
|
+
if (result.doneReason !== "loaded_awaiting_scheduler" &&
|
|
1757
|
+
nextSelection.status === "selected" &&
|
|
1758
|
+
sameWorkspaceExactAction(selectedCoordinatorTarget, nextSelection.selectedTarget)) {
|
|
1759
|
+
// Avoid treating a single eventually-consistent reread as a blocker,
|
|
1760
|
+
// but never create a new fence for the same action after it already
|
|
1761
|
+
// terminalized. The second read is still mutation-free.
|
|
1762
|
+
nextSelection = await selectWorkspaceExactTarget(scopedInput, undefined, {
|
|
1763
|
+
excludeTargets: excludedTargets,
|
|
1764
|
+
});
|
|
1765
|
+
}
|
|
1895
1766
|
if (nextSelection.status === "complete") {
|
|
1896
1767
|
return {
|
|
1897
1768
|
...command,
|
|
@@ -2019,6 +1890,29 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
2019
1890
|
},
|
|
2020
1891
|
};
|
|
2021
1892
|
}
|
|
1893
|
+
if (sameWorkspaceExactAction(selectedCoordinatorTarget, nextSelection.selectedTarget)) {
|
|
1894
|
+
return {
|
|
1895
|
+
...command,
|
|
1896
|
+
ok: false,
|
|
1897
|
+
status: "blocked",
|
|
1898
|
+
code: "exact_target_terminal_no_progress",
|
|
1899
|
+
blocker: "exact_target_terminal_no_progress",
|
|
1900
|
+
note: "The exact run terminalized, but two fresh preflights selected the same campaign/table/sender/lane action. No identical fence was reopened.",
|
|
1901
|
+
targetPlan: nextSelection.workspacePlan,
|
|
1902
|
+
exactTargetPlan: nextSelection.exactTargetPlan,
|
|
1903
|
+
coordinator: {
|
|
1904
|
+
scope: "workspace",
|
|
1905
|
+
status: "blocked",
|
|
1906
|
+
completedTarget: selectedCoordinatorTarget,
|
|
1907
|
+
selectedTarget: nextSelection.selectedTarget,
|
|
1908
|
+
exactRunResult: result,
|
|
1909
|
+
},
|
|
1910
|
+
continuation: {
|
|
1911
|
+
required: false,
|
|
1912
|
+
userActionRequired: false,
|
|
1913
|
+
},
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
2022
1916
|
return {
|
|
2023
1917
|
...command,
|
|
2024
1918
|
status: "in_progress",
|
|
@@ -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),
|
package/package.json
CHANGED