@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.
@@ -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 === "copyIntegrityBlockedMessages:actionable:v1") {
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 { advanceRefillV3Tool, } from "./refill-v3-advance.js";
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: advanceRefillV3Tool,
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 === "copyIntegrityBlockedMessages:actionable:v1" ||
1382
- advanceResult.packet.evidence.diagnosticRowRef?.selectorRef === "copyIntegrityBlockedMessages:actionable:v1") {
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
  }