@sellable/mcp 0.1.756 → 0.1.758
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/refill-run-loop.js +273 -33
- package/dist/tools/campaign-message-preparation.d.ts +6 -0
- package/dist/tools/campaign-message-preparation.js +1 -0
- package/dist/tools/evergreen-refill-plan.d.ts +34 -0
- package/dist/tools/evergreen-refill-plan.js +1 -1
- package/dist/tools/refill-executors.d.ts +2 -0
- package/dist/tools/refill-executors.js +21 -6
- package/dist/tools/refill-sends.js +25 -1
- package/dist/tools/registry.d.ts +5 -0
- package/package.json +1 -1
package/dist/refill-run-loop.js
CHANGED
|
@@ -61,6 +61,14 @@ const DEFAULT_BUDGETS = {
|
|
|
61
61
|
// refuses to promote unversioned legacy creditTrust packets into scheduling
|
|
62
62
|
// authority, even when they contain a fresh provider response.
|
|
63
63
|
const REFILL_CREDIT_TRUST_VERSION = 2;
|
|
64
|
+
const PAID_INMAIL_CREDIT_RECEIPT_VERSION = "paid_inmail_credit_evaluation.v2";
|
|
65
|
+
const PAID_INMAIL_CREDIT_POLICY_VERSION = 1;
|
|
66
|
+
const PAID_INMAIL_CREDIT_POLICY_TTL_MS = 6 * 60 * 60 * 1000;
|
|
67
|
+
const USABLE_CONTEXTUAL_CREDIT_RECEIPT_STATUSES = new Set([
|
|
68
|
+
"fresh",
|
|
69
|
+
"refreshed",
|
|
70
|
+
"below_threshold",
|
|
71
|
+
]);
|
|
64
72
|
// Mirrors RefillDoneReason in src/lib/workflow-tables/refill-target-plan.ts,
|
|
65
73
|
// plus loop-authored terminals the planner never emits:
|
|
66
74
|
// - abandoned_stale_checkpoint (fix 110-20d): a resumed wait the loop declares
|
|
@@ -885,6 +893,39 @@ function runScopedSignalDiscoveryAttemptUpdate(action, runState, now) {
|
|
|
885
893
|
],
|
|
886
894
|
};
|
|
887
895
|
}
|
|
896
|
+
function runScopedSignalSourceValidationUpdate(action, runState, now) {
|
|
897
|
+
const toolInput = actionToolInput(action);
|
|
898
|
+
const provider = stringValue(toolInput.leadSourceProvider);
|
|
899
|
+
if (provider !== "signal-discovery" && provider !== "campaign-tracked-post") {
|
|
900
|
+
return {};
|
|
901
|
+
}
|
|
902
|
+
const campaignId = actionCampaignId(action);
|
|
903
|
+
const payload = resultPayload(runState.lastOutcome);
|
|
904
|
+
const reviewBatch = recordValue(payload.reviewBatch);
|
|
905
|
+
const reviewBatchBasisHash = stringValue(reviewBatch?.basisHash);
|
|
906
|
+
const reviewBatchRowCount = numberValue(reviewBatch?.rowCount);
|
|
907
|
+
if (!campaignId || !reviewBatchBasisHash || !reviewBatchRowCount)
|
|
908
|
+
return {};
|
|
909
|
+
const attempts = arrayValue(runState.signalSourceValidationAttempts).filter(isRecord);
|
|
910
|
+
return {
|
|
911
|
+
signalSourceValidationAttempts: [
|
|
912
|
+
...attempts.filter((attempt) => stringValue(attempt.campaignId) !== campaignId),
|
|
913
|
+
{
|
|
914
|
+
campaignId,
|
|
915
|
+
tableId: actionTableId(action),
|
|
916
|
+
senderId: actionSenderId(action),
|
|
917
|
+
sourceLeadListId: stringValue(toolInput.sourceLeadListId),
|
|
918
|
+
sourceFingerprint: actionSourceFingerprint(action),
|
|
919
|
+
reviewBatchBasisHash,
|
|
920
|
+
reviewBatchRowCount,
|
|
921
|
+
threshold: 0.1,
|
|
922
|
+
status: "pending",
|
|
923
|
+
copiedAt: now.toISOString(),
|
|
924
|
+
scope: "refill_run",
|
|
925
|
+
},
|
|
926
|
+
],
|
|
927
|
+
};
|
|
928
|
+
}
|
|
888
929
|
// Phase 110-05: Sales Nav / Prospeo same-source continuation gets exactly one
|
|
889
930
|
// bounded add per (campaign, sender) per run, mirroring the Signal Discovery
|
|
890
931
|
// run-scoped attempt bound so the loop never repeats an unchanged failed
|
|
@@ -2274,6 +2315,116 @@ async function startOrResume(input, deps) {
|
|
|
2274
2315
|
}
|
|
2275
2316
|
return ctx;
|
|
2276
2317
|
}
|
|
2318
|
+
function sha256ReceiptParts(parts) {
|
|
2319
|
+
return createHash("sha256").update(JSON.stringify(parts)).digest("hex");
|
|
2320
|
+
}
|
|
2321
|
+
function sameReceiptContext(value, expected) {
|
|
2322
|
+
return (stringValue(value.workspaceId) === expected.workspaceId &&
|
|
2323
|
+
stringValue(value.senderId) === expected.senderId &&
|
|
2324
|
+
stringValue(value.runId) === expected.runId &&
|
|
2325
|
+
stringValue(value.campaignId) === expected.campaignId &&
|
|
2326
|
+
stringValue(value.tableId) === expected.tableId &&
|
|
2327
|
+
stringValue(value.columnId) === expected.columnId &&
|
|
2328
|
+
(stringValue(value.cellId) ?? null) === expected.cellId &&
|
|
2329
|
+
stringValue(value.actionType) === expected.actionType &&
|
|
2330
|
+
numberValue(value.threshold) === expected.threshold);
|
|
2331
|
+
}
|
|
2332
|
+
/**
|
|
2333
|
+
* Decode the backend-owned Phase 123 receipt enough to prove that the client
|
|
2334
|
+
* is handing the exact fact/context identity back to the planner. The backend
|
|
2335
|
+
* performs the same validation again when parsing run state; this client-side
|
|
2336
|
+
* check prevents advancing g0 with bytes that are already known to fail there.
|
|
2337
|
+
*/
|
|
2338
|
+
function decodeContextualPaidInmailReceipt(value, expected, now) {
|
|
2339
|
+
const response = recordValue(value);
|
|
2340
|
+
const receipt = recordValue(response?.receipt) ??
|
|
2341
|
+
(stringValue(response?.receiptVersion) ===
|
|
2342
|
+
PAID_INMAIL_CREDIT_RECEIPT_VERSION
|
|
2343
|
+
? response
|
|
2344
|
+
: null);
|
|
2345
|
+
if (!receipt ||
|
|
2346
|
+
stringValue(receipt.receiptVersion) !==
|
|
2347
|
+
PAID_INMAIL_CREDIT_RECEIPT_VERSION ||
|
|
2348
|
+
numberValue(receipt.policyVersion) !== PAID_INMAIL_CREDIT_POLICY_VERSION ||
|
|
2349
|
+
numberValue(receipt.policyTtlMs) !== PAID_INMAIL_CREDIT_POLICY_TTL_MS ||
|
|
2350
|
+
numberValue(receipt.maxStalenessMs) !== PAID_INMAIL_CREDIT_POLICY_TTL_MS ||
|
|
2351
|
+
!USABLE_CONTEXTUAL_CREDIT_RECEIPT_STATUSES.has(stringValue(receipt.status) ?? "")) {
|
|
2352
|
+
return null;
|
|
2353
|
+
}
|
|
2354
|
+
const evaluationContext = recordValue(receipt.evaluationContext);
|
|
2355
|
+
if (!evaluationContext ||
|
|
2356
|
+
!sameReceiptContext(evaluationContext, expected) ||
|
|
2357
|
+
!sameReceiptContext(receipt, expected)) {
|
|
2358
|
+
return null;
|
|
2359
|
+
}
|
|
2360
|
+
const evaluatedAt = new Date(stringValue(receipt.evaluatedAt) ?? "");
|
|
2361
|
+
const after = recordValue(receipt.after);
|
|
2362
|
+
const checkedAt = new Date(stringValue(after?.checkedAt) ?? "");
|
|
2363
|
+
const balance = numberValue(after?.balance);
|
|
2364
|
+
const sentSince = numberValue(after?.sentSince);
|
|
2365
|
+
const available = numberValue(after?.available);
|
|
2366
|
+
const logicalRefreshId = stringValue(receipt.logicalRefreshId);
|
|
2367
|
+
const transportAttempt = recordValue(receipt.transportAttempt);
|
|
2368
|
+
if (!Number.isFinite(evaluatedAt.getTime()) ||
|
|
2369
|
+
!Number.isFinite(checkedAt.getTime()) ||
|
|
2370
|
+
evaluatedAt.getTime() > now.getTime() ||
|
|
2371
|
+
checkedAt.getTime() > now.getTime() ||
|
|
2372
|
+
balance === null ||
|
|
2373
|
+
!Number.isInteger(balance) ||
|
|
2374
|
+
balance < 0 ||
|
|
2375
|
+
sentSince === null ||
|
|
2376
|
+
!Number.isInteger(sentSince) ||
|
|
2377
|
+
sentSince < 0 ||
|
|
2378
|
+
available === null ||
|
|
2379
|
+
!Number.isInteger(available) ||
|
|
2380
|
+
available < 0 ||
|
|
2381
|
+
!logicalRefreshId ||
|
|
2382
|
+
stringValue(transportAttempt?.logicalRefreshId) !== logicalRefreshId) {
|
|
2383
|
+
return null;
|
|
2384
|
+
}
|
|
2385
|
+
const factIdentity = sha256ReceiptParts([
|
|
2386
|
+
"paid-inmail-credit-fact",
|
|
2387
|
+
PAID_INMAIL_CREDIT_POLICY_VERSION,
|
|
2388
|
+
expected.senderId,
|
|
2389
|
+
checkedAt.toISOString(),
|
|
2390
|
+
balance,
|
|
2391
|
+
]);
|
|
2392
|
+
if (stringValue(receipt.factIdentity) !== factIdentity ||
|
|
2393
|
+
stringValue(after?.factIdentity) !== factIdentity) {
|
|
2394
|
+
return null;
|
|
2395
|
+
}
|
|
2396
|
+
const snapshotIdentity = sha256ReceiptParts([
|
|
2397
|
+
"paid-inmail-credit-evaluation-snapshot",
|
|
2398
|
+
factIdentity,
|
|
2399
|
+
sentSince,
|
|
2400
|
+
available,
|
|
2401
|
+
evaluatedAt.toISOString(),
|
|
2402
|
+
]);
|
|
2403
|
+
if (stringValue(receipt.evaluationSnapshotIdentity) !== snapshotIdentity) {
|
|
2404
|
+
return null;
|
|
2405
|
+
}
|
|
2406
|
+
const contextIdentity = sha256ReceiptParts([
|
|
2407
|
+
expected.workspaceId,
|
|
2408
|
+
expected.senderId,
|
|
2409
|
+
expected.runId,
|
|
2410
|
+
expected.campaignId,
|
|
2411
|
+
expected.tableId,
|
|
2412
|
+
expected.columnId,
|
|
2413
|
+
expected.cellId,
|
|
2414
|
+
expected.actionType,
|
|
2415
|
+
expected.threshold,
|
|
2416
|
+
]);
|
|
2417
|
+
const receiptId = sha256ReceiptParts([
|
|
2418
|
+
PAID_INMAIL_CREDIT_RECEIPT_VERSION,
|
|
2419
|
+
PAID_INMAIL_CREDIT_POLICY_VERSION,
|
|
2420
|
+
contextIdentity,
|
|
2421
|
+
factIdentity,
|
|
2422
|
+
snapshotIdentity,
|
|
2423
|
+
logicalRefreshId,
|
|
2424
|
+
stringValue(receipt.status),
|
|
2425
|
+
]);
|
|
2426
|
+
return stringValue(receipt.receiptId) === receiptId ? receipt : null;
|
|
2427
|
+
}
|
|
2277
2428
|
async function gateBootstrap(input, deps, ctx) {
|
|
2278
2429
|
const plan = await deps.readPlan(refillPlanReadInput(input, { ctx, journal: false }));
|
|
2279
2430
|
const issuedReportingContext = sanitizeRefillReportingContextProjection(plan.reportingContext);
|
|
@@ -2294,46 +2445,127 @@ async function gateBootstrap(input, deps, ctx) {
|
|
|
2294
2445
|
if (issuedReportingContext) {
|
|
2295
2446
|
input.reportingContext = issuedReportingContext;
|
|
2296
2447
|
}
|
|
2297
|
-
const
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
const
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2448
|
+
const pinnedRunState = pinInitialSenderLaneChains(plan, ctx.runState);
|
|
2449
|
+
// Exact fenced plans are projected by projectedV2Packet(), whose legacy
|
|
2450
|
+
// bootstrap.paidCredit array is intentionally empty. The canonical refresh
|
|
2451
|
+
// authority is packet.target.globalActionQueue; reading the bootstrap array
|
|
2452
|
+
// minted `{ version: 2, receipts: [] }` on every run, which made the next
|
|
2453
|
+
// exact planner read receipt-aware without giving it a receipt to validate.
|
|
2454
|
+
const packet = recordValue(plan.packet);
|
|
2455
|
+
const packetTarget = recordValue(packet?.target);
|
|
2456
|
+
const paidCreditActions = arrayValue(packetTarget?.globalActionQueue)
|
|
2457
|
+
.filter(isRecord)
|
|
2458
|
+
.filter((action) => stringValue(action.type) === "refresh_paid_inmail_credits");
|
|
2459
|
+
const contextualReceipts = [];
|
|
2460
|
+
const refreshedSenderIds = [];
|
|
2461
|
+
const now = deps.now?.() ?? new Date();
|
|
2462
|
+
for (const action of paidCreditActions) {
|
|
2463
|
+
const toolInput = actionToolInput(action);
|
|
2464
|
+
const ids = actionIds(action);
|
|
2465
|
+
const senderId = actionSenderId(action);
|
|
2466
|
+
const campaignId = actionCampaignId(action);
|
|
2467
|
+
const tableId = actionTableId(action);
|
|
2468
|
+
const columnId = stringValue(toolInput.columnId) ??
|
|
2469
|
+
stringValue(ids.columnId) ??
|
|
2470
|
+
stringValue(action.columnId);
|
|
2471
|
+
const actionType = stringValue(action.actionType) ?? stringValue(toolInput.actionType);
|
|
2472
|
+
const threshold = numberValue(toolInput.threshold) ??
|
|
2473
|
+
numberValue(toolInput.oldThreshold) ??
|
|
2474
|
+
numberValue(action.threshold) ??
|
|
2475
|
+
numberValue(action.oldThreshold);
|
|
2476
|
+
const completeContext = senderId &&
|
|
2477
|
+
campaignId &&
|
|
2478
|
+
tableId &&
|
|
2479
|
+
columnId &&
|
|
2480
|
+
actionType === "send_inmail_closed" &&
|
|
2481
|
+
threshold !== null &&
|
|
2482
|
+
Number.isInteger(threshold) &&
|
|
2483
|
+
threshold >= 0
|
|
2484
|
+
? {
|
|
2485
|
+
workspaceId: input.workspaceId,
|
|
2486
|
+
senderId,
|
|
2487
|
+
runId: ctx.runId,
|
|
2488
|
+
campaignId,
|
|
2489
|
+
tableId,
|
|
2490
|
+
columnId,
|
|
2491
|
+
cellId: null,
|
|
2492
|
+
actionType: "INMAIL_CLOSED",
|
|
2493
|
+
threshold,
|
|
2494
|
+
}
|
|
2495
|
+
: null;
|
|
2496
|
+
if (!completeContext) {
|
|
2497
|
+
return {
|
|
2498
|
+
status: "blocked",
|
|
2499
|
+
blocker: "paid_inmail_refresh_failed",
|
|
2500
|
+
runId: ctx.runId,
|
|
2501
|
+
fence: ctx.fence,
|
|
2502
|
+
gate: ctx.gate,
|
|
2503
|
+
guidance: "The exact paid InMail refresh action did not carry a complete sender/campaign/table/column/action/threshold context. The run stayed at bootstrap and no refill primitive executed.",
|
|
2504
|
+
report: { action },
|
|
2505
|
+
journalPath: ctx.journalPath,
|
|
2506
|
+
targetConfig: ctx.targetConfig ?? undefined,
|
|
2507
|
+
runHandle: ctx.runHandle,
|
|
2508
|
+
};
|
|
2312
2509
|
}
|
|
2313
|
-
const
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2510
|
+
const refresh = await deps.executors.refreshPaidInmailCreditsWithRetry(completeContext.senderId, completeContext.workspaceId, {
|
|
2511
|
+
runId: completeContext.runId,
|
|
2512
|
+
actionType: "send_inmail_closed",
|
|
2513
|
+
campaignId: completeContext.campaignId,
|
|
2514
|
+
tableId: completeContext.tableId,
|
|
2515
|
+
columnId: completeContext.columnId,
|
|
2516
|
+
threshold: completeContext.threshold,
|
|
2517
|
+
});
|
|
2518
|
+
const classification = classifyPaidInmailRefreshReceipt(refresh.receipt);
|
|
2519
|
+
const decodedReceipt = decodeContextualPaidInmailReceipt(refresh.receipt, completeContext, now);
|
|
2520
|
+
if (!classification.usableCurrentFacts || !decodedReceipt) {
|
|
2521
|
+
return {
|
|
2522
|
+
status: "blocked",
|
|
2523
|
+
blocker: "paid_inmail_refresh_failed",
|
|
2524
|
+
runId: ctx.runId,
|
|
2525
|
+
fence: ctx.fence,
|
|
2526
|
+
gate: ctx.gate,
|
|
2527
|
+
guidance: "Paid InMail credit refresh did not return one usable receipt matching the exact fenced context. The run stayed at bootstrap and did not advance with invalid authority.",
|
|
2528
|
+
report: {
|
|
2529
|
+
senderId: completeContext.senderId,
|
|
2530
|
+
receiptStatus: classification.status,
|
|
2531
|
+
error: classification.error,
|
|
2532
|
+
attempts: refresh.attempts,
|
|
2533
|
+
errors: refresh.errors,
|
|
2534
|
+
},
|
|
2535
|
+
journalPath: ctx.journalPath,
|
|
2536
|
+
targetConfig: ctx.targetConfig ?? undefined,
|
|
2537
|
+
runHandle: ctx.runHandle,
|
|
2538
|
+
};
|
|
2319
2539
|
}
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2540
|
+
contextualReceipts.push(decodedReceipt);
|
|
2541
|
+
refreshedSenderIds.push(completeContext.senderId);
|
|
2542
|
+
}
|
|
2543
|
+
// Never promote an empty or legacy packet into receipt authority. With no
|
|
2544
|
+
// refresh head, the exact planner remains cache-fact based and can execute
|
|
2545
|
+
// the source/preparation action it already selected.
|
|
2546
|
+
const { creditTrust: _discardedCreditTrust, ...runStateWithoutCreditTrust } = pinnedRunState;
|
|
2547
|
+
const nextRunState = contextualReceipts.length > 0
|
|
2548
|
+
? mergeRunState(runStateWithoutCreditTrust, {
|
|
2549
|
+
creditTrust: {
|
|
2550
|
+
version: REFILL_CREDIT_TRUST_VERSION,
|
|
2551
|
+
receiptId: stringValue(contextualReceipts[0]?.receiptId),
|
|
2552
|
+
refreshedAt: now.toISOString(),
|
|
2553
|
+
senderIds: [...new Set(refreshedSenderIds)],
|
|
2554
|
+
attemptedSenderIds: [...new Set(refreshedSenderIds)],
|
|
2555
|
+
receipts: contextualReceipts,
|
|
2556
|
+
},
|
|
2557
|
+
})
|
|
2558
|
+
: runStateWithoutCreditTrust;
|
|
2331
2559
|
await safeJournalAppend(ctx, deps, deps.journal.renderBootstrapSection({
|
|
2332
2560
|
summary: "Refill v2 execution bootstrap",
|
|
2333
2561
|
senderSummary: safeJson(recordValue(plan.bootstrap)?.senders ?? []),
|
|
2334
2562
|
laneSummary: safeJson(recordValue(plan.bootstrap)?.laneOrder ?? []),
|
|
2335
2563
|
targetSummary: safeJson(recordValue(plan.bootstrap)?.target ?? []),
|
|
2336
|
-
creditSummary: safeJson(
|
|
2564
|
+
creditSummary: safeJson({
|
|
2565
|
+
refreshActionCount: paidCreditActions.length,
|
|
2566
|
+
persistedReceiptCount: contextualReceipts.length,
|
|
2567
|
+
refreshedSenderIds: [...new Set(refreshedSenderIds)],
|
|
2568
|
+
}),
|
|
2337
2569
|
}));
|
|
2338
2570
|
const advanced = await deps.runClient.advanceRefillRunGateRemote({
|
|
2339
2571
|
workspaceId: input.workspaceId,
|
|
@@ -4314,6 +4546,14 @@ async function gateVerify(input, deps, ctx, budgets) {
|
|
|
4314
4546
|
// keep going — instead of terminalizing the invite lane with confirmed-fillable
|
|
4315
4547
|
// slots still open. Once a broadening round proves the universe dry this run, this
|
|
4316
4548
|
// no longer intercepts and the refusal terminalizes typed below.
|
|
4549
|
+
if (actionType === "copy_selected_source_rows" &&
|
|
4550
|
+
stringValue(recordValue(ctx.runState.lastOutcome)?.status) ===
|
|
4551
|
+
"executed_and_reread") {
|
|
4552
|
+
return advanceBackToPlan(input, deps, ctx, {
|
|
4553
|
+
...runScopedSignalSourceValidationUpdate(action, ctx.runState, deps.now?.() ?? new Date()),
|
|
4554
|
+
...clearRunScopedDrySourcePrepAttempts(actionCampaignId(action), ctx.runState),
|
|
4555
|
+
});
|
|
4556
|
+
}
|
|
4317
4557
|
if (actionType === "continue_signal_discovery_source" &&
|
|
4318
4558
|
isSignalSelectionBroadenableRefusal(ctx.runState.lastOutcome) &&
|
|
4319
4559
|
!hasRunScopedSignalUniverseExhausted(action, ctx.runState)) {
|
|
@@ -12,6 +12,7 @@ type RowSelector = {
|
|
|
12
12
|
} | {
|
|
13
13
|
type: "reviewBatch";
|
|
14
14
|
limit?: number;
|
|
15
|
+
basisHash?: string;
|
|
15
16
|
} | {
|
|
16
17
|
type: "staleGeneratedMessages";
|
|
17
18
|
limit?: number;
|
|
@@ -121,6 +122,11 @@ export declare const campaignMessagePreparationToolDefinitions: ({
|
|
|
121
122
|
minimum: number;
|
|
122
123
|
maximum: number;
|
|
123
124
|
};
|
|
125
|
+
basisHash: {
|
|
126
|
+
type: string;
|
|
127
|
+
minLength: number;
|
|
128
|
+
maxLength: number;
|
|
129
|
+
};
|
|
124
130
|
rowIds: {
|
|
125
131
|
type: string;
|
|
126
132
|
items: {
|
|
@@ -75,6 +75,7 @@ export const campaignMessagePreparationToolDefinitions = [
|
|
|
75
75
|
],
|
|
76
76
|
},
|
|
77
77
|
limit: { type: "number", minimum: 1, maximum: 500 },
|
|
78
|
+
basisHash: { type: "string", minLength: 1, maxLength: 200 },
|
|
78
79
|
rowIds: {
|
|
79
80
|
type: "array",
|
|
80
81
|
items: { type: "string", minLength: 1 },
|
|
@@ -17,6 +17,40 @@ type GetRefillPlanV2Input = {
|
|
|
17
17
|
journalNote?: string;
|
|
18
18
|
};
|
|
19
19
|
export declare function sanitizeEvergreenRefillPlanResult(value: unknown): Record<string, unknown>;
|
|
20
|
+
export declare function projectedV2Packet(packet: Record<string, unknown>): {
|
|
21
|
+
sideEffects: {
|
|
22
|
+
scheduled: boolean;
|
|
23
|
+
mutated: boolean;
|
|
24
|
+
sent: boolean;
|
|
25
|
+
refreshedPaidInmailCredits: boolean;
|
|
26
|
+
};
|
|
27
|
+
warnings: unknown;
|
|
28
|
+
reporting?: Record<string, unknown> | undefined;
|
|
29
|
+
refillReporting?: Record<string, unknown> | undefined;
|
|
30
|
+
reportingContext?: RefillReportingContextV2 | undefined;
|
|
31
|
+
readOnly: boolean;
|
|
32
|
+
generatedAt: unknown;
|
|
33
|
+
bootstrap: {
|
|
34
|
+
senders: never[];
|
|
35
|
+
laneOrder: never[];
|
|
36
|
+
target: never[];
|
|
37
|
+
paidCredit: never[];
|
|
38
|
+
laneMemoryHints: {
|
|
39
|
+
appliedFromRunState: boolean;
|
|
40
|
+
cooldownsApplied: never[];
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
plans: {};
|
|
44
|
+
globalActionQueue: {};
|
|
45
|
+
evergreen: unknown;
|
|
46
|
+
planRevision: unknown;
|
|
47
|
+
stateRevision: unknown;
|
|
48
|
+
packet: Record<string, unknown>;
|
|
49
|
+
targetConfig: unknown;
|
|
50
|
+
targetConfigBytes: unknown;
|
|
51
|
+
targetConfigDigest: unknown;
|
|
52
|
+
runHandle: {} | null;
|
|
53
|
+
};
|
|
20
54
|
export declare function buildRunStateFromLocalHints(workspaceId: string): {
|
|
21
55
|
version: number;
|
|
22
56
|
senderCursors: never[];
|
|
@@ -76,7 +76,7 @@ export function sanitizeEvergreenRefillPlanResult(value) {
|
|
|
76
76
|
}
|
|
77
77
|
return sanitized;
|
|
78
78
|
}
|
|
79
|
-
function projectedV2Packet(packet) {
|
|
79
|
+
export function projectedV2Packet(packet) {
|
|
80
80
|
const target = isRecord(packet.target) ? packet.target : {};
|
|
81
81
|
const reporting = sanitizeRefillReportingProjection(packet.reporting);
|
|
82
82
|
const reportingContext = sanitizeRefillReportingContextProjection(packet.reportingContext);
|
|
@@ -7,6 +7,7 @@ export type PaidInmailRefreshAction = {
|
|
|
7
7
|
action?: Record<string, unknown>;
|
|
8
8
|
};
|
|
9
9
|
export type PaidInmailCreditRefreshOptions = {
|
|
10
|
+
runId?: string | null;
|
|
10
11
|
actionType?: string | null;
|
|
11
12
|
campaignId?: string | null;
|
|
12
13
|
tableId?: string | null;
|
|
@@ -119,6 +120,7 @@ export type PrepareRowSelector = {
|
|
|
119
120
|
} | {
|
|
120
121
|
type: "reviewBatch";
|
|
121
122
|
limit?: number;
|
|
123
|
+
basisHash?: string;
|
|
122
124
|
} | {
|
|
123
125
|
type: "staleGeneratedMessages";
|
|
124
126
|
limit?: number;
|
|
@@ -450,9 +450,11 @@ export function prepareRowSelectorValue(value) {
|
|
|
450
450
|
return undefined;
|
|
451
451
|
}
|
|
452
452
|
const limit = numberValue(selector?.limit);
|
|
453
|
+
const basisHash = type === "reviewBatch" ? stringValue(selector?.basisHash) : null;
|
|
453
454
|
return {
|
|
454
455
|
type,
|
|
455
456
|
...(limit && limit > 0 ? { limit: Math.floor(limit) } : {}),
|
|
457
|
+
...(type === "reviewBatch" && basisHash ? { basisHash } : {}),
|
|
456
458
|
};
|
|
457
459
|
}
|
|
458
460
|
export function uniqueStrings(values) {
|
|
@@ -1052,7 +1054,9 @@ export function refillPrepareRequestHash(params) {
|
|
|
1052
1054
|
? `rowIds:${createHash("sha256")
|
|
1053
1055
|
.update(JSON.stringify([...params.rowSelector.rowIds].sort()))
|
|
1054
1056
|
.digest("hex")}`
|
|
1055
|
-
:
|
|
1057
|
+
: params.rowSelector.type === "reviewBatch"
|
|
1058
|
+
? `reviewBatch:${params.rowSelector.limit ?? "no-limit"}:${params.rowSelector.basisHash ?? "no-basis"}`
|
|
1059
|
+
: `${params.rowSelector.type}:${params.rowSelector.limit ?? "no-limit"}`
|
|
1056
1060
|
: "no-row-selector";
|
|
1057
1061
|
// Request hashes are persisted in the preparation job's normalized config.
|
|
1058
1062
|
// Keep the public identity fixed-width: the former concatenated form could
|
|
@@ -3221,6 +3225,16 @@ export async function executeOneYoloPrimitive(action, workspaceId) {
|
|
|
3221
3225
|
const allowPartialSourceList = toolInput.allowPartialSourceList === true;
|
|
3222
3226
|
const sourceFingerprint = stringValue(toolInput.sourceFingerprint) ??
|
|
3223
3227
|
stringValue(actionIds(action).sourceFingerprint);
|
|
3228
|
+
const signalSource = actionLeadSourceProvider(action) === "signal-discovery" ||
|
|
3229
|
+
actionLeadSourceProvider(action) === "campaign-tracked-post";
|
|
3230
|
+
const requestedReviewBatchLimit = sourceRowIds.length > 0
|
|
3231
|
+
? sourceRowIds.length
|
|
3232
|
+
: (sourceRowLimit ?? undefined);
|
|
3233
|
+
const reviewBatchLimit = requestedReviewBatchLimit
|
|
3234
|
+
? signalSource
|
|
3235
|
+
? Math.min(15, requestedReviewBatchLimit)
|
|
3236
|
+
: requestedReviewBatchLimit
|
|
3237
|
+
: undefined;
|
|
3224
3238
|
if (!campaignOfferId || !sourceLeadListId || !sourceFingerprint) {
|
|
3225
3239
|
return {
|
|
3226
3240
|
status: "refused",
|
|
@@ -3248,9 +3262,7 @@ export async function executeOneYoloPrimitive(action, workspaceId) {
|
|
|
3248
3262
|
...(workspaceId ? { workspaceId } : {}),
|
|
3249
3263
|
sourceRowIds: sourceRowIds.length > 0 ? sourceRowIds : undefined,
|
|
3250
3264
|
sourceRowLimit: sourceRowIds.length > 0 ? undefined : (sourceRowLimit ?? undefined),
|
|
3251
|
-
reviewBatchLimit
|
|
3252
|
-
? sourceRowIds.length
|
|
3253
|
-
: (sourceRowLimit ?? undefined),
|
|
3265
|
+
reviewBatchLimit,
|
|
3254
3266
|
...(allowPartialSourceList ? { allowPartialSourceList: true } : {}),
|
|
3255
3267
|
};
|
|
3256
3268
|
let result;
|
|
@@ -3288,16 +3300,19 @@ export async function executeOneYoloPrimitive(action, workspaceId) {
|
|
|
3288
3300
|
const retrySourceRowLimit = retrySourceRowIds.length > 0
|
|
3289
3301
|
? undefined
|
|
3290
3302
|
: Math.min(sourceRowLimit ?? remainingRows, remainingRows);
|
|
3291
|
-
const
|
|
3303
|
+
const retryCopyLimit = retrySourceRowIds.length > 0
|
|
3292
3304
|
? retrySourceRowIds.length
|
|
3293
3305
|
: retrySourceRowLimit;
|
|
3294
|
-
if (!
|
|
3306
|
+
if (!retryCopyLimit || retryCopyLimit <= 0) {
|
|
3295
3307
|
return {
|
|
3296
3308
|
status: "refused",
|
|
3297
3309
|
refusalReason: rowLimitPayload.error ??
|
|
3298
3310
|
"selected campaign table cannot accept more workflow rows",
|
|
3299
3311
|
};
|
|
3300
3312
|
}
|
|
3313
|
+
const retryReviewBatchLimit = signalSource
|
|
3314
|
+
? Math.min(15, retryCopyLimit)
|
|
3315
|
+
: retryCopyLimit;
|
|
3301
3316
|
const retryResult = await confirmLeadList({
|
|
3302
3317
|
...copyInput,
|
|
3303
3318
|
sourceRowIds: retrySourceRowIds.length > 0 ? retrySourceRowIds : undefined,
|
|
@@ -247,6 +247,7 @@ const WORKSPACE_ATTEMPT_LEDGER_KEYS = [
|
|
|
247
247
|
"signalDiscoveryAttempts",
|
|
248
248
|
"providerSourceAttempts",
|
|
249
249
|
"broadenSignalSearchAttempts",
|
|
250
|
+
"signalSourceValidationAttempts",
|
|
250
251
|
"broadenProviderSearchAttempts",
|
|
251
252
|
"rubricRelaxationAttempts",
|
|
252
253
|
"senderPostRefreshAttempts",
|
|
@@ -790,8 +791,29 @@ function sameWorkspaceExactAction(left, right) {
|
|
|
790
791
|
Boolean(left?.actionKey) &&
|
|
791
792
|
left?.actionKey === right.actionKey);
|
|
792
793
|
}
|
|
794
|
+
function coordinatorSenderIds(input) {
|
|
795
|
+
const explicit = normalizeStrings(input.senderIds);
|
|
796
|
+
if (explicit.length > 0)
|
|
797
|
+
return explicit;
|
|
798
|
+
const targetConfigSenderIds = normalizeStrings(input.targetConfig?.senderScope.senderIds);
|
|
799
|
+
if (targetConfigSenderIds.length > 0)
|
|
800
|
+
return targetConfigSenderIds;
|
|
801
|
+
const runState = recordValue(input.workspaceRunState);
|
|
802
|
+
const senderCursors = Array.isArray(runState?.senderCursors)
|
|
803
|
+
? runState.senderCursors
|
|
804
|
+
: [];
|
|
805
|
+
return normalizeStrings(senderCursors
|
|
806
|
+
.map((entry) => stringValue(recordValue(entry)?.senderId))
|
|
807
|
+
.filter((senderId) => Boolean(senderId)));
|
|
808
|
+
}
|
|
793
809
|
function workspaceCoordinatorInput(input) {
|
|
794
810
|
const coordinatorInput = { ...input };
|
|
811
|
+
const senderIds = coordinatorSenderIds(input);
|
|
812
|
+
if (senderIds.length > 0) {
|
|
813
|
+
coordinatorInput.senderIds = senderIds;
|
|
814
|
+
coordinatorInput.senders = [];
|
|
815
|
+
coordinatorInput.senderNames = [];
|
|
816
|
+
}
|
|
795
817
|
delete coordinatorInput.campaignId;
|
|
796
818
|
delete coordinatorInput.tableId;
|
|
797
819
|
delete coordinatorInput.runId;
|
|
@@ -843,7 +865,9 @@ function withKeywordsHandoffScope(result, scopedInput) {
|
|
|
843
865
|
(handleRunId && typeof handleFence === "number"
|
|
844
866
|
? { runId: handleRunId, fence: handleFence }
|
|
845
867
|
: null);
|
|
868
|
+
const senderIds = coordinatorSenderIds(scopedInput);
|
|
846
869
|
const scopeAdditions = {
|
|
870
|
+
...(senderIds.length > 0 ? { senderIds } : {}),
|
|
847
871
|
...(scopedInput.targetDate ? { targetDate: scopedInput.targetDate } : {}),
|
|
848
872
|
...(scopedInput.untilDate ? { untilDate: scopedInput.untilDate } : {}),
|
|
849
873
|
...(typeof scopedInput.horizonSendDays === "number"
|
|
@@ -4150,7 +4174,7 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
4150
4174
|
intent: targetConfigIntent(continuationTargetConfig) ??
|
|
4151
4175
|
scopedInput.intent ??
|
|
4152
4176
|
"auto",
|
|
4153
|
-
senderIds:
|
|
4177
|
+
senderIds: coordinatorSenderIds(scopedInput),
|
|
4154
4178
|
...(scopedInput.actionTypes
|
|
4155
4179
|
? { actionTypes: scopedInput.actionTypes }
|
|
4156
4180
|
: {}),
|
package/dist/tools/registry.d.ts
CHANGED