@sellable/mcp 0.1.793 → 0.1.794
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/agent-tool-names.d.ts +2 -2
- package/dist/agent-tool-names.js +14 -0
- package/dist/scheduler-envelope.d.ts +190 -0
- package/dist/scheduler-envelope.js +412 -0
- package/dist/server.js +28 -2
- package/dist/tools/campaign-message-preparation.d.ts +25 -0
- package/dist/tools/campaign-message-preparation.js +37 -0
- package/dist/tools/campaign-processing.d.ts +74 -1
- package/dist/tools/campaign-processing.js +57 -1
- package/dist/tools/prompts.d.ts +15 -3
- package/dist/tools/prompts.js +25 -0
- package/dist/tools/refill-executors.d.ts +43 -70
- package/dist/tools/refill-executors.js +201 -273
- package/dist/tools/refill-sends.d.ts +3 -3
- package/dist/tools/refill-sends.js +1 -1
- package/dist/tools/refill-target-plan.js +1 -1
- package/dist/tools/refill-v3-advance-contract.d.ts +618 -0
- package/dist/tools/refill-v3-advance-contract.js +920 -0
- package/dist/tools/refill-v3-advance.d.ts +397 -0
- package/dist/tools/refill-v3-advance.js +75 -0
- package/dist/tools/refill-v3-attention-packet-contract.d.ts +608 -0
- package/dist/tools/refill-v3-attention-packet-contract.js +911 -0
- package/dist/tools/refill-v3-campaign-work.d.ts +324 -0
- package/dist/tools/refill-v3-campaign-work.js +1015 -0
- package/dist/tools/refill-v3-continue-contract.d.ts +433 -0
- package/dist/tools/refill-v3-continue-contract.js +112 -0
- package/dist/tools/refill-v3-continue.d.ts +501 -0
- package/dist/tools/refill-v3-continue.js +654 -0
- package/dist/tools/refill-v3-edit-selected-campaign-contract.d.ts +208 -0
- package/dist/tools/refill-v3-edit-selected-campaign-contract.js +351 -0
- package/dist/tools/refill-v3-edit-selected-campaign.d.ts +232 -0
- package/dist/tools/refill-v3-edit-selected-campaign.js +147 -0
- package/dist/tools/refill-v3-fill-ready-contract.d.ts +198 -0
- package/dist/tools/refill-v3-fill-ready-contract.js +324 -0
- package/dist/tools/refill-v3-fill-ready.d.ts +199 -0
- package/dist/tools/refill-v3-fill-ready.js +136 -0
- package/dist/tools/refill-v3-source-family-dispatch.d.ts +249 -0
- package/dist/tools/refill-v3-source-family-dispatch.js +514 -0
- package/dist/tools/refill-v3-waterfall-contract.d.ts +156 -0
- package/dist/tools/refill-v3-waterfall-contract.js +181 -0
- package/dist/tools/refill-v3-waterfall.d.ts +136 -0
- package/dist/tools/refill-v3-waterfall.js +161 -0
- package/dist/tools/refill-v3-world-state-contract.d.ts +666 -0
- package/dist/tools/refill-v3-world-state-contract.js +790 -0
- package/dist/tools/refill-v3-world-state.d.ts +592 -0
- package/dist/tools/refill-v3-world-state.js +137 -0
- package/dist/tools/registry.d.ts +1710 -3
- package/dist/tools/registry.js +19 -0
- package/package.json +1 -1
- package/skills/refill-sends/SKILL.md +89 -4
- package/skills/refill-sends-waterfall-order/SKILL.md +174 -0
- package/skills/refill-sends-work-campaign/SKILL.md +253 -0
- package/skills/refill-sends-workflow/SKILL.md +112 -5
- package/skills/refill-sends-workflow/core/flow.v3.json +209 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { getApi, SellableApiError } from "../api.js";
|
|
3
3
|
import { decodeRefillReadinessAuthority, decodeRefillSchedulerDateGuards, refillReadinessExpectationInput, refillReadinessFingerprint, } from "../refill-readiness.js";
|
|
4
|
+
import { hasTerminalSchedulerReceipt, normalizeSchedulerChangedCounts, normalizeSchedulerPrimitiveResult, numberValue, recordValue, SCHEDULER_ATTACHING_ENVELOPE_STATUSES, schedulerEnvelopeIsTerminal, stringArray, stringValue, } from "../scheduler-envelope.js";
|
|
4
5
|
import { startPrepareCampaignMessages } from "./campaign-message-preparation.js";
|
|
5
6
|
import { normalizeMessageTemplateRevision, normalizeRefillRunHandle, normalizeRefillTargetConfig, reviseMessageTemplateAndRerun, } from "./campaign-processing.js";
|
|
6
7
|
import { startCampaign } from "./campaigns.js";
|
|
7
8
|
import { confirmLeadList, importLeads, searchProspeo, searchSalesNav, searchSignals, selectPromisingPosts, } from "./leads.js";
|
|
8
|
-
import { markProviderPromptLoaded } from "./provider-preflight.js";
|
|
9
9
|
import { refreshSenderEngagementCommand } from "./refresh-sender-engagement.js";
|
|
10
10
|
import { runSchedulerSweep, } from "./scheduler-run.js";
|
|
11
11
|
import { refreshPaidInmailCredits } from "./senders.js";
|
|
@@ -51,6 +51,80 @@ const USABLE_PAID_INMAIL_REFRESH_RECEIPT_STATUSES = new Set([
|
|
|
51
51
|
"refreshed",
|
|
52
52
|
"below_threshold",
|
|
53
53
|
]);
|
|
54
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
55
|
+
// Refill V3 source-dispatch seam (Phase 146.5 Plan 09 T3, RV3PRM-002).
|
|
56
|
+
//
|
|
57
|
+
// T3 is the SOLE STRUCTURAL owner of this seam (`A09-EXECUTORS-SERIALIZATION`):
|
|
58
|
+
// it introduces the receipt-shape extension and the dispatcher call seam, and
|
|
59
|
+
// T4/T5 make only additive table-entry and characterization changes. There is
|
|
60
|
+
// EXACTLY ONE receipt-normalization path for source dispatch, because a second
|
|
61
|
+
// one would let two families' receipts drift apart silently (error law 2).
|
|
62
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
63
|
+
/**
|
|
64
|
+
* Normalize ONE existing family owner's `YoloPrimitiveAttempt` into the closed
|
|
65
|
+
* dispatch outcome. Missing or uncertain effect evidence is NEVER success:
|
|
66
|
+
* `no_action` is a bounded zero-yield success only when its receipt carries
|
|
67
|
+
* before/after evidence, and a refusal or an absent receipt is a typed failure.
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* The V3 source path is SINGLE-PASS by construction.
|
|
71
|
+
*
|
|
72
|
+
* V1 keeps its two-attempt signal helper and its depth-scaled provider target;
|
|
73
|
+
* V3 removes both. `searchSignalsWithProviderRetry` hides TWO transport attempts
|
|
74
|
+
* inside one "bounded" owner call, and `priorSearchDepth` is retry/expansion
|
|
75
|
+
* MEMORY that multiplies the target the AI never asked for. Under V3 a provider
|
|
76
|
+
* outcome is a typed no-effect receipt followed by a reread, and only a later
|
|
77
|
+
* fresh attention iteration may choose another action.
|
|
78
|
+
*/
|
|
79
|
+
export const REFILL_V3_SIGNAL_SEARCH_ATTEMPTS = 1;
|
|
80
|
+
export function isRefillV3SinglePassAction(action) {
|
|
81
|
+
return actionToolInput(action).refillV3SinglePass === true;
|
|
82
|
+
}
|
|
83
|
+
export function normalizeRefillV3SourceOwnerReceipt(params) {
|
|
84
|
+
const { attempt, ownerKey, requestFingerprint, idempotencyKey } = params;
|
|
85
|
+
if (attempt.status === "refused") {
|
|
86
|
+
return {
|
|
87
|
+
kind: "failure",
|
|
88
|
+
failureClass: "owner_threw",
|
|
89
|
+
detail: attempt.refusalReason ?? `${ownerKey} refused without a reason`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const record = recordValue(attempt.result) ?? {};
|
|
93
|
+
const before = stringValue(record.sourceBeforeFingerprint);
|
|
94
|
+
const after = stringValue(record.sourceAfterFingerprint);
|
|
95
|
+
if (!before || !after) {
|
|
96
|
+
return { kind: "timeout", effectIdentity: null };
|
|
97
|
+
}
|
|
98
|
+
const importedCount = Math.max(0, numberValue(record.importedCount) ?? 0);
|
|
99
|
+
const receipt = {
|
|
100
|
+
ownerKey,
|
|
101
|
+
requestFingerprint,
|
|
102
|
+
idempotencyKey,
|
|
103
|
+
sourceBeforeFingerprint: before,
|
|
104
|
+
sourceAfterFingerprint: after,
|
|
105
|
+
sourceBeforeCount: Math.max(0, numberValue(record.sourceBeforeCount) ?? 0),
|
|
106
|
+
sourceAfterCount: Math.max(0, numberValue(record.sourceAfterCount) ?? 0),
|
|
107
|
+
importedCount,
|
|
108
|
+
effects: stringArray(record.effects),
|
|
109
|
+
replayState: stringValue(record.replayState) === "exact_replay"
|
|
110
|
+
? "exact_replay"
|
|
111
|
+
: importedCount > 0
|
|
112
|
+
? "first"
|
|
113
|
+
: "not_applicable",
|
|
114
|
+
rollback: importedCount > 0
|
|
115
|
+
? {
|
|
116
|
+
available: true,
|
|
117
|
+
input: recordValue(record.rollbackInput) ?? {},
|
|
118
|
+
result: stringValue(record.rollbackResult),
|
|
119
|
+
}
|
|
120
|
+
: { available: false, reason: "not_applicable" },
|
|
121
|
+
postRationales: null,
|
|
122
|
+
yieldEvidence: null,
|
|
123
|
+
};
|
|
124
|
+
return importedCount > 0
|
|
125
|
+
? { kind: "receipt", receipt }
|
|
126
|
+
: { kind: "no_effect", receipt };
|
|
127
|
+
}
|
|
54
128
|
function isSenderOwnedPostEngagersCampaign(campaign) {
|
|
55
129
|
return Boolean((campaign.leadSourceProvider === "signal-discovery" ||
|
|
56
130
|
campaign.leadSourceProvider === "campaign-tracked-post") &&
|
|
@@ -148,152 +222,6 @@ export function sleep(ms) {
|
|
|
148
222
|
return Promise.resolve();
|
|
149
223
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
150
224
|
}
|
|
151
|
-
export function recordValue(value) {
|
|
152
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
153
|
-
? value
|
|
154
|
-
: null;
|
|
155
|
-
}
|
|
156
|
-
export function stringValue(value) {
|
|
157
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
158
|
-
}
|
|
159
|
-
export function numberValue(value) {
|
|
160
|
-
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
161
|
-
}
|
|
162
|
-
export function normalizeSchedulerChangedCounts(value) {
|
|
163
|
-
const changedCounts = recordValue(value);
|
|
164
|
-
if (!changedCounts ||
|
|
165
|
-
changedCounts.complete !== true ||
|
|
166
|
-
changedCounts.truncated !== false) {
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
const total = numberValue(changedCounts.total);
|
|
170
|
-
if (total === null || total < 0 || !Number.isInteger(total))
|
|
171
|
-
return null;
|
|
172
|
-
if (!Array.isArray(changedCounts.byCampaignTableActionType))
|
|
173
|
-
return null;
|
|
174
|
-
const groups = [];
|
|
175
|
-
const seen = new Set();
|
|
176
|
-
for (const rawGroup of changedCounts.byCampaignTableActionType) {
|
|
177
|
-
const group = recordValue(rawGroup);
|
|
178
|
-
const campaignId = stringValue(group?.campaignId);
|
|
179
|
-
const tableId = stringValue(group?.tableId);
|
|
180
|
-
const actionType = stringValue(group?.actionType);
|
|
181
|
-
const count = numberValue(group?.count);
|
|
182
|
-
if (!campaignId ||
|
|
183
|
-
!tableId ||
|
|
184
|
-
!actionType ||
|
|
185
|
-
count === null ||
|
|
186
|
-
count <= 0 ||
|
|
187
|
-
!Number.isInteger(count)) {
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
const key = `${campaignId}:${tableId}:${actionType}`;
|
|
191
|
-
if (seen.has(key))
|
|
192
|
-
return null;
|
|
193
|
-
seen.add(key);
|
|
194
|
-
groups.push({ campaignId, tableId, actionType, count });
|
|
195
|
-
}
|
|
196
|
-
groups.sort((a, b) => {
|
|
197
|
-
if (a.campaignId !== b.campaignId) {
|
|
198
|
-
return a.campaignId.localeCompare(b.campaignId);
|
|
199
|
-
}
|
|
200
|
-
if (a.tableId !== b.tableId)
|
|
201
|
-
return a.tableId.localeCompare(b.tableId);
|
|
202
|
-
return a.actionType.localeCompare(b.actionType);
|
|
203
|
-
});
|
|
204
|
-
if (groups.reduce((sum, group) => sum + group.count, 0) !== total) {
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
if (!Array.isArray(changedCounts.byCampaignTableActionTypeSender)) {
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
const senderGroups = [];
|
|
211
|
-
const senderSeen = new Set();
|
|
212
|
-
for (const rawGroup of changedCounts.byCampaignTableActionTypeSender) {
|
|
213
|
-
const group = recordValue(rawGroup);
|
|
214
|
-
const campaignId = stringValue(group?.campaignId);
|
|
215
|
-
const tableId = stringValue(group?.tableId);
|
|
216
|
-
const actionType = stringValue(group?.actionType);
|
|
217
|
-
const senderId = stringValue(group?.senderId);
|
|
218
|
-
const count = numberValue(group?.count);
|
|
219
|
-
if (!campaignId ||
|
|
220
|
-
!tableId ||
|
|
221
|
-
!actionType ||
|
|
222
|
-
!senderId ||
|
|
223
|
-
count === null ||
|
|
224
|
-
count <= 0 ||
|
|
225
|
-
!Number.isInteger(count)) {
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
const key = `${campaignId}:${tableId}:${actionType}:${senderId}`;
|
|
229
|
-
if (senderSeen.has(key))
|
|
230
|
-
return null;
|
|
231
|
-
senderSeen.add(key);
|
|
232
|
-
senderGroups.push({ campaignId, tableId, actionType, senderId, count });
|
|
233
|
-
}
|
|
234
|
-
if (senderGroups.reduce((sum, group) => sum + group.count, 0) !== total) {
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
return {
|
|
238
|
-
complete: true,
|
|
239
|
-
truncated: false,
|
|
240
|
-
total,
|
|
241
|
-
byCampaignTableActionType: groups,
|
|
242
|
-
byCampaignTableActionTypeSender: senderGroups,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
function completeExpectedTargetSummary(value, expectedTargets, maxPlacements) {
|
|
246
|
-
const summary = recordValue(value);
|
|
247
|
-
const items = Array.isArray(summary?.items) ? summary.items : [];
|
|
248
|
-
if (summary?.complete !== true ||
|
|
249
|
-
summary.truncated !== false ||
|
|
250
|
-
numberValue(summary.requested) !== expectedTargets.length ||
|
|
251
|
-
numberValue(summary.included) == null ||
|
|
252
|
-
numberValue(summary.omitted) == null ||
|
|
253
|
-
numberValue(summary.included) + numberValue(summary.omitted) !==
|
|
254
|
-
expectedTargets.length ||
|
|
255
|
-
numberValue(summary.maxPlacements) !== maxPlacements ||
|
|
256
|
-
items.length !== expectedTargets.length) {
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
259
|
-
return items.every((rawItem, index) => {
|
|
260
|
-
const item = recordValue(rawItem);
|
|
261
|
-
const target = expectedTargets[index];
|
|
262
|
-
const senderIds = stringArray(item?.senderIds).sort();
|
|
263
|
-
const bySender = Array.isArray(item?.bySender) ? item.bySender : [];
|
|
264
|
-
const accounted = bySender
|
|
265
|
-
.map((entry) => stringValue(recordValue(entry)?.senderId))
|
|
266
|
-
.filter((senderId) => Boolean(senderId))
|
|
267
|
-
.sort();
|
|
268
|
-
return (Boolean(target) &&
|
|
269
|
-
item?.campaignId === target?.campaignId &&
|
|
270
|
-
item?.tableId === target?.tableId &&
|
|
271
|
-
item?.refillLaneKey === target?.refillLaneKey &&
|
|
272
|
-
(item?.outcome === "included" || item?.outcome === "omitted") &&
|
|
273
|
-
JSON.stringify(senderIds) === JSON.stringify(target?.senderIds) &&
|
|
274
|
-
JSON.stringify(accounted) === JSON.stringify(target?.senderIds));
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
// fix(112ao-2): the scheduler's ATTACHING envelope statuses. These describe a
|
|
278
|
-
// dispatch that joined an existing run rather than producing its own outcome.
|
|
279
|
-
// They are terminal ONLY when the attach actually yielded a terminal receipt.
|
|
280
|
-
const SCHEDULER_ATTACHING_ENVELOPE_STATUSES = new Set(["accepted", "attached"]);
|
|
281
|
-
// The envelope statuses that report an outcome of this dispatch directly.
|
|
282
|
-
const SCHEDULER_TERMINAL_ENVELOPE_STATUSES = new Set([
|
|
283
|
-
"ran",
|
|
284
|
-
"window_closed_noop",
|
|
285
|
-
]);
|
|
286
|
-
// The only receipt statuses that constitute a settled outcome. A receipt
|
|
287
|
-
// without one of these is not evidence that anything finished.
|
|
288
|
-
const SCHEDULER_TERMINAL_RECEIPT_STATUSES = new Set([
|
|
289
|
-
"ran",
|
|
290
|
-
"window_closed_noop",
|
|
291
|
-
"failed",
|
|
292
|
-
]);
|
|
293
|
-
function hasTerminalSchedulerReceipt(receipt) {
|
|
294
|
-
const status = receipt ? stringValue(receipt.status) : null;
|
|
295
|
-
return Boolean(status && SCHEDULER_TERMINAL_RECEIPT_STATUSES.has(status));
|
|
296
|
-
}
|
|
297
225
|
// fix(112ap): poll ONLY a genuinely in-flight attach. `!terminal` is the wrong
|
|
298
226
|
// test — it is also true for `backoff` and `failed`, and re-polling those would
|
|
299
227
|
// both waste the budget and misrepresent a refused dispatch as a pending one.
|
|
@@ -322,103 +250,12 @@ export function setSweepSettlePollDelayMsForTests(ms) {
|
|
|
322
250
|
export function resetSweepSettlePollDelayMsForTests() {
|
|
323
251
|
sweepSettlePollDelayMs = SWEEP_SETTLE_POLL_DEFAULT_DELAY_MS;
|
|
324
252
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
export function normalizeSchedulerPrimitiveResult(value, expected) {
|
|
332
|
-
const response = recordValue(value);
|
|
333
|
-
const receipt = recordValue(response?.receipt);
|
|
334
|
-
const schedulerStatus = stringValue(response?.status) ?? stringValue(receipt?.status) ?? "unknown";
|
|
335
|
-
const errorCode = stringValue(recordValue(response?.error)?.code);
|
|
336
|
-
// fix(112ao-2): whether this dispatch finished is decided explicitly from the
|
|
337
|
-
// envelope status plus the RECEIPT'S OWN STATUS — never from the bare
|
|
338
|
-
// presence of a receipt field.
|
|
339
|
-
//
|
|
340
|
-
// The previous guard was `!receipt && (accepted || attached)`. Truthiness is
|
|
341
|
-
// the wrong test: an attach that carries a receipt with no terminal status is
|
|
342
|
-
// not evidence that anything settled, yet it skipped this branch, ran the v3
|
|
343
|
-
// audit on that non-evidence, and could return auditComplete: true — a
|
|
344
|
-
// completion claim for a run still in flight, which the coordinator then
|
|
345
|
-
// treats as done rather than awaiting (Dotwork 2026-07-29: 21 exact runs,
|
|
346
|
-
// coverage frozen at sent:0/scheduled:0).
|
|
347
|
-
//
|
|
348
|
-
// The attach is NOT always in flight, and this deliberately does not treat it
|
|
349
|
-
// so: scheduler-run.ts:466-483 only returns a receipt from
|
|
350
|
-
// waitForSchedulerRunReceipt when it is scope-matched to THIS request, so an
|
|
351
|
-
// attach carrying a terminal receipt is a legitimate idempotent completion of
|
|
352
|
-
// this exact operation and must keep its counters. Discarding it would report
|
|
353
|
-
// zero placements for a sweep that really placed cells.
|
|
354
|
-
//
|
|
355
|
-
// `ran`/`window_closed_noop` remain terminal. `backoff`/`failed` keep their
|
|
356
|
-
// existing handling — the run loop already unwraps a backoff envelope's
|
|
357
|
-
// replayed receipt deliberately (schedulerRunReceiptIsFreshZeroScheduled).
|
|
358
|
-
if (SCHEDULER_ATTACHING_ENVELOPE_STATUSES.has(schedulerStatus) &&
|
|
359
|
-
!hasTerminalSchedulerReceipt(receipt)) {
|
|
360
|
-
return {
|
|
361
|
-
status: "matching_scheduler_active",
|
|
362
|
-
schedulerStatus,
|
|
363
|
-
terminal: false,
|
|
364
|
-
disposition: "bounded_reread_wait",
|
|
365
|
-
receipt: null,
|
|
366
|
-
// A non-terminal receipt is not this dispatch's effect. Surfaced under a
|
|
367
|
-
// distinct name so it stays readable for diagnostics but can never be
|
|
368
|
-
// mistaken for a settled outcome.
|
|
369
|
-
...(receipt ? { unsettledReceipt: receipt } : {}),
|
|
370
|
-
retryAfterMs: numberValue(response?.retryAfterMs) ?? 30_000,
|
|
371
|
-
changedCounts: null,
|
|
372
|
-
auditComplete: false,
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
if (errorCode === "scheduler_scope_busy" ||
|
|
376
|
-
errorCode === "uncertain_outcome" ||
|
|
377
|
-
schedulerStatus === "uncertain_outcome") {
|
|
378
|
-
const status = errorCode === "scheduler_scope_busy"
|
|
379
|
-
? "scheduler_scope_busy"
|
|
380
|
-
: "uncertain_outcome";
|
|
381
|
-
return {
|
|
382
|
-
status,
|
|
383
|
-
schedulerStatus,
|
|
384
|
-
disposition: status === "scheduler_scope_busy"
|
|
385
|
-
? "bounded_backoff_wait"
|
|
386
|
-
: "diagnostic_stop",
|
|
387
|
-
receipt: response?.receipt ?? null,
|
|
388
|
-
retryAfterMs: numberValue(response?.retryAfterMs),
|
|
389
|
-
changedCounts: null,
|
|
390
|
-
auditComplete: false,
|
|
391
|
-
blocker: status,
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
const changedCounts = normalizeSchedulerChangedCounts(receipt?.changedCounts ?? response?.changedCounts);
|
|
395
|
-
const targetAuditComplete = !expected ||
|
|
396
|
-
(receipt?.receiptVersion === 3 &&
|
|
397
|
-
receipt.expectedTargetsHash === expected.expectedTargetsHash &&
|
|
398
|
-
receipt.maxPlacements === expected.maxPlacements &&
|
|
399
|
-
completeExpectedTargetSummary(receipt.expectedTargetSummary, expected.expectedTargets, expected.maxPlacements));
|
|
400
|
-
if (!changedCounts || !targetAuditComplete) {
|
|
401
|
-
return {
|
|
402
|
-
status: "scheduler_receipt_incomplete",
|
|
403
|
-
schedulerStatus,
|
|
404
|
-
receipt: response?.receipt ?? null,
|
|
405
|
-
retryAfterMs: numberValue(response?.retryAfterMs),
|
|
406
|
-
changedCounts: null,
|
|
407
|
-
auditComplete: false,
|
|
408
|
-
blocker: "scheduler_receipt_incomplete",
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
return {
|
|
412
|
-
status: schedulerStatus,
|
|
413
|
-
// fix(112ao-2): the completion decision, carried explicitly instead of
|
|
414
|
-
// leaving each consumer to re-infer it from the status string.
|
|
415
|
-
terminal: schedulerEnvelopeIsTerminal(schedulerStatus, receipt),
|
|
416
|
-
receipt: response?.receipt ?? null,
|
|
417
|
-
retryAfterMs: numberValue(response?.retryAfterMs),
|
|
418
|
-
changedCounts,
|
|
419
|
-
auditComplete: true,
|
|
420
|
-
};
|
|
421
|
-
}
|
|
253
|
+
// The scheduler envelope normalizer, the terminality predicate, and the
|
|
254
|
+
// primitive value readers now live in the dependency-free package module
|
|
255
|
+
// `mcp/sellable/src/scheduler-envelope.ts` so the Next app service can import
|
|
256
|
+
// them without pulling this executor graph. They are re-exported here so every
|
|
257
|
+
// existing caller and pin keeps its exact import path.
|
|
258
|
+
export { hasTerminalSchedulerReceipt, normalizeSchedulerChangedCounts, normalizeSchedulerPrimitiveResult, numberValue, recordValue, schedulerEnvelopeIsTerminal, stringArray, stringValue, };
|
|
422
259
|
function exactDateValue(value) {
|
|
423
260
|
const targetDate = stringValue(value);
|
|
424
261
|
if (!targetDate || !/^\d{4}-\d{2}-\d{2}$/.test(targetDate))
|
|
@@ -429,11 +266,6 @@ function exactDateValue(value) {
|
|
|
429
266
|
? targetDate
|
|
430
267
|
: null;
|
|
431
268
|
}
|
|
432
|
-
export function stringArray(value) {
|
|
433
|
-
if (!Array.isArray(value))
|
|
434
|
-
return [];
|
|
435
|
-
return value.filter((item) => typeof item === "string");
|
|
436
|
-
}
|
|
437
269
|
export function prepareRowSelectorValue(value) {
|
|
438
270
|
const selector = recordValue(value);
|
|
439
271
|
const type = stringValue(selector?.type);
|
|
@@ -687,7 +519,9 @@ function rerunErroredCellsOperations(action) {
|
|
|
687
519
|
return operations.flatMap((raw) => {
|
|
688
520
|
const operation = recordValue(raw);
|
|
689
521
|
const columnRole = stringValue(operation?.columnRole);
|
|
690
|
-
if (columnRole !== "
|
|
522
|
+
if (columnRole !== "enrich" &&
|
|
523
|
+
columnRole !== "icpScore" &&
|
|
524
|
+
columnRole !== "generateMessage") {
|
|
691
525
|
return [];
|
|
692
526
|
}
|
|
693
527
|
const rowSelector = recordValue(operation?.rowSelector);
|
|
@@ -792,6 +626,19 @@ function signalSearchProviderErrors(summary) {
|
|
|
792
626
|
: [];
|
|
793
627
|
return uniqueStrings(keywordResults.map((result) => stringValue(result?.error)));
|
|
794
628
|
}
|
|
629
|
+
/**
|
|
630
|
+
* V3-aware single-pass entry. Under V3 `searchSignals` is called AT MOST ONCE
|
|
631
|
+
* per campaign-attention iteration with the exact validated AI keywords; a
|
|
632
|
+
* provider-unavailable result becomes a typed no-effect receipt rather than a
|
|
633
|
+
* hidden second transport attempt.
|
|
634
|
+
*/
|
|
635
|
+
async function searchSignalsBoundedForAction(input, action) {
|
|
636
|
+
if (isRefillV3SinglePassAction(action)) {
|
|
637
|
+
const summary = await searchSignals(input);
|
|
638
|
+
return { summary, attempts: [summary] };
|
|
639
|
+
}
|
|
640
|
+
return searchSignalsWithProviderRetry(input);
|
|
641
|
+
}
|
|
795
642
|
async function searchSignalsWithProviderRetry(input) {
|
|
796
643
|
const attempts = [];
|
|
797
644
|
for (let attempt = 0; attempt < SIGNAL_SEARCH_PROVIDER_MAX_ATTEMPTS; attempt += 1) {
|
|
@@ -1361,12 +1208,13 @@ export async function continueSignalDiscoverySource(action, workspaceId) {
|
|
|
1361
1208
|
},
|
|
1362
1209
|
};
|
|
1363
1210
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1211
|
+
// Phase 146.5 Plan 09 T5: the owner self-mark is REMOVED. Only the existing
|
|
1212
|
+
// `get_provider_prompt` tool may mark the exact `(campaignOfferId, provider)`
|
|
1213
|
+
// preflight; an owner that marks its own preflight satisfies the gate it is
|
|
1214
|
+
// supposed to be gated by, which makes the active-provider-only rule
|
|
1215
|
+
// unenforceable. The assertion side of the gate is unchanged.
|
|
1368
1216
|
const searchAttempt = keywords.length > 0
|
|
1369
|
-
? await
|
|
1217
|
+
? await searchSignalsBoundedForAction({
|
|
1370
1218
|
type: "keywords",
|
|
1371
1219
|
keywords: keywords.map((keyword) => ({
|
|
1372
1220
|
keyword,
|
|
@@ -1379,7 +1227,7 @@ export async function continueSignalDiscoverySource(action, workspaceId) {
|
|
|
1379
1227
|
confirmed: true,
|
|
1380
1228
|
limit: 50,
|
|
1381
1229
|
...(workspaceId ? { workspaceId } : {}),
|
|
1382
|
-
})
|
|
1230
|
+
}, action)
|
|
1383
1231
|
: null;
|
|
1384
1232
|
const searchSummary = searchAttempt?.summary ?? null;
|
|
1385
1233
|
if (searchSummary && signalSearchProviderUnavailable(searchSummary)) {
|
|
@@ -1713,10 +1561,11 @@ export async function broadenSignalSearch(action, workspaceId) {
|
|
|
1713
1561
|
searchKeywords = staleKeywords;
|
|
1714
1562
|
effectiveKeywordSource = "stale_researched";
|
|
1715
1563
|
}
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1564
|
+
// Phase 146.5 Plan 09 T5: the owner self-mark is REMOVED. Only the existing
|
|
1565
|
+
// `get_provider_prompt` tool may mark the exact `(campaignOfferId, provider)`
|
|
1566
|
+
// preflight; an owner that marks its own preflight satisfies the gate it is
|
|
1567
|
+
// supposed to be gated by, which makes the active-provider-only rule
|
|
1568
|
+
// unenforceable. The assertion side of the gate is unchanged.
|
|
1720
1569
|
let searchSummary = null;
|
|
1721
1570
|
let providerSearchAttempts = 0;
|
|
1722
1571
|
let runScopedDerivedTabIds = [];
|
|
@@ -1739,7 +1588,7 @@ export async function broadenSignalSearch(action, workspaceId) {
|
|
|
1739
1588
|
carriedRemainder = freshCarriedPostIds.slice(scrapeCap);
|
|
1740
1589
|
}
|
|
1741
1590
|
else {
|
|
1742
|
-
const searchAttempt = await
|
|
1591
|
+
const searchAttempt = await searchSignalsBoundedForAction({
|
|
1743
1592
|
type: "keywords",
|
|
1744
1593
|
keywords: searchKeywords.map((keyword) => ({
|
|
1745
1594
|
keyword,
|
|
@@ -1756,7 +1605,7 @@ export async function broadenSignalSearch(action, workspaceId) {
|
|
|
1756
1605
|
confirmed: true,
|
|
1757
1606
|
limit: 50,
|
|
1758
1607
|
...(workspaceId ? { workspaceId } : {}),
|
|
1759
|
-
});
|
|
1608
|
+
}, action);
|
|
1760
1609
|
searchSummary = searchAttempt.summary;
|
|
1761
1610
|
providerSearchAttempts = searchAttempt.attempts.length;
|
|
1762
1611
|
// Derived/agent broadening keywords are transient (not the campaign's persistent
|
|
@@ -2077,10 +1926,11 @@ async function startBoundedProviderSearchClone(params) {
|
|
|
2077
1926
|
receipt: { originalSearchId: snapshot.searchId },
|
|
2078
1927
|
};
|
|
2079
1928
|
}
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
1929
|
+
// Phase 146.5 Plan 09 T5: the owner self-mark is REMOVED. Only the existing
|
|
1930
|
+
// `get_provider_prompt` tool may mark the exact `(campaignOfferId, provider)`
|
|
1931
|
+
// preflight; an owner that marks its own preflight satisfies the gate it is
|
|
1932
|
+
// supposed to be gated by, which makes the active-provider-only rule
|
|
1933
|
+
// unenforceable. The assertion side of the gate is unchanged.
|
|
2084
1934
|
const searchName = `${snapshot.searchName ?? "Provider search"} · refill trial`;
|
|
2085
1935
|
const result = params.provider === "sales-nav"
|
|
2086
1936
|
? await searchSalesNav({
|
|
@@ -2451,9 +2301,15 @@ export async function executeProviderSavedSearch(action, workspaceId, mode) {
|
|
|
2451
2301
|
numberValue(toolInput.targetRows) ??
|
|
2452
2302
|
numberValue(action.targetRows) ??
|
|
2453
2303
|
PROVIDER_SOURCE_CONTINUATION_DEFAULT_ROWS));
|
|
2454
|
-
|
|
2304
|
+
// V3 removes `priorSearchDepth` as retry/expansion memory: the target comes
|
|
2305
|
+
// ONLY from the authoritative remaining gap and the provider caps, never from
|
|
2306
|
+
// how many times the campaign has been broadened before.
|
|
2307
|
+
const singlePass = isRefillV3SinglePassAction(action);
|
|
2308
|
+
const priorDepth = singlePass
|
|
2309
|
+
? 1
|
|
2310
|
+
: Math.max(1, Math.floor(numberValue(toolInput.priorSearchDepth) ?? 1));
|
|
2455
2311
|
const targetLeadCount = mode === "broadening"
|
|
2456
|
-
? Math.min(PROVIDER_SEARCH_BROADEN_MAX_ROWS, baseTarget * (priorDepth + 1))
|
|
2312
|
+
? Math.min(PROVIDER_SEARCH_BROADEN_MAX_ROWS, singlePass ? baseTarget : baseTarget * (priorDepth + 1))
|
|
2457
2313
|
: Math.min(PROVIDER_SOURCE_CONTINUATION_MAX_ROWS, baseTarget);
|
|
2458
2314
|
let searchIdForImport = reference.searchId;
|
|
2459
2315
|
let broadenReceipt = null;
|
|
@@ -2511,9 +2367,11 @@ export async function executeProviderSavedSearch(action, workspaceId, mode) {
|
|
|
2511
2367
|
});
|
|
2512
2368
|
}
|
|
2513
2369
|
}
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2370
|
+
// Phase 146.5 Plan 09 T5: the owner self-mark is REMOVED. Only the existing
|
|
2371
|
+
// `get_provider_prompt` tool may mark the exact `(campaignOfferId, provider)`
|
|
2372
|
+
// preflight; an owner that marks its own preflight satisfies the gate it is
|
|
2373
|
+
// supposed to be gated by, which makes the active-provider-only rule
|
|
2374
|
+
// unenforceable. The assertion side of the gate is unchanged.
|
|
2517
2375
|
let importResult;
|
|
2518
2376
|
try {
|
|
2519
2377
|
importResult = await importLeads({
|
|
@@ -2620,8 +2478,8 @@ export async function executeProviderSavedSearch(action, workspaceId, mode) {
|
|
|
2620
2478
|
...(broadenReceipt ?? {}),
|
|
2621
2479
|
targetLeadCount,
|
|
2622
2480
|
importSearchId: searchIdForImport,
|
|
2623
|
-
priorSearchDepth: mode === "broadening" ? priorDepth : null,
|
|
2624
|
-
newSearchDepth: mode === "broadening" ? priorDepth + 1 : null,
|
|
2481
|
+
priorSearchDepth: mode === "broadening" && !singlePass ? priorDepth : null,
|
|
2482
|
+
newSearchDepth: mode === "broadening" && !singlePass ? priorDepth + 1 : null,
|
|
2625
2483
|
netNewRows: rowsGained,
|
|
2626
2484
|
},
|
|
2627
2485
|
};
|
|
@@ -2636,6 +2494,76 @@ export async function continueProviderSource(action, workspaceId) {
|
|
|
2636
2494
|
export async function broadenProviderSearch(action, workspaceId) {
|
|
2637
2495
|
return executeProviderSavedSearch(action, workspaceId, "broadening");
|
|
2638
2496
|
}
|
|
2497
|
+
export function buildRefillV3SourceOwnerAction(request) {
|
|
2498
|
+
const actionKey = `refill-v3-source:${request.scope.idempotencyKey}`;
|
|
2499
|
+
const effectId = `refill-v3-source-effect:${request.scope.idempotencyKey}`;
|
|
2500
|
+
const providerSearchReference = request.scope.providerSearchReference ?? undefined;
|
|
2501
|
+
return {
|
|
2502
|
+
actionKey,
|
|
2503
|
+
effectId,
|
|
2504
|
+
...(providerSearchReference ? { providerSearchReference } : {}),
|
|
2505
|
+
ids: {
|
|
2506
|
+
campaignId: request.scope.campaignId,
|
|
2507
|
+
tableId: request.scope.tableId,
|
|
2508
|
+
sourceLeadListId: request.scope.sourceLeadListId,
|
|
2509
|
+
senderId: request.scope.senderId,
|
|
2510
|
+
sourceFingerprint: request.scope.priorSourceFingerprint,
|
|
2511
|
+
...(providerSearchReference ? { providerSearchReference } : {}),
|
|
2512
|
+
},
|
|
2513
|
+
toolInput: {
|
|
2514
|
+
// The V3 marker. It is the ONLY thing that changes owner behaviour, and
|
|
2515
|
+
// it removes policy rather than adding any.
|
|
2516
|
+
refillV3SinglePass: true,
|
|
2517
|
+
priorSearchDepth: null,
|
|
2518
|
+
leadSourceProvider: request.command.provider,
|
|
2519
|
+
provider: request.command.provider,
|
|
2520
|
+
sourceRowLimit: request.scope.count,
|
|
2521
|
+
sourceLeadListId: request.scope.sourceLeadListId,
|
|
2522
|
+
query: request.command.query,
|
|
2523
|
+
filters: request.command.filterDelta,
|
|
2524
|
+
keywords: request.command.keywordBatch
|
|
2525
|
+
? [...request.command.keywordBatch]
|
|
2526
|
+
: undefined,
|
|
2527
|
+
selectedPostIds: request.command.selectedPostIds
|
|
2528
|
+
? [...request.command.selectedPostIds]
|
|
2529
|
+
: undefined,
|
|
2530
|
+
sourceFingerprint: request.command.requestFingerprint,
|
|
2531
|
+
effectId,
|
|
2532
|
+
...(providerSearchReference ? { providerSearchReference } : {}),
|
|
2533
|
+
},
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
export function refillV3SourceFamilyOwners(workspaceId) {
|
|
2537
|
+
async function run(ownerKey, owner, request) {
|
|
2538
|
+
const action = buildRefillV3SourceOwnerAction(request);
|
|
2539
|
+
let attempt;
|
|
2540
|
+
try {
|
|
2541
|
+
attempt = await owner(action, workspaceId);
|
|
2542
|
+
}
|
|
2543
|
+
catch (error) {
|
|
2544
|
+
// An owner THROW is a typed scoped failure, never a crash that takes the
|
|
2545
|
+
// outer queue down and never a generic no-effect success.
|
|
2546
|
+
return {
|
|
2547
|
+
kind: "failure",
|
|
2548
|
+
failureClass: "owner_threw",
|
|
2549
|
+
detail: `${ownerKey}: ${error instanceof Error ? error.message : "unknown"}`,
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2552
|
+
return normalizeRefillV3SourceOwnerReceipt({
|
|
2553
|
+
ownerKey,
|
|
2554
|
+
attempt,
|
|
2555
|
+
requestFingerprint: request.command.requestFingerprint,
|
|
2556
|
+
idempotencyKey: request.scope.idempotencyKey,
|
|
2557
|
+
});
|
|
2558
|
+
}
|
|
2559
|
+
return Object.freeze({
|
|
2560
|
+
refreshSenderPostEngagers: (request) => run("refreshSenderPostEngagers", refreshSenderPostEngagers, request),
|
|
2561
|
+
continueSignalDiscoverySource: (request) => run("continueSignalDiscoverySource", continueSignalDiscoverySource, request),
|
|
2562
|
+
broadenSignalSearch: (request) => run("broadenSignalSearch", broadenSignalSearch, request),
|
|
2563
|
+
continueProviderSource: (request) => run("continueProviderSource", continueProviderSource, request),
|
|
2564
|
+
broadenProviderSearch: (request) => run("broadenProviderSearch", broadenProviderSearch, request),
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2639
2567
|
export async function refreshSenderPostEngagers(action, workspaceId) {
|
|
2640
2568
|
const senderId = actionSenderId(action);
|
|
2641
2569
|
const campaignId = actionCampaignId(action);
|
|
@@ -14273,13 +14273,13 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
|
|
|
14273
14273
|
planReadCount: number;
|
|
14274
14274
|
refusalReason?: string | undefined;
|
|
14275
14275
|
enabled: true;
|
|
14276
|
-
status: "blocked" | "
|
|
14276
|
+
status: "blocked" | "refused" | "no_action" | "read_only_reread" | "executed_and_reread";
|
|
14277
14277
|
selectedAction: null;
|
|
14278
14278
|
result: unknown;
|
|
14279
14279
|
targetPlanReread: boolean;
|
|
14280
14280
|
} | {
|
|
14281
14281
|
enabled: true;
|
|
14282
|
-
status: "blocked" | "
|
|
14282
|
+
status: "blocked" | "refused" | "no_action" | "read_only_reread" | "executed_and_reread";
|
|
14283
14283
|
selectedAction: Record<string, unknown>;
|
|
14284
14284
|
result: unknown;
|
|
14285
14285
|
targetPlanReread: boolean;
|
|
@@ -14293,7 +14293,7 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
|
|
|
14293
14293
|
planReadCount: number;
|
|
14294
14294
|
refusalReason?: string | undefined;
|
|
14295
14295
|
enabled: true;
|
|
14296
|
-
status: "blocked" | "
|
|
14296
|
+
status: "blocked" | "refused" | "no_action" | "read_only_reread" | "executed_and_reread";
|
|
14297
14297
|
selectedAction: Record<string, unknown>;
|
|
14298
14298
|
result: unknown;
|
|
14299
14299
|
targetPlanReread: true;
|
|
@@ -293,7 +293,7 @@ function mergeAttemptLedgersIntoWorkspaceRunState(workspaceRunState, fenceRunSta
|
|
|
293
293
|
export const refillSendsToolDefinitions = [
|
|
294
294
|
{
|
|
295
295
|
name: "refill_sends",
|
|
296
|
-
description: "Typed command entrypoint for Sellable refill sends. Every mode begins with a read-only visible plan pinned to expectedTargetShapeRevision and expectedActionKey; no credit refresh, refill mutation, or run fence may exist before that render. A first manual call without those pins returns status plan_ready and requires explicit acceptance before its planned_manual_run continuation. A first explicit workspace-wide --yolo coordinator call without pins also returns status plan_ready with the fresh sender/campaign waterfall, selected exact target, and a planned_workspace_run continuation; yolo auto-accepts the displayed packet, then traverses its planner-ranked existing targets autonomously. Execution preflights globalActionQueue[0] against the rendered authority 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. After a terminal exact run, two fresh read-only preflights stabilize the workspace queue; if the same terminal action remains visible, the coordinator excludes only that exact sender/lane edge and preflights the next planner-ranked existing edge instead of reopening an identical fence. A retryable Signal Discovery provider outage defers that exact signal-search action family within its campaign/table/lane for the remainder of the current workspace command, even if the eligible sender cohort changes, records the blocker in workspaceRunState, and continues independent scheduler, credit, or source-family work; a fresh user command retries the deferred family. If no other exact edge exists while the workspace gap remains positive, it returns the concrete deferred provider blocker or a classified typed terminal: new_campaign_required when receipts prove every existing lane/source family is spent, actionable_supply_not_queued (retryable) when actionable row frontiers remain but no executable exact edge surfaced, or workspace_exact_target_unavailable for the residual case. Once a scheduler sweep is dispatched, the exact fence remains active and every in_progress continuation polls only that request key until its official terminal receipt. A host wait budget never converts the active sweep into loaded_awaiting_scheduler. The post-receipt target reread returns complete, another planner action, or a receipt-backed scheduler_terminal_with_remaining_gap blocker. loaded_awaiting_scheduler is a workspace terminal only when no refill-owned scheduler request is active. After the plan is rendered, the packaged workflow must immediately invoke yolo continuation arguments until workspace projected coverage fills, the workspace scheduler-loaded terminal is returned, or no independent exact work remains behind concrete blockers, 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 source rows, continue or broaden a selected source, traverse any planner-ranked EXISTING campaign/lane/source family, start a route-resolver start-eligible PAUSED existing campaign, run a request-scoped product scheduler sweep, and wait/reread. Each bounded primitive is followed by a full replan. It never sends directly, lowers paid InMail thresholds/floors, creates NEW campaigns, archives/deletes, or invents source selection outside the planner. Provider failure is retryable and never proves source exhaustion. Unselected PAUSED campaigns that are not start-eligible remain paused. Scheduler placement stays product-native; the command never raw-writes scheduler fields.",
|
|
296
|
+
description: "SUPERSEDED V1 REFILL COORDINATOR — CHECK THE ROUTE BEFORE CALLING. If this server also exposes `refill_v3_advance`, that tool owns refill execution and this one must NOT be called: every ordinary request to refill, top up, fill, max out, or load up sends goes to `refill_v3_advance` with exactly {workspaceId, scope}. Call this tool only on servers where `refill_v3_advance` is absent. Typed command entrypoint for Sellable refill sends. Every mode begins with a read-only visible plan pinned to expectedTargetShapeRevision and expectedActionKey; no credit refresh, refill mutation, or run fence may exist before that render. A first manual call without those pins returns status plan_ready and requires explicit acceptance before its planned_manual_run continuation. A first explicit workspace-wide --yolo coordinator call without pins also returns status plan_ready with the fresh sender/campaign waterfall, selected exact target, and a planned_workspace_run continuation; yolo auto-accepts the displayed packet, then traverses its planner-ranked existing targets autonomously. Execution preflights globalActionQueue[0] against the rendered authority 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. After a terminal exact run, two fresh read-only preflights stabilize the workspace queue; if the same terminal action remains visible, the coordinator excludes only that exact sender/lane edge and preflights the next planner-ranked existing edge instead of reopening an identical fence. A retryable Signal Discovery provider outage defers that exact signal-search action family within its campaign/table/lane for the remainder of the current workspace command, even if the eligible sender cohort changes, records the blocker in workspaceRunState, and continues independent scheduler, credit, or source-family work; a fresh user command retries the deferred family. If no other exact edge exists while the workspace gap remains positive, it returns the concrete deferred provider blocker or a classified typed terminal: new_campaign_required when receipts prove every existing lane/source family is spent, actionable_supply_not_queued (retryable) when actionable row frontiers remain but no executable exact edge surfaced, or workspace_exact_target_unavailable for the residual case. Once a scheduler sweep is dispatched, the exact fence remains active and every in_progress continuation polls only that request key until its official terminal receipt. A host wait budget never converts the active sweep into loaded_awaiting_scheduler. The post-receipt target reread returns complete, another planner action, or a receipt-backed scheduler_terminal_with_remaining_gap blocker. loaded_awaiting_scheduler is a workspace terminal only when no refill-owned scheduler request is active. After the plan is rendered, the packaged workflow must immediately invoke yolo continuation arguments until workspace projected coverage fills, the workspace scheduler-loaded terminal is returned, or no independent exact work remains behind concrete blockers, 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 source rows, continue or broaden a selected source, traverse any planner-ranked EXISTING campaign/lane/source family, start a route-resolver start-eligible PAUSED existing campaign, run a request-scoped product scheduler sweep, and wait/reread. Each bounded primitive is followed by a full replan. It never sends directly, lowers paid InMail thresholds/floors, creates NEW campaigns, archives/deletes, or invents source selection outside the planner. Provider failure is retryable and never proves source exhaustion. Unselected PAUSED campaigns that are not start-eligible remain paused. Scheduler placement stays product-native; the command never raw-writes scheduler fields.",
|
|
297
297
|
inputSchema: {
|
|
298
298
|
type: "object",
|
|
299
299
|
properties: {
|
|
@@ -2279,7 +2279,7 @@ function attachExplicitMessageTemplateRevision(params) {
|
|
|
2279
2279
|
export const refillTargetPlanToolDefinitions = [
|
|
2280
2280
|
{
|
|
2281
2281
|
name: "get_refill_target_plan",
|
|
2282
|
-
description: "read-only refill target planner to call before any refill mutation. It identifies eligible senders and infers one implicit refill lane per sender. For unified Sales Nav cascade campaigns, the public refill lane is the paid-InMail cascade target (send_inmail_closed) with campaign classification sales_nav_cascade; the same campaign can still route prospects to Open InMail or connection-request fallback based on row eligibility and fresh paid credits >= 5. Non-cascade lanes remain connection invites or paid InMails, chosen from future scheduled, recent scheduled, and ready-to-schedule evidence in active campaign-backed sequence campaigns before falling back to campaign sequences. DMs remain follow-up sequence actions, not refill target lanes. By default the planner computes the scheduler-forward 48-hour target window, selected sender-local days whose sending windows overlap that window, gross target, actual sent coverage, scheduler-owned scheduled coverage, projected coverage (sent + scheduled), ready buffer, remaining projected gap, paid InMail credit/threshold/freshness feasibility, same-campaign connection fallback availability, bounded action candidates, and targetRevision drift proof. This tool does not create rows, import leads, prepare messages, approve messages, does not schedule sends, start campaigns, lower paid InMail thresholds, create campaigns, launch, spend InMail credits, or write scheduler fields. Complete projected targets no-op without approval; loaded, awaiting scheduler targets use read-only wait/reread until projected coverage fills.",
|
|
2282
|
+
description: "V1-ROUTE PLANNER — CHECK THE ROUTE BEFORE CALLING. If this server also exposes `refill_v3_advance`, that tool owns refill execution and makes its own fresh observation, so a refill must NOT be opened here; send every ordinary refill, top up, fill, max out, or load up request to `refill_v3_advance` instead. Call this tool only on servers where `refill_v3_advance` is absent. read-only refill target planner to call before any refill mutation. It identifies eligible senders and infers one implicit refill lane per sender. For unified Sales Nav cascade campaigns, the public refill lane is the paid-InMail cascade target (send_inmail_closed) with campaign classification sales_nav_cascade; the same campaign can still route prospects to Open InMail or connection-request fallback based on row eligibility and fresh paid credits >= 5. Non-cascade lanes remain connection invites or paid InMails, chosen from future scheduled, recent scheduled, and ready-to-schedule evidence in active campaign-backed sequence campaigns before falling back to campaign sequences. DMs remain follow-up sequence actions, not refill target lanes. By default the planner computes the scheduler-forward 48-hour target window, selected sender-local days whose sending windows overlap that window, gross target, actual sent coverage, scheduler-owned scheduled coverage, projected coverage (sent + scheduled), ready buffer, remaining projected gap, paid InMail credit/threshold/freshness feasibility, same-campaign connection fallback availability, bounded action candidates, and targetRevision drift proof. This tool does not create rows, import leads, prepare messages, approve messages, does not schedule sends, start campaigns, lower paid InMail thresholds, create campaigns, launch, spend InMail credits, or write scheduler fields. Complete projected targets no-op without approval; loaded, awaiting scheduler targets use read-only wait/reread until projected coverage fills.",
|
|
2283
2283
|
inputSchema: {
|
|
2284
2284
|
type: "object",
|
|
2285
2285
|
properties: {
|