@sellable/mcp 0.1.957 → 0.1.959
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/agents/post-find-leads-message-scout.md +4 -4
- package/agents/registry.json +1 -1
- package/dist/server.js +2 -2
- package/dist/tools/campaigns.js +5 -4
- package/dist/tools/prompts.d.ts +12 -4
- package/dist/tools/prompts.js +23 -7
- package/dist/tools/refill-v3-advance.js +1 -1
- package/dist/tools/refill-v3-campaign-work.d.ts +6 -0
- package/dist/tools/refill-v3-continue-contract.d.ts +4 -0
- package/dist/tools/refill-v3-continue-contract.js +3 -0
- package/dist/tools/refill-v3-continue.d.ts +18 -5
- package/dist/tools/refill-v3-continue.js +177 -85
- package/dist/tools/registry.d.ts +6 -2
- 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-campaign-v2/SOUL.md +2 -1
- package/skills/create-campaign-v2/core/policy.md +2 -2
- package/skills/create-campaign-v2/references/approval-gate-framing.md +2 -1
- package/skills/create-campaign-v2/references/message-approach.md +25 -16
- package/skills/create-campaign-v2/references/parallel-critique-protocol.md +2 -2
- package/skills/create-campaign-v2/references/sample-validation-loop.md +1 -1
- package/skills/create-campaign-v2-tail/SKILL.md +2 -2
- package/skills/create-campaign-v2-validation/SKILL.md +2 -2
- package/skills/{conversation-chess → draft-message}/SKILL.md +2 -2
- package/skills/{conversation-chess → draft-message}/compiled/follow-up/endpoint.md +1 -1
- package/skills/{conversation-chess → draft-message}/compiled/follow-up/semantic.md +3 -3
- package/skills/{conversation-chess → draft-message}/compiled/manifest.json +22 -22
- package/skills/{conversation-chess → draft-message}/compiled/opener/endpoint.md +1 -1
- package/skills/{conversation-chess → draft-message}/compiled/opener/semantic.md +3 -3
- package/skills/{conversation-chess → draft-message}/compiled/reply/endpoint.md +1 -1
- package/skills/{conversation-chess → draft-message}/compiled/reply/semantic.md +3 -3
- package/skills/{conversation-chess → draft-message}/installed/SKILL.md +7 -6
- package/skills/first-campaign-onboarding/SKILL.md +8 -7
- package/skills/generate-messages/SKILL.md +13 -11
- package/skills/refill-sends/SKILL.md +7 -0
- package/skills/refill-sends-workflow/SKILL.md +7 -0
- package/skills/weekly-campaign-summary/SKILL.md +1 -1
- /package/skills/{conversation-chess → draft-message}/core/input-contract.md +0 -0
- /package/skills/{conversation-chess → draft-message}/core/situation-index.json +0 -0
- /package/skills/{conversation-chess → draft-message}/modes/follow-up.md +0 -0
- /package/skills/{conversation-chess → draft-message}/modes/opener.md +0 -0
- /package/skills/{conversation-chess → draft-message}/modes/reply.md +0 -0
- /package/skills/{conversation-chess → draft-message}/references/follow-ups.md +0 -0
- /package/skills/{conversation-chess → draft-message}/references/opener-editor.md +0 -0
- /package/skills/{conversation-chess → draft-message}/references/openers.md +0 -0
- /package/skills/{conversation-chess → draft-message}/references/rapport.md +0 -0
- /package/skills/{conversation-chess → draft-message}/references/replies.md +0 -0
|
@@ -18,7 +18,7 @@ export const REFILL_V3_CONTINUE_TOOL_NAME = "refill_v3_continue";
|
|
|
18
18
|
export const refillV3ContinueToolDefinitions = [
|
|
19
19
|
{
|
|
20
20
|
name: REFILL_V3_CONTINUE_TOOL_NAME,
|
|
21
|
-
description: "Continue exactly one server-issued Refill V3 campaign-attention packet. continuation_pending means the same operation is still running: wait retryAfterMs and call this tool with identical scope and no decision until it returns. Reconcile transport timeouts the same way; do not call advance or dispatch another search while pending. Call first without decision: after a fresh revalidation, the tool deterministically owns live-job waiting, selected-source row copying, exact actionable enrichment, and same-template message generation. It returns model_decision_required only when the accurate packet needs a genuine repair, criteria, rubric, waterfall, or source-expansion judgment; then call again with that one closed decision. Only an exact fresh packet can dispatch one existing campaign-work authority, which re-observes the exact sender/date and settles before a sibling campaign may advance. The caller cannot supply batch sizes, readiness digests, retry counters, fill inputs, scheduler inputs, or idempotency keys.",
|
|
21
|
+
description: "Continue exactly one server-issued Refill V3 campaign-attention packet. continuation_pending means the same operation is still running: wait retryAfterMs and call this tool with identical scope and no decision until it returns. Reconcile transport timeouts the same way; do not call advance or dispatch another search while pending. Call first without decision: after a fresh revalidation, the tool deterministically owns live-job waiting, selected-source row copying, exact actionable enrichment, and same-template message generation. It returns model_decision_required only when the accurate packet needs a genuine repair, criteria, rubric, waterfall, or source-expansion judgment; then call again with that one closed decision. Only an exact fresh packet can dispatch one existing campaign-work authority, which re-observes the exact sender/date and settles before a sibling campaign may advance. The caller cannot supply batch sizes, readiness digests, retry counters, fill inputs, scheduler inputs, or idempotency keys. A result with recoveredFromError already re-observed after a failure: handle it by its kind. A thrown error is not a terminal: call refill_v3_advance once more with the identical scope and report a blocker only if it fails the same way.",
|
|
22
22
|
inputSchema: REFILL_V3_CONTINUE_INPUT_SCHEMA,
|
|
23
23
|
outputSchema: REFILL_V3_CONTINUE_OUTPUT_SCHEMA,
|
|
24
24
|
annotations: {
|
|
@@ -183,6 +183,33 @@ const DEFAULT_PRODUCTION_OWNERS = {
|
|
|
183
183
|
export async function runRefillV3CampaignWorkWithProductionOwners(input, owners = DEFAULT_PRODUCTION_OWNERS) {
|
|
184
184
|
const decoded = decodeRefillV3CampaignWorkOutput(input.output);
|
|
185
185
|
const chosen = decoded.ok ? decoded.value.actions[0] : null;
|
|
186
|
+
// A source-cycle row is the run's BOOKKEEPING for a source effect that has
|
|
187
|
+
// already happened or been refused; it is never the effect itself. Throwing
|
|
188
|
+
// here on 2026-10-05 discarded Justin's applied 396-row engager import and
|
|
189
|
+
// stopped the Slack run with nothing scheduled. A transient failure is
|
|
190
|
+
// retried in place (retrying the whole stage would re-dispatch the search);
|
|
191
|
+
// any other failure rides on the receipt, and the persisted campaign-work
|
|
192
|
+
// receipt plus the fresh re-observation still guard against a repeat.
|
|
193
|
+
const recordSourceCycle = async (record) => {
|
|
194
|
+
if (!owners.recordSourceCycle)
|
|
195
|
+
return undefined;
|
|
196
|
+
const sleep = owners.sleep ??
|
|
197
|
+
((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
198
|
+
for (let attempt = 0;; attempt += 1) {
|
|
199
|
+
try {
|
|
200
|
+
await owners.recordSourceCycle(record);
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
const retryDelay = POST_ADVANCE_TRANSPORT_RETRY_DELAYS_MS[attempt];
|
|
205
|
+
if (retryDelay === undefined ||
|
|
206
|
+
!isRetryablePostAdvanceTransportError(error)) {
|
|
207
|
+
return error instanceof Error ? error.message : String(error);
|
|
208
|
+
}
|
|
209
|
+
await sleep(retryDelay);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
186
213
|
let diagnosticRows = [];
|
|
187
214
|
const record = (value) => value && typeof value === "object" && !Array.isArray(value)
|
|
188
215
|
? value
|
|
@@ -195,6 +222,9 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
195
222
|
const scopeEcho = `${input.packet.scope.workspaceId}:${input.packet.scope.campaignId}:${input.packet.scope.tableId}`;
|
|
196
223
|
const receipt = (params) => ({
|
|
197
224
|
...(params.batchPlan === undefined ? {} : { batchPlan: params.batchPlan }),
|
|
225
|
+
...(params.sourceCycleRecordError === undefined
|
|
226
|
+
? {}
|
|
227
|
+
: { sourceCycleRecordError: params.sourceCycleRecordError }),
|
|
198
228
|
outcome: params.outcome,
|
|
199
229
|
result: params.result,
|
|
200
230
|
scopeEcho,
|
|
@@ -813,7 +843,10 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
813
843
|
senderId: request.scope.senderId,
|
|
814
844
|
date: request.scope.date,
|
|
815
845
|
lane: input.packet.scope.lane,
|
|
816
|
-
family
|
|
846
|
+
// The family being SEARCHED, which a family switch makes differ from
|
|
847
|
+
// the packet's observed family. Its negative set is the one that
|
|
848
|
+
// guards this dispatch, and the receipt below names the same family.
|
|
849
|
+
family: request.sourceCommand.family,
|
|
817
850
|
protectedFitDigest,
|
|
818
851
|
};
|
|
819
852
|
// The run fact scopes both sides of reconciliation: writes are stamped
|
|
@@ -884,28 +917,27 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
884
917
|
: null,
|
|
885
918
|
});
|
|
886
919
|
if (sourceOutcome.kind === "dispatched") {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
});
|
|
906
|
-
}
|
|
920
|
+
const sourceCycleRecordError = await recordSourceCycle({
|
|
921
|
+
...sourceCycleScope,
|
|
922
|
+
...sourceCycleRunFact,
|
|
923
|
+
receipt: {
|
|
924
|
+
schemaVersion: 1,
|
|
925
|
+
kind: "search",
|
|
926
|
+
campaignId: request.scope.campaignId,
|
|
927
|
+
family: request.sourceCommand.family,
|
|
928
|
+
protectedFitDigest,
|
|
929
|
+
requestFingerprint: request.sourceCommand.requestFingerprint,
|
|
930
|
+
sourceFingerprint: sourceOutcome.receipt.sourceAfterFingerprint,
|
|
931
|
+
keywordBatch: request.sourceCommand.keywordBatch,
|
|
932
|
+
postIds: request.sourceCommand.selectedPostIds,
|
|
933
|
+
importedCount: sourceOutcome.receipt.importedCount,
|
|
934
|
+
yieldEvidence: sourceOutcome.receipt.yieldEvidence,
|
|
935
|
+
observedAt: request.scope.date,
|
|
936
|
+
},
|
|
937
|
+
});
|
|
907
938
|
const changed = sourceOutcome.receipt.importedCount > 0;
|
|
908
939
|
return receipt({
|
|
940
|
+
sourceCycleRecordError,
|
|
909
941
|
outcome: changed ? "applied" : "no_effect",
|
|
910
942
|
result,
|
|
911
943
|
idempotencyKey: request.idempotencyKey,
|
|
@@ -914,27 +946,26 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
914
946
|
});
|
|
915
947
|
}
|
|
916
948
|
if (sourceOutcome.kind === "recorded_exhaustion") {
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
});
|
|
936
|
-
}
|
|
949
|
+
const sourceCycleRecordError = await recordSourceCycle({
|
|
950
|
+
...sourceCycleScope,
|
|
951
|
+
...sourceCycleRunFact,
|
|
952
|
+
receipt: {
|
|
953
|
+
schemaVersion: 1,
|
|
954
|
+
kind: "exhaustion",
|
|
955
|
+
campaignId: request.scope.campaignId,
|
|
956
|
+
family: request.sourceCommand.family,
|
|
957
|
+
protectedFitDigest,
|
|
958
|
+
requestFingerprint: request.idempotencyKey,
|
|
959
|
+
sourceFingerprint: priorSourceFingerprint ?? request.scope.evidenceFingerprint,
|
|
960
|
+
keywordBatch: null,
|
|
961
|
+
postIds: null,
|
|
962
|
+
importedCount: 0,
|
|
963
|
+
yieldEvidence: sourceOutcome.receipt.yieldEvidence,
|
|
964
|
+
observedAt: request.scope.date,
|
|
965
|
+
},
|
|
966
|
+
});
|
|
937
967
|
return receipt({
|
|
968
|
+
sourceCycleRecordError,
|
|
938
969
|
outcome: "no_effect",
|
|
939
970
|
result,
|
|
940
971
|
idempotencyKey: request.idempotencyKey,
|
|
@@ -949,7 +980,7 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
949
980
|
// returning the retained job wait. Previously the pending branch
|
|
950
981
|
// wrote nothing, so the job could settle and disappear while the
|
|
951
982
|
// next advance still advertised the identical round as untried.
|
|
952
|
-
await
|
|
983
|
+
const sourceCycleRecordError = await recordSourceCycle({
|
|
953
984
|
...sourceCycleScope,
|
|
954
985
|
...sourceCycleRunFact,
|
|
955
986
|
receipt: {
|
|
@@ -975,6 +1006,7 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
975
1006
|
// evidence-free timeout: reobserve so the next coordinator step can
|
|
976
1007
|
// wait on (or consume) the exact job without replaying the search.
|
|
977
1008
|
return receipt({
|
|
1009
|
+
sourceCycleRecordError,
|
|
978
1010
|
outcome: "no_effect",
|
|
979
1011
|
result,
|
|
980
1012
|
idempotencyKey: request.idempotencyKey,
|
|
@@ -995,27 +1027,26 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
995
1027
|
// authority, and let the same outer run re-observe/continue. Returning
|
|
996
1028
|
// `receipt_incomplete` here left settlement null and repeated the
|
|
997
1029
|
// same packet while every sibling sender/lane stayed unreachable.
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
});
|
|
1017
|
-
}
|
|
1030
|
+
const sourceCycleRecordError = await recordSourceCycle({
|
|
1031
|
+
...sourceCycleScope,
|
|
1032
|
+
...sourceCycleRunFact,
|
|
1033
|
+
receipt: {
|
|
1034
|
+
schemaVersion: 1,
|
|
1035
|
+
kind: "exhaustion",
|
|
1036
|
+
campaignId: request.scope.campaignId,
|
|
1037
|
+
family: request.sourceCommand.family,
|
|
1038
|
+
protectedFitDigest,
|
|
1039
|
+
requestFingerprint: request.idempotencyKey,
|
|
1040
|
+
sourceFingerprint: priorSourceFingerprint ?? request.scope.evidenceFingerprint,
|
|
1041
|
+
keywordBatch: null,
|
|
1042
|
+
postIds: null,
|
|
1043
|
+
importedCount: 0,
|
|
1044
|
+
yieldEvidence: input.packet.evidence.priorYieldReceipt,
|
|
1045
|
+
observedAt: request.scope.date,
|
|
1046
|
+
},
|
|
1047
|
+
});
|
|
1018
1048
|
return receipt({
|
|
1049
|
+
sourceCycleRecordError,
|
|
1019
1050
|
outcome: "no_effect",
|
|
1020
1051
|
result: "source_frontier_exhausted",
|
|
1021
1052
|
idempotencyKey: request.idempotencyKey,
|
|
@@ -1059,28 +1090,26 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
1059
1090
|
yieldEvidence: input.packet.evidence.priorYieldReceipt,
|
|
1060
1091
|
});
|
|
1061
1092
|
if (exhaustionVerdict.exhausted) {
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
},
|
|
1081
|
-
});
|
|
1082
|
-
}
|
|
1093
|
+
const sourceCycleRecordError = await recordSourceCycle({
|
|
1094
|
+
...sourceCycleScope,
|
|
1095
|
+
...sourceCycleRunFact,
|
|
1096
|
+
receipt: {
|
|
1097
|
+
schemaVersion: 1,
|
|
1098
|
+
kind: "exhaustion",
|
|
1099
|
+
campaignId: request.scope.campaignId,
|
|
1100
|
+
family: request.sourceCommand.family,
|
|
1101
|
+
protectedFitDigest,
|
|
1102
|
+
requestFingerprint: request.idempotencyKey,
|
|
1103
|
+
sourceFingerprint: priorSourceFingerprint ?? request.scope.evidenceFingerprint,
|
|
1104
|
+
keywordBatch: null,
|
|
1105
|
+
postIds: null,
|
|
1106
|
+
importedCount: 0,
|
|
1107
|
+
yieldEvidence: input.packet.evidence.priorYieldReceipt,
|
|
1108
|
+
observedAt: request.scope.date,
|
|
1109
|
+
},
|
|
1110
|
+
});
|
|
1083
1111
|
return receipt({
|
|
1112
|
+
sourceCycleRecordError,
|
|
1084
1113
|
outcome: "no_effect",
|
|
1085
1114
|
result: "source_frontier_exhausted",
|
|
1086
1115
|
idempotencyKey: request.idempotencyKey,
|
|
@@ -1143,12 +1172,13 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
1143
1172
|
});
|
|
1144
1173
|
}
|
|
1145
1174
|
if (request.action === "record_source_frontier_exhausted") {
|
|
1175
|
+
let sourceCycleRecordError;
|
|
1146
1176
|
if (owners.recordSourceCycle) {
|
|
1147
1177
|
const protectedFitDigest = input.packet.evidence.protectedFitDigest ||
|
|
1148
1178
|
`sha256:${createHash("sha256")
|
|
1149
1179
|
.update(input.packet.evidence.campaignObjective)
|
|
1150
1180
|
.digest("hex")}`;
|
|
1151
|
-
await
|
|
1181
|
+
sourceCycleRecordError = await recordSourceCycle({
|
|
1152
1182
|
workspaceId: request.scope.workspaceId,
|
|
1153
1183
|
runToken: input.runToken,
|
|
1154
1184
|
scopeFingerprint: input.scopeFingerprint,
|
|
@@ -1176,6 +1206,7 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
1176
1206
|
});
|
|
1177
1207
|
}
|
|
1178
1208
|
return receipt({
|
|
1209
|
+
sourceCycleRecordError,
|
|
1179
1210
|
outcome: "no_effect",
|
|
1180
1211
|
result,
|
|
1181
1212
|
idempotencyKey: request.idempotencyKey,
|
|
@@ -1368,7 +1399,63 @@ function deterministicCampaignDecision(packet) {
|
|
|
1368
1399
|
consumedDiagnosticRowIds: null,
|
|
1369
1400
|
};
|
|
1370
1401
|
}
|
|
1402
|
+
/**
|
|
1403
|
+
* V33-JUSTIN-SELF-HEAL · an unattended refill never ends on a raw error that
|
|
1404
|
+
* a fresh observation can answer.
|
|
1405
|
+
*
|
|
1406
|
+
* On 2026-10-05 Justin's own-post refresh imported 396 engagers and a
|
|
1407
|
+
* bookkeeping 409 then escaped as a raw error, so the Slack host reported a
|
|
1408
|
+
* blocker with nothing scheduled although the run credential was still
|
|
1409
|
+
* current. Once this continuation holds a refreshed credential, any failure
|
|
1410
|
+
* gets ONE re-observation with it. When the world moved, that observation is
|
|
1411
|
+
* returned like any other and names the error it recovered from. When nothing
|
|
1412
|
+
* moved, the same work would fail the same way, so the original error stands:
|
|
1413
|
+
* a deterministic failure surfaces instead of looping. A failure before the
|
|
1414
|
+
* credential was refreshed is never masked.
|
|
1415
|
+
*/
|
|
1371
1416
|
async function executeRefillV3Continuation(input, dependencies) {
|
|
1417
|
+
const progress = {
|
|
1418
|
+
runToken: null,
|
|
1419
|
+
packet: null,
|
|
1420
|
+
};
|
|
1421
|
+
try {
|
|
1422
|
+
return await executeRefillV3ContinuationStep(input, dependencies, progress);
|
|
1423
|
+
}
|
|
1424
|
+
catch (error) {
|
|
1425
|
+
const runToken = progress.runToken;
|
|
1426
|
+
const failedPacket = progress.packet;
|
|
1427
|
+
if (!runToken || !failedPacket)
|
|
1428
|
+
throw error;
|
|
1429
|
+
let observed;
|
|
1430
|
+
try {
|
|
1431
|
+
observed = await reconnectPostAdvanceStage({
|
|
1432
|
+
sleep: dependencies.sleep,
|
|
1433
|
+
operation: () => dependencies.advance({
|
|
1434
|
+
workspaceId: failedPacket.scope.workspaceId,
|
|
1435
|
+
scope: input.scope,
|
|
1436
|
+
...(input.campaignId === undefined
|
|
1437
|
+
? {}
|
|
1438
|
+
: { campaignId: input.campaignId }),
|
|
1439
|
+
...(input.yolo === undefined ? {} : { yolo: input.yolo }),
|
|
1440
|
+
runToken,
|
|
1441
|
+
}),
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
catch {
|
|
1445
|
+
throw error;
|
|
1446
|
+
}
|
|
1447
|
+
if (observed.kind === "campaign_attention_required" &&
|
|
1448
|
+
samePacket(failedPacket, observed.packet)) {
|
|
1449
|
+
throw error;
|
|
1450
|
+
}
|
|
1451
|
+
return {
|
|
1452
|
+
...continuationResultFromAdvance(observed),
|
|
1453
|
+
recoveredFromError: (error instanceof Error ? error.message : String(error)) ||
|
|
1454
|
+
"unknown_error",
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
async function executeRefillV3ContinuationStep(input, dependencies, progress) {
|
|
1372
1459
|
const workspaceId = normalizeExplicitWorkspaceId(input.workspaceId);
|
|
1373
1460
|
if (!workspaceId || workspaceId !== input.packet.scope.workspaceId) {
|
|
1374
1461
|
throw new Error("refill_v3_continue requires one exact workspace matching the server-issued packet.");
|
|
@@ -1422,6 +1509,8 @@ async function executeRefillV3Continuation(input, dependencies) {
|
|
|
1422
1509
|
if (!runToken || !scopeFingerprint) {
|
|
1423
1510
|
throw new Error("refill_v3_continue got no refreshed run credential from its own internal advance, which already consumed this run's sequence. Refusing to return a spent token.");
|
|
1424
1511
|
}
|
|
1512
|
+
progress.runToken = runToken;
|
|
1513
|
+
progress.packet = advanceResult.packet;
|
|
1425
1514
|
const deterministicDecision = deterministicCampaignDecision(advanceResult.packet);
|
|
1426
1515
|
const decision = deterministicDecision ?? input.decision;
|
|
1427
1516
|
if (decision === undefined) {
|
|
@@ -1642,6 +1731,9 @@ async function executeRefillV3Continuation(input, dependencies) {
|
|
|
1642
1731
|
if (input.yolo === true &&
|
|
1643
1732
|
(preparationOwnsSettlement || directlyQueuedRowsOwnSettlement) &&
|
|
1644
1733
|
campaignSettlementIsReady(settlement)) {
|
|
1734
|
+
// This advance spends the refreshed credential, so a failure past here has
|
|
1735
|
+
// no unspent credential left to re-observe with.
|
|
1736
|
+
progress.runToken = null;
|
|
1645
1737
|
const postSettlementAdvance = await reconnectPostAdvanceStage({
|
|
1646
1738
|
sleep: dependencies.sleep,
|
|
1647
1739
|
operation: () => dependencies.advance({
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -1313,7 +1313,7 @@ export declare const allTools: ({
|
|
|
1313
1313
|
properties: {
|
|
1314
1314
|
subskillName: {
|
|
1315
1315
|
type: string;
|
|
1316
|
-
enum: readonly ["avoid-ai-writing", "building-gtm-tables", "campaign-daily-results", "campaign-daily-review", "campaign-weekly-review", "content", "conversation-chess", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-evergreen-campaigns", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "find-leads-v2", "first-campaign-onboarding", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "refill-sends", "refill-sends-waterfall-order", "refill-sends-work-campaign", "refill-sends-workflow", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
1316
|
+
enum: readonly ["avoid-ai-writing", "building-gtm-tables", "campaign-daily-results", "campaign-daily-review", "campaign-weekly-review", "content", "conversation-chess", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-evergreen-campaigns", "create-post", "create-rubric", "draft-message", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "find-leads-v2", "first-campaign-onboarding", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "refill-sends", "refill-sends-waterfall-order", "refill-sends-work-campaign", "refill-sends-workflow", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
1317
1317
|
description: string;
|
|
1318
1318
|
};
|
|
1319
1319
|
offset: {
|
|
@@ -1348,7 +1348,7 @@ export declare const allTools: ({
|
|
|
1348
1348
|
properties: {
|
|
1349
1349
|
subskillName: {
|
|
1350
1350
|
type: string;
|
|
1351
|
-
enum: readonly ["avoid-ai-writing", "building-gtm-tables", "campaign-daily-results", "campaign-daily-review", "campaign-weekly-review", "content", "conversation-chess", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-evergreen-campaigns", "create-post", "create-rubric", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "find-leads-v2", "first-campaign-onboarding", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "refill-sends", "refill-sends-waterfall-order", "refill-sends-work-campaign", "refill-sends-workflow", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
1351
|
+
enum: readonly ["avoid-ai-writing", "building-gtm-tables", "campaign-daily-results", "campaign-daily-review", "campaign-weekly-review", "content", "conversation-chess", "create-ab-test", "create-campaign", "create-campaign-brief", "create-campaign-v2", "create-campaign-v2-tail", "create-campaign-v2-validation", "create-evergreen-campaigns", "create-post", "create-rubric", "draft-message", "engage", "enrich-prospects", "fill-send-horizon", "find-leads", "find-leads-v2", "first-campaign-onboarding", "foundation", "generate-messages", "interview", "load-voice", "refresh-sender-engagement", "refill-sends", "refill-sends-waterfall-order", "refill-sends-work-campaign", "refill-sends-workflow", "research", "research-prospect", "research-sender", "weekly-campaign-summary", "workflow-sequences"];
|
|
1352
1352
|
description: string;
|
|
1353
1353
|
};
|
|
1354
1354
|
assetPath: {
|
|
@@ -11516,6 +11516,10 @@ export declare const allTools: ({
|
|
|
11516
11516
|
readonly receiptPersistence: {
|
|
11517
11517
|
readonly type: "object";
|
|
11518
11518
|
};
|
|
11519
|
+
readonly recoveredFromError: {
|
|
11520
|
+
readonly type: "string";
|
|
11521
|
+
readonly minLength: 1;
|
|
11522
|
+
};
|
|
11519
11523
|
};
|
|
11520
11524
|
readonly additionalProperties: false;
|
|
11521
11525
|
readonly allOf: readonly [{
|
package/package.json
CHANGED
|
@@ -216,8 +216,8 @@ preparation, the target is wrong, or status shows the wrong campaign/table, call
|
|
|
216
216
|
`cancel_campaign_message_preparation`; otherwise do not cancel a healthy prepare
|
|
217
217
|
run. Never call `start_campaign` from message preparation; final launch remains
|
|
218
218
|
a separate explicit user greenlight.
|
|
219
|
-
|
|
220
|
-
|
|
219
|
+
First messages use Conversations or Message Testing; name the one you pick
|
|
220
|
+
as create-campaign-v2 `references/message-approach.md` says.
|
|
221
221
|
|
|
222
222
|
## DNC And Blocklist Handling
|
|
223
223
|
|
|
@@ -598,7 +598,7 @@ Drafting finishes or the recommendation is reviewed. If the user skips filters,
|
|
|
598
598
|
first, then move to Messages/message review after it has started or returned a
|
|
599
599
|
ready recommendation. `update_campaign({ currentStep: "messages" })` is not
|
|
600
600
|
proof of kickoff. Enrichment/filtering and Generate Message cells wait for
|
|
601
|
-
message approval
|
|
601
|
+
message approval (Message Testing only; Conversations has no template).
|
|
602
602
|
|
|
603
603
|
The Message Drafting handoff must stay lean. Include only `campaignId`,
|
|
604
604
|
`workflowTableId`, a concise brief summary, concise source summary/source-use
|
|
@@ -86,7 +86,7 @@ and carry proof gaps into the brief.
|
|
|
86
86
|
|
|
87
87
|
Before `create_campaign`, load `references/message-approach.md` and choose how
|
|
88
88
|
first messages are written: Conversations (each written for the person, no
|
|
89
|
-
template) or Message
|
|
89
|
+
template) or Message Testing (one approved template). Pass it as `messageApproach`.
|
|
90
90
|
|
|
91
91
|
## Brief Provenance
|
|
92
92
|
|
|
@@ -339,7 +339,8 @@ Leads with durable `currentStep: "apply-icp-rubric"` while Message Drafting
|
|
|
339
339
|
finishes or the template is approved. The join gate is saved-filter approval or
|
|
340
340
|
a resolved skip-filter choice, plus a message recommendation grounded in the
|
|
341
341
|
same campaign/table basis. Enrichment, filtering, and Generate Message cells
|
|
342
|
-
wait for template approval
|
|
342
|
+
wait for template approval in a Message Testing campaign; a Conversations
|
|
343
|
+
campaign has no template (see `references/message-approach.md`).
|
|
343
344
|
Message Drafting does not wait for `save_rubrics`: if filters were chosen but
|
|
344
345
|
saved `leadScoringRubrics` are not visible yet, it should still return the
|
|
345
346
|
initial reusable template from the campaign-table execution slice with
|
|
@@ -39,8 +39,8 @@ mutation gates:
|
|
|
39
39
|
- Copy the confirmed source list into the campaign, but process only the first
|
|
40
40
|
review/process sample before message approval.
|
|
41
41
|
- Do not call `queue_campaign_cells` for enrichment/filtering until filters are
|
|
42
|
-
approved when enabled and
|
|
43
|
-
message template/token rule set.
|
|
42
|
+
approved when enabled and, in a Message Testing campaign, there is an approved
|
|
43
|
+
message template/token rule set (a Conversations campaign has none).
|
|
44
44
|
- Do not attach a sequence or call `start_campaign` until the user explicitly chooses to launch, unless `launchConsent.mode` is `autopilot`: that one-time answer pre-approves every gate in this list, while the quality floors and sample caps still apply.
|
|
45
45
|
- If the user requested an exact scheduled send count, or asked to fill up/load
|
|
46
46
|
sends for attached senders, prepare that bounded count first. Capacity-fill
|
|
@@ -213,7 +213,8 @@ If `message-validation.md` has an extractable `Selected Winner`, the packet's
|
|
|
213
213
|
supported prospect-row `{{tokens}}`. Do not write a fresh approval-template
|
|
214
214
|
message from the brief. If no winner/template can be extracted, this exact-match
|
|
215
215
|
gate is skipped, but the packet still needs a usable approved template unless
|
|
216
|
-
the
|
|
216
|
+
the campaign uses Conversations (`messageApproach: "conversation"`), which has
|
|
217
|
+
no template.
|
|
217
218
|
|
|
218
219
|
Do NOT render the packet as a bundle of opaque validation anchors or a dump
|
|
219
220
|
of raw JSON. The user is approving a live campaign — they need to see what
|
|
@@ -2,25 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
Every campaign writes its first message one of two ways. Decide which before
|
|
4
4
|
`create_campaign`, pass it as `messageApproach`, and say which you picked in
|
|
5
|
-
one plain line the customer can correct.
|
|
5
|
+
one plain line the customer can correct. Use these two names, and no other
|
|
6
|
+
names, for them.
|
|
6
7
|
|
|
7
|
-
- **Conversations** (`messageApproach: "conversation"`). Each first message is
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- **Message
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
- **Conversations** (`messageApproach: "conversation"`). Each first message is written for that one person and starts a real conversation. The pitch waits until they reply.
|
|
9
|
+
Sellable writes each message from the person's profile; it carries no pitch
|
|
10
|
+
and no ask, and the offer comes up once they reply. Choose it when replies
|
|
11
|
+
have an owner: reply drafting is on for the campaign (the default), so each
|
|
12
|
+
reply arrives as a draft to approve, or the workspace's Reply Agent answers.
|
|
13
|
+
Best for fewer, higher-value prospects, where you want replies you can talk with.
|
|
14
|
+
- **Message Testing** (`messageApproach: "message_test"`). You approve one message, it goes to everyone, and you see which wording gets replies.
|
|
15
|
+
This is the template flow described in the create-campaign-v2 prompt and
|
|
16
|
+
flow file. Choose it when the customer wants to approve exact wording, test
|
|
17
|
+
copy against copy, or has switched reply drafting off. Best for a clear
|
|
18
|
+
offer and a large market, where you want to know which copy wins.
|
|
19
|
+
|
|
20
|
+
When you explain the choice, use the two one-line descriptions above as they
|
|
21
|
+
are. Short form: "Message Testing finds the right pitch. Conversations earn
|
|
22
|
+
the right to pitch."
|
|
18
23
|
|
|
19
24
|
When the customer has not said, use Conversations if replies have an owner and
|
|
20
|
-
Message
|
|
21
|
-
to approve the wording" means Message
|
|
22
|
-
"conversational" or "use the reply agent" means Conversations.
|
|
23
|
-
existing campaign with `update_campaign({ campaignId,
|
|
25
|
+
Message Testing otherwise. "Same message to everyone", "test my copy" or "I
|
|
26
|
+
want to approve the wording" means Message Testing. "Write each one",
|
|
27
|
+
"personal", "conversational" or "use the reply agent" means Conversations.
|
|
28
|
+
Change an existing campaign with `update_campaign({ campaignId,
|
|
29
|
+
messageApproach })`; the customer can also change it in the app. Both
|
|
30
|
+
approaches draft new copy with the same writing skill, `draft-message`; the
|
|
31
|
+
approach decides whether one approved message goes to everyone or each person
|
|
32
|
+
gets their own.
|
|
24
33
|
|
|
25
34
|
**A Conversations campaign has no template.** The backend writes each message
|
|
26
35
|
only when the campaign carries no approved template, so:
|
|
@@ -108,8 +108,8 @@ return results
|
|
|
108
108
|
`is_highest_value(...)` and Opus eligibility live in
|
|
109
109
|
`references/thomas-variant-selection.md`. The synthesis step's
|
|
110
110
|
finalizer pass belongs to this optional legacy/template critique protocol.
|
|
111
|
-
It does not apply a second writing-strategy gate to new
|
|
112
|
-
recommendations; their selected
|
|
111
|
+
It does not apply a second writing-strategy gate to new draft-message
|
|
112
|
+
recommendations; their selected draft-message assets and one language check remain
|
|
113
113
|
authoritative.
|
|
114
114
|
|
|
115
115
|
## Critic Groups
|
|
@@ -54,7 +54,7 @@ rerun `import_leads`.
|
|
|
54
54
|
only after saved-filter approval; `validate-sample` is the logical loop name,
|
|
55
55
|
not a visible route)
|
|
56
56
|
- Approved message template/token rules exist in the campaign brief before any
|
|
57
|
-
enrichment or scoring cells are queued (Message
|
|
57
|
+
enrichment or scoring cells are queued (Message Testing only; a Conversations
|
|
58
58
|
campaign has no template)
|
|
59
59
|
- Confirmed campaign rows from Step 13, with the internal execution slice stored
|
|
60
60
|
in `WorkflowTable.config.mcp.reviewBatch` (default 15)
|
|
@@ -12,7 +12,7 @@ This is the tail detail extracted from the main create-campaign-v2 SKILL.md to k
|
|
|
12
12
|
`create-campaign-v2` `references/message-approach.md`) have no message
|
|
13
13
|
template. Every rule below that requires an approved template, `{{...}}`
|
|
14
14
|
tokens in the brief, Message Drafting, or template approval before queueing
|
|
15
|
-
applies to Message
|
|
15
|
+
applies to Message Testing campaigns only. In a Conversations campaign the same
|
|
16
16
|
cascade writes each message once a row passes the fit check.
|
|
17
17
|
|
|
18
18
|
CampaignOffer state and the watch link are canonical. Disk artifacts are
|
|
@@ -409,7 +409,7 @@ the output — not to generate messages manually.
|
|
|
409
409
|
|
|
410
410
|
1. Before queueing or waiting on Generate Message cells, confirm the minted
|
|
411
411
|
campaign brief still contains `{{...}}` in `## Approved Message Template`
|
|
412
|
-
(Message
|
|
412
|
+
(Message Testing only; a Conversations campaign must have no template).
|
|
413
413
|
If it does not, fail before the cascade runs. Do not repair after mint; the
|
|
414
414
|
template must be present during mint so Step 15 never starts in freeform
|
|
415
415
|
generation mode.
|
|
@@ -216,12 +216,12 @@ This gate has two existing callers:
|
|
|
216
216
|
call generate-messages in DRY MODE and write message-validation.md atomically.
|
|
217
217
|
No campaignId, live context reads, fresh research or DB writes in DRY MODE.
|
|
218
218
|
|
|
219
|
-
For NEW, UNAPPROVED recommendations, require the actual
|
|
219
|
+
For NEW, UNAPPROVED recommendations, require the actual draft-message
|
|
220
220
|
prompt, core input contract and selected mode/examples loaded through
|
|
221
221
|
packaged MCP asset reads, with complete pagination. Preserve approved template
|
|
222
222
|
bytes and explicit operator instructions; there is no writing-strategy flag.
|
|
223
223
|
Do not layer gold-standard product-clarity, CTA/PS, required candidates,
|
|
224
|
-
critic/finalizer or source→product→next-step recipes over
|
|
224
|
+
critic/finalizer or source→product→next-step recipes over draft-message.
|
|
225
225
|
|
|
226
226
|
Validate these observable properties:
|
|
227
227
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: draft-message
|
|
3
3
|
description: Draft or improve a prospect opener, reply or caller-eligible follow-up from the actual sender, offer and conversation, with relevant message examples.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Draft Message
|
|
7
7
|
|
|
8
8
|
Write a message a person can naturally answer. One skill, three sections; select a useful conversational move rather than assemble a fixed sequence.
|
|
9
9
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Draft Message
|
|
2
2
|
|
|
3
3
|
One draft for the actual sender/situation. Rules/modes/examples loaded: select relevant ones; never fetch assets. Synthetic examples and histories: their facts, links and outcomes are not customer evidence. Creative rapport needs scoped permission.
|
|
4
4
|
|