@sellable/mcp 0.1.556 → 0.1.557
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/README.md +13 -2
- package/agents/registry.json +2 -2
- package/dist/api.js +3 -6
- package/dist/auth.d.ts +0 -6
- package/dist/auth.js +2 -44
- package/dist/refill-run-client.d.ts +0 -5
- package/dist/refill-run-client.js +0 -15
- package/dist/refill-run-loop.d.ts +1 -12
- package/dist/refill-run-loop.js +13 -158
- package/dist/server.js +23 -0
- package/dist/tools/auth.d.ts +0 -5
- package/dist/tools/auth.js +12 -49
- package/dist/tools/campaign-message-preparation.d.ts +0 -62
- package/dist/tools/campaign-message-preparation.js +0 -41
- package/dist/tools/campaigns.js +2 -2
- package/dist/tools/csv-dnc.js +2 -2
- package/dist/tools/evergreen-refill-plan.d.ts +0 -3
- package/dist/tools/evergreen-refill-plan.js +7 -29
- package/dist/tools/find-leads-runs.d.ts +151 -0
- package/dist/tools/find-leads-runs.js +98 -0
- package/dist/tools/leads.d.ts +317 -32
- package/dist/tools/leads.js +171 -10
- package/dist/tools/model-quality.js +4 -6
- package/dist/tools/prompts.d.ts +3 -3
- package/dist/tools/prompts.js +7 -15
- package/dist/tools/provider-preflight.d.ts +65 -2
- package/dist/tools/provider-preflight.js +97 -10
- package/dist/tools/readiness.d.ts +89 -5
- package/dist/tools/readiness.js +66 -0
- package/dist/tools/refill-executors.d.ts +0 -38
- package/dist/tools/refill-executors.js +3 -222
- package/dist/tools/refill-sends-v2.d.ts +1 -118
- package/dist/tools/refill-sends-v2.js +2 -310
- package/dist/tools/refill-sends.d.ts +32 -678
- package/dist/tools/refill-sends.js +13 -274
- package/dist/tools/refill-target-plan.js +14 -486
- package/dist/tools/registry.d.ts +330 -115
- package/dist/tools/registry.js +7 -1
- package/dist/tools/scheduler-fill-capacity.js +1 -1
- package/dist/tools/scheduler-run.d.ts +0 -71
- package/dist/tools/scheduler-run.js +1 -203
- package/dist/tools/setup-evergreen-campaigns.js +1 -1
- package/dist/tools/workspace-context.d.ts +1 -1
- package/dist/tools/workspace-context.js +3 -8
- package/dist/tools/workspace-export.js +2 -2
- package/dist/tools/workspaces.d.ts +2 -48
- package/dist/tools/workspaces.js +5 -48
- 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-evergreen-campaigns/SKILL.md +16 -16
- package/skills/find-leads/SKILL.md +48 -630
- package/skills/find-leads-v2/SKILL.md +70 -0
- package/skills/find-leads-v2/core/flow.v1.json +31 -0
- package/skills/refill-sends/SKILL.md +353 -91
- package/skills/refill-sends-v2/SKILL.md +6 -6
- package/skills/refill-sends-v2-workflow/SKILL.md +5 -5
- package/skills/refill-sends-v2-workflow/core/flow.v1.json +8 -8
- package/skills/refill-sends-workflow/SKILL.md +743 -100
- package/skills/refill-sends-workflow/core/flow.v1.json +1 -185
- package/dist/refill-contract.d.ts +0 -157
- package/dist/refill-contract.js +0 -487
- package/skills/refill-sends-workflow/core/contract.v2.json +0 -543
package/README.md
CHANGED
|
@@ -16,8 +16,9 @@ Each message gets 5+ minutes of Claude attention with deep research - no other t
|
|
|
16
16
|
|
|
17
17
|
### Prompt Source Of Truth
|
|
18
18
|
|
|
19
|
-
There are
|
|
19
|
+
There are nine public Sellable entrypoints shared across hosts:
|
|
20
20
|
|
|
21
|
+
- `sellable:find-leads`
|
|
21
22
|
- `sellable:create-campaign`
|
|
22
23
|
- `sellable:create-ab-test`
|
|
23
24
|
- `sellable:create-evergreen-campaigns`
|
|
@@ -33,6 +34,11 @@ the approval-gated workflow from:
|
|
|
33
34
|
|
|
34
35
|
- `mcp/sellable/skills/create-campaign-v2/SKILL.md`
|
|
35
36
|
|
|
37
|
+
The find-leads public wrapper builds a live exportable source list without a
|
|
38
|
+
campaign and loads its durable approval-gated workflow from:
|
|
39
|
+
|
|
40
|
+
- `mcp/sellable/skills/find-leads-v2/SKILL.md`
|
|
41
|
+
|
|
36
42
|
The create-ab-test public wrapper prepares clean A/B split lead lists and
|
|
37
43
|
review-copy campaigns from:
|
|
38
44
|
|
|
@@ -205,7 +211,7 @@ thread and select `Sellable Create Campaign`, `Sellable Create A/B Test`,
|
|
|
205
211
|
`Sellable Create Evergreen Campaigns`, `Sellable Foundation`,
|
|
206
212
|
`Sellable Content`, `Sellable Create Post`, `Sellable Refresh Sender Engagement`,
|
|
207
213
|
or `Sellable Refill Sends`; or invoke
|
|
208
|
-
`$sellable:create-campaign`, `$sellable:create-ab-test`,
|
|
214
|
+
`$sellable:find-leads`, `$sellable:create-campaign`, `$sellable:create-ab-test`,
|
|
209
215
|
`$sellable:create-evergreen-campaigns`, `$sellable:foundation`,
|
|
210
216
|
`$sellable:content`, `$sellable:create-post`,
|
|
211
217
|
`$sellable:refresh-sender-engagement`, or `$sellable:refill-sends`. If the app still says
|
|
@@ -220,6 +226,7 @@ the Sellable MCP tools for Codex Desktop.
|
|
|
220
226
|
Use these names consistently:
|
|
221
227
|
|
|
222
228
|
- Claude Code command: `/sellable:create-campaign`
|
|
229
|
+
- Claude Code command: `/sellable:find-leads`
|
|
223
230
|
- Claude Code command: `/sellable:create-ab-test`
|
|
224
231
|
- Claude Code command: `/sellable:create-evergreen-campaigns`
|
|
225
232
|
- Claude Code command: `/sellable:foundation`
|
|
@@ -228,6 +235,7 @@ Use these names consistently:
|
|
|
228
235
|
- Claude Code command: `/sellable:refresh-sender-engagement`
|
|
229
236
|
- Claude Code command: `/sellable:refill-sends`
|
|
230
237
|
- Codex command: `$sellable:create-campaign`
|
|
238
|
+
- Codex command: `$sellable:find-leads`
|
|
231
239
|
- Codex command: `$sellable:create-ab-test`
|
|
232
240
|
- Codex command: `$sellable:create-evergreen-campaigns`
|
|
233
241
|
- Codex command: `$sellable:foundation`
|
|
@@ -237,6 +245,7 @@ Use these names consistently:
|
|
|
237
245
|
- Codex command: `$sellable:refill-sends`
|
|
238
246
|
- Codex Desktop plugin: `sellable@sellable`
|
|
239
247
|
- Codex visible skill: `Sellable Create Campaign`
|
|
248
|
+
- Codex visible skill: `Sellable Find Leads`
|
|
240
249
|
- Codex visible skill: `Sellable Create A/B Test`
|
|
241
250
|
- Codex visible skill: `Sellable Create Evergreen Campaigns`
|
|
242
251
|
- Codex visible skill: `Sellable Foundation`
|
|
@@ -245,6 +254,7 @@ Use these names consistently:
|
|
|
245
254
|
- Codex visible skill: `Sellable Refresh Sender Engagement`
|
|
246
255
|
- Codex visible skill: `Sellable Refill Sends`
|
|
247
256
|
- Codex skill frontmatter name: `create-campaign`
|
|
257
|
+
- Codex skill frontmatter name: `find-leads`
|
|
248
258
|
- Codex skill frontmatter name: `create-ab-test`
|
|
249
259
|
- Codex skill frontmatter name: `create-evergreen-campaigns`
|
|
250
260
|
- Codex skill frontmatter name: `foundation`
|
|
@@ -254,6 +264,7 @@ Use these names consistently:
|
|
|
254
264
|
- Codex skill frontmatter name: `refill-sends`
|
|
255
265
|
- MCP server name: `sellable`
|
|
256
266
|
- Internal workflow prompt: `create-campaign-v2`
|
|
267
|
+
- Internal workflow prompt: `find-leads-v2`
|
|
257
268
|
- Internal workflow prompt: `refill-sends-workflow`
|
|
258
269
|
- Internal/backward-compatible memory prompt: `interview`
|
|
259
270
|
|
package/agents/registry.json
CHANGED
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"ownership": "message strategy, proof inventory, token rules, skeptical-prospect review, and selected winner only",
|
|
27
27
|
"codex": {
|
|
28
28
|
"description": "Message Drafting worker for campaign-backed template proposals after confirm_lead_list imports a non-empty bounded review batch.",
|
|
29
|
-
"model": "gpt-5.
|
|
30
|
-
"modelReasoningEffort": "
|
|
29
|
+
"model": "gpt-5.5",
|
|
30
|
+
"modelReasoningEffort": "xhigh",
|
|
31
31
|
"sandboxMode": "read-only",
|
|
32
32
|
"nicknameCandidates": [
|
|
33
33
|
"Message Drafting",
|
package/dist/api.js
CHANGED
|
@@ -3,7 +3,7 @@ import { mkdir, rename, rm, stat } from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { Readable } from "node:stream";
|
|
5
5
|
import { pipeline } from "node:stream/promises";
|
|
6
|
-
import { getConfig, getConfigPath
|
|
6
|
+
import { getConfig, getConfigPath } from "./auth.js";
|
|
7
7
|
export class SellableApiError extends Error {
|
|
8
8
|
status;
|
|
9
9
|
body;
|
|
@@ -42,12 +42,9 @@ export class SellableApi {
|
|
|
42
42
|
// Re-read config on every request so workspace switches take effect immediately
|
|
43
43
|
const config = getConfig();
|
|
44
44
|
const url = `${config.apiUrl}${path}`;
|
|
45
|
-
const requestedWorkspaceId = options && Object.prototype.hasOwnProperty.call(options, "workspaceId")
|
|
46
|
-
? options.workspaceId
|
|
47
|
-
: getEffectiveConfiguredWorkspaceId(config);
|
|
48
45
|
const workspaceId = options && Object.prototype.hasOwnProperty.call(options, "workspaceId")
|
|
49
|
-
?
|
|
50
|
-
:
|
|
46
|
+
? options.workspaceId
|
|
47
|
+
: config.activeWorkspaceId || config.workspaceId || null;
|
|
51
48
|
const response = await fetch(url, {
|
|
52
49
|
method,
|
|
53
50
|
headers: {
|
package/dist/auth.d.ts
CHANGED
|
@@ -40,12 +40,6 @@ export type SkillState = {
|
|
|
40
40
|
export declare function setConfigFile(fileName: string): void;
|
|
41
41
|
export declare function getResolvedConfigsDir(): string | null;
|
|
42
42
|
export declare function getConfigPath(): string;
|
|
43
|
-
export declare function getLockedWorkspaceId(): string | null;
|
|
44
|
-
export declare function isWorkspaceLockRequired(): boolean;
|
|
45
|
-
export declare function workspaceLockRequiredMessage(action?: string): string;
|
|
46
|
-
export declare function assertWorkspaceLockSatisfied(action?: string): void;
|
|
47
|
-
export declare function getEffectiveConfiguredWorkspaceId(config: Pick<SellableConfig, "activeWorkspaceId" | "workspaceId">): string | null;
|
|
48
|
-
export declare function resolveWorkspaceIdForRequest(requestedWorkspaceId: string | null | undefined, action?: string): string | null;
|
|
49
43
|
export declare function getConfig(): SellableConfig;
|
|
50
44
|
export declare function updateActiveWorkspace(params: {
|
|
51
45
|
workspaceId: string;
|
package/dist/auth.js
CHANGED
|
@@ -2,8 +2,6 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as os from "os";
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
const DEFAULT_API_URL = "https://app.sellable.dev";
|
|
5
|
-
const WORKSPACE_LOCK_ENV = "SELLABLE_LOCK_WORKSPACE_ID";
|
|
6
|
-
const REQUIRE_WORKSPACE_LOCK_ENV = "SELLABLE_REQUIRE_WORKSPACE_LOCK";
|
|
7
5
|
// No caching — re-read config on every call so token/workspace changes
|
|
8
6
|
// made by the LLM (editing sellable.json) take effect immediately
|
|
9
7
|
// without requiring an MCP server restart.
|
|
@@ -87,44 +85,6 @@ function normalizeConfig(raw) {
|
|
|
87
85
|
apiUrl: raw.apiUrl || DEFAULT_API_URL,
|
|
88
86
|
};
|
|
89
87
|
}
|
|
90
|
-
export function getLockedWorkspaceId() {
|
|
91
|
-
const value = process.env[WORKSPACE_LOCK_ENV]?.trim();
|
|
92
|
-
return value || null;
|
|
93
|
-
}
|
|
94
|
-
export function isWorkspaceLockRequired() {
|
|
95
|
-
const value = process.env[REQUIRE_WORKSPACE_LOCK_ENV]?.trim().toLowerCase();
|
|
96
|
-
return value === "1" || value === "true" || value === "yes";
|
|
97
|
-
}
|
|
98
|
-
export function workspaceLockRequiredMessage(action = "Sellable request") {
|
|
99
|
-
return (`Workspace lock required: ${action} cannot run because ` +
|
|
100
|
-
`${REQUIRE_WORKSPACE_LOCK_ENV}=1 but ${WORKSPACE_LOCK_ENV} is missing. ` +
|
|
101
|
-
"Ask the sellable-admin profile to provision this customer profile with an approved customer workspace.");
|
|
102
|
-
}
|
|
103
|
-
export function assertWorkspaceLockSatisfied(action = "Sellable request") {
|
|
104
|
-
if (isWorkspaceLockRequired() && !getLockedWorkspaceId()) {
|
|
105
|
-
throw new Error(workspaceLockRequiredMessage(action));
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
function normalizeWorkspaceId(value) {
|
|
109
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
110
|
-
}
|
|
111
|
-
export function getEffectiveConfiguredWorkspaceId(config) {
|
|
112
|
-
assertWorkspaceLockSatisfied("workspace resolution");
|
|
113
|
-
return (getLockedWorkspaceId() ||
|
|
114
|
-
normalizeWorkspaceId(config.activeWorkspaceId) ||
|
|
115
|
-
normalizeWorkspaceId(config.workspaceId));
|
|
116
|
-
}
|
|
117
|
-
export function resolveWorkspaceIdForRequest(requestedWorkspaceId, action = "Sellable request") {
|
|
118
|
-
assertWorkspaceLockSatisfied(action);
|
|
119
|
-
const lockedWorkspaceId = getLockedWorkspaceId();
|
|
120
|
-
const requested = normalizeWorkspaceId(requestedWorkspaceId);
|
|
121
|
-
if (!lockedWorkspaceId)
|
|
122
|
-
return requested;
|
|
123
|
-
if (requested && requested !== lockedWorkspaceId) {
|
|
124
|
-
throw new Error(`Workspace lock violation: ${action} requested workspace ${requested}, but this profile is locked to ${lockedWorkspaceId}.`);
|
|
125
|
-
}
|
|
126
|
-
return lockedWorkspaceId;
|
|
127
|
-
}
|
|
128
88
|
export function getConfig() {
|
|
129
89
|
// Config cache removed 2026-05-04: we always re-read from disk so
|
|
130
90
|
// workspace switches (set_active_workspace) take effect immediately
|
|
@@ -179,7 +139,6 @@ export function getConfig() {
|
|
|
179
139
|
}
|
|
180
140
|
}
|
|
181
141
|
export function updateActiveWorkspace(params) {
|
|
182
|
-
resolveWorkspaceIdForRequest(params.workspaceId, "set_active_workspace");
|
|
183
142
|
const configPath = getConfigPath();
|
|
184
143
|
const configDir = path.dirname(configPath);
|
|
185
144
|
if (!fs.existsSync(configDir)) {
|
|
@@ -267,7 +226,6 @@ function writeRawConfigFile(configPath, raw) {
|
|
|
267
226
|
* new config without an MCP server restart.
|
|
268
227
|
*/
|
|
269
228
|
export function writeNewConfig(opts) {
|
|
270
|
-
resolveWorkspaceIdForRequest(opts.activeWorkspaceId, "write Sellable auth config");
|
|
271
229
|
const configPath = getConfigWritePath();
|
|
272
230
|
let raw = {};
|
|
273
231
|
if (fs.existsSync(configPath)) {
|
|
@@ -325,7 +283,7 @@ function getActiveEnvConfigRef(raw) {
|
|
|
325
283
|
}
|
|
326
284
|
export function getEngageState() {
|
|
327
285
|
const config = getConfig();
|
|
328
|
-
const workspaceId =
|
|
286
|
+
const workspaceId = config.activeWorkspaceId || config.workspaceId || null;
|
|
329
287
|
if (!workspaceId) {
|
|
330
288
|
return { activeWorkspaceId: null, state: null };
|
|
331
289
|
}
|
|
@@ -336,7 +294,7 @@ export function updateEngageState(patch) {
|
|
|
336
294
|
const { configPath, raw } = readRawConfigFile();
|
|
337
295
|
const { envConfig, set } = getActiveEnvConfigRef(raw);
|
|
338
296
|
const normalized = normalizeConfig(envConfig);
|
|
339
|
-
const workspaceId =
|
|
297
|
+
const workspaceId = normalized.activeWorkspaceId || normalized.workspaceId || null;
|
|
340
298
|
if (!workspaceId) {
|
|
341
299
|
throw new Error("No active workspace selected. Run list_workspaces then set_active_workspace.");
|
|
342
300
|
}
|
|
@@ -5,11 +5,6 @@ export type RefillRunBlockerResult = {
|
|
|
5
5
|
runId: string;
|
|
6
6
|
gate?: string;
|
|
7
7
|
gateSeq?: number;
|
|
8
|
-
} | {
|
|
9
|
-
blocker: "scope_drift" | "approval_expired" | "approval_fingerprint_mismatch";
|
|
10
|
-
runId: string;
|
|
11
|
-
expectedScopeHash: string | null;
|
|
12
|
-
receivedScopeHash: string | null;
|
|
13
8
|
};
|
|
14
9
|
export declare function startRefillRunRemote(input: {
|
|
15
10
|
workspaceId?: string;
|
|
@@ -15,21 +15,6 @@ function parseBlockerBody(body) {
|
|
|
15
15
|
gateSeq: typeof parsed.gateSeq === "number" ? parsed.gateSeq : undefined,
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
if ((parsed.blocker === "scope_drift" ||
|
|
19
|
-
parsed.blocker === "approval_expired" ||
|
|
20
|
-
parsed.blocker === "approval_fingerprint_mismatch") &&
|
|
21
|
-
typeof parsed.runId === "string") {
|
|
22
|
-
return {
|
|
23
|
-
blocker: parsed.blocker,
|
|
24
|
-
runId: parsed.runId,
|
|
25
|
-
expectedScopeHash: typeof parsed.expectedScopeHash === "string"
|
|
26
|
-
? parsed.expectedScopeHash
|
|
27
|
-
: null,
|
|
28
|
-
receivedScopeHash: typeof parsed.receivedScopeHash === "string"
|
|
29
|
-
? parsed.receivedScopeHash
|
|
30
|
-
: null,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
18
|
}
|
|
34
19
|
catch {
|
|
35
20
|
return null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { appendIndexLine, appendJournalEvent, createRunJournal, renderBootstrapSection, renderCrashedAbandonedSection, renderExecutionEventSection, renderPlanSection, renderTerminalSection } from "./refill-journal.js";
|
|
2
2
|
import type { advanceRefillRunGateRemote, completeRefillRun, heartbeatRefillRun, recordRunOutcome, recordRunPlanned, refillRunStatus, startRefillRunRemote } from "./refill-run-client.js";
|
|
3
|
-
import type { getPrepareCampaignMessagesStatus
|
|
3
|
+
import type { getPrepareCampaignMessagesStatus } from "./tools/campaign-message-preparation.js";
|
|
4
4
|
import type { executeOneYoloPrimitive, executeStartCampaignPrimitive, prepareRowSelectorValue, refillPrepareRequestHash, refreshPaidInmailCreditsWithRetry } from "./tools/refill-executors.js";
|
|
5
5
|
export type RefillLoopGate = "g0_bootstrap" | "g1_plan" | "g2_execute" | "g3_verify" | "g4_terminal";
|
|
6
6
|
export interface RefillV2LoopInput {
|
|
@@ -13,12 +13,6 @@ export interface RefillV2LoopInput {
|
|
|
13
13
|
};
|
|
14
14
|
approvalMode?: "approve" | "mark_ready";
|
|
15
15
|
dryRun?: boolean;
|
|
16
|
-
targetDate?: string;
|
|
17
|
-
untilDate?: string;
|
|
18
|
-
horizonSendDays?: number;
|
|
19
|
-
scope?: Record<string, unknown>;
|
|
20
|
-
scopeHash?: string;
|
|
21
|
-
approval?: Record<string, unknown>;
|
|
22
16
|
}
|
|
23
17
|
export interface RefillV2LoopBudgets {
|
|
24
18
|
maxGateCyclesPerInvocation: number;
|
|
@@ -42,7 +36,6 @@ export interface RefillV2Executors {
|
|
|
42
36
|
executeStartCampaignPrimitive: typeof executeStartCampaignPrimitive;
|
|
43
37
|
refreshPaidInmailCreditsWithRetry: typeof refreshPaidInmailCreditsWithRetry;
|
|
44
38
|
getPrepareCampaignMessagesStatus?: typeof getPrepareCampaignMessagesStatus;
|
|
45
|
-
stopSatisfiedPrepareCampaignMessages?: typeof stopSatisfiedPrepareCampaignMessages;
|
|
46
39
|
refillPrepareRequestHash?: typeof refillPrepareRequestHash;
|
|
47
40
|
prepareRowSelectorValue?: typeof prepareRowSelectorValue;
|
|
48
41
|
}
|
|
@@ -68,10 +61,6 @@ export interface RefillV2LoopDeps {
|
|
|
68
61
|
senderIds?: string[];
|
|
69
62
|
runState?: Record<string, unknown>;
|
|
70
63
|
journal?: boolean;
|
|
71
|
-
targetDate?: string;
|
|
72
|
-
untilDate?: string;
|
|
73
|
-
horizonSendDays?: number;
|
|
74
|
-
scopeHash?: string;
|
|
75
64
|
}) => Promise<Record<string, unknown>>;
|
|
76
65
|
executors: RefillV2Executors;
|
|
77
66
|
journal: RefillV2JournalDeps;
|
package/dist/refill-run-loop.js
CHANGED
|
@@ -59,16 +59,6 @@ const SCHEDULER_RUN_NEXT_ACTIONS = new Set([
|
|
|
59
59
|
function isRecord(value) {
|
|
60
60
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
61
61
|
}
|
|
62
|
-
function planScopeInput(input) {
|
|
63
|
-
return {
|
|
64
|
-
...(input.targetDate ? { targetDate: input.targetDate } : {}),
|
|
65
|
-
...(input.untilDate ? { untilDate: input.untilDate } : {}),
|
|
66
|
-
...(input.horizonSendDays !== undefined
|
|
67
|
-
? { horizonSendDays: input.horizonSendDays }
|
|
68
|
-
: {}),
|
|
69
|
-
...(input.scopeHash ? { scopeHash: input.scopeHash } : {}),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
62
|
function stringValue(value) {
|
|
73
63
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
74
64
|
}
|
|
@@ -266,8 +256,10 @@ function sanitizeSchedulerRunReceipt(raw) {
|
|
|
266
256
|
const prefilteredCells = numberValue(rawReceipt.prefilteredCells) ?? 0;
|
|
267
257
|
const tablesFilteredForSenderMismatch = numberValue(rawReceipt.tablesFilteredForSenderMismatch) ?? 0;
|
|
268
258
|
dominantSkipReason = dominantSchedulerRunSkipReason(skipReasons);
|
|
269
|
-
dominantPrefilterReason =
|
|
270
|
-
|
|
259
|
+
dominantPrefilterReason =
|
|
260
|
+
dominantSchedulerRunCountReason(prefilterReasons);
|
|
261
|
+
dominantDeferredReason =
|
|
262
|
+
dominantSchedulerRunSkipReason(deferredReasons);
|
|
271
263
|
recommendedNextAction = schedulerRunRecommendedAction({
|
|
272
264
|
rawAction: stringValue(rawReceipt.nextAction),
|
|
273
265
|
readyCellsFound,
|
|
@@ -437,43 +429,6 @@ function normalizeDoneReason(value) {
|
|
|
437
429
|
function runStateProgress(ctx) {
|
|
438
430
|
return recordValue(ctx.runState.progress) ?? {};
|
|
439
431
|
}
|
|
440
|
-
function prepCircuitFrom(value) {
|
|
441
|
-
const root = recordValue(value);
|
|
442
|
-
const result = recordValue(root?.result);
|
|
443
|
-
const progress = recordValue(root?.progress);
|
|
444
|
-
return (recordValue(root?.circuit) ??
|
|
445
|
-
recordValue(result?.circuit) ??
|
|
446
|
-
recordValue(progress?.circuit) ??
|
|
447
|
-
null);
|
|
448
|
-
}
|
|
449
|
-
function openCircuitWaits(circuit, now) {
|
|
450
|
-
if (stringValue(circuit?.state) !== "open")
|
|
451
|
-
return false;
|
|
452
|
-
const nextProbeAt = stringValue(circuit?.nextProbeAt);
|
|
453
|
-
return Boolean(nextProbeAt && Date.parse(nextProbeAt) > now.getTime());
|
|
454
|
-
}
|
|
455
|
-
function activePreparationScopeSatisfied(plan, action) {
|
|
456
|
-
const packet = recordValue(plan.packet) ?? plan;
|
|
457
|
-
const target = recordValue(packet.target) ?? recordValue(plan.target) ?? {};
|
|
458
|
-
const senderId = actionSenderId(action);
|
|
459
|
-
const toolInput = actionToolInput(action);
|
|
460
|
-
const targetDate = stringValue(toolInput.targetDate);
|
|
461
|
-
const refillLaneKey = stringValue(toolInput.refillLaneKey);
|
|
462
|
-
const senderPlans = arrayValue(target.senderRefillPlans).filter(isRecord);
|
|
463
|
-
const ledgers = senderPlans
|
|
464
|
-
.filter((senderPlan) => !senderId || stringValue(senderPlan.senderId) === senderId)
|
|
465
|
-
.flatMap((senderPlan) => arrayValue(senderPlan.laneLedgers).filter(isRecord));
|
|
466
|
-
const matching = ledgers.filter((ledger) => (!senderId || stringValue(ledger.senderId) === senderId) &&
|
|
467
|
-
(!targetDate || stringValue(ledger.targetDate) === targetDate) &&
|
|
468
|
-
(!refillLaneKey || stringValue(ledger.refillLaneKey) === refillLaneKey));
|
|
469
|
-
return (matching.length > 0 &&
|
|
470
|
-
matching.every((ledger) => {
|
|
471
|
-
const checks = recordValue(ledger.identityChecks);
|
|
472
|
-
return (numberValue(ledger.initialAllocatablePrepDeficit) === 0 &&
|
|
473
|
-
checks != null &&
|
|
474
|
-
Object.values(checks).every((value) => value === true));
|
|
475
|
-
}));
|
|
476
|
-
}
|
|
477
432
|
function mergeProgress(ctx, updates) {
|
|
478
433
|
return {
|
|
479
434
|
...runStateProgress(ctx),
|
|
@@ -717,28 +672,14 @@ async function createJournal(input, deps, runId) {
|
|
|
717
672
|
}
|
|
718
673
|
async function startOrResume(input, deps) {
|
|
719
674
|
const initialRunState = buildRunStateFromLocalHints(input.workspaceId);
|
|
720
|
-
const scopedInitialRunState = input.scope
|
|
721
|
-
? {
|
|
722
|
-
...initialRunState,
|
|
723
|
-
scope: input.scope,
|
|
724
|
-
scopeHash: input.scopeHash,
|
|
725
|
-
approval: input.approval,
|
|
726
|
-
}
|
|
727
|
-
: initialRunState;
|
|
728
675
|
const started = await deps.runClient.startRefillRunRemote({
|
|
729
676
|
workspaceId: input.workspaceId,
|
|
730
|
-
config:
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
: {
|
|
737
|
-
intent: input.intent ?? "auto",
|
|
738
|
-
senderIds: input.senderIds,
|
|
739
|
-
approvalMode: input.approvalMode ?? "approve",
|
|
740
|
-
},
|
|
741
|
-
runState: scopedInitialRunState,
|
|
677
|
+
config: {
|
|
678
|
+
intent: input.intent ?? "auto",
|
|
679
|
+
senderIds: input.senderIds,
|
|
680
|
+
approvalMode: input.approvalMode ?? "approve",
|
|
681
|
+
},
|
|
682
|
+
runState: initialRunState,
|
|
742
683
|
resume: input.resume,
|
|
743
684
|
});
|
|
744
685
|
if (isLeaseLost(started)) {
|
|
@@ -771,7 +712,7 @@ async function startOrResume(input, deps) {
|
|
|
771
712
|
const gate = (stringValue(result.gate) ?? "g0_bootstrap");
|
|
772
713
|
const gateSeq = numberValue(result.gateSeq) ?? 0;
|
|
773
714
|
const runState = recordValue(result.runState) ??
|
|
774
|
-
recordValue(
|
|
715
|
+
recordValue(initialRunState) ??
|
|
775
716
|
{
|
|
776
717
|
version: 1,
|
|
777
718
|
senderCursors: [],
|
|
@@ -814,7 +755,6 @@ async function gateBootstrap(input, deps, ctx) {
|
|
|
814
755
|
senderIds: input.senderIds,
|
|
815
756
|
runState: ctx.runState,
|
|
816
757
|
journal: false,
|
|
817
|
-
...planScopeInput(input),
|
|
818
758
|
});
|
|
819
759
|
const paidCredit = arrayValue(recordValue(plan.bootstrap)?.paidCredit).filter(isRecord);
|
|
820
760
|
const refreshedSenderIds = new Set(arrayValue(recordValue(ctx.runState.creditTrust)?.senderIds)
|
|
@@ -961,7 +901,6 @@ async function gatePlan(input, deps, ctx) {
|
|
|
961
901
|
senderIds: input.senderIds,
|
|
962
902
|
runState: ctx.runState,
|
|
963
903
|
journal: false,
|
|
964
|
-
...planScopeInput(input),
|
|
965
904
|
});
|
|
966
905
|
if (plan.blocker === "workspace_access") {
|
|
967
906
|
return {
|
|
@@ -1021,17 +960,6 @@ async function gatePlan(input, deps, ctx) {
|
|
|
1021
960
|
fallbackSummary: line.fallbackSummary,
|
|
1022
961
|
}));
|
|
1023
962
|
const fingerprint = fingerprintFromPlan(plan);
|
|
1024
|
-
const persistedCircuit = recordValue(runStateProgress(ctx).prepCircuit);
|
|
1025
|
-
if (stringValue(action.type) === "prepare_messages" &&
|
|
1026
|
-
openCircuitWaits(persistedCircuit, deps.now?.() ?? new Date())) {
|
|
1027
|
-
const heartbeat = await heartbeatAndContinue(input, deps, ctx);
|
|
1028
|
-
if (heartbeat.status !== "in_progress")
|
|
1029
|
-
return heartbeat;
|
|
1030
|
-
return {
|
|
1031
|
-
...heartbeat,
|
|
1032
|
-
guidance: `Preparation circuit remains open until ${String(persistedCircuit?.nextProbeAt)}; re-invoke with this handle after the probe deadline.`,
|
|
1033
|
-
};
|
|
1034
|
-
}
|
|
1035
963
|
const noProgress = nextNoProgressState(ctx, plan, action);
|
|
1036
964
|
if (noProgress.repeatCount >= 3) {
|
|
1037
965
|
return completeTerminal(input, deps, ctx, "blocked", {
|
|
@@ -1042,17 +970,10 @@ async function gatePlan(input, deps, ctx) {
|
|
|
1042
970
|
guidance: "The same refill rung repeated three times with no coverage, frontier, or lane movement.",
|
|
1043
971
|
});
|
|
1044
972
|
}
|
|
1045
|
-
const circuitProgress = stringValue(action.type) === "prepare_messages" &&
|
|
1046
|
-
stringValue(persistedCircuit?.state) === "open"
|
|
1047
|
-
? { ...persistedCircuit, state: "half_open" }
|
|
1048
|
-
: persistedCircuit;
|
|
1049
973
|
const nextRunState = mergeRunState(ctx.runState, {
|
|
1050
974
|
lastPlan: fingerprint,
|
|
1051
975
|
headAction: action,
|
|
1052
|
-
progress: mergeProgress(ctx, {
|
|
1053
|
-
noProgress,
|
|
1054
|
-
...(circuitProgress ? { prepCircuit: circuitProgress } : {}),
|
|
1055
|
-
}),
|
|
976
|
+
progress: mergeProgress(ctx, { noProgress }),
|
|
1056
977
|
});
|
|
1057
978
|
const advanced = await deps.runClient.advanceRefillRunGateRemote({
|
|
1058
979
|
workspaceId: input.workspaceId,
|
|
@@ -1089,7 +1010,6 @@ async function verifyRecoveryPoint(input, deps, ctx) {
|
|
|
1089
1010
|
senderIds: input.senderIds,
|
|
1090
1011
|
runState: ctx.runState,
|
|
1091
1012
|
journal: false,
|
|
1092
|
-
...planScopeInput(input),
|
|
1093
1013
|
});
|
|
1094
1014
|
const changed = fingerprintChanged(expected, fingerprintFromPlan(fresh));
|
|
1095
1015
|
if (changed.length > 0) {
|
|
@@ -1159,9 +1079,6 @@ async function gateExecute(input, deps, ctx) {
|
|
|
1159
1079
|
dispatchKey: stringValue(action.type),
|
|
1160
1080
|
evergreenRung: stringValue(action.evergreenRung),
|
|
1161
1081
|
sideEffectClass: stringValue(action.sideEffectClass),
|
|
1162
|
-
scopeHash: input.scopeHash ?? null,
|
|
1163
|
-
expectedTargetsHash: stringValue(input.scope?.expectedTargetsHash) ?? null,
|
|
1164
|
-
dateSelector: recordValue(input.scope?.dateSelector),
|
|
1165
1082
|
};
|
|
1166
1083
|
const plannedResult = await deps.runClient.recordRunPlanned({
|
|
1167
1084
|
workspaceId: input.workspaceId,
|
|
@@ -1255,12 +1172,6 @@ async function advanceBackToPlan(input, deps, ctx, runStateUpdates = {}) {
|
|
|
1255
1172
|
async function verifyPrepareAction(input, deps, ctx, action, budgets) {
|
|
1256
1173
|
const getStatus = deps.executors.getPrepareCampaignMessagesStatus;
|
|
1257
1174
|
const jobId = activeJobIdFromOutcome(ctx.runState.lastOutcome);
|
|
1258
|
-
const executionCircuit = prepCircuitFrom(ctx.runState.lastOutcome);
|
|
1259
|
-
if (!jobId && executionCircuit) {
|
|
1260
|
-
return advanceBackToPlan(input, deps, ctx, {
|
|
1261
|
-
progress: mergeProgress(ctx, { prepCircuit: executionCircuit }),
|
|
1262
|
-
});
|
|
1263
|
-
}
|
|
1264
1175
|
if (!getStatus || !jobId) {
|
|
1265
1176
|
return {
|
|
1266
1177
|
status: "in_progress",
|
|
@@ -1300,58 +1211,6 @@ async function verifyPrepareAction(input, deps, ctx, action, budgets) {
|
|
|
1300
1211
|
}),
|
|
1301
1212
|
});
|
|
1302
1213
|
}
|
|
1303
|
-
if (ownJob &&
|
|
1304
|
-
!isTerminalJobStatus(latestStatus) &&
|
|
1305
|
-
deps.executors.stopSatisfiedPrepareCampaignMessages) {
|
|
1306
|
-
const fresh = await deps.readPlan({
|
|
1307
|
-
workspaceId: input.workspaceId,
|
|
1308
|
-
intent: input.intent,
|
|
1309
|
-
senderIds: input.senderIds,
|
|
1310
|
-
runState: ctx.runState,
|
|
1311
|
-
journal: false,
|
|
1312
|
-
...planScopeInput(input),
|
|
1313
|
-
});
|
|
1314
|
-
ctx.plan = fresh;
|
|
1315
|
-
if (activePreparationScopeSatisfied(fresh, action)) {
|
|
1316
|
-
const plannedStop = await deps.runClient.recordRunPlanned({
|
|
1317
|
-
workspaceId: input.workspaceId,
|
|
1318
|
-
runId: ctx.runId,
|
|
1319
|
-
fence: ctx.fence,
|
|
1320
|
-
gate: ctx.gate,
|
|
1321
|
-
gateSeq: ctx.gateSeq,
|
|
1322
|
-
planned: {
|
|
1323
|
-
toolName: "stop_satisfied_campaign_message_preparation",
|
|
1324
|
-
toolInput: { jobId },
|
|
1325
|
-
dispatchKey: `stop_satisfied:${jobId}`,
|
|
1326
|
-
sideEffectClass: "message_preparation_stop",
|
|
1327
|
-
scopeHash: input.scopeHash ?? null,
|
|
1328
|
-
},
|
|
1329
|
-
});
|
|
1330
|
-
if (isLeaseLost(plannedStop))
|
|
1331
|
-
return leaseLostResult(ctx);
|
|
1332
|
-
const stopEventId = stringValue(recordValue(plannedStop)?.eventId) ??
|
|
1333
|
-
`${ctx.runId}:${ctx.gateSeq}:stop`;
|
|
1334
|
-
const stopReceipt = await deps.executors.stopSatisfiedPrepareCampaignMessages({
|
|
1335
|
-
workspaceId: input.workspaceId,
|
|
1336
|
-
jobId,
|
|
1337
|
-
expectedUpdatedAt: stringValue(statusRecord.updatedAt) ?? undefined,
|
|
1338
|
-
});
|
|
1339
|
-
await deps.runClient.recordRunOutcome({
|
|
1340
|
-
workspaceId: input.workspaceId,
|
|
1341
|
-
runId: ctx.runId,
|
|
1342
|
-
fence: ctx.fence,
|
|
1343
|
-
eventId: stopEventId,
|
|
1344
|
-
did: { jobId },
|
|
1345
|
-
outcome: stopReceipt,
|
|
1346
|
-
});
|
|
1347
|
-
return advanceBackToPlan(input, deps, ctx, {
|
|
1348
|
-
progress: mergeProgress(ctx, {
|
|
1349
|
-
prepStopReceipt: stopReceipt,
|
|
1350
|
-
prepStoppedAt: (deps.now?.() ?? new Date()).toISOString(),
|
|
1351
|
-
}),
|
|
1352
|
-
});
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
1214
|
if (isTerminalJobStatus(latestStatus)) {
|
|
1356
1215
|
const preparedCount = preparedCountFromStatus(latestStatus);
|
|
1357
1216
|
const checkedRows = checkedRowsFromStatus(latestStatus);
|
|
@@ -1419,7 +1278,6 @@ async function verifyReadOnlyWait(input, deps, ctx, budgets, waitKind) {
|
|
|
1419
1278
|
senderIds: input.senderIds,
|
|
1420
1279
|
runState: ctx.runState,
|
|
1421
1280
|
journal: false,
|
|
1422
|
-
...planScopeInput(input),
|
|
1423
1281
|
});
|
|
1424
1282
|
ctx.plan = fresh;
|
|
1425
1283
|
const doneReason = doneReasonFromPlan(fresh);
|
|
@@ -1502,7 +1360,6 @@ async function verifySchedulerWait(input, deps, ctx, action, budgets) {
|
|
|
1502
1360
|
senderIds: input.senderIds,
|
|
1503
1361
|
runState: ctx.runState,
|
|
1504
1362
|
journal: false,
|
|
1505
|
-
...planScopeInput(input),
|
|
1506
1363
|
});
|
|
1507
1364
|
ctx.plan = fresh;
|
|
1508
1365
|
const doneReason = normalizeDoneReason(doneReasonFromPlan(fresh));
|
|
@@ -1565,7 +1422,6 @@ async function gateVerify(input, deps, ctx, budgets) {
|
|
|
1565
1422
|
senderIds: input.senderIds,
|
|
1566
1423
|
runState: ctx.runState,
|
|
1567
1424
|
journal: false,
|
|
1568
|
-
...planScopeInput(input),
|
|
1569
1425
|
});
|
|
1570
1426
|
ctx.plan = fresh;
|
|
1571
1427
|
const doneReason = doneReasonFromPlan(fresh);
|
|
@@ -1630,7 +1486,7 @@ async function heartbeatAndContinue(input, deps, ctx) {
|
|
|
1630
1486
|
runId: ctx.runId,
|
|
1631
1487
|
fence: ctx.fence,
|
|
1632
1488
|
gate: ctx.gate,
|
|
1633
|
-
guidance: "Per-invocation gate budget reached. Re-invoke
|
|
1489
|
+
guidance: "Per-invocation gate budget reached. Re-invoke refill_sends_v2 with this runId/fence to continue.",
|
|
1634
1490
|
journalPath: ctx.journalPath,
|
|
1635
1491
|
};
|
|
1636
1492
|
}
|
|
@@ -1644,7 +1500,6 @@ export async function runRefillV2Loop(input, deps) {
|
|
|
1644
1500
|
intent: input.intent,
|
|
1645
1501
|
senderIds: input.senderIds,
|
|
1646
1502
|
journal: true,
|
|
1647
|
-
...planScopeInput(input),
|
|
1648
1503
|
});
|
|
1649
1504
|
return { status: "dry_run", plan };
|
|
1650
1505
|
}
|
package/dist/server.js
CHANGED
|
@@ -29,6 +29,7 @@ import { getEngageStateTool, setEngageStateTool, } from "./tools/engage-state.js
|
|
|
29
29
|
import { bulkEnrichWithProspeo, enrichWithProspeo, getProspeoCredits, } from "./tools/enrichment.js";
|
|
30
30
|
import { getRefillPlanV2 } from "./tools/evergreen-refill-plan.js";
|
|
31
31
|
import { getCampaignFramework } from "./tools/framework.js";
|
|
32
|
+
import { bootstrapFindLeads, cancelFindLeads, getFindLeadsRun, reissueFindLeadsWatchLink, updateFindLeadsRun, waitForFindLeadsRun, } from "./tools/find-leads-runs.js";
|
|
32
33
|
import { confirmHarvestJobCompanies, searchHarvestJobs, } from "./tools/harvest-jobs.js";
|
|
33
34
|
import { checkInboxReplyEligibility, getInboxThread, searchInboxThreads, sendInboxDraft, sendInboxManualReply, updateInboxDraft, } from "./tools/inbox.js";
|
|
34
35
|
import { cancelLeadImport, confirmLeadList, confirmProspeoCompanyAccounts, getProviderPrompt, importLeads, listDncEntriesTool, loadCsvDncEntriesTool, loadCsvDomains, loadCsvLinkedinLeads, lookupSalesNavFilter, saveDomainFilters, searchApollo, searchProspeo, searchProspeoCompanies, searchSalesNav, searchSignals, selectPromisingPosts, setHeadlineICPCriteria, } from "./tools/leads.js";
|
|
@@ -36,6 +37,7 @@ import { fetchCompany, fetchCompanyPosts, fetchLinkedInPosts, fetchLinkedInProfi
|
|
|
36
37
|
import { getCampaignNavigationState } from "./tools/navigation.js";
|
|
37
38
|
import { addOnDemandLeads, createOnDemandCampaign, createOnDemandTable, initOnDemandSequence, pauseOnDemandCampaign, startOnDemandCampaign, } from "./tools/one-off.js";
|
|
38
39
|
import { upsertRubric } from "./tools/processing.js";
|
|
40
|
+
import { preflightFindLeadsProvider } from "./tools/provider-preflight.js";
|
|
39
41
|
import { completeSenderResearch, getPostFindLeadsScoutRegistry, getSourceScoutRegistry, getSubskillAsset, getSubskillPrompt, listSubskillPrompts, searchSubskillPrompts, } from "./tools/prompts.js";
|
|
40
42
|
import { waitForCampaignTableReady, waitForLeadListReady, } from "./tools/readiness.js";
|
|
41
43
|
import { refreshSenderEngagementCommand } from "./tools/refresh-sender-engagement.js";
|
|
@@ -146,6 +148,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
146
148
|
case "wait_for_cli_login":
|
|
147
149
|
result = await handleWaitForCliLogin(args);
|
|
148
150
|
break;
|
|
151
|
+
case "bootstrap_find_leads":
|
|
152
|
+
result = await bootstrapFindLeads(args);
|
|
153
|
+
break;
|
|
154
|
+
case "get_find_leads_run":
|
|
155
|
+
result = await getFindLeadsRun(args);
|
|
156
|
+
break;
|
|
157
|
+
case "update_find_leads_run":
|
|
158
|
+
result = await updateFindLeadsRun(args);
|
|
159
|
+
break;
|
|
160
|
+
case "wait_for_find_leads_run":
|
|
161
|
+
result = await waitForFindLeadsRun(args);
|
|
162
|
+
break;
|
|
163
|
+
case "cancel_find_leads":
|
|
164
|
+
result = await cancelFindLeads(args);
|
|
165
|
+
break;
|
|
166
|
+
case "reissue_find_leads_watch_link":
|
|
167
|
+
result = await reissueFindLeadsWatchLink(args);
|
|
168
|
+
break;
|
|
169
|
+
case "preflight_find_leads_provider":
|
|
170
|
+
result = await preflightFindLeadsProvider(args);
|
|
171
|
+
break;
|
|
149
172
|
case "bootstrap_engage":
|
|
150
173
|
result = await bootstrapEngage(args);
|
|
151
174
|
break;
|
package/dist/tools/auth.d.ts
CHANGED
|
@@ -11,11 +11,6 @@ export type AuthStatus = {
|
|
|
11
11
|
tokenPresent: boolean;
|
|
12
12
|
tokenPrefix: string | null;
|
|
13
13
|
workspacesCount: number | null;
|
|
14
|
-
workspaceLock: {
|
|
15
|
-
enabled: boolean;
|
|
16
|
-
workspaceId: string | null;
|
|
17
|
-
required: boolean;
|
|
18
|
-
};
|
|
19
14
|
checkedAt: string;
|
|
20
15
|
/** Short message the LLM MUST show the user after auth succeeds. */
|
|
21
16
|
_userNotice: string | null;
|