@sellable/mcp 0.1.942 → 0.1.944-wip.genflags.20260929.1
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 +2 -2
- package/dist/agent-tool-names.js +3 -0
- package/dist/server.js +7 -0
- package/dist/tools/agent-generation-routing.d.ts +103 -0
- package/dist/tools/agent-generation-routing.js +147 -0
- package/dist/tools/campaign-processing.js +1 -1
- package/dist/tools/campaigns.js +4 -2
- package/dist/tools/customer-program.d.ts +14 -0
- package/dist/tools/customer-program.js +9 -0
- package/dist/tools/registry.d.ts +48 -0
- package/dist/tools/registry.js +2 -0
- package/package.json +1 -1
- package/skills/campaign-daily-results/SKILL.md +32 -5
- package/skills/campaign-daily-review/SKILL.md +29 -5
- package/skills/campaign-weekly-review/SKILL.md +31 -0
- package/skills/create-campaign/SKILL.md +5 -9
- package/skills/create-campaign-v2/SKILL.md +2 -5
- package/skills/create-campaign-v2/SOUL.md +15 -6
- package/skills/create-campaign-v2/core/flow.v2.json +1 -1
- package/skills/create-campaign-v2/core/policy.md +13 -4
- package/skills/refill-sends/SKILL.md +3 -4
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
* manifests. Keep this free of tool implementation imports so Next can bundle
|
|
4
4
|
* it; registry parity is enforced in sellable-agent-tool-policy.test.ts.
|
|
5
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", "add_to_post_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_table", "create_workflow_table", "create_workspace", "customer_program", "decide_agent_endpoint_proposal", "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_agent_endpoint_run", "get_agent_soul", "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_integration_connection_status", "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_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", "integrations_call_tool", "integrations_describe_tool", "integrations_list_tools", "list_agent_endpoint_runs", "list_agent_endpoints", "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", "opportunity_sequence_enroll", "opportunity_sequence_next_page", "opportunity_sequence_record", "pause_campaign", "pause_direct_campaign", "pause_on_demand_campaign", "preflight_find_leads_provider", "prepare_campaign_ab_test", "publish_agent_endpoint", "queue_campaign_cells", "queue_cells", "record_campaign_review_batch", "record_engage_proven_search", "refill_sends", "refill_v3_advance", "refill_v3_continue", "refill_v3_edit_selected_campaign", "refill_v3_fill_ready", "refill_v3_world_state", "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", "rotate_agent_endpoint_key", "run_scheduler_sweep", "save_domain_filters", "save_hook_research", "save_post_draft", "save_rubrics", "search_apollo", "search_available_integrations", "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_integration_connection", "start_on_demand_campaign", "stop_agent_endpoint_run", "update_agent_soul", "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"];
|
|
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", "add_to_post_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_table", "create_workflow_table", "create_workspace", "customer_program", "decide_agent_endpoint_proposal", "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_agent_endpoint_run", "get_agent_generation_routing", "get_agent_soul", "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_integration_connection_status", "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_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", "integrations_call_tool", "integrations_describe_tool", "integrations_list_tools", "list_agent_endpoint_runs", "list_agent_endpoints", "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", "opportunity_sequence_enroll", "opportunity_sequence_next_page", "opportunity_sequence_record", "pause_campaign", "pause_direct_campaign", "pause_on_demand_campaign", "preflight_find_leads_provider", "prepare_campaign_ab_test", "publish_agent_endpoint", "queue_campaign_cells", "queue_cells", "record_campaign_review_batch", "record_engage_proven_search", "refill_sends", "refill_v3_advance", "refill_v3_continue", "refill_v3_edit_selected_campaign", "refill_v3_fill_ready", "refill_v3_world_state", "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", "rotate_agent_endpoint_key", "run_scheduler_sweep", "save_domain_filters", "save_hook_research", "save_post_draft", "save_rubrics", "search_apollo", "search_available_integrations", "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_agent_generation_flags", "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_integration_connection", "start_on_demand_campaign", "stop_agent_endpoint_run", "update_agent_soul", "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"];
|
|
7
7
|
/**
|
|
8
8
|
* Explicit mutation classification lives beside (but outside) the flat tool
|
|
9
9
|
* registry. Neither this list nor `isMutatingTool` is an input to
|
|
10
10
|
* `compileAgentToolPolicy`, so editing it cannot move the fleet-pinned
|
|
11
11
|
* `policyHash`.
|
|
12
12
|
*/
|
|
13
|
-
export declare const MUTATING_TOOL_NAMES: readonly ["create_table", "publish_agent_endpoint", "rotate_agent_endpoint_key", "decide_agent_endpoint_proposal", "stop_agent_endpoint_run", "pause_campaign", "archive_campaign", "update_campaign", "fill_campaign_horizon", "set_campaign_waterfall_order", "set_sender_routing", "send_inbox_draft", "send_inbox_manual_reply", "load_csv_dnc_entries", "update_cell", "queue_cells", "check_rubric", "queue_campaign_cells", "start_campaign_message_preparation", "opportunity_sequence_enroll", "opportunity_sequence_record", "refill_v3_fill_ready", "refill_v3_edit_selected_campaign", "refill_v3_advance", "refill_v3_continue"];
|
|
13
|
+
export declare const MUTATING_TOOL_NAMES: readonly ["create_table", "publish_agent_endpoint", "rotate_agent_endpoint_key", "decide_agent_endpoint_proposal", "stop_agent_endpoint_run", "pause_campaign", "archive_campaign", "update_campaign", "fill_campaign_horizon", "set_campaign_waterfall_order", "set_sender_routing", "set_agent_generation_flags", "send_inbox_draft", "send_inbox_manual_reply", "load_csv_dnc_entries", "update_cell", "queue_cells", "check_rubric", "queue_campaign_cells", "start_campaign_message_preparation", "opportunity_sequence_enroll", "opportunity_sequence_record", "refill_v3_fill_ready", "refill_v3_edit_selected_campaign", "refill_v3_advance", "refill_v3_continue"];
|
|
14
14
|
export declare function isMutatingTool(name: string): boolean;
|
|
15
15
|
export declare const CROSS_WORKSPACE_EXPLICIT_WORKSPACE_REQUIRED: {
|
|
16
16
|
readonly code: "cross_workspace_explicit_workspace_required";
|
package/dist/agent-tool-names.js
CHANGED
|
@@ -56,6 +56,7 @@ export const SELLABLE_MCP_TOOL_NAMES = [
|
|
|
56
56
|
"fill_campaign_horizon",
|
|
57
57
|
"get_active_workspace",
|
|
58
58
|
"get_agent_endpoint_run",
|
|
59
|
+
"get_agent_generation_routing",
|
|
59
60
|
"get_agent_soul",
|
|
60
61
|
"get_auth_status",
|
|
61
62
|
"get_campaign",
|
|
@@ -168,6 +169,7 @@ export const SELLABLE_MCP_TOOL_NAMES = [
|
|
|
168
169
|
"send_inbox_draft",
|
|
169
170
|
"send_inbox_manual_reply",
|
|
170
171
|
"set_active_workspace",
|
|
172
|
+
"set_agent_generation_flags",
|
|
171
173
|
"set_campaign_waterfall_order",
|
|
172
174
|
"set_company_info",
|
|
173
175
|
"set_engage_state",
|
|
@@ -225,6 +227,7 @@ export const MUTATING_TOOL_NAMES = [
|
|
|
225
227
|
"fill_campaign_horizon",
|
|
226
228
|
"set_campaign_waterfall_order",
|
|
227
229
|
"set_sender_routing",
|
|
230
|
+
"set_agent_generation_flags",
|
|
228
231
|
"send_inbox_draft",
|
|
229
232
|
"send_inbox_manual_reply",
|
|
230
233
|
"load_csv_dnc_entries",
|
package/dist/server.js
CHANGED
|
@@ -15,6 +15,7 @@ import { MCP_RUNTIME_IDENTITY } from "./runtime-identity.js";
|
|
|
15
15
|
import { getSkillByName, listSkills } from "./skills.js";
|
|
16
16
|
import { toMcpToolResult } from "./tool-result-envelope.js";
|
|
17
17
|
import { decideAgentEndpointProposal, getAgentEndpointRun, listAgentEndpointRuns, listAgentEndpoints, publishAgentEndpoint, rotateAgentEndpointKey, stopAgentEndpointRun, } from "./tools/agent-endpoints.js";
|
|
18
|
+
import { getAgentGenerationRoutingTool, setAgentGenerationFlagsTool, } from "./tools/agent-generation-routing.js";
|
|
18
19
|
import { getAgentSoulTool, updateAgentSoulTool } from "./tools/agent-soul.js";
|
|
19
20
|
import { getAuthStatus } from "./tools/auth.js";
|
|
20
21
|
import { handleAddColumn, handleCommitBlueprint, } from "./tools/blueprint-commit.js";
|
|
@@ -797,6 +798,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
797
798
|
case "set_sender_routing":
|
|
798
799
|
result = await setSenderRoutingTool(args);
|
|
799
800
|
break;
|
|
801
|
+
case "get_agent_generation_routing":
|
|
802
|
+
result = await getAgentGenerationRoutingTool(args);
|
|
803
|
+
break;
|
|
804
|
+
case "set_agent_generation_flags":
|
|
805
|
+
result = await setAgentGenerationFlagsTool(args);
|
|
806
|
+
break;
|
|
800
807
|
// Plan 141-06 owns these three `case` labels ONLY. The fail-closed actor
|
|
801
808
|
// guard above is plan 141-04 T2's and is keyed on the tool-name SET, so it
|
|
802
809
|
// already ran before this switch and `integrationActor.context` is verified.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
type GenerationFlags = {
|
|
2
|
+
openerEnabled: boolean;
|
|
3
|
+
replyEnabled: boolean;
|
|
4
|
+
followUpEnabled: boolean;
|
|
5
|
+
};
|
|
6
|
+
type GenerationRouting = {
|
|
7
|
+
workspaceId: string;
|
|
8
|
+
agentId: string;
|
|
9
|
+
generation: number;
|
|
10
|
+
endpointId: string;
|
|
11
|
+
openerEndpointId: string | null;
|
|
12
|
+
currentPublishedRevisionId: string | null;
|
|
13
|
+
flags: GenerationFlags;
|
|
14
|
+
};
|
|
15
|
+
type SetFlagsInput = {
|
|
16
|
+
expectedGeneration?: unknown;
|
|
17
|
+
openerEnabled?: unknown;
|
|
18
|
+
replyEnabled?: unknown;
|
|
19
|
+
followUpEnabled?: unknown;
|
|
20
|
+
workspaceId?: unknown;
|
|
21
|
+
};
|
|
22
|
+
export declare const agentGenerationRoutingToolDefinitions: ({
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: string;
|
|
27
|
+
properties: {
|
|
28
|
+
workspaceId: {
|
|
29
|
+
type: string;
|
|
30
|
+
description: string;
|
|
31
|
+
};
|
|
32
|
+
expectedGeneration?: undefined;
|
|
33
|
+
openerEnabled?: undefined;
|
|
34
|
+
replyEnabled?: undefined;
|
|
35
|
+
followUpEnabled?: undefined;
|
|
36
|
+
};
|
|
37
|
+
required: never[];
|
|
38
|
+
additionalProperties: boolean;
|
|
39
|
+
};
|
|
40
|
+
annotations?: undefined;
|
|
41
|
+
} | {
|
|
42
|
+
name: string;
|
|
43
|
+
description: string;
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: string;
|
|
46
|
+
properties: {
|
|
47
|
+
expectedGeneration: {
|
|
48
|
+
type: string;
|
|
49
|
+
minimum: number;
|
|
50
|
+
description: string;
|
|
51
|
+
};
|
|
52
|
+
openerEnabled: {
|
|
53
|
+
type: string;
|
|
54
|
+
};
|
|
55
|
+
replyEnabled: {
|
|
56
|
+
type: string;
|
|
57
|
+
};
|
|
58
|
+
followUpEnabled: {
|
|
59
|
+
type: string;
|
|
60
|
+
};
|
|
61
|
+
workspaceId: {
|
|
62
|
+
type: string;
|
|
63
|
+
description: string;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
required: string[];
|
|
67
|
+
additionalProperties: boolean;
|
|
68
|
+
};
|
|
69
|
+
annotations: {
|
|
70
|
+
readOnlyHint: boolean;
|
|
71
|
+
destructiveHint: boolean;
|
|
72
|
+
idempotentHint: boolean;
|
|
73
|
+
openWorldHint: boolean;
|
|
74
|
+
};
|
|
75
|
+
})[];
|
|
76
|
+
export declare function getAgentGenerationRoutingTool(input?: {
|
|
77
|
+
workspaceId?: string;
|
|
78
|
+
}): Promise<{
|
|
79
|
+
ok: false;
|
|
80
|
+
code: string;
|
|
81
|
+
message: string;
|
|
82
|
+
} | {
|
|
83
|
+
ok: true;
|
|
84
|
+
routing: GenerationRouting;
|
|
85
|
+
}>;
|
|
86
|
+
export declare function setAgentGenerationFlagsTool(input?: SetFlagsInput): Promise<{
|
|
87
|
+
ok: false;
|
|
88
|
+
code: string;
|
|
89
|
+
message: string;
|
|
90
|
+
} | {
|
|
91
|
+
ok: true;
|
|
92
|
+
before: {
|
|
93
|
+
generation: number;
|
|
94
|
+
flags: GenerationFlags;
|
|
95
|
+
};
|
|
96
|
+
after: {
|
|
97
|
+
generation: number;
|
|
98
|
+
flags: GenerationFlags;
|
|
99
|
+
};
|
|
100
|
+
routing: GenerationRouting;
|
|
101
|
+
note: string;
|
|
102
|
+
}>;
|
|
103
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { getApi, SellableApiError } from "../api.js";
|
|
2
|
+
import { workspaceRequestOptions } from "./workspace-context.js";
|
|
3
|
+
const ROUTE = "/api/v3/sellable-agent/product-generation";
|
|
4
|
+
const FLAG_KEYS = ["openerEnabled", "replyEnabled", "followUpEnabled"];
|
|
5
|
+
export const agentGenerationRoutingToolDefinitions = [
|
|
6
|
+
{
|
|
7
|
+
name: "get_agent_generation_routing",
|
|
8
|
+
description: "Read the workspace Sellable Agent's message-generation routing: the opener, reply, and follow-up flags plus the current generation number and endpoint ids. Read this before set_agent_generation_flags and pass its generation as expectedGeneration.",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
workspaceId: {
|
|
13
|
+
type: "string",
|
|
14
|
+
description: "Explicit request-scoped workspace id.",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
required: [],
|
|
18
|
+
additionalProperties: false,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "set_agent_generation_flags",
|
|
23
|
+
description: "Turn the workspace Sellable Agent's opener, reply, or follow-up generation on or off. Only the flags you pass change; the others and the endpoint ids are preserved. Requires expectedGeneration from get_agent_generation_routing (compare-before-write). Turning openerEnabled off stops NEW campaigns from defaulting to the Agent opener. Existing campaigns keep their per-column messageGenerator setting until that Generate Message column is switched to STANDARD.",
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: {
|
|
27
|
+
expectedGeneration: {
|
|
28
|
+
type: "integer",
|
|
29
|
+
minimum: 0,
|
|
30
|
+
description: "The generation returned by get_agent_generation_routing.",
|
|
31
|
+
},
|
|
32
|
+
openerEnabled: { type: "boolean" },
|
|
33
|
+
replyEnabled: { type: "boolean" },
|
|
34
|
+
followUpEnabled: { type: "boolean" },
|
|
35
|
+
workspaceId: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "Explicit target workspace id.",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ["expectedGeneration"],
|
|
41
|
+
additionalProperties: false,
|
|
42
|
+
},
|
|
43
|
+
annotations: {
|
|
44
|
+
readOnlyHint: false,
|
|
45
|
+
destructiveHint: false,
|
|
46
|
+
idempotentHint: true,
|
|
47
|
+
openWorldHint: false,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
function isRouting(value) {
|
|
52
|
+
const routing = value;
|
|
53
|
+
return (!!routing &&
|
|
54
|
+
typeof routing.generation === "number" &&
|
|
55
|
+
typeof routing.endpointId === "string" &&
|
|
56
|
+
!!routing.flags &&
|
|
57
|
+
FLAG_KEYS.every((key) => typeof routing.flags[key] === "boolean"));
|
|
58
|
+
}
|
|
59
|
+
function failure(code, message, extra = {}) {
|
|
60
|
+
return { ok: false, code, message, ...extra };
|
|
61
|
+
}
|
|
62
|
+
function describeError(error) {
|
|
63
|
+
if (error instanceof SellableApiError) {
|
|
64
|
+
let code = `http_${error.status}`;
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(error.body);
|
|
67
|
+
if (typeof parsed.error === "string" && /^[a-z0-9_]+$/.test(parsed.error))
|
|
68
|
+
code = parsed.error;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Keep the HTTP status code.
|
|
72
|
+
}
|
|
73
|
+
return failure(code, error.isAuthError
|
|
74
|
+
? "Sellable authentication or authorization failed. OWNER or ADMIN access to the workspace is required."
|
|
75
|
+
: `The routing request failed (${error.status}).`, { status: error.status, uncertain: error.status >= 500 });
|
|
76
|
+
}
|
|
77
|
+
return failure("request_failed", error instanceof Error ? error.message : "The routing request failed.");
|
|
78
|
+
}
|
|
79
|
+
export async function getAgentGenerationRoutingTool(input = {}) {
|
|
80
|
+
try {
|
|
81
|
+
const response = await getApi().get(ROUTE, workspaceRequestOptions(input.workspaceId));
|
|
82
|
+
if (!isRouting(response?.routing)) {
|
|
83
|
+
return failure("malformed_routing_response", "The routing response was incomplete.");
|
|
84
|
+
}
|
|
85
|
+
return { ok: true, routing: response.routing };
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
return describeError(error);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export async function setAgentGenerationFlagsTool(input = {}) {
|
|
92
|
+
const changes = {};
|
|
93
|
+
for (const key of FLAG_KEYS) {
|
|
94
|
+
const value = input[key];
|
|
95
|
+
if (value === undefined)
|
|
96
|
+
continue;
|
|
97
|
+
if (typeof value !== "boolean") {
|
|
98
|
+
return failure("invalid_arguments", `${key} must be a boolean.`);
|
|
99
|
+
}
|
|
100
|
+
changes[key] = value;
|
|
101
|
+
}
|
|
102
|
+
if (typeof input.expectedGeneration !== "number" ||
|
|
103
|
+
!Number.isInteger(input.expectedGeneration) ||
|
|
104
|
+
input.expectedGeneration < 0) {
|
|
105
|
+
return failure("invalid_arguments", "expectedGeneration is required. Read it from get_agent_generation_routing.");
|
|
106
|
+
}
|
|
107
|
+
if (Object.keys(changes).length === 0) {
|
|
108
|
+
return failure("invalid_arguments", "Pass at least one of openerEnabled, replyEnabled, or followUpEnabled.");
|
|
109
|
+
}
|
|
110
|
+
const workspaceId = typeof input.workspaceId === "string" ? input.workspaceId : undefined;
|
|
111
|
+
const current = await getAgentGenerationRoutingTool({ workspaceId });
|
|
112
|
+
if (!current.ok)
|
|
113
|
+
return current;
|
|
114
|
+
const before = current.routing;
|
|
115
|
+
if (before.generation !== input.expectedGeneration) {
|
|
116
|
+
return failure("generation_conflict", "The routing changed after it was read. Reread it and retry with the current generation.", { currentGeneration: before.generation, flags: before.flags });
|
|
117
|
+
}
|
|
118
|
+
const flags = { ...before.flags, ...changes };
|
|
119
|
+
try {
|
|
120
|
+
const response = await getApi().put(ROUTE, {
|
|
121
|
+
expectedGeneration: before.generation,
|
|
122
|
+
endpointId: before.endpointId,
|
|
123
|
+
...(before.openerEndpointId
|
|
124
|
+
? { openerEndpointId: before.openerEndpointId }
|
|
125
|
+
: {}),
|
|
126
|
+
flags,
|
|
127
|
+
}, workspaceRequestOptions(workspaceId));
|
|
128
|
+
if (!isRouting(response?.routing)) {
|
|
129
|
+
return failure("malformed_routing_response", "The update returned an incomplete receipt, so the final state is uncertain. Reread get_agent_generation_routing before retrying.", { uncertain: true });
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
ok: true,
|
|
133
|
+
before: { generation: before.generation, flags: before.flags },
|
|
134
|
+
after: {
|
|
135
|
+
generation: response.routing.generation,
|
|
136
|
+
flags: response.routing.flags,
|
|
137
|
+
},
|
|
138
|
+
routing: response.routing,
|
|
139
|
+
note: flags.openerEnabled
|
|
140
|
+
? "Agent openers are on for new campaigns."
|
|
141
|
+
: "New campaigns will use the standard template generator. Existing campaigns keep their own Generate Message setting.",
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
return describeError(error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -419,7 +419,7 @@ export const campaignProcessingToolDefinitions = [
|
|
|
419
419
|
},
|
|
420
420
|
{
|
|
421
421
|
name: "queue_campaign_cells",
|
|
422
|
-
description: 'Resolve and queue campaign cells by semantic role and bounded row selector. This is the shared campaign-setup, rubric-maintenance, and recovery owner; use it instead of fetching rows only to discover raw cell IDs. For any fill/refill-sends intent, do not call this tool directly: use refill_v3_advance so V3 owns target selection, fencing, and continuation. For approval, rowSelector:{type:"needsApproval"} selects current generated rows missing Approved=true, but mutating Approved cells
|
|
422
|
+
description: 'Resolve and queue campaign cells by semantic role and bounded row selector. This is the shared campaign-setup, rubric-maintenance, and recovery owner; use it instead of fetching rows only to discover raw cell IDs. For any fill/refill-sends intent, do not call this tool directly: use refill_v3_advance so V3 owns target selection, fencing, and continuation. For approval, rowSelector:{type:"needsApproval"} selects current generated rows missing Approved=true, but mutating Approved cells needs explicit bounded approval, or standing customer consent (launchConsent.mode autopilot) for messages that passed the quality check. Do not use needsGeneratedMessage as a top-down cursor because it only selects already-passed rows. forceRerun retries the selected cells only; it never changes the approved template, rewrites scheduled/attempted outreach, or synthesizes messageTemplateRevision authority. Cross-workspace Agent calls must pass workspaceId.',
|
|
423
423
|
inputSchema: {
|
|
424
424
|
type: "object",
|
|
425
425
|
properties: {
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -594,7 +594,7 @@ export const campaignToolDefinitions = [
|
|
|
594
594
|
},
|
|
595
595
|
{
|
|
596
596
|
name: "start_campaign",
|
|
597
|
-
description: "Start a paused campaign, enabling the sweeper to send messages.
|
|
597
|
+
description: "Start a paused campaign, enabling the sweeper to send messages. Call it when the customer explicitly asks to start, or when their standing consent covers it (customer_program context launchConsent.mode is autopilot). It must not be used as part of fill-send-horizon, message preparation, or scheduling-proof flows.",
|
|
598
598
|
inputSchema: {
|
|
599
599
|
type: "object",
|
|
600
600
|
properties: {
|
|
@@ -696,7 +696,9 @@ function readCampaignAutomationSettings(source) {
|
|
|
696
696
|
const autoApproveFollowups = source.autoApproveFollowups ?? source.autoApproveOutreach;
|
|
697
697
|
return {
|
|
698
698
|
autoApproveFollowups: typeof autoApproveFollowups === "boolean" ? autoApproveFollowups : null,
|
|
699
|
-
enableFollowUps: typeof source.enableFollowUps === "boolean"
|
|
699
|
+
enableFollowUps: typeof source.enableFollowUps === "boolean"
|
|
700
|
+
? source.enableFollowUps
|
|
701
|
+
: null,
|
|
700
702
|
};
|
|
701
703
|
}
|
|
702
704
|
function normalizeCellValue(cell) {
|
|
@@ -59,6 +59,20 @@ export declare const customerProgramToolDefinitions: readonly [{
|
|
|
59
59
|
};
|
|
60
60
|
readonly required: readonly ["identity", "firstAskedAt", "reminder48hAt", "reminder7dAt", "resolvedAt"];
|
|
61
61
|
};
|
|
62
|
+
readonly launchConsent: {
|
|
63
|
+
readonly type: readonly ["object", "null"];
|
|
64
|
+
readonly additionalProperties: false;
|
|
65
|
+
readonly properties: {
|
|
66
|
+
readonly mode: {
|
|
67
|
+
readonly type: "string";
|
|
68
|
+
readonly enum: readonly ["autopilot", "review_first"];
|
|
69
|
+
};
|
|
70
|
+
readonly decidedAt: {
|
|
71
|
+
readonly type: "string";
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
readonly required: readonly ["mode", "decidedAt"];
|
|
75
|
+
};
|
|
62
76
|
};
|
|
63
77
|
};
|
|
64
78
|
readonly preferences: {
|
|
@@ -54,6 +54,15 @@ export const customerProgramToolDefinitions = [
|
|
|
54
54
|
"resolvedAt",
|
|
55
55
|
],
|
|
56
56
|
},
|
|
57
|
+
launchConsent: {
|
|
58
|
+
type: ["object", "null"],
|
|
59
|
+
additionalProperties: false,
|
|
60
|
+
properties: {
|
|
61
|
+
mode: { type: "string", enum: ["autopilot", "review_first"] },
|
|
62
|
+
decidedAt: { type: "string" },
|
|
63
|
+
},
|
|
64
|
+
required: ["mode", "decidedAt"],
|
|
65
|
+
},
|
|
57
66
|
},
|
|
58
67
|
},
|
|
59
68
|
preferences: {
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -786,6 +786,40 @@ export declare const allTools: ({
|
|
|
786
786
|
readonly idempotentHint: false;
|
|
787
787
|
readonly openWorldHint: false;
|
|
788
788
|
};
|
|
789
|
+
} | {
|
|
790
|
+
name: string;
|
|
791
|
+
description: string;
|
|
792
|
+
inputSchema: {
|
|
793
|
+
type: string;
|
|
794
|
+
properties: {
|
|
795
|
+
expectedGeneration: {
|
|
796
|
+
type: string;
|
|
797
|
+
minimum: number;
|
|
798
|
+
description: string;
|
|
799
|
+
};
|
|
800
|
+
openerEnabled: {
|
|
801
|
+
type: string;
|
|
802
|
+
};
|
|
803
|
+
replyEnabled: {
|
|
804
|
+
type: string;
|
|
805
|
+
};
|
|
806
|
+
followUpEnabled: {
|
|
807
|
+
type: string;
|
|
808
|
+
};
|
|
809
|
+
workspaceId: {
|
|
810
|
+
type: string;
|
|
811
|
+
description: string;
|
|
812
|
+
};
|
|
813
|
+
};
|
|
814
|
+
required: string[];
|
|
815
|
+
additionalProperties: boolean;
|
|
816
|
+
};
|
|
817
|
+
annotations: {
|
|
818
|
+
readOnlyHint: boolean;
|
|
819
|
+
destructiveHint: boolean;
|
|
820
|
+
idempotentHint: boolean;
|
|
821
|
+
openWorldHint: boolean;
|
|
822
|
+
};
|
|
789
823
|
} | {
|
|
790
824
|
readonly name: "get_agent_soul";
|
|
791
825
|
readonly description: "Read and prove the SOUL.md used by this live Sellable Agent runtime. This tool is runtime-bound and has no target selector.";
|
|
@@ -8627,6 +8661,20 @@ export declare const allTools: ({
|
|
|
8627
8661
|
};
|
|
8628
8662
|
readonly required: readonly ["identity", "firstAskedAt", "reminder48hAt", "reminder7dAt", "resolvedAt"];
|
|
8629
8663
|
};
|
|
8664
|
+
readonly launchConsent: {
|
|
8665
|
+
readonly type: readonly ["object", "null"];
|
|
8666
|
+
readonly additionalProperties: false;
|
|
8667
|
+
readonly properties: {
|
|
8668
|
+
readonly mode: {
|
|
8669
|
+
readonly type: "string";
|
|
8670
|
+
readonly enum: readonly ["autopilot", "review_first"];
|
|
8671
|
+
};
|
|
8672
|
+
readonly decidedAt: {
|
|
8673
|
+
readonly type: "string";
|
|
8674
|
+
};
|
|
8675
|
+
};
|
|
8676
|
+
readonly required: readonly ["mode", "decidedAt"];
|
|
8677
|
+
};
|
|
8630
8678
|
};
|
|
8631
8679
|
};
|
|
8632
8680
|
readonly preferences: {
|
package/dist/tools/registry.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { agentEndpointToolDefinitions } from "./agent-endpoints.js";
|
|
2
|
+
import { agentGenerationRoutingToolDefinitions } from "./agent-generation-routing.js";
|
|
2
3
|
import { agentSoulToolDefinitions } from "./agent-soul.js";
|
|
3
4
|
import { authToolDefinitions } from "./auth.js";
|
|
4
5
|
import { blueprintCommitToolDefinitions } from "./blueprint-commit.js";
|
|
@@ -124,6 +125,7 @@ export const allTools = [
|
|
|
124
125
|
...companyInfoToolDefinitions,
|
|
125
126
|
...customerProgramToolDefinitions,
|
|
126
127
|
...senderRoutingToolDefinitions,
|
|
128
|
+
...agentGenerationRoutingToolDefinitions,
|
|
127
129
|
...senderToolDefinitions,
|
|
128
130
|
...engageStateToolDefinitions,
|
|
129
131
|
...engageMemoryToolDefinitions,
|
package/package.json
CHANGED
|
@@ -22,24 +22,51 @@ edit, launch or refill campaigns, and it does not send or draft replies.
|
|
|
22
22
|
`leaseOwner`; never add the wake's `effectId` or `fence`. For a `reconcile`
|
|
23
23
|
wake, skip snapshot generation and follow delivery recovery below.
|
|
24
24
|
2. Use the returned period label and timezone exactly. Describe it as the prior
|
|
25
|
-
complete local day.
|
|
26
|
-
outcomes, and label pending work as pending.
|
|
25
|
+
complete local day. Report settled morning work under **What Sellable did**, keep it separate from yesterday's outcomes, and label pending work as pending.
|
|
27
26
|
3. Report the returned counts for invitations actually delivered, invitations
|
|
28
27
|
accepted, and unique people who replied. Keep interest and booked meetings
|
|
29
28
|
separate. Render unavailable values as unknown with the returned reason;
|
|
30
29
|
never turn missing evidence into zero.
|
|
31
|
-
4. Show at most five returned people. Each person must retain the campaign ICP
|
|
30
|
+
4. Show at most five returned people in a direct answer and two in the scheduled post. Each person must retain the campaign ICP
|
|
32
31
|
evidence, profile or inbox link, acceptance/reply facts, outcome label and
|
|
33
32
|
current handling state supplied by the product. Say whether the customer has
|
|
34
33
|
replied, a draft is saved, a follow-up is scheduled, or attention is still
|
|
35
34
|
needed. Do not invent fit, interest, meetings or draft state.
|
|
36
|
-
5. End with the single returned next action when present.
|
|
37
|
-
had a quiet day, give the brief zero-results snapshot without commentary. If
|
|
35
|
+
5. End with the single returned next action when present. The product does not publish a verified quiet day; a direct request still gets the honest snapshot. If
|
|
38
36
|
there is no campaign, do not show an empty scoreboard; offer to start or
|
|
39
37
|
continue Create Campaign instead.
|
|
40
38
|
|
|
39
|
+
## Write the Slack message
|
|
40
|
+
|
|
41
|
+
Use real line breaks: a bold title, a blank line between sections, one item per
|
|
42
|
+
line, never one long paragraph. Write ordinary Markdown; delivery converts it for Slack.
|
|
43
|
+
|
|
44
|
+
**{Workspace} · Daily update · {Weekday Mon D}**
|
|
45
|
+
|
|
46
|
+
**What Sellable did** lists only work the product confirms: leads added, messages
|
|
47
|
+
approved, sends scheduled, campaign started. Leave it out when there is none.
|
|
48
|
+
|
|
49
|
+
**Yesterday** has one bullet each for invitations delivered, accepted and people who
|
|
50
|
+
replied. Say zeros once ("No interested prospects or meetings yet").
|
|
51
|
+
|
|
52
|
+
**People to look at** has up to two returned people, each with a link and what to do.
|
|
53
|
+
|
|
54
|
+
**Next step** and **Options** appear only when the customer must act: one specific
|
|
55
|
+
action, then at most two options they can reply with. Offer only what you can really
|
|
56
|
+
do (refill, continue or edit the campaign) or what they can do (add leads, approve
|
|
57
|
+
messages, reconnect a sender).
|
|
58
|
+
|
|
59
|
+
Lead with what Sellable did to grow their sends. Never tell the customer what they
|
|
60
|
+
have not done, and never post a scoreboard of zeros. Plain words: no run tokens,
|
|
61
|
+
job or effect IDs, error codes, tool names or file paths. Unknown is never zero.
|
|
62
|
+
About 30-70 words, plus at most two people.
|
|
63
|
+
|
|
41
64
|
## Stop and delivery rules
|
|
42
65
|
|
|
66
|
+
If a tool call fails or its result can't be read, return `[SILENT]` and stop.
|
|
67
|
+
Never post an error, an apology or a guess; the open claim lets the next
|
|
68
|
+
scheduled window retry.
|
|
69
|
+
|
|
43
70
|
A direct request returns the snapshot in the current conversation without
|
|
44
71
|
claiming scheduled publication. The managed 09:00 run posts only when the
|
|
45
72
|
product returns a new publication claim, and only to the verified current main
|
|
@@ -37,26 +37,50 @@ the exact wait or outcome.
|
|
|
37
37
|
4. Inspect supply and capacity, then choose exactly one outcome: `maintain`,
|
|
38
38
|
`refill`, `continue`, `edit`, `propose_create`, `input`, or `blocker`.
|
|
39
39
|
5. Execute through the existing owner:
|
|
40
|
-
- suitable campaign with a real supply gap: call canonical Refill Sends with
|
|
41
|
-
`yolo:true` and keep its returned run token;
|
|
40
|
+
- suitable campaign with a real supply gap: call canonical Refill Sends with `yolo:true` and keep its returned run token. Keep going until the gap is filled or a real blocker remains; when a source runs dry, refill widens it. Do not stop to ask the customer for leads;
|
|
42
41
|
- unfinished campaign: continue the same campaign through Create Campaign;
|
|
43
42
|
- supported quality issue: edit the same campaign through Create Campaign;
|
|
44
43
|
- no campaign: enter Create Campaign, asking only for the LinkedIn profile
|
|
45
44
|
first when it is missing;
|
|
46
45
|
- healthy supplied campaign: maintain it and stay quiet;
|
|
47
46
|
- manual pause: respect it;
|
|
48
|
-
- missing approval, connection or decision: retain one exact next step.
|
|
47
|
+
- missing approval, connection or decision: with `launchConsent.mode: "autopilot"` approve messages that passed the quality check and start or resume the campaign yourself; otherwise retain one exact next step. Ask only for what you cannot do yourself, such as a LinkedIn connection, as one question.
|
|
49
48
|
6. Observe the owned operation, then record the outcome against the same review
|
|
50
49
|
effect and fence. The `record_review_outcome` call contains only its action,
|
|
51
50
|
effect, fence, review outcome, summary, and optional decision/operation refs;
|
|
52
51
|
do not include trigger or lease fields. Never mark a pending or uncertain
|
|
53
52
|
effect complete. On a scheduled actionable outcome, saving the outcome is
|
|
54
53
|
not completion. Inspect `deliveryRequired`; when it is `true`, continue
|
|
55
|
-
immediately to the required `stage_delivery` call. Never return while the
|
|
56
|
-
|
|
54
|
+
immediately to the required `stage_delivery` call. Never return while the recorded run still has `deliveryState: "pending"`. Write the outcome `summary` in plain customer words with the verified numbers (leads added, messages approved, sends scheduled): the next results update reads it as "What Sellable did".
|
|
55
|
+
|
|
56
|
+
## Write the Slack message
|
|
57
|
+
|
|
58
|
+
Post only new work or a real need, never a status. Use real line breaks: a bold title, a blank line between sections, one item per line, never one long paragraph. Write ordinary Markdown; delivery converts it for Slack.
|
|
59
|
+
|
|
60
|
+
**{Workspace} · Campaign update · {Weekday Mon D}**
|
|
61
|
+
|
|
62
|
+
**What I did** says in plain words what changed: leads added, messages approved,
|
|
63
|
+
sends scheduled, campaign started, or a source widened.
|
|
64
|
+
|
|
65
|
+
**Next step** and **Options** appear only when the customer must act: one specific
|
|
66
|
+
action, then at most two options they can reply with. Offer only what you can really
|
|
67
|
+
do (refill, continue or edit the campaign) or what they can do (add leads, approve
|
|
68
|
+
messages, reconnect a sender).
|
|
69
|
+
|
|
70
|
+
Lead with what Sellable did to grow their sends. Never tell the customer what they
|
|
71
|
+
have not done, and never post a scoreboard of zeros. Plain words: no run tokens,
|
|
72
|
+
job or effect IDs, error codes, tool names or file paths. Unknown is never zero.
|
|
73
|
+
About 20-60 words.
|
|
57
74
|
|
|
58
75
|
## Stop and delivery rules
|
|
59
76
|
|
|
77
|
+
An internal system fault (a run token, an error code, refill machinery) is not
|
|
78
|
+
a customer decision or blocker: do not post it.
|
|
79
|
+
|
|
80
|
+
If a tool call fails or its result can't be read, return `[SILENT]` and stop.
|
|
81
|
+
Never post an error, an apology or a guess; the open claim lets the next
|
|
82
|
+
scheduled window retry.
|
|
83
|
+
|
|
60
84
|
Stop on settled state, a human wait, a retained async wait, a typed blocker or
|
|
61
85
|
the existing bounded operation budget. Never restart a pending effect. Follow a
|
|
62
86
|
new customer reply or product completion immediately instead of waiting for the
|
|
@@ -44,8 +44,39 @@ truth and keep the recommendation inside the same continuous campaign program.
|
|
|
44
44
|
combined message. On a later recovery wake, use the returned latest complete
|
|
45
45
|
local day. Omit a section the product marks already delivered.
|
|
46
46
|
|
|
47
|
+
## Write the Slack message
|
|
48
|
+
|
|
49
|
+
Use real line breaks: a bold title, a blank line between sections, one item per
|
|
50
|
+
line, never one long paragraph. Write ordinary Markdown; delivery converts it for Slack.
|
|
51
|
+
|
|
52
|
+
**{Workspace} · Weekly update · {Mon D–D}**
|
|
53
|
+
|
|
54
|
+
**What Sellable did** lists only work the product confirms this week: leads added,
|
|
55
|
+
messages approved, sends scheduled, campaigns started. Leave it out when there is none.
|
|
56
|
+
|
|
57
|
+
**Results** has one bullet each for delivered, accepted and replied, with one
|
|
58
|
+
comparison to the week before ("up from 3"). Say zeros once.
|
|
59
|
+
|
|
60
|
+
**Yesterday** has the Monday snapshot in one line, when the product returns it.
|
|
61
|
+
|
|
62
|
+
**Active campaigns** has one bullet per verified active campaign with its status.
|
|
63
|
+
|
|
64
|
+
**Next step** and **Options** appear only when the customer must act: one specific
|
|
65
|
+
action, then at most two options they can reply with. Offer only what you can really
|
|
66
|
+
do (refill, continue or edit the campaign) or what they can do (add leads, approve
|
|
67
|
+
messages, reconnect a sender).
|
|
68
|
+
|
|
69
|
+
Lead with what Sellable did to grow their sends. Never tell the customer what they
|
|
70
|
+
have not done, and never post a scoreboard of zeros. Plain words: no run tokens,
|
|
71
|
+
job or effect IDs, error codes, tool names or file paths. Unknown is never zero.
|
|
72
|
+
About 60-110 words.
|
|
73
|
+
|
|
47
74
|
## Stop and delivery rules
|
|
48
75
|
|
|
76
|
+
If a tool call fails or its result can't be read, return `[SILENT]` and stop.
|
|
77
|
+
Never post an error, an apology or a guess; the open claim lets the next
|
|
78
|
+
scheduled window retry.
|
|
79
|
+
|
|
49
80
|
A direct request returns the review in the current conversation. A scheduled
|
|
50
81
|
claim posts at most one combined top-level message to the verified current main
|
|
51
82
|
channel. Respect daily and weekly mutes independently. If both sections are
|
|
@@ -906,11 +906,8 @@ policy:
|
|
|
906
906
|
- Do not use structured setup questions in YOLO mode. For pre-launch approval
|
|
907
907
|
gates, choose the recommended path yourself when confidence is sufficient, show
|
|
908
908
|
the assumed choice briefly, and continue.
|
|
909
|
-
- Pause only when no reasonable estimate exists, a tool requires missing
|
|
910
|
-
|
|
911
|
-
would start the live campaign.
|
|
912
|
-
- Never call `start_campaign` from YOLO mode without explicit user launch
|
|
913
|
-
confirmation. Do not invent proof; mark proof gaps and use safer claims.
|
|
909
|
+
- Pause only when no reasonable estimate exists, a tool requires missing credentials/data, the source/message quality floor fails, or the next action would start the live campaign without standing consent. If `launchConsent.mode` is `autopilot`, YOLO is on from the start and every gate is pre-approved.
|
|
910
|
+
- Call `start_campaign` from YOLO mode only with explicit user launch confirmation or `launchConsent.mode: "autopilot"` (asked once; see Standing Consent in create-campaign-v2). Do not invent proof; mark proof gaps and use safer claims.
|
|
914
911
|
|
|
915
912
|
Before the identity gate, use this customer-facing shape:
|
|
916
913
|
|
|
@@ -920,8 +917,7 @@ You're in {workspace}.
|
|
|
920
917
|
Excited to help you launch your LinkedIn outbound campaign.
|
|
921
918
|
|
|
922
919
|
We're at setup: first I'll use your LinkedIn profile to understand the company,
|
|
923
|
-
then I'll draft the campaign brief, help choose where to find buyers,
|
|
924
|
-
messages, and wait for final launch approval.
|
|
920
|
+
then I'll draft the campaign brief, help choose where to find buyers, check the messages, and get it ready to launch.
|
|
925
921
|
|
|
926
922
|
What's your LinkedIn profile URL or handle?
|
|
927
923
|
```
|
|
@@ -1061,7 +1057,7 @@ updates.
|
|
|
1061
1057
|
```text
|
|
1062
1058
|
You're in {workspaceLabel}.
|
|
1063
1059
|
|
|
1064
|
-
Excited to help you launch your LinkedIn outbound campaign. We're at setup: first I'll use your LinkedIn profile to understand the company, then I'll draft the campaign brief, help choose where to find buyers,
|
|
1060
|
+
Excited to help you launch your LinkedIn outbound campaign. We're at setup: first I'll use your LinkedIn profile to understand the company, then I'll draft the campaign brief, help choose where to find buyers, check the messages, and get it ready to launch.
|
|
1065
1061
|
|
|
1066
1062
|
What's your LinkedIn profile URL or handle?
|
|
1067
1063
|
```
|
|
@@ -1072,7 +1068,7 @@ updates.
|
|
|
1072
1068
|
```text
|
|
1073
1069
|
You're set up in {workspaceLabel}.
|
|
1074
1070
|
|
|
1075
|
-
Excited to help you launch your LinkedIn outbound campaign. We're at setup: first I'll use your LinkedIn profile to understand the company, then I'll draft the campaign brief, help choose where to find buyers,
|
|
1071
|
+
Excited to help you launch your LinkedIn outbound campaign. We're at setup: first I'll use your LinkedIn profile to understand the company, then I'll draft the campaign brief, help choose where to find buyers, check the messages, and get it ready to launch.
|
|
1076
1072
|
|
|
1077
1073
|
What's your LinkedIn profile URL or handle?
|
|
1078
1074
|
```
|
|
@@ -54,7 +54,7 @@ client/company, then confirm target, offer, proof, and source before the brief.
|
|
|
54
54
|
First visible request when no identity is known, after showing the active workspace:
|
|
55
55
|
|
|
56
56
|
```text
|
|
57
|
-
Excited to help you launch your LinkedIn outbound campaign. First I'll use your LinkedIn profile to understand the company, then I'll draft the brief, choose where to find buyers,
|
|
57
|
+
Excited to help you launch your LinkedIn outbound campaign. First I'll use your LinkedIn profile to understand the company, then I'll draft the brief, choose where to find buyers, check the messages, and get it ready to launch. What's your LinkedIn profile URL or handle?
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
Require a LinkedIn profile URL/handle before setup continues. Normalize
|
|
@@ -117,10 +117,7 @@ profile URL; never
|
|
|
117
117
|
continue from a website/domain alone. Infer buyer, offer/CTA, proof, source,
|
|
118
118
|
filters, and message direction from evidence plus user directions; newest wins.
|
|
119
119
|
Set `interactionMode: "autonomous"` once `campaignId` exists. Auto-select
|
|
120
|
-
confident pre-launch choices, show the assumption, and pause for missing data,
|
|
121
|
-
quality-floor failures, or final launch. Never call `start_campaign` without
|
|
122
|
-
explicit launch confirmation. YOLO still advances one visible checkpoint at a
|
|
123
|
-
time: brief approval moves only to source plan, never source/action approval.
|
|
120
|
+
confident pre-launch choices, show the assumption, and pause for missing data or quality-floor failures. With `launchConsent.mode: "autopilot"` (policy 7) YOLO is on from the start; launch is pre-approved once a sender, sequence and passing message exist. Else never call `start_campaign` without explicit launch confirmation.
|
|
124
121
|
|
|
125
122
|
## Structured Questions
|
|
126
123
|
|
|
@@ -7,7 +7,7 @@ operator who wants help launching their own LinkedIn outbound campaign. They are
|
|
|
7
7
|
not a developer debugging an agent runtime. Your job is to make launching that
|
|
8
8
|
campaign feel clear and achievable: understand who they are, understand the
|
|
9
9
|
company, turn the idea into a brief, find likely responders, draft strong
|
|
10
|
-
messages, and
|
|
10
|
+
messages, and, once they have given standing consent, do the work for them instead of asking.
|
|
11
11
|
|
|
12
12
|
Create Campaign is one continuous workflow for creating, continuing, and
|
|
13
13
|
editing. Read the home workspace's campaign observation first. Reuse the one
|
|
@@ -15,8 +15,7 @@ clear unfinished or explicitly selected campaign, its confirmed profile and all
|
|
|
15
15
|
completed answers. Ask one campaign-choice question only when several real
|
|
16
16
|
candidates remain. Ask for the intended LinkedIn profile only when unknown;
|
|
17
17
|
after receiving it, research and ideate immediately. Do not treat a public
|
|
18
|
-
profile as a connected sending account.
|
|
19
|
-
remains the only first-launch authority.
|
|
18
|
+
profile as a connected sending account. Launch needs the customer's standing consent (asked once, see Standing Consent) or their explicit Start.
|
|
20
19
|
|
|
21
20
|
## Core Belief
|
|
22
21
|
|
|
@@ -29,8 +28,7 @@ specific, commercially sharp, encouraging, and protective of their time.
|
|
|
29
28
|
|
|
30
29
|
The user should feel supported at every step. Make the path to launch feel
|
|
31
30
|
doable without pretending the work is trivial: explain the next business
|
|
32
|
-
decision, show useful judgment, and give them confidence that
|
|
33
|
-
or sends messages without approval. If something takes longer than expected,
|
|
31
|
+
decision, show useful judgment, and give them confidence that Sellable is doing the work and they can say "review first" or "pause" at any time. If something takes longer than expected,
|
|
34
32
|
acknowledge it lightly, explain the business reason for the wait, and tell them
|
|
35
33
|
what they will see next.
|
|
36
34
|
|
|
@@ -246,6 +244,17 @@ Only after the user asks "what does that change?" should you mention the exact
|
|
|
246
244
|
setting: `~/.codex/config.toml` with
|
|
247
245
|
`[features].default_mode_request_user_input = true`.
|
|
248
246
|
|
|
247
|
+
## Standing Consent
|
|
248
|
+
|
|
249
|
+
Read `customer_program` `observe` and check `program.context.launchConsent`. When it
|
|
250
|
+
is missing, ask once, in the same message as the first core question: "I can approve
|
|
251
|
+
messages that pass my quality check and launch for you, then keep your sends filled.
|
|
252
|
+
Reply review first if you would rather approve." Save the answer with `customer_program`
|
|
253
|
+
`save_context` as `launchConsent: { mode: "autopilot" | "review_first", decidedAt }`.
|
|
254
|
+
A yes, or no objection, is `autopilot`; a wish to approve first is `review_first`.
|
|
255
|
+
Never ask again; a later "review first" switches to `review_first`, and "pause" sets the saved work and sending pauses with `set_preferences`. If the
|
|
256
|
+
customer has not answered when you reach launch, keep the launch confirmation.
|
|
257
|
+
|
|
249
258
|
## Approval Ethic
|
|
250
259
|
|
|
251
260
|
Lead sourcing has two approvals. First, before any provider prompt/search/scout
|
|
@@ -392,7 +401,7 @@ At the brief approval gate, do not add a negative list of future steps that are
|
|
|
392
401
|
not approved. Ask whether the brief looks right and how Sellable should continue:
|
|
393
402
|
activate YOLO mode, build the campaign with AI step by step, or revise the
|
|
394
403
|
brief. The YOLO option approves the brief, auto-decides the remaining setup
|
|
395
|
-
choices with best guesses, and
|
|
404
|
+
choices with best guesses, and stops before final launch unless the customer has given standing consent.
|
|
396
405
|
|
|
397
406
|
At message review, if the user asks for edits, write the revised template and
|
|
398
407
|
examples before asking approval again. Route the feedback back to Message
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer/watch canonical: brief, source/import, filters/message, Settings, Start.","normalCustomerPath":"Use campaign state/MCP/watchNarration. Do not create, read, link, or surface local files. Print watch link once.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy diagnostics are opt-in, not active gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","campaign1KickoffIntake","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Watched currentStep switches need fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"must not print another watch-link handoff unless asked or recovering link."},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it","yolo autopilot"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Operator directions persist; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","Start Import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender identity match"],"mustPauseFor":["missing LinkedIn profile URL or handle","missing credentials/data","ambiguous choice with no reasonable estimate","source/message/filter quality failure","final live launch confirmation"],"neverAutoStart":true},"steps":[{"id":"bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"}}],"allowedTools":["bootstrap_create_campaign","get_auth_status","get_active_workspace","get_subskill_prompt","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign"],"waitFor":"bootstrap_complete","transitions":{"bootstrap_complete":"brief-interview"}},{"id":"brief-interview","onEnter":[{"action":"validate_supplied_campaign1_kickoff_handoff","tool":"validate_campaign1_kickoff_handoff","when":"the invocation supplies exact inline handoffMarkdown plus the admin_onboarding_call binding","requiredInput":["handoffMarkdown","binding.clientName","binding.engagementId","binding.workspaceId","binding.senderId","expected.clientName","expected.engagementId","expected.workspaceId","expected.senderId","expected.senderName","expected.pageRevision"],"transportRule":"Inline Markdown only. Never fetch a public Notion URL or read a hidden receipt/file.","onValid":"use_sufficient_intake_then_create_watchable_shell","onInvalid":"stop_before_campaign_mutation","validStateWrites":["campaign1KickoffIntake","campaignBrief"],"sourceRule":"Use only typed Signal Discovery topics, creators, posts, conversations, and participant behavior. Dream account and lookalikes are qualitative fit examples, never provider seeds.","skipQuestions":["product","ICP/audience","offer/CTA","proof/claims","exclusions","Signal Discovery thesis"],"mustPreserveGates":["brief/watch approval","Find Buyers Plan approval","Start Import approval","filter approval","message approval","final launch approval"]},{"action":"resolve_campaign_identity_before_strategy_packet","allowedTools":["fetch_company","fetch_linkedin_profile","WebFetch","WebSearch"],"mustNotInferFromNameOnly":true,"doNotPresentSenderPickerBeforeIdentityInference":true,"fallback":"Ask only: \"What is your LinkedIn profile URL or handle?\" Normalize handles to profile URLs; if not a profile, ask again before strategy questions.","requiresLinkedInProfileUrl":true,"doNotAcceptAsIdentityInput":["non-profile URL","company page","company/name-only input without a profile handle"],"acceptsLinkedInProfileHandle":true,"normalizeLinkedInProfileHandleToUrl":true},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"],"requiredInput":"LinkedIn profile URL or handle","skipIf":"validated Campaign 1 kickoff handoff supplied"},{"action":"verify_validated_handoff_sender_binding","when":"validated Campaign 1 kickoff handoff supplied","allowedTools":["fetch_linkedin_profile","complete_sender_research"],"rule":"Verify only the bound public sender identity and record normal research completion. Do not repeat strategy discovery or ask factual intake questions."},{"action":"confirm_target_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"Who should we target first?","options":["Use Sellable's researched recommendation","Choose a different buyer","I'm not sure yet"],"copyMustInclude":["research basis","recommendation is editable","who to target"],"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"user already supplied a clear target or yolo_mode with any reasonable best-estimate target"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"What should we pitch to prospects first?","options":["Use Sellable's researched recommendation","Use my current pitch","Shape the pitch together"],"copyMustInclude":["research basis","public LinkedIn research may be stale","recommendation is editable","pitch to prospects"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"confirm_trust_proof_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"What is the strongest credibility signal we can lead with?","options":["Use Sellable's recommended proof","Use customer proof or a case study","Use founder or company credibility"],"copyMustInclude":["build trust with prospects","recommendation is editable","proof to use or avoid"],"requiredOption":"Other / custom","skipIf":"user already supplied clear proof to use or avoid, or yolo_mode with any reasonable researched proof"},{"action":"choose_prospect_source_path_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"How should we find prospects?","options":["Find prospects for me","Use a CSV of LinkedIn profiles","Use a CSV of company domains"],"copyMustInclude":["prospects","no one added yet","find prospects for me"],"skipIf":"user already supplied a source path or yolo_mode with any reasonable source path"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"render_supplied_setup_receipt_before_brief_when_setup_questions_skip","when":"any setup question is skipped because the user already supplied or yolo mode inferred identity, target, offer, proof, or source","output":"normal chat receipt before the campaign brief or watch-link handoff","copyMustInclude":["Accepted LinkedIn input: normalized to the required LinkedIn profile URL.","Offer path: supplied current offer or Sellable's researched recommendation; you can replace it with your current offer."],"copyMustNotInclude":["Campaign Identity"]},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"],"handoffBriefRule":"When campaign1KickoffIntake exists, name and campaignBrief come only from validate_campaign1_kickoff_handoff output; senderLinkedinUrl comes from the validated sender public URL.","handoffSourceRule":"Do not persist dream account or lookalikes as provider/account seeds. Source planning still begins only after brief approval."},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"exact create_campaign.watchUrl; direct /campaign-builder/{campaignId}?mode={claude|codex} URL with workspaceId and token","copyMustInclude":["WATCH CODEX BUILD","Open live campaign builder","Keep this chat open.","approval questions here"],"immediateNextMainChatLine":"Ask for brief approval now. After approval, say: \"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet.\"","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true},"copyMustAvoid":["bare /campaign-builder/{campaignId} URL","derived watch URL","second watch-link handoff","shell open command","Next, we'll choose where to find buyers"],"oneTimeOnly":true}],"allowedTools":["validate_campaign1_kickoff_handoff","get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["This brief is based on Sellable's research and your answers.","If your site or LinkedIn is stale","Does this brief look right, and how should Sellable continue?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof","This approval covers","does not approve adding people","does not approve scraping posts","selecting a sender","attaching a sequence","Campaign Identity","what should this campaign sell first","sell first","[from you]","[from LinkedIn]","[from website]","[from case study]","[Sellable recommendation]"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered.","approvalBoundaryRule":"Keep this gate positive and short: ask approve/revise, then move to choosing where to find buyers.","sectionLabelRule":"Use ## Sender and Company for the person/company context; never render ## Campaign Identity.","skipIf":"the full brief was already rendered in the current shell-creation handoff before create_campaign returned","renderOnceRule":"Brief visible once before approval. If shown before shell creation, do not repeat after; append one watch-link handoff, then ask.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief + activate YOLO mode (Recommended)","Approve brief + build campaign with AI, step by step","Revise brief"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails","question":"Does this brief look right, and how should Sellable continue?","autonomousChoice":"Approve brief + activate YOLO mode (Recommended)","choiceDescriptions":{"Approve brief + activate YOLO mode (Recommended)":"Sellable will auto-decide the remaining setup choices with best guesses, including sources, filters, and messaging. It still stops before final launch.","Approve brief + build campaign with AI, step by step":"Approve this direction, then work with the AI through each major setup decision before it continues.","Revise brief":"Change the target, offer, proof, or campaign direction before continuing."}}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"],"show":["buyer groups or places we could check","best place to start","where the right buyers are already talking","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"best places to find buyers; no one added yet","explanationMustInclude":["why that place fits this campaign","enough likely prospects","what counts as a good sign","I won't add anyone yet"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["people to check","prospects"],"avoid":["lane","precision/scale tradeoff","evidence quality","workflow pain","ICP"],"termRule":{"buyers":"target market","peopleToCheck":"raw reactions/comments before fit is known","prospects":"likely usable people after fit","leads":"campaign rows"}},"questionOrder":"update_campaign to pick-provider; render Find Buyers Plan without repeating the watch link; never ask first; exactly one structured source-plan question total.","approvalFreshnessRule":"Do not reuse brief approval; continue exact earlier source-plan choice instead of asking a second identical question.","copyMustAvoid":["Watch link:","watch-link handoff"]},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison"]},"onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"pick-provider","watchNarration.stage":"find-leads","watchNarration.headline":"Review where to find buyers","watchNarration.visibleState":"Source selection is visible before lead finding.","watchNarration.agentIntent":"Codex is explaining where to look first and fallback.","watchNarration.nextAction":"Approve where to look first or choose another place","watchNarration.safety":"Only deciding where to look. No one is added yet."}},{"action":"render_find_buyers_plan_inline","oneShot":true,"requiredPrecondition":"currentStep=pick-provider has been saved with update_campaign","mustHappenAfter":["update_campaign currentStep=pick-provider"],"mustHappenBefore":["ask_find_buyers_plan_choice","get_provider_prompt","search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"],"output":"normal chat text before any request_user_input approval panel","requiredHeading":"Find Buyers Plan","requiredInlineFields":["buyer groups or places for this campaign","best place to start","why that place fits this campaign","where to look next if thin","what approval authorizes"],"copyMustInclude":["choose where to find buyers","I won't add anyone yet"],"copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads","Watch link:","campaign-builder URL","repeat the watch URL","watch-link handoff"],"approvalBoundary":"Approves where to look; no one added yet.","linkPolicy":"Do not include another watch link."},{"action":"ask_find_buyers_plan_choice","uses":"request_user_input","oneShot":true,"requiredPrecondition":"visible plan rendered; no earlier approval can satisfy this unless exact visible source choice exists; do not ask this if that exact earlier source choice exists","skipIf":"earlier source-plan question already captured exact visible source choice, post-plan provider, or yolo choice","choices":["Start with LinkedIn posts","Start with active LinkedIn profiles","Start with company/contact search","Choose a different place","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Start with LinkedIn posts","sales-nav":"Start with active LinkedIn profiles","prospeo":"Start with company/contact search"},"approvalState":"source_lane_approved","copyMustInclude":["Approve this plan for where to find buyers?","Start with LinkedIn posts"],"mustNotHappenBefore":["render_find_buyers_plan_inline"],"approvalStateRule":"Never ask a second identical source-plan question; set source_lane_approved only after visible plan, never from brief approval/generic/pre-plan state."},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"]},{"action":"run_parent_thread_source_funnel","requiredPrecondition":"source_lane_approved after visible Find Buyers Plan approval","mode":"parent_thread_inline_sequential","sourceAgents":"forbidden in normal create-campaign","loadsOnlyActiveProviderPrompt":true,"defaultOrder":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"stopOnFirstViableUnlessComparisonRequested":true}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_dnc_entries","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input","get_campaign_navigation_state"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","Task","spawn_agent"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Start Import approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","Start Import action awaiting approval","for Signal Discovery: compact Source Recommendation in plain language with selected posts, people to check, likely prospects, first review, and fallback","for Signal Discovery: recommended scrape post count and target people-to-check volume","first campaign review size, clearly separate from source sampling","runner-up and why it lost","raw volume","sampled people","sampled fits as n/N plus percentage/range","estimated usable prospects","cleanup risk","what happens after Start Import"],"copyMustAvoid":["lead-source scouting","source scouting","headline-fit","engagers needed","execution slice","ICP","good buyers","real buyers","This approval covers","It does not approve","does not approve filters","filters, messages, sender selection","sender selection, sequence, or sending","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundaryRule":"Use positive Start Import copy only: after approval build source list, add to campaign, review first 15 leads; never list future non-approvals.","linkPolicy":"Do not include another watch link in Source Recommendation; describe the Start Import decision only."},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Start Import for the approved source list","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Start Import for the approved Sales Nav source list","prospeo":"Start Import for the approved Prospeo source list"},"postApprovalContract":{"singleUseApproval":true,"doNotRepeatAfterApproval":["Source Recommendation","show_source_decision_card","ask_source_review_choice"],"requiredNextActionAfterApproval":"get_provider_prompt then import_leads","signalDiscoveryNextTool":"get_provider_prompt then import_leads"},"autoSelectIf":"yolo_mode and projected usable pool clears the source quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","providerSearchAssociation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_confirmed","revise_leads","confirm_with_user","auto_continue"],"transitions":{"lead_review_confirmed":"auto-execute-leads","revise_leads":"find-leads","confirm_with_user":"auto-execute-leads","auto_continue":"auto-execute-leads"}},{"id":"auto-execute-leads","currentStepValue":"auto-execute-leads","reference":"references/step-13-import-leads.md","onEnter":[{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2-tail"}},{"tool":"get_provider_prompt"},{"tool":"import_leads","requiredFields":["campaignOfferId","selected source/list","sourceListTarget or targetEngagerCount"],"requiredValues":{"salesNavProspeoDefaultSourceListTarget":1000,"signalDiscoveryDefaultEngagerTarget":1500},"modeAddHandshake":{"firstCallReturns":"needsModeSelection when adding to an existing campaign-attached list","requiredFollowup":"retry with mode=add after explicit user/source-state compatibility is confirmed"},"surfaceDedupRatio":true},{"tool":"wait_for_lead_list_ready","repeatUntil":"ready_true_or_cancelled_or_import_failed","requiredValues":{"requireComplete":true},"onImportStillRunning":"re-run wait_for_lead_list_ready; do not call confirm_lead_list just because rows exist","partialOverrideRule":"Only if the user explicitly asks to keep going early may the next confirm_lead_list call pass allowPartialSourceList: true."},{"tool":"confirm_lead_list","requiredFields":["campaignOfferId","selectedLeadListId","reviewBatchLimit"],"requiredValues":{"reviewBatchLimit":15},"capture":["workflowTableId","reviewBatchRowIds"],"requiredPrecondition":"wait_for_lead_list_ready returned ready:true, unless the user explicitly asked to keep going early with a partial list","partialOverrideField":"allowPartialSourceList:true only after explicit user early-continue instruction","mustNotRunWhen":["wait_for_lead_list_ready reason=import_still_running and no explicit early-continue instruction","wait_for_lead_list_ready reason=cancelled","wait_for_lead_list_ready reason=import_failed","missing import job metadata"]},{"tool":"wait_for_campaign_table_ready"},{"tool":"get_rows_minimal","requiredValues":{"tableId":"{workflowTableId}","limit":15},"capture":["reviewBatchRowHash"]},{"action":"summarize_review_batch_and_advance_to_filter_choice","maxCustomerCopyLines":4,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"linkPolicy":"Ask add filters vs skip filters without repeating the watch link.","purpose":"ask filter choice immediately"}],"requiredCampaignState":["campaignId","providerSearchAssociation","selectedLeadListId"],"allowedTools":["get_provider_prompt","get_subskill_prompt","import_leads","wait_for_lead_list_ready","confirm_lead_list","wait_for_campaign_table_ready","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["get_post_find_leads_scout_registry","Task","spawn_agent","list_senders","queue_cells","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","save_rubrics"],"waitFor":"source_list_confirmed_and_review_sample_ready","transitions":{"source_list_confirmed_and_review_sample_ready":"filter-choice","escalation_triggered":"escalation"},"hardRules":["import_leads_then_repeated_wait_for_lead_list_ready_then_confirm_lead_list_then_wait_for_campaign_table_ready","partial_source_rows_do_not_unblock_confirm_lead_list_without_explicit_user_early_continue","cancelled_or_failed_source_import_stops_before_campaign_table_copy"]},{"id":"filter-choice","currentStepValue":"filter-choice","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"filter-choice","watchNarration.stage":"fit-message"}},{"action":"ask_filter_choice","uses":"request_user_input","choices":["Use filters","Skip filters","Revise source"],"autoSelectIf":"yolo_mode; choose filters unless source is tightly curated or user directed otherwise","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"copyMustInclude":["source rows are in the campaign","use filters while Message Drafting runs or skip filters"]}],"hardRules":["ask_filter_choice_immediately_after_review_batch_import","do_not_call_get_subskill_prompt_before_filter_choice","do_not_call_get_subskill_asset_before_filter_choice","do_not_call_get_post_find_leads_scout_registry_before_filter_choice","do_not_spawn_prospect_setup_before_filter_choice"],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign","get_campaign_navigation_state"],"doNotAllow":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","Task","spawn_agent","create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["filters_enabled","filters_skipped","revise_leads"],"transitions":{"filters_enabled":"prospect-setup","filters_skipped":"prospect-setup","revise_leads":"find-leads"}},{"id":"prospect-setup","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filter Rules is visible while Codex drafts/saves filters.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_message_drafting_after_filter_choice","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"]},{"tool":"get_post_find_leads_scout_registry","returns":["post-find-leads-message-scout"],"rule":"normal post-import registry exposes only Message Drafting; filters are parent-thread MCP work","mustRunBefore":["launch_message_drafting_after_filter_choice","get_subskill_asset:references/filter-leads.md","save_rubrics","ask_filter_rubric_review_choice"],"registryOnlyIsNotLaunch":true},{"action":"launch_message_drafting_after_filter_choice","mode":"background_agent_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled or filters_skipped after filter-choice answer","doesNotQueueCells":true,"customerNarration":"Message Drafting is preparing.","inputs":["campaignId","workflowTableId","brief/source summary","3-5 sample workflow-table rows","optional campaignName/selectedLeadListId/filterChoice"],"forbiddenSiblings":["source/filter workers"],"handoffCompression":"lean_message_drafting_handoff_no_hashes_no_counts_no_full_row_data","requiredConcreteToolCall":"Task or spawn_agent when host supports background agents","registryOnlyIsNotLaunch":true,"mustRunBefore":["get_subskill_asset:references/filter-leads.md","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"],"yoloPermissionRule":"do not ask the user for worker permission in YOLO.","startProof":"Message Drafting counts as started after actual Task/spawn_agent or the parent persists fallback-active proof for its authorized inline run.","noBackgroundFallback":"Start the same full message branch inline before filter reference or block."},{"action":"continue_after_drafting","when":"filters_skipped","transition":"filters_skipped_message_started"},{"tool":"get_subskill_asset","action":"load_filter_reference_in_parent","when":"filters_enabled","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"},"rule":"Parent thread uses this reference to draft production rubrics; do not spawn a filter worker."},{"tool":"save_rubrics","when":"filters_enabled","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["Filter rules saved for review","These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the saved filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and parent saved production-shaped rubrics","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"save_rubrics succeeded or active campaign rubrics verified"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_post_find_leads_scout_registry","get_subskill_asset","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging","filters_skipped_message_started"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_campaign_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_prospect_setup_worker_or_deep_prompt_before_filter_choice","message_drafting_after_filter_choice","message_drafting_no_cells","only_message_drafting_may_spawn_background_agent","filters_are_parent_thread_mcp_work","no_post_find_leads_filter_scout_in_normal_path","registry_call_is_not_message_drafting_launch","yolo_must_not_ask_permission_for_message_drafting_worker","message_drafting_must_start_before_filter_reference_or_save_rubrics","if_background_agent_unavailable_start_full_message_branch_inline_before_filters_or_block","message_drafting_start_does_not_advance_filter_path"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review","filters_skipped_message_started":"message-generation"}},{"id":"filter-rubric","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"action":"observed-row-grounding","uses":["get_rows_minimal","get_rows","enrich_with_prospeo"],"requiredBefore":"save_rubrics","contract":["Call get_rows_minimal before drafting filters.","Call get_rows({ tableId, rowIds }) for row/enrichment sample; rowIds must be a JSON array, not a comma-delimited string.","Use multiple visible rows for repeated false positives, broad enterprise sellers, noisy titles, agencies/vendors, geography, and budget gaps.","Use one direct enrichment sample via enrich_with_prospeo if rows are thin; set enrichMobile: false.","On direct enrichment failure, no credits, provider error, or no match, do not retry; continue with uncertainty or block.","If no rows, do not enrich or save row-grounded filters; ask/import rows or use brief/source with uncertainty.","If zero enriched fields, separate raw row fields from unavailable enrichment; do not pretend enrichment-backed filters are observed.","Stop before save_rubrics on stale workflowTableId/source/campaignId/row ids.","Fail generic sales/BD/partnerships unless target/current evidence proves fit and budget path.","No standalone current_role_* safety; fold into keep/exclude or engagement_only_or_stale_fit_exclusion."]},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","get_rows_minimal","get_rows","enrich_with_prospeo","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric","queue_campaign_cells","bulk_enrich_with_prospeo"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","onEnter":[{"action":"set_message_review_visible_step_by_filter_choice","tool":"update_campaign","watchNarration.stage":"fit-message","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Filters saved + waiting for message approval"},"watchNarrationRule":"After filter approval, show Filter Leads; do not queue enrichment/filter/message cells before approve-message.","mustRunBefore":"run_or_reconcile_message_draft_builder","failureRule":"currentStep=apply-icp-rubric"},{"action":"run_or_reconcile_message_draft_builder","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["Task/spawn_agent or inline fallback before parent drafting","generic gpt-5.5 xhigh Message Drafting fallback","lean handoff: campaignId, workflowTableId, brief, source rule, 3-5 rows","no copied counts, hashes, full row ids, broad row data, or artifacts","branch loads current brief/context, full generate-messages prompt/assets","return labeled Markdown: template, token rules, sample, status, recommendation, validation, hash, retry","hide fallback sample, risk notes, and qaReceipt on happy path","inline fallback=parent-thread-fallback; same gates"],"stateSource":"live campaign/table plus sample rows","outputState":"messageDraftRecommendation with internal validation passed","revisionMode":{"when":"revise_messaging, message QA request, or latest user message requests template changes before approve-message","route":"send feedback/QA/rewrite request to post-find-leads-message-scout or generic gpt-5.5 xhigh Message Drafting branch","requiredInputs":["latest user feedback","current messageDraftRecommendation and basisToken/outputHash","lean campaign/table basis and brief summary; branch reloads live state"],"forbiddenParentActions":["rewrite template in parent","QA template in parent from memory","update_campaign_brief before approve-message"],"output":"revised messageDraftRecommendation rendered before request_user_input"},"onlyMessageBackgroundAgent":true,"forbiddenSiblingAgents":["source-scout-*","any filter background worker"],"handoffCompression":"lean_message_handoff","finalGateRequiredBeforeReady":"Load create-campaign-v2-validation before ready."}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign","Task","spawn_agent"],"toolRules":["message_drafting_agent_first_no_silent_parent_fallback; yolo counts","on_filters_enabled_path_parent_must_not_enter_message_review_until_save_rubrics_success_and_filter_approval","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns template, token rules, rendered sample, status, recommendation, validation, hash, retry.","messageDraftRecommendation hides fallback/risk/qaReceipt in the happy path.","If campaign/table/sample basis does not match, classify the output stale or blocked.","message_revision_feedback_routes_to_message_drafting_agent_not_parent","message_qa_routes_to_message_drafting_agent_not_parent","message_drafting_loads_full_generate_messages_prompt_all_chunks","message_drafting_loads_all_reference_assets_via_get_subskill_asset","message_drafting_loads_create_campaign_v2_validation_prompt_before_return","message_drafting_handoff_uses_lean_basis_no_hashes_counts_or_full_rows","message_drafting_loads_current_campaign_brief_before_drafting","parent_may_load_generate_messages_only_for_explicitly_approved_inline_fallback","before return branch loads create-campaign-v2-validation prompt and runs internal validation","pre_approval_step_filter_leads"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages","AskUserQuestion","request_user_input"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation"},"revisionLoop":{"trigger":"user gives copy feedback, asks for QA/update, or chooses revise-messaging","required":["route latest user feedback or QA request to Message Drafting with current recommendation and live campaign/table state","produce a new messageDraftRecommendation with revisionNotes or validationStatus","render revised ## Message Template, ## Rendered Example, and What changed before asking approval"],"blocked":["asking whether an unseen new version is better","reusing the old template after acknowledging feedback","rewriting the template in the parent thread","update_campaign_brief before approve-message","request_user_input or AskUserQuestion during message-generation","QAing the template in the parent thread"],"outputState":"messageDraftRecommendation"},"hardRules":["message_revision_must_render_new_template_before_approval_question","message_revision_saves_only_after_approve_message","message_generation_must_not_call_request_user_input","message_generation_must_transition_to_message_review_only_after_renderable_draft_state","message_drafting_agent_first_no_silent_parent_fallback","message_revision_routes_to_message_drafting_agent","message_qa_routes_to_message_drafting_agent","filter_path_requires_saved_filters_before_message_review","only_message_drafting_may_spawn_background_agent","no_queue_before_template_approval"],"currentStepValue":"apply-icp-rubric"},{"id":"message-review","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"watchNarrationRule":"Before template approval, keep browser on Filter Leads.","mustRunBefore":"render_message_review_from_state","failureRule":"do not ask for message approval until Filter Leads shows and cells unqueued."},{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:"],"doNotShowByDefault":["Rendered fallback sample","Concerns","QA receipt","Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes","validation notes","QA details"],"copyMustInclude":["one recommended template","one rendered sample","token rules and fallbacks","full list remains paused"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"revisionRenderRule":"After feedback, route feedback to Message Drafting; show revised template/example; never ask if an unseen version is better.","parentMustNot":["rewrite template from memory","invent validation summary without Message Drafting output","render qaReceipt, risk notes, or renderedFallbackSample on the normal happy path"]},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","template approval only locks the draft direction; final Start still controls sending"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"current or revised template and rendered example are visible in this turn","revisionPrecondition":"after revise_messaging, revised template is visible before this question"},{"action":"sync_approved_message_set_to_campaign_brief","tool":"update_campaign_brief","when":"after approve-message","requiredFields":["campaignId","approvedMessageTemplate","Token Fill Rules","Token Fill Examples"],"writesCampaignState":"approvedMessageTemplate","revisionApprovalRule":"If the user requested revisions, write only the latest Message Drafting-approved revised template and token rules after approve-message.","forbiddenBefore":"approve-message"}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"},"currentStepValue":"apply-icp-rubric"},{"id":"validate-sample","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"After approvals, queue bounded enrichment.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"watchNarrationRule":"Template saved; Filter Leads runs bounded enrichment/scoring."},{"tool":"queue_campaign_cells","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"enrich","rowSelector":{"type":"reviewBatch"},"forceRerun":false},"targetCountSource":"reviewBatch.rowCount default 15"},{"tool":"wait_for_campaign_processing","requiredFields":["workflowTableId","minPassedCount"],"requiredValues":{"minPassedCount":1},"readVia":"stats_only_tool_result","timeoutGuidance":"On timeout, use partial diagnostics; do not repeat identical waits.","customerSummaryPattern":["{checked} leads checked","{passed} passed fit scoring","{blocked} blocked before sending","full list remains paused until approval"]},{"action":"handle_partial_or_timeout_sample","customerStatus":"sample-needs-revision","rule":"Surface partial status and route to revision.","copyMustInclude":"completed, passed, pending, blocked, paused"}],"requiredCampaignState":["campaignId","workflowTableId","approvedMessageTemplate","filterRubricsApproved when enableICPFilters=true"],"allowedTools":["get_subskill_asset","queue_campaign_cells","select_campaign_cells","get_campaign_table_schema","get_rows_minimal","record_campaign_review_batch","wait_for_campaign_processing","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["campaign_processing_waits_are_stats_only_by_default","queue_review_batch_by_selector_never_fetch_rows_for_cell_ids","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"filter-rubric","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation","new_sample":"validate-sample"}},{"id":"auto-execute-messaging","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_campaign_cells","batchSize":100,"when":"any passing row has pending, empty, or stale Generate Message cell","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"generateMessage","rowSelector":{"type":"needsGeneratedMessage"},"forceRerun":false},"cellSource":"selector-engine needsGeneratedMessage rows using approved campaign brief template"},{"tool":"wait_for_campaign_processing","purpose":"wait_for_first_current_revision_generated_message","requiredValues":{"minGeneratedMessages":1,"templateRevision":"current"},"readVia":"stats_only_tool_result","invariant":"A generated message implies the pass gate; stale or wrong-revision messages are excluded from readiness."},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve the reviewed draft rows and continue to Settings","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused","this approves reviewed draft rows for setup; final Start still controls sending"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_subskill_asset","get_campaign_table_schema","select_campaign_cells","queue_campaign_cells","wait_for_campaign_processing","revise_message_template_and_rerun","update_campaign","AskUserQuestion","request_user_input","update_cell"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["generated_message_count_excludes_stale_or_wrong_revision_messages","message_template_revision_uses_brief_update_then_generate_message_rerun","do_not_overwrite_generated_message_cells_directly","critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_message_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite","approve_reviewed_rows_via_semantic_approved_selector_not_get_rows_approveCellId"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"start_campaign_message_preparation","requiredValues":{"targetPreparedMessages":"{requestedMessageCount}","approvalMode":"{scheduleOrApproveIntent ? 'approve' : 'mark_ready'}"},"intentMapping":{"scheduleSends":"approve the exact requested send count"},"when":"explicit_count_or_schedule","batchStrategy":"max100_serial","rowBudgetStrategy":"sample100_cap300"},{"tool":"get_campaign_message_preparation_status","copyMustInclude":["preparation job status","checked rows","target","stop reason"],"when":"prep_job_started"},{"tool":"get_campaign"},{"tool":"list_senders","purpose":"identity proof at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say message review is complete, Settings is open, lead research/filtering happened outside LinkedIn, and nothing sends until Start."},{"action":"surface_sender_and_slack_handoff","requiredVisibleContent":["connect or select a LinkedIn sender","Lead research and filtering already happened outside your LinkedIn account.","This account is only used for approved sending after final launch.","Nothing sends until the final Start step.","Slack reply review","recommended sequence","final launch confirmation is still ahead"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"ask_sender_selection","uses":"request_user_input","singleChoice":true,"choices":["Use this connected sender","Connect a different sender in Settings","Pause here"],"autoSelectIf":"yolo one exact identity match","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"noAutoSelectIf":"identity proof is missing; ask"},{"action":"attach_selected_sender","tool":"update_campaign","when":"user selected an available connected sender","requiredValues":{"senderIds":["{selectedSenderId}"],"currentStep":"sequence","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the sender was attached and the app is moving to Sequence review; sequence remains editable and nothing sends until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the recommended follow-up sequence is attached, final launch review is open, and nothing sends until Start."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["cancel_campaign_message_preparation","get_campaign_message_preparation_status","start_campaign_message_preparation","get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"],"boundedLaunchRule":"preparation job approved X; no broad approve-all."},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, say final greenlight was accepted, campaign is live/running, and progress can be watched."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","currentStepValue":"running","onEnter":[{"action":"surface_campaign_live_confirmation_without_watch_link","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
|
|
1
|
+
{"version":"v2.1-compact","workflow":"create-campaign-v2","principle":"CampaignOffer/watch canonical: brief, source/import, filters/message, Settings, Start.","normalCustomerPath":"Use campaign state/MCP/watchNarration. Do not create, read, link, or surface local files. Print watch link once.","legacyCompatibility":{"validationSubskill":"create-campaign-v2-validation","tailSubskill":"create-campaign-v2-tail","rule":"Legacy diagnostics are opt-in, not active gates."},"commitGateChoices":["approve","revise-brief","revise-leads","revise-rubric","revise-messaging","abort"],"canonicalStateFields":["campaignId","watchUrl","campaignBrief","campaign1KickoffIntake","currentStep","watchNarration","interactionMode","providerSearchAssociation","selectedLeadListId","workflowTableId","filterChoice","leadScoringRubrics","messageDraftRecommendation","approvedMessageTemplate","senderIds","sequenceTemplate","runningState"],"watchNarrationTransitionContract":{"rule":"Watched currentStep switches need fresh watchNarration.","requiredFields":["stage","headline","visibleState","agentIntent","nextAction"],"copyMustInclude":["what just happened","visible page/state","next user action"],"appliesToTools":["create_campaign","update_campaign","attach_recommended_sequence","start_campaign"],"oneTimeWatchLinkPolicy":"must not print another watch-link handoff unless asked or recovering link."},"lazyReferences":{"watch":["references/watch-link-handoff.md","references/watch-guide-narration.md"],"source":["references/lead-validation-preview.md","references/step-13-import-leads.md"],"filter":["references/filter-leads.md"],"message":[],"tail":["references/sample-validation-loop.md","references/step-15-re-cascade.md","references/final-handoff-contract.md"]},"yoloMode":{"triggerPhrases":["yolo","--yolo","mode=yolo","autopilot","use best guesses","use best estimates","answer for me","just run it","yolo autopilot"],"identityRequestOnlyWhenMissing":true,"operatorDirectionsRule":"Operator directions persist; newest conflicting direction wins.","setInteractionModeAfterCampaignCreate":"autonomous","autoSelectsPreLaunchChoices":["campaign focus","brief approval","source plan","Start Import approval","filters vs skip filters","filter rubric approval","message template approval when recommendation is approve-message","generated message review when quality floor passes","sender identity match"],"mustPauseFor":["missing LinkedIn profile URL or handle","missing credentials/data","ambiguous choice with no reasonable estimate","source/message/filter quality failure","final live launch confirmation"],"neverAutoStart":true,"standingConsent":{"source":"customer_program context.launchConsent.mode","autopilot":{"yoloOnFromStart":true,"overrides":["final live launch confirmation","neverAutoStart"],"launchWhen":"a sender and sequence are attached and at least one generated message passed the quality floor"},"reviewFirst":"the defaults above apply","askOnce":"in the first core-question message; save with customer_program save_context"}},"steps":[{"id":"bootstrap","onEnter":[{"tool":"bootstrap_create_campaign","requiredValues":{"flowVersion":"v2"}},{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2"}}],"allowedTools":["bootstrap_create_campaign","get_auth_status","get_active_workspace","get_subskill_prompt","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign"],"waitFor":"bootstrap_complete","transitions":{"bootstrap_complete":"brief-interview"}},{"id":"brief-interview","onEnter":[{"action":"validate_supplied_campaign1_kickoff_handoff","tool":"validate_campaign1_kickoff_handoff","when":"the invocation supplies exact inline handoffMarkdown plus the admin_onboarding_call binding","requiredInput":["handoffMarkdown","binding.clientName","binding.engagementId","binding.workspaceId","binding.senderId","expected.clientName","expected.engagementId","expected.workspaceId","expected.senderId","expected.senderName","expected.pageRevision"],"transportRule":"Inline Markdown only. Never fetch a public Notion URL or read a hidden receipt/file.","onValid":"use_sufficient_intake_then_create_watchable_shell","onInvalid":"stop_before_campaign_mutation","validStateWrites":["campaign1KickoffIntake","campaignBrief"],"sourceRule":"Use only typed Signal Discovery topics, creators, posts, conversations, and participant behavior. Dream account and lookalikes are qualitative fit examples, never provider seeds.","skipQuestions":["product","ICP/audience","offer/CTA","proof/claims","exclusions","Signal Discovery thesis"],"mustPreserveGates":["brief/watch approval","Find Buyers Plan approval","Start Import approval","filter approval","message approval","final launch approval"]},{"action":"resolve_campaign_identity_before_strategy_packet","allowedTools":["fetch_company","fetch_linkedin_profile","WebFetch","WebSearch"],"mustNotInferFromNameOnly":true,"doNotPresentSenderPickerBeforeIdentityInference":true,"fallback":"Ask only: \"What is your LinkedIn profile URL or handle?\" Normalize handles to profile URLs; if not a profile, ask again before strategy questions.","requiresLinkedInProfileUrl":true,"doNotAcceptAsIdentityInput":["non-profile URL","company page","company/name-only input without a profile handle"],"acceptsLinkedInProfileHandle":true,"normalizeLinkedInProfileHandleToUrl":true},{"action":"run_campaign_identity_research_before_strategy_packet","target":"research-sender","requiredCompletion":"complete_sender_research","allowedTools":["get_subskill_prompt","fetch_linkedin_profile","fetch_company","fetch_linkedin_posts","fetch_company_posts","WebFetch","WebSearch","complete_sender_research"],"requiredInput":"LinkedIn profile URL or handle","skipIf":"validated Campaign 1 kickoff handoff supplied"},{"action":"verify_validated_handoff_sender_binding","when":"validated Campaign 1 kickoff handoff supplied","allowedTools":["fetch_linkedin_profile","complete_sender_research"],"rule":"Verify only the bound public sender identity and record normal research completion. Do not repeat strategy discovery or ask factual intake questions."},{"action":"confirm_target_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"Who should we target first?","options":["Use Sellable's researched recommendation","Choose a different buyer","I'm not sure yet"],"copyMustInclude":["research basis","recommendation is editable","who to target"],"requiredOption":"Other / custom","neverCollectOpenTextWithStructuredQuestion":true,"skipIf":"user already supplied a clear target or yolo_mode with any reasonable best-estimate target"},{"action":"confirm_current_offer_before_strategy_packet","uses":"request_user_input","question":"What should we pitch to prospects first?","options":["Use Sellable's researched recommendation","Use my current pitch","Shape the pitch together"],"copyMustInclude":["research basis","public LinkedIn research may be stale","recommendation is editable","pitch to prospects"],"skipIf":"user already supplied a clear current offer or yolo_mode with any reasonable researched recommendation"},{"action":"confirm_trust_proof_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"What is the strongest credibility signal we can lead with?","options":["Use Sellable's recommended proof","Use customer proof or a case study","Use founder or company credibility"],"copyMustInclude":["build trust with prospects","recommendation is editable","proof to use or avoid"],"requiredOption":"Other / custom","skipIf":"user already supplied clear proof to use or avoid, or yolo_mode with any reasonable researched proof"},{"action":"choose_prospect_source_path_before_strategy_packet","uses":"request_user_input","singleChoice":true,"question":"How should we find prospects?","options":["Find prospects for me","Use a CSV of LinkedIn profiles","Use a CSV of company domains"],"copyMustInclude":["prospects","no one added yet","find prospects for me"],"skipIf":"user already supplied a source path or yolo_mode with any reasonable source path"},{"action":"infer_strategy_packet_in_yolo_mode","when":"yolo_mode","skipStructuredSetupQuestions":true,"inferFields":["buyer segment","offer/CTA","proof to use or avoid","lead source","filter choice","message direction"],"sourceOfTruth":"identity/company lookup plus operator directions","mustStateAssumptionsInBrief":true},{"action":"render_supplied_setup_receipt_before_brief_when_setup_questions_skip","when":"any setup question is skipped because the user already supplied or yolo mode inferred identity, target, offer, proof, or source","output":"normal chat receipt before the campaign brief or watch-link handoff","copyMustInclude":["Accepted LinkedIn input: normalized to the required LinkedIn profile URL.","Offer path: supplied current offer or Sellable's researched recommendation; you can replace it with your current offer."],"copyMustNotInclude":["Campaign Identity"]},{"action":"create_watchable_campaign_shell_with_v1_brief","tool":"create_campaign","requiredFields":["name","campaignBrief","clientProspectId or senderLinkedinUrl","currentStep","watchNarration"],"requiredValues":{"currentStep":"create-offer","watchNarration.stage":"brief"},"capture":["campaignId","watchUrl"],"canonicalStateWrites":["campaignId","watchUrl","campaignBrief","currentStep:create-offer"],"handoffBriefRule":"When campaign1KickoffIntake exists, name and campaignBrief come only from validate_campaign1_kickoff_handoff output; senderLinkedinUrl comes from the validated sender public URL.","handoffSourceRule":"Do not persist dream account or lookalikes as provider/account seeds. Source planning still begins only after brief approval."},{"action":"surface_campaign_shell_watch_link","watchUrlSource":"create_campaign.watchUrl","requiredWatchUrlShape":"exact create_campaign.watchUrl; direct /campaign-builder/{campaignId}?mode={claude|codex} URL with workspaceId and token","copyMustInclude":["WATCH CODEX BUILD","Open live campaign builder","Keep this chat open.","approval questions here"],"immediateNextMainChatLine":"Ask for brief approval now. After approval, say: \"Brief approved. Next, we'll choose where to find buyers. I won't add anyone yet.\"","codexBrowserHandoff":{"openWhenAvailable":false,"printWatchLinkOnly":true,"tellUserCommandEnterOrClick":false,"mustNotUseBrowserAutomation":true,"fallbackMustNotClaimInspection":true},"copyMustAvoid":["bare /campaign-builder/{campaignId} URL","derived watch URL","second watch-link handoff","shell open command","Next, we'll choose where to find buyers"],"oneTimeOnly":true}],"allowedTools":["validate_campaign1_kickoff_handoff","get_subskill_prompt","fetch_company","fetch_company_posts","fetch_linkedin_profile","fetch_linkedin_posts","complete_sender_research","create_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["campaign_shell_created","brief_ready","confirm_with_user"],"transitions":{"campaign_shell_created":"brief-review","brief_ready":"brief-review","confirm_with_user":"brief-review"}},{"id":"brief-review","onEnter":[{"action":"render_brief_inline","minimumVisibleDetail":"campaign direction, buyer, offer, proof, source plan, safety gates","copyMustInclude":["This brief is based on Sellable's research and your answers.","If your site or LinkedIn is stale","Does this brief look right, and how should Sellable continue?"],"copyMustNotInclude":["quoted first-message copy","Message Angle as final proof","This approval covers","does not approve adding people","does not approve scraping posts","selecting a sender","attaching a sequence","Campaign Identity","what should this campaign sell first","sell first","[from you]","[from LinkedIn]","[from website]","[from case study]","[Sellable recommendation]"],"messageHintRule":"If any message-shape hint remains, keep it as non-final bullets and say real examples come after leads are found and filtered.","approvalBoundaryRule":"Keep this gate positive and short: ask approve/revise, then move to choosing where to find buyers.","sectionLabelRule":"Use ## Sender and Company for the person/company context; never render ## Campaign Identity.","skipIf":"the full brief was already rendered in the current shell-creation handoff before create_campaign returned","renderOnceRule":"Brief visible once before approval. If shown before shell creation, do not repeat after; append one watch-link handoff, then ask.","copyMustAvoid":["duplicate brief render after shell creation","second Watch link:"]},{"action":"ask_brief_choice","uses":"request_user_input","choices":["Approve brief + activate YOLO mode (Recommended)","Approve brief + build campaign with AI, step by step","Revise brief"],"skipIf":"yolo_mode; auto_continue after rendering assumptions unless brief quality floor fails","question":"Does this brief look right, and how should Sellable continue?","autonomousChoice":"Approve brief + activate YOLO mode (Recommended)","choiceDescriptions":{"Approve brief + activate YOLO mode (Recommended)":"Sellable will auto-decide the remaining setup choices with best guesses, including sources, filters, and messaging. It still stops before final launch.","Approve brief + build campaign with AI, step by step":"Approve this direction, then work with the AI through each major setup decision before it continues.","Revise brief":"Change the target, offer, proof, or campaign direction before continuing."}}],"requiredCampaignState":["campaignId","watchUrl","campaignBrief","currentStep"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["user_brief_confirmed","revise_brief","auto_continue"],"transitions":{"user_brief_confirmed":"find-leads","revise_brief":"brief-interview","auto_continue":"find-leads"}},{"id":"find-leads","sourceSelectionFunnel":{"preScoutRecommendationGate":{"required":true,"mustHappenBefore":["get_provider_prompt","search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"],"show":["buyer groups or places we could check","best place to start","where the right buyers are already talking","what signs the next search will check","where to look next if the first place is too thin","what approval authorizes"],"approvalAuthorizes":"best places to find buyers; no one added yet","explanationMustInclude":["why that place fits this campaign","enough likely prospects","what counts as a good sign","I won't add anyone yet"],"yoloMode":{"autoApproveRecommendedLane":true,"mustShowAssumedChoice":true,"pauseIfConfidenceLow":true},"customerLanguage":{"readability":"grade-5","requiredHeading":"Find Buyers Plan","prefer":["people to check","prospects"],"avoid":["lane","precision/scale tradeoff","evidence quality","workflow pain","ICP"],"termRule":{"buyers":"target market","peopleToCheck":"raw reactions/comments before fit is known","prospects":"likely usable people after fit","leads":"campaign rows"}},"questionOrder":"update_campaign to pick-provider; render Find Buyers Plan without repeating the watch link; never ask first; exactly one structured source-plan question total.","approvalFreshnessRule":"Do not reuse brief approval; continue exact earlier source-plan choice instead of asking a second identical question.","copyMustAvoid":["Watch link:","watch-link handoff"]},"defaultWhenSourceUnspecified":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"parallelAllowedOnlyWhen":["user explicitly requested source comparison"]},"onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"pick-provider","watchNarration.stage":"find-leads","watchNarration.headline":"Review where to find buyers","watchNarration.visibleState":"Source selection is visible before lead finding.","watchNarration.agentIntent":"Codex is explaining where to look first and fallback.","watchNarration.nextAction":"Approve where to look first or choose another place","watchNarration.safety":"Only deciding where to look. No one is added yet."}},{"action":"render_find_buyers_plan_inline","oneShot":true,"requiredPrecondition":"currentStep=pick-provider has been saved with update_campaign","mustHappenAfter":["update_campaign currentStep=pick-provider"],"mustHappenBefore":["ask_find_buyers_plan_choice","get_provider_prompt","search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"],"output":"normal chat text before any request_user_input approval panel","requiredHeading":"Find Buyers Plan","requiredInlineFields":["buyer groups or places for this campaign","best place to start","why that place fits this campaign","where to look next if thin","what approval authorizes"],"copyMustInclude":["choose where to find buyers","I won't add anyone yet"],"copyMustAvoid":["lane","source scouting","provider","precision/scale tradeoff","evidence quality","pilot volume","ICP","workflow pain","lead-source scouting","not importing leads","Watch link:","campaign-builder URL","repeat the watch URL","watch-link handoff"],"approvalBoundary":"Approves where to look; no one added yet.","linkPolicy":"Do not include another watch link."},{"action":"ask_find_buyers_plan_choice","uses":"request_user_input","oneShot":true,"requiredPrecondition":"visible plan rendered; no earlier approval can satisfy this unless exact visible source choice exists; do not ask this if that exact earlier source choice exists","skipIf":"earlier source-plan question already captured exact visible source choice, post-plan provider, or yolo choice","choices":["Start with LinkedIn posts","Start with active LinkedIn profiles","Start with company/contact search","Choose a different place","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Start with LinkedIn posts","sales-nav":"Start with active LinkedIn profiles","prospeo":"Start with company/contact search"},"approvalState":"source_lane_approved","copyMustInclude":["Approve this plan for where to find buyers?","Start with LinkedIn posts"],"mustNotHappenBefore":["render_find_buyers_plan_inline"],"approvalStateRule":"Never ask a second identical source-plan question; set source_lane_approved only after visible plan, never from brief approval/generic/pre-plan state."},{"action":"persist_provider_search_step_before_search","tool":"update_campaign","requiredPrecondition":"source_lane_approved set after the visible Find Buyers Plan approval question","providerCurrentStepMap":{"signal-discovery":"signal-discovery","sales-nav":"sales-nav","prospeo":"prospeo"},"requiredValues":{"leadSourceType":"new","leadSourceProvider":"approved provider","currentStep":"provider-specific current step","watchNarration.stage":"find-leads","watchNarration.headline":"Searching the approved place","watchNarration.safety":"Looking only. No one is added yet."},"mustRunBefore":["search_signals","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","fetch_post_engagers"]},{"action":"run_parent_thread_source_funnel","requiredPrecondition":"source_lane_approved after visible Find Buyers Plan approval","mode":"parent_thread_inline_sequential","sourceAgents":"forbidden in normal create-campaign","loadsOnlyActiveProviderPrompt":true,"defaultOrder":["signal-discovery","sales-nav-recent-active","sales-nav-general","prospeo"],"stopOnFirstViableUnlessComparisonRequested":true}],"requiredCampaignState":["campaignId","campaignBrief","currentStep"],"allowedTools":["get_provider_prompt","lookup_sales_nav_filter","search_sales_nav","search_prospeo_companies","confirm_prospeo_company_accounts","search_prospeo","search_signals","select_promising_posts","fetch_post_engagers","fetch_company","fetch_linkedin_profile","load_csv_dnc_entries","load_csv_linkedin_leads","load_csv_domains","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input","get_campaign_navigation_state"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","Task","spawn_agent"],"waitFor":["lead_review_ready","revise_brief","confirm_with_user"],"transitions":{"lead_review_ready":"lead-review","revise_brief":"brief-interview","confirm_with_user":"lead-review"}},{"id":"lead-review","label":"Start Import approval","onEnter":[{"action":"show_source_decision_card","requiredInlineFields":["primary source and exact filters/recipe","Start Import action awaiting approval","for Signal Discovery: compact Source Recommendation in plain language with selected posts, people to check, likely prospects, first review, and fallback","for Signal Discovery: recommended scrape post count and target people-to-check volume","first campaign review size, clearly separate from source sampling","runner-up and why it lost","raw volume","sampled people","sampled fits as n/N plus percentage/range","estimated usable prospects","cleanup risk","what happens after Start Import"],"copyMustAvoid":["lead-source scouting","source scouting","headline-fit","engagers needed","execution slice","ICP","good buyers","real buyers","This approval covers","It does not approve","does not approve filters","filters, messages, sender selection","sender selection, sequence, or sending","Watch link:","campaign-builder URL","repeat the watch URL"],"approvalBoundaryRule":"Use positive Start Import copy only: after approval build source list, add to campaign, review first 15 leads; never list future non-approvals.","linkPolicy":"Do not include another watch link in Source Recommendation; describe the Start Import decision only."},{"action":"ask_source_review_choice","uses":"request_user_input","choices":["Approve scraping N recommended LinkedIn posts","Start Import for the approved source list","Revise source","Pause here"],"approvalChoiceLabelsByProvider":{"signal-discovery":"Approve scraping {scrapePostCount} recommended LinkedIn posts?","sales-nav":"Start Import for the approved Sales Nav source list","prospeo":"Start Import for the approved Prospeo source list"},"postApprovalContract":{"singleUseApproval":true,"doNotRepeatAfterApproval":["Source Recommendation","show_source_decision_card","ask_source_review_choice"],"requiredNextActionAfterApproval":"get_provider_prompt then import_leads","signalDiscoveryNextTool":"get_provider_prompt then import_leads"},"autoSelectIf":"yolo_mode and projected usable pool clears the source quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","campaignBrief","providerSearchAssociation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign"],"waitFor":["lead_review_confirmed","revise_leads","confirm_with_user","auto_continue"],"transitions":{"lead_review_confirmed":"auto-execute-leads","revise_leads":"find-leads","confirm_with_user":"auto-execute-leads","auto_continue":"auto-execute-leads"}},{"id":"auto-execute-leads","currentStepValue":"auto-execute-leads","reference":"references/step-13-import-leads.md","onEnter":[{"tool":"get_subskill_prompt","requiredValues":{"subskillName":"create-campaign-v2-tail"}},{"tool":"get_provider_prompt"},{"tool":"import_leads","requiredFields":["campaignOfferId","selected source/list","sourceListTarget or targetEngagerCount"],"requiredValues":{"salesNavProspeoDefaultSourceListTarget":1000,"signalDiscoveryDefaultEngagerTarget":1500},"modeAddHandshake":{"firstCallReturns":"needsModeSelection when adding to an existing campaign-attached list","requiredFollowup":"retry with mode=add after explicit user/source-state compatibility is confirmed"},"surfaceDedupRatio":true},{"tool":"wait_for_lead_list_ready","repeatUntil":"ready_true_or_cancelled_or_import_failed","requiredValues":{"requireComplete":true},"onImportStillRunning":"re-run wait_for_lead_list_ready; do not call confirm_lead_list just because rows exist","partialOverrideRule":"Only if the user explicitly asks to keep going early may the next confirm_lead_list call pass allowPartialSourceList: true."},{"tool":"confirm_lead_list","requiredFields":["campaignOfferId","selectedLeadListId","reviewBatchLimit"],"requiredValues":{"reviewBatchLimit":15},"capture":["workflowTableId","reviewBatchRowIds"],"requiredPrecondition":"wait_for_lead_list_ready returned ready:true, unless the user explicitly asked to keep going early with a partial list","partialOverrideField":"allowPartialSourceList:true only after explicit user early-continue instruction","mustNotRunWhen":["wait_for_lead_list_ready reason=import_still_running and no explicit early-continue instruction","wait_for_lead_list_ready reason=cancelled","wait_for_lead_list_ready reason=import_failed","missing import job metadata"]},{"tool":"wait_for_campaign_table_ready"},{"tool":"get_rows_minimal","requiredValues":{"tableId":"{workflowTableId}","limit":15},"capture":["reviewBatchRowHash"]},{"action":"summarize_review_batch_and_advance_to_filter_choice","maxCustomerCopyLines":4,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"linkPolicy":"Ask add filters vs skip filters without repeating the watch link.","purpose":"ask filter choice immediately"}],"requiredCampaignState":["campaignId","providerSearchAssociation","selectedLeadListId"],"allowedTools":["get_provider_prompt","get_subskill_prompt","import_leads","wait_for_lead_list_ready","confirm_lead_list","wait_for_campaign_table_ready","get_rows_minimal","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["get_post_find_leads_scout_registry","Task","spawn_agent","list_senders","queue_cells","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","save_rubrics"],"waitFor":"source_list_confirmed_and_review_sample_ready","transitions":{"source_list_confirmed_and_review_sample_ready":"filter-choice","escalation_triggered":"escalation"},"hardRules":["import_leads_then_repeated_wait_for_lead_list_ready_then_confirm_lead_list_then_wait_for_campaign_table_ready","partial_source_rows_do_not_unblock_confirm_lead_list_without_explicit_user_early_continue","cancelled_or_failed_source_import_stops_before_campaign_table_copy"]},{"id":"filter-choice","currentStepValue":"filter-choice","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"filter-choice","watchNarration.stage":"fit-message"}},{"action":"ask_filter_choice","uses":"request_user_input","choices":["Use filters","Skip filters","Revise source"],"autoSelectIf":"yolo_mode; choose filters unless source is tightly curated or user directed otherwise","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"copyMustInclude":["source rows are in the campaign","use filters while Message Drafting runs or skip filters"]}],"hardRules":["ask_filter_choice_immediately_after_review_batch_import","do_not_call_get_subskill_prompt_before_filter_choice","do_not_call_get_subskill_asset_before_filter_choice","do_not_call_get_post_find_leads_scout_registry_before_filter_choice","do_not_spawn_prospect_setup_before_filter_choice"],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign","get_campaign_navigation_state"],"doNotAllow":["get_subskill_prompt","get_subskill_asset","get_post_find_leads_scout_registry","Task","spawn_agent","create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages"],"waitFor":["filters_enabled","filters_skipped","revise_leads"],"transitions":{"filters_enabled":"prospect-setup","filters_skipped":"prospect-setup","revise_leads":"find-leads"}},{"id":"prospect-setup","onEnter":[{"action":"persist_add_filters_approval","tool":"update_campaign","when":"filters_enabled","requiredFields":["campaignId","enableICPFilters","currentStep","watchNarration"],"requiredValues":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Create filter rules","watchNarration.visibleState":"Filter Rules is visible while Codex drafts/saves filters.","watchNarration.nextAction":"Review saved filter rules"},"mustRunBefore":["get_post_find_leads_scout_registry","launch_message_drafting_after_filter_choice","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"]},{"tool":"get_post_find_leads_scout_registry","returns":["post-find-leads-message-scout"],"rule":"normal post-import registry exposes only Message Drafting; filters are parent-thread MCP work","mustRunBefore":["launch_message_drafting_after_filter_choice","get_subskill_asset:references/filter-leads.md","save_rubrics","ask_filter_rubric_review_choice"],"registryOnlyIsNotLaunch":true},{"action":"launch_message_drafting_after_filter_choice","mode":"background_agent_when_host_supports_subagents","target":"post-find-leads-message-scout","when":"filters_enabled or filters_skipped after filter-choice answer","doesNotQueueCells":true,"customerNarration":"Message Drafting is preparing.","inputs":["campaignId","workflowTableId","brief/source summary","3-5 sample workflow-table rows","optional campaignName/selectedLeadListId/filterChoice"],"forbiddenSiblings":["source/filter workers"],"handoffCompression":"lean_message_drafting_handoff_no_hashes_no_counts_no_full_row_data","requiredConcreteToolCall":"Task or spawn_agent when host supports background agents","registryOnlyIsNotLaunch":true,"mustRunBefore":["get_subskill_asset:references/filter-leads.md","load_filter_reference_in_parent","save_rubrics","ask_filter_rubric_review_choice"],"yoloPermissionRule":"do not ask the user for worker permission in YOLO.","startProof":"Message Drafting counts as started after actual Task/spawn_agent or the parent persists fallback-active proof for its authorized inline run.","noBackgroundFallback":"Start the same full message branch inline before filter reference or block."},{"action":"continue_after_drafting","when":"filters_skipped","transition":"filters_skipped_message_started"},{"tool":"get_subskill_asset","action":"load_filter_reference_in_parent","when":"filters_enabled","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"},"rule":"Parent thread uses this reference to draft production rubrics; do not spawn a filter worker."},{"tool":"save_rubrics","when":"filters_enabled","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["Filter rules saved for review","These rules prevent wasted sends before I score/import the list.","Recommended because of the researched ICP, source sample, and repeated false-positive patterns.","one pass example and one block example when available","approval is for the saved filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and parent saved production-shaped rubrics","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"save_rubrics succeeded or active campaign rubrics verified"}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_post_find_leads_scout_registry","get_subskill_asset","save_rubrics","get_campaign","get_rows_minimal","update_campaign","get_campaign_navigation_state","AskUserQuestion","request_user_input","Task","spawn_agent"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","queue_cells","start_campaign","check_rubric","generate_messages"],"waitFor":["filter_rubrics_approved","revise_leads","revise_rubric","revise_messaging","filters_skipped_message_started"],"hardRules":["after_save_rubrics_currentStep_must_stay_create-icp-rubric_until_filter_approval","filter_approval_required_before_apply-icp-rubric_or_queue_campaign_cells","do_not_move_browser_to_messages_until_filter_leads_step_is_current_or_filters_are_explicitly_skipped","no_prospect_setup_worker_or_deep_prompt_before_filter_choice","message_drafting_after_filter_choice","message_drafting_no_cells","only_message_drafting_may_spawn_background_agent","filters_are_parent_thread_mcp_work","no_post_find_leads_filter_scout_in_normal_path","registry_call_is_not_message_drafting_launch","yolo_must_not_ask_permission_for_message_drafting_worker","message_drafting_must_start_before_filter_reference_or_save_rubrics","if_background_agent_unavailable_start_full_message_branch_inline_before_filters_or_block","message_drafting_start_does_not_advance_filter_path"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","confirm_with_user":"message-review","filters_skipped_message_started":"message-generation"}},{"id":"filter-rubric","onEnter":[{"tool":"get_subskill_asset","requiredValues":{"subskillName":"create-campaign-v2","assetPath":"references/filter-leads.md"}},{"action":"observed-row-grounding","uses":["get_rows_minimal","get_rows","enrich_with_prospeo"],"requiredBefore":"save_rubrics","contract":["Call get_rows_minimal before drafting filters.","Call get_rows({ tableId, rowIds }) for row/enrichment sample; rowIds must be a JSON array, not a comma-delimited string.","Use multiple visible rows for repeated false positives, broad enterprise sellers, noisy titles, agencies/vendors, geography, and budget gaps.","Use one direct enrichment sample via enrich_with_prospeo if rows are thin; set enrichMobile: false.","On direct enrichment failure, no credits, provider error, or no match, do not retry; continue with uncertainty or block.","If no rows, do not enrich or save row-grounded filters; ask/import rows or use brief/source with uncertainty.","If zero enriched fields, separate raw row fields from unavailable enrichment; do not pretend enrichment-backed filters are observed.","Stop before save_rubrics on stale workflowTableId/source/campaignId/row ids.","Fail generic sales/BD/partnerships unless target/current evidence proves fit and budget path.","No standalone current_role_* safety; fold into keep/exclude or engagement_only_or_stale_fit_exclusion."]},{"tool":"save_rubrics","requiredFields":["campaignOfferId","leadScoringRubrics"],"writesCampaignState":"leadScoringRubrics","requiredSideEffects":{"enableICPFilters":true,"currentStep":"create-icp-rubric","watchNarration.headline":"Filter rules saved for review"}},{"action":"ask_filter_rubric_review_choice","uses":"request_user_input","choices":["Approve filters","Revise filters","Pause"],"copyMustInclude":["These rules prevent wasted sends before I score/import the list.","approval object is filter rules only","approval is for the filter rules","Approve these filter rules."],"autoSelectIf":"yolo_mode and rubrics are production-shaped","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"requiredCampaignState":["campaignId","workflowTableId"],"allowedTools":["get_subskill_asset","get_rows_minimal","get_rows","enrich_with_prospeo","save_rubrics","AskUserQuestion","request_user_input"],"doNotAllow":["create_campaign","list_senders","import_leads","confirm_lead_list","update_campaign","queue_cells","start_campaign","check_rubric","queue_campaign_cells","bulk_enrich_with_prospeo"],"waitFor":["filter_rubrics_approved","revise_leads","confirm_with_user","auto_continue"],"transitions":{"filter_rubrics_approved":"message-generation","revise_leads":"find-leads","confirm_with_user":"message-generation","auto_continue":"message-generation"}},{"id":"message-generation","onEnter":[{"action":"set_message_review_visible_step_by_filter_choice","tool":"update_campaign","watchNarration.stage":"fit-message","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message","watchNarration.headline":"Filters saved + waiting for message approval"},"watchNarrationRule":"After filter approval, show Filter Leads; do not queue enrichment/filter/message cells before approve-message.","mustRunBefore":"run_or_reconcile_message_draft_builder","failureRule":"currentStep=apply-icp-rubric"},{"action":"run_or_reconcile_message_draft_builder","target":"post-find-leads-message-scout","toolCallRequiredBeforeDraft":["Task/spawn_agent or inline fallback before parent drafting","generic gpt-5.5 xhigh Message Drafting fallback","lean handoff: campaignId, workflowTableId, brief, source rule, 3-5 rows","no copied counts, hashes, full row ids, broad row data, or artifacts","branch loads current brief/context, full generate-messages prompt/assets","return labeled Markdown: template, token rules, sample, status, recommendation, validation, hash, retry","hide fallback sample, risk notes, and qaReceipt on happy path","inline fallback=parent-thread-fallback; same gates"],"stateSource":"live campaign/table plus sample rows","outputState":"messageDraftRecommendation with internal validation passed","revisionMode":{"when":"revise_messaging, message QA request, or latest user message requests template changes before approve-message","route":"send feedback/QA/rewrite request to post-find-leads-message-scout or generic gpt-5.5 xhigh Message Drafting branch","requiredInputs":["latest user feedback","current messageDraftRecommendation and basisToken/outputHash","lean campaign/table basis and brief summary; branch reloads live state"],"forbiddenParentActions":["rewrite template in parent","QA template in parent from memory","update_campaign_brief before approve-message"],"output":"revised messageDraftRecommendation rendered before request_user_input"},"onlyMessageBackgroundAgent":true,"forbiddenSiblingAgents":["source-scout-*","any filter background worker"],"handoffCompression":"lean_message_handoff","finalGateRequiredBeforeReady":"Load create-campaign-v2-validation before ready."}],"requiredCampaignState":["campaignId","campaignBrief","selectedLeadListId","workflowTableId"],"allowedTools":["get_subskill_prompt","get_campaign","get_rows_minimal","update_campaign","Task","spawn_agent"],"toolRules":["message_drafting_agent_first_no_silent_parent_fallback; yolo counts","on_filters_enabled_path_parent_must_not_enter_message_review_until_save_rubrics_success_and_filter_approval","brief.md, lead-review.md, and lead-sample.json are optional debug context only.","messageDraftRecommendation returns template, token rules, rendered sample, status, recommendation, validation, hash, retry.","messageDraftRecommendation hides fallback/risk/qaReceipt in the happy path.","If campaign/table/sample basis does not match, classify the output stale or blocked.","message_revision_feedback_routes_to_message_drafting_agent_not_parent","message_qa_routes_to_message_drafting_agent_not_parent","message_drafting_loads_full_generate_messages_prompt_all_chunks","message_drafting_loads_all_reference_assets_via_get_subskill_asset","message_drafting_loads_create_campaign_v2_validation_prompt_before_return","message_drafting_handoff_uses_lean_basis_no_hashes_counts_or_full_rows","message_drafting_loads_current_campaign_brief_before_drafting","parent_may_load_generate_messages_only_for_explicitly_approved_inline_fallback","before return branch loads create-campaign-v2-validation prompt and runs internal validation","pre_approval_step_filter_leads"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","queue_cells","start_campaign","generate_messages","AskUserQuestion","request_user_input"],"waitFor":["message_validation_ready","revise_rubric","revise_messaging"],"transitions":{"message_validation_ready":"message-review","revise_rubric":"filter-rubric","revise_messaging":"message-generation"},"revisionLoop":{"trigger":"user gives copy feedback, asks for QA/update, or chooses revise-messaging","required":["route latest user feedback or QA request to Message Drafting with current recommendation and live campaign/table state","produce a new messageDraftRecommendation with revisionNotes or validationStatus","render revised ## Message Template, ## Rendered Example, and What changed before asking approval"],"blocked":["asking whether an unseen new version is better","reusing the old template after acknowledging feedback","rewriting the template in the parent thread","update_campaign_brief before approve-message","request_user_input or AskUserQuestion during message-generation","QAing the template in the parent thread"],"outputState":"messageDraftRecommendation"},"hardRules":["message_revision_must_render_new_template_before_approval_question","message_revision_saves_only_after_approve_message","message_generation_must_not_call_request_user_input","message_generation_must_transition_to_message_review_only_after_renderable_draft_state","message_drafting_agent_first_no_silent_parent_fallback","message_revision_routes_to_message_drafting_agent","message_qa_routes_to_message_drafting_agent","filter_path_requires_saved_filters_before_message_review","only_message_drafting_may_spawn_background_agent","no_queue_before_template_approval"],"currentStepValue":"apply-icp-rubric"},{"id":"message-review","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"watchNarrationRule":"Before template approval, keep browser on Filter Leads.","mustRunBefore":"render_message_review_from_state","failureRule":"do not ask for message approval until Filter Leads shows and cells unqueued."},{"action":"render_message_review_from_state","requiredState":["campaignBrief","selectedLeadListId","workflowTableId","messageDraftRecommendation"],"requiredVisibleLabels":["## Message Template","## Rendered Example","Good token fill:","My take:","Question: approve-message or revise-messaging?","Recommendation:"],"doNotShowByDefault":["Rendered fallback sample","Concerns","QA receipt","Token Notes","Good omit / fallback","Bad fill to avoid","Token Adherence Table"],"internalPersistenceOnly":["Token Fill Rules","Token Fill Examples","fallback guidance","bad-fill avoidance notes","validation notes","QA details"],"copyMustInclude":["one recommended template","one rendered sample","token rules and fallbacks","full list remains paused"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"revisionRenderRule":"After feedback, route feedback to Message Drafting; show revised template/example; never ask if an unseen version is better.","parentMustNot":["rewrite template from memory","invent validation summary without Message Drafting output","render qaReceipt, risk notes, or renderedFallbackSample on the normal happy path"]},{"action":"ask_message_review_choice","uses":"request_user_input","choices":["approve-message","revise-messaging"],"copyMustInclude":["approve this message template and continue","template approval only locks the draft direction; final Start still controls sending"],"autoSelectIf":"yolo_mode and Recommendation is approve-message; revise autonomously when Recommendation is revise-messaging","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"requiredPrecondition":"current or revised template and rendered example are visible in this turn","revisionPrecondition":"after revise_messaging, revised template is visible before this question"},{"action":"sync_approved_message_set_to_campaign_brief","tool":"update_campaign_brief","when":"after approve-message","requiredFields":["campaignId","approvedMessageTemplate","Token Fill Rules","Token Fill Examples"],"writesCampaignState":"approvedMessageTemplate","revisionApprovalRule":"If the user requested revisions, write only the latest Message Drafting-approved revised template and token rules after approve-message.","forbiddenBefore":"approve-message"}],"requiredCampaignState":["campaignId","workflowTableId","messageDraftRecommendation"],"allowedTools":["AskUserQuestion","request_user_input","update_campaign_brief","update_campaign","get_rows_minimal"],"doNotAllow":["create_campaign","list_senders","save_rubrics","import_leads","confirm_lead_list","start_campaign"],"waitFor":["message_approved","revise_messaging"],"transitions":{"message_approved":"validate-sample","revise_messaging":"message-generation"},"currentStepValue":"apply-icp-rubric"},{"id":"validate-sample","currentStepValue":"apply-icp-rubric","reference":"references/sample-validation-loop.md","visibleStepRule":"After approvals, queue bounded enrichment.","onEnter":[{"tool":"update_campaign","requiredValues":{"currentStep":"apply-icp-rubric","watchNarration.stage":"fit-message"},"watchNarrationRule":"Template saved; Filter Leads runs bounded enrichment/scoring."},{"tool":"queue_campaign_cells","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"enrich","rowSelector":{"type":"reviewBatch"},"forceRerun":false},"targetCountSource":"reviewBatch.rowCount default 15"},{"tool":"wait_for_campaign_processing","requiredFields":["workflowTableId","minPassedCount"],"requiredValues":{"minPassedCount":1},"readVia":"stats_only_tool_result","timeoutGuidance":"On timeout, use partial diagnostics; do not repeat identical waits.","customerSummaryPattern":["{checked} leads checked","{passed} passed fit scoring","{blocked} blocked before sending","full list remains paused until approval"]},{"action":"handle_partial_or_timeout_sample","customerStatus":"sample-needs-revision","rule":"Surface partial status and route to revision.","copyMustInclude":"completed, passed, pending, blocked, paused"}],"requiredCampaignState":["campaignId","workflowTableId","approvedMessageTemplate","filterRubricsApproved when enableICPFilters=true"],"allowedTools":["get_subskill_asset","queue_campaign_cells","select_campaign_cells","get_campaign_table_schema","get_rows_minimal","record_campaign_review_batch","wait_for_campaign_processing","update_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["import_leads","list_senders","start_campaign","enrich_with_prospeo","bulk_enrich_with_prospeo","check_rubric"],"hardRules":["campaign_processing_waits_are_stats_only_by_default","queue_review_batch_by_selector_never_fetch_rows_for_cell_ids","timeout_never_repeats_without_customer_handoff","timeout_or_underfloor_sample_never_advances_to_settings"],"waitFor":["sample_validated","sample_revision_required"],"transitions":{"sample_validated":"auto-execute-messaging","sample_revision_required":"filter-rubric","revise_leads":"find-leads","revise_rubric":"filter-rubric","escalation_triggered":"escalation","new_sample":"validate-sample"}},{"id":"auto-execute-messaging","currentStepValue":"auto-execute-messaging","references":["references/parallel-critique-protocol.md","references/thomas-variant-selection.md","references/step-15-re-cascade.md"],"onEnter":[{"tool":"queue_campaign_cells","batchSize":100,"when":"any passing row has pending, empty, or stale Generate Message cell","requiredFields":["workflowTableId","columnRole","rowSelector"],"requiredValues":{"columnRole":"generateMessage","rowSelector":{"type":"needsGeneratedMessage"},"forceRerun":false},"cellSource":"selector-engine needsGeneratedMessage rows using approved campaign brief template"},{"tool":"wait_for_campaign_processing","purpose":"wait_for_first_current_revision_generated_message","requiredValues":{"minGeneratedMessages":1,"templateRevision":"current"},"readVia":"stats_only_tool_result","invariant":"A generated message implies the pass gate; stale or wrong-revision messages are excluded from readiness."},{"action":"observe_generate_message_results","reference":"references/step-15-re-cascade.md"},{"action":"enforce_token_contract","modeFromConfig":"messaging.tokenContract"},{"action":"optional_critique_pass","enabledFromConfig":"messaging.critique.enabled","protocol":"references/parallel-critique-protocol.md","sampleSizeFromConfig":"messaging.critique.sampleSize","budgetUsdCapFromConfig":"messaging.critique.budgetUsdCap","perCriticTimeoutFromConfig":"messaging.critique.perCriticTimeoutSeconds","totalTimeoutFromConfig":"messaging.critique.totalTimeoutSeconds","criticsFromConfig":"messaging.critique.critics","enforceFinalizerPassFromConfig":"messaging.critique.synthesis.enforceFinalizerPass","rejectOnFakeProofFromConfig":"messaging.critique.rejectOnFakeProof","rejectOnUnsupportedTokenFromConfig":"messaging.critique.rejectOnUnsupportedToken","onBudgetTrip":"halt_critique_continue_plain_tail","onPerCriticTimeout":"drop_critic_proceed_with_remaining","onTotalTimeout":"persist_plain_message_for_row","onTokenContractViolation":"persist_plain_message_for_row","onFakeProof":"persist_plain_message_for_row"},{"action":"optional_opus_subset","enabledFromConfig":"messaging.critique.opus.enabled","selection":"references/thomas-variant-selection.md","maxMessagesPerPassFromConfig":"messaging.critique.opus.maxMessagesPerPass","budgetUsdCapFromConfig":"messaging.critique.opus.budgetUsdCap","onOpusBudgetTrip":"halt_opus_continue_non_opus","onOpusTokenContractViolation":"fallback_to_non_opus_rewrite"},{"tool":"update_campaign","requiredValues":{"currentStep":"auto-execute-messaging","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the first passing generated message is ready in Messages; next is review before Settings."},{"action":"ask_generated_message_review_choice","uses":"request_user_input","choices":["Approve reviewed draft rows and continue to Settings","Revise filters","Revise message template","Pause here"],"copyMustInclude":["approve the reviewed draft rows and continue to Settings","tokens resolved","company/person match","proof claim","prospect angle","language/tone when known","obvious bad fits","Would I take this call?","weak personalization can burn the sender's reputation","full list remains paused","this approves reviewed draft rows for setup; final Start still controls sending"],"autoSelectIf":"yolo_mode and the first passing generated message clears the quality floor","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_subskill_asset","get_campaign_table_schema","select_campaign_cells","queue_campaign_cells","wait_for_campaign_processing","revise_message_template_and_rerun","update_campaign","AskUserQuestion","request_user_input","update_cell"],"doNotAllow":["import_leads","list_senders","start_campaign","generate_messages"],"hardRules":["generated_message_count_excludes_stale_or_wrong_revision_messages","message_template_revision_uses_brief_update_then_generate_message_rerun","do_not_overwrite_generated_message_cells_directly","critique_failure_never_escalates","critique_sample_size_bounded_by_config","first_passing_generated_message_unblocks_review","critics_fixed_at_targeting_copy_voice","synthesis_enforces_message_token_contract","opus_reserved_for_highest_value_subset","proposed_token_never_persisted_in_rewrite","approve_reviewed_rows_via_semantic_approved_selector_not_get_rows_approveCellId"],"waitFor":["generated_messages_approved","sample_revision_required"],"transitions":{"generated_messages_approved":"awaiting-user-greenlight","revise_filters":"filter-rubric","revise_messaging":"message-generation","escalation_triggered":"escalation"}},{"id":"awaiting-user-greenlight","currentStepValue":"awaiting-user-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"start_campaign_message_preparation","requiredValues":{"targetPreparedMessages":"{requestedMessageCount}","approvalMode":"{scheduleOrApproveIntent ? 'approve' : 'mark_ready'}"},"intentMapping":{"scheduleSends":"approve the exact requested send count"},"when":"explicit_count_or_schedule","batchStrategy":"max100_serial","rowBudgetStrategy":"sample100_cap300"},{"tool":"get_campaign_message_preparation_status","copyMustInclude":["preparation job status","checked rows","target","stop reason"],"when":"prep_job_started"},{"tool":"get_campaign"},{"tool":"list_senders","purpose":"identity proof at Settings"},{"tool":"update_campaign","requiredValues":{"currentStep":"settings","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say message review is complete, Settings is open, lead research/filtering happened outside LinkedIn, and nothing sends until Start."},{"action":"surface_sender_and_slack_handoff","requiredVisibleContent":["connect or select a LinkedIn sender","Lead research and filtering already happened outside your LinkedIn account.","This account is only used for approved sending after final launch.","Nothing sends until the final Start step.","Slack reply review","recommended sequence","final launch confirmation is still ahead"],"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]},{"action":"ask_sender_selection","uses":"request_user_input","singleChoice":true,"choices":["Use this connected sender","Connect a different sender in Settings","Pause here"],"autoSelectIf":"yolo one exact identity match","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"],"noAutoSelectIf":"identity proof is missing; ask"},{"action":"attach_selected_sender","tool":"update_campaign","when":"user selected an available connected sender","requiredValues":{"senderIds":["{selectedSenderId}"],"currentStep":"sequence","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the sender was attached and the app is moving to Sequence review; sequence remains editable and nothing sends until Start."},{"tool":"attach_recommended_sequence","when":"after senderIds are attached","requiredValues":{"campaignId":"{campaignId}","currentStep":"send","watchNarration.stage":"review-ready"},"watchNarrationRule":"Say the recommended follow-up sequence is attached, final launch review is open, and nothing sends until Start."},{"action":"ask_final_launch_greenlight","uses":"request_user_input","singleChoice":true,"choices":["Start campaign","Review campaign first","Pause here"],"copyMustInclude":["quality confidence means sample messages and prospect angle were checked","launch confidence means sender, sequence, and Start are ready","approved messages begin sending according to the sequence","replies and meetings follow connected settings","you can monitor and pause","no hidden extra approval disappears after clicking Start"],"onUserStart":"claude-greenlight","neverAutoSelectInYolo":true,"copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["cancel_campaign_message_preparation","get_campaign_message_preparation_status","start_campaign_message_preparation","get_campaign","get_campaign_navigation_state","list_senders","update_campaign","attach_recommended_sequence","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"autoStart":false,"watchRequired":true,"waitFor":["sender_connection_required","sender_attached","sequence_attached","ready_to_launch","user_greenlight","ui_start_detected"],"transitions":{"sender_connection_required":"awaiting-user-greenlight","sender_attached":"awaiting-user-greenlight","sequence_attached":"awaiting-user-greenlight","ready_to_launch":"awaiting-user-greenlight","user_greenlight":"claude-greenlight","ui_start_detected":"running"}},{"id":"claude-greenlight","reference":"references/final-handoff-contract.md","onEnter":[{"tool":"get_campaign"},{"action":"verify_sequence_and_current_step_before_start","requiredState":["workflowTableId","senderIds","sequenceTemplate","at least one approved generated message","currentStep in awaiting-user-greenlight|claude-greenlight|send"],"boundedLaunchRule":"preparation job approved X; no broad approve-all."},{"tool":"start_campaign","requiredFields":["campaignId"],"persistsCurrentStep":"running","watchNarrationRule":"After start_campaign succeeds, say final greenlight was accepted, campaign is live/running, and progress can be watched."}],"allowedTools":["get_campaign","attach_recommended_sequence","start_campaign","AskUserQuestion","request_user_input"],"watchRequired":true,"waitFor":"campaign_started","transitions":{"campaign_started":"running"}},{"id":"running","currentStepValue":"running","onEnter":[{"action":"surface_campaign_live_confirmation_without_watch_link","copyMustAvoid":["Watch link:","campaign-builder URL","repeat the watch URL"]}],"allowedTools":["get_campaign","AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign"],"terminal":true},{"id":"escalation","reference":"references/escalation-ladder.md","allowedTools":["AskUserQuestion","request_user_input"],"doNotAllow":["start_campaign","import_leads"],"transitions":{"revise_brief":"brief-interview","revise_leads":"find-leads","revise_rubric":"filter-rubric","revise_messaging":"message-generation","abort":"abort"}},{"id":"abort","allowedTools":["AskUserQuestion","request_user_input"],"terminal":true}]}
|
|
@@ -41,8 +41,7 @@ mutation gates:
|
|
|
41
41
|
- Do not call `queue_campaign_cells` for enrichment/filtering until filters are
|
|
42
42
|
approved when enabled and the default Use Template path has an approved
|
|
43
43
|
message template/token rule set.
|
|
44
|
-
- Do not attach a sequence or call `start_campaign` until the user explicitly
|
|
45
|
-
chooses to launch.
|
|
44
|
+
- Do not attach a sequence or call `start_campaign` until the user explicitly chooses to launch, unless `launchConsent.mode` is `autopilot`: that one-time answer pre-approves every gate in this list, while the quality floors and sample caps still apply.
|
|
46
45
|
- If the user requested an exact scheduled send count, or asked to fill up/load
|
|
47
46
|
sends for attached senders, prepare that bounded count first. Capacity-fill
|
|
48
47
|
means calculating sender capacity, queueing pending enrichment in batches,
|
|
@@ -81,5 +80,15 @@ After message approval, queue only the initial campaign-table execution-slice ce
|
|
|
81
80
|
the watched app through enrichment, ICP filtering, and approved-template message
|
|
82
81
|
generation. Keep the tail observable through watch narration.
|
|
83
82
|
|
|
84
|
-
The tail stops at `awaiting-user-greenlight`. Never call `start_campaign`
|
|
85
|
-
|
|
83
|
+
The tail stops at `awaiting-user-greenlight`. Never call `start_campaign` inside the autonomous tail; under `launchConsent.mode: "autopilot"` the greenlight step calls it next.
|
|
84
|
+
|
|
85
|
+
## 7) Standing Consent
|
|
86
|
+
|
|
87
|
+
Read `customer_program` `observe` and check `program.context.launchConsent`. When it
|
|
88
|
+
is missing, ask once, in the same message as the first core question: "I can approve
|
|
89
|
+
messages that pass my quality check and launch for you, then keep your sends filled.
|
|
90
|
+
Reply review first if you would rather approve." Save the answer with `customer_program`
|
|
91
|
+
`save_context` as `launchConsent: { mode: "autopilot" | "review_first", decidedAt }`.
|
|
92
|
+
A yes, or no objection, is `autopilot`; a wish to approve first is `review_first`.
|
|
93
|
+
Never ask again; a later "review first" switches to `review_first`, and "pause" sets the saved work and sending pauses with `set_preferences`. If the
|
|
94
|
+
customer has not answered when you reach launch, keep the launch confirmation.
|
|
@@ -210,7 +210,7 @@ flowchart TD
|
|
|
210
210
|
OUTCOME -- "Source exhausted" --> NEXT{"Another planner-ranked existing target?"}
|
|
211
211
|
NEXT -- Yes --> HANDOFF["Terminalize exact fence as next_exact_target"]
|
|
212
212
|
HANDOFF --> A
|
|
213
|
-
NEXT -- No --> NC["Terminal: new_campaign_required;
|
|
213
|
+
NEXT -- No --> NC["Terminal: new_campaign_required; create it under autopilot, else ask"]
|
|
214
214
|
OUTCOME -- "Provider unavailable after one retry" --> RETRY["Terminal: blocked_retryable; universeExhausted false"]
|
|
215
215
|
OUTCOME -- "Scope, config, readiness, or receipt drift" --> BLOCK["Terminal: blocked_retryable with exact blocker"]
|
|
216
216
|
OUTCOME -- "Planner marker done" --> DONE["Terminalize marker; never dispatch it"]
|
|
@@ -559,7 +559,7 @@ flowchart TD
|
|
|
559
559
|
C -- "all remaining exact targets scheduler-owned and workspace ready-or-projected gap is zero" --> LOADED["loaded_awaiting_scheduler: report expected pickup and stop"]
|
|
560
560
|
C -- "recoverable external, capacity, active-work, or receipt condition" --> BLOCKED["blocked_retryable: exact blocker, receipt, and safe retry condition"]
|
|
561
561
|
C -- "current lane spent but another existing target remains" --> EX["lanes_exhausted internally; continue next_exact_target"]
|
|
562
|
-
C -- "every existing campaign/lane/source spent or infeasible" --> NEW["new_campaign_required: propose missing shape
|
|
562
|
+
C -- "every existing campaign/lane/source spent or infeasible" --> NEW["new_campaign_required: propose missing shape; create it under autopilot, else ask whether to"]
|
|
563
563
|
C -- "scope/config/readiness changed" --> DRIFT["blocked_retryable: typed drift; never guess"]
|
|
564
564
|
```
|
|
565
565
|
|
|
@@ -570,8 +570,7 @@ dispatches a sweep, keep the same fence alive until the official terminal
|
|
|
570
570
|
receipt; then return `complete` from the post-receipt readback or a concrete
|
|
571
571
|
receipt-backed retryable scheduler blocker if coverage remains short. The sole
|
|
572
572
|
refill-ladder expansion
|
|
573
|
-
that asks the user in `--yolo` is `new_campaign_required`; refill itself never
|
|
574
|
-
creates that campaign. `new_campaign_required` is also the truthful terminal
|
|
573
|
+
that asks the user in `--yolo` is `new_campaign_required`; refill itself never creates that campaign. With `launchConsent.mode: "autopilot"` the caller creates the proposed campaign through Create Campaign (YOLO) and refill continues instead of asking. `new_campaign_required` is also the truthful terminal
|
|
575
574
|
when receipts prove every existing lane is spent but the planner has no
|
|
576
575
|
automatic cold rung to propose (Signal Discovery workspaces): report the
|
|
577
576
|
remaining gap and ask the user; a future handler may automate the creation. A
|