@sellable/mcp 0.1.554 → 0.1.555
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/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/refill-contract.d.ts +157 -0
- package/dist/refill-contract.js +487 -0
- package/dist/refill-run-client.d.ts +5 -0
- package/dist/refill-run-client.js +15 -0
- package/dist/refill-run-loop.d.ts +12 -1
- package/dist/refill-run-loop.js +158 -13
- package/dist/tools/campaign-message-preparation.d.ts +62 -0
- package/dist/tools/campaign-message-preparation.js +41 -0
- package/dist/tools/evergreen-refill-plan.d.ts +3 -0
- package/dist/tools/evergreen-refill-plan.js +29 -7
- package/dist/tools/prompts.js +9 -0
- 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 +112 -1
- package/dist/tools/refill-sends-v2.js +302 -2
- 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 +96 -27
- package/dist/tools/registry.js +1 -3
- 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/workspaces.d.ts +4 -6
- package/dist/tools/workspaces.js +11 -13
- package/package.json +1 -1
- 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/refill-date-window.d.ts +0 -34
- package/dist/refill-date-window.js +0 -210
- package/dist/tools/refill-sends-evergreen.d.ts +0 -28
- package/dist/tools/refill-sends-evergreen.js +0 -47
- package/skills/research/config.json +0 -9
|
@@ -15,6 +15,21 @@ function parseBlockerBody(body) {
|
|
|
15
15
|
gateSeq: typeof parsed.gateSeq === "number" ? parsed.gateSeq : undefined,
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
+
if ((parsed.blocker === "scope_drift" ||
|
|
19
|
+
parsed.blocker === "approval_expired" ||
|
|
20
|
+
parsed.blocker === "approval_fingerprint_mismatch") &&
|
|
21
|
+
typeof parsed.runId === "string") {
|
|
22
|
+
return {
|
|
23
|
+
blocker: parsed.blocker,
|
|
24
|
+
runId: parsed.runId,
|
|
25
|
+
expectedScopeHash: typeof parsed.expectedScopeHash === "string"
|
|
26
|
+
? parsed.expectedScopeHash
|
|
27
|
+
: null,
|
|
28
|
+
receivedScopeHash: typeof parsed.receivedScopeHash === "string"
|
|
29
|
+
? parsed.receivedScopeHash
|
|
30
|
+
: null,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
18
33
|
}
|
|
19
34
|
catch {
|
|
20
35
|
return null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { appendIndexLine, appendJournalEvent, createRunJournal, renderBootstrapSection, renderCrashedAbandonedSection, renderExecutionEventSection, renderPlanSection, renderTerminalSection } from "./refill-journal.js";
|
|
2
2
|
import type { advanceRefillRunGateRemote, completeRefillRun, heartbeatRefillRun, recordRunOutcome, recordRunPlanned, refillRunStatus, startRefillRunRemote } from "./refill-run-client.js";
|
|
3
|
-
import type { getPrepareCampaignMessagesStatus } from "./tools/campaign-message-preparation.js";
|
|
3
|
+
import type { getPrepareCampaignMessagesStatus, stopSatisfiedPrepareCampaignMessages } from "./tools/campaign-message-preparation.js";
|
|
4
4
|
import type { executeOneYoloPrimitive, executeStartCampaignPrimitive, prepareRowSelectorValue, refillPrepareRequestHash, refreshPaidInmailCreditsWithRetry } from "./tools/refill-executors.js";
|
|
5
5
|
export type RefillLoopGate = "g0_bootstrap" | "g1_plan" | "g2_execute" | "g3_verify" | "g4_terminal";
|
|
6
6
|
export interface RefillV2LoopInput {
|
|
@@ -13,6 +13,12 @@ export interface RefillV2LoopInput {
|
|
|
13
13
|
};
|
|
14
14
|
approvalMode?: "approve" | "mark_ready";
|
|
15
15
|
dryRun?: boolean;
|
|
16
|
+
targetDate?: string;
|
|
17
|
+
untilDate?: string;
|
|
18
|
+
horizonSendDays?: number;
|
|
19
|
+
scope?: Record<string, unknown>;
|
|
20
|
+
scopeHash?: string;
|
|
21
|
+
approval?: Record<string, unknown>;
|
|
16
22
|
}
|
|
17
23
|
export interface RefillV2LoopBudgets {
|
|
18
24
|
maxGateCyclesPerInvocation: number;
|
|
@@ -36,6 +42,7 @@ export interface RefillV2Executors {
|
|
|
36
42
|
executeStartCampaignPrimitive: typeof executeStartCampaignPrimitive;
|
|
37
43
|
refreshPaidInmailCreditsWithRetry: typeof refreshPaidInmailCreditsWithRetry;
|
|
38
44
|
getPrepareCampaignMessagesStatus?: typeof getPrepareCampaignMessagesStatus;
|
|
45
|
+
stopSatisfiedPrepareCampaignMessages?: typeof stopSatisfiedPrepareCampaignMessages;
|
|
39
46
|
refillPrepareRequestHash?: typeof refillPrepareRequestHash;
|
|
40
47
|
prepareRowSelectorValue?: typeof prepareRowSelectorValue;
|
|
41
48
|
}
|
|
@@ -61,6 +68,10 @@ export interface RefillV2LoopDeps {
|
|
|
61
68
|
senderIds?: string[];
|
|
62
69
|
runState?: Record<string, unknown>;
|
|
63
70
|
journal?: boolean;
|
|
71
|
+
targetDate?: string;
|
|
72
|
+
untilDate?: string;
|
|
73
|
+
horizonSendDays?: number;
|
|
74
|
+
scopeHash?: string;
|
|
64
75
|
}) => Promise<Record<string, unknown>>;
|
|
65
76
|
executors: RefillV2Executors;
|
|
66
77
|
journal: RefillV2JournalDeps;
|
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);
|
|
@@ -1486,7 +1630,7 @@ async function heartbeatAndContinue(input, deps, ctx) {
|
|
|
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
|
}
|
|
@@ -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
|
+
}
|
|
@@ -2,6 +2,9 @@ type GetRefillPlanV2Input = {
|
|
|
2
2
|
workspaceId?: string;
|
|
3
3
|
intent?: "auto" | "evergreen" | "plain" | "active";
|
|
4
4
|
senderIds?: string[];
|
|
5
|
+
horizonSendDays?: number;
|
|
6
|
+
untilDate?: string;
|
|
7
|
+
targetDate?: string;
|
|
5
8
|
runState?: Record<string, unknown>;
|
|
6
9
|
journal?: boolean;
|
|
7
10
|
journalNote?: string;
|
|
@@ -2,6 +2,7 @@ import * as path from "node:path";
|
|
|
2
2
|
import { getApi, SellableApiError } from "../api.js";
|
|
3
3
|
import { appendIndexLine, appendJournalEvent, createRunJournal, renderBootstrapSection, renderPlanSection, renderTerminalSection, } from "../refill-journal.js";
|
|
4
4
|
import { readRefillWorkspaceState } from "../refill-local-state.js";
|
|
5
|
+
import { getRefillTargetPlan } from "./refill-target-plan.js";
|
|
5
6
|
import { normalizeExplicitWorkspaceId, workspaceRequestOptions, } from "./workspace-context.js";
|
|
6
7
|
const KNOWN_TOP_LEVEL_FIELDS = [
|
|
7
8
|
"readOnly",
|
|
@@ -199,12 +200,24 @@ export async function getRefillPlanV2(input) {
|
|
|
199
200
|
: buildRunStateFromLocalHints(workspaceId);
|
|
200
201
|
let raw;
|
|
201
202
|
try {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
203
|
+
const hasExplicitDateSelector = input.targetDate !== undefined ||
|
|
204
|
+
input.untilDate !== undefined ||
|
|
205
|
+
input.horizonSendDays !== undefined;
|
|
206
|
+
raw = hasExplicitDateSelector
|
|
207
|
+
? await getRefillTargetPlan({
|
|
208
|
+
workspaceId,
|
|
209
|
+
intent: input.intent === "auto" ? undefined : (input.intent ?? undefined),
|
|
210
|
+
senderIds: input.senderIds,
|
|
211
|
+
targetDate: input.targetDate,
|
|
212
|
+
untilDate: input.untilDate,
|
|
213
|
+
horizonSendDays: input.horizonSendDays,
|
|
214
|
+
})
|
|
215
|
+
: await postRefillPlanV2({
|
|
216
|
+
workspaceId,
|
|
217
|
+
intent: input.intent ?? "auto",
|
|
218
|
+
senderIds: input.senderIds,
|
|
219
|
+
runState,
|
|
220
|
+
}, workspaceId);
|
|
208
221
|
}
|
|
209
222
|
catch (error) {
|
|
210
223
|
if (!isWorkspaceAccessError(error))
|
|
@@ -238,7 +251,16 @@ export async function getRefillPlanV2(input) {
|
|
|
238
251
|
workspaceResolution: "explicit",
|
|
239
252
|
};
|
|
240
253
|
}
|
|
241
|
-
const
|
|
254
|
+
const rawRecord = isRecord(raw) ? raw : {};
|
|
255
|
+
const target = isRecord(rawRecord.target) ? rawRecord.target : null;
|
|
256
|
+
const sanitized = sanitizeEvergreenRefillPlanResult(target
|
|
257
|
+
? {
|
|
258
|
+
...rawRecord,
|
|
259
|
+
plans: target.senderRefillPlans,
|
|
260
|
+
globalActionQueue: target.globalActionQueue,
|
|
261
|
+
packet: rawRecord,
|
|
262
|
+
}
|
|
263
|
+
: rawRecord);
|
|
242
264
|
const warnings = Array.isArray(sanitized.warnings)
|
|
243
265
|
? [...sanitized.warnings]
|
|
244
266
|
: [];
|
package/dist/tools/prompts.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { getSkillByName, listSkills, resolveSkillsDir, stripFrontmatter, } from "../skills.js";
|
|
4
4
|
import { markCreateCampaignPromptLoaded, markResearchPromptLoaded, markSenderResearchCompleted, } from "./flow-preflight.js";
|
|
5
|
+
import { validateRefillContractAsset, validateRefillFlowProjection, } from "../refill-contract.js";
|
|
5
6
|
// Chunk size sits below Claude Code's max-token cap on tool results
|
|
6
7
|
// (empirically around ~50k chars — fetch_linkedin_profile breached at
|
|
7
8
|
// 53,295 chars, which forced a python3 chunked-read fallback). 48k gives
|
|
@@ -511,6 +512,14 @@ export function getSubskillAsset(subskillName, assetPath, offset, limit) {
|
|
|
511
512
|
throw new Error(`Unknown subskill asset: ${subskillName}/${cleanAssetPath}`);
|
|
512
513
|
}
|
|
513
514
|
const content = fs.readFileSync(resolved, "utf8");
|
|
515
|
+
if (subskillName === "refill-sends-workflow" &&
|
|
516
|
+
cleanAssetPath === "core/contract.v2.json") {
|
|
517
|
+
validateRefillContractAsset(JSON.parse(content));
|
|
518
|
+
}
|
|
519
|
+
if (subskillName === "refill-sends-workflow" &&
|
|
520
|
+
cleanAssetPath === "core/flow.v1.json") {
|
|
521
|
+
validateRefillFlowProjection(JSON.parse(content));
|
|
522
|
+
}
|
|
514
523
|
const chunked = readChunkedContent(content, offset, limit, (nextOffset, safeLimit) => `Continue this same asset load with get_subskill_asset({ subskillName: "${subskillName}", assetPath: "${cleanAssetPath}", offset: ${nextOffset}, limit: ${safeLimit} }) until hasMore is false. Treat all chunks as one asset load; do not use saved local tool-output files or machine-specific paths.`);
|
|
515
524
|
return {
|
|
516
525
|
subskillName,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { searchSignals } from "./leads.js";
|
|
2
|
+
import { type SchedulerExpectedTargetInput } from "./scheduler-run.js";
|
|
2
3
|
export type RefillSendsApprovalMode = "approve" | "mark_ready";
|
|
3
4
|
export type PaidInmailRefreshAction = {
|
|
4
5
|
senderId: string;
|
|
@@ -134,6 +135,43 @@ export declare function sleep(ms: number): Promise<unknown>;
|
|
|
134
135
|
export declare function recordValue(value: unknown): Record<string, unknown> | null;
|
|
135
136
|
export declare function stringValue(value: unknown): string | null;
|
|
136
137
|
export declare function numberValue(value: unknown): number | null;
|
|
138
|
+
export type SchedulerChangedCount = {
|
|
139
|
+
campaignId: string;
|
|
140
|
+
tableId: string;
|
|
141
|
+
actionType: string;
|
|
142
|
+
count: number;
|
|
143
|
+
};
|
|
144
|
+
export type SchedulerChangedCounts = {
|
|
145
|
+
complete: true;
|
|
146
|
+
truncated: false;
|
|
147
|
+
total: number;
|
|
148
|
+
byCampaignTableActionType: SchedulerChangedCount[];
|
|
149
|
+
byCampaignTableActionTypeSender: Array<SchedulerChangedCount & {
|
|
150
|
+
senderId: string;
|
|
151
|
+
}>;
|
|
152
|
+
};
|
|
153
|
+
export declare function normalizeSchedulerChangedCounts(value: unknown): SchedulerChangedCounts | null;
|
|
154
|
+
export declare function normalizeSchedulerPrimitiveResult(value: unknown, expected?: {
|
|
155
|
+
expectedTargets: SchedulerExpectedTargetInput[];
|
|
156
|
+
expectedTargetsHash: string;
|
|
157
|
+
maxPlacements: number;
|
|
158
|
+
}): {
|
|
159
|
+
status: "scheduler_receipt_incomplete";
|
|
160
|
+
schedulerStatus: string;
|
|
161
|
+
receipt: {} | null;
|
|
162
|
+
retryAfterMs: number | null;
|
|
163
|
+
changedCounts: null;
|
|
164
|
+
auditComplete: false;
|
|
165
|
+
blocker: "scheduler_receipt_incomplete";
|
|
166
|
+
} | {
|
|
167
|
+
status: string;
|
|
168
|
+
receipt: {} | null;
|
|
169
|
+
retryAfterMs: number | null;
|
|
170
|
+
changedCounts: SchedulerChangedCounts;
|
|
171
|
+
auditComplete: true;
|
|
172
|
+
schedulerStatus?: undefined;
|
|
173
|
+
blocker?: undefined;
|
|
174
|
+
};
|
|
137
175
|
export declare function stringArray(value: unknown): string[];
|
|
138
176
|
export declare function prepareRowSelectorValue(value: unknown): PrepareRowSelector | undefined;
|
|
139
177
|
export declare function uniqueStrings(values: Array<string | null | undefined>): string[];
|