@signaliz/sdk 1.0.10 → 1.0.12

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
@@ -98,6 +98,18 @@ console.log(plan.strategyMemoryStatus?.ready);
98
98
  console.log(plan.mcpFlow.filter((step) =>
99
99
  ['gtm_provider_recipe_prepare', 'gtm_provider_route_activate'].includes(step.tool)
100
100
  ));
101
+
102
+ // After an approved launch, keep review and delivery in the agent surface.
103
+ const review = await signaliz.campaignBuilderAgent.reviewBuild('campaign_build_id', {
104
+ limit: 100,
105
+ });
106
+ const finalStatus = await signaliz.campaignBuilderAgent.getBuildStatus('campaign_build_id');
107
+ const reviewRows = await signaliz.campaignBuilderAgent.getBuildRows('campaign_build_id', {
108
+ limit: 100,
109
+ qualified: true,
110
+ });
111
+ const artifacts = await signaliz.campaignBuilderAgent.listBuildArtifacts('campaign_build_id');
112
+ console.log(review.summary.readyForDeliveryApproval);
101
113
  ```
102
114
 
103
115
  The CLI can run the same request shape from a reusable JSON file, with flags
@@ -115,6 +127,10 @@ npx @signaliz/sdk campaign-agent plan \
115
127
  --request-file campaign-request.json \
116
128
  --target-count 250 \
117
129
  --json
130
+
131
+ npx @signaliz/sdk campaign-agent status campaign_build_id --json
132
+ npx @signaliz/sdk campaign-agent review campaign_build_id --limit 100 --json
133
+ npx @signaliz/sdk campaign-agent rows campaign_build_id --limit 100 --json
118
134
  ```
119
135
 
120
136
  Use the hosted MCP campaign-agent planner when you want Signaliz to return the
@@ -1847,6 +1847,42 @@ var CampaignBuilderAgent = class {
1847
1847
  }
1848
1848
  return result;
1849
1849
  }
1850
+ async getBuildStatus(campaignBuildId) {
1851
+ return this.getCampaignBuildStatus(campaignBuildId);
1852
+ }
1853
+ async getBuildRows(campaignBuildId, options = {}) {
1854
+ const filters = compact({
1855
+ segment: options.segment,
1856
+ qualified: options.qualified,
1857
+ row_status: options.rowStatus
1858
+ });
1859
+ const args = compact({
1860
+ campaign_build_id: campaignBuildId,
1861
+ page_size: options.limit,
1862
+ cursor: options.cursor
1863
+ });
1864
+ if (Object.keys(filters).length > 0) args.filters = filters;
1865
+ const data = await this.callMcp("get_campaign_build_rows", args);
1866
+ return mapAgentCampaignRowsResult(data);
1867
+ }
1868
+ async listBuildArtifacts(campaignBuildId) {
1869
+ const data = await this.callMcp("list_campaign_build_artifacts", {
1870
+ campaign_build_id: campaignBuildId
1871
+ });
1872
+ return Array.isArray(data.artifacts) ? data.artifacts.map(mapAgentCampaignArtifact) : [];
1873
+ }
1874
+ async approveDelivery(campaignBuildId, destinationType, options) {
1875
+ return this.approveCampaignDelivery(campaignBuildId, destinationType, options);
1876
+ }
1877
+ async reviewBuild(campaignBuildId, options = {}) {
1878
+ const status = await this.getBuildStatus(campaignBuildId);
1879
+ const rows = await this.getBuildRows(campaignBuildId, {
1880
+ ...options,
1881
+ limit: options.limit ?? 100
1882
+ });
1883
+ const artifacts = await this.listBuildArtifacts(campaignBuildId);
1884
+ return createCampaignBuilderBuildReview(status, rows, artifacts);
1885
+ }
1850
1886
  async getCampaignBuildStatus(campaignBuildId) {
1851
1887
  const data = await this.callMcp("get_campaign_build_status", {
1852
1888
  campaign_build_id: campaignBuildId
@@ -3240,6 +3276,154 @@ function mapAgentCampaignBuildStatus(data) {
3240
3276
  completedAt: stringValue(data.completed_at) ?? null
3241
3277
  };
3242
3278
  }
3279
+ function mapAgentCampaignRowsResult(data) {
3280
+ const rows = Array.isArray(data.rows) ? data.rows : [];
3281
+ return {
3282
+ campaignBuildId: stringValue(data.campaign_build_id) ?? "",
3283
+ rows: rows.map((row, index) => {
3284
+ const record = asRecord(row);
3285
+ return {
3286
+ index: numberOrUndefined2(record.index) ?? index,
3287
+ status: stringValue(record.status) ?? stringValue(record.row_status) ?? "unknown",
3288
+ qualified: record.qualified !== false,
3289
+ segment: stringValue(record.segment) ?? null,
3290
+ data: nullableRecord(record.data) ?? record
3291
+ };
3292
+ }),
3293
+ count: numberOrUndefined2(data.count) ?? rows.length,
3294
+ pageSize: numberOrUndefined2(data.page_size) ?? rows.length,
3295
+ nextCursor: stringValue(data.next_cursor) ?? null,
3296
+ hasMore: data.has_more === true
3297
+ };
3298
+ }
3299
+ function mapAgentCampaignArtifact(data) {
3300
+ return {
3301
+ id: stringValue(data.id) ?? "",
3302
+ campaignBuildId: stringValue(data.campaign_build_id) ?? "",
3303
+ artifactType: stringValue(data.artifact_type) ?? "",
3304
+ destination: stringValue(data.destination) ?? "",
3305
+ storagePath: stringValue(data.storage_path) ?? "",
3306
+ signedUrl: stringValue(data.signed_url) ?? null,
3307
+ rowCount: numberOrUndefined2(data.row_count) ?? 0,
3308
+ checksum: stringValue(data.checksum) ?? null,
3309
+ metadata: nullableRecord(data.metadata) ?? {},
3310
+ createdAt: stringValue(data.created_at) ?? ""
3311
+ };
3312
+ }
3313
+ function createCampaignBuilderBuildReview(status, rows, artifacts) {
3314
+ const sampledRows = rows.rows.length;
3315
+ const availableRows = rows.count || sampledRows;
3316
+ const qualifiedRows = rows.rows.filter((row) => row.qualified !== false).length;
3317
+ const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
3318
+ const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
3319
+ const artifactCount = artifacts.length || status.artifactCount || 0;
3320
+ const gates = [
3321
+ {
3322
+ id: "build_completed",
3323
+ label: "Build completed",
3324
+ passed: status.status === "completed",
3325
+ detail: status.status === "completed" ? "The campaign build is completed." : `The campaign build is ${status.status}; review again after completion.`
3326
+ },
3327
+ {
3328
+ id: "rows_available",
3329
+ label: "Rows available",
3330
+ passed: sampledRows > 0,
3331
+ detail: sampledRows > 0 ? `${sampledRows} sampled row(s) are available for review.` : "No rows were returned for review."
3332
+ },
3333
+ {
3334
+ id: "qualified_sample",
3335
+ label: "Sample rows qualified",
3336
+ passed: sampledRows > 0 && qualifiedRows === sampledRows,
3337
+ detail: sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
3338
+ },
3339
+ {
3340
+ id: "email_coverage",
3341
+ label: "Email coverage",
3342
+ passed: sampledRows > 0 && rowsWithEmail === sampledRows,
3343
+ detail: sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
3344
+ },
3345
+ {
3346
+ id: "artifact_available",
3347
+ label: "Artifact available",
3348
+ passed: artifactCount > 0,
3349
+ detail: artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
3350
+ },
3351
+ {
3352
+ id: "no_failed_rows",
3353
+ label: "No failed rows",
3354
+ passed: status.recordsFailed === 0,
3355
+ detail: status.recordsFailed === 0 ? "The build reports zero failed rows." : `${status.recordsFailed} row(s) failed during the build.`
3356
+ }
3357
+ ];
3358
+ const blockedReasons = gates.filter((gate) => !gate.passed).map((gate) => gate.detail);
3359
+ const warnings = [
3360
+ rows.hasMore ? "This review is based on a row sample; page through remaining rows before final approval." : void 0,
3361
+ sampledRows > 0 && copyReadyRows < sampledRows ? `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.` : void 0,
3362
+ ...status.warnings
3363
+ ].filter((item) => Boolean(item));
3364
+ const readyForDeliveryApproval = blockedReasons.length === 0;
3365
+ return {
3366
+ campaignBuildId: status.campaignBuildId || rows.campaignBuildId,
3367
+ status,
3368
+ rows,
3369
+ artifacts,
3370
+ gates,
3371
+ summary: {
3372
+ status: status.status,
3373
+ phase: status.currentPhase,
3374
+ sampledRows,
3375
+ availableRows,
3376
+ qualifiedRows,
3377
+ rowsWithEmail,
3378
+ copyReadyRows,
3379
+ artifactCount,
3380
+ readyForDeliveryApproval,
3381
+ blockedReasons,
3382
+ warnings
3383
+ },
3384
+ nextActions: campaignReviewNextActions(status, readyForDeliveryApproval)
3385
+ };
3386
+ }
3387
+ function campaignReviewNextActions(status, readyForDeliveryApproval) {
3388
+ const buildId = status.campaignBuildId;
3389
+ if (!buildId) return [];
3390
+ if (status.status === "completed" && readyForDeliveryApproval) {
3391
+ return [
3392
+ `signaliz campaign-agent approve ${buildId} --destination-type json`,
3393
+ `signaliz campaign-agent artifacts ${buildId}`
3394
+ ];
3395
+ }
3396
+ if (status.status === "completed") {
3397
+ return [
3398
+ `signaliz campaign-agent rows ${buildId} --limit 100`,
3399
+ `signaliz campaign-agent artifacts ${buildId}`
3400
+ ];
3401
+ }
3402
+ if (status.status === "pending_approval") {
3403
+ return [
3404
+ `signaliz campaign-agent rows ${buildId} --limit 100`,
3405
+ `signaliz campaign-agent approve ${buildId} --destination-type json`
3406
+ ];
3407
+ }
3408
+ if ((status.status === "queued" || status.status === "running") && status.triggerRunId) {
3409
+ return [`signaliz ops run-status ${status.triggerRunId} --watch`];
3410
+ }
3411
+ return [`signaliz campaign-agent status ${buildId}`];
3412
+ }
3413
+ function campaignReviewRowHasEmail(row) {
3414
+ const data = asRecord(row.data);
3415
+ return Boolean(
3416
+ stringValue(data.email) || stringValue(data.work_email) || stringValue(data.workEmail)
3417
+ );
3418
+ }
3419
+ function campaignReviewRowHasCopy(row) {
3420
+ const data = asRecord(row.data);
3421
+ const copy = asRecord(data.copy);
3422
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
3423
+ return row.status === "copy_ready" || data.copy_ready === true || Boolean(
3424
+ 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
+ );
3426
+ }
3243
3427
  function diagnosticMessages2(value) {
3244
3428
  if (!Array.isArray(value)) return [];
3245
3429
  return value.map((item) => {
package/dist/cli.js CHANGED
@@ -1853,6 +1853,42 @@ var CampaignBuilderAgent = class {
1853
1853
  }
1854
1854
  return result;
1855
1855
  }
1856
+ async getBuildStatus(campaignBuildId) {
1857
+ return this.getCampaignBuildStatus(campaignBuildId);
1858
+ }
1859
+ async getBuildRows(campaignBuildId, options = {}) {
1860
+ const filters = compact({
1861
+ segment: options.segment,
1862
+ qualified: options.qualified,
1863
+ row_status: options.rowStatus
1864
+ });
1865
+ const args = compact({
1866
+ campaign_build_id: campaignBuildId,
1867
+ page_size: options.limit,
1868
+ cursor: options.cursor
1869
+ });
1870
+ if (Object.keys(filters).length > 0) args.filters = filters;
1871
+ const data = await this.callMcp("get_campaign_build_rows", args);
1872
+ return mapAgentCampaignRowsResult(data);
1873
+ }
1874
+ async listBuildArtifacts(campaignBuildId) {
1875
+ const data = await this.callMcp("list_campaign_build_artifacts", {
1876
+ campaign_build_id: campaignBuildId
1877
+ });
1878
+ return Array.isArray(data.artifacts) ? data.artifacts.map(mapAgentCampaignArtifact) : [];
1879
+ }
1880
+ async approveDelivery(campaignBuildId, destinationType, options) {
1881
+ return this.approveCampaignDelivery(campaignBuildId, destinationType, options);
1882
+ }
1883
+ async reviewBuild(campaignBuildId, options = {}) {
1884
+ const status = await this.getBuildStatus(campaignBuildId);
1885
+ const rows = await this.getBuildRows(campaignBuildId, {
1886
+ ...options,
1887
+ limit: options.limit ?? 100
1888
+ });
1889
+ const artifacts = await this.listBuildArtifacts(campaignBuildId);
1890
+ return createCampaignBuilderBuildReview(status, rows, artifacts);
1891
+ }
1856
1892
  async getCampaignBuildStatus(campaignBuildId) {
1857
1893
  const data = await this.callMcp("get_campaign_build_status", {
1858
1894
  campaign_build_id: campaignBuildId
@@ -3246,6 +3282,154 @@ function mapAgentCampaignBuildStatus(data) {
3246
3282
  completedAt: stringValue(data.completed_at) ?? null
3247
3283
  };
3248
3284
  }
3285
+ function mapAgentCampaignRowsResult(data) {
3286
+ const rows = Array.isArray(data.rows) ? data.rows : [];
3287
+ return {
3288
+ campaignBuildId: stringValue(data.campaign_build_id) ?? "",
3289
+ rows: rows.map((row, index) => {
3290
+ const record = asRecord(row);
3291
+ return {
3292
+ index: numberOrUndefined2(record.index) ?? index,
3293
+ status: stringValue(record.status) ?? stringValue(record.row_status) ?? "unknown",
3294
+ qualified: record.qualified !== false,
3295
+ segment: stringValue(record.segment) ?? null,
3296
+ data: nullableRecord(record.data) ?? record
3297
+ };
3298
+ }),
3299
+ count: numberOrUndefined2(data.count) ?? rows.length,
3300
+ pageSize: numberOrUndefined2(data.page_size) ?? rows.length,
3301
+ nextCursor: stringValue(data.next_cursor) ?? null,
3302
+ hasMore: data.has_more === true
3303
+ };
3304
+ }
3305
+ function mapAgentCampaignArtifact(data) {
3306
+ return {
3307
+ id: stringValue(data.id) ?? "",
3308
+ campaignBuildId: stringValue(data.campaign_build_id) ?? "",
3309
+ artifactType: stringValue(data.artifact_type) ?? "",
3310
+ destination: stringValue(data.destination) ?? "",
3311
+ storagePath: stringValue(data.storage_path) ?? "",
3312
+ signedUrl: stringValue(data.signed_url) ?? null,
3313
+ rowCount: numberOrUndefined2(data.row_count) ?? 0,
3314
+ checksum: stringValue(data.checksum) ?? null,
3315
+ metadata: nullableRecord(data.metadata) ?? {},
3316
+ createdAt: stringValue(data.created_at) ?? ""
3317
+ };
3318
+ }
3319
+ function createCampaignBuilderBuildReview(status, rows, artifacts) {
3320
+ const sampledRows = rows.rows.length;
3321
+ const availableRows = rows.count || sampledRows;
3322
+ const qualifiedRows = rows.rows.filter((row) => row.qualified !== false).length;
3323
+ const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
3324
+ const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
3325
+ const artifactCount = artifacts.length || status.artifactCount || 0;
3326
+ const gates = [
3327
+ {
3328
+ id: "build_completed",
3329
+ label: "Build completed",
3330
+ passed: status.status === "completed",
3331
+ detail: status.status === "completed" ? "The campaign build is completed." : `The campaign build is ${status.status}; review again after completion.`
3332
+ },
3333
+ {
3334
+ id: "rows_available",
3335
+ label: "Rows available",
3336
+ passed: sampledRows > 0,
3337
+ detail: sampledRows > 0 ? `${sampledRows} sampled row(s) are available for review.` : "No rows were returned for review."
3338
+ },
3339
+ {
3340
+ id: "qualified_sample",
3341
+ label: "Sample rows qualified",
3342
+ passed: sampledRows > 0 && qualifiedRows === sampledRows,
3343
+ detail: sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
3344
+ },
3345
+ {
3346
+ id: "email_coverage",
3347
+ label: "Email coverage",
3348
+ passed: sampledRows > 0 && rowsWithEmail === sampledRows,
3349
+ detail: sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
3350
+ },
3351
+ {
3352
+ id: "artifact_available",
3353
+ label: "Artifact available",
3354
+ passed: artifactCount > 0,
3355
+ detail: artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
3356
+ },
3357
+ {
3358
+ id: "no_failed_rows",
3359
+ label: "No failed rows",
3360
+ passed: status.recordsFailed === 0,
3361
+ detail: status.recordsFailed === 0 ? "The build reports zero failed rows." : `${status.recordsFailed} row(s) failed during the build.`
3362
+ }
3363
+ ];
3364
+ const blockedReasons = gates.filter((gate) => !gate.passed).map((gate) => gate.detail);
3365
+ const warnings = [
3366
+ rows.hasMore ? "This review is based on a row sample; page through remaining rows before final approval." : void 0,
3367
+ sampledRows > 0 && copyReadyRows < sampledRows ? `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.` : void 0,
3368
+ ...status.warnings
3369
+ ].filter((item) => Boolean(item));
3370
+ const readyForDeliveryApproval = blockedReasons.length === 0;
3371
+ return {
3372
+ campaignBuildId: status.campaignBuildId || rows.campaignBuildId,
3373
+ status,
3374
+ rows,
3375
+ artifacts,
3376
+ gates,
3377
+ summary: {
3378
+ status: status.status,
3379
+ phase: status.currentPhase,
3380
+ sampledRows,
3381
+ availableRows,
3382
+ qualifiedRows,
3383
+ rowsWithEmail,
3384
+ copyReadyRows,
3385
+ artifactCount,
3386
+ readyForDeliveryApproval,
3387
+ blockedReasons,
3388
+ warnings
3389
+ },
3390
+ nextActions: campaignReviewNextActions(status, readyForDeliveryApproval)
3391
+ };
3392
+ }
3393
+ function campaignReviewNextActions(status, readyForDeliveryApproval) {
3394
+ const buildId = status.campaignBuildId;
3395
+ if (!buildId) return [];
3396
+ if (status.status === "completed" && readyForDeliveryApproval) {
3397
+ return [
3398
+ `signaliz campaign-agent approve ${buildId} --destination-type json`,
3399
+ `signaliz campaign-agent artifacts ${buildId}`
3400
+ ];
3401
+ }
3402
+ if (status.status === "completed") {
3403
+ return [
3404
+ `signaliz campaign-agent rows ${buildId} --limit 100`,
3405
+ `signaliz campaign-agent artifacts ${buildId}`
3406
+ ];
3407
+ }
3408
+ if (status.status === "pending_approval") {
3409
+ return [
3410
+ `signaliz campaign-agent rows ${buildId} --limit 100`,
3411
+ `signaliz campaign-agent approve ${buildId} --destination-type json`
3412
+ ];
3413
+ }
3414
+ if ((status.status === "queued" || status.status === "running") && status.triggerRunId) {
3415
+ return [`signaliz ops run-status ${status.triggerRunId} --watch`];
3416
+ }
3417
+ return [`signaliz campaign-agent status ${buildId}`];
3418
+ }
3419
+ function campaignReviewRowHasEmail(row) {
3420
+ const data = asRecord(row.data);
3421
+ return Boolean(
3422
+ stringValue(data.email) || stringValue(data.work_email) || stringValue(data.workEmail)
3423
+ );
3424
+ }
3425
+ function campaignReviewRowHasCopy(row) {
3426
+ const data = asRecord(row.data);
3427
+ const copy = asRecord(data.copy);
3428
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
3429
+ return row.status === "copy_ready" || data.copy_ready === true || Boolean(
3430
+ 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)
3431
+ );
3432
+ }
3249
3433
  function diagnosticMessages2(value) {
3250
3434
  if (!Array.isArray(value)) return [];
3251
3435
  return value.map((item) => {
@@ -6619,6 +6803,10 @@ async function handleCampaignAgentCommand(signaliz, argv) {
6619
6803
  }
6620
6804
  return;
6621
6805
  }
6806
+ if (["review", "status", "rows", "artifacts", "approve", "approve-delivery"].includes(subcommand)) {
6807
+ await handleCampaignAgentReviewCommand(signaliz, subcommand, argv.slice(1));
6808
+ return;
6809
+ }
6622
6810
  if (!["plan", "proof", "smoke", "commit-plan", "dry-run", "build-approved", "launch-approved"].includes(subcommand)) {
6623
6811
  printCampaignAgentHelp();
6624
6812
  return;
@@ -6718,6 +6906,90 @@ async function handleCampaignAgentCommand(signaliz, argv) {
6718
6906
  printCampaignAgentPlan(plan);
6719
6907
  }
6720
6908
  }
6909
+ async function handleCampaignAgentReviewCommand(signaliz, subcommand, argv) {
6910
+ const flags = parseFlags(argv);
6911
+ const buildId = argv[0] && !argv[0].startsWith("--") ? argv[0] : stringFlag(flags, "campaign-build-id", "build-id");
6912
+ if (!buildId) throw new Error(`campaign-agent ${subcommand} requires <campaign_build_id> or --campaign-build-id`);
6913
+ if (subcommand === "review") {
6914
+ const review = await signaliz.campaignBuilderAgent.reviewBuild(buildId, {
6915
+ limit: numberFlag(flags, "limit") ?? 100,
6916
+ cursor: stringFlag(flags, "cursor"),
6917
+ segment: stringFlag(flags, "segment"),
6918
+ rowStatus: stringFlag(flags, "row-status"),
6919
+ qualified: booleanFlag(flags, "qualified") ? true : booleanFlag(flags, "disqualified") ? false : void 0
6920
+ });
6921
+ const operatorReview = {
6922
+ ...review,
6923
+ rows: {
6924
+ ...review.rows,
6925
+ rows: formatCampaignAgentRows(review.rows.rows)
6926
+ }
6927
+ };
6928
+ if (booleanFlag(flags, "json")) {
6929
+ printJson(operatorReview);
6930
+ } else {
6931
+ printCampaignAgentBuildReview(operatorReview);
6932
+ }
6933
+ return;
6934
+ }
6935
+ if (subcommand === "status") {
6936
+ const status = await signaliz.campaignBuilderAgent.getBuildStatus(buildId);
6937
+ if (booleanFlag(flags, "json")) {
6938
+ printJson(status);
6939
+ } else {
6940
+ printCampaignAgentBuildStatus(status);
6941
+ }
6942
+ return;
6943
+ }
6944
+ if (subcommand === "rows") {
6945
+ const result2 = await signaliz.campaignBuilderAgent.getBuildRows(buildId, {
6946
+ limit: numberFlag(flags, "limit"),
6947
+ cursor: stringFlag(flags, "cursor"),
6948
+ segment: stringFlag(flags, "segment"),
6949
+ rowStatus: stringFlag(flags, "row-status"),
6950
+ qualified: booleanFlag(flags, "qualified") ? true : booleanFlag(flags, "disqualified") ? false : void 0
6951
+ });
6952
+ const operatorResult = {
6953
+ ...result2,
6954
+ rows: formatCampaignAgentRows(result2.rows)
6955
+ };
6956
+ if (booleanFlag(flags, "json")) {
6957
+ printJson(operatorResult);
6958
+ } else {
6959
+ printCampaignAgentRows(operatorResult);
6960
+ }
6961
+ return;
6962
+ }
6963
+ if (subcommand === "artifacts") {
6964
+ const artifacts = await signaliz.campaignBuilderAgent.listBuildArtifacts(buildId);
6965
+ if (booleanFlag(flags, "json")) {
6966
+ printJson(artifacts);
6967
+ } else if (artifacts.length === 0) {
6968
+ console.log("No artifacts yet.");
6969
+ console.log(`Next: signaliz campaign-agent status ${buildId}`);
6970
+ } else {
6971
+ for (const artifact of artifacts) {
6972
+ console.log(`${artifact.artifactType || "artifact"}: ${artifact.rowCount} rows${artifact.signedUrl ? `
6973
+ ${artifact.signedUrl}` : ""}`);
6974
+ }
6975
+ }
6976
+ return;
6977
+ }
6978
+ const destinationType = stringFlag(flags, "destination-type", "delivery-destination-type", "type");
6979
+ if (!destinationType || !["json", "csv", "webhook"].includes(destinationType)) {
6980
+ throw new Error("campaign-agent approve requires --destination-type json|csv|webhook");
6981
+ }
6982
+ const result = await signaliz.campaignBuilderAgent.approveDelivery(buildId, destinationType, {
6983
+ destinationId: stringFlag(flags, "destination-id", "delivery-destination-id"),
6984
+ destinationConfig: jsonObjectFlag(flags, "destination-config", "delivery-config")
6985
+ });
6986
+ if (booleanFlag(flags, "json")) {
6987
+ printJson(result);
6988
+ } else {
6989
+ console.log(`Delivery ${result.status}: ${result.destinationType}`);
6990
+ if (result.message) console.log(result.message);
6991
+ }
6992
+ }
6721
6993
  function campaignAgentRequestTemplateFromFlags(flags) {
6722
6994
  const goal = stringFlag(flags, "goal", "brief") || "Build a strategy-template campaign for mature operators in the target ICP.";
6723
6995
  const request = campaignAgentRequestFromFlags(goal, flags, { includeDefaults: true });
@@ -7054,6 +7326,87 @@ function printCampaignAgentExecution(payload, flags) {
7054
7326
  console.log("\nResult:");
7055
7327
  printJson(payload.result);
7056
7328
  }
7329
+ function printCampaignAgentBuildStatus(status) {
7330
+ console.log(`Campaign: ${status.name || status.campaignBuildId}`);
7331
+ if (status.campaignId) console.log(`Campaign ID: ${status.campaignId}`);
7332
+ console.log(`Status: ${status.status}`);
7333
+ console.log(`Phase: ${status.currentPhase || "N/A"}`);
7334
+ console.log(`Rows: ${status.recordsSucceeded}/${status.recordsTotal} succeeded, ${status.recordsFailed} failed`);
7335
+ console.log(`Artifacts: ${status.artifactCount}`);
7336
+ if (status.triggerRunId) console.log(`Trigger: ${status.triggerRunId}`);
7337
+ if (Array.isArray(status.warnings) && status.warnings.length > 0) {
7338
+ console.log(`Warnings: ${status.warnings.slice(0, 3).join("; ")}`);
7339
+ }
7340
+ if (Array.isArray(status.errors) && status.errors.length > 0) {
7341
+ console.log(`Errors: ${status.errors.slice(0, 3).join("; ")}`);
7342
+ }
7343
+ if (status.status === "completed") {
7344
+ console.log(`Next: signaliz campaign-agent rows ${status.campaignBuildId} --limit 100`);
7345
+ } else if (status.status === "pending_approval") {
7346
+ console.log(`Next: signaliz campaign-agent approve ${status.campaignBuildId} --destination-type webhook`);
7347
+ }
7348
+ }
7349
+ function printCampaignAgentBuildReview(review) {
7350
+ const summary = asRecord4(review.summary);
7351
+ console.log(`Campaign: ${review.status?.name || review.campaignBuildId}`);
7352
+ console.log(`Status: ${summary.status || review.status?.status}`);
7353
+ console.log(`Phase: ${summary.phase || review.status?.currentPhase || "N/A"}`);
7354
+ console.log(`Rows: ${summary.sampledRows || 0} sampled, ${summary.availableRows || 0} available`);
7355
+ console.log(`Qualified: ${summary.qualifiedRows || 0}`);
7356
+ console.log(`Emails: ${summary.rowsWithEmail || 0}`);
7357
+ console.log(`Copy ready: ${summary.copyReadyRows || 0}`);
7358
+ console.log(`Artifacts: ${summary.artifactCount || 0}`);
7359
+ console.log(`Delivery approval ready: ${summary.readyForDeliveryApproval === true ? "yes" : "no"}`);
7360
+ if (Array.isArray(review.gates) && review.gates.length > 0) {
7361
+ console.log("\nGates:");
7362
+ for (const gate of review.gates) {
7363
+ console.log(`- ${gate.passed ? "PASS" : "BLOCKED"} ${gate.label}: ${gate.detail}`);
7364
+ }
7365
+ }
7366
+ const warnings = Array.isArray(summary.warnings) ? summary.warnings : [];
7367
+ if (warnings.length > 0) {
7368
+ console.log("\nWarnings:");
7369
+ for (const warning of warnings.slice(0, 5)) console.log(`- ${warning}`);
7370
+ }
7371
+ const nextActions = Array.isArray(review.nextActions) ? review.nextActions : [];
7372
+ if (nextActions.length > 0) {
7373
+ console.log("\nNext:");
7374
+ for (const action of nextActions) console.log(`- ${action}`);
7375
+ }
7376
+ }
7377
+ function formatCampaignAgentRows(rows) {
7378
+ return rows.map((row) => {
7379
+ const data = asRecord4(row.data);
7380
+ const fallbackName = `${data.first_name || ""} ${data.last_name || ""}`.trim() || null;
7381
+ return {
7382
+ ...row,
7383
+ email: data.email ?? null,
7384
+ name: data.full_name ?? fallbackName,
7385
+ title: data.title ?? null,
7386
+ company: data.company_name ?? null,
7387
+ domain: data.company_domain ?? null,
7388
+ score: data.overall_score ?? null,
7389
+ has_copy: Boolean(data.subject || data.opener || data.body || data.cta || asRecord4(data.raw_copy).subject)
7390
+ };
7391
+ });
7392
+ }
7393
+ function printCampaignAgentRows(result) {
7394
+ if (!Array.isArray(result.rows) || result.rows.length === 0) {
7395
+ console.log("No rows found.");
7396
+ return;
7397
+ }
7398
+ console.table(result.rows.map((row) => ({
7399
+ email: row.email || "",
7400
+ name: row.name || "",
7401
+ title: row.title || "",
7402
+ company: row.company || row.domain || "",
7403
+ status: row.status || "",
7404
+ segment: row.segment || "",
7405
+ score: row.score ?? ""
7406
+ })));
7407
+ console.log(`Showing ${result.rows.length} row(s)${result.hasMore ? " (more available)" : ""}`);
7408
+ if (result.nextCursor) console.log(`Next: signaliz campaign-agent rows ${result.campaignBuildId} --cursor ${result.nextCursor}`);
7409
+ }
7057
7410
  function asRecord4(value) {
7058
7411
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
7059
7412
  }
@@ -7230,6 +7583,9 @@ Usage:
7230
7583
  signaliz campaign-agent dry-run --goal "Build a strategy-template campaign..." --target-count 500 --use-local-leads --json
7231
7584
  signaliz campaign-agent build-approved --goal "Build a strategy-template campaign..." --confirm-launch --approve-all --approved-by you@example.com --spend-limit 500
7232
7585
  signaliz campaign-agent build-approved --request-file campaign-request.json --confirm-launch --approve-all --approved-by you@example.com --spend-limit 500 --wait --approve-delivery --delivery-destination-type json
7586
+ signaliz campaign-agent review <campaign_build_id> --limit 100 --json
7587
+ signaliz campaign-agent status <campaign_build_id> --json
7588
+ signaliz campaign-agent rows <campaign_build_id> --limit 100 --json
7233
7589
 
7234
7590
  Environment:
7235
7591
  SIGNALIZ_API_KEY Your API key (not required for campaign-agent init)
@@ -7261,8 +7617,13 @@ Signaliz campaign-agent commands:
7261
7617
  signaliz campaign-agent commit-plan (--goal TEXT | --request-file FILE) [--confirm-write --approved-by EMAIL] [--idempotency-key KEY] [--json]
7262
7618
  signaliz campaign-agent dry-run (--goal TEXT | --request-file FILE) [--gtm-campaign-id ID] [--delivery-risk JSON] [--idempotency-key KEY] [--json]
7263
7619
  signaliz campaign-agent build-approved (--goal TEXT | --request-file FILE) --confirm-launch --approved-by EMAIL (--approve-all | --approved-types a,b) [--commit-before-launch] [--spend-limit N] [--approved-route-ids a,b] [--wait] [--approve-delivery] [--delivery-destination-type json|csv|webhook] [--idempotency-key KEY] [--json]
7620
+ signaliz campaign-agent review <campaign_build_id> [--limit N] [--qualified|--disqualified] [--json]
7621
+ signaliz campaign-agent status <campaign_build_id> [--json]
7622
+ signaliz campaign-agent rows <campaign_build_id> [--limit N] [--cursor CURSOR] [--qualified|--disqualified] [--json]
7623
+ signaliz campaign-agent artifacts <campaign_build_id> [--json]
7624
+ signaliz campaign-agent approve <campaign_build_id> --destination-type json|csv|webhook [--destination-config JSON] [--json]
7264
7625
 
7265
- The init command emits a reusable JSON CampaignBuilderAgentRequest. The plan command composes an approval-gated strategy-template MCP flow. Proof runs the same plan path plus build_campaign dry_run=true and returns a no-spend receipt. Use --request-file to load a reusable request, then override fields such as --target-count or --strategy-template from the command line. Use --strategy-template to merge a private-safe strategy pattern into the plan without exposing private account labels, and --operating-playbooks to add reusable build-loop gates such as proof-first vertical qualification, net-new suppression, cache-first inventory, and signal-led copy approval. Built-ins map to Signaliz-native tools: lead_generation -> generate_leads, local_leads -> generate_local_leads, email_finding -> find_and_verify_emails, email_verification -> verify_emails, signals -> enrich_company_signals. Add BYO tools with --custom-tool provider:layer:tool[:mcp] or key=value pairs such as provider=apollo,layer=source,tool=apollo_search,mcp=apollo. Use --no-strategy-patterns or --no-workflow-patterns to disable default private strategy memory hints. Commit-plan dry-runs by default and writes only with --confirm-write plus approval identity. Dry-run executes build_campaign with dry_run=true. Approved build requires explicit launch confirmation and approval metadata; add --commit-before-launch to persist the reviewed plan as a GTM campaign before launch, --wait to poll until completion or pending approval, and --approve-delivery to approve reviewed JSON/CSV/webhook delivery after pending_approval.
7626
+ The init command emits a reusable JSON CampaignBuilderAgentRequest. The plan command composes an approval-gated strategy-template MCP flow. Proof runs the same plan path plus build_campaign dry_run=true and returns a no-spend receipt. Use --request-file to load a reusable request, then override fields such as --target-count or --strategy-template from the command line. Use --strategy-template to merge a private-safe strategy pattern into the plan without exposing private account labels, and --operating-playbooks to add reusable build-loop gates such as proof-first vertical qualification, net-new suppression, cache-first inventory, and signal-led copy approval. Built-ins map to Signaliz-native tools: lead_generation -> generate_leads, local_leads -> generate_local_leads, email_finding -> find_and_verify_emails, email_verification -> verify_emails, signals -> enrich_company_signals. Add BYO tools with --custom-tool provider:layer:tool[:mcp] or key=value pairs such as provider=apollo,layer=source,tool=apollo_search,mcp=apollo. Use --no-strategy-patterns or --no-workflow-patterns to disable default private strategy memory hints. Commit-plan dry-runs by default and writes only with --confirm-write plus approval identity. Dry-run executes build_campaign with dry_run=true. Approved build requires explicit launch confirmation and approval metadata; add --commit-before-launch to persist the reviewed plan as a GTM campaign before launch, --wait to poll until completion or pending approval, and --approve-delivery to approve reviewed webhook delivery after pending_approval. Use review to inspect status, sampled rows, artifacts, and delivery-readiness gates before approving delivery or routing rows to external tools.
7266
7627
  `);
7267
7628
  }
7268
7629
  main().catch((e) => {