@sellable/mcp 0.1.746 → 0.1.748
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/agent-tool-names.d.ts +6 -0
- package/dist/agent-tool-names.js +182 -0
- package/dist/agent-tool-policy.js +51 -22
- package/dist/tools/refill-executors.d.ts +2 -0
- package/dist/tools/refill-executors.js +79 -1
- package/dist/tools/refill-sends.d.ts +5 -0
- package/dist/tools/refill-sends.js +43 -4
- package/package.json +1 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight projection of the canonical MCP registry for control-plane
|
|
3
|
+
* manifests. Keep this free of tool implementation imports so Next can bundle
|
|
4
|
+
* it; registry parity is enforced in sellable-agent-tool-policy.test.ts.
|
|
5
|
+
*/
|
|
6
|
+
export declare const SELLABLE_MCP_TOOL_NAMES: readonly ["add_column", "add_on_demand_leads", "add_rubric_item", "add_teammate", "add_to_comment_campaign", "add_to_connection_campaign", "add_to_inmail_campaign", "archive_campaign", "attach_recommended_sequence", "attach_sequence", "bootstrap_create_campaign", "bootstrap_engage", "bootstrap_engage_multi", "bootstrap_find_leads", "bulk_enrich_with_prospeo", "calculate_linkedin_hook_preview", "cancel_campaign_message_preparation", "cancel_find_leads", "cancel_lead_import", "capture_post_idea", "check_inbox_reply_eligibility", "check_rubric", "commit_blueprint", "complete_sender_research", "confirm_harvest_job_companies", "confirm_lead_list", "confirm_prospeo_company_accounts", "copy_sender_config", "create_campaign", "create_on_demand_campaign", "create_on_demand_table", "create_workflow_table", "create_workspace", "delete_column", "delete_rubric_item", "draft_rubrics", "duplicate_campaign", "enrich_with_prospeo", "export_table_csv", "export_workspace_csv", "fetch_company", "fetch_company_posts", "fetch_linkedin_posts", "fetch_linkedin_profile", "fetch_post_engagers", "fill_campaign_horizon", "get_active_workspace", "get_auth_status", "get_campaign", "get_campaign_context", "get_campaign_framework", "get_campaign_message_preparation_status", "get_campaign_messages_preview", "get_campaign_navigation_state", "get_campaign_refill_state", "get_campaign_table_schema", "get_campaign_waterfall", "get_campaigns", "get_column_schema", "get_company_info", "get_engage_memory", "get_engage_state", "get_engaged_posts", "get_find_leads_run", "get_inbox_thread", "get_linkedin_profile", "get_or_create_direct_campaign_table", "get_post_draft", "get_post_find_leads_scout_registry", "get_post_idea", "get_prospeo_credits", "get_provider_prompt", "get_published_post", "get_refill_plan_v2", "get_refill_target_plan", "get_rows", "get_rows_minimal", "get_runtime_identity", "get_scheduler_fill_capacity", "get_sender", "get_sender_routing", "get_source_scout_registry", "get_subskill_asset", "get_subskill_prompt", "get_table_rows", "get_user_posts", "get_workspace", "import_leads", "init_on_demand_sequence", "list_column_types", "list_dnc_entries", "list_post_draft_iterations", "list_post_drafts", "list_post_ideas", "list_published_posts", "list_senders", "list_subskill_prompts", "list_tables", "list_workspaces", "load_csv_dnc_entries", "load_csv_domains", "load_csv_linkedin_leads", "lookup_sales_nav_filter", "mark_post_published", "migrate_flat_configs", "pause_campaign", "pause_direct_campaign", "pause_on_demand_campaign", "preflight_find_leads_provider", "prepare_campaign_ab_test", "queue_campaign_cells", "queue_cells", "record_campaign_review_batch", "record_engage_proven_search", "refill_sends", "refill_sends_v2", "refresh_paid_inmail_credits", "refresh_sender_engagement", "reissue_find_leads_watch_link", "render_linkedin_post_preview", "reorder_columns", "resolve_campaign_fill_route", "revise_message_template_and_rerun", "run_scheduler_sweep", "save_domain_filters", "save_hook_research", "save_post_draft", "save_rubrics", "search_apollo", "search_engagement_posts", "search_harvest_jobs", "search_inbox_threads", "search_prospeo", "search_prospeo_companies", "search_sales_nav", "search_signals", "search_subskill_prompts", "select_campaign_cells", "select_necessary_rubrics", "select_promising_posts", "send_inbox_draft", "send_inbox_manual_reply", "set_active_workspace", "set_campaign_waterfall_order", "set_company_info", "set_engage_state", "set_engage_style_guide", "set_headline_icp_criteria", "set_sender_calendar_link", "set_sender_routing", "setup_evergreen_campaigns", "start_campaign", "start_campaign_message_preparation", "start_cli_login", "start_direct_campaign", "start_on_demand_campaign", "update_campaign", "update_campaign_brief", "update_cell", "update_column", "update_find_leads_run", "update_inbox_draft", "update_post_draft", "update_published_post_metrics", "update_rubric_item", "upsert_engage_tracked_person", "upsert_rubric", "validate_campaign1_kickoff_handoff", "verify_table_row", "wait_for_campaign_processing", "wait_for_campaign_table_ready", "wait_for_cli_login", "wait_for_find_leads_run", "wait_for_lead_list_ready", "wait_for_rubric_results"];
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight projection of the canonical MCP registry for control-plane
|
|
3
|
+
* manifests. Keep this free of tool implementation imports so Next can bundle
|
|
4
|
+
* it; registry parity is enforced in sellable-agent-tool-policy.test.ts.
|
|
5
|
+
*/
|
|
6
|
+
export const SELLABLE_MCP_TOOL_NAMES = [
|
|
7
|
+
"add_column",
|
|
8
|
+
"add_on_demand_leads",
|
|
9
|
+
"add_rubric_item",
|
|
10
|
+
"add_teammate",
|
|
11
|
+
"add_to_comment_campaign",
|
|
12
|
+
"add_to_connection_campaign",
|
|
13
|
+
"add_to_inmail_campaign",
|
|
14
|
+
"archive_campaign",
|
|
15
|
+
"attach_recommended_sequence",
|
|
16
|
+
"attach_sequence",
|
|
17
|
+
"bootstrap_create_campaign",
|
|
18
|
+
"bootstrap_engage",
|
|
19
|
+
"bootstrap_engage_multi",
|
|
20
|
+
"bootstrap_find_leads",
|
|
21
|
+
"bulk_enrich_with_prospeo",
|
|
22
|
+
"calculate_linkedin_hook_preview",
|
|
23
|
+
"cancel_campaign_message_preparation",
|
|
24
|
+
"cancel_find_leads",
|
|
25
|
+
"cancel_lead_import",
|
|
26
|
+
"capture_post_idea",
|
|
27
|
+
"check_inbox_reply_eligibility",
|
|
28
|
+
"check_rubric",
|
|
29
|
+
"commit_blueprint",
|
|
30
|
+
"complete_sender_research",
|
|
31
|
+
"confirm_harvest_job_companies",
|
|
32
|
+
"confirm_lead_list",
|
|
33
|
+
"confirm_prospeo_company_accounts",
|
|
34
|
+
"copy_sender_config",
|
|
35
|
+
"create_campaign",
|
|
36
|
+
"create_on_demand_campaign",
|
|
37
|
+
"create_on_demand_table",
|
|
38
|
+
"create_workflow_table",
|
|
39
|
+
"create_workspace",
|
|
40
|
+
"delete_column",
|
|
41
|
+
"delete_rubric_item",
|
|
42
|
+
"draft_rubrics",
|
|
43
|
+
"duplicate_campaign",
|
|
44
|
+
"enrich_with_prospeo",
|
|
45
|
+
"export_table_csv",
|
|
46
|
+
"export_workspace_csv",
|
|
47
|
+
"fetch_company",
|
|
48
|
+
"fetch_company_posts",
|
|
49
|
+
"fetch_linkedin_posts",
|
|
50
|
+
"fetch_linkedin_profile",
|
|
51
|
+
"fetch_post_engagers",
|
|
52
|
+
"fill_campaign_horizon",
|
|
53
|
+
"get_active_workspace",
|
|
54
|
+
"get_auth_status",
|
|
55
|
+
"get_campaign",
|
|
56
|
+
"get_campaign_context",
|
|
57
|
+
"get_campaign_framework",
|
|
58
|
+
"get_campaign_message_preparation_status",
|
|
59
|
+
"get_campaign_messages_preview",
|
|
60
|
+
"get_campaign_navigation_state",
|
|
61
|
+
"get_campaign_refill_state",
|
|
62
|
+
"get_campaign_table_schema",
|
|
63
|
+
"get_campaign_waterfall",
|
|
64
|
+
"get_campaigns",
|
|
65
|
+
"get_column_schema",
|
|
66
|
+
"get_company_info",
|
|
67
|
+
"get_engage_memory",
|
|
68
|
+
"get_engage_state",
|
|
69
|
+
"get_engaged_posts",
|
|
70
|
+
"get_find_leads_run",
|
|
71
|
+
"get_inbox_thread",
|
|
72
|
+
"get_linkedin_profile",
|
|
73
|
+
"get_or_create_direct_campaign_table",
|
|
74
|
+
"get_post_draft",
|
|
75
|
+
"get_post_find_leads_scout_registry",
|
|
76
|
+
"get_post_idea",
|
|
77
|
+
"get_prospeo_credits",
|
|
78
|
+
"get_provider_prompt",
|
|
79
|
+
"get_published_post",
|
|
80
|
+
"get_refill_plan_v2",
|
|
81
|
+
"get_refill_target_plan",
|
|
82
|
+
"get_rows",
|
|
83
|
+
"get_rows_minimal",
|
|
84
|
+
"get_runtime_identity",
|
|
85
|
+
"get_scheduler_fill_capacity",
|
|
86
|
+
"get_sender",
|
|
87
|
+
"get_sender_routing",
|
|
88
|
+
"get_source_scout_registry",
|
|
89
|
+
"get_subskill_asset",
|
|
90
|
+
"get_subskill_prompt",
|
|
91
|
+
"get_table_rows",
|
|
92
|
+
"get_user_posts",
|
|
93
|
+
"get_workspace",
|
|
94
|
+
"import_leads",
|
|
95
|
+
"init_on_demand_sequence",
|
|
96
|
+
"list_column_types",
|
|
97
|
+
"list_dnc_entries",
|
|
98
|
+
"list_post_draft_iterations",
|
|
99
|
+
"list_post_drafts",
|
|
100
|
+
"list_post_ideas",
|
|
101
|
+
"list_published_posts",
|
|
102
|
+
"list_senders",
|
|
103
|
+
"list_subskill_prompts",
|
|
104
|
+
"list_tables",
|
|
105
|
+
"list_workspaces",
|
|
106
|
+
"load_csv_dnc_entries",
|
|
107
|
+
"load_csv_domains",
|
|
108
|
+
"load_csv_linkedin_leads",
|
|
109
|
+
"lookup_sales_nav_filter",
|
|
110
|
+
"mark_post_published",
|
|
111
|
+
"migrate_flat_configs",
|
|
112
|
+
"pause_campaign",
|
|
113
|
+
"pause_direct_campaign",
|
|
114
|
+
"pause_on_demand_campaign",
|
|
115
|
+
"preflight_find_leads_provider",
|
|
116
|
+
"prepare_campaign_ab_test",
|
|
117
|
+
"queue_campaign_cells",
|
|
118
|
+
"queue_cells",
|
|
119
|
+
"record_campaign_review_batch",
|
|
120
|
+
"record_engage_proven_search",
|
|
121
|
+
"refill_sends",
|
|
122
|
+
"refill_sends_v2",
|
|
123
|
+
"refresh_paid_inmail_credits",
|
|
124
|
+
"refresh_sender_engagement",
|
|
125
|
+
"reissue_find_leads_watch_link",
|
|
126
|
+
"render_linkedin_post_preview",
|
|
127
|
+
"reorder_columns",
|
|
128
|
+
"resolve_campaign_fill_route",
|
|
129
|
+
"revise_message_template_and_rerun",
|
|
130
|
+
"run_scheduler_sweep",
|
|
131
|
+
"save_domain_filters",
|
|
132
|
+
"save_hook_research",
|
|
133
|
+
"save_post_draft",
|
|
134
|
+
"save_rubrics",
|
|
135
|
+
"search_apollo",
|
|
136
|
+
"search_engagement_posts",
|
|
137
|
+
"search_harvest_jobs",
|
|
138
|
+
"search_inbox_threads",
|
|
139
|
+
"search_prospeo",
|
|
140
|
+
"search_prospeo_companies",
|
|
141
|
+
"search_sales_nav",
|
|
142
|
+
"search_signals",
|
|
143
|
+
"search_subskill_prompts",
|
|
144
|
+
"select_campaign_cells",
|
|
145
|
+
"select_necessary_rubrics",
|
|
146
|
+
"select_promising_posts",
|
|
147
|
+
"send_inbox_draft",
|
|
148
|
+
"send_inbox_manual_reply",
|
|
149
|
+
"set_active_workspace",
|
|
150
|
+
"set_campaign_waterfall_order",
|
|
151
|
+
"set_company_info",
|
|
152
|
+
"set_engage_state",
|
|
153
|
+
"set_engage_style_guide",
|
|
154
|
+
"set_headline_icp_criteria",
|
|
155
|
+
"set_sender_calendar_link",
|
|
156
|
+
"set_sender_routing",
|
|
157
|
+
"setup_evergreen_campaigns",
|
|
158
|
+
"start_campaign",
|
|
159
|
+
"start_campaign_message_preparation",
|
|
160
|
+
"start_cli_login",
|
|
161
|
+
"start_direct_campaign",
|
|
162
|
+
"start_on_demand_campaign",
|
|
163
|
+
"update_campaign",
|
|
164
|
+
"update_campaign_brief",
|
|
165
|
+
"update_cell",
|
|
166
|
+
"update_column",
|
|
167
|
+
"update_find_leads_run",
|
|
168
|
+
"update_inbox_draft",
|
|
169
|
+
"update_post_draft",
|
|
170
|
+
"update_published_post_metrics",
|
|
171
|
+
"update_rubric_item",
|
|
172
|
+
"upsert_engage_tracked_person",
|
|
173
|
+
"upsert_rubric",
|
|
174
|
+
"validate_campaign1_kickoff_handoff",
|
|
175
|
+
"verify_table_row",
|
|
176
|
+
"wait_for_campaign_processing",
|
|
177
|
+
"wait_for_campaign_table_ready",
|
|
178
|
+
"wait_for_cli_login",
|
|
179
|
+
"wait_for_find_leads_run",
|
|
180
|
+
"wait_for_lead_list_ready",
|
|
181
|
+
"wait_for_rubric_results",
|
|
182
|
+
];
|
|
@@ -47,10 +47,28 @@ export function decideAgentTool(compiled, tool) {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
export function listToolsForAgent(compiled, definitions) {
|
|
50
|
-
const
|
|
51
|
-
.filter((entry) => entry.decision
|
|
50
|
+
const denied = new Set(compiled.entries
|
|
51
|
+
.filter((entry) => entry.decision === "DENY")
|
|
52
52
|
.map((entry) => entry.tool));
|
|
53
|
-
return definitions.filter((definition) =>
|
|
53
|
+
return definitions.filter((definition) => !denied.has(definition.name));
|
|
54
|
+
}
|
|
55
|
+
function decideRegisteredAgentTool(compiled, definitions, tool) {
|
|
56
|
+
const explicit = decideAgentTool(compiled, tool);
|
|
57
|
+
if (explicit.version !== "unknown")
|
|
58
|
+
return explicit;
|
|
59
|
+
if (!definitions.some((definition) => definition.name === tool)) {
|
|
60
|
+
return explicit;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
tool,
|
|
64
|
+
version: "registry-v1",
|
|
65
|
+
decision: "AUTO",
|
|
66
|
+
method: "POST",
|
|
67
|
+
route: `mcp://sellable/${tool}`,
|
|
68
|
+
handler: tool,
|
|
69
|
+
workspaceBinding: "workspaceId",
|
|
70
|
+
allowed: true,
|
|
71
|
+
};
|
|
54
72
|
}
|
|
55
73
|
function canonicalize(value) {
|
|
56
74
|
if (Array.isArray(value))
|
|
@@ -63,18 +81,16 @@ function canonicalize(value) {
|
|
|
63
81
|
return value;
|
|
64
82
|
}
|
|
65
83
|
const SHA256_CONSTANTS = [
|
|
66
|
-
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
|
|
77
|
-
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
84
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
|
85
|
+
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
86
|
+
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
|
87
|
+
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
88
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
|
89
|
+
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
90
|
+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
|
91
|
+
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
92
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
|
93
|
+
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
78
94
|
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
79
95
|
];
|
|
80
96
|
function rotateRight(value, bits) {
|
|
@@ -91,8 +107,8 @@ export function portableSha256(value) {
|
|
|
91
107
|
view.setUint32(paddedLength - 8, Math.floor(bitLength / 0x100000000));
|
|
92
108
|
view.setUint32(paddedLength - 4, bitLength >>> 0);
|
|
93
109
|
const hash = [
|
|
94
|
-
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f,
|
|
95
|
-
|
|
110
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
|
|
111
|
+
0x1f83d9ab, 0x5be0cd19,
|
|
96
112
|
];
|
|
97
113
|
const words = new Uint32Array(64);
|
|
98
114
|
for (let offset = 0; offset < paddedLength; offset += 64) {
|
|
@@ -212,15 +228,28 @@ export function createAgentMcpAuthorizationHandlers(input) {
|
|
|
212
228
|
async authorizeCall(call) {
|
|
213
229
|
const principal = call.context.principal;
|
|
214
230
|
if (!principal) {
|
|
215
|
-
return {
|
|
231
|
+
return {
|
|
232
|
+
allowed: false,
|
|
233
|
+
decision: "DENY",
|
|
234
|
+
code: "agent_principal_required",
|
|
235
|
+
};
|
|
216
236
|
}
|
|
217
237
|
if (principal.policyHash !== policy.policyHash) {
|
|
218
|
-
return {
|
|
238
|
+
return {
|
|
239
|
+
allowed: false,
|
|
240
|
+
decision: "DENY",
|
|
241
|
+
code: "policy_hash_mismatch",
|
|
242
|
+
};
|
|
219
243
|
}
|
|
220
|
-
if (!Number.isSafeInteger(principal.generation) ||
|
|
221
|
-
|
|
244
|
+
if (!Number.isSafeInteger(principal.generation) ||
|
|
245
|
+
principal.generation < 1) {
|
|
246
|
+
return {
|
|
247
|
+
allowed: false,
|
|
248
|
+
decision: "DENY",
|
|
249
|
+
code: "credential_generation_invalid",
|
|
250
|
+
};
|
|
222
251
|
}
|
|
223
|
-
const decision =
|
|
252
|
+
const decision = decideRegisteredAgentTool(policy, input.allTools, call.name);
|
|
224
253
|
if (!decision.allowed) {
|
|
225
254
|
return { allowed: false, decision: "DENY", code: "tool_denied" };
|
|
226
255
|
}
|
|
@@ -214,6 +214,8 @@ export type SchedulerChangedCounts = {
|
|
|
214
214
|
}>;
|
|
215
215
|
};
|
|
216
216
|
export declare function normalizeSchedulerChangedCounts(value: unknown): SchedulerChangedCounts | null;
|
|
217
|
+
export declare function setSweepSettlePollDelayMsForTests(ms: number): void;
|
|
218
|
+
export declare function resetSweepSettlePollDelayMsForTests(): void;
|
|
217
219
|
export declare function schedulerEnvelopeIsTerminal(schedulerStatus: string, receipt?: Record<string, unknown> | null): boolean;
|
|
218
220
|
export declare function normalizeSchedulerPrimitiveResult(value: unknown, expected?: {
|
|
219
221
|
expectedTargets: SchedulerExpectedTargetInput[];
|
|
@@ -294,6 +294,34 @@ function hasTerminalSchedulerReceipt(receipt) {
|
|
|
294
294
|
const status = receipt ? stringValue(receipt.status) : null;
|
|
295
295
|
return Boolean(status && SCHEDULER_TERMINAL_RECEIPT_STATUSES.has(status));
|
|
296
296
|
}
|
|
297
|
+
// fix(112ap): poll ONLY a genuinely in-flight attach. `!terminal` is the wrong
|
|
298
|
+
// test — it is also true for `backoff` and `failed`, and re-polling those would
|
|
299
|
+
// both waste the budget and misrepresent a refused dispatch as a pending one.
|
|
300
|
+
// In-flight means an attaching envelope (accepted|attached) that has NOT yet
|
|
301
|
+
// yielded a terminal receipt, matching the 112ao-2b contract exactly.
|
|
302
|
+
function schedulerDispatchIsStillRunning(value) {
|
|
303
|
+
const response = recordValue(value);
|
|
304
|
+
if (!response)
|
|
305
|
+
return false;
|
|
306
|
+
const status = stringValue(response.status) ?? "";
|
|
307
|
+
if (!SCHEDULER_ATTACHING_ENVELOPE_STATUSES.has(status))
|
|
308
|
+
return false;
|
|
309
|
+
return !hasTerminalSchedulerReceipt(recordValue(response.receipt));
|
|
310
|
+
}
|
|
311
|
+
// fix(112ap): bounded self-settle for a dispatched sweep. 4 x 8s = 32s, just past
|
|
312
|
+
// the scheduler's own ~28s observed pass duration and its 30s retryAfterMs hint,
|
|
313
|
+
// while staying well inside the coordinator's per-call budget.
|
|
314
|
+
const SWEEP_SETTLE_POLL_ATTEMPTS = 4;
|
|
315
|
+
const SWEEP_SETTLE_POLL_DEFAULT_DELAY_MS = 8_000;
|
|
316
|
+
// Injectable so pins can exercise the settle loop without real wall-clock waits.
|
|
317
|
+
// Production never calls the setter; the default is what ships.
|
|
318
|
+
let sweepSettlePollDelayMs = SWEEP_SETTLE_POLL_DEFAULT_DELAY_MS;
|
|
319
|
+
export function setSweepSettlePollDelayMsForTests(ms) {
|
|
320
|
+
sweepSettlePollDelayMs = ms;
|
|
321
|
+
}
|
|
322
|
+
export function resetSweepSettlePollDelayMsForTests() {
|
|
323
|
+
sweepSettlePollDelayMs = SWEEP_SETTLE_POLL_DEFAULT_DELAY_MS;
|
|
324
|
+
}
|
|
297
325
|
export function schedulerEnvelopeIsTerminal(schedulerStatus, receipt = null) {
|
|
298
326
|
if (SCHEDULER_TERMINAL_ENVELOPE_STATUSES.has(schedulerStatus))
|
|
299
327
|
return true;
|
|
@@ -3052,7 +3080,7 @@ export async function executeOneYoloPrimitive(action, workspaceId) {
|
|
|
3052
3080
|
},
|
|
3053
3081
|
};
|
|
3054
3082
|
}
|
|
3055
|
-
|
|
3083
|
+
let result = await runSchedulerSweep({
|
|
3056
3084
|
workspaceId,
|
|
3057
3085
|
action: "run",
|
|
3058
3086
|
targetDate,
|
|
@@ -3062,6 +3090,56 @@ export async function executeOneYoloPrimitive(action, workspaceId) {
|
|
|
3062
3090
|
expectedTargetsHash,
|
|
3063
3091
|
maxPlacements,
|
|
3064
3092
|
});
|
|
3093
|
+
// fix(112ap): settle our OWN dispatch before returning.
|
|
3094
|
+
//
|
|
3095
|
+
// The route holds a sweep for SCHEDULER_RUN_RESPONSE_BUDGET_MS (1s,
|
|
3096
|
+
// route.ts:20) and then returns `attached` with the real pass still running
|
|
3097
|
+
// in the background. We used to return that immediately, so the coordinator
|
|
3098
|
+
// replanned; concurrent approve/prepare churned readyCounts, which churns
|
|
3099
|
+
// stateRevision, which is hashed into actionKey/requestKey — so the NEXT
|
|
3100
|
+
// iteration minted a BRAND NEW key and polled a run that had never been
|
|
3101
|
+
// dispatched. scopeMatches was correctly false, and it dispatched again.
|
|
3102
|
+
//
|
|
3103
|
+
// Dotwork 2026-07-29 receipts: 12 DISTINCT run_scheduler_sweep keys, each
|
|
3104
|
+
// dispatched exactly once, every one returning exact_action_complete with
|
|
3105
|
+
// `lanesTouched: []`, coverage frozen at {sent:0, scheduled:0, ready:20}.
|
|
3106
|
+
// approve_messages/prepare_messages in the same runs DID report lanesTouched.
|
|
3107
|
+
// Meanwhile the scheduler itself was healthy — its own on-demand receipt
|
|
3108
|
+
// placed 12 ready / 12 considered / 12 scheduled with zero skips, zero
|
|
3109
|
+
// defers, zero prefilters. The sweeps simply never became scheduler runs.
|
|
3110
|
+
//
|
|
3111
|
+
// The key we just dispatched is in scope RIGHT HERE, so poll it here instead
|
|
3112
|
+
// of trying to remember it across a replan. That matters: per-fence runState
|
|
3113
|
+
// does NOT survive next_exact_target (it reset 112e's cohort guard, 112aj's
|
|
3114
|
+
// counter and 112an's stall bound), so a cross-fence memory would have needed
|
|
3115
|
+
// the WORKSPACE_ATTEMPT_LEDGER_KEYS allowlist. Polling in-scope needs none of
|
|
3116
|
+
// that. Bounded so a slow pass still degrades to the existing in-flight
|
|
3117
|
+
// handling rather than holding the tool call open.
|
|
3118
|
+
for (let attempt = 0; attempt < SWEEP_SETTLE_POLL_ATTEMPTS &&
|
|
3119
|
+
schedulerDispatchIsStillRunning(result); attempt += 1) {
|
|
3120
|
+
await sleep(sweepSettlePollDelayMs);
|
|
3121
|
+
const settled = await runSchedulerSweep({
|
|
3122
|
+
workspaceId,
|
|
3123
|
+
action: "status",
|
|
3124
|
+
targetDate,
|
|
3125
|
+
requestKey,
|
|
3126
|
+
targetShapeRevision,
|
|
3127
|
+
expectedTargetsHash,
|
|
3128
|
+
maxPlacements,
|
|
3129
|
+
});
|
|
3130
|
+
// Adopt the polled response ONLY when it actually settled. A status
|
|
3131
|
+
// probe returns a DIFFERENT shape ({scopeMatches, running}) than a
|
|
3132
|
+
// dispatch envelope ({status, receipt}); overwriting unconditionally
|
|
3133
|
+
// destroyed the in-flight envelope and with it
|
|
3134
|
+
// matching_scheduler_active / bounded_reread_wait / retryAfterMs, which
|
|
3135
|
+
// is the handling the coordinator relies on when a run is still going.
|
|
3136
|
+
// Keep the dispatch envelope until a terminal receipt exists.
|
|
3137
|
+
const settledRecord = recordValue(settled);
|
|
3138
|
+
if (settledRecord &&
|
|
3139
|
+
hasTerminalSchedulerReceipt(recordValue(settledRecord.receipt))) {
|
|
3140
|
+
result = settled;
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3065
3143
|
return {
|
|
3066
3144
|
status: "executed_and_reread",
|
|
3067
3145
|
result: normalizeSchedulerPrimitiveResult(result, {
|
|
@@ -688,6 +688,7 @@ type WorkspaceCoordinatorWaitAction = {
|
|
|
688
688
|
senderId: string | null;
|
|
689
689
|
reason: string | null;
|
|
690
690
|
};
|
|
691
|
+
declare function clearCompleteDefersForCampaign(runState: Record<string, unknown> | undefined, campaignId: string | null, preserveActionKey?: string | null): Record<string, unknown> | undefined;
|
|
691
692
|
export declare function approvedPacketComparison(input: RefillSendsCommandInput, targetPlan: unknown): {
|
|
692
693
|
changed: boolean;
|
|
693
694
|
expectedTargetShapeRevision: string | null;
|
|
@@ -728,6 +729,10 @@ type RefillSenderSelectorMiss = {
|
|
|
728
729
|
displayName: string;
|
|
729
730
|
}>;
|
|
730
731
|
};
|
|
732
|
+
export declare const refillSendsDeferInternals: {
|
|
733
|
+
clearCompleteDefersForCampaign: typeof clearCompleteDefersForCampaign;
|
|
734
|
+
SUPPLY_INVALIDATED_DEFER_BLOCKERS: Set<string>;
|
|
735
|
+
};
|
|
731
736
|
export declare function executeRefillSendsCommand(input?: RefillSendsCommandInput): Promise<import("./workspace-context.js").WorkspaceRequiredResult | {
|
|
732
737
|
ok: boolean;
|
|
733
738
|
code: string;
|
|
@@ -918,7 +918,32 @@ const WORKSPACE_SUPPLY_ACTION_FAMILIES = new Set([
|
|
|
918
918
|
"broaden_provider_search",
|
|
919
919
|
"refresh_sender_post_engagers",
|
|
920
920
|
]);
|
|
921
|
-
|
|
921
|
+
// fix(112ar): the blocker vocabulary the ledger WRITES and the one this cleaner
|
|
922
|
+
// MATCHED had drifted apart. `workspaceExactDeferredBlocker` records
|
|
923
|
+
// "exact_action_complete" (see deferWorkspaceExactTarget), but the filter only
|
|
924
|
+
// recognised "complete"/"capped_by_scheduler" -- so the clear never fired for
|
|
925
|
+
// the blocker that actually gets written, and a satisfied-before-supply defer
|
|
926
|
+
// became a STANDING claim for the rest of the session.
|
|
927
|
+
//
|
|
928
|
+
// Amplify 7-30: three exposed edges (run_scheduler_sweep, approve_messages,
|
|
929
|
+
// prepare_messages) were all held at "exact_action_complete" while the same
|
|
930
|
+
// payload reported 18 ready and 12 rows still to prepare. Every edge that could
|
|
931
|
+
// have filled the day was suppressed, and the run terminaled
|
|
932
|
+
// `actionable_supply_not_queued` -- an owner-invariant breach produced by a
|
|
933
|
+
// stale proof, not by absent supply.
|
|
934
|
+
//
|
|
935
|
+
// Same shape as 112c: a bounded ATTEMPT must not harden into a standing claim.
|
|
936
|
+
const SUPPLY_INVALIDATED_DEFER_BLOCKERS = new Set([
|
|
937
|
+
"complete",
|
|
938
|
+
"capped_by_scheduler",
|
|
939
|
+
"exact_action_complete",
|
|
940
|
+
]);
|
|
941
|
+
function clearCompleteDefersForCampaign(runState, campaignId,
|
|
942
|
+
// fix(112ar): the action that JUST landed keeps its own defer. Clearing it
|
|
943
|
+
// would re-expose the edge we just completed and invite a duplicate copy /
|
|
944
|
+
// approve. Only the OTHER campaign edges held on a now-stale completeness
|
|
945
|
+
// proof are released.
|
|
946
|
+
preserveActionKey) {
|
|
922
947
|
if (!runState || !campaignId)
|
|
923
948
|
return runState;
|
|
924
949
|
const entries = Array.isArray(runState.coordinatorBlockedExactTargets)
|
|
@@ -929,7 +954,11 @@ function clearCompleteDefersForCampaign(runState, campaignId) {
|
|
|
929
954
|
const kept = entries.filter((entry) => {
|
|
930
955
|
const blocked = recordValue(entry);
|
|
931
956
|
const blocker = stringValue(blocked?.blocker);
|
|
932
|
-
if (blocker
|
|
957
|
+
if (!SUPPLY_INVALIDATED_DEFER_BLOCKERS.has(blocker ?? "")) {
|
|
958
|
+
return true;
|
|
959
|
+
}
|
|
960
|
+
if (preserveActionKey &&
|
|
961
|
+
stringValue(blocked?.actionKey) === preserveActionKey) {
|
|
933
962
|
return true;
|
|
934
963
|
}
|
|
935
964
|
return stringValue(blocked?.campaignId) !== campaignId;
|
|
@@ -3018,6 +3047,16 @@ async function resolveRefillSenderSelectors(params) {
|
|
|
3018
3047
|
resolvedSelectors,
|
|
3019
3048
|
};
|
|
3020
3049
|
}
|
|
3050
|
+
// fix(112ar): exported for a DIRECT pin. Binding this rule through the full
|
|
3051
|
+
// command harness proved impossible without silently voiding the test --
|
|
3052
|
+
// deferWorkspaceExactTarget replaces same-identity entries, and
|
|
3053
|
+
// clearApprovalAttemptsForCampaign removes approve-family keys, so a seeded
|
|
3054
|
+
// defer disappears for reasons that have nothing to do with this fix. A unit
|
|
3055
|
+
// pin binds the writer/cleaner vocabulary contract directly.
|
|
3056
|
+
export const refillSendsDeferInternals = {
|
|
3057
|
+
clearCompleteDefersForCampaign,
|
|
3058
|
+
SUPPLY_INVALIDATED_DEFER_BLOCKERS,
|
|
3059
|
+
};
|
|
3021
3060
|
export async function executeRefillSendsCommand(input = {}) {
|
|
3022
3061
|
const normalizedInput = normalizeRefillSendsInput(input);
|
|
3023
3062
|
const yolo = normalizedInput.yolo === true;
|
|
@@ -3871,10 +3910,10 @@ export async function executeRefillSendsCommand(input = {}) {
|
|
|
3871
3910
|
WORKSPACE_SUPPLY_ACTION_FAMILIES.has(supplyActionFamily)));
|
|
3872
3911
|
const coordinatorWorkspaceRunState = prepFamilyLandedOnCampaign
|
|
3873
3912
|
? clearApprovalAttemptsForCampaign(supplyLandedOnCampaign
|
|
3874
|
-
? clearCompleteDefersForCampaign(deferredCoordinatorWorkspaceRunState, selectedCoordinatorTarget?.campaignId ?? null)
|
|
3913
|
+
? clearCompleteDefersForCampaign(deferredCoordinatorWorkspaceRunState, selectedCoordinatorTarget?.campaignId ?? null, selectedCoordinatorTarget?.actionKey ?? null)
|
|
3875
3914
|
: deferredCoordinatorWorkspaceRunState, selectedCoordinatorTarget?.campaignId ?? null)
|
|
3876
3915
|
: supplyLandedOnCampaign
|
|
3877
|
-
? clearCompleteDefersForCampaign(deferredCoordinatorWorkspaceRunState, selectedCoordinatorTarget?.campaignId ?? null)
|
|
3916
|
+
? clearCompleteDefersForCampaign(deferredCoordinatorWorkspaceRunState, selectedCoordinatorTarget?.campaignId ?? null, selectedCoordinatorTarget?.actionKey ?? null)
|
|
3878
3917
|
: deferredCoordinatorWorkspaceRunState;
|
|
3879
3918
|
// fix(112aj): continuation-level progress-or-settle. When this dispatch is an
|
|
3880
3919
|
// active_exact_run resume (continuationHandle set, in_progress), fingerprint
|