@sellable/mcp 0.1.557 → 0.1.558
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 +160 -15
- 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 +91 -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,
|
|
@@ -429,6 +437,43 @@ function normalizeDoneReason(value) {
|
|
|
429
437
|
function runStateProgress(ctx) {
|
|
430
438
|
return recordValue(ctx.runState.progress) ?? {};
|
|
431
439
|
}
|
|
440
|
+
function prepCircuitFrom(value) {
|
|
441
|
+
const root = recordValue(value);
|
|
442
|
+
const result = recordValue(root?.result);
|
|
443
|
+
const progress = recordValue(root?.progress);
|
|
444
|
+
return (recordValue(root?.circuit) ??
|
|
445
|
+
recordValue(result?.circuit) ??
|
|
446
|
+
recordValue(progress?.circuit) ??
|
|
447
|
+
null);
|
|
448
|
+
}
|
|
449
|
+
function openCircuitWaits(circuit, now) {
|
|
450
|
+
if (stringValue(circuit?.state) !== "open")
|
|
451
|
+
return false;
|
|
452
|
+
const nextProbeAt = stringValue(circuit?.nextProbeAt);
|
|
453
|
+
return Boolean(nextProbeAt && Date.parse(nextProbeAt) > now.getTime());
|
|
454
|
+
}
|
|
455
|
+
function activePreparationScopeSatisfied(plan, action) {
|
|
456
|
+
const packet = recordValue(plan.packet) ?? plan;
|
|
457
|
+
const target = recordValue(packet.target) ?? recordValue(plan.target) ?? {};
|
|
458
|
+
const senderId = actionSenderId(action);
|
|
459
|
+
const toolInput = actionToolInput(action);
|
|
460
|
+
const targetDate = stringValue(toolInput.targetDate);
|
|
461
|
+
const refillLaneKey = stringValue(toolInput.refillLaneKey);
|
|
462
|
+
const senderPlans = arrayValue(target.senderRefillPlans).filter(isRecord);
|
|
463
|
+
const ledgers = senderPlans
|
|
464
|
+
.filter((senderPlan) => !senderId || stringValue(senderPlan.senderId) === senderId)
|
|
465
|
+
.flatMap((senderPlan) => arrayValue(senderPlan.laneLedgers).filter(isRecord));
|
|
466
|
+
const matching = ledgers.filter((ledger) => (!senderId || stringValue(ledger.senderId) === senderId) &&
|
|
467
|
+
(!targetDate || stringValue(ledger.targetDate) === targetDate) &&
|
|
468
|
+
(!refillLaneKey || stringValue(ledger.refillLaneKey) === refillLaneKey));
|
|
469
|
+
return (matching.length > 0 &&
|
|
470
|
+
matching.every((ledger) => {
|
|
471
|
+
const checks = recordValue(ledger.identityChecks);
|
|
472
|
+
return (numberValue(ledger.initialAllocatablePrepDeficit) === 0 &&
|
|
473
|
+
checks != null &&
|
|
474
|
+
Object.values(checks).every((value) => value === true));
|
|
475
|
+
}));
|
|
476
|
+
}
|
|
432
477
|
function mergeProgress(ctx, updates) {
|
|
433
478
|
return {
|
|
434
479
|
...runStateProgress(ctx),
|
|
@@ -672,14 +717,28 @@ async function createJournal(input, deps, runId) {
|
|
|
672
717
|
}
|
|
673
718
|
async function startOrResume(input, deps) {
|
|
674
719
|
const initialRunState = buildRunStateFromLocalHints(input.workspaceId);
|
|
720
|
+
const scopedInitialRunState = input.scope
|
|
721
|
+
? {
|
|
722
|
+
...initialRunState,
|
|
723
|
+
scope: input.scope,
|
|
724
|
+
scopeHash: input.scopeHash,
|
|
725
|
+
approval: input.approval,
|
|
726
|
+
}
|
|
727
|
+
: initialRunState;
|
|
675
728
|
const started = await deps.runClient.startRefillRunRemote({
|
|
676
729
|
workspaceId: input.workspaceId,
|
|
677
|
-
config:
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
730
|
+
config: input.scope
|
|
731
|
+
? {
|
|
732
|
+
scope: input.scope,
|
|
733
|
+
scopeHash: input.scopeHash,
|
|
734
|
+
approval: input.approval,
|
|
735
|
+
}
|
|
736
|
+
: {
|
|
737
|
+
intent: input.intent ?? "auto",
|
|
738
|
+
senderIds: input.senderIds,
|
|
739
|
+
approvalMode: input.approvalMode ?? "approve",
|
|
740
|
+
},
|
|
741
|
+
runState: scopedInitialRunState,
|
|
683
742
|
resume: input.resume,
|
|
684
743
|
});
|
|
685
744
|
if (isLeaseLost(started)) {
|
|
@@ -712,7 +771,7 @@ async function startOrResume(input, deps) {
|
|
|
712
771
|
const gate = (stringValue(result.gate) ?? "g0_bootstrap");
|
|
713
772
|
const gateSeq = numberValue(result.gateSeq) ?? 0;
|
|
714
773
|
const runState = recordValue(result.runState) ??
|
|
715
|
-
recordValue(
|
|
774
|
+
recordValue(scopedInitialRunState) ??
|
|
716
775
|
{
|
|
717
776
|
version: 1,
|
|
718
777
|
senderCursors: [],
|
|
@@ -755,6 +814,7 @@ async function gateBootstrap(input, deps, ctx) {
|
|
|
755
814
|
senderIds: input.senderIds,
|
|
756
815
|
runState: ctx.runState,
|
|
757
816
|
journal: false,
|
|
817
|
+
...planScopeInput(input),
|
|
758
818
|
});
|
|
759
819
|
const paidCredit = arrayValue(recordValue(plan.bootstrap)?.paidCredit).filter(isRecord);
|
|
760
820
|
const refreshedSenderIds = new Set(arrayValue(recordValue(ctx.runState.creditTrust)?.senderIds)
|
|
@@ -901,6 +961,7 @@ async function gatePlan(input, deps, ctx) {
|
|
|
901
961
|
senderIds: input.senderIds,
|
|
902
962
|
runState: ctx.runState,
|
|
903
963
|
journal: false,
|
|
964
|
+
...planScopeInput(input),
|
|
904
965
|
});
|
|
905
966
|
if (plan.blocker === "workspace_access") {
|
|
906
967
|
return {
|
|
@@ -960,6 +1021,17 @@ async function gatePlan(input, deps, ctx) {
|
|
|
960
1021
|
fallbackSummary: line.fallbackSummary,
|
|
961
1022
|
}));
|
|
962
1023
|
const fingerprint = fingerprintFromPlan(plan);
|
|
1024
|
+
const persistedCircuit = recordValue(runStateProgress(ctx).prepCircuit);
|
|
1025
|
+
if (stringValue(action.type) === "prepare_messages" &&
|
|
1026
|
+
openCircuitWaits(persistedCircuit, deps.now?.() ?? new Date())) {
|
|
1027
|
+
const heartbeat = await heartbeatAndContinue(input, deps, ctx);
|
|
1028
|
+
if (heartbeat.status !== "in_progress")
|
|
1029
|
+
return heartbeat;
|
|
1030
|
+
return {
|
|
1031
|
+
...heartbeat,
|
|
1032
|
+
guidance: `Preparation circuit remains open until ${String(persistedCircuit?.nextProbeAt)}; re-invoke with this handle after the probe deadline.`,
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
963
1035
|
const noProgress = nextNoProgressState(ctx, plan, action);
|
|
964
1036
|
if (noProgress.repeatCount >= 3) {
|
|
965
1037
|
return completeTerminal(input, deps, ctx, "blocked", {
|
|
@@ -970,10 +1042,17 @@ async function gatePlan(input, deps, ctx) {
|
|
|
970
1042
|
guidance: "The same refill rung repeated three times with no coverage, frontier, or lane movement.",
|
|
971
1043
|
});
|
|
972
1044
|
}
|
|
1045
|
+
const circuitProgress = stringValue(action.type) === "prepare_messages" &&
|
|
1046
|
+
stringValue(persistedCircuit?.state) === "open"
|
|
1047
|
+
? { ...persistedCircuit, state: "half_open" }
|
|
1048
|
+
: persistedCircuit;
|
|
973
1049
|
const nextRunState = mergeRunState(ctx.runState, {
|
|
974
1050
|
lastPlan: fingerprint,
|
|
975
1051
|
headAction: action,
|
|
976
|
-
progress: mergeProgress(ctx, {
|
|
1052
|
+
progress: mergeProgress(ctx, {
|
|
1053
|
+
noProgress,
|
|
1054
|
+
...(circuitProgress ? { prepCircuit: circuitProgress } : {}),
|
|
1055
|
+
}),
|
|
977
1056
|
});
|
|
978
1057
|
const advanced = await deps.runClient.advanceRefillRunGateRemote({
|
|
979
1058
|
workspaceId: input.workspaceId,
|
|
@@ -1010,6 +1089,7 @@ async function verifyRecoveryPoint(input, deps, ctx) {
|
|
|
1010
1089
|
senderIds: input.senderIds,
|
|
1011
1090
|
runState: ctx.runState,
|
|
1012
1091
|
journal: false,
|
|
1092
|
+
...planScopeInput(input),
|
|
1013
1093
|
});
|
|
1014
1094
|
const changed = fingerprintChanged(expected, fingerprintFromPlan(fresh));
|
|
1015
1095
|
if (changed.length > 0) {
|
|
@@ -1079,6 +1159,9 @@ async function gateExecute(input, deps, ctx) {
|
|
|
1079
1159
|
dispatchKey: stringValue(action.type),
|
|
1080
1160
|
evergreenRung: stringValue(action.evergreenRung),
|
|
1081
1161
|
sideEffectClass: stringValue(action.sideEffectClass),
|
|
1162
|
+
scopeHash: input.scopeHash ?? null,
|
|
1163
|
+
expectedTargetsHash: stringValue(input.scope?.expectedTargetsHash) ?? null,
|
|
1164
|
+
dateSelector: recordValue(input.scope?.dateSelector),
|
|
1082
1165
|
};
|
|
1083
1166
|
const plannedResult = await deps.runClient.recordRunPlanned({
|
|
1084
1167
|
workspaceId: input.workspaceId,
|
|
@@ -1172,6 +1255,12 @@ async function advanceBackToPlan(input, deps, ctx, runStateUpdates = {}) {
|
|
|
1172
1255
|
async function verifyPrepareAction(input, deps, ctx, action, budgets) {
|
|
1173
1256
|
const getStatus = deps.executors.getPrepareCampaignMessagesStatus;
|
|
1174
1257
|
const jobId = activeJobIdFromOutcome(ctx.runState.lastOutcome);
|
|
1258
|
+
const executionCircuit = prepCircuitFrom(ctx.runState.lastOutcome);
|
|
1259
|
+
if (!jobId && executionCircuit) {
|
|
1260
|
+
return advanceBackToPlan(input, deps, ctx, {
|
|
1261
|
+
progress: mergeProgress(ctx, { prepCircuit: executionCircuit }),
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1175
1264
|
if (!getStatus || !jobId) {
|
|
1176
1265
|
return {
|
|
1177
1266
|
status: "in_progress",
|
|
@@ -1211,6 +1300,58 @@ async function verifyPrepareAction(input, deps, ctx, action, budgets) {
|
|
|
1211
1300
|
}),
|
|
1212
1301
|
});
|
|
1213
1302
|
}
|
|
1303
|
+
if (ownJob &&
|
|
1304
|
+
!isTerminalJobStatus(latestStatus) &&
|
|
1305
|
+
deps.executors.stopSatisfiedPrepareCampaignMessages) {
|
|
1306
|
+
const fresh = await deps.readPlan({
|
|
1307
|
+
workspaceId: input.workspaceId,
|
|
1308
|
+
intent: input.intent,
|
|
1309
|
+
senderIds: input.senderIds,
|
|
1310
|
+
runState: ctx.runState,
|
|
1311
|
+
journal: false,
|
|
1312
|
+
...planScopeInput(input),
|
|
1313
|
+
});
|
|
1314
|
+
ctx.plan = fresh;
|
|
1315
|
+
if (activePreparationScopeSatisfied(fresh, action)) {
|
|
1316
|
+
const plannedStop = await deps.runClient.recordRunPlanned({
|
|
1317
|
+
workspaceId: input.workspaceId,
|
|
1318
|
+
runId: ctx.runId,
|
|
1319
|
+
fence: ctx.fence,
|
|
1320
|
+
gate: ctx.gate,
|
|
1321
|
+
gateSeq: ctx.gateSeq,
|
|
1322
|
+
planned: {
|
|
1323
|
+
toolName: "stop_satisfied_campaign_message_preparation",
|
|
1324
|
+
toolInput: { jobId },
|
|
1325
|
+
dispatchKey: `stop_satisfied:${jobId}`,
|
|
1326
|
+
sideEffectClass: "message_preparation_stop",
|
|
1327
|
+
scopeHash: input.scopeHash ?? null,
|
|
1328
|
+
},
|
|
1329
|
+
});
|
|
1330
|
+
if (isLeaseLost(plannedStop))
|
|
1331
|
+
return leaseLostResult(ctx);
|
|
1332
|
+
const stopEventId = stringValue(recordValue(plannedStop)?.eventId) ??
|
|
1333
|
+
`${ctx.runId}:${ctx.gateSeq}:stop`;
|
|
1334
|
+
const stopReceipt = await deps.executors.stopSatisfiedPrepareCampaignMessages({
|
|
1335
|
+
workspaceId: input.workspaceId,
|
|
1336
|
+
jobId,
|
|
1337
|
+
expectedUpdatedAt: stringValue(statusRecord.updatedAt) ?? undefined,
|
|
1338
|
+
});
|
|
1339
|
+
await deps.runClient.recordRunOutcome({
|
|
1340
|
+
workspaceId: input.workspaceId,
|
|
1341
|
+
runId: ctx.runId,
|
|
1342
|
+
fence: ctx.fence,
|
|
1343
|
+
eventId: stopEventId,
|
|
1344
|
+
did: { jobId },
|
|
1345
|
+
outcome: stopReceipt,
|
|
1346
|
+
});
|
|
1347
|
+
return advanceBackToPlan(input, deps, ctx, {
|
|
1348
|
+
progress: mergeProgress(ctx, {
|
|
1349
|
+
prepStopReceipt: stopReceipt,
|
|
1350
|
+
prepStoppedAt: (deps.now?.() ?? new Date()).toISOString(),
|
|
1351
|
+
}),
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1214
1355
|
if (isTerminalJobStatus(latestStatus)) {
|
|
1215
1356
|
const preparedCount = preparedCountFromStatus(latestStatus);
|
|
1216
1357
|
const checkedRows = checkedRowsFromStatus(latestStatus);
|
|
@@ -1278,6 +1419,7 @@ async function verifyReadOnlyWait(input, deps, ctx, budgets, waitKind) {
|
|
|
1278
1419
|
senderIds: input.senderIds,
|
|
1279
1420
|
runState: ctx.runState,
|
|
1280
1421
|
journal: false,
|
|
1422
|
+
...planScopeInput(input),
|
|
1281
1423
|
});
|
|
1282
1424
|
ctx.plan = fresh;
|
|
1283
1425
|
const doneReason = doneReasonFromPlan(fresh);
|
|
@@ -1360,6 +1502,7 @@ async function verifySchedulerWait(input, deps, ctx, action, budgets) {
|
|
|
1360
1502
|
senderIds: input.senderIds,
|
|
1361
1503
|
runState: ctx.runState,
|
|
1362
1504
|
journal: false,
|
|
1505
|
+
...planScopeInput(input),
|
|
1363
1506
|
});
|
|
1364
1507
|
ctx.plan = fresh;
|
|
1365
1508
|
const doneReason = normalizeDoneReason(doneReasonFromPlan(fresh));
|
|
@@ -1422,6 +1565,7 @@ async function gateVerify(input, deps, ctx, budgets) {
|
|
|
1422
1565
|
senderIds: input.senderIds,
|
|
1423
1566
|
runState: ctx.runState,
|
|
1424
1567
|
journal: false,
|
|
1568
|
+
...planScopeInput(input),
|
|
1425
1569
|
});
|
|
1426
1570
|
ctx.plan = fresh;
|
|
1427
1571
|
const doneReason = doneReasonFromPlan(fresh);
|
|
@@ -1474,19 +1618,19 @@ function classifyCaughtError(error, ctx) {
|
|
|
1474
1618
|
};
|
|
1475
1619
|
}
|
|
1476
1620
|
async function heartbeatAndContinue(input, deps, ctx) {
|
|
1477
|
-
const
|
|
1621
|
+
const yielded = await deps.runClient.yieldRefillRun({
|
|
1478
1622
|
workspaceId: input.workspaceId,
|
|
1479
1623
|
runId: ctx.runId,
|
|
1480
1624
|
fence: ctx.fence,
|
|
1481
1625
|
});
|
|
1482
|
-
if (isLeaseLost(
|
|
1626
|
+
if (isLeaseLost(yielded))
|
|
1483
1627
|
return leaseLostResult(ctx);
|
|
1484
1628
|
return {
|
|
1485
1629
|
status: "in_progress",
|
|
1486
1630
|
runId: ctx.runId,
|
|
1487
1631
|
fence: ctx.fence,
|
|
1488
1632
|
gate: ctx.gate,
|
|
1489
|
-
guidance: "Per-invocation gate budget reached. Re-invoke
|
|
1633
|
+
guidance: "Per-invocation gate budget reached. Re-invoke public refill_sends with this durable run handle to continue.",
|
|
1490
1634
|
journalPath: ctx.journalPath,
|
|
1491
1635
|
};
|
|
1492
1636
|
}
|
|
@@ -1500,6 +1644,7 @@ export async function runRefillV2Loop(input, deps) {
|
|
|
1500
1644
|
intent: input.intent,
|
|
1501
1645
|
senderIds: input.senderIds,
|
|
1502
1646
|
journal: true,
|
|
1647
|
+
...planScopeInput(input),
|
|
1503
1648
|
});
|
|
1504
1649
|
return { status: "dry_run", plan };
|
|
1505
1650
|
}
|
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
|
+
}
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getApi } from "../api.js";
|
|
2
|
-
import { getConfig } from "../auth.js";
|
|
2
|
+
import { getConfig, getEffectiveConfiguredWorkspaceId } from "../auth.js";
|
|
3
3
|
import { assertCreateCampaignPromptLoaded, assertNetNewCreateCampaignResearchReady, } from "./flow-preflight.js";
|
|
4
4
|
import { setCampaignInteractionMode, } from "./interaction-mode.js";
|
|
5
5
|
import { isLinkedInProfileInput, normalizeLinkedInProfileInput, } from "./linkedin-url.js";
|
|
@@ -114,7 +114,7 @@ function normalizeLeadSourceProvider(input) {
|
|
|
114
114
|
: null;
|
|
115
115
|
}
|
|
116
116
|
export function buildWatchUrl(config, path) {
|
|
117
|
-
const workspaceId = config
|
|
117
|
+
const workspaceId = getEffectiveConfiguredWorkspaceId(config);
|
|
118
118
|
const url = new URL(path, config.apiUrl);
|
|
119
119
|
if (workspaceId && !url.searchParams.has("workspaceId")) {
|
|
120
120
|
url.searchParams.set("workspaceId", workspaceId);
|