@sellable/mcp 0.1.735 → 0.1.737
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/refill-run-loop.d.ts +45 -0
- package/dist/refill-run-loop.js +119 -1
- package/package.json +1 -1
|
@@ -133,6 +133,45 @@ export type RefillV2LoopResult = {
|
|
|
133
133
|
refillReporting?: Record<string, unknown>;
|
|
134
134
|
runState?: Record<string, unknown>;
|
|
135
135
|
};
|
|
136
|
+
interface LoopContext {
|
|
137
|
+
workspaceId: string;
|
|
138
|
+
runId: string;
|
|
139
|
+
fence: number;
|
|
140
|
+
runHandle: RefillRunHandleV1;
|
|
141
|
+
targetConfig: RefillTargetConfigV1 | null;
|
|
142
|
+
gate: RefillLoopGate;
|
|
143
|
+
gateSeq: number;
|
|
144
|
+
runState: Record<string, unknown>;
|
|
145
|
+
journalPath: string | null;
|
|
146
|
+
plan: Record<string, unknown> | null;
|
|
147
|
+
headAction: Record<string, unknown> | null;
|
|
148
|
+
lastFingerprint: Fingerprint | null;
|
|
149
|
+
recoveryPoint: Record<string, unknown> | null;
|
|
150
|
+
gateCycles: number;
|
|
151
|
+
sleptMs: number;
|
|
152
|
+
blockedContinuations: unknown[];
|
|
153
|
+
lanesStarted: string[];
|
|
154
|
+
lanesTouched: string[];
|
|
155
|
+
invocationStartedAtMs: number;
|
|
156
|
+
agentKeywordsConsumed?: boolean;
|
|
157
|
+
}
|
|
158
|
+
interface Fingerprint {
|
|
159
|
+
planRevision: string | null;
|
|
160
|
+
stateRevision: string | null;
|
|
161
|
+
targetShapeRevision: string | null;
|
|
162
|
+
actionReadinessFingerprint: string | null;
|
|
163
|
+
}
|
|
164
|
+
declare function dominantSchedulerRefusalReason(receipt: Record<string, unknown> | null): {
|
|
165
|
+
source: string;
|
|
166
|
+
reason: string;
|
|
167
|
+
count: number;
|
|
168
|
+
} | null;
|
|
169
|
+
type SchedulerRefusalDisposition = {
|
|
170
|
+
retryable: boolean | null;
|
|
171
|
+
blockerClass: "transient_or_refreshable" | "time_bound_capacity" | "operator_action_required" | "unknown";
|
|
172
|
+
};
|
|
173
|
+
declare function classifySchedulerRefusal(reason: string | null): SchedulerRefusalDisposition;
|
|
174
|
+
declare function schedulerSweepNoOpBlocker(ctx: LoopContext, reportInput: Record<string, unknown>): Record<string, unknown> | null;
|
|
136
175
|
export declare function packetCoherenceFailure(plan: Record<string, unknown>): string | null;
|
|
137
176
|
export declare function terminalProjection(value: {
|
|
138
177
|
targetConfig?: unknown;
|
|
@@ -147,3 +186,9 @@ export declare function terminalProjection(value: {
|
|
|
147
186
|
targetConfig?: RefillTargetConfigV1 | undefined;
|
|
148
187
|
};
|
|
149
188
|
export declare function runRefillV2Loop(input: RefillV2LoopInput, deps: RefillV2LoopDeps): Promise<RefillV2LoopResult>;
|
|
189
|
+
export declare const refillRunLoopInternals: {
|
|
190
|
+
schedulerSweepNoOpBlocker: typeof schedulerSweepNoOpBlocker;
|
|
191
|
+
dominantSchedulerRefusalReason: typeof dominantSchedulerRefusalReason;
|
|
192
|
+
classifySchedulerRefusal: typeof classifySchedulerRefusal;
|
|
193
|
+
};
|
|
194
|
+
export {};
|
package/dist/refill-run-loop.js
CHANGED
|
@@ -405,6 +405,91 @@ function schedulerRunReceiptIsFreshZeroScheduled(schedulerRunReceipt) {
|
|
|
405
405
|
return (effectiveStatus === "window_closed_noop" ||
|
|
406
406
|
numberValue(receipt?.cellsScheduled) === 0);
|
|
407
407
|
}
|
|
408
|
+
// fix(112al): the receipt's own dominant refusal reason, preferred in the order
|
|
409
|
+
// the scheduler applies its gates: prefilters drop ready cells before allocation
|
|
410
|
+
// (stale/invalid paid-InMail credit facts land here), skips are hard gate
|
|
411
|
+
// rejections of considered cells, defers are window/capacity/cooldown waits.
|
|
412
|
+
function dominantSchedulerRefusalReason(receipt) {
|
|
413
|
+
const sources = [
|
|
414
|
+
["prefilterReasons", receipt?.prefilterReasons],
|
|
415
|
+
["skippedReasons", receipt?.skippedReasons],
|
|
416
|
+
["deferredReasons", receipt?.deferredReasons],
|
|
417
|
+
];
|
|
418
|
+
for (const [source, value] of sources) {
|
|
419
|
+
const counts = recordValue(value);
|
|
420
|
+
if (!counts)
|
|
421
|
+
continue;
|
|
422
|
+
let best = null;
|
|
423
|
+
for (const [reason, rawCount] of Object.entries(counts)) {
|
|
424
|
+
const count = numberValue(rawCount) ?? 0;
|
|
425
|
+
if (count <= 0)
|
|
426
|
+
continue;
|
|
427
|
+
if (!best || count > best.count)
|
|
428
|
+
best = { reason, count };
|
|
429
|
+
}
|
|
430
|
+
if (best)
|
|
431
|
+
return { source, reason: best.reason, count: best.count };
|
|
432
|
+
}
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
function classifySchedulerRefusal(reason) {
|
|
436
|
+
if (!reason)
|
|
437
|
+
return { retryable: null, blockerClass: "unknown" };
|
|
438
|
+
// Infrastructure or freshness failures: the underlying facts can be re-read
|
|
439
|
+
// and the same sweep can then succeed without anyone changing configuration.
|
|
440
|
+
if (/(pool|timeout|timed[_ -]?out|connection|transient|unavailable|receipt[_ -]?invalid|stale|refresh)/i.test(reason)) {
|
|
441
|
+
return { retryable: true, blockerClass: "transient_or_refreshable" };
|
|
442
|
+
}
|
|
443
|
+
// Time-bound capacity: nothing is broken, the slot simply is not open yet.
|
|
444
|
+
if (/(window|capacity|daily[_ -]?limit|cooldown|rate[_ -]?limit)/i.test(reason)) {
|
|
445
|
+
return { retryable: true, blockerClass: "time_bound_capacity" };
|
|
446
|
+
}
|
|
447
|
+
// Standing gates: re-running cannot clear these; a human must act.
|
|
448
|
+
if (/(billing|credit[_ -]?threshold|disconnected|sender[_ -]?mismatch|not[_ -]?scheduler[_ -]?eligible|suspended|unauthor)/i.test(reason)) {
|
|
449
|
+
return { retryable: false, blockerClass: "operator_action_required" };
|
|
450
|
+
}
|
|
451
|
+
return { retryable: null, blockerClass: "unknown" };
|
|
452
|
+
}
|
|
453
|
+
// fix(112al): a sweep whose own receipt proves it placed zero while ready
|
|
454
|
+
// inventory existed. Gated on the executed action actually being a scheduler
|
|
455
|
+
// sweep and on a FRESH terminal receipt (ran / window_closed_noop), so an
|
|
456
|
+
// attached/backoff envelope with no outcome of its own never manufactures a
|
|
457
|
+
// blocker. readyCellsFound > 0 is required: a sweep that found no ready supply
|
|
458
|
+
// placed zero honestly and still completes.
|
|
459
|
+
function schedulerSweepNoOpBlocker(ctx, reportInput) {
|
|
460
|
+
const completedType = stringValue(reportInput.completedActionType) ??
|
|
461
|
+
stringValue(reportInput.nextActionType) ??
|
|
462
|
+
stringValue(recordValue(ctx.runState.lastExecutedAction)?.type);
|
|
463
|
+
if (completedType !== "run_scheduler_sweep")
|
|
464
|
+
return null;
|
|
465
|
+
const envelope = recordValue(runStateProgress(ctx).schedulerRunReceipt) ?? null;
|
|
466
|
+
if (!schedulerRunReceiptIsFreshZeroScheduled(envelope))
|
|
467
|
+
return null;
|
|
468
|
+
const receipt = recordValue(envelope?.receipt) ?? envelope;
|
|
469
|
+
const readyCellsFound = numberValue(receipt?.readyCellsFound) ?? 0;
|
|
470
|
+
if (readyCellsFound <= 0)
|
|
471
|
+
return null;
|
|
472
|
+
const dominant = dominantSchedulerRefusalReason(receipt);
|
|
473
|
+
return {
|
|
474
|
+
readyCellsFound,
|
|
475
|
+
cellsScheduled: numberValue(receipt?.cellsScheduled) ?? 0,
|
|
476
|
+
cellsConsidered: numberValue(receipt?.cellsConsidered) ?? 0,
|
|
477
|
+
...(dominant
|
|
478
|
+
? {
|
|
479
|
+
dominantReason: dominant.reason,
|
|
480
|
+
dominantReasonSource: dominant.source,
|
|
481
|
+
dominantReasonCount: dominant.count,
|
|
482
|
+
...classifySchedulerRefusal(dominant.reason),
|
|
483
|
+
}
|
|
484
|
+
: classifySchedulerRefusal(null)),
|
|
485
|
+
...(stringValue(receipt?.nextAction)
|
|
486
|
+
? { recommendedAction: stringValue(receipt?.nextAction) }
|
|
487
|
+
: {}),
|
|
488
|
+
guidance: "The scheduler sweep executed and placed zero cells while ready inventory was present. " +
|
|
489
|
+
"Report the named reason above rather than re-running the identical sweep; re-running " +
|
|
490
|
+
"cannot place these rows until that gate clears.",
|
|
491
|
+
};
|
|
492
|
+
}
|
|
408
493
|
// The settled nested receipt of a scheduler status read or a persisted
|
|
409
494
|
// scheduler-run envelope: a receipt whose status is a terminal run outcome
|
|
410
495
|
// (ran/window_closed_noop/failed). A status read nests it under `.receipt`; a
|
|
@@ -2205,7 +2290,31 @@ async function gateBootstrap(input, deps, ctx) {
|
|
|
2205
2290
|
return null;
|
|
2206
2291
|
}
|
|
2207
2292
|
async function completeTerminal(input, deps, ctx, doneReasonInput, reportInput = {}) {
|
|
2208
|
-
|
|
2293
|
+
// fix(112al, intent-vs-effect): a scheduler sweep that placed NOTHING while
|
|
2294
|
+
// ready inventory existed must never report exact_action_complete. Dotwork
|
|
2295
|
+
// 2026-07-29 ran 68 consecutive refill_sends calls in which every executed
|
|
2296
|
+
// sweep returned exact_action_complete with cellsScheduled 0 against 20 ready
|
|
2297
|
+
// rows; the coordinator advanced next_exact_target and re-issued, and the
|
|
2298
|
+
// concrete blocker — paid_inmail_credit_receipt_invalid, present 460 times in
|
|
2299
|
+
// that run's own receipts — stayed invisible for the entire hour. The receipt
|
|
2300
|
+
// already carried the answer; nothing read it. Convert the terminal to the
|
|
2301
|
+
// documented blocked_retryable class and NAME the dominant receipt reason so
|
|
2302
|
+
// the next run reports the blocker instead of re-running the same no-op. The
|
|
2303
|
+
// scheduler action family is already deferred for the command by
|
|
2304
|
+
// coordinatorDeferredBlocker, so this only changes truthfulness, not routing.
|
|
2305
|
+
const sweepNoOp = normalizeDoneReason(doneReasonInput) === "exact_action_complete"
|
|
2306
|
+
? schedulerSweepNoOpBlocker(ctx, reportInput)
|
|
2307
|
+
: null;
|
|
2308
|
+
const doneReason = sweepNoOp
|
|
2309
|
+
? "blocked"
|
|
2310
|
+
: normalizeDoneReason(doneReasonInput);
|
|
2311
|
+
if (sweepNoOp) {
|
|
2312
|
+
reportInput = {
|
|
2313
|
+
...reportInput,
|
|
2314
|
+
blocker: "scheduler_placed_zero_with_ready_supply",
|
|
2315
|
+
schedulerBlocker: sweepNoOp,
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2209
2318
|
// A lanes-exhausted terminal can still hold staged ready inventory whose
|
|
2210
2319
|
// window days were never swept: the JIT walk only runs on the
|
|
2211
2320
|
// scheduler-wait path, and the exhausted path went straight to terminal
|
|
@@ -4709,3 +4818,12 @@ export async function runRefillV2Loop(input, deps) {
|
|
|
4709
4818
|
return classifyCaughtError(error, ctx);
|
|
4710
4819
|
}
|
|
4711
4820
|
}
|
|
4821
|
+
// fix(112al): exported for the intent-vs-effect pins. The wiring into
|
|
4822
|
+
// completeTerminal is covered by the existing run-loop suites staying green;
|
|
4823
|
+
// these expose the receipt-reading decision itself so each branch of the
|
|
4824
|
+
// contract is pinned directly against real receipt shapes.
|
|
4825
|
+
export const refillRunLoopInternals = {
|
|
4826
|
+
schedulerSweepNoOpBlocker,
|
|
4827
|
+
dominantSchedulerRefusalReason,
|
|
4828
|
+
classifySchedulerRefusal,
|
|
4829
|
+
};
|