@sellable/mcp 0.1.737 → 0.1.739
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.
|
@@ -172,6 +172,21 @@ type SchedulerRefusalDisposition = {
|
|
|
172
172
|
};
|
|
173
173
|
declare function classifySchedulerRefusal(reason: string | null): SchedulerRefusalDisposition;
|
|
174
174
|
declare function schedulerSweepNoOpBlocker(ctx: LoopContext, reportInput: Record<string, unknown>): Record<string, unknown> | null;
|
|
175
|
+
declare function coverageOnlySignature(plan: Record<string, unknown>): {
|
|
176
|
+
sent: number;
|
|
177
|
+
scheduled: number;
|
|
178
|
+
ready: number;
|
|
179
|
+
goal: number;
|
|
180
|
+
};
|
|
181
|
+
declare function nextCoverageStallState(ctx: LoopContext, plan: Record<string, unknown>): {
|
|
182
|
+
signature: {
|
|
183
|
+
sent: number;
|
|
184
|
+
scheduled: number;
|
|
185
|
+
ready: number;
|
|
186
|
+
goal: number;
|
|
187
|
+
};
|
|
188
|
+
repeatCount: number;
|
|
189
|
+
};
|
|
175
190
|
export declare function packetCoherenceFailure(plan: Record<string, unknown>): string | null;
|
|
176
191
|
export declare function terminalProjection(value: {
|
|
177
192
|
targetConfig?: unknown;
|
|
@@ -190,5 +205,8 @@ export declare const refillRunLoopInternals: {
|
|
|
190
205
|
schedulerSweepNoOpBlocker: typeof schedulerSweepNoOpBlocker;
|
|
191
206
|
dominantSchedulerRefusalReason: typeof dominantSchedulerRefusalReason;
|
|
192
207
|
classifySchedulerRefusal: typeof classifySchedulerRefusal;
|
|
208
|
+
coverageOnlySignature: typeof coverageOnlySignature;
|
|
209
|
+
nextCoverageStallState: typeof nextCoverageStallState;
|
|
210
|
+
COVERAGE_STALL_LIMIT: number;
|
|
193
211
|
};
|
|
194
212
|
export {};
|
package/dist/refill-run-loop.js
CHANGED
|
@@ -1864,6 +1864,46 @@ function nextNoProgressState(ctx, plan, action) {
|
|
|
1864
1864
|
repeatCount,
|
|
1865
1865
|
};
|
|
1866
1866
|
}
|
|
1867
|
+
// fix(112an, UNIVERSAL progress-or-settle): 112aj's bound keys on
|
|
1868
|
+
// noProgressSignature, which includes actionFingerprint (type:campaign:table:
|
|
1869
|
+
// sender:actionKey:effectId). A loop that re-plans a DIFFERENT action each
|
|
1870
|
+
// iteration therefore resets the counter and never trips it, no matter how many
|
|
1871
|
+
// iterations produce zero coverage movement. Both expensive livelocks we hit
|
|
1872
|
+
// had exactly that shape:
|
|
1873
|
+
// - Dotwork s3: 136 refill_sends calls, 21 exact_action_complete, coverage
|
|
1874
|
+
// frozen at {sent:0, scheduled:0, ready:20, goal:50} throughout.
|
|
1875
|
+
// - sellable FINAL: 6 of 7 exact runs returned byte-identical coverage
|
|
1876
|
+
// {goal:180, ready:5, scheduled:9, sent:0}, each under a distinct
|
|
1877
|
+
// next_exact_target, so 112aj saw six "different" actions.
|
|
1878
|
+
// This is the coverage-only companion: it ignores WHICH action ran and asks
|
|
1879
|
+
// only whether the world moved. A command that cannot move sent/scheduled/ready
|
|
1880
|
+
// across COVERAGE_STALL_LIMIT consecutive iterations is burning wall-clock and
|
|
1881
|
+
// credits for nothing and must settle with an honest terminal naming the frozen
|
|
1882
|
+
// counters and the last action it attempted.
|
|
1883
|
+
//
|
|
1884
|
+
// Threshold is deliberately looser than 112aj's 3: distinct actions legitimately
|
|
1885
|
+
// run in sequence before coverage moves (prepare -> approve -> sweep), so a tight
|
|
1886
|
+
// cap would cut off real ladders. Six is above the longest legitimate
|
|
1887
|
+
// zero-movement prefix observed across the 8-workspace matrix (the healthy rows
|
|
1888
|
+
// settle in 0-20 calls; only true stalls exceed it).
|
|
1889
|
+
const COVERAGE_STALL_LIMIT = 6;
|
|
1890
|
+
function coverageOnlySignature(plan) {
|
|
1891
|
+
const coverage = coverageSnapshot(plan);
|
|
1892
|
+
return {
|
|
1893
|
+
sent: coverage.sent,
|
|
1894
|
+
scheduled: coverage.scheduled,
|
|
1895
|
+
ready: coverage.ready,
|
|
1896
|
+
goal: coverage.goal,
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
function nextCoverageStallState(ctx, plan) {
|
|
1900
|
+
const next = coverageOnlySignature(plan);
|
|
1901
|
+
const prior = recordValue(runStateProgress(ctx).coverageStall) ?? {};
|
|
1902
|
+
const priorSignature = recordValue(prior.signature);
|
|
1903
|
+
const same = priorSignature !== undefined && safeJson(priorSignature) === safeJson(next);
|
|
1904
|
+
const repeatCount = same ? (numberValue(prior.repeatCount) ?? 1) + 1 : 1;
|
|
1905
|
+
return { signature: next, repeatCount };
|
|
1906
|
+
}
|
|
1867
1907
|
function sourceHandoffText(action) {
|
|
1868
1908
|
const campaignId = actionCampaignId(action) ?? "campaign";
|
|
1869
1909
|
const toolInput = actionToolInput(action);
|
|
@@ -2601,6 +2641,11 @@ async function gatePlan(input, deps, ctx) {
|
|
|
2601
2641
|
fallbackSummary: line.fallbackSummary,
|
|
2602
2642
|
}));
|
|
2603
2643
|
const fingerprint = fingerprintFromPlan(plan);
|
|
2644
|
+
// fix(112an): universal coverage-only bound. Evaluated BEFORE 112aj's
|
|
2645
|
+
// action-scoped bound only for its counter update, but enforced AFTER it so
|
|
2646
|
+
// the more specific typed terminals (broaden keyword handoff, etc.) keep
|
|
2647
|
+
// precedence when they apply. See COVERAGE_STALL_LIMIT.
|
|
2648
|
+
const coverageStall = nextCoverageStallState(ctx, plan);
|
|
2604
2649
|
const noProgress = nextNoProgressState(ctx, plan, action);
|
|
2605
2650
|
if (noProgress.repeatCount >= 3) {
|
|
2606
2651
|
// fix(111y, CloverL): when the stuck rung is a broaden whose derived tier
|
|
@@ -2665,14 +2710,33 @@ async function gatePlan(input, deps, ctx) {
|
|
|
2665
2710
|
blocker: "no_forward_progress",
|
|
2666
2711
|
stuckRung: stringValue(action.evergreenRung) ?? stringValue(action.type),
|
|
2667
2712
|
repeatedHeadAction: action,
|
|
2668
|
-
progress: { noProgress },
|
|
2713
|
+
progress: { noProgress, coverageStall },
|
|
2669
2714
|
guidance: "The same refill rung repeated three times with no coverage, frontier, or lane movement.",
|
|
2670
2715
|
});
|
|
2671
2716
|
}
|
|
2717
|
+
// fix(112an): universal bound — the world did not move for
|
|
2718
|
+
// COVERAGE_STALL_LIMIT consecutive iterations, regardless of which action ran
|
|
2719
|
+
// each time. Settle with an honest terminal naming the frozen counters rather
|
|
2720
|
+
// than burning further wall-clock and credits on a command that cannot advance.
|
|
2721
|
+
if (coverageStall.repeatCount >= COVERAGE_STALL_LIMIT) {
|
|
2722
|
+
const frozen = coverageStall.signature;
|
|
2723
|
+
return completeTerminal(input, deps, ctx, "blocked", {
|
|
2724
|
+
blocker: "no_coverage_movement",
|
|
2725
|
+
stuckRung: stringValue(action.evergreenRung) ?? stringValue(action.type),
|
|
2726
|
+
repeatedHeadAction: action,
|
|
2727
|
+
progress: { coverageStall },
|
|
2728
|
+
coverage: frozen,
|
|
2729
|
+
guidance: `Coverage did not move across ${COVERAGE_STALL_LIMIT} consecutive refill iterations ` +
|
|
2730
|
+
`(sent ${frozen.sent}, scheduled ${frozen.scheduled}, ready ${frozen.ready}, ` +
|
|
2731
|
+
`goal ${frozen.goal}); the actions differed each time, so this is a workspace-level ` +
|
|
2732
|
+
"stall rather than one repeated rung. Settling instead of continuing to spend. " +
|
|
2733
|
+
"Re-run after the underlying blocker changes; the last attempted action is reported above.",
|
|
2734
|
+
});
|
|
2735
|
+
}
|
|
2672
2736
|
const nextRunState = mergeRunState(ctx.runState, {
|
|
2673
2737
|
lastPlan: fingerprint,
|
|
2674
2738
|
headAction: action,
|
|
2675
|
-
progress: mergeProgress(ctx, { noProgress }),
|
|
2739
|
+
progress: mergeProgress(ctx, { noProgress, coverageStall }),
|
|
2676
2740
|
});
|
|
2677
2741
|
const advanced = await deps.runClient.advanceRefillRunGateRemote({
|
|
2678
2742
|
workspaceId: input.workspaceId,
|
|
@@ -4826,4 +4890,7 @@ export const refillRunLoopInternals = {
|
|
|
4826
4890
|
schedulerSweepNoOpBlocker,
|
|
4827
4891
|
dominantSchedulerRefusalReason,
|
|
4828
4892
|
classifySchedulerRefusal,
|
|
4893
|
+
coverageOnlySignature,
|
|
4894
|
+
nextCoverageStallState,
|
|
4895
|
+
COVERAGE_STALL_LIMIT,
|
|
4829
4896
|
};
|
|
@@ -22,7 +22,7 @@ export const refreshSenderEngagementToolDefinitions = [
|
|
|
22
22
|
},
|
|
23
23
|
campaignId: {
|
|
24
24
|
type: "string",
|
|
25
|
-
description: "Optional exact sender-owned Post Engagers campaign id.
|
|
25
|
+
description: "Optional exact sender-owned Post Engagers campaign id. Omit it and the command discovers the sender's Post Engagers campaign itself, so managed evergreen configuration is not required; pass it only to pin one exact campaign. When the sender owns several lanes, the response reports the rest under discovery.additionalCandidates — refresh each by repeating the call with that campaignId.",
|
|
26
26
|
},
|
|
27
27
|
tableId: {
|
|
28
28
|
type: "string",
|
package/package.json
CHANGED
|
@@ -33,7 +33,9 @@ You are a pipeline supply agent. People who engage with a sender's LinkedIn post
|
|
|
33
33
|
<inputs>
|
|
34
34
|
The invoking prompt names the senders ("refresh sender engagement for csreyes92 and thomas"). Resolve each via `list_senders` (match name/handle/LinkedIn URL). With no names given, inspect the active workspace and refresh every connected sender that has an active/paused sender-owned Post Engagers campaign backed by Signal Discovery.
|
|
35
35
|
|
|
36
|
-
Optional: target sender names/ids, `tableId` when the user wants to force a specific campaign table, maximum posts per sender (default 5, hard cap 5 unless the user explicitly asks for more), and maximum engager pages per tracked post.
|
|
36
|
+
Optional: target sender names/ids, `campaignId` when the user wants to force a specific Post Engagers campaign, `tableId` when the user wants to force a specific campaign table, maximum posts per sender (default 5, hard cap 5 unless the user explicitly asks for more), and maximum engager pages per tracked post.
|
|
37
|
+
|
|
38
|
+
`campaignId` is optional. When it is omitted the command discovers the sender's Post Engagers campaign itself — it searches the workspace for campaigns the sender solely owns that are backed by a post-engager provider and named as a Post Engagers lane. A workspace can hold one such lane per sender, so discovery is always scoped to the sender you are refreshing; another sender's lane and shared multi-sender lanes are never selected. Do not pass a `campaignId` you guessed.
|
|
37
39
|
</inputs>
|
|
38
40
|
|
|
39
41
|
<entrypoint>
|
|
@@ -58,9 +60,12 @@ For each target sender/campaign:
|
|
|
58
60
|
- Resolve each target sender with `list_senders`/`get_sender`. Use the sender id returned by the product, not a guessed handle.
|
|
59
61
|
2. **Use the typed product command first**:
|
|
60
62
|
- Call `refresh_sender_engagement` in `mode:"dry_run"` for each sender.
|
|
61
|
-
- Always pass `workspaceId`, `senderId`, and any user-specified `tableId`, `maxPosts`, or `maxEngagerPages`.
|
|
63
|
+
- Always pass `workspaceId`, `senderId`, and any user-specified `campaignId`, `tableId`, `maxPosts`, or `maxEngagerPages`. Omitting `campaignId` is correct and normal — the command resolves the campaign itself.
|
|
64
|
+
- Read `discovery` on the response to see how the campaign was resolved (`managed_slot`, `explicit_campaign_id`, or `auto_discovered`) and which one was selected.
|
|
65
|
+
- If `discovery.additionalCandidates` is non-empty, that sender owns more than one Post Engagers lane. Refresh each one by repeating the dry-run/apply cycle with that candidate's `campaignId`. Every lane gets its own dry run and its own `dryRunFingerprint`; never reuse a fingerprint across campaigns.
|
|
62
66
|
- Treat the dry-run response as the campaign/source boundary authority. It should identify the sender-owned Post Engagers slot, campaign, workflow table, source provider `campaign-tracked-post`, source table type `tracked_post_engager_source_list`, expected tracked posts, expected engager refresh/import work, and a `dryRunFingerprint`.
|
|
63
67
|
- If the typed command reports no eligible sender-owned Post Engagers campaign, no tracked posts, no recent posts, or a workspace/sender mismatch, report that exact blocker and stop for that sender. Do not silently create campaigns or fall back to shared lanes.
|
|
68
|
+
- `sender_owned_post_engagers_slot_not_found` now means discovery genuinely found no eligible lane, not that a campaign id was missing. Report `discovery.campaignsInspected` alongside it, and describe it as missing campaign setup — never as "the sender has no posts", since no LinkedIn fetch happens before this point.
|
|
64
69
|
3. **Gate writes with dry-run proof**:
|
|
65
70
|
- Never call `refresh_sender_engagement` in `mode:"apply"` before a successful dry run from this same run.
|
|
66
71
|
- For manual runs, show the target workspace, sender, campaign/table, source lead list, expected tracked posts, expected row/import impact, and `dryRunFingerprint`, then wait for explicit user approval before apply.
|