@sellable/mcp 0.1.557 → 0.1.559
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/README.md +2 -13
- package/agents/registry.json +2 -2
- package/dist/api.js +6 -3
- package/dist/auth.d.ts +6 -0
- package/dist/auth.js +44 -2
- package/dist/refill-contract.d.ts +157 -0
- package/dist/refill-contract.js +487 -0
- package/dist/refill-run-client.d.ts +10 -0
- package/dist/refill-run-client.js +22 -0
- package/dist/refill-run-loop.d.ts +14 -2
- package/dist/refill-run-loop.js +169 -18
- package/dist/server.js +0 -23
- package/dist/tools/auth.d.ts +5 -0
- package/dist/tools/auth.js +49 -12
- package/dist/tools/campaign-message-preparation.d.ts +62 -0
- package/dist/tools/campaign-message-preparation.js +41 -0
- package/dist/tools/campaigns.js +2 -2
- package/dist/tools/csv-dnc.js +2 -2
- package/dist/tools/evergreen-refill-plan.d.ts +3 -0
- package/dist/tools/evergreen-refill-plan.js +29 -7
- package/dist/tools/leads.d.ts +32 -317
- package/dist/tools/leads.js +10 -171
- package/dist/tools/model-quality.js +6 -4
- package/dist/tools/prompts.d.ts +3 -3
- package/dist/tools/prompts.js +15 -7
- package/dist/tools/provider-preflight.d.ts +2 -65
- package/dist/tools/provider-preflight.js +10 -97
- package/dist/tools/readiness.d.ts +5 -89
- package/dist/tools/readiness.js +0 -66
- package/dist/tools/refill-executors.d.ts +38 -0
- package/dist/tools/refill-executors.js +222 -3
- package/dist/tools/refill-sends-v2.d.ts +118 -1
- package/dist/tools/refill-sends-v2.js +312 -3
- package/dist/tools/refill-sends.d.ts +678 -32
- package/dist/tools/refill-sends.js +274 -13
- package/dist/tools/refill-target-plan.js +486 -14
- package/dist/tools/registry.d.ts +115 -330
- package/dist/tools/registry.js +1 -7
- package/dist/tools/scheduler-fill-capacity.js +1 -1
- package/dist/tools/scheduler-run.d.ts +71 -0
- package/dist/tools/scheduler-run.js +203 -1
- package/dist/tools/setup-evergreen-campaigns.js +1 -1
- package/dist/tools/workspace-context.d.ts +1 -1
- package/dist/tools/workspace-context.js +8 -3
- package/dist/tools/workspace-export.js +2 -2
- package/dist/tools/workspaces.d.ts +48 -2
- package/dist/tools/workspaces.js +48 -5
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +3 -3
- package/skills/create-campaign-v2/SKILL.md +1 -1
- package/skills/create-evergreen-campaigns/SKILL.md +16 -16
- package/skills/find-leads/SKILL.md +630 -48
- package/skills/refill-sends/SKILL.md +94 -353
- package/skills/refill-sends-v2/SKILL.md +6 -6
- package/skills/refill-sends-v2-workflow/SKILL.md +5 -5
- package/skills/refill-sends-v2-workflow/core/flow.v1.json +8 -8
- package/skills/refill-sends-workflow/SKILL.md +100 -743
- package/skills/refill-sends-workflow/core/contract.v2.json +543 -0
- package/skills/refill-sends-workflow/core/flow.v1.json +185 -1
- package/dist/tools/find-leads-runs.d.ts +0 -151
- package/dist/tools/find-leads-runs.js +0 -98
- package/skills/find-leads-v2/SKILL.md +0 -70
- package/skills/find-leads-v2/core/flow.v1.json +0 -31
package/dist/refill-run-loop.js
CHANGED
|
@@ -59,6 +59,16 @@ const SCHEDULER_RUN_NEXT_ACTIONS = new Set([
|
|
|
59
59
|
function isRecord(value) {
|
|
60
60
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
61
61
|
}
|
|
62
|
+
function planScopeInput(input) {
|
|
63
|
+
return {
|
|
64
|
+
...(input.targetDate ? { targetDate: input.targetDate } : {}),
|
|
65
|
+
...(input.untilDate ? { untilDate: input.untilDate } : {}),
|
|
66
|
+
...(input.horizonSendDays !== undefined
|
|
67
|
+
? { horizonSendDays: input.horizonSendDays }
|
|
68
|
+
: {}),
|
|
69
|
+
...(input.scopeHash ? { scopeHash: input.scopeHash } : {}),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
62
72
|
function stringValue(value) {
|
|
63
73
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
64
74
|
}
|
|
@@ -256,10 +266,8 @@ function sanitizeSchedulerRunReceipt(raw) {
|
|
|
256
266
|
const prefilteredCells = numberValue(rawReceipt.prefilteredCells) ?? 0;
|
|
257
267
|
const tablesFilteredForSenderMismatch = numberValue(rawReceipt.tablesFilteredForSenderMismatch) ?? 0;
|
|
258
268
|
dominantSkipReason = dominantSchedulerRunSkipReason(skipReasons);
|
|
259
|
-
dominantPrefilterReason =
|
|
260
|
-
|
|
261
|
-
dominantDeferredReason =
|
|
262
|
-
dominantSchedulerRunSkipReason(deferredReasons);
|
|
269
|
+
dominantPrefilterReason = dominantSchedulerRunCountReason(prefilterReasons);
|
|
270
|
+
dominantDeferredReason = dominantSchedulerRunSkipReason(deferredReasons);
|
|
263
271
|
recommendedNextAction = schedulerRunRecommendedAction({
|
|
264
272
|
rawAction: stringValue(rawReceipt.nextAction),
|
|
265
273
|
readyCellsFound,
|
|
@@ -353,11 +361,17 @@ function actionSenderId(action) {
|
|
|
353
361
|
}
|
|
354
362
|
function fingerprintFromPlan(plan) {
|
|
355
363
|
const packet = recordValue(plan.packet) ?? {};
|
|
364
|
+
const targetShapeRevision = stringValue(packet.targetShapeRevision) ??
|
|
365
|
+
stringValue(packet.targetRevision) ??
|
|
366
|
+
stringValue(plan.targetShapeRevision) ??
|
|
367
|
+
stringValue(plan.targetRevision);
|
|
356
368
|
return {
|
|
357
|
-
|
|
369
|
+
// Evergreen packets carry their own semantic plan revision. Regular
|
|
370
|
+
// active-campaign packets intentionally do not, so their immutable target
|
|
371
|
+
// shape is the durable planning revision for fencing and recovery.
|
|
372
|
+
planRevision: stringValue(plan.planRevision) ?? targetShapeRevision,
|
|
358
373
|
stateRevision: stringValue(plan.stateRevision) ?? stringValue(packet.stateRevision),
|
|
359
|
-
targetShapeRevision
|
|
360
|
-
stringValue(packet.targetRevision),
|
|
374
|
+
targetShapeRevision,
|
|
361
375
|
};
|
|
362
376
|
}
|
|
363
377
|
function fingerprintChanged(expected, actual) {
|
|
@@ -429,6 +443,43 @@ function normalizeDoneReason(value) {
|
|
|
429
443
|
function runStateProgress(ctx) {
|
|
430
444
|
return recordValue(ctx.runState.progress) ?? {};
|
|
431
445
|
}
|
|
446
|
+
function prepCircuitFrom(value) {
|
|
447
|
+
const root = recordValue(value);
|
|
448
|
+
const result = recordValue(root?.result);
|
|
449
|
+
const progress = recordValue(root?.progress);
|
|
450
|
+
return (recordValue(root?.circuit) ??
|
|
451
|
+
recordValue(result?.circuit) ??
|
|
452
|
+
recordValue(progress?.circuit) ??
|
|
453
|
+
null);
|
|
454
|
+
}
|
|
455
|
+
function openCircuitWaits(circuit, now) {
|
|
456
|
+
if (stringValue(circuit?.state) !== "open")
|
|
457
|
+
return false;
|
|
458
|
+
const nextProbeAt = stringValue(circuit?.nextProbeAt);
|
|
459
|
+
return Boolean(nextProbeAt && Date.parse(nextProbeAt) > now.getTime());
|
|
460
|
+
}
|
|
461
|
+
function activePreparationScopeSatisfied(plan, action) {
|
|
462
|
+
const packet = recordValue(plan.packet) ?? plan;
|
|
463
|
+
const target = recordValue(packet.target) ?? recordValue(plan.target) ?? {};
|
|
464
|
+
const senderId = actionSenderId(action);
|
|
465
|
+
const toolInput = actionToolInput(action);
|
|
466
|
+
const targetDate = stringValue(toolInput.targetDate);
|
|
467
|
+
const refillLaneKey = stringValue(toolInput.refillLaneKey);
|
|
468
|
+
const senderPlans = arrayValue(target.senderRefillPlans).filter(isRecord);
|
|
469
|
+
const ledgers = senderPlans
|
|
470
|
+
.filter((senderPlan) => !senderId || stringValue(senderPlan.senderId) === senderId)
|
|
471
|
+
.flatMap((senderPlan) => arrayValue(senderPlan.laneLedgers).filter(isRecord));
|
|
472
|
+
const matching = ledgers.filter((ledger) => (!senderId || stringValue(ledger.senderId) === senderId) &&
|
|
473
|
+
(!targetDate || stringValue(ledger.targetDate) === targetDate) &&
|
|
474
|
+
(!refillLaneKey || stringValue(ledger.refillLaneKey) === refillLaneKey));
|
|
475
|
+
return (matching.length > 0 &&
|
|
476
|
+
matching.every((ledger) => {
|
|
477
|
+
const checks = recordValue(ledger.identityChecks);
|
|
478
|
+
return (numberValue(ledger.initialAllocatablePrepDeficit) === 0 &&
|
|
479
|
+
checks != null &&
|
|
480
|
+
Object.values(checks).every((value) => value === true));
|
|
481
|
+
}));
|
|
482
|
+
}
|
|
432
483
|
function mergeProgress(ctx, updates) {
|
|
433
484
|
return {
|
|
434
485
|
...runStateProgress(ctx),
|
|
@@ -672,14 +723,28 @@ async function createJournal(input, deps, runId) {
|
|
|
672
723
|
}
|
|
673
724
|
async function startOrResume(input, deps) {
|
|
674
725
|
const initialRunState = buildRunStateFromLocalHints(input.workspaceId);
|
|
726
|
+
const scopedInitialRunState = input.scope
|
|
727
|
+
? {
|
|
728
|
+
...initialRunState,
|
|
729
|
+
scope: input.scope,
|
|
730
|
+
scopeHash: input.scopeHash,
|
|
731
|
+
approval: input.approval,
|
|
732
|
+
}
|
|
733
|
+
: initialRunState;
|
|
675
734
|
const started = await deps.runClient.startRefillRunRemote({
|
|
676
735
|
workspaceId: input.workspaceId,
|
|
677
|
-
config:
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
736
|
+
config: input.scope
|
|
737
|
+
? {
|
|
738
|
+
scope: input.scope,
|
|
739
|
+
scopeHash: input.scopeHash,
|
|
740
|
+
approval: input.approval,
|
|
741
|
+
}
|
|
742
|
+
: {
|
|
743
|
+
intent: input.intent ?? "auto",
|
|
744
|
+
senderIds: input.senderIds,
|
|
745
|
+
approvalMode: input.approvalMode ?? "approve",
|
|
746
|
+
},
|
|
747
|
+
runState: scopedInitialRunState,
|
|
683
748
|
resume: input.resume,
|
|
684
749
|
});
|
|
685
750
|
if (isLeaseLost(started)) {
|
|
@@ -712,7 +777,7 @@ async function startOrResume(input, deps) {
|
|
|
712
777
|
const gate = (stringValue(result.gate) ?? "g0_bootstrap");
|
|
713
778
|
const gateSeq = numberValue(result.gateSeq) ?? 0;
|
|
714
779
|
const runState = recordValue(result.runState) ??
|
|
715
|
-
recordValue(
|
|
780
|
+
recordValue(scopedInitialRunState) ??
|
|
716
781
|
{
|
|
717
782
|
version: 1,
|
|
718
783
|
senderCursors: [],
|
|
@@ -755,6 +820,7 @@ async function gateBootstrap(input, deps, ctx) {
|
|
|
755
820
|
senderIds: input.senderIds,
|
|
756
821
|
runState: ctx.runState,
|
|
757
822
|
journal: false,
|
|
823
|
+
...planScopeInput(input),
|
|
758
824
|
});
|
|
759
825
|
const paidCredit = arrayValue(recordValue(plan.bootstrap)?.paidCredit).filter(isRecord);
|
|
760
826
|
const refreshedSenderIds = new Set(arrayValue(recordValue(ctx.runState.creditTrust)?.senderIds)
|
|
@@ -901,6 +967,7 @@ async function gatePlan(input, deps, ctx) {
|
|
|
901
967
|
senderIds: input.senderIds,
|
|
902
968
|
runState: ctx.runState,
|
|
903
969
|
journal: false,
|
|
970
|
+
...planScopeInput(input),
|
|
904
971
|
});
|
|
905
972
|
if (plan.blocker === "workspace_access") {
|
|
906
973
|
return {
|
|
@@ -960,6 +1027,17 @@ async function gatePlan(input, deps, ctx) {
|
|
|
960
1027
|
fallbackSummary: line.fallbackSummary,
|
|
961
1028
|
}));
|
|
962
1029
|
const fingerprint = fingerprintFromPlan(plan);
|
|
1030
|
+
const persistedCircuit = recordValue(runStateProgress(ctx).prepCircuit);
|
|
1031
|
+
if (stringValue(action.type) === "prepare_messages" &&
|
|
1032
|
+
openCircuitWaits(persistedCircuit, deps.now?.() ?? new Date())) {
|
|
1033
|
+
const heartbeat = await heartbeatAndContinue(input, deps, ctx);
|
|
1034
|
+
if (heartbeat.status !== "in_progress")
|
|
1035
|
+
return heartbeat;
|
|
1036
|
+
return {
|
|
1037
|
+
...heartbeat,
|
|
1038
|
+
guidance: `Preparation circuit remains open until ${String(persistedCircuit?.nextProbeAt)}; re-invoke with this handle after the probe deadline.`,
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
963
1041
|
const noProgress = nextNoProgressState(ctx, plan, action);
|
|
964
1042
|
if (noProgress.repeatCount >= 3) {
|
|
965
1043
|
return completeTerminal(input, deps, ctx, "blocked", {
|
|
@@ -970,10 +1048,17 @@ async function gatePlan(input, deps, ctx) {
|
|
|
970
1048
|
guidance: "The same refill rung repeated three times with no coverage, frontier, or lane movement.",
|
|
971
1049
|
});
|
|
972
1050
|
}
|
|
1051
|
+
const circuitProgress = stringValue(action.type) === "prepare_messages" &&
|
|
1052
|
+
stringValue(persistedCircuit?.state) === "open"
|
|
1053
|
+
? { ...persistedCircuit, state: "half_open" }
|
|
1054
|
+
: persistedCircuit;
|
|
973
1055
|
const nextRunState = mergeRunState(ctx.runState, {
|
|
974
1056
|
lastPlan: fingerprint,
|
|
975
1057
|
headAction: action,
|
|
976
|
-
progress: mergeProgress(ctx, {
|
|
1058
|
+
progress: mergeProgress(ctx, {
|
|
1059
|
+
noProgress,
|
|
1060
|
+
...(circuitProgress ? { prepCircuit: circuitProgress } : {}),
|
|
1061
|
+
}),
|
|
977
1062
|
});
|
|
978
1063
|
const advanced = await deps.runClient.advanceRefillRunGateRemote({
|
|
979
1064
|
workspaceId: input.workspaceId,
|
|
@@ -1010,6 +1095,7 @@ async function verifyRecoveryPoint(input, deps, ctx) {
|
|
|
1010
1095
|
senderIds: input.senderIds,
|
|
1011
1096
|
runState: ctx.runState,
|
|
1012
1097
|
journal: false,
|
|
1098
|
+
...planScopeInput(input),
|
|
1013
1099
|
});
|
|
1014
1100
|
const changed = fingerprintChanged(expected, fingerprintFromPlan(fresh));
|
|
1015
1101
|
if (changed.length > 0) {
|
|
@@ -1079,6 +1165,9 @@ async function gateExecute(input, deps, ctx) {
|
|
|
1079
1165
|
dispatchKey: stringValue(action.type),
|
|
1080
1166
|
evergreenRung: stringValue(action.evergreenRung),
|
|
1081
1167
|
sideEffectClass: stringValue(action.sideEffectClass),
|
|
1168
|
+
scopeHash: input.scopeHash ?? null,
|
|
1169
|
+
expectedTargetsHash: stringValue(input.scope?.expectedTargetsHash) ?? null,
|
|
1170
|
+
dateSelector: recordValue(input.scope?.dateSelector),
|
|
1082
1171
|
};
|
|
1083
1172
|
const plannedResult = await deps.runClient.recordRunPlanned({
|
|
1084
1173
|
workspaceId: input.workspaceId,
|
|
@@ -1172,6 +1261,12 @@ async function advanceBackToPlan(input, deps, ctx, runStateUpdates = {}) {
|
|
|
1172
1261
|
async function verifyPrepareAction(input, deps, ctx, action, budgets) {
|
|
1173
1262
|
const getStatus = deps.executors.getPrepareCampaignMessagesStatus;
|
|
1174
1263
|
const jobId = activeJobIdFromOutcome(ctx.runState.lastOutcome);
|
|
1264
|
+
const executionCircuit = prepCircuitFrom(ctx.runState.lastOutcome);
|
|
1265
|
+
if (!jobId && executionCircuit) {
|
|
1266
|
+
return advanceBackToPlan(input, deps, ctx, {
|
|
1267
|
+
progress: mergeProgress(ctx, { prepCircuit: executionCircuit }),
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1175
1270
|
if (!getStatus || !jobId) {
|
|
1176
1271
|
return {
|
|
1177
1272
|
status: "in_progress",
|
|
@@ -1211,6 +1306,58 @@ async function verifyPrepareAction(input, deps, ctx, action, budgets) {
|
|
|
1211
1306
|
}),
|
|
1212
1307
|
});
|
|
1213
1308
|
}
|
|
1309
|
+
if (ownJob &&
|
|
1310
|
+
!isTerminalJobStatus(latestStatus) &&
|
|
1311
|
+
deps.executors.stopSatisfiedPrepareCampaignMessages) {
|
|
1312
|
+
const fresh = await deps.readPlan({
|
|
1313
|
+
workspaceId: input.workspaceId,
|
|
1314
|
+
intent: input.intent,
|
|
1315
|
+
senderIds: input.senderIds,
|
|
1316
|
+
runState: ctx.runState,
|
|
1317
|
+
journal: false,
|
|
1318
|
+
...planScopeInput(input),
|
|
1319
|
+
});
|
|
1320
|
+
ctx.plan = fresh;
|
|
1321
|
+
if (activePreparationScopeSatisfied(fresh, action)) {
|
|
1322
|
+
const plannedStop = await deps.runClient.recordRunPlanned({
|
|
1323
|
+
workspaceId: input.workspaceId,
|
|
1324
|
+
runId: ctx.runId,
|
|
1325
|
+
fence: ctx.fence,
|
|
1326
|
+
gate: ctx.gate,
|
|
1327
|
+
gateSeq: ctx.gateSeq,
|
|
1328
|
+
planned: {
|
|
1329
|
+
toolName: "stop_satisfied_campaign_message_preparation",
|
|
1330
|
+
toolInput: { jobId },
|
|
1331
|
+
dispatchKey: `stop_satisfied:${jobId}`,
|
|
1332
|
+
sideEffectClass: "message_preparation_stop",
|
|
1333
|
+
scopeHash: input.scopeHash ?? null,
|
|
1334
|
+
},
|
|
1335
|
+
});
|
|
1336
|
+
if (isLeaseLost(plannedStop))
|
|
1337
|
+
return leaseLostResult(ctx);
|
|
1338
|
+
const stopEventId = stringValue(recordValue(plannedStop)?.eventId) ??
|
|
1339
|
+
`${ctx.runId}:${ctx.gateSeq}:stop`;
|
|
1340
|
+
const stopReceipt = await deps.executors.stopSatisfiedPrepareCampaignMessages({
|
|
1341
|
+
workspaceId: input.workspaceId,
|
|
1342
|
+
jobId,
|
|
1343
|
+
expectedUpdatedAt: stringValue(statusRecord.updatedAt) ?? undefined,
|
|
1344
|
+
});
|
|
1345
|
+
await deps.runClient.recordRunOutcome({
|
|
1346
|
+
workspaceId: input.workspaceId,
|
|
1347
|
+
runId: ctx.runId,
|
|
1348
|
+
fence: ctx.fence,
|
|
1349
|
+
eventId: stopEventId,
|
|
1350
|
+
did: { jobId },
|
|
1351
|
+
outcome: stopReceipt,
|
|
1352
|
+
});
|
|
1353
|
+
return advanceBackToPlan(input, deps, ctx, {
|
|
1354
|
+
progress: mergeProgress(ctx, {
|
|
1355
|
+
prepStopReceipt: stopReceipt,
|
|
1356
|
+
prepStoppedAt: (deps.now?.() ?? new Date()).toISOString(),
|
|
1357
|
+
}),
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1214
1361
|
if (isTerminalJobStatus(latestStatus)) {
|
|
1215
1362
|
const preparedCount = preparedCountFromStatus(latestStatus);
|
|
1216
1363
|
const checkedRows = checkedRowsFromStatus(latestStatus);
|
|
@@ -1278,6 +1425,7 @@ async function verifyReadOnlyWait(input, deps, ctx, budgets, waitKind) {
|
|
|
1278
1425
|
senderIds: input.senderIds,
|
|
1279
1426
|
runState: ctx.runState,
|
|
1280
1427
|
journal: false,
|
|
1428
|
+
...planScopeInput(input),
|
|
1281
1429
|
});
|
|
1282
1430
|
ctx.plan = fresh;
|
|
1283
1431
|
const doneReason = doneReasonFromPlan(fresh);
|
|
@@ -1360,6 +1508,7 @@ async function verifySchedulerWait(input, deps, ctx, action, budgets) {
|
|
|
1360
1508
|
senderIds: input.senderIds,
|
|
1361
1509
|
runState: ctx.runState,
|
|
1362
1510
|
journal: false,
|
|
1511
|
+
...planScopeInput(input),
|
|
1363
1512
|
});
|
|
1364
1513
|
ctx.plan = fresh;
|
|
1365
1514
|
const doneReason = normalizeDoneReason(doneReasonFromPlan(fresh));
|
|
@@ -1422,6 +1571,7 @@ async function gateVerify(input, deps, ctx, budgets) {
|
|
|
1422
1571
|
senderIds: input.senderIds,
|
|
1423
1572
|
runState: ctx.runState,
|
|
1424
1573
|
journal: false,
|
|
1574
|
+
...planScopeInput(input),
|
|
1425
1575
|
});
|
|
1426
1576
|
ctx.plan = fresh;
|
|
1427
1577
|
const doneReason = doneReasonFromPlan(fresh);
|
|
@@ -1474,19 +1624,19 @@ function classifyCaughtError(error, ctx) {
|
|
|
1474
1624
|
};
|
|
1475
1625
|
}
|
|
1476
1626
|
async function heartbeatAndContinue(input, deps, ctx) {
|
|
1477
|
-
const
|
|
1627
|
+
const yielded = await deps.runClient.yieldRefillRun({
|
|
1478
1628
|
workspaceId: input.workspaceId,
|
|
1479
1629
|
runId: ctx.runId,
|
|
1480
1630
|
fence: ctx.fence,
|
|
1481
1631
|
});
|
|
1482
|
-
if (isLeaseLost(
|
|
1632
|
+
if (isLeaseLost(yielded))
|
|
1483
1633
|
return leaseLostResult(ctx);
|
|
1484
1634
|
return {
|
|
1485
1635
|
status: "in_progress",
|
|
1486
1636
|
runId: ctx.runId,
|
|
1487
1637
|
fence: ctx.fence,
|
|
1488
1638
|
gate: ctx.gate,
|
|
1489
|
-
guidance: "Per-invocation gate budget reached. Re-invoke
|
|
1639
|
+
guidance: "Per-invocation gate budget reached. Re-invoke public refill_sends with this durable run handle to continue.",
|
|
1490
1640
|
journalPath: ctx.journalPath,
|
|
1491
1641
|
};
|
|
1492
1642
|
}
|
|
@@ -1500,6 +1650,7 @@ export async function runRefillV2Loop(input, deps) {
|
|
|
1500
1650
|
intent: input.intent,
|
|
1501
1651
|
senderIds: input.senderIds,
|
|
1502
1652
|
journal: true,
|
|
1653
|
+
...planScopeInput(input),
|
|
1503
1654
|
});
|
|
1504
1655
|
return { status: "dry_run", plan };
|
|
1505
1656
|
}
|
package/dist/server.js
CHANGED
|
@@ -29,7 +29,6 @@ import { getEngageStateTool, setEngageStateTool, } from "./tools/engage-state.js
|
|
|
29
29
|
import { bulkEnrichWithProspeo, enrichWithProspeo, getProspeoCredits, } from "./tools/enrichment.js";
|
|
30
30
|
import { getRefillPlanV2 } from "./tools/evergreen-refill-plan.js";
|
|
31
31
|
import { getCampaignFramework } from "./tools/framework.js";
|
|
32
|
-
import { bootstrapFindLeads, cancelFindLeads, getFindLeadsRun, reissueFindLeadsWatchLink, updateFindLeadsRun, waitForFindLeadsRun, } from "./tools/find-leads-runs.js";
|
|
33
32
|
import { confirmHarvestJobCompanies, searchHarvestJobs, } from "./tools/harvest-jobs.js";
|
|
34
33
|
import { checkInboxReplyEligibility, getInboxThread, searchInboxThreads, sendInboxDraft, sendInboxManualReply, updateInboxDraft, } from "./tools/inbox.js";
|
|
35
34
|
import { cancelLeadImport, confirmLeadList, confirmProspeoCompanyAccounts, getProviderPrompt, importLeads, listDncEntriesTool, loadCsvDncEntriesTool, loadCsvDomains, loadCsvLinkedinLeads, lookupSalesNavFilter, saveDomainFilters, searchApollo, searchProspeo, searchProspeoCompanies, searchSalesNav, searchSignals, selectPromisingPosts, setHeadlineICPCriteria, } from "./tools/leads.js";
|
|
@@ -37,7 +36,6 @@ import { fetchCompany, fetchCompanyPosts, fetchLinkedInPosts, fetchLinkedInProfi
|
|
|
37
36
|
import { getCampaignNavigationState } from "./tools/navigation.js";
|
|
38
37
|
import { addOnDemandLeads, createOnDemandCampaign, createOnDemandTable, initOnDemandSequence, pauseOnDemandCampaign, startOnDemandCampaign, } from "./tools/one-off.js";
|
|
39
38
|
import { upsertRubric } from "./tools/processing.js";
|
|
40
|
-
import { preflightFindLeadsProvider } from "./tools/provider-preflight.js";
|
|
41
39
|
import { completeSenderResearch, getPostFindLeadsScoutRegistry, getSourceScoutRegistry, getSubskillAsset, getSubskillPrompt, listSubskillPrompts, searchSubskillPrompts, } from "./tools/prompts.js";
|
|
42
40
|
import { waitForCampaignTableReady, waitForLeadListReady, } from "./tools/readiness.js";
|
|
43
41
|
import { refreshSenderEngagementCommand } from "./tools/refresh-sender-engagement.js";
|
|
@@ -148,27 +146,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
148
146
|
case "wait_for_cli_login":
|
|
149
147
|
result = await handleWaitForCliLogin(args);
|
|
150
148
|
break;
|
|
151
|
-
case "bootstrap_find_leads":
|
|
152
|
-
result = await bootstrapFindLeads(args);
|
|
153
|
-
break;
|
|
154
|
-
case "get_find_leads_run":
|
|
155
|
-
result = await getFindLeadsRun(args);
|
|
156
|
-
break;
|
|
157
|
-
case "update_find_leads_run":
|
|
158
|
-
result = await updateFindLeadsRun(args);
|
|
159
|
-
break;
|
|
160
|
-
case "wait_for_find_leads_run":
|
|
161
|
-
result = await waitForFindLeadsRun(args);
|
|
162
|
-
break;
|
|
163
|
-
case "cancel_find_leads":
|
|
164
|
-
result = await cancelFindLeads(args);
|
|
165
|
-
break;
|
|
166
|
-
case "reissue_find_leads_watch_link":
|
|
167
|
-
result = await reissueFindLeadsWatchLink(args);
|
|
168
|
-
break;
|
|
169
|
-
case "preflight_find_leads_provider":
|
|
170
|
-
result = await preflightFindLeadsProvider(args);
|
|
171
|
-
break;
|
|
172
149
|
case "bootstrap_engage":
|
|
173
150
|
result = await bootstrapEngage(args);
|
|
174
151
|
break;
|
package/dist/tools/auth.d.ts
CHANGED
|
@@ -11,6 +11,11 @@ export type AuthStatus = {
|
|
|
11
11
|
tokenPresent: boolean;
|
|
12
12
|
tokenPrefix: string | null;
|
|
13
13
|
workspacesCount: number | null;
|
|
14
|
+
workspaceLock: {
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
workspaceId: string | null;
|
|
17
|
+
required: boolean;
|
|
18
|
+
};
|
|
14
19
|
checkedAt: string;
|
|
15
20
|
/** Short message the LLM MUST show the user after auth succeeds. */
|
|
16
21
|
_userNotice: string | null;
|
package/dist/tools/auth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import { getApi, SellableApiError } from "../api.js";
|
|
3
|
-
import { getConfig, getConfigPath, getResolvedConfigsDir } from "../auth.js";
|
|
3
|
+
import { getConfig, getConfigPath, getEffectiveConfiguredWorkspaceId, getLockedWorkspaceId, getResolvedConfigsDir, isWorkspaceLockRequired, workspaceLockRequiredMessage, } from "../auth.js";
|
|
4
4
|
import { checkForUpdates } from "../update-check.js";
|
|
5
5
|
function maskToken(token) {
|
|
6
6
|
if (token.length <= 10)
|
|
@@ -38,6 +38,8 @@ export async function getAuthStatus() {
|
|
|
38
38
|
const checkedAt = new Date().toISOString();
|
|
39
39
|
const update = await getUpdateStatus();
|
|
40
40
|
let tokenPresent = false;
|
|
41
|
+
const workspaceLockRequired = isWorkspaceLockRequired();
|
|
42
|
+
const lockedWorkspaceId = getLockedWorkspaceId();
|
|
41
43
|
const base = {
|
|
42
44
|
ok: false,
|
|
43
45
|
configPath,
|
|
@@ -50,27 +52,47 @@ export async function getAuthStatus() {
|
|
|
50
52
|
tokenPresent: false,
|
|
51
53
|
tokenPrefix: null,
|
|
52
54
|
workspacesCount: null,
|
|
55
|
+
workspaceLock: {
|
|
56
|
+
enabled: Boolean(lockedWorkspaceId),
|
|
57
|
+
workspaceId: lockedWorkspaceId,
|
|
58
|
+
required: workspaceLockRequired,
|
|
59
|
+
},
|
|
53
60
|
_userNotice: appendUpdateNotice(null, update),
|
|
54
61
|
update,
|
|
55
62
|
checkedAt,
|
|
56
63
|
};
|
|
64
|
+
if (workspaceLockRequired && !lockedWorkspaceId) {
|
|
65
|
+
return {
|
|
66
|
+
...base,
|
|
67
|
+
error: {
|
|
68
|
+
type: "workspace",
|
|
69
|
+
message: "Customer profile workspace lock is missing.",
|
|
70
|
+
guidance: workspaceLockRequiredMessage("get_auth_status"),
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
57
74
|
try {
|
|
58
75
|
const config = getConfig();
|
|
59
76
|
tokenPresent = Boolean(config.token);
|
|
60
77
|
base.activeEnvName = config.activeEnvName || null;
|
|
61
78
|
const api = getApi();
|
|
62
79
|
const { workspaces } = await api.get("/api/v3/workspaces");
|
|
63
|
-
const activeWorkspaceId = config
|
|
80
|
+
const activeWorkspaceId = getEffectiveConfiguredWorkspaceId(config);
|
|
64
81
|
const activeWorkspace = activeWorkspaceId
|
|
65
82
|
? workspaces.find((ws) => ws.id === activeWorkspaceId)
|
|
66
83
|
: null;
|
|
84
|
+
const visibleWorkspacesCount = lockedWorkspaceId
|
|
85
|
+
? activeWorkspace
|
|
86
|
+
? 1
|
|
87
|
+
: 0
|
|
88
|
+
: workspaces.length;
|
|
67
89
|
if (!activeWorkspaceId) {
|
|
68
90
|
return {
|
|
69
91
|
...base,
|
|
70
92
|
apiUrl: config.apiUrl || null,
|
|
71
93
|
tokenPresent,
|
|
72
94
|
tokenPrefix: maskToken(config.token),
|
|
73
|
-
workspacesCount:
|
|
95
|
+
workspacesCount: visibleWorkspacesCount,
|
|
74
96
|
error: {
|
|
75
97
|
type: "workspace",
|
|
76
98
|
message: "No active workspace selected.",
|
|
@@ -87,22 +109,26 @@ export async function getAuthStatus() {
|
|
|
87
109
|
apiUrl: config.apiUrl || null,
|
|
88
110
|
tokenPresent,
|
|
89
111
|
tokenPrefix: maskToken(config.token),
|
|
90
|
-
workspacesCount:
|
|
112
|
+
workspacesCount: visibleWorkspacesCount,
|
|
91
113
|
error: {
|
|
92
114
|
type: "workspace",
|
|
93
115
|
message: `Active workspace ${activeWorkspaceId} not found in access list.`,
|
|
94
|
-
guidance:
|
|
95
|
-
?
|
|
96
|
-
:
|
|
116
|
+
guidance: lockedWorkspaceId
|
|
117
|
+
? "This profile is locked to a workspace that the configured Sellable token cannot access. Re-provision the customer profile from sellable-admin with the correct workspace token."
|
|
118
|
+
: available
|
|
119
|
+
? `Available: ${available}. Run set_active_workspace with a valid ID.`
|
|
120
|
+
: "Run list_workspaces to view available workspaces, then set_active_workspace.",
|
|
97
121
|
},
|
|
98
122
|
};
|
|
99
123
|
}
|
|
100
124
|
const workspaceName = activeWorkspace.name || config.activeWorkspaceName || null;
|
|
101
125
|
const envLabel = config.activeEnvName || null;
|
|
102
126
|
const wsLabel = workspaceName || activeWorkspaceId;
|
|
103
|
-
const
|
|
104
|
-
? `You're
|
|
105
|
-
:
|
|
127
|
+
const noticePrefix = lockedWorkspaceId
|
|
128
|
+
? `You're locked to ${wsLabel}.`
|
|
129
|
+
: envLabel
|
|
130
|
+
? `You're in ${wsLabel} (${envLabel}).`
|
|
131
|
+
: `You're in ${wsLabel}.`;
|
|
106
132
|
return {
|
|
107
133
|
ok: true,
|
|
108
134
|
configPath,
|
|
@@ -114,8 +140,19 @@ export async function getAuthStatus() {
|
|
|
114
140
|
activeWorkspaceName: workspaceName,
|
|
115
141
|
tokenPresent,
|
|
116
142
|
tokenPrefix: maskToken(config.token),
|
|
117
|
-
workspacesCount:
|
|
118
|
-
|
|
143
|
+
workspacesCount: visibleWorkspacesCount,
|
|
144
|
+
workspaceLock: lockedWorkspaceId
|
|
145
|
+
? {
|
|
146
|
+
enabled: true,
|
|
147
|
+
workspaceId: lockedWorkspaceId,
|
|
148
|
+
required: workspaceLockRequired,
|
|
149
|
+
}
|
|
150
|
+
: {
|
|
151
|
+
enabled: false,
|
|
152
|
+
workspaceId: null,
|
|
153
|
+
required: workspaceLockRequired,
|
|
154
|
+
},
|
|
155
|
+
_userNotice: appendUpdateNotice(noticePrefix, update),
|
|
119
156
|
update,
|
|
120
157
|
checkedAt,
|
|
121
158
|
error: null,
|
|
@@ -36,9 +36,16 @@ type StartPrepareMessagesInput = PrepareMessagesBaseInput & {
|
|
|
36
36
|
requestSource?: string;
|
|
37
37
|
senderId?: string;
|
|
38
38
|
actionType?: string;
|
|
39
|
+
targetDate?: string;
|
|
40
|
+
refillLaneKey?: string;
|
|
41
|
+
sourceEpoch?: string;
|
|
39
42
|
};
|
|
40
43
|
type StatusPrepareMessagesInput = PrepareMessagesBaseInput;
|
|
41
44
|
type CancelPrepareMessagesInput = PrepareMessagesBaseInput;
|
|
45
|
+
type StopSatisfiedPrepareMessagesInput = PrepareMessagesBaseInput & {
|
|
46
|
+
jobId: string;
|
|
47
|
+
expectedUpdatedAt?: string;
|
|
48
|
+
};
|
|
42
49
|
export declare const campaignMessagePreparationToolDefinitions: ({
|
|
43
50
|
name: string;
|
|
44
51
|
description: string;
|
|
@@ -117,6 +124,18 @@ export declare const campaignMessagePreparationToolDefinitions: ({
|
|
|
117
124
|
enum: string[];
|
|
118
125
|
description: string;
|
|
119
126
|
};
|
|
127
|
+
targetDate: {
|
|
128
|
+
type: string;
|
|
129
|
+
description: string;
|
|
130
|
+
};
|
|
131
|
+
refillLaneKey: {
|
|
132
|
+
type: string;
|
|
133
|
+
description: string;
|
|
134
|
+
};
|
|
135
|
+
sourceEpoch: {
|
|
136
|
+
type: string;
|
|
137
|
+
description: string;
|
|
138
|
+
};
|
|
120
139
|
disableLowPassRateStop: {
|
|
121
140
|
type: string;
|
|
122
141
|
description: string;
|
|
@@ -126,6 +145,7 @@ export declare const campaignMessagePreparationToolDefinitions: ({
|
|
|
126
145
|
description: string;
|
|
127
146
|
};
|
|
128
147
|
jobId?: undefined;
|
|
148
|
+
expectedUpdatedAt?: undefined;
|
|
129
149
|
};
|
|
130
150
|
required: string[];
|
|
131
151
|
additionalProperties: boolean;
|
|
@@ -160,13 +180,55 @@ export declare const campaignMessagePreparationToolDefinitions: ({
|
|
|
160
180
|
autoContinue?: undefined;
|
|
161
181
|
senderId?: undefined;
|
|
162
182
|
actionType?: undefined;
|
|
183
|
+
targetDate?: undefined;
|
|
184
|
+
refillLaneKey?: undefined;
|
|
185
|
+
sourceEpoch?: undefined;
|
|
163
186
|
disableLowPassRateStop?: undefined;
|
|
187
|
+
expectedUpdatedAt?: undefined;
|
|
164
188
|
};
|
|
165
189
|
additionalProperties: boolean;
|
|
166
190
|
required?: undefined;
|
|
167
191
|
};
|
|
192
|
+
} | {
|
|
193
|
+
name: string;
|
|
194
|
+
description: string;
|
|
195
|
+
inputSchema: {
|
|
196
|
+
type: string;
|
|
197
|
+
properties: {
|
|
198
|
+
jobId: {
|
|
199
|
+
type: string;
|
|
200
|
+
};
|
|
201
|
+
expectedUpdatedAt: {
|
|
202
|
+
type: string;
|
|
203
|
+
description: string;
|
|
204
|
+
};
|
|
205
|
+
workspaceId: {
|
|
206
|
+
type: string;
|
|
207
|
+
description?: undefined;
|
|
208
|
+
};
|
|
209
|
+
campaignId?: undefined;
|
|
210
|
+
tableId?: undefined;
|
|
211
|
+
targetPreparedMessages?: undefined;
|
|
212
|
+
maxRowsToCheck?: undefined;
|
|
213
|
+
batchSize?: undefined;
|
|
214
|
+
maxBatchRows?: undefined;
|
|
215
|
+
approvalMode?: undefined;
|
|
216
|
+
rowSelector?: undefined;
|
|
217
|
+
excludeRowIds?: undefined;
|
|
218
|
+
autoContinue?: undefined;
|
|
219
|
+
senderId?: undefined;
|
|
220
|
+
actionType?: undefined;
|
|
221
|
+
targetDate?: undefined;
|
|
222
|
+
refillLaneKey?: undefined;
|
|
223
|
+
sourceEpoch?: undefined;
|
|
224
|
+
disableLowPassRateStop?: undefined;
|
|
225
|
+
};
|
|
226
|
+
required: string[];
|
|
227
|
+
additionalProperties: boolean;
|
|
228
|
+
};
|
|
168
229
|
})[];
|
|
169
230
|
export declare function startPrepareCampaignMessages(input: StartPrepareMessagesInput): Promise<unknown>;
|
|
170
231
|
export declare function getPrepareCampaignMessagesStatus(input: StatusPrepareMessagesInput): Promise<unknown>;
|
|
171
232
|
export declare function cancelPrepareCampaignMessages(input: CancelPrepareMessagesInput): Promise<unknown>;
|
|
233
|
+
export declare function stopSatisfiedPrepareCampaignMessages(input: StopSatisfiedPrepareMessagesInput): Promise<unknown>;
|
|
172
234
|
export {};
|
|
@@ -84,6 +84,18 @@ export const campaignMessagePreparationToolDefinitions = [
|
|
|
84
84
|
],
|
|
85
85
|
description: "Optional refill-scope send action. InMail scopes treat open and paid InMail as one contact-reuse family.",
|
|
86
86
|
},
|
|
87
|
+
targetDate: {
|
|
88
|
+
type: "string",
|
|
89
|
+
description: "Exact sender-local YYYY-MM-DD refill target date.",
|
|
90
|
+
},
|
|
91
|
+
refillLaneKey: {
|
|
92
|
+
type: "string",
|
|
93
|
+
description: "Normalized refill lane identity from the canonical target plan.",
|
|
94
|
+
},
|
|
95
|
+
sourceEpoch: {
|
|
96
|
+
type: "string",
|
|
97
|
+
description: "Stable source/provider epoch; only a material epoch change resets zero-yield recurrence.",
|
|
98
|
+
},
|
|
87
99
|
disableLowPassRateStop: {
|
|
88
100
|
type: "boolean",
|
|
89
101
|
description: "Operator override for explicit fill/exhaust requests. When true, keep processing eligible rows even if the enriched sample pass rate is low. Still bounded by targetPreparedMessages and maxRowsToCheck.",
|
|
@@ -111,6 +123,23 @@ export const campaignMessagePreparationToolDefinitions = [
|
|
|
111
123
|
additionalProperties: false,
|
|
112
124
|
},
|
|
113
125
|
},
|
|
126
|
+
{
|
|
127
|
+
name: "stop_satisfied_campaign_message_preparation",
|
|
128
|
+
description: "Cooperatively stop exactly one preparation job after the server rereads its persisted refill scope and proves its allocatable preparation deficit is zero. This never cancels table-wide workflow cells.",
|
|
129
|
+
inputSchema: {
|
|
130
|
+
type: "object",
|
|
131
|
+
properties: {
|
|
132
|
+
jobId: { type: "string" },
|
|
133
|
+
expectedUpdatedAt: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: "Optional optimistic job updatedAt revision from status.",
|
|
136
|
+
},
|
|
137
|
+
workspaceId: { type: "string" },
|
|
138
|
+
},
|
|
139
|
+
required: ["jobId"],
|
|
140
|
+
additionalProperties: false,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
114
143
|
{
|
|
115
144
|
name: "cancel_campaign_message_preparation",
|
|
116
145
|
description: "Cancel an active campaign message preparation job and cancel the same pending workflow-table cells that the UI Cancel Pending Cells button would cancel. Launch remains a separate explicit start_campaign step.",
|
|
@@ -145,6 +174,9 @@ export function startPrepareCampaignMessages(input) {
|
|
|
145
174
|
requestSource: input.requestSource,
|
|
146
175
|
senderId: input.senderId,
|
|
147
176
|
actionType: input.actionType,
|
|
177
|
+
targetDate: input.targetDate,
|
|
178
|
+
refillLaneKey: input.refillLaneKey,
|
|
179
|
+
sourceEpoch: input.sourceEpoch,
|
|
148
180
|
...(workspaceId ? { workspaceId } : {}),
|
|
149
181
|
}, workspaceId ?? undefined);
|
|
150
182
|
}
|
|
@@ -168,3 +200,12 @@ export function cancelPrepareCampaignMessages(input) {
|
|
|
168
200
|
...(workspaceId ? { workspaceId } : {}),
|
|
169
201
|
}, workspaceId ?? undefined);
|
|
170
202
|
}
|
|
203
|
+
export function stopSatisfiedPrepareCampaignMessages(input) {
|
|
204
|
+
const workspaceId = normalizeExplicitWorkspaceId(input.workspaceId);
|
|
205
|
+
return postPrepareMessages({
|
|
206
|
+
action: "stop_if_target_satisfied",
|
|
207
|
+
jobId: input.jobId,
|
|
208
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
209
|
+
...(workspaceId ? { workspaceId } : {}),
|
|
210
|
+
}, workspaceId ?? undefined);
|
|
211
|
+
}
|