@signaliz/sdk 1.0.11 → 1.0.13

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/index.js CHANGED
@@ -38,6 +38,7 @@ __export(index_exports, {
38
38
  createCampaignBuilderAgentPlan: () => createCampaignBuilderAgentPlan,
39
39
  createCampaignBuilderAgentRequestTemplate: () => createCampaignBuilderAgentRequestTemplate,
40
40
  createCampaignBuilderApproval: () => createCampaignBuilderApproval,
41
+ createCampaignBuilderReadiness: () => createCampaignBuilderReadiness,
41
42
  createCampaignBuilderToolRoute: () => createCampaignBuilderToolRoute,
42
43
  getCampaignBuilderOperatingPlaybook: () => getCampaignBuilderOperatingPlaybook,
43
44
  getCampaignBuilderOperatingPlaybooksForRequest: () => getCampaignBuilderOperatingPlaybooksForRequest,
@@ -1240,6 +1241,13 @@ var DEFAULT_CAMPAIGN_BUILDER_BUILT_INS = [
1240
1241
  "email_verification",
1241
1242
  "signals"
1242
1243
  ];
1244
+ var CAMPAIGN_BUILDER_BUILT_IN_ROUTE_IDS = {
1245
+ lead_generation: "signaliz-lead-generation",
1246
+ local_leads: "signaliz-local-leads",
1247
+ email_finding: "signaliz-email-finding",
1248
+ email_verification: "signaliz-email-verification",
1249
+ signals: "signaliz-signals"
1250
+ };
1243
1251
  var DEFAULT_CAMPAIGN_BUILDER_APPROVAL_TYPES = [
1244
1252
  "memory_retrieval",
1245
1253
  "spend",
@@ -1736,6 +1744,10 @@ var CampaignBuilderAgent = class {
1736
1744
  }
1737
1745
  return plan;
1738
1746
  }
1747
+ async readiness(request, options = {}) {
1748
+ const plan = await this.createPlan(request, options);
1749
+ return createCampaignBuilderReadiness(plan);
1750
+ }
1739
1751
  async commitPlan(plan, options = {}) {
1740
1752
  const writeMode = options.confirm === true && options.dryRun === false;
1741
1753
  if (writeMode && !options.approvedBy) {
@@ -1923,6 +1935,15 @@ var CampaignBuilderAgent = class {
1923
1935
  async approveDelivery(campaignBuildId, destinationType, options) {
1924
1936
  return this.approveCampaignDelivery(campaignBuildId, destinationType, options);
1925
1937
  }
1938
+ async reviewBuild(campaignBuildId, options = {}) {
1939
+ const status = await this.getBuildStatus(campaignBuildId);
1940
+ const rows = await this.getBuildRows(campaignBuildId, {
1941
+ ...options,
1942
+ limit: options.limit ?? 100
1943
+ });
1944
+ const artifacts = await this.listBuildArtifacts(campaignBuildId);
1945
+ return createCampaignBuilderBuildReview(status, rows, artifacts);
1946
+ }
1926
1947
  async getCampaignBuildStatus(campaignBuildId) {
1927
1948
  const data = await this.callMcp("get_campaign_build_status", {
1928
1949
  campaign_build_id: campaignBuildId
@@ -2087,6 +2108,91 @@ function createCampaignBuilderAgentPlan(request, context = {}) {
2087
2108
  warnings: context.warnings ?? []
2088
2109
  };
2089
2110
  }
2111
+ function createCampaignBuilderReadiness(plan) {
2112
+ const lanes = plan.routes.map((route) => createCampaignBuilderReadinessLane(route, plan));
2113
+ const builtInLanes = lanes.filter((lane) => lane.builtIn);
2114
+ const customRoutes = lanes.filter((lane) => lane.customRoute);
2115
+ const strategyMemory = asRecord(plan.strategyMemoryStatus);
2116
+ const deliveryRisk = asRecord(plan.buildRequest.deliveryRisk);
2117
+ const deliveryRiskBlockers = arrayOfStrings(deliveryRisk.blockers) ?? [];
2118
+ const deliveryRiskValue = asRecord(deliveryRisk.risk);
2119
+ const deliveryRiskLevel = stringValue(deliveryRiskValue.risk_level ?? deliveryRiskValue.riskLevel);
2120
+ const memoryReady = !plan.strategyMemoryStatus || strategyMemory.ready !== false;
2121
+ const kernelPlanReady = Object.keys(asRecord(plan.kernelPlan)).length > 0;
2122
+ const deliveryRiskReady = !plan.buildRequest.deliveryRisk || deliveryRiskBlockers.length === 0 && !["high", "critical", "blocked"].includes(String(deliveryRiskLevel ?? "").toLowerCase());
2123
+ const builtInsReady = builtInLanes.length > 0 && builtInLanes.every((lane) => lane.ready);
2124
+ const customRoutesReady = customRoutes.every((lane) => lane.ready);
2125
+ const approvalGatesReady = plan.approvals.length > 0;
2126
+ const gates = [
2127
+ {
2128
+ id: "campaign_plan_created",
2129
+ label: "Campaign plan created",
2130
+ passed: Boolean(plan.planId && plan.mcpFlow.length > 0),
2131
+ detail: plan.planId ? `Plan ${plan.planId} has ${plan.mcpFlow.length} MCP step(s).` : "No campaign-builder plan was created."
2132
+ },
2133
+ {
2134
+ id: "built_in_lanes_ready",
2135
+ label: "Built-in lanes ready",
2136
+ passed: builtInsReady,
2137
+ detail: `${builtInLanes.filter((lane) => lane.ready).length}/${builtInLanes.length} Signaliz built-in lane(s) are present.`
2138
+ },
2139
+ {
2140
+ id: "strategy_memory_ready",
2141
+ label: "Strategy memory ready",
2142
+ passed: memoryReady,
2143
+ detail: plan.strategyMemoryStatus ? "Strategy memory readiness was checked." : "Strategy memory readiness was not requested."
2144
+ },
2145
+ {
2146
+ id: "kernel_plan_attached",
2147
+ label: "Kernel plan attached",
2148
+ passed: kernelPlanReady,
2149
+ detail: kernelPlanReady ? "Kernel planner evidence is attached." : "Kernel planner evidence is not attached."
2150
+ },
2151
+ {
2152
+ id: "delivery_risk_clear",
2153
+ label: "Delivery risk clear",
2154
+ passed: deliveryRiskReady,
2155
+ detail: plan.buildRequest.deliveryRisk ? `Delivery risk is ${deliveryRiskLevel || "available"} with ${deliveryRiskBlockers.length} blocker(s).` : "Delivery risk preflight was not requested."
2156
+ },
2157
+ {
2158
+ id: "custom_routes_prepared",
2159
+ label: "Custom routes prepared",
2160
+ passed: customRoutesReady,
2161
+ detail: customRoutes.length === 0 ? "No BYO routes were requested." : `${customRoutes.filter((lane) => lane.ready).length}/${customRoutes.length} BYO route(s) have read-only setup steps.`
2162
+ },
2163
+ {
2164
+ id: "approval_gates_present",
2165
+ label: "Approval gates present",
2166
+ passed: approvalGatesReady,
2167
+ detail: approvalGatesReady ? `${plan.approvals.length} approval gate(s) are attached.` : "No approval gates are attached."
2168
+ }
2169
+ ];
2170
+ const blockers = [
2171
+ ...gates.filter((gate) => !gate.passed).map((gate) => gate.detail),
2172
+ ...lanes.flatMap((lane) => lane.blockers)
2173
+ ];
2174
+ const score = gates.length === 0 ? 0 : Math.round(gates.filter((gate) => gate.passed).length / gates.length * 100);
2175
+ const ready = blockers.length === 0;
2176
+ return {
2177
+ plan,
2178
+ lanes,
2179
+ gates,
2180
+ summary: {
2181
+ ready,
2182
+ score,
2183
+ targetCount: plan.targetCount,
2184
+ estimatedCredits: plan.estimatedCredits,
2185
+ builtInLanesReady: builtInLanes.filter((lane) => lane.ready).length,
2186
+ builtInLanesTotal: builtInLanes.length,
2187
+ customRoutesReady: customRoutes.filter((lane) => lane.ready).length,
2188
+ customRoutesTotal: customRoutes.length,
2189
+ approvalGateCount: plan.approvals.length,
2190
+ blockers,
2191
+ warnings: plan.warnings
2192
+ },
2193
+ nextActions: campaignReadinessNextActions(plan, ready, customRoutes.length)
2194
+ };
2195
+ }
2090
2196
  function getMissingCampaignBuilderApprovals(plan, approval) {
2091
2197
  if (approval.planId !== plan.planId) {
2092
2198
  return plan.approvals;
@@ -3350,6 +3456,185 @@ function mapAgentCampaignArtifact(data) {
3350
3456
  createdAt: stringValue(data.created_at) ?? ""
3351
3457
  };
3352
3458
  }
3459
+ function createCampaignBuilderBuildReview(status, rows, artifacts) {
3460
+ const sampledRows = rows.rows.length;
3461
+ const availableRows = rows.count || sampledRows;
3462
+ const qualifiedRows = rows.rows.filter((row) => row.qualified !== false).length;
3463
+ const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
3464
+ const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
3465
+ const artifactCount = artifacts.length || status.artifactCount || 0;
3466
+ const gates = [
3467
+ {
3468
+ id: "build_completed",
3469
+ label: "Build completed",
3470
+ passed: status.status === "completed",
3471
+ detail: status.status === "completed" ? "The campaign build is completed." : `The campaign build is ${status.status}; review again after completion.`
3472
+ },
3473
+ {
3474
+ id: "rows_available",
3475
+ label: "Rows available",
3476
+ passed: sampledRows > 0,
3477
+ detail: sampledRows > 0 ? `${sampledRows} sampled row(s) are available for review.` : "No rows were returned for review."
3478
+ },
3479
+ {
3480
+ id: "qualified_sample",
3481
+ label: "Sample rows qualified",
3482
+ passed: sampledRows > 0 && qualifiedRows === sampledRows,
3483
+ detail: sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
3484
+ },
3485
+ {
3486
+ id: "email_coverage",
3487
+ label: "Email coverage",
3488
+ passed: sampledRows > 0 && rowsWithEmail === sampledRows,
3489
+ detail: sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
3490
+ },
3491
+ {
3492
+ id: "artifact_available",
3493
+ label: "Artifact available",
3494
+ passed: artifactCount > 0,
3495
+ detail: artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
3496
+ },
3497
+ {
3498
+ id: "no_failed_rows",
3499
+ label: "No failed rows",
3500
+ passed: status.recordsFailed === 0,
3501
+ detail: status.recordsFailed === 0 ? "The build reports zero failed rows." : `${status.recordsFailed} row(s) failed during the build.`
3502
+ }
3503
+ ];
3504
+ const blockedReasons = gates.filter((gate) => !gate.passed).map((gate) => gate.detail);
3505
+ const warnings = [
3506
+ rows.hasMore ? "This review is based on a row sample; page through remaining rows before final approval." : void 0,
3507
+ sampledRows > 0 && copyReadyRows < sampledRows ? `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.` : void 0,
3508
+ ...status.warnings
3509
+ ].filter((item) => Boolean(item));
3510
+ const readyForDeliveryApproval = blockedReasons.length === 0;
3511
+ return {
3512
+ campaignBuildId: status.campaignBuildId || rows.campaignBuildId,
3513
+ status,
3514
+ rows,
3515
+ artifacts,
3516
+ gates,
3517
+ summary: {
3518
+ status: status.status,
3519
+ phase: status.currentPhase,
3520
+ sampledRows,
3521
+ availableRows,
3522
+ qualifiedRows,
3523
+ rowsWithEmail,
3524
+ copyReadyRows,
3525
+ artifactCount,
3526
+ readyForDeliveryApproval,
3527
+ blockedReasons,
3528
+ warnings
3529
+ },
3530
+ nextActions: campaignReviewNextActions(status, readyForDeliveryApproval)
3531
+ };
3532
+ }
3533
+ function campaignReviewNextActions(status, readyForDeliveryApproval) {
3534
+ const buildId = status.campaignBuildId;
3535
+ if (!buildId) return [];
3536
+ if (status.status === "completed" && readyForDeliveryApproval) {
3537
+ return [
3538
+ `signaliz campaign-agent approve ${buildId} --destination-type json`,
3539
+ `signaliz campaign-agent artifacts ${buildId}`
3540
+ ];
3541
+ }
3542
+ if (status.status === "completed") {
3543
+ return [
3544
+ `signaliz campaign-agent rows ${buildId} --limit 100`,
3545
+ `signaliz campaign-agent artifacts ${buildId}`
3546
+ ];
3547
+ }
3548
+ if (status.status === "pending_approval") {
3549
+ return [
3550
+ `signaliz campaign-agent rows ${buildId} --limit 100`,
3551
+ `signaliz campaign-agent approve ${buildId} --destination-type json`
3552
+ ];
3553
+ }
3554
+ if ((status.status === "queued" || status.status === "running") && status.triggerRunId) {
3555
+ return [`signaliz ops run-status ${status.triggerRunId} --watch`];
3556
+ }
3557
+ return [`signaliz campaign-agent status ${buildId}`];
3558
+ }
3559
+ function campaignReviewRowHasEmail(row) {
3560
+ const data = asRecord(row.data);
3561
+ return Boolean(
3562
+ stringValue(data.email) || stringValue(data.work_email) || stringValue(data.workEmail)
3563
+ );
3564
+ }
3565
+ function campaignReviewRowHasCopy(row) {
3566
+ const data = asRecord(row.data);
3567
+ const copy = asRecord(data.copy);
3568
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
3569
+ return row.status === "copy_ready" || data.copy_ready === true || Boolean(
3570
+ stringValue(data.subject) || stringValue(data.subject_line) || stringValue(data.subjectLine) || stringValue(data.opener) || stringValue(data.body) || stringValue(data.cta) || stringValue(copy.subject) || stringValue(copy.body) || stringValue(rawCopy.subject) || stringValue(rawCopy.body)
3571
+ );
3572
+ }
3573
+ function createCampaignBuilderReadinessLane(route, plan) {
3574
+ const builtIn = campaignBuilderBuiltInForRoute(route);
3575
+ const customRoute = route.provider !== "signaliz" && route.provider !== "csv" && builtIn === void 0;
3576
+ const prepareStep = plan.mcpFlow.find(
3577
+ (step) => step.tool === "gtm_provider_recipe_prepare" && step.arguments.provider_id === route.provider
3578
+ );
3579
+ const activateStep = plan.mcpFlow.find(
3580
+ (step) => step.tool === "gtm_provider_route_activate" && step.arguments.provider_id === route.provider
3581
+ );
3582
+ const readOnlySetup = !customRoute || prepareStep?.readOnly === true && activateStep?.readOnly === true && activateStep.arguments.dry_run === true && activateStep.arguments.confirm === false;
3583
+ const blockers = [
3584
+ customRoute && !prepareStep ? `${route.label ?? route.provider} is missing provider recipe setup.` : void 0,
3585
+ customRoute && !activateStep ? `${route.label ?? route.provider} is missing provider route dry-run setup.` : void 0,
3586
+ customRoute && !readOnlySetup ? `${route.label ?? route.provider} route setup must stay read-only before approval.` : void 0,
3587
+ route.mode === "required" && !route.toolName && customRoute ? `${route.label ?? route.provider} is required but has no tool name.` : void 0
3588
+ ].filter((item) => Boolean(item));
3589
+ return {
3590
+ id: route.id ?? `${route.provider}-${route.layer}`,
3591
+ label: route.label ?? readinessLaneLabel(route, builtIn),
3592
+ layer: route.layer,
3593
+ gtmLayers: gtmLayersForRoute(route),
3594
+ provider: route.provider,
3595
+ toolName: route.toolName,
3596
+ mode: route.mode ?? "if_available",
3597
+ builtIn,
3598
+ approvalRequired: route.approvalRequired === true || plan.approvals.some((approval) => approval.routeId === route.id),
3599
+ customRoute,
3600
+ readOnlySetup,
3601
+ ready: blockers.length === 0,
3602
+ blockers,
3603
+ nextAction: customRoute ? `Review ${route.provider} route setup before launch approval.` : void 0
3604
+ };
3605
+ }
3606
+ function campaignBuilderBuiltInForRoute(route) {
3607
+ return Object.entries(CAMPAIGN_BUILDER_BUILT_IN_ROUTE_IDS).find(([, routeId]) => route.id === routeId)?.[0];
3608
+ }
3609
+ function readinessLaneLabel(route, builtIn) {
3610
+ if (builtIn) {
3611
+ const labels = {
3612
+ lead_generation: "Signaliz lead generation",
3613
+ local_leads: "Signaliz local leads",
3614
+ email_finding: "Signaliz email finding",
3615
+ email_verification: "Signaliz email verification",
3616
+ signals: "Signaliz signals"
3617
+ };
3618
+ return labels[builtIn];
3619
+ }
3620
+ return `${route.provider} ${route.layer}`;
3621
+ }
3622
+ function campaignReadinessNextActions(plan, ready, customRouteCount) {
3623
+ const requestHint = "--request-file campaign-request.json";
3624
+ if (ready) {
3625
+ return [
3626
+ `signaliz campaign-agent proof ${requestHint} --json`,
3627
+ `signaliz campaign-agent commit-plan ${requestHint} --json`,
3628
+ `signaliz campaign-agent build-approved ${requestHint} --confirm-launch --approve-all --approved-by operator@example.com --spend-limit ${plan.estimatedCredits}`
3629
+ ];
3630
+ }
3631
+ const actions = [
3632
+ plan.strategyMemoryStatus && asRecord(plan.strategyMemoryStatus).ready === false ? "signaliz gtm strategy-memory --strategy-template <strategy_template> --include-samples --json" : void 0,
3633
+ customRouteCount > 0 ? "Review BYO provider recipe and route dry-run steps in the readiness packet." : void 0,
3634
+ `signaliz campaign-agent plan ${requestHint} --json`
3635
+ ];
3636
+ return actions.filter((item) => Boolean(item));
3637
+ }
3353
3638
  function diagnosticMessages2(value) {
3354
3639
  if (!Array.isArray(value)) return [];
3355
3640
  return value.map((item) => {
@@ -6369,6 +6654,7 @@ var Signaliz = class {
6369
6654
  createCampaignBuilderAgentPlan,
6370
6655
  createCampaignBuilderAgentRequestTemplate,
6371
6656
  createCampaignBuilderApproval,
6657
+ createCampaignBuilderReadiness,
6372
6658
  createCampaignBuilderToolRoute,
6373
6659
  getCampaignBuilderOperatingPlaybook,
6374
6660
  getCampaignBuilderOperatingPlaybooksForRequest,
package/dist/index.mjs CHANGED
@@ -17,13 +17,14 @@ import {
17
17
  createCampaignBuilderAgentPlan,
18
18
  createCampaignBuilderAgentRequestTemplate,
19
19
  createCampaignBuilderApproval,
20
+ createCampaignBuilderReadiness,
20
21
  createCampaignBuilderToolRoute,
21
22
  getCampaignBuilderOperatingPlaybook,
22
23
  getCampaignBuilderOperatingPlaybooksForRequest,
23
24
  getCampaignBuilderStrategyTemplate,
24
25
  getMissingCampaignBuilderApprovals,
25
26
  normalizeExecutionReference
26
- } from "./chunk-QQW6DZQ4.mjs";
27
+ } from "./chunk-DJVOGXVD.mjs";
27
28
  export {
28
29
  Ai,
29
30
  CAMPAIGN_BUILDER_OPERATING_PLAYBOOKS,
@@ -43,6 +44,7 @@ export {
43
44
  createCampaignBuilderAgentPlan,
44
45
  createCampaignBuilderAgentRequestTemplate,
45
46
  createCampaignBuilderApproval,
47
+ createCampaignBuilderReadiness,
46
48
  createCampaignBuilderToolRoute,
47
49
  getCampaignBuilderOperatingPlaybook,
48
50
  getCampaignBuilderOperatingPlaybooksForRequest,
@@ -1222,6 +1222,13 @@ var DEFAULT_CAMPAIGN_BUILDER_BUILT_INS = [
1222
1222
  "email_verification",
1223
1223
  "signals"
1224
1224
  ];
1225
+ var CAMPAIGN_BUILDER_BUILT_IN_ROUTE_IDS = {
1226
+ lead_generation: "signaliz-lead-generation",
1227
+ local_leads: "signaliz-local-leads",
1228
+ email_finding: "signaliz-email-finding",
1229
+ email_verification: "signaliz-email-verification",
1230
+ signals: "signaliz-signals"
1231
+ };
1225
1232
  var CAMPAIGN_BUILDER_OPERATING_PLAYBOOKS = [
1226
1233
  {
1227
1234
  slug: "cache-first-large-list",
@@ -1710,6 +1717,10 @@ var CampaignBuilderAgent = class {
1710
1717
  }
1711
1718
  return plan;
1712
1719
  }
1720
+ async readiness(request, options = {}) {
1721
+ const plan = await this.createPlan(request, options);
1722
+ return createCampaignBuilderReadiness(plan);
1723
+ }
1713
1724
  async commitPlan(plan, options = {}) {
1714
1725
  const writeMode = options.confirm === true && options.dryRun === false;
1715
1726
  if (writeMode && !options.approvedBy) {
@@ -1897,6 +1908,15 @@ var CampaignBuilderAgent = class {
1897
1908
  async approveDelivery(campaignBuildId, destinationType, options) {
1898
1909
  return this.approveCampaignDelivery(campaignBuildId, destinationType, options);
1899
1910
  }
1911
+ async reviewBuild(campaignBuildId, options = {}) {
1912
+ const status = await this.getBuildStatus(campaignBuildId);
1913
+ const rows = await this.getBuildRows(campaignBuildId, {
1914
+ ...options,
1915
+ limit: options.limit ?? 100
1916
+ });
1917
+ const artifacts = await this.listBuildArtifacts(campaignBuildId);
1918
+ return createCampaignBuilderBuildReview(status, rows, artifacts);
1919
+ }
1900
1920
  async getCampaignBuildStatus(campaignBuildId) {
1901
1921
  const data = await this.callMcp("get_campaign_build_status", {
1902
1922
  campaign_build_id: campaignBuildId
@@ -2061,6 +2081,91 @@ function createCampaignBuilderAgentPlan(request, context = {}) {
2061
2081
  warnings: context.warnings ?? []
2062
2082
  };
2063
2083
  }
2084
+ function createCampaignBuilderReadiness(plan) {
2085
+ const lanes = plan.routes.map((route) => createCampaignBuilderReadinessLane(route, plan));
2086
+ const builtInLanes = lanes.filter((lane) => lane.builtIn);
2087
+ const customRoutes = lanes.filter((lane) => lane.customRoute);
2088
+ const strategyMemory = asRecord(plan.strategyMemoryStatus);
2089
+ const deliveryRisk = asRecord(plan.buildRequest.deliveryRisk);
2090
+ const deliveryRiskBlockers = arrayOfStrings(deliveryRisk.blockers) ?? [];
2091
+ const deliveryRiskValue = asRecord(deliveryRisk.risk);
2092
+ const deliveryRiskLevel = stringValue(deliveryRiskValue.risk_level ?? deliveryRiskValue.riskLevel);
2093
+ const memoryReady = !plan.strategyMemoryStatus || strategyMemory.ready !== false;
2094
+ const kernelPlanReady = Object.keys(asRecord(plan.kernelPlan)).length > 0;
2095
+ const deliveryRiskReady = !plan.buildRequest.deliveryRisk || deliveryRiskBlockers.length === 0 && !["high", "critical", "blocked"].includes(String(deliveryRiskLevel ?? "").toLowerCase());
2096
+ const builtInsReady = builtInLanes.length > 0 && builtInLanes.every((lane) => lane.ready);
2097
+ const customRoutesReady = customRoutes.every((lane) => lane.ready);
2098
+ const approvalGatesReady = plan.approvals.length > 0;
2099
+ const gates = [
2100
+ {
2101
+ id: "campaign_plan_created",
2102
+ label: "Campaign plan created",
2103
+ passed: Boolean(plan.planId && plan.mcpFlow.length > 0),
2104
+ detail: plan.planId ? `Plan ${plan.planId} has ${plan.mcpFlow.length} MCP step(s).` : "No campaign-builder plan was created."
2105
+ },
2106
+ {
2107
+ id: "built_in_lanes_ready",
2108
+ label: "Built-in lanes ready",
2109
+ passed: builtInsReady,
2110
+ detail: `${builtInLanes.filter((lane) => lane.ready).length}/${builtInLanes.length} Signaliz built-in lane(s) are present.`
2111
+ },
2112
+ {
2113
+ id: "strategy_memory_ready",
2114
+ label: "Strategy memory ready",
2115
+ passed: memoryReady,
2116
+ detail: plan.strategyMemoryStatus ? "Strategy memory readiness was checked." : "Strategy memory readiness was not requested."
2117
+ },
2118
+ {
2119
+ id: "kernel_plan_attached",
2120
+ label: "Kernel plan attached",
2121
+ passed: kernelPlanReady,
2122
+ detail: kernelPlanReady ? "Kernel planner evidence is attached." : "Kernel planner evidence is not attached."
2123
+ },
2124
+ {
2125
+ id: "delivery_risk_clear",
2126
+ label: "Delivery risk clear",
2127
+ passed: deliveryRiskReady,
2128
+ detail: plan.buildRequest.deliveryRisk ? `Delivery risk is ${deliveryRiskLevel || "available"} with ${deliveryRiskBlockers.length} blocker(s).` : "Delivery risk preflight was not requested."
2129
+ },
2130
+ {
2131
+ id: "custom_routes_prepared",
2132
+ label: "Custom routes prepared",
2133
+ passed: customRoutesReady,
2134
+ detail: customRoutes.length === 0 ? "No BYO routes were requested." : `${customRoutes.filter((lane) => lane.ready).length}/${customRoutes.length} BYO route(s) have read-only setup steps.`
2135
+ },
2136
+ {
2137
+ id: "approval_gates_present",
2138
+ label: "Approval gates present",
2139
+ passed: approvalGatesReady,
2140
+ detail: approvalGatesReady ? `${plan.approvals.length} approval gate(s) are attached.` : "No approval gates are attached."
2141
+ }
2142
+ ];
2143
+ const blockers = [
2144
+ ...gates.filter((gate) => !gate.passed).map((gate) => gate.detail),
2145
+ ...lanes.flatMap((lane) => lane.blockers)
2146
+ ];
2147
+ const score = gates.length === 0 ? 0 : Math.round(gates.filter((gate) => gate.passed).length / gates.length * 100);
2148
+ const ready = blockers.length === 0;
2149
+ return {
2150
+ plan,
2151
+ lanes,
2152
+ gates,
2153
+ summary: {
2154
+ ready,
2155
+ score,
2156
+ targetCount: plan.targetCount,
2157
+ estimatedCredits: plan.estimatedCredits,
2158
+ builtInLanesReady: builtInLanes.filter((lane) => lane.ready).length,
2159
+ builtInLanesTotal: builtInLanes.length,
2160
+ customRoutesReady: customRoutes.filter((lane) => lane.ready).length,
2161
+ customRoutesTotal: customRoutes.length,
2162
+ approvalGateCount: plan.approvals.length,
2163
+ blockers,
2164
+ warnings: plan.warnings
2165
+ },
2166
+ nextActions: campaignReadinessNextActions(plan, ready, customRoutes.length)
2167
+ };
2168
+ }
2064
2169
  function getMissingCampaignBuilderApprovals(plan, approval) {
2065
2170
  if (approval.planId !== plan.planId) {
2066
2171
  return plan.approvals;
@@ -3223,6 +3328,185 @@ function mapAgentCampaignArtifact(data) {
3223
3328
  createdAt: stringValue(data.created_at) ?? ""
3224
3329
  };
3225
3330
  }
3331
+ function createCampaignBuilderBuildReview(status, rows, artifacts) {
3332
+ const sampledRows = rows.rows.length;
3333
+ const availableRows = rows.count || sampledRows;
3334
+ const qualifiedRows = rows.rows.filter((row) => row.qualified !== false).length;
3335
+ const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
3336
+ const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
3337
+ const artifactCount = artifacts.length || status.artifactCount || 0;
3338
+ const gates = [
3339
+ {
3340
+ id: "build_completed",
3341
+ label: "Build completed",
3342
+ passed: status.status === "completed",
3343
+ detail: status.status === "completed" ? "The campaign build is completed." : `The campaign build is ${status.status}; review again after completion.`
3344
+ },
3345
+ {
3346
+ id: "rows_available",
3347
+ label: "Rows available",
3348
+ passed: sampledRows > 0,
3349
+ detail: sampledRows > 0 ? `${sampledRows} sampled row(s) are available for review.` : "No rows were returned for review."
3350
+ },
3351
+ {
3352
+ id: "qualified_sample",
3353
+ label: "Sample rows qualified",
3354
+ passed: sampledRows > 0 && qualifiedRows === sampledRows,
3355
+ detail: sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
3356
+ },
3357
+ {
3358
+ id: "email_coverage",
3359
+ label: "Email coverage",
3360
+ passed: sampledRows > 0 && rowsWithEmail === sampledRows,
3361
+ detail: sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
3362
+ },
3363
+ {
3364
+ id: "artifact_available",
3365
+ label: "Artifact available",
3366
+ passed: artifactCount > 0,
3367
+ detail: artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
3368
+ },
3369
+ {
3370
+ id: "no_failed_rows",
3371
+ label: "No failed rows",
3372
+ passed: status.recordsFailed === 0,
3373
+ detail: status.recordsFailed === 0 ? "The build reports zero failed rows." : `${status.recordsFailed} row(s) failed during the build.`
3374
+ }
3375
+ ];
3376
+ const blockedReasons = gates.filter((gate) => !gate.passed).map((gate) => gate.detail);
3377
+ const warnings = [
3378
+ rows.hasMore ? "This review is based on a row sample; page through remaining rows before final approval." : void 0,
3379
+ sampledRows > 0 && copyReadyRows < sampledRows ? `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.` : void 0,
3380
+ ...status.warnings
3381
+ ].filter((item) => Boolean(item));
3382
+ const readyForDeliveryApproval = blockedReasons.length === 0;
3383
+ return {
3384
+ campaignBuildId: status.campaignBuildId || rows.campaignBuildId,
3385
+ status,
3386
+ rows,
3387
+ artifacts,
3388
+ gates,
3389
+ summary: {
3390
+ status: status.status,
3391
+ phase: status.currentPhase,
3392
+ sampledRows,
3393
+ availableRows,
3394
+ qualifiedRows,
3395
+ rowsWithEmail,
3396
+ copyReadyRows,
3397
+ artifactCount,
3398
+ readyForDeliveryApproval,
3399
+ blockedReasons,
3400
+ warnings
3401
+ },
3402
+ nextActions: campaignReviewNextActions(status, readyForDeliveryApproval)
3403
+ };
3404
+ }
3405
+ function campaignReviewNextActions(status, readyForDeliveryApproval) {
3406
+ const buildId = status.campaignBuildId;
3407
+ if (!buildId) return [];
3408
+ if (status.status === "completed" && readyForDeliveryApproval) {
3409
+ return [
3410
+ `signaliz campaign-agent approve ${buildId} --destination-type json`,
3411
+ `signaliz campaign-agent artifacts ${buildId}`
3412
+ ];
3413
+ }
3414
+ if (status.status === "completed") {
3415
+ return [
3416
+ `signaliz campaign-agent rows ${buildId} --limit 100`,
3417
+ `signaliz campaign-agent artifacts ${buildId}`
3418
+ ];
3419
+ }
3420
+ if (status.status === "pending_approval") {
3421
+ return [
3422
+ `signaliz campaign-agent rows ${buildId} --limit 100`,
3423
+ `signaliz campaign-agent approve ${buildId} --destination-type json`
3424
+ ];
3425
+ }
3426
+ if ((status.status === "queued" || status.status === "running") && status.triggerRunId) {
3427
+ return [`signaliz ops run-status ${status.triggerRunId} --watch`];
3428
+ }
3429
+ return [`signaliz campaign-agent status ${buildId}`];
3430
+ }
3431
+ function campaignReviewRowHasEmail(row) {
3432
+ const data = asRecord(row.data);
3433
+ return Boolean(
3434
+ stringValue(data.email) || stringValue(data.work_email) || stringValue(data.workEmail)
3435
+ );
3436
+ }
3437
+ function campaignReviewRowHasCopy(row) {
3438
+ const data = asRecord(row.data);
3439
+ const copy = asRecord(data.copy);
3440
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
3441
+ return row.status === "copy_ready" || data.copy_ready === true || Boolean(
3442
+ stringValue(data.subject) || stringValue(data.subject_line) || stringValue(data.subjectLine) || stringValue(data.opener) || stringValue(data.body) || stringValue(data.cta) || stringValue(copy.subject) || stringValue(copy.body) || stringValue(rawCopy.subject) || stringValue(rawCopy.body)
3443
+ );
3444
+ }
3445
+ function createCampaignBuilderReadinessLane(route, plan) {
3446
+ const builtIn = campaignBuilderBuiltInForRoute(route);
3447
+ const customRoute = route.provider !== "signaliz" && route.provider !== "csv" && builtIn === void 0;
3448
+ const prepareStep = plan.mcpFlow.find(
3449
+ (step) => step.tool === "gtm_provider_recipe_prepare" && step.arguments.provider_id === route.provider
3450
+ );
3451
+ const activateStep = plan.mcpFlow.find(
3452
+ (step) => step.tool === "gtm_provider_route_activate" && step.arguments.provider_id === route.provider
3453
+ );
3454
+ const readOnlySetup = !customRoute || prepareStep?.readOnly === true && activateStep?.readOnly === true && activateStep.arguments.dry_run === true && activateStep.arguments.confirm === false;
3455
+ const blockers = [
3456
+ customRoute && !prepareStep ? `${route.label ?? route.provider} is missing provider recipe setup.` : void 0,
3457
+ customRoute && !activateStep ? `${route.label ?? route.provider} is missing provider route dry-run setup.` : void 0,
3458
+ customRoute && !readOnlySetup ? `${route.label ?? route.provider} route setup must stay read-only before approval.` : void 0,
3459
+ route.mode === "required" && !route.toolName && customRoute ? `${route.label ?? route.provider} is required but has no tool name.` : void 0
3460
+ ].filter((item) => Boolean(item));
3461
+ return {
3462
+ id: route.id ?? `${route.provider}-${route.layer}`,
3463
+ label: route.label ?? readinessLaneLabel(route, builtIn),
3464
+ layer: route.layer,
3465
+ gtmLayers: gtmLayersForRoute(route),
3466
+ provider: route.provider,
3467
+ toolName: route.toolName,
3468
+ mode: route.mode ?? "if_available",
3469
+ builtIn,
3470
+ approvalRequired: route.approvalRequired === true || plan.approvals.some((approval) => approval.routeId === route.id),
3471
+ customRoute,
3472
+ readOnlySetup,
3473
+ ready: blockers.length === 0,
3474
+ blockers,
3475
+ nextAction: customRoute ? `Review ${route.provider} route setup before launch approval.` : void 0
3476
+ };
3477
+ }
3478
+ function campaignBuilderBuiltInForRoute(route) {
3479
+ return Object.entries(CAMPAIGN_BUILDER_BUILT_IN_ROUTE_IDS).find(([, routeId]) => route.id === routeId)?.[0];
3480
+ }
3481
+ function readinessLaneLabel(route, builtIn) {
3482
+ if (builtIn) {
3483
+ const labels = {
3484
+ lead_generation: "Signaliz lead generation",
3485
+ local_leads: "Signaliz local leads",
3486
+ email_finding: "Signaliz email finding",
3487
+ email_verification: "Signaliz email verification",
3488
+ signals: "Signaliz signals"
3489
+ };
3490
+ return labels[builtIn];
3491
+ }
3492
+ return `${route.provider} ${route.layer}`;
3493
+ }
3494
+ function campaignReadinessNextActions(plan, ready, customRouteCount) {
3495
+ const requestHint = "--request-file campaign-request.json";
3496
+ if (ready) {
3497
+ return [
3498
+ `signaliz campaign-agent proof ${requestHint} --json`,
3499
+ `signaliz campaign-agent commit-plan ${requestHint} --json`,
3500
+ `signaliz campaign-agent build-approved ${requestHint} --confirm-launch --approve-all --approved-by operator@example.com --spend-limit ${plan.estimatedCredits}`
3501
+ ];
3502
+ }
3503
+ const actions = [
3504
+ plan.strategyMemoryStatus && asRecord(plan.strategyMemoryStatus).ready === false ? "signaliz gtm strategy-memory --strategy-template <strategy_template> --include-samples --json" : void 0,
3505
+ customRouteCount > 0 ? "Review BYO provider recipe and route dry-run steps in the readiness packet." : void 0,
3506
+ `signaliz campaign-agent plan ${requestHint} --json`
3507
+ ];
3508
+ return actions.filter((item) => Boolean(item));
3509
+ }
3226
3510
  function diagnosticMessages2(value) {
3227
3511
  if (!Array.isArray(value)) return [];
3228
3512
  return value.map((item) => {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Signaliz
4
- } from "./chunk-QQW6DZQ4.mjs";
4
+ } from "./chunk-DJVOGXVD.mjs";
5
5
 
6
6
  // src/mcp-config.ts
7
7
  import * as fs from "fs";