@signaliz/sdk 1.0.12 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,8 +93,22 @@ const plan = await signaliz.campaignBuilderAgent.createPlan({
93
93
  });
94
94
 
95
95
  const dryRun = await signaliz.campaignBuilderAgent.dryRunPlan(plan);
96
+ const readiness = await signaliz.campaignBuilderAgent.readiness({
97
+ goal: 'Build a strategy-template campaign for mature local operators',
98
+ strategyTemplate: 'non-medical-home-care',
99
+ targetCount: 500,
100
+ builtIns: ['lead_generation', 'local_leads', 'email_finding', 'email_verification', 'signals'],
101
+ });
102
+ const proof = await signaliz.campaignBuilderAgent.proof({
103
+ goal: 'Build a strategy-template campaign for mature local operators',
104
+ strategyTemplate: 'non-medical-home-care',
105
+ targetCount: 25,
106
+ builtIns: ['lead_generation', 'local_leads', 'email_finding', 'email_verification', 'signals'],
107
+ });
96
108
 
97
109
  console.log(plan.strategyMemoryStatus?.ready);
110
+ console.log(readiness.summary.ready);
111
+ console.log(proof.success);
98
112
  console.log(plan.mcpFlow.filter((step) =>
99
113
  ['gtm_provider_recipe_prepare', 'gtm_provider_route_activate'].includes(step.tool)
100
114
  ));
@@ -128,6 +142,15 @@ npx @signaliz/sdk campaign-agent plan \
128
142
  --target-count 250 \
129
143
  --json
130
144
 
145
+ npx @signaliz/sdk campaign-agent doctor \
146
+ --request-file campaign-request.json \
147
+ --json
148
+
149
+ npx @signaliz/cli build "Build a strategy-template campaign for local operators" \
150
+ --strategy-template non-medical-home-care \
151
+ --target-count 25 \
152
+ --use-local-leads
153
+
131
154
  npx @signaliz/sdk campaign-agent status campaign_build_id --json
132
155
  npx @signaliz/sdk campaign-agent review campaign_build_id --limit 100 --json
133
156
  npx @signaliz/sdk campaign-agent rows campaign_build_id --limit 100 --json
@@ -1191,6 +1191,13 @@ var DEFAULT_CAMPAIGN_BUILDER_BUILT_INS = [
1191
1191
  "email_verification",
1192
1192
  "signals"
1193
1193
  ];
1194
+ var CAMPAIGN_BUILDER_BUILT_IN_ROUTE_IDS = {
1195
+ lead_generation: "signaliz-lead-generation",
1196
+ local_leads: "signaliz-local-leads",
1197
+ email_finding: "signaliz-email-finding",
1198
+ email_verification: "signaliz-email-verification",
1199
+ signals: "signaliz-signals"
1200
+ };
1194
1201
  var DEFAULT_CAMPAIGN_BUILDER_APPROVAL_TYPES = [
1195
1202
  "memory_retrieval",
1196
1203
  "spend",
@@ -1687,6 +1694,17 @@ var CampaignBuilderAgent = class {
1687
1694
  }
1688
1695
  return plan;
1689
1696
  }
1697
+ async readiness(request, options = {}) {
1698
+ const plan = await this.createPlan(request, options);
1699
+ return createCampaignBuilderReadiness(plan);
1700
+ }
1701
+ async proof(request, options = {}) {
1702
+ const plan = await this.createPlan(request, options);
1703
+ const result = await this.dryRunPlan(plan, {
1704
+ idempotencyKey: options.idempotencyKey
1705
+ });
1706
+ return createCampaignBuilderProofReceipt(plan, result);
1707
+ }
1690
1708
  async commitPlan(plan, options = {}) {
1691
1709
  const writeMode = options.confirm === true && options.dryRun === false;
1692
1710
  if (writeMode && !options.approvedBy) {
@@ -2047,6 +2065,157 @@ function createCampaignBuilderAgentPlan(request, context = {}) {
2047
2065
  warnings: context.warnings ?? []
2048
2066
  };
2049
2067
  }
2068
+ function createCampaignBuilderReadiness(plan) {
2069
+ const lanes = plan.routes.map((route) => createCampaignBuilderReadinessLane(route, plan));
2070
+ const builtInLanes = lanes.filter((lane) => lane.builtIn);
2071
+ const customRoutes = lanes.filter((lane) => lane.customRoute);
2072
+ const strategyMemory = asRecord(plan.strategyMemoryStatus);
2073
+ const deliveryRisk = asRecord(plan.buildRequest.deliveryRisk);
2074
+ const deliveryRiskBlockers = arrayOfStrings(deliveryRisk.blockers) ?? [];
2075
+ const deliveryRiskValue = asRecord(deliveryRisk.risk);
2076
+ const deliveryRiskLevel = stringValue(deliveryRiskValue.risk_level ?? deliveryRiskValue.riskLevel);
2077
+ const memoryReady = !plan.strategyMemoryStatus || strategyMemory.ready !== false;
2078
+ const kernelPlanReady = Object.keys(asRecord(plan.kernelPlan)).length > 0;
2079
+ const deliveryRiskReady = !plan.buildRequest.deliveryRisk || deliveryRiskBlockers.length === 0 && !["high", "critical", "blocked"].includes(String(deliveryRiskLevel ?? "").toLowerCase());
2080
+ const builtInsReady = builtInLanes.length > 0 && builtInLanes.every((lane) => lane.ready);
2081
+ const customRoutesReady = customRoutes.every((lane) => lane.ready);
2082
+ const approvalGatesReady = plan.approvals.length > 0;
2083
+ const gates = [
2084
+ {
2085
+ id: "campaign_plan_created",
2086
+ label: "Campaign plan created",
2087
+ passed: Boolean(plan.planId && plan.mcpFlow.length > 0),
2088
+ detail: plan.planId ? `Plan ${plan.planId} has ${plan.mcpFlow.length} MCP step(s).` : "No campaign-builder plan was created."
2089
+ },
2090
+ {
2091
+ id: "built_in_lanes_ready",
2092
+ label: "Built-in lanes ready",
2093
+ passed: builtInsReady,
2094
+ detail: `${builtInLanes.filter((lane) => lane.ready).length}/${builtInLanes.length} Signaliz built-in lane(s) are present.`
2095
+ },
2096
+ {
2097
+ id: "strategy_memory_ready",
2098
+ label: "Strategy memory ready",
2099
+ passed: memoryReady,
2100
+ detail: plan.strategyMemoryStatus ? "Strategy memory readiness was checked." : "Strategy memory readiness was not requested."
2101
+ },
2102
+ {
2103
+ id: "kernel_plan_attached",
2104
+ label: "Kernel plan attached",
2105
+ passed: kernelPlanReady,
2106
+ detail: kernelPlanReady ? "Kernel planner evidence is attached." : "Kernel planner evidence is not attached."
2107
+ },
2108
+ {
2109
+ id: "delivery_risk_clear",
2110
+ label: "Delivery risk clear",
2111
+ passed: deliveryRiskReady,
2112
+ detail: plan.buildRequest.deliveryRisk ? `Delivery risk is ${deliveryRiskLevel || "available"} with ${deliveryRiskBlockers.length} blocker(s).` : "Delivery risk preflight was not requested."
2113
+ },
2114
+ {
2115
+ id: "custom_routes_prepared",
2116
+ label: "Custom routes prepared",
2117
+ passed: customRoutesReady,
2118
+ 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.`
2119
+ },
2120
+ {
2121
+ id: "approval_gates_present",
2122
+ label: "Approval gates present",
2123
+ passed: approvalGatesReady,
2124
+ detail: approvalGatesReady ? `${plan.approvals.length} approval gate(s) are attached.` : "No approval gates are attached."
2125
+ }
2126
+ ];
2127
+ const blockers = [
2128
+ ...gates.filter((gate) => !gate.passed).map((gate) => gate.detail),
2129
+ ...lanes.flatMap((lane) => lane.blockers)
2130
+ ];
2131
+ const score = gates.length === 0 ? 0 : Math.round(gates.filter((gate) => gate.passed).length / gates.length * 100);
2132
+ const ready = blockers.length === 0;
2133
+ return {
2134
+ plan,
2135
+ lanes,
2136
+ gates,
2137
+ summary: {
2138
+ ready,
2139
+ score,
2140
+ targetCount: plan.targetCount,
2141
+ estimatedCredits: plan.estimatedCredits,
2142
+ builtInLanesReady: builtInLanes.filter((lane) => lane.ready).length,
2143
+ builtInLanesTotal: builtInLanes.length,
2144
+ customRoutesReady: customRoutes.filter((lane) => lane.ready).length,
2145
+ customRoutesTotal: customRoutes.length,
2146
+ approvalGateCount: plan.approvals.length,
2147
+ blockers,
2148
+ warnings: plan.warnings
2149
+ },
2150
+ nextActions: campaignReadinessNextActions(plan, ready, customRoutes.length)
2151
+ };
2152
+ }
2153
+ function createCampaignBuilderProofReceipt(plan, result) {
2154
+ const strategyMemory = asRecord(plan.strategyMemoryStatus);
2155
+ const dryRunResult = asRecord(result);
2156
+ const memoryReady = plan.strategyMemoryStatus ? strategyMemory.ready !== false : false;
2157
+ const dryRunCompleted = dryRunResult.dryRun === true || dryRunResult.dry_run === true || dryRunResult.status === "dry_run";
2158
+ const noLaunch = dryRunCompleted && !dryRunResult.campaignBuildId && !dryRunResult.campaign_build_id;
2159
+ const gates = [
2160
+ {
2161
+ id: "strategy_memory_ready",
2162
+ passed: memoryReady,
2163
+ ready: strategyMemory.ready ?? null,
2164
+ blocker_count: Array.isArray(strategyMemory.blockers) ? strategyMemory.blockers.length : 0
2165
+ },
2166
+ {
2167
+ id: "campaign_plan_ready",
2168
+ passed: Boolean(plan.planId && plan.mcpFlow.length > 0),
2169
+ plan_id: plan.planId,
2170
+ flow_steps: plan.mcpFlow.length
2171
+ },
2172
+ {
2173
+ id: "approval_gates_present",
2174
+ passed: plan.approvals.length > 0,
2175
+ approval_types: plan.approvals.map((approval) => approval.type)
2176
+ },
2177
+ {
2178
+ id: "dry_run_completed",
2179
+ passed: dryRunCompleted,
2180
+ status: dryRunResult.status ?? null,
2181
+ current_phase: dryRunResult.currentPhase ?? dryRunResult.current_phase ?? null
2182
+ },
2183
+ {
2184
+ id: "no_spendful_launch",
2185
+ passed: noLaunch,
2186
+ campaign_build_id: dryRunResult.campaignBuildId ?? dryRunResult.campaign_build_id ?? null
2187
+ }
2188
+ ];
2189
+ return {
2190
+ receipt_version: "campaign-agent-proof.v1",
2191
+ source: "signaliz.campaignBuilderAgent.proof",
2192
+ success: gates.every((gate) => gate.passed),
2193
+ safety: {
2194
+ spendful_tools_called: false,
2195
+ external_writes_called: false,
2196
+ sender_loads_called: false,
2197
+ email_sends_called: false,
2198
+ dry_run_only: true
2199
+ },
2200
+ campaign: {
2201
+ plan_id: plan.planId,
2202
+ campaign_name: plan.campaignName,
2203
+ strategy_template: campaignBuilderProofStrategyTemplate(plan),
2204
+ target_count: plan.targetCount,
2205
+ estimated_credits: plan.estimatedCredits,
2206
+ operating_playbooks: plan.operatingPlaybooks.map((playbook) => playbook.slug)
2207
+ },
2208
+ gates,
2209
+ strategy_memory_status: summarizeCampaignBuilderProofStrategyMemory(strategyMemory),
2210
+ dry_run_result: summarizeCampaignBuilderProofDryRun(dryRunResult),
2211
+ recommended_next_actions: [
2212
+ "Review the plan, approvals, and dry-run result.",
2213
+ "Use campaign-agent commit-plan --confirm-write only after review.",
2214
+ "Use campaign-agent build-approved only with --confirm-launch, approval identity, approval types, and spend limit.",
2215
+ "When an approved build reaches pending_approval, review rows and approve delivery through the Campaigns SDK."
2216
+ ]
2217
+ };
2218
+ }
2050
2219
  function getMissingCampaignBuilderApprovals(plan, approval) {
2051
2220
  if (approval.planId !== plan.planId) {
2052
2221
  return plan.approvals;
@@ -3424,6 +3593,137 @@ function campaignReviewRowHasCopy(row) {
3424
3593
  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)
3425
3594
  );
3426
3595
  }
3596
+ function createCampaignBuilderReadinessLane(route, plan) {
3597
+ const builtIn = campaignBuilderBuiltInForRoute(route);
3598
+ const customRoute = route.provider !== "signaliz" && route.provider !== "csv" && builtIn === void 0;
3599
+ const prepareStep = plan.mcpFlow.find(
3600
+ (step) => step.tool === "gtm_provider_recipe_prepare" && step.arguments.provider_id === route.provider
3601
+ );
3602
+ const activateStep = plan.mcpFlow.find(
3603
+ (step) => step.tool === "gtm_provider_route_activate" && step.arguments.provider_id === route.provider
3604
+ );
3605
+ const readOnlySetup = !customRoute || prepareStep?.readOnly === true && activateStep?.readOnly === true && activateStep.arguments.dry_run === true && activateStep.arguments.confirm === false;
3606
+ const blockers = [
3607
+ customRoute && !prepareStep ? `${route.label ?? route.provider} is missing provider recipe setup.` : void 0,
3608
+ customRoute && !activateStep ? `${route.label ?? route.provider} is missing provider route dry-run setup.` : void 0,
3609
+ customRoute && !readOnlySetup ? `${route.label ?? route.provider} route setup must stay read-only before approval.` : void 0,
3610
+ route.mode === "required" && !route.toolName && customRoute ? `${route.label ?? route.provider} is required but has no tool name.` : void 0
3611
+ ].filter((item) => Boolean(item));
3612
+ return {
3613
+ id: route.id ?? `${route.provider}-${route.layer}`,
3614
+ label: route.label ?? readinessLaneLabel(route, builtIn),
3615
+ layer: route.layer,
3616
+ gtmLayers: gtmLayersForRoute(route),
3617
+ provider: route.provider,
3618
+ toolName: route.toolName,
3619
+ mode: route.mode ?? "if_available",
3620
+ builtIn,
3621
+ approvalRequired: route.approvalRequired === true || plan.approvals.some((approval) => approval.routeId === route.id),
3622
+ customRoute,
3623
+ readOnlySetup,
3624
+ ready: blockers.length === 0,
3625
+ blockers,
3626
+ nextAction: customRoute ? `Review ${route.provider} route setup before launch approval.` : void 0
3627
+ };
3628
+ }
3629
+ function campaignBuilderBuiltInForRoute(route) {
3630
+ return Object.entries(CAMPAIGN_BUILDER_BUILT_IN_ROUTE_IDS).find(([, routeId]) => route.id === routeId)?.[0];
3631
+ }
3632
+ function readinessLaneLabel(route, builtIn) {
3633
+ if (builtIn) {
3634
+ const labels = {
3635
+ lead_generation: "Signaliz lead generation",
3636
+ local_leads: "Signaliz local leads",
3637
+ email_finding: "Signaliz email finding",
3638
+ email_verification: "Signaliz email verification",
3639
+ signals: "Signaliz signals"
3640
+ };
3641
+ return labels[builtIn];
3642
+ }
3643
+ return `${route.provider} ${route.layer}`;
3644
+ }
3645
+ function campaignReadinessNextActions(plan, ready, customRouteCount) {
3646
+ const requestHint = "--request-file campaign-request.json";
3647
+ if (ready) {
3648
+ return [
3649
+ `signaliz campaign-agent proof ${requestHint} --json`,
3650
+ `signaliz campaign-agent commit-plan ${requestHint} --json`,
3651
+ `signaliz campaign-agent build-approved ${requestHint} --confirm-launch --approve-all --approved-by operator@example.com --spend-limit ${plan.estimatedCredits}`
3652
+ ];
3653
+ }
3654
+ const actions = [
3655
+ plan.strategyMemoryStatus && asRecord(plan.strategyMemoryStatus).ready === false ? "signaliz gtm strategy-memory --strategy-template <strategy_template> --include-samples --json" : void 0,
3656
+ customRouteCount > 0 ? "Review BYO provider recipe and route dry-run steps in the readiness packet." : void 0,
3657
+ `signaliz campaign-agent plan ${requestHint} --json`
3658
+ ];
3659
+ return actions.filter((item) => Boolean(item));
3660
+ }
3661
+ function campaignBuilderProofStrategyTemplate(plan) {
3662
+ const buildRequest = asRecord(plan.buildRequest);
3663
+ const strategyMemory = asRecord(plan.strategyMemoryStatus);
3664
+ const statusTemplate = strategyMemory.strategy_template ?? strategyMemory.strategyTemplate;
3665
+ const statusTemplateRecord = asRecord(statusTemplate);
3666
+ const flowTemplate = plan.mcpFlow.map((step) => asRecord(step.arguments).strategy_template ?? asRecord(step.arguments).strategyTemplate).find((value) => typeof value === "string" && value.trim());
3667
+ return firstNonEmptyString(
3668
+ buildRequest.strategyTemplate,
3669
+ buildRequest.strategy_template,
3670
+ statusTemplate,
3671
+ statusTemplateRecord.slug,
3672
+ statusTemplateRecord.id,
3673
+ flowTemplate
3674
+ );
3675
+ }
3676
+ function summarizeCampaignBuilderProofStrategyMemory(strategyMemory) {
3677
+ if (Object.keys(strategyMemory).length === 0) return { ready: false };
3678
+ const template = strategyMemory.strategy_template ?? strategyMemory.strategyTemplate;
3679
+ const templateRecord = asRecord(template);
3680
+ const coverage = asRecord(strategyMemory.coverage);
3681
+ const rawSourceGroups = Array.isArray(strategyMemory.source_groups) ? strategyMemory.source_groups : Array.isArray(strategyMemory.sourceGroups) ? strategyMemory.sourceGroups : [];
3682
+ const sourceGroups = rawSourceGroups.map((group) => {
3683
+ const sourceGroup = asRecord(group);
3684
+ return {
3685
+ id: sourceGroup.id ?? null,
3686
+ required: sourceGroup.required ?? null,
3687
+ ready: sourceGroup.ready ?? null,
3688
+ counts: asRecord(sourceGroup.counts)
3689
+ };
3690
+ });
3691
+ return {
3692
+ ready: strategyMemory.ready ?? null,
3693
+ read_only: strategyMemory.read_only ?? strategyMemory.readOnly ?? null,
3694
+ source_tool: firstNonEmptyString(strategyMemory.source_tool, strategyMemory.sourceTool),
3695
+ strategy_template: {
3696
+ slug: firstNonEmptyString(template, templateRecord.slug, templateRecord.id),
3697
+ label: firstNonEmptyString(templateRecord.label)
3698
+ },
3699
+ coverage: {
3700
+ required_groups_ready: coverage.required_groups_ready ?? coverage.requiredGroupsReady ?? null,
3701
+ required_groups_total: coverage.required_groups_total ?? coverage.requiredGroupsTotal ?? null,
3702
+ source_groups_ready: coverage.source_groups_ready ?? coverage.sourceGroupsReady ?? null,
3703
+ source_groups_total: coverage.source_groups_total ?? coverage.sourceGroupsTotal ?? null,
3704
+ knowledge_docs: coverage.knowledge_docs ?? coverage.knowledgeDocs ?? null,
3705
+ memories: coverage.memories ?? null
3706
+ },
3707
+ source_groups: sourceGroups,
3708
+ blocker_count: Array.isArray(strategyMemory.blockers) ? strategyMemory.blockers.length : 0,
3709
+ warning_count: Array.isArray(strategyMemory.warnings) ? strategyMemory.warnings.length : 0
3710
+ };
3711
+ }
3712
+ function summarizeCampaignBuilderProofDryRun(dryRunResult) {
3713
+ return {
3714
+ dryRun: dryRunResult.dryRun ?? dryRunResult.dry_run ?? null,
3715
+ status: dryRunResult.status ?? null,
3716
+ currentPhase: dryRunResult.currentPhase ?? dryRunResult.current_phase ?? null,
3717
+ campaignBuildId: dryRunResult.campaignBuildId ?? dryRunResult.campaign_build_id ?? null,
3718
+ plannedTargetCount: dryRunResult.plannedTargetCount ?? dryRunResult.planned_target_count ?? null
3719
+ };
3720
+ }
3721
+ function firstNonEmptyString(...values) {
3722
+ for (const value of values) {
3723
+ if (typeof value === "string" && value.trim()) return value;
3724
+ }
3725
+ return null;
3726
+ }
3427
3727
  function diagnosticMessages2(value) {
3428
3728
  if (!Array.isArray(value)) return [];
3429
3729
  return value.map((item) => {
@@ -6434,6 +6734,8 @@ export {
6434
6734
  CAMPAIGN_BUILDER_STRATEGY_TEMPLATES,
6435
6735
  CampaignBuilderAgent,
6436
6736
  createCampaignBuilderAgentPlan,
6737
+ createCampaignBuilderReadiness,
6738
+ createCampaignBuilderProofReceipt,
6437
6739
  getMissingCampaignBuilderApprovals,
6438
6740
  createCampaignBuilderApproval,
6439
6741
  getCampaignBuilderStrategyTemplate,