@sellable/mcp 0.1.938 → 0.1.939-wip.refill-autonomy.20260920182009
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/tools/refill-v3-advance.d.ts +2 -0
- package/dist/tools/refill-v3-advance.js +11 -4
- package/dist/tools/refill-v3-campaign-work.d.ts +2 -0
- package/dist/tools/refill-v3-campaign-work.js +2 -1
- package/dist/tools/refill-v3-continue-contract.d.ts +18 -2
- package/dist/tools/refill-v3-continue-contract.js +9 -0
- package/dist/tools/refill-v3-continue.d.ts +22 -2
- package/dist/tools/refill-v3-continue.js +35 -11
- package/dist/tools/refill-v3-run-token-transport.d.ts +13 -0
- package/dist/tools/refill-v3-run-token-transport.js +66 -3
- package/dist/tools/registry.d.ts +1497 -1481
- package/package.json +1 -1
- package/skills/refill-sends/SKILL.md +17 -2
- package/skills/refill-sends-workflow/SKILL.md +15 -7
- package/skills/refill-sends-workflow/core/flow.v3.json +30 -6
|
@@ -896,3 +896,5 @@ export type RefillV3AdvanceToolInput = {
|
|
|
896
896
|
* as a thrown transport error and can never become a plausible receipt.
|
|
897
897
|
*/
|
|
898
898
|
export declare function advanceRefillV3Tool(input: RefillV3AdvanceToolInput): Promise<RefillV3AdvanceResult>;
|
|
899
|
+
/** Internal continuation owns the transport flight; public advance cannot overtake it. */
|
|
900
|
+
export declare function advanceRefillV3FromContinuation(input: RefillV3AdvanceToolInput): Promise<RefillV3AdvanceResult>;
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import { getApi } from "../api.js";
|
|
29
29
|
import { REFILL_V3_ADVANCE_INPUT_SCHEMA, REFILL_V3_ADVANCE_OUTPUT_SCHEMA, decodeRefillV3AdvanceResult, } from "./refill-v3-advance-contract.js";
|
|
30
|
-
import { recordRefillV3RunTransition, resolveRefillV3RunToken, } from "./refill-v3-run-token-transport.js";
|
|
30
|
+
import { assertNoRefillV3Continuation, recordRefillV3RunTransition, resolveRefillV3RunToken, } from "./refill-v3-run-token-transport.js";
|
|
31
31
|
import { normalizeExplicitWorkspaceId, workspaceRequestOptions, } from "./workspace-context.js";
|
|
32
32
|
/** The one authenticated backend boundary this projection may reach. */
|
|
33
33
|
export const REFILL_V3_ADVANCE_ROUTE = "/api/v3/mcp/refill-v3/advance";
|
|
@@ -59,6 +59,15 @@ export const refillV3AdvanceToolDefinitions = [
|
|
|
59
59
|
* as a thrown transport error and can never become a plausible receipt.
|
|
60
60
|
*/
|
|
61
61
|
export async function advanceRefillV3Tool(input) {
|
|
62
|
+
assertNoRefillV3Continuation({
|
|
63
|
+
...input,
|
|
64
|
+
workspaceId: normalizeExplicitWorkspaceId(input.workspaceId) ?? "",
|
|
65
|
+
scope: input.scope,
|
|
66
|
+
});
|
|
67
|
+
return advanceRefillV3FromContinuation(input);
|
|
68
|
+
}
|
|
69
|
+
/** Internal continuation owns the transport flight; public advance cannot overtake it. */
|
|
70
|
+
export async function advanceRefillV3FromContinuation(input) {
|
|
62
71
|
const workspaceId = normalizeExplicitWorkspaceId(input.workspaceId);
|
|
63
72
|
if (!workspaceId) {
|
|
64
73
|
throw new Error("Explicit workspaceId is required for refill_v3_advance. Name the target workspace instead of switching the shared active workspace.");
|
|
@@ -67,9 +76,7 @@ export async function advanceRefillV3Tool(input) {
|
|
|
67
76
|
const transportScope = {
|
|
68
77
|
workspaceId,
|
|
69
78
|
scope: input.scope,
|
|
70
|
-
...(input.campaignId === undefined
|
|
71
|
-
? {}
|
|
72
|
-
: { campaignId: input.campaignId }),
|
|
79
|
+
...(input.campaignId === undefined ? {} : { campaignId: input.campaignId }),
|
|
73
80
|
yolo,
|
|
74
81
|
};
|
|
75
82
|
const runToken = resolveRefillV3RunToken(transportScope, input.runToken);
|
|
@@ -230,6 +230,8 @@ export type RefillV3AuthorityReceipt = {
|
|
|
230
230
|
beforeFingerprint: string | null;
|
|
231
231
|
afterFingerprint: string | null;
|
|
232
232
|
changedFields: readonly string[];
|
|
233
|
+
/** Net-new rows confirmed by the source-copy owner, excluding skipped duplicates. */
|
|
234
|
+
sourceRowsCopied?: number;
|
|
233
235
|
effectKey: string | null;
|
|
234
236
|
rollback: {
|
|
235
237
|
available: boolean;
|
|
@@ -694,7 +694,8 @@ export async function runRefillV3CampaignWork(input) {
|
|
|
694
694
|
echo.evidenceFingerprint !== packet.scope.evidenceFingerprint) {
|
|
695
695
|
return refuse(callLog, "stale_evidence", `The output was formed against version ${echo.campaignVersion}/${echo.evidenceFingerprint}, not ${packet.scope.campaignVersion}/${packet.scope.evidenceFingerprint}.`);
|
|
696
696
|
}
|
|
697
|
-
if (packet.evidence.diagnosticRowRef?.selectorRef ===
|
|
697
|
+
if (packet.evidence.diagnosticRowRef?.selectorRef ===
|
|
698
|
+
"copyIntegrityBlockedMessages:actionable:v1") {
|
|
698
699
|
return refuse(callLog, "stale_evidence", "refresh the current plan; this diagnostic selector is retired");
|
|
699
700
|
}
|
|
700
701
|
// 5. One evidence packet can authorize at most one mutation per task.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const REFILL_V3_CONTINUE_RESULT_KINDS: readonly ["world_advanced", "world_step_pending", "world_terminal", "attention_refresh_required", "model_decision_required", "attention_correction_required", "campaign_work_completed"];
|
|
1
|
+
export declare const REFILL_V3_CONTINUE_RESULT_KINDS: readonly ["continuation_pending", "world_advanced", "world_step_pending", "world_terminal", "attention_refresh_required", "model_decision_required", "attention_correction_required", "campaign_work_completed"];
|
|
2
2
|
export declare const REFILL_V3_CONTINUE_INPUT_SCHEMA: Readonly<{
|
|
3
3
|
readonly type: "object";
|
|
4
4
|
readonly additionalProperties: false;
|
|
@@ -220,7 +220,11 @@ export declare const REFILL_V3_CONTINUE_OUTPUT_SCHEMA: Readonly<{
|
|
|
220
220
|
readonly properties: {
|
|
221
221
|
readonly kind: {
|
|
222
222
|
readonly type: "string";
|
|
223
|
-
readonly enum: readonly ["world_advanced", "world_step_pending", "world_terminal", "attention_refresh_required", "model_decision_required", "attention_correction_required", "campaign_work_completed"];
|
|
223
|
+
readonly enum: readonly ["continuation_pending", "world_advanced", "world_step_pending", "world_terminal", "attention_refresh_required", "model_decision_required", "attention_correction_required", "campaign_work_completed"];
|
|
224
|
+
};
|
|
225
|
+
readonly retryAfterMs: {
|
|
226
|
+
readonly type: "integer";
|
|
227
|
+
readonly minimum: 1;
|
|
224
228
|
};
|
|
225
229
|
readonly advanceResult: {
|
|
226
230
|
readonly type: "object";
|
|
@@ -265,6 +269,18 @@ export declare const REFILL_V3_CONTINUE_OUTPUT_SCHEMA: Readonly<{
|
|
|
265
269
|
};
|
|
266
270
|
readonly additionalProperties: false;
|
|
267
271
|
readonly allOf: readonly [{
|
|
272
|
+
readonly if: {
|
|
273
|
+
readonly required: readonly ["kind"];
|
|
274
|
+
readonly properties: {
|
|
275
|
+
readonly kind: {
|
|
276
|
+
readonly const: "continuation_pending";
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
};
|
|
280
|
+
readonly then: {
|
|
281
|
+
readonly required: readonly ["kind", "retryAfterMs"];
|
|
282
|
+
};
|
|
283
|
+
}, {
|
|
268
284
|
readonly if: {
|
|
269
285
|
readonly required: readonly ["kind"];
|
|
270
286
|
readonly properties: {
|
|
@@ -43,6 +43,7 @@ const REFILL_V3_CAMPAIGN_WORK_ACTION_SCHEMA = Object.freeze({
|
|
|
43
43
|
},
|
|
44
44
|
});
|
|
45
45
|
export const REFILL_V3_CONTINUE_RESULT_KINDS = [
|
|
46
|
+
"continuation_pending",
|
|
46
47
|
"world_advanced",
|
|
47
48
|
"world_step_pending",
|
|
48
49
|
"world_terminal",
|
|
@@ -120,6 +121,7 @@ export const REFILL_V3_CONTINUE_OUTPUT_SCHEMA = Object.freeze({
|
|
|
120
121
|
required: ["kind"],
|
|
121
122
|
properties: {
|
|
122
123
|
kind: { type: "string", enum: [...REFILL_V3_CONTINUE_RESULT_KINDS] },
|
|
124
|
+
retryAfterMs: { type: "integer", minimum: 1 },
|
|
123
125
|
advanceResult: { type: "object" },
|
|
124
126
|
packet: { type: "object" },
|
|
125
127
|
correction: { type: "object" },
|
|
@@ -142,6 +144,13 @@ export const REFILL_V3_CONTINUE_OUTPUT_SCHEMA = Object.freeze({
|
|
|
142
144
|
},
|
|
143
145
|
additionalProperties: false,
|
|
144
146
|
allOf: [
|
|
147
|
+
{
|
|
148
|
+
if: {
|
|
149
|
+
required: ["kind"],
|
|
150
|
+
properties: { kind: { const: "continuation_pending" } },
|
|
151
|
+
},
|
|
152
|
+
then: { required: ["kind", "retryAfterMs"] },
|
|
153
|
+
},
|
|
145
154
|
{
|
|
146
155
|
if: {
|
|
147
156
|
required: ["kind"],
|
|
@@ -215,7 +215,11 @@ export declare const refillV3ContinueToolDefinitions: {
|
|
|
215
215
|
readonly properties: {
|
|
216
216
|
readonly kind: {
|
|
217
217
|
readonly type: "string";
|
|
218
|
-
readonly enum: readonly ["world_advanced", "world_step_pending", "world_terminal", "attention_refresh_required", "model_decision_required", "attention_correction_required", "campaign_work_completed"];
|
|
218
|
+
readonly enum: readonly ["continuation_pending", "world_advanced", "world_step_pending", "world_terminal", "attention_refresh_required", "model_decision_required", "attention_correction_required", "campaign_work_completed"];
|
|
219
|
+
};
|
|
220
|
+
readonly retryAfterMs: {
|
|
221
|
+
readonly type: "integer";
|
|
222
|
+
readonly minimum: 1;
|
|
219
223
|
};
|
|
220
224
|
readonly advanceResult: {
|
|
221
225
|
readonly type: "object";
|
|
@@ -260,6 +264,18 @@ export declare const refillV3ContinueToolDefinitions: {
|
|
|
260
264
|
};
|
|
261
265
|
readonly additionalProperties: false;
|
|
262
266
|
readonly allOf: readonly [{
|
|
267
|
+
readonly if: {
|
|
268
|
+
readonly required: readonly ["kind"];
|
|
269
|
+
readonly properties: {
|
|
270
|
+
readonly kind: {
|
|
271
|
+
readonly const: "continuation_pending";
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
readonly then: {
|
|
276
|
+
readonly required: readonly ["kind", "retryAfterMs"];
|
|
277
|
+
};
|
|
278
|
+
}, {
|
|
263
279
|
readonly if: {
|
|
264
280
|
readonly required: readonly ["kind"];
|
|
265
281
|
readonly properties: {
|
|
@@ -371,6 +387,9 @@ export type RefillV3ContinueInput = {
|
|
|
371
387
|
decision?: unknown;
|
|
372
388
|
};
|
|
373
389
|
export type RefillV3ContinueResult = {
|
|
390
|
+
kind: "continuation_pending";
|
|
391
|
+
retryAfterMs: number;
|
|
392
|
+
} | {
|
|
374
393
|
kind: "world_advanced";
|
|
375
394
|
advanceResult: Extract<RefillV3AdvanceResult, {
|
|
376
395
|
kind: "advanced";
|
|
@@ -484,6 +503,7 @@ export type RefillV3ContinueDependencies = {
|
|
|
484
503
|
lane: RefillWorldStateLane;
|
|
485
504
|
evidenceFingerprint: string;
|
|
486
505
|
effectFingerprint: string;
|
|
506
|
+
sourceRowsCopied?: number;
|
|
487
507
|
/** Closed external wait classification; never diagnostic provider prose. */
|
|
488
508
|
externalReasonCode?: "endpoint_temporarily_unavailable";
|
|
489
509
|
/**
|
|
@@ -561,7 +581,7 @@ export declare function continueRefillV3Tool(input: {
|
|
|
561
581
|
campaignId?: string;
|
|
562
582
|
yolo?: boolean;
|
|
563
583
|
decision?: unknown;
|
|
564
|
-
}): Promise<RefillV3ContinueResult>;
|
|
584
|
+
}, dependencies?: RefillV3ContinueDependencies): Promise<RefillV3ContinueResult>;
|
|
565
585
|
import { getPrepareCampaignMessagesStatus, startPrepareCampaignMessages } from "./campaign-message-preparation.js";
|
|
566
586
|
import { queueCampaignCells, selectCampaignCells, selectRepairableCampaignCells } from "./campaign-processing.js";
|
|
567
587
|
import { getProviderPrompt } from "./leads.js";
|
|
@@ -4,13 +4,13 @@ import { handleAddColumn } from "./blueprint-commit.js";
|
|
|
4
4
|
import { getCampaignTableSchema, waitForCampaignProcessing, } from "./campaign-processing.js";
|
|
5
5
|
import { handleUpdateColumn } from "./column-update.js";
|
|
6
6
|
import { executeOneYoloPrimitive } from "./refill-executors.js";
|
|
7
|
-
import {
|
|
7
|
+
import { advanceRefillV3FromContinuation, } from "./refill-v3-advance.js";
|
|
8
8
|
import { REFILL_V3_EXHAUSTION_PREDICATE, REFILL_V3_POSTS_SEARCH_SPENT_AVENUE, } from "./refill-v3-attention-packet-contract.js";
|
|
9
9
|
import { isRefillV3CampaignWorkReceiptAction, } from "./refill-v3-campaign-work-receipt-contract.js";
|
|
10
10
|
import { REFILL_V3_ACTION_RESULTS, REFILL_V3_PREPARATION_REUSE_POINTS, decodeRefillV3CampaignWorkOutput, runRefillV3CampaignWork, } from "./refill-v3-campaign-work.js";
|
|
11
11
|
import { REFILL_V3_CONTINUE_INPUT_SCHEMA, REFILL_V3_CONTINUE_OUTPUT_SCHEMA, } from "./refill-v3-continue-contract.js";
|
|
12
12
|
import { refillV3EnrichmentBatchPlan, refillV3LastCohortStatsFromStatus, } from "./refill-v3-enrichment-batch-plan.js";
|
|
13
|
-
import { retainedRefillV3Attention } from "./refill-v3-run-token-transport.js";
|
|
13
|
+
import { resumeRefillV3Continuation, retainedRefillV3Attention, startRefillV3Continuation, } from "./refill-v3-run-token-transport.js";
|
|
14
14
|
import { dispatchRefillV3SourceFamily, } from "./refill-v3-source-family-dispatch.js";
|
|
15
15
|
import { getRefillV3WorldStateTool } from "./refill-v3-world-state.js";
|
|
16
16
|
import { normalizeExplicitWorkspaceId } from "./workspace-context.js";
|
|
@@ -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. 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.",
|
|
22
22
|
inputSchema: REFILL_V3_CONTINUE_INPUT_SCHEMA,
|
|
23
23
|
outputSchema: REFILL_V3_CONTINUE_OUTPUT_SCHEMA,
|
|
24
24
|
annotations: {
|
|
@@ -204,6 +204,9 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
204
204
|
: null,
|
|
205
205
|
afterFingerprint: params.outcome === "applied" ? fingerprint(params.after) : null,
|
|
206
206
|
changedFields: params.changedFields ?? [],
|
|
207
|
+
...(params.sourceRowsCopied === undefined
|
|
208
|
+
? {}
|
|
209
|
+
: { sourceRowsCopied: params.sourceRowsCopied }),
|
|
207
210
|
effectKey: params.outcome === "applied" ? params.idempotencyKey : null,
|
|
208
211
|
rollback: params.outcome === "applied"
|
|
209
212
|
? { available: false, input: null, result: null }
|
|
@@ -407,11 +410,21 @@ export async function runRefillV3CampaignWorkWithProductionOwners(input, owners
|
|
|
407
410
|
},
|
|
408
411
|
}, request.scope.workspaceId);
|
|
409
412
|
if (attempt.status === "executed_and_reread") {
|
|
413
|
+
// confirm_lead_list's rowCount includes already-present rows. Only
|
|
414
|
+
// leadsImported proves a new cohort; a duplicate copy cannot reset
|
|
415
|
+
// the lane's bounded preparation budget.
|
|
416
|
+
const imported = record(record(attempt.result)?.importSummary)?.leadsImported;
|
|
417
|
+
const sourceRowsCopied = typeof imported === "number" &&
|
|
418
|
+
Number.isSafeInteger(imported) &&
|
|
419
|
+
imported > 0
|
|
420
|
+
? imported
|
|
421
|
+
: 0;
|
|
410
422
|
return receipt({
|
|
411
|
-
outcome: "applied",
|
|
423
|
+
outcome: sourceRowsCopied > 0 ? "applied" : "no_effect",
|
|
412
424
|
result,
|
|
413
425
|
idempotencyKey: request.idempotencyKey,
|
|
414
|
-
changedFields: ["campaignSourceRows"],
|
|
426
|
+
changedFields: sourceRowsCopied > 0 ? ["campaignSourceRows"] : [],
|
|
427
|
+
sourceRowsCopied,
|
|
415
428
|
after: attempt.result,
|
|
416
429
|
});
|
|
417
430
|
}
|
|
@@ -1206,7 +1219,7 @@ export async function readRefillV3CampaignMessageCount(input) {
|
|
|
1206
1219
|
return generatedMessageCountFrom(stats);
|
|
1207
1220
|
}
|
|
1208
1221
|
const DEFAULT_DEPENDENCIES = {
|
|
1209
|
-
advance:
|
|
1222
|
+
advance: advanceRefillV3FromContinuation,
|
|
1210
1223
|
runCampaignWork: runRefillV3CampaignWorkWithProductionOwners,
|
|
1211
1224
|
waitForCampaignProcessing,
|
|
1212
1225
|
async attestCampaignTargetAfterWait(input) {
|
|
@@ -1378,8 +1391,10 @@ async function executeRefillV3Continuation(input, dependencies) {
|
|
|
1378
1391
|
input.decision !== undefined) {
|
|
1379
1392
|
return { kind: "attention_refresh_required", advanceResult };
|
|
1380
1393
|
}
|
|
1381
|
-
if (input.packet.evidence.diagnosticRowRef?.selectorRef ===
|
|
1382
|
-
|
|
1394
|
+
if (input.packet.evidence.diagnosticRowRef?.selectorRef ===
|
|
1395
|
+
"copyIntegrityBlockedMessages:actionable:v1" ||
|
|
1396
|
+
advanceResult.packet.evidence.diagnosticRowRef?.selectorRef ===
|
|
1397
|
+
"copyIntegrityBlockedMessages:actionable:v1") {
|
|
1383
1398
|
return { kind: "attention_refresh_required", advanceResult };
|
|
1384
1399
|
}
|
|
1385
1400
|
// V33-VINCENT-001: a no-decision call is the server-owned classification
|
|
@@ -1483,6 +1498,10 @@ async function executeRefillV3Continuation(input, dependencies) {
|
|
|
1483
1498
|
lane: advanceResult.packet.scope.lane,
|
|
1484
1499
|
evidenceFingerprint: advanceResult.packet.scope.evidenceFingerprint,
|
|
1485
1500
|
effectFingerprint: receipt.afterFingerprint ?? receipt.idempotencyKey,
|
|
1501
|
+
...(receiptAction === "copy_selected_source_rows" &&
|
|
1502
|
+
receipt.sourceRowsCopied !== undefined
|
|
1503
|
+
? { sourceRowsCopied: receipt.sourceRowsCopied }
|
|
1504
|
+
: {}),
|
|
1486
1505
|
...(receipt.externalReasonCode === undefined
|
|
1487
1506
|
? {}
|
|
1488
1507
|
: { externalReasonCode: receipt.externalReasonCode }),
|
|
@@ -1716,7 +1735,7 @@ export function continueRefillV3(input, dependencies = DEFAULT_DEPENDENCIES) {
|
|
|
1716
1735
|
void entry.promise.then(retainResolvedResult, evictRejectedPromise);
|
|
1717
1736
|
return entry.promise;
|
|
1718
1737
|
}
|
|
1719
|
-
export async function continueRefillV3Tool(input) {
|
|
1738
|
+
export async function continueRefillV3Tool(input, dependencies = DEFAULT_DEPENDENCIES) {
|
|
1720
1739
|
const workspaceId = normalizeExplicitWorkspaceId(input.workspaceId);
|
|
1721
1740
|
const yolo = input.yolo ?? true;
|
|
1722
1741
|
const transportScope = workspaceId
|
|
@@ -1729,6 +1748,11 @@ export async function continueRefillV3Tool(input) {
|
|
|
1729
1748
|
yolo,
|
|
1730
1749
|
}
|
|
1731
1750
|
: null;
|
|
1751
|
+
const pending = transportScope
|
|
1752
|
+
? resumeRefillV3Continuation(transportScope)
|
|
1753
|
+
: null;
|
|
1754
|
+
if (pending)
|
|
1755
|
+
return pending;
|
|
1732
1756
|
const retained = transportScope
|
|
1733
1757
|
? retainedRefillV3Attention(transportScope)
|
|
1734
1758
|
: null;
|
|
@@ -1740,7 +1764,7 @@ export async function continueRefillV3Tool(input) {
|
|
|
1740
1764
|
input.campaignId !== retained.campaignId) {
|
|
1741
1765
|
throw new Error("campaign_target_identity_mismatch: the explicit continuation campaignId differs from the retained server-issued run target");
|
|
1742
1766
|
}
|
|
1743
|
-
return continueRefillV3({
|
|
1767
|
+
return startRefillV3Continuation({ ...transportScope, campaignId: input.campaignId ?? retained.campaignId }, () => continueRefillV3({
|
|
1744
1768
|
workspaceId,
|
|
1745
1769
|
scope: input.scope,
|
|
1746
1770
|
...((input.campaignId ?? retained.campaignId) === undefined
|
|
@@ -1750,7 +1774,7 @@ export async function continueRefillV3Tool(input) {
|
|
|
1750
1774
|
runToken: retained.runToken,
|
|
1751
1775
|
packet: retained.packet,
|
|
1752
1776
|
...(input.decision === undefined ? {} : { decision: input.decision }),
|
|
1753
|
-
});
|
|
1777
|
+
}, dependencies));
|
|
1754
1778
|
}
|
|
1755
1779
|
import { getPrepareCampaignMessagesStatus, startPrepareCampaignMessages, } from "./campaign-message-preparation.js";
|
|
1756
1780
|
import { queueCampaignCells, selectCampaignCells, selectRepairableCampaignCells, } from "./campaign-processing.js";
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { RefillV3AdvanceResult } from "./refill-v3-advance-contract.js";
|
|
16
16
|
import type { RefillV3CampaignAttentionPacket } from "./refill-v3-attention-packet-contract.js";
|
|
17
|
+
import type { RefillV3ContinueResult } from "./refill-v3-continue.js";
|
|
17
18
|
export type RefillV3RunTransportScope = {
|
|
18
19
|
workspaceId: string;
|
|
19
20
|
scope: unknown;
|
|
@@ -32,3 +33,15 @@ export declare function retainedRefillV3Attention(input: RefillV3RunTransportSco
|
|
|
32
33
|
} | null;
|
|
33
34
|
/** Test-only isolation owner; production code never clears live continuity. */
|
|
34
35
|
export declare function resetRefillV3RunTokenTransportForTests(): void;
|
|
36
|
+
export declare function assertNoRefillV3Continuation(input: RefillV3RunTransportScope): void;
|
|
37
|
+
/**
|
|
38
|
+
* Keep a public retry attached BEFORE reading the successor token. The Sep 21
|
|
39
|
+
* Sellable source search outlived the MCP response; reading that newer token
|
|
40
|
+
* let a retry overtake the source receipt write. The flight owns no product
|
|
41
|
+
* decisions: it only retains one exact continuation through response loss.
|
|
42
|
+
* Completed results stay collectible after a pending response, including
|
|
43
|
+
* errors, until a caller observes them. This is process-local transport state;
|
|
44
|
+
* durable owner/run receipts remain the authority after an MCP restart.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resumeRefillV3Continuation(input: RefillV3RunTransportScope): Promise<RefillV3ContinueResult> | null;
|
|
47
|
+
export declare function startRefillV3Continuation(input: RefillV3RunTransportScope, execute: () => Promise<RefillV3ContinueResult>): Promise<RefillV3ContinueResult>;
|
|
@@ -31,9 +31,7 @@ function runKey(input) {
|
|
|
31
31
|
return canonical({
|
|
32
32
|
workspaceId: input.workspaceId,
|
|
33
33
|
scope: input.scope,
|
|
34
|
-
...(input.campaignId === undefined
|
|
35
|
-
? {}
|
|
36
|
-
: { campaignId: input.campaignId }),
|
|
34
|
+
...(input.campaignId === undefined ? {} : { campaignId: input.campaignId }),
|
|
37
35
|
yolo: input.yolo !== false,
|
|
38
36
|
});
|
|
39
37
|
}
|
|
@@ -95,4 +93,69 @@ export function retainedRefillV3Attention(input) {
|
|
|
95
93
|
/** Test-only isolation owner; production code never clears live continuity. */
|
|
96
94
|
export function resetRefillV3RunTokenTransportForTests() {
|
|
97
95
|
retainedRuns.clear();
|
|
96
|
+
continuationFlights.clear();
|
|
97
|
+
}
|
|
98
|
+
const continuationFlights = new Map();
|
|
99
|
+
function continuationFlight(input) {
|
|
100
|
+
const exact = continuationFlights.get(runKey(input));
|
|
101
|
+
if (exact || input.campaignId !== undefined)
|
|
102
|
+
return exact;
|
|
103
|
+
const matches = [...continuationFlights.values()].filter((entry) => baseRunKey(entry.scope) === baseRunKey(input));
|
|
104
|
+
if (matches.length > 1)
|
|
105
|
+
throw new Error("Specify campaignId to resume the exact pending continuation.");
|
|
106
|
+
return matches[0];
|
|
107
|
+
}
|
|
108
|
+
export function assertNoRefillV3Continuation(input) {
|
|
109
|
+
if (continuationFlight(input)) {
|
|
110
|
+
throw new Error("continuation_pending: call refill_v3_continue with the same scope and no decision to collect the running operation before advancing.");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Keep a public retry attached BEFORE reading the successor token. The Sep 21
|
|
115
|
+
* Sellable source search outlived the MCP response; reading that newer token
|
|
116
|
+
* let a retry overtake the source receipt write. The flight owns no product
|
|
117
|
+
* decisions: it only retains one exact continuation through response loss.
|
|
118
|
+
* Completed results stay collectible after a pending response, including
|
|
119
|
+
* errors, until a caller observes them. This is process-local transport state;
|
|
120
|
+
* durable owner/run receipts remain the authority after an MCP restart.
|
|
121
|
+
*/
|
|
122
|
+
export function resumeRefillV3Continuation(input) {
|
|
123
|
+
const entry = continuationFlight(input);
|
|
124
|
+
return entry ? collectContinuation(entry) : null;
|
|
125
|
+
}
|
|
126
|
+
export function startRefillV3Continuation(input, execute) {
|
|
127
|
+
const existing = resumeRefillV3Continuation(input);
|
|
128
|
+
if (existing)
|
|
129
|
+
return existing;
|
|
130
|
+
if (continuationFlights.size >= MAX_RETAINED_REFILL_RUNS) {
|
|
131
|
+
throw new Error("Collect pending refill continuations before starting another scope.");
|
|
132
|
+
}
|
|
133
|
+
const entry = { scope: input, promise: Promise.resolve().then(execute) };
|
|
134
|
+
continuationFlights.set(runKey(input), entry);
|
|
135
|
+
return collectContinuation(entry);
|
|
136
|
+
}
|
|
137
|
+
async function collectContinuation(entry) {
|
|
138
|
+
let timer;
|
|
139
|
+
const pending = Symbol("pending");
|
|
140
|
+
try {
|
|
141
|
+
const result = await Promise.race([
|
|
142
|
+
entry.promise,
|
|
143
|
+
new Promise((resolve) => {
|
|
144
|
+
timer = setTimeout(() => resolve(pending), 20_000);
|
|
145
|
+
}),
|
|
146
|
+
]);
|
|
147
|
+
if (result === pending)
|
|
148
|
+
return { kind: "continuation_pending", retryAfterMs: 1000 };
|
|
149
|
+
if (continuationFlights.get(runKey(entry.scope)) === entry)
|
|
150
|
+
continuationFlights.delete(runKey(entry.scope));
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (continuationFlights.get(runKey(entry.scope)) === entry)
|
|
155
|
+
continuationFlights.delete(runKey(entry.scope));
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
clearTimeout(timer);
|
|
160
|
+
}
|
|
98
161
|
}
|