@sellable/mcp 0.1.746 → 0.1.747
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.
|
@@ -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, {
|