@signaliz/sdk 1.0.16 → 1.0.18

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.
@@ -212,25 +212,31 @@ var HttpClient = class {
212
212
  async getToken() {
213
213
  if (this.apiKey) return this.apiKey;
214
214
  if (this.accessToken) return this.accessToken;
215
- const res = await fetch("https://api.signaliz.com/token", {
216
- method: "POST",
217
- headers: { "Content-Type": "application/json" },
218
- body: JSON.stringify({
219
- grant_type: "client_credentials",
220
- client_id: this.clientId,
221
- client_secret: this.clientSecret
222
- })
223
- });
224
- if (!res.ok) {
225
- throw new SignalizError({
226
- code: "AUTH_FAILED",
227
- message: "Failed to obtain access token",
228
- errorType: "auth_expired"
215
+ if (this.accessTokenPromise) return this.accessTokenPromise;
216
+ this.accessTokenPromise = (async () => {
217
+ const res = await fetch("https://api.signaliz.com/token", {
218
+ method: "POST",
219
+ headers: { "Content-Type": "application/json" },
220
+ body: JSON.stringify({
221
+ grant_type: "client_credentials",
222
+ client_id: this.clientId,
223
+ client_secret: this.clientSecret
224
+ })
229
225
  });
230
- }
231
- const data = await res.json();
232
- this.accessToken = data.access_token;
233
- return this.accessToken;
226
+ if (!res.ok) {
227
+ throw new SignalizError({
228
+ code: "AUTH_FAILED",
229
+ message: "Failed to obtain access token",
230
+ errorType: "auth_expired"
231
+ });
232
+ }
233
+ const data = await res.json();
234
+ this.accessToken = data.access_token;
235
+ return this.accessToken;
236
+ })().finally(() => {
237
+ this.accessTokenPromise = void 0;
238
+ });
239
+ return this.accessTokenPromise;
234
240
  }
235
241
  };
236
242
  function sleep(ms) {
@@ -479,7 +485,6 @@ var Signals = class {
479
485
  if (params.online !== void 0) args.online = params.online;
480
486
  if (params.targetSignalCount) args.target_signal_count = params.targetSignalCount;
481
487
  if (params.lookbackDays) args.lookback_days = params.lookbackDays;
482
- if (params.model) args.model = params.model;
483
488
  if (params.enableDeepSearch) args.enable_deep_search = params.enableDeepSearch;
484
489
  if (params.enableOutreachIntelligence) args.enable_outreach_intelligence = params.enableOutreachIntelligence;
485
490
  if (params.enablePredictiveIntelligence) args.enable_predictive_intelligence = params.enablePredictiveIntelligence;
@@ -774,6 +779,10 @@ var Campaigns = class {
774
779
  async artifacts(campaignBuildId) {
775
780
  return this.listCampaignBuildArtifacts(campaignBuildId);
776
781
  }
782
+ /** Generate fresh signed URLs for a downloadable campaign artifact. */
783
+ async downloadArtifact(campaignBuildId, options) {
784
+ return this.downloadCampaignArtifact(campaignBuildId, options);
785
+ }
777
786
  /** Cancel a queued or running build. */
778
787
  async cancel(campaignBuildId, reason) {
779
788
  return this.cancelCampaignBuild(campaignBuildId, reason);
@@ -818,13 +827,21 @@ var Campaigns = class {
818
827
  dryRun: isDryRun,
819
828
  requestedTargetCount: data.requested_target_count,
820
829
  plannedTargetCount: data.planned_target_count,
830
+ acquisitionMode: data.acquisition_mode,
831
+ acquisitionSource: data.acquisition_source,
832
+ cacheReusePolicy: data.cache_reuse_policy,
821
833
  estimatedCredits: data.estimated_credits,
822
834
  estimatedDurationSeconds: data.estimated_duration_seconds,
823
835
  targetLimitApplied: data.target_limit_applied,
824
836
  targetLimitReason: data.target_limit_reason,
825
837
  maxSupportedTargetCount: data.max_supported_target_count,
826
838
  brainContext: data.brain_context ?? {},
827
- providerRoute: mapProviderRoute(data)
839
+ learningHoldout: data.learning_holdout ?? {},
840
+ providerRoute: mapProviderRoute(data),
841
+ effectiveSourceRouting: recordOrNull(data.effective_source_routing ?? data.source_routing),
842
+ sourcePlan: recordOrNull(data.source_plan ?? data.plan?.source_plan),
843
+ sourceRoutes: recordArray(data.source_routes ?? data.source_plan?.routes ?? data.plan?.source_plan?.routes),
844
+ sourceShards: recordArray(data.source_shards ?? data.source_plan?.shards ?? data.plan?.source_plan?.shards)
828
845
  };
829
846
  }
830
847
  async getCampaignBuildStatus(campaignBuildId) {
@@ -839,14 +856,24 @@ var Campaigns = class {
839
856
  });
840
857
  return (data.artifacts ?? []).map(mapArtifact);
841
858
  }
859
+ async downloadCampaignArtifact(campaignBuildId, options) {
860
+ const args = { campaign_build_id: campaignBuildId };
861
+ if (options?.format) args.format = options.format;
862
+ const data = await this.callMcp("download_campaign_artifact", args);
863
+ return mapArtifactDownloadResult(data);
864
+ }
842
865
  async getCampaignBuildRows(campaignBuildId, options) {
843
866
  const args = { campaign_build_id: campaignBuildId };
844
867
  if (options?.limit) args.page_size = options.limit;
845
868
  if (options?.cursor) args.cursor = options.cursor;
869
+ if (options?.includeData !== void 0) args.include_data = options.includeData;
870
+ if (options?.includeRaw !== void 0) args.include_raw = options.includeRaw;
846
871
  const filters = {};
847
872
  if (options?.segment) filters.segment = options.segment;
848
873
  if (options?.rowStatus) filters.row_status = options.rowStatus;
849
874
  if (options?.qualified !== void 0) filters.qualified = options.qualified;
875
+ if (options?.cached !== void 0) filters.cached = options.cached;
876
+ if (options?.exportReady !== void 0) filters.export_ready = options.exportReady;
850
877
  if (Object.keys(filters).length > 0) args.filters = filters;
851
878
  const data = await this.callMcp("get_campaign_build_rows", args);
852
879
  return {
@@ -871,6 +898,7 @@ var Campaigns = class {
871
898
  };
872
899
  if (options?.destinationId) args.destination_id = options.destinationId;
873
900
  if (options?.destinationConfig) args.destination_config = options.destinationConfig;
901
+ if (options?.allowPartialDelivery === true) args.allow_partial_delivery = true;
874
902
  const data = await this.callMcp("approve_campaign_delivery", args);
875
903
  return {
876
904
  campaignBuildId: data.campaign_build_id,
@@ -879,7 +907,10 @@ var Campaigns = class {
879
907
  message: data.message ?? "",
880
908
  deliveryId: data.delivery_id,
881
909
  deliveryIds: data.delivery_ids,
882
- approvedCount: data.approved_count
910
+ approvedCount: data.approved_count,
911
+ partialDelivery: data.partial_delivery,
912
+ effectiveTargetCount: data.effective_target_count,
913
+ requestedTargetCount: data.requested_target_count
883
914
  };
884
915
  }
885
916
  async cancelCampaignBuild(campaignBuildId, reason) {
@@ -932,17 +963,50 @@ function mapStatus(data) {
932
963
  warnings: diagnosticMessages(data.warnings),
933
964
  errors: diagnosticMessages(data.errors),
934
965
  artifactCount: data.artifact_count ?? 0,
966
+ artifactDownloads: Array.isArray(data.artifact_downloads) ? data.artifact_downloads.map(mapArtifactDownload) : [],
967
+ customerRowCounts: mapCustomerRowCounts(data.customer_row_counts),
968
+ phaseAttemptTotals: recordOrNull(data.phase_attempt_totals) ?? void 0,
935
969
  providerRoute: mapProviderRoute(data),
936
970
  triggerRunId: typeof data.trigger_run_id === "string" ? data.trigger_run_id : null,
937
971
  staleRunningPhase: data.stale_running_phase === true,
938
972
  phaseAgeSeconds: numberOrNull(data.phase_age_seconds),
939
973
  diagnostics: recordOrNull(data.diagnostics) ?? {},
940
974
  nextAction: formatNextAction(data.next_action),
975
+ approvalAction: formatNextAction(data.approval_action),
941
976
  createdAt: data.created_at,
942
977
  updatedAt: data.updated_at,
943
978
  completedAt: data.completed_at
944
979
  };
945
980
  }
981
+ function mapCustomerRowCounts(value) {
982
+ const record = recordOrNull(value);
983
+ if (!record) return void 0;
984
+ return {
985
+ requestedTarget: numberOrNull(record.requested_target),
986
+ acquiredRows: numberOrNull(record.acquired_rows) ?? void 0,
987
+ acceptedRows: numberOrNull(record.accepted_rows) ?? void 0,
988
+ usableRows: numberOrNull(record.usable_rows) ?? void 0,
989
+ copiedRows: numberOrNull(record.copied_rows) ?? void 0,
990
+ approvalReadyRows: numberOrNull(record.approval_ready_rows) ?? void 0,
991
+ qaReadyRows: numberOrNull(record.qa_ready_rows) ?? void 0,
992
+ deliveredRows: numberOrNull(record.delivered_rows) ?? void 0,
993
+ disqualifiedRows: numberOrNull(record.disqualified_rows) ?? void 0,
994
+ reviewableRows: numberOrNull(record.reviewable_rows) ?? void 0,
995
+ rowFailures: numberOrNull(record.row_failures) ?? void 0,
996
+ acceptedShortfall: numberOrNull(record.accepted_shortfall),
997
+ usableShortfall: numberOrNull(record.usable_shortfall),
998
+ qaReadyShortfall: numberOrNull(record.qa_ready_shortfall),
999
+ deliveryShortfall: numberOrNull(record.delivery_shortfall),
1000
+ fillRatio: numberOrNull(record.fill_ratio),
1001
+ qaReadyFillRatio: numberOrNull(record.qa_ready_fill_ratio),
1002
+ deliveredFillRatio: numberOrNull(record.delivered_fill_ratio),
1003
+ terminalReason: typeof record.terminal_reason === "string" ? record.terminal_reason : null,
1004
+ acceptedShortfallReason: typeof record.accepted_shortfall_reason === "string" ? record.accepted_shortfall_reason : null,
1005
+ usableShortfallReason: typeof record.usable_shortfall_reason === "string" ? record.usable_shortfall_reason : null,
1006
+ qaReadyShortfallReason: typeof record.qa_ready_shortfall_reason === "string" ? record.qa_ready_shortfall_reason : null,
1007
+ deliveryShortfallReason: typeof record.delivery_shortfall_reason === "string" ? record.delivery_shortfall_reason : null
1008
+ };
1009
+ }
946
1010
  function mapProviderRoute(data) {
947
1011
  const brainContext = recordOrNull(data?.brain_context);
948
1012
  const plan = recordOrNull(data?.provider_route_plan) ?? recordOrNull(brainContext?.provider_route_plan);
@@ -969,6 +1033,9 @@ function recordOrNull(value) {
969
1033
  function stringArray(value) {
970
1034
  return Array.isArray(value) ? value.map((item) => String(item || "").trim()).filter(Boolean) : [];
971
1035
  }
1036
+ function recordArray(value) {
1037
+ return Array.isArray(value) ? value.map(recordOrNull).filter((item) => Boolean(item)) : [];
1038
+ }
972
1039
  function diagnosticMessages(value) {
973
1040
  if (!Array.isArray(value)) return [];
974
1041
  return value.map((item) => {
@@ -1020,13 +1087,56 @@ function mapArtifact(a) {
1020
1087
  artifactType: a.artifact_type,
1021
1088
  destination: a.destination,
1022
1089
  storagePath: a.storage_path,
1090
+ format: a.format ?? null,
1023
1091
  signedUrl: a.signed_url,
1092
+ downloadUrl: a.download_url ?? null,
1093
+ manifestUrl: a.manifest_url ?? null,
1094
+ expiresAt: a.expires_at ?? null,
1024
1095
  rowCount: a.row_count ?? 0,
1025
1096
  checksum: a.checksum,
1026
1097
  metadata: a.metadata ?? {},
1027
1098
  createdAt: a.created_at
1028
1099
  };
1029
1100
  }
1101
+ function mapArtifactDownload(data) {
1102
+ return {
1103
+ id: data.id,
1104
+ artifactType: data.artifact_type ?? null,
1105
+ format: data.format ?? null,
1106
+ rowCount: data.row_count ?? 0,
1107
+ signedUrl: data.signed_url ?? null,
1108
+ downloadUrl: data.download_url ?? null,
1109
+ manifestUrl: data.manifest_url ?? null,
1110
+ expiresAt: data.expires_at ?? null
1111
+ };
1112
+ }
1113
+ function mapArtifactDownloadResult(data) {
1114
+ return {
1115
+ campaignBuildId: data.campaign_build_id,
1116
+ artifactId: data.artifact_id,
1117
+ format: data.format,
1118
+ rowCount: data.row_count ?? 0,
1119
+ byteSize: numberOrNull(data.byte_size),
1120
+ partCount: data.part_count ?? 0,
1121
+ checksum: data.checksum ?? null,
1122
+ signedUrl: data.signed_url ?? null,
1123
+ downloadUrl: data.download_url ?? null,
1124
+ manifestUrl: data.manifest_url ?? null,
1125
+ expiresAt: data.expires_at ?? null,
1126
+ parts: Array.isArray(data.parts) ? data.parts.map(mapArtifactDownloadPart) : [],
1127
+ downloadCommands: recordOrNull(data.download_commands) ?? {},
1128
+ expiresInSeconds: numberOrNull(data.expires_in_seconds)
1129
+ };
1130
+ }
1131
+ function mapArtifactDownloadPart(data) {
1132
+ return {
1133
+ partIndex: data.part_index ?? 0,
1134
+ rowCount: data.row_count ?? 0,
1135
+ byteSize: numberOrNull(data.byte_size),
1136
+ checksum: data.checksum ?? null,
1137
+ signedUrl: data.signed_url ?? null
1138
+ };
1139
+ }
1030
1140
  function mapScope(data) {
1031
1141
  return {
1032
1142
  campaignScopeId: data.campaign_scope_id,
@@ -1080,8 +1190,30 @@ function buildArgs(request) {
1080
1190
  if (request.allowDownscale !== void 0) args.allow_downscale = request.allowDownscale;
1081
1191
  if (request.confirmSpend !== void 0) args.confirm_spend = request.confirmSpend;
1082
1192
  if (request.dedupKeys) args.dedup_keys = request.dedupKeys;
1193
+ if (request.acquisitionMode) args.acquisition_mode = request.acquisitionMode;
1194
+ if (request.cacheReusePolicy) {
1195
+ args.cache_reuse_policy = {
1196
+ allow_verified_cache: request.cacheReusePolicy.allowVerifiedCache,
1197
+ suppress_prior_campaign_ids: request.cacheReusePolicy.suppressPriorCampaignIds,
1198
+ suppress_prior_list_ids: request.cacheReusePolicy.suppressPriorListIds,
1199
+ uniqueness: request.cacheReusePolicy.uniqueness,
1200
+ require_usage_metadata: request.cacheReusePolicy.requireUsageMetadata,
1201
+ verified_cache_ttl_days: request.cacheReusePolicy.verifiedCacheTtlDays
1202
+ };
1203
+ }
1083
1204
  if (request.brainPreflight) args.brain_preflight = request.brainPreflight;
1084
1205
  if (request.brainDefaults) args.brain_defaults = request.brainDefaults;
1206
+ if (request.learningHoldout) {
1207
+ args.learning_holdout = {
1208
+ enabled: request.learningHoldout.enabled,
1209
+ control_percentage: request.learningHoldout.controlPercentage,
1210
+ min_control_size: request.learningHoldout.minControlSize,
1211
+ experiment_key: request.learningHoldout.experimentKey,
1212
+ source_campaign_id: request.learningHoldout.sourceCampaignId,
1213
+ primary_metric: request.learningHoldout.primaryMetric
1214
+ };
1215
+ }
1216
+ if (request.campaignStrategyContext) args.campaign_strategy_context = request.campaignStrategyContext;
1085
1217
  if (request.deliveryRisk) args.delivery_risk = request.deliveryRisk;
1086
1218
  if (request.icp) {
1087
1219
  args.icp = {
@@ -1123,7 +1255,17 @@ function buildArgs(request) {
1123
1255
  max_body_words: request.copy.maxBodyWords,
1124
1256
  sender_context: request.copy.senderContext,
1125
1257
  offer_context: request.copy.offerContext,
1126
- model: request.copy.model
1258
+ model: request.copy.model,
1259
+ style_source: request.copy.styleSource ? {
1260
+ sample_text: request.copy.styleSource.sampleText,
1261
+ campaign_ids: request.copy.styleSource.campaignIds,
1262
+ artifact_ids: request.copy.styleSource.artifactIds
1263
+ } : void 0,
1264
+ sequence_steps: request.copy.sequenceSteps,
1265
+ copy_schema: request.copy.copySchema,
1266
+ banned_phrases: request.copy.bannedPhrases,
1267
+ approval_required: request.copy.approvalRequired,
1268
+ quality_tier: request.copy.qualityTier
1127
1269
  };
1128
1270
  }
1129
1271
  if (request.delivery) {
@@ -1186,6 +1328,8 @@ var DEFAULT_SIGNALIZ_CAMPAIGN_BUILDER_DEFAULTS = {
1186
1328
  }
1187
1329
  };
1188
1330
  var DEFAULT_CAMPAIGN_BUILDER_BUILT_INS = [
1331
+ "company_discovery",
1332
+ "people_discovery",
1189
1333
  "lead_generation",
1190
1334
  "email_finding",
1191
1335
  "email_verification",
@@ -1194,6 +1338,8 @@ var DEFAULT_CAMPAIGN_BUILDER_BUILT_INS = [
1194
1338
  var CAMPAIGN_BUILDER_BUILT_IN_ROUTE_IDS = {
1195
1339
  lead_generation: "signaliz-lead-generation",
1196
1340
  local_leads: "signaliz-local-leads",
1341
+ company_discovery: "signaliz-company-discovery",
1342
+ people_discovery: "signaliz-people-discovery",
1197
1343
  email_finding: "signaliz-email-finding",
1198
1344
  email_verification: "signaliz-email-verification",
1199
1345
  signals: "signaliz-signals"
@@ -1211,8 +1357,8 @@ var CAMPAIGN_BUILDER_OPERATING_PLAYBOOKS = [
1211
1357
  slug: "cache-first-large-list",
1212
1358
  label: "Cache-First Large List",
1213
1359
  whenToUse: [
1214
- "The target count is high and reusable workspace lead or cache coverage may exist.",
1215
- "The deliverable is a verified list or top-up rather than a deeply custom one-off workflow."
1360
+ "The operator explicitly references a previous campaign, prior list, seed list, or suppression baseline.",
1361
+ "The deliverable is a top-up, continuation, or backfill where prior campaign context should guide reuse."
1216
1362
  ],
1217
1363
  sequence: [
1218
1364
  "Inventory reusable cache before paid sourcing.",
@@ -1418,7 +1564,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1418
1564
  ],
1419
1565
  requireVerifiedEmail: true
1420
1566
  },
1421
- builtIns: ["lead_generation", "email_finding", "email_verification", "signals"],
1567
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "email_finding", "email_verification", "signals"],
1422
1568
  preferredProviders: ["signaliz_native"],
1423
1569
  memoryQueries: [{
1424
1570
  query: "industrial OT net-new prior email suppression verified manufacturing embedded systems field service cyber resilience business continuity",
@@ -1492,7 +1638,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1492
1638
  ],
1493
1639
  requireVerifiedEmail: true
1494
1640
  },
1495
- builtIns: ["lead_generation", "local_leads", "email_finding", "email_verification", "signals"],
1641
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "local_leads", "email_finding", "email_verification", "signals"],
1496
1642
  preferredProviders: ["signaliz_native", "octave", "clay_webhook", "apify"],
1497
1643
  memoryQueries: [{
1498
1644
  query: "mature non-medical home care agencies billable hours franchise caregiver recruiting proof gate qualification export approval",
@@ -1558,7 +1704,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1558
1704
  exclusions: ["enterprise holding company", "staffing", "recruiting", "software-only vendor", "freelancer-only profile"],
1559
1705
  requireVerifiedEmail: true
1560
1706
  },
1561
- builtIns: ["lead_generation", "email_finding", "email_verification", "signals"],
1707
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "email_finding", "email_verification", "signals"],
1562
1708
  preferredProviders: ["signaliz_native", "octave"],
1563
1709
  memoryQueries: [{
1564
1710
  query: "agency founder owner CEO marketing services suppression domain pool email verification final approval gate",
@@ -1640,7 +1786,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1640
1786
  ],
1641
1787
  requireVerifiedEmail: true
1642
1788
  },
1643
- builtIns: ["lead_generation", "email_finding", "email_verification", "signals"],
1789
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "email_finding", "email_verification", "signals"],
1644
1790
  preferredProviders: ["signaliz_native", "octave"],
1645
1791
  memoryQueries: [{
1646
1792
  query: "cloud hosting infrastructure VMware Broadcom qualification verified email persona trigger SKU mapping",
@@ -1673,25 +1819,21 @@ var CampaignBuilderAgent = class {
1673
1819
  async createPlan(request, options = {}) {
1674
1820
  const resolvedRequest = applyCampaignBuilderStrategyTemplate(request);
1675
1821
  const warnings = [];
1676
- const workspaceContext = resolvedRequest.workspaceContext ?? await this.getWorkspaceContext(warnings);
1677
- const scopeResult = options.scopeCampaign === false ? void 0 : await this.scopeCampaign(resolvedRequest, warnings);
1678
- if (options.discoverCapabilities !== false) {
1679
- await this.discoverPlannedRoutes(resolvedRequest, warnings);
1680
- }
1822
+ const [workspaceContext, scopeResult] = await Promise.all([
1823
+ resolvedRequest.workspaceContext ? Promise.resolve(resolvedRequest.workspaceContext) : this.getWorkspaceContext(warnings),
1824
+ options.scopeCampaign === false ? Promise.resolve(void 0) : this.scopeCampaign(resolvedRequest, warnings),
1825
+ options.discoverCapabilities === false ? Promise.resolve(void 0) : this.discoverPlannedRoutes(resolvedRequest, warnings)
1826
+ ]);
1681
1827
  const plan = createCampaignBuilderAgentPlan(resolvedRequest, {
1682
1828
  workspaceContext,
1683
1829
  scopeBuildArgs: asRecord(scopeResult?.build_campaign_args ?? scopeResult?.buildCampaignArgs),
1684
1830
  warnings
1685
1831
  });
1686
- if (options.includeStrategyMemoryStatus !== false) {
1687
- await this.attachStrategyMemoryStatus(plan, warnings);
1688
- }
1689
- if (options.includeKernelPlan !== false) {
1690
- await this.attachKernelCampaignBuildPlan(plan, warnings);
1691
- }
1692
- if (options.includeDeliveryRisk !== false) {
1693
- await this.attachDeliveryRiskPreflight(plan, warnings);
1694
- }
1832
+ await Promise.all([
1833
+ options.includeStrategyMemoryStatus === false ? Promise.resolve() : this.attachStrategyMemoryStatus(plan, warnings),
1834
+ options.includeKernelPlan === false ? Promise.resolve() : this.attachKernelCampaignBuildPlan(plan, warnings),
1835
+ options.includeDeliveryRisk === false ? Promise.resolve() : this.attachDeliveryRiskPreflight(plan, warnings)
1836
+ ]);
1695
1837
  return plan;
1696
1838
  }
1697
1839
  async readiness(request, options = {}) {
@@ -1739,6 +1881,8 @@ var CampaignBuilderAgent = class {
1739
1881
  },
1740
1882
  brain_defaults: plan.buildRequest.brainDefaults,
1741
1883
  brain_preflight: plan.buildRequest.brainPreflight,
1884
+ learning_holdout: plan.buildRequest.learningHoldout,
1885
+ campaign_strategy_context: plan.buildRequest.campaignStrategyContext,
1742
1886
  delivery_risk_preflight: plan.buildRequest.deliveryRisk,
1743
1887
  delivery_risk: plan.buildRequest.deliveryRisk
1744
1888
  }),
@@ -1751,7 +1895,8 @@ var CampaignBuilderAgent = class {
1751
1895
  estimated_credits: plan.estimatedCredits
1752
1896
  },
1753
1897
  brain_delivery_risk_preflight: plan.buildRequest.deliveryRisk,
1754
- delivery_risk: plan.buildRequest.deliveryRisk
1898
+ delivery_risk: plan.buildRequest.deliveryRisk,
1899
+ learning_holdout: plan.buildRequest.learningHoldout
1755
1900
  },
1756
1901
  approval_required: plan.approvals.some((approval) => approval.blocking),
1757
1902
  actor_type: "agent",
@@ -1860,7 +2005,8 @@ var CampaignBuilderAgent = class {
1860
2005
  const destinationType = options.deliveryDestinationType ?? plan.buildRequest.delivery?.destinationType ?? "json";
1861
2006
  result.deliveryApproval = await this.approveCampaignDelivery(campaignBuildId, destinationType, {
1862
2007
  destinationId: options.deliveryDestinationId,
1863
- destinationConfig: options.deliveryDestinationConfig ?? plan.buildRequest.delivery?.destinationConfig
2008
+ destinationConfig: options.deliveryDestinationConfig ?? plan.buildRequest.delivery?.destinationConfig,
2009
+ allowPartialDelivery: options.allowPartialDelivery
1864
2010
  });
1865
2011
  finalStatus = await this.waitForCampaignBuildStatus(
1866
2012
  campaignBuildId,
@@ -1883,8 +2029,12 @@ var CampaignBuilderAgent = class {
1883
2029
  const args = compact({
1884
2030
  campaign_build_id: campaignBuildId,
1885
2031
  page_size: options.limit,
1886
- cursor: options.cursor
2032
+ cursor: options.cursor,
2033
+ include_data: options.includeData,
2034
+ include_raw: options.includeRaw
1887
2035
  });
2036
+ if (options.cached !== void 0) filters.cached = options.cached;
2037
+ if (options.exportReady !== void 0) filters.export_ready = options.exportReady;
1888
2038
  if (Object.keys(filters).length > 0) args.filters = filters;
1889
2039
  const data = await this.callMcp("get_campaign_build_rows", args);
1890
2040
  return mapAgentCampaignRowsResult(data);
@@ -1899,12 +2049,14 @@ var CampaignBuilderAgent = class {
1899
2049
  return this.approveCampaignDelivery(campaignBuildId, destinationType, options);
1900
2050
  }
1901
2051
  async reviewBuild(campaignBuildId, options = {}) {
1902
- const status = await this.getBuildStatus(campaignBuildId);
1903
- const rows = await this.getBuildRows(campaignBuildId, {
1904
- ...options,
1905
- limit: options.limit ?? 100
1906
- });
1907
- const artifacts = await this.listBuildArtifacts(campaignBuildId);
2052
+ const [status, rows, artifacts] = await Promise.all([
2053
+ this.getBuildStatus(campaignBuildId),
2054
+ this.getBuildRows(campaignBuildId, {
2055
+ ...options,
2056
+ limit: options.limit ?? 100
2057
+ }),
2058
+ this.listBuildArtifacts(campaignBuildId)
2059
+ ]);
1908
2060
  return createCampaignBuilderBuildReview(status, rows, artifacts);
1909
2061
  }
1910
2062
  async getCampaignBuildStatus(campaignBuildId) {
@@ -1932,7 +2084,8 @@ var CampaignBuilderAgent = class {
1932
2084
  campaign_build_id: campaignBuildId,
1933
2085
  destination_type: destinationType,
1934
2086
  destination_id: options?.destinationId,
1935
- destination_config: options?.destinationConfig
2087
+ destination_config: options?.destinationConfig,
2088
+ allow_partial_delivery: options?.allowPartialDelivery === true ? true : void 0
1936
2089
  });
1937
2090
  const data = await this.callMcp("approve_campaign_delivery", args);
1938
2091
  return {
@@ -1942,7 +2095,10 @@ var CampaignBuilderAgent = class {
1942
2095
  message: data.message ?? "",
1943
2096
  deliveryId: data.delivery_id,
1944
2097
  deliveryIds: data.delivery_ids,
1945
- approvedCount: data.approved_count
2098
+ approvedCount: data.approved_count,
2099
+ partialDelivery: data.partial_delivery,
2100
+ effectiveTargetCount: data.effective_target_count,
2101
+ requestedTargetCount: data.requested_target_count
1946
2102
  };
1947
2103
  }
1948
2104
  async getWorkspaceContext(warnings) {
@@ -1978,15 +2134,19 @@ var CampaignBuilderAgent = class {
1978
2134
  const providers = new Set(
1979
2135
  [...request.integrations ?? [], ...routesFromCustomerTools(request.customerTools ?? [])].filter((route) => route.provider !== "signaliz").map((route) => route.provider)
1980
2136
  );
1981
- for (const provider of providers) {
2137
+ const warningsByProvider = await Promise.all(Array.from(providers, async (provider) => {
1982
2138
  try {
1983
2139
  await this.callMcp("discover_capabilities", {
1984
2140
  query: `campaign builder ${provider} ${request.goal}`,
1985
2141
  category: "campaign"
1986
2142
  });
2143
+ return void 0;
1987
2144
  } catch (error) {
1988
- warnings.push(`Capability discovery for ${provider} failed: ${errorMessage(error)}`);
2145
+ return `Capability discovery for ${provider} failed: ${errorMessage(error)}`;
1989
2146
  }
2147
+ }));
2148
+ for (const warning of warningsByProvider) {
2149
+ if (warning) warnings.push(warning);
1990
2150
  }
1991
2151
  }
1992
2152
  async attachStrategyMemoryStatus(plan, warnings) {
@@ -2010,8 +2170,24 @@ var CampaignBuilderAgent = class {
2010
2170
  const kernelPlan = await this.callMcp("gtm_campaign_build_plan", asRecord(plannerStep.arguments));
2011
2171
  plan.kernelPlan = asRecord(kernelPlan);
2012
2172
  const brainDefaults = asRecord(plan.kernelPlan.brain_defaults ?? plan.kernelPlan.brainDefaults);
2173
+ const corpusContext = asRecord(plan.kernelPlan.corpus_context ?? plan.kernelPlan.corpusContext);
2174
+ const campaignStrategyContext = asRecord(plan.kernelPlan.campaign_strategy_context ?? plan.kernelPlan.campaignStrategyContext);
2175
+ const memoryExamples = Array.isArray(plan.kernelPlan.memory_examples) ? plan.kernelPlan.memory_examples.map((item) => asRecord(item)).filter((item) => Object.keys(item).length > 0).slice(0, 15) : [];
2013
2176
  if (Object.keys(brainDefaults).length > 0 && Object.keys(asRecord(plan.buildRequest.brainDefaults)).length === 0) {
2014
2177
  plan.buildRequest.brainDefaults = brainDefaults;
2178
+ }
2179
+ if (Object.keys(campaignStrategyContext).length > 0) {
2180
+ plan.buildRequest.campaignStrategyContext = campaignStrategyContext;
2181
+ }
2182
+ if (Object.keys(corpusContext).length > 0 || memoryExamples.length > 0 || Object.keys(brainDefaults).length > 0 || Object.keys(campaignStrategyContext).length > 0) {
2183
+ plan.buildRequest.brainPreflight = compact({
2184
+ ...asRecord(plan.buildRequest.brainPreflight),
2185
+ corpus_context: Object.keys(corpusContext).length > 0 ? corpusContext : void 0,
2186
+ memory_examples: memoryExamples.length > 0 ? memoryExamples : void 0,
2187
+ brain_defaults: Object.keys(brainDefaults).length > 0 ? brainDefaults : void 0,
2188
+ campaign_strategy_context: Object.keys(campaignStrategyContext).length > 0 ? campaignStrategyContext : void 0
2189
+ });
2190
+ plan.brainPreflight = asRecord(plan.buildRequest.brainPreflight);
2015
2191
  refreshBuildStepArguments(plan);
2016
2192
  }
2017
2193
  } catch (error) {
@@ -2192,6 +2368,9 @@ function createCampaignBuilderProofReceipt(plan, result) {
2192
2368
  campaign_build_id: dryRunResult.campaignBuildId ?? dryRunResult.campaign_build_id ?? null
2193
2369
  }
2194
2370
  ];
2371
+ const learnBackPlan = summarizeCampaignBuilderProofLearnBack(
2372
+ asRecord(asRecord(plan.buildRequest.brainPreflight).learn_back_plan ?? asRecord(plan.brainPreflight).learn_back_plan)
2373
+ );
2195
2374
  return {
2196
2375
  receipt_version: "campaign-agent-proof.v1",
2197
2376
  source: "signaliz.campaignBuilderAgent.proof",
@@ -2214,6 +2393,7 @@ function createCampaignBuilderProofReceipt(plan, result) {
2214
2393
  gates,
2215
2394
  strategy_memory_status: summarizeCampaignBuilderProofStrategyMemory(strategyMemory),
2216
2395
  dry_run_result: summarizeCampaignBuilderProofDryRun(dryRunResult),
2396
+ learn_back_plan: learnBackPlan,
2217
2397
  recommended_next_actions: [
2218
2398
  "Review the plan, approvals, and dry-run result.",
2219
2399
  "Use campaign-agent commit-plan --confirm-write only after review.",
@@ -2770,6 +2950,13 @@ function campaignBuilderMemoryKitSdkExample(request, seedManifestFile) {
2770
2950
  function campaignBuilderMemoryKitSource(source) {
2771
2951
  const normalized = normalizeTemplateKey(source || "agency");
2772
2952
  const catalog = {
2953
+ northStar: {
2954
+ id: "north_star_campaign_builder",
2955
+ label: "North Star campaign-builder acceptance patterns",
2956
+ seed_source: "north-star",
2957
+ scope: "redacted_acceptance_seed_packs",
2958
+ privacy: "redacted aggregate counts, QA gates, and fixture shape only"
2959
+ },
2773
2960
  agency: {
2774
2961
  id: "agency_campaign_builder",
2775
2962
  label: "Agency campaign-builder strategy patterns",
@@ -2795,6 +2982,10 @@ function campaignBuilderMemoryKitSource(source) {
2795
2982
  const sourceMap = {
2796
2983
  agency: "agency",
2797
2984
  "campaign-builder": "agency",
2985
+ "north-star": "northStar",
2986
+ northstar: "northStar",
2987
+ "campaign-builder-north-star": "northStar",
2988
+ acceptance: "northStar",
2798
2989
  "strategy-memory": "agency",
2799
2990
  "workflow-patterns": "workflow",
2800
2991
  workflow: "workflow",
@@ -2810,13 +3001,13 @@ function campaignBuilderMemoryKitSource(source) {
2810
3001
  return {
2811
3002
  seedSource: "all",
2812
3003
  verifyScript: null,
2813
- memorySources: [catalog.agency, catalog.workflow, catalog.feedback]
3004
+ memorySources: [catalog.northStar, catalog.agency, catalog.workflow, catalog.feedback]
2814
3005
  };
2815
3006
  }
2816
3007
  const item = catalog[selected];
2817
3008
  return {
2818
3009
  seedSource: item.seed_source,
2819
- verifyScript: item.seed_source === "agency" ? "scripts/gtm-kernel/verify-agency-import-manifest.mjs" : item.seed_source === "building-clay" ? "scripts/gtm-kernel/verify-building-clay-import-manifest.mjs" : null,
3010
+ verifyScript: item.seed_source === "north-star" ? "scripts/gtm-kernel/verify-north-star-import-manifest.mjs" : item.seed_source === "agency" ? "scripts/gtm-kernel/verify-agency-import-manifest.mjs" : item.seed_source === "building-clay" ? "scripts/gtm-kernel/verify-building-clay-import-manifest.mjs" : null,
2820
3011
  memorySources: [item]
2821
3012
  };
2822
3013
  }
@@ -2996,11 +3187,11 @@ function builtInRoutesFor(request) {
2996
3187
  routes.push({
2997
3188
  id: "signaliz-lead-generation",
2998
3189
  layer: "source",
2999
- gtmLayers: ["find_company", "find_people", "lead_generation"],
3190
+ gtmLayer: "lead_generation",
3000
3191
  provider: "signaliz",
3001
3192
  mode: "required",
3002
3193
  toolName: "generate_leads",
3003
- rationale: "Use Signaliz fresh workspace acquisition as the default source of truth."
3194
+ rationale: "Inventory verified cache first, then use Signaliz fresh workspace acquisition for the remaining approved gap."
3004
3195
  });
3005
3196
  }
3006
3197
  if (builtIns.has("local_leads")) {
@@ -3014,6 +3205,28 @@ function builtInRoutesFor(request) {
3014
3205
  rationale: "Use Signaliz local-leads sourcing for geography-bound local business campaigns."
3015
3206
  });
3016
3207
  }
3208
+ if (builtIns.has("company_discovery")) {
3209
+ routes.push({
3210
+ id: "signaliz-company-discovery",
3211
+ layer: "source",
3212
+ gtmLayer: "find_company",
3213
+ provider: "signaliz",
3214
+ mode: "if_available",
3215
+ toolName: "find_companies_signaliz",
3216
+ rationale: "Find and qualify company domains before people/email fill for account-led campaigns."
3217
+ });
3218
+ }
3219
+ if (builtIns.has("people_discovery")) {
3220
+ routes.push({
3221
+ id: "signaliz-people-discovery",
3222
+ layer: "source",
3223
+ gtmLayer: "find_people",
3224
+ provider: "signaliz",
3225
+ mode: "if_available",
3226
+ toolName: "find_people_signaliz",
3227
+ rationale: "Find people directly when the brief is contact research or LinkedIn-style prospecting."
3228
+ });
3229
+ }
3017
3230
  if (builtIns.has("email_finding")) {
3018
3231
  routes.push({
3019
3232
  id: "signaliz-email-finding",
@@ -3058,7 +3271,7 @@ function normalizeBuiltIns(request) {
3058
3271
  return [...builtIns].filter(isCampaignBuilderBuiltInTool);
3059
3272
  }
3060
3273
  function isCampaignBuilderBuiltInTool(value) {
3061
- return ["lead_generation", "local_leads", "email_finding", "email_verification", "signals"].includes(String(value));
3274
+ return ["lead_generation", "local_leads", "company_discovery", "people_discovery", "email_finding", "email_verification", "signals"].includes(String(value));
3062
3275
  }
3063
3276
  function looksLikeLocalLeadCampaign(request) {
3064
3277
  const text = [
@@ -3089,6 +3302,21 @@ function routesFromCustomerTools(tools) {
3089
3302
  }))
3090
3303
  );
3091
3304
  }
3305
+ function normalizeCampaignBuilderLearningHoldout(input, gtmCampaignId, campaignName) {
3306
+ const source = asRecord(input);
3307
+ if (source.enabled === false) return { enabled: false };
3308
+ const controlPercentage = numberOrUndefined2(source.control_percentage ?? source.controlPercentage);
3309
+ const minControlSize = numberOrUndefined2(source.min_control_size ?? source.minControlSize);
3310
+ const experimentKey = stringValue(source.experiment_key ?? source.experimentKey) ?? (gtmCampaignId ? `gtm_campaign:${gtmCampaignId}:learning_holdout` : `campaign_builder_agent:${hashString(campaignName)}:learning_holdout`);
3311
+ return {
3312
+ enabled: true,
3313
+ controlPercentage: controlPercentage ?? 0.2,
3314
+ minControlSize: minControlSize ?? 50,
3315
+ experimentKey,
3316
+ sourceCampaignId: stringValue(source.source_campaign_id ?? source.sourceCampaignId) ?? gtmCampaignId,
3317
+ primaryMetric: stringValue(source.primary_metric ?? source.primaryMetric) ?? "positive_reply_rate"
3318
+ };
3319
+ }
3092
3320
  function createBuildRequest(request, defaults, targetCount, campaignName, scopeBuildArgs, routes = [], memory, scopeBrainPreflight) {
3093
3321
  const buildRequest = {
3094
3322
  name: campaignName,
@@ -3113,10 +3341,19 @@ function createBuildRequest(request, defaults, targetCount, campaignName, scopeB
3113
3341
  qualification: defaults.qualification,
3114
3342
  copy: defaults.copy,
3115
3343
  delivery: defaults.delivery,
3116
- enhancers: enhancerConfig(request)
3344
+ enhancers: enhancerConfig(request),
3345
+ sourceRouting: request.sourceRouting ?? {
3346
+ mode: looksLikeLocalLeadCampaign(request) ? "local_leads" : "auto",
3347
+ fillPolicy: "aggressive"
3348
+ }
3117
3349
  };
3118
3350
  const scoped = asRecord(scopeBuildArgs);
3119
3351
  buildRequest.gtmCampaignId = request.gtmCampaignId ?? stringValue(scoped.gtm_campaign_id ?? scoped.gtmCampaignId);
3352
+ buildRequest.learningHoldout = normalizeCampaignBuilderLearningHoldout(
3353
+ request.learningHoldout ?? scoped.learning_holdout ?? scoped.learningHoldout,
3354
+ buildRequest.gtmCampaignId,
3355
+ campaignName
3356
+ );
3120
3357
  if (typeof scoped.name === "string") buildRequest.name = scoped.name;
3121
3358
  if (typeof scoped.prompt === "string") buildRequest.prompt = scoped.prompt;
3122
3359
  if (typeof scoped.target_count === "number") buildRequest.targetCount = scoped.target_count;
@@ -3191,13 +3428,137 @@ function createBrainPreflight(request, buildRequest, routes, memory) {
3191
3428
  target_icp: targetIcp,
3192
3429
  provider_chain: providerChain,
3193
3430
  lead_source: providerChain[0] ?? "signaliz",
3431
+ learning_holdout: buildRequest.learningHoldout,
3194
3432
  tool_sequence: [
3195
3433
  { tool: "gtm_brain_learning_cycle_plan", purpose: "Plan the Brain learning loop for this campaign.", arguments: learningArgs },
3196
3434
  { tool: "gtm_brain_seed_defaults", purpose: "Seed campaign defaults from workspace memory and safe aggregate patterns.", arguments: defaultsArgs },
3197
3435
  { tool: "gtm_brain_delivery_risk", purpose: "Check deliverability risk before launch or external writes.", arguments: riskArgs }
3198
- ]
3436
+ ],
3437
+ learn_back_plan: createCampaignBuilderLearnBackPlan(request, buildRequest)
3438
+ };
3439
+ }
3440
+ function createCampaignBuilderLearnBackPlan(request, buildRequest) {
3441
+ const instantlyFeedbackContext = campaignBuilderProviderFeedbackContext(request, buildRequest, "instantly");
3442
+ const postBuildSequence = [
3443
+ {
3444
+ tool: "get_campaign_build_status",
3445
+ approval_boundary: "read_only",
3446
+ arguments_template: { campaign_build_id: "<build_campaign.campaign_build_id>" },
3447
+ reason: "Wait for the Campaign Builder run to finish before deriving memory."
3448
+ },
3449
+ {
3450
+ tool: "gtm_campaign_build_backfill_preview",
3451
+ approval_boundary: "read_only",
3452
+ arguments_template: { campaign_build_id: "<build_campaign.campaign_build_id>", create_memory: true },
3453
+ reason: "Preview the provider-neutral Kernel import payload without writing rows."
3454
+ },
3455
+ {
3456
+ tool: "gtm_campaign_build_backfill_run",
3457
+ approval_boundary: "dry_run",
3458
+ arguments_template: { campaign_build_ids: ["<build_campaign.campaign_build_id>"], dry_run: true, create_memory: true, run_brain_cycle: false },
3459
+ reason: "Dry-run the Kernel history import before creating campaign memory."
3460
+ },
3461
+ {
3462
+ tool: "gtm_campaign_build_backfill_run",
3463
+ approval_boundary: "explicit_write_approval",
3464
+ arguments_template: { campaign_build_ids: ["<build_campaign.campaign_build_id>"], dry_run: false, create_memory: true, run_brain_cycle: true, brain_cycle_write_mode: "dry_run" },
3465
+ reason: "After review, queue the Kernel history import; Brain learning remains dry-run unless separately approved."
3466
+ }
3467
+ ];
3468
+ if (instantlyFeedbackContext) {
3469
+ postBuildSequence.push({
3470
+ tool: "gtm_instantly_feedback_pull",
3471
+ approval_boundary: "dry_run_first_explicit_write_approval",
3472
+ arguments_template: {
3473
+ campaign_id: buildRequest.gtmCampaignId || "<gtm_campaign_id_after_commit>",
3474
+ campaign_build_id: "<build_campaign.campaign_build_id>",
3475
+ source: "emails",
3476
+ email_type: "received",
3477
+ ...instantlyFeedbackContext,
3478
+ dry_run: true,
3479
+ create_memory: true,
3480
+ write_routine_outcomes: true,
3481
+ run_brain_cycle: true,
3482
+ brain_cycle_write_mode: "dry_run"
3483
+ },
3484
+ reason: "After Instantly delivery has live outcomes, pull reply and bounce history into private feedback memory before Brain learning."
3485
+ });
3486
+ }
3487
+ postBuildSequence.push(
3488
+ {
3489
+ tool: "gtm_campaign_learning_status",
3490
+ approval_boundary: "read_only",
3491
+ arguments_template: {
3492
+ campaign_id: buildRequest.gtmCampaignId || "<gtm_campaign_id_after_commit>",
3493
+ campaign_build_id: "<build_campaign.campaign_build_id>",
3494
+ write_mode: "dry_run"
3495
+ },
3496
+ reason: "Confirm feedback, memory, and Brain learning readiness after the import."
3497
+ },
3498
+ {
3499
+ tool: "gtm_memory_search",
3500
+ approval_boundary: "read_only",
3501
+ arguments_template: {
3502
+ query: request.goal,
3503
+ ...buildRequest.gtmCampaignId ? { campaign_id: buildRequest.gtmCampaignId } : {},
3504
+ limit: 10
3505
+ },
3506
+ reason: "Verify the new private memory is retrievable for future campaign planning."
3507
+ },
3508
+ {
3509
+ tool: "gtm_brain_learning_cycle_plan",
3510
+ approval_boundary: "read_only",
3511
+ arguments_template: {
3512
+ ...buildRequest.gtmCampaignId ? { campaign_id: buildRequest.gtmCampaignId } : {},
3513
+ include_memory: true,
3514
+ write_mode: "dry_run"
3515
+ },
3516
+ reason: "Plan the Brain phases from the imported outcomes before any pattern writes."
3517
+ }
3518
+ );
3519
+ return {
3520
+ source_tool: "campaign-builder-agent",
3521
+ gtm_campaign_id: buildRequest.gtmCampaignId ?? null,
3522
+ trigger: "After build_campaign returns a campaign_build_id and the build reaches a terminal reviewed state.",
3523
+ build_result_placeholders: {
3524
+ campaign_build_id: "<build_campaign.campaign_build_id>"
3525
+ },
3526
+ memory_write_policy: {
3527
+ target_tables: ["gtm_campaigns", "gtm_campaign_provider_links", "gtm_campaign_feedback_events", "gtm_memory_items", "gtm_brain_patterns"],
3528
+ gtm_memory_items: {
3529
+ shareable: false,
3530
+ allowed_redaction_states: ["redacted", "abstracted"],
3531
+ raw_private_fields_withheld: ["reply_text", "copy_body", "lead_email", "lead_domain", "company_domain", "provider_campaign_id", "provider_payload", "webhook_url", "secret"]
3532
+ },
3533
+ gtm_brain_patterns: "Workspace-scoped by default. Global promotion requires abstracted_only=true, shared opt-in, and privacy thresholds."
3534
+ },
3535
+ post_build_sequence: postBuildSequence.map((item, index) => ({ step: index + 1, ...item })),
3536
+ approval_policy: "This agent plan never writes learn-back rows. Backfill and provider-feedback writes require dry_run=false after review, and Brain writes require their own approved learning-cycle run.",
3537
+ privacy_policy: "Learn-back memory is workspace-private. Raw replies, copy bodies, leads, emails, domains, provider payloads, webhook URLs, secrets, private source paths, and raw client labels are withheld from memory rows and agent-visible outputs."
3199
3538
  };
3200
3539
  }
3540
+ function campaignBuilderProviderFeedbackContext(request, buildRequest, provider) {
3541
+ const normalizedProvider = String(provider).toLowerCase();
3542
+ const routes = [...request.integrations ?? [], ...routesFromCustomerTools(request.customerTools ?? [])];
3543
+ const providerRoute = routes.find((route) => String(route.provider).toLowerCase() === normalizedProvider);
3544
+ const routeConfig = asRecord(providerRoute?.config);
3545
+ const deliveryConfig = asRecord(buildRequest.delivery?.destinationConfig);
3546
+ const hasProviderContext = [
3547
+ ...request.preferredProviders ?? [],
3548
+ ...routes.map((route) => route.provider),
3549
+ stringValue(deliveryConfig.provider),
3550
+ stringValue(deliveryConfig.provider_id ?? deliveryConfig.providerId),
3551
+ stringValue(deliveryConfig.destination_provider ?? deliveryConfig.destinationProvider),
3552
+ stringValue(deliveryConfig.sink ?? deliveryConfig.sink_type ?? deliveryConfig.sinkType)
3553
+ ].some((value) => String(value).toLowerCase() === normalizedProvider);
3554
+ if (!hasProviderContext) return void 0;
3555
+ return compact({
3556
+ provider_link_id: stringValue(routeConfig.provider_link_id ?? routeConfig.providerLinkId ?? deliveryConfig.provider_link_id ?? deliveryConfig.providerLinkId),
3557
+ provider_campaign_id: stringValue(routeConfig.provider_campaign_id ?? routeConfig.providerCampaignId ?? routeConfig.instantly_campaign_id ?? routeConfig.instantlyCampaignId ?? deliveryConfig.provider_campaign_id ?? deliveryConfig.providerCampaignId ?? deliveryConfig.instantly_campaign_id ?? deliveryConfig.instantlyCampaignId),
3558
+ integration_id: stringValue(routeConfig.integration_id ?? routeConfig.integrationId ?? deliveryConfig.integration_id ?? deliveryConfig.integrationId),
3559
+ instantly_profile: stringValue(routeConfig.instantly_profile ?? routeConfig.instantlyProfile ?? routeConfig.profile ?? deliveryConfig.instantly_profile ?? deliveryConfig.instantlyProfile ?? deliveryConfig.profile)
3560
+ });
3561
+ }
3201
3562
  function gtmLayersForBuilderLayer(layer) {
3202
3563
  const map = {
3203
3564
  workspace_context: ["customer_data"],
@@ -3602,6 +3963,7 @@ function buildFallbackPlanSnapshot(plan) {
3602
3963
  target_count: plan.targetCount,
3603
3964
  approvals: plan.approvals,
3604
3965
  brain_preflight: plan.brainPreflight,
3966
+ campaign_strategy_context: plan.buildRequest.campaignStrategyContext,
3605
3967
  operating_playbooks: plan.operatingPlaybooks
3606
3968
  });
3607
3969
  }
@@ -3734,6 +4096,17 @@ function buildCampaignArgs(request) {
3734
4096
  if (request.dedupKeys) args.dedup_keys = request.dedupKeys;
3735
4097
  if (request.brainPreflight) args.brain_preflight = request.brainPreflight;
3736
4098
  if (request.brainDefaults) args.brain_defaults = request.brainDefaults;
4099
+ if (request.learningHoldout) {
4100
+ args.learning_holdout = compact({
4101
+ enabled: request.learningHoldout.enabled,
4102
+ control_percentage: request.learningHoldout.controlPercentage,
4103
+ min_control_size: request.learningHoldout.minControlSize,
4104
+ experiment_key: request.learningHoldout.experimentKey,
4105
+ source_campaign_id: request.learningHoldout.sourceCampaignId,
4106
+ primary_metric: request.learningHoldout.primaryMetric
4107
+ });
4108
+ }
4109
+ if (request.campaignStrategyContext) args.campaign_strategy_context = request.campaignStrategyContext;
3737
4110
  if (request.deliveryRisk) args.delivery_risk = request.deliveryRisk;
3738
4111
  if (request.icp) {
3739
4112
  args.icp = compact({
@@ -3753,6 +4126,15 @@ function buildCampaignArgs(request) {
3753
4126
  model: request.policy.model
3754
4127
  });
3755
4128
  }
4129
+ if (request.sourceRouting) {
4130
+ args.source_routing = compact({
4131
+ mode: request.sourceRouting.mode,
4132
+ fill_policy: request.sourceRouting.fillPolicy,
4133
+ shard_size: request.sourceRouting.shardSize,
4134
+ max_source_shard_size: request.sourceRouting.maxSourceShardSize,
4135
+ max_local_source_shard_size: request.sourceRouting.maxLocalSourceShardSize
4136
+ });
4137
+ }
3756
4138
  if (request.signals) {
3757
4139
  args.signals = compact({
3758
4140
  enabled: request.signals.enabled,
@@ -3820,11 +4202,13 @@ function buildExecutionPrepareArgs(request, dryRun, confirmSpend) {
3820
4202
  delivery_risk: buildArgs2.delivery_risk,
3821
4203
  dedup_keys: buildArgs2.dedup_keys,
3822
4204
  policy: buildArgs2.policy,
4205
+ learning_holdout: buildArgs2.learning_holdout,
3823
4206
  signals: buildArgs2.signals,
3824
4207
  qualification: buildArgs2.qualification,
3825
4208
  copy: buildArgs2.copy,
3826
4209
  delivery: buildArgs2.delivery,
3827
- enhancers: buildArgs2.enhancers
4210
+ enhancers: buildArgs2.enhancers,
4211
+ source_routing: buildArgs2.source_routing
3828
4212
  });
3829
4213
  }
3830
4214
  function buildDeliveryApprovalArgs(request) {
@@ -3853,10 +4237,145 @@ function mapBuildResult(data, dryRun) {
3853
4237
  targetLimitReason: data.target_limit_reason,
3854
4238
  maxSupportedTargetCount: data.max_supported_target_count,
3855
4239
  brainContext: data.brain_context ?? {},
4240
+ learningHoldout: data.learning_holdout ?? {},
3856
4241
  providerRoute: mapProviderRoute2(data)
3857
4242
  };
3858
4243
  }
4244
+ var CAMPAIGN_BUILDER_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "canceled", "cancelled"]);
4245
+ var CAMPAIGN_BUILDER_BLOCKED_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["failed", "canceled", "cancelled"]);
4246
+ function mapAgentCampaignPhaseHealth(value) {
4247
+ if (!Array.isArray(value)) return [];
4248
+ return value.map((phase) => {
4249
+ const record = asRecord(phase);
4250
+ return {
4251
+ phase: stringValue(record.phase) ?? "unknown",
4252
+ status: stringValue(record.status) ?? "unknown",
4253
+ recordsProcessed: numberOrNull2(record.records_processed ?? record.recordsProcessed),
4254
+ recordsSucceeded: numberOrNull2(record.records_succeeded ?? record.recordsSucceeded),
4255
+ recordsFailed: numberOrNull2(record.records_failed ?? record.recordsFailed),
4256
+ heartbeatAgeSeconds: numberOrNull2(record.heartbeat_age_seconds ?? record.heartbeatAgeSeconds ?? record.phase_age_seconds)
4257
+ };
4258
+ });
4259
+ }
4260
+ function deriveCampaignBuildTerminalState(input) {
4261
+ const status = input.status.toLowerCase();
4262
+ const heartbeatAgeSeconds = input.phaseAgeSeconds;
4263
+ const stale = input.staleRunningPhase === true;
4264
+ const safeWatchAction = input.triggerRunId ? `signaliz ops run-status ${input.triggerRunId} --watch` : "signaliz campaign-agent status <campaign_build_id>";
4265
+ if (CAMPAIGN_BUILDER_BLOCKED_TERMINAL_STATUSES.has(status)) {
4266
+ return {
4267
+ kind: "blocked",
4268
+ label: "Blocked terminal state",
4269
+ reviewable: true,
4270
+ terminal: true,
4271
+ stale: false,
4272
+ phase: input.phase,
4273
+ phaseStatus: input.phaseStatus,
4274
+ heartbeatAgeSeconds,
4275
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4276
+ terminalReason: input.terminalReason,
4277
+ staleReason: null,
4278
+ safeNextAction: input.nextAction || "Review errors and rerun only after the blocker is resolved."
4279
+ };
4280
+ }
4281
+ if (status === "pending_approval") {
4282
+ return {
4283
+ kind: "pending_approval",
4284
+ label: "Pending delivery approval",
4285
+ reviewable: true,
4286
+ terminal: false,
4287
+ stale: false,
4288
+ phase: input.phase,
4289
+ phaseStatus: input.phaseStatus,
4290
+ heartbeatAgeSeconds,
4291
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4292
+ terminalReason: input.terminalReason,
4293
+ staleReason: null,
4294
+ safeNextAction: input.approvalAction || input.nextAction || "Review rows and approve delivery only after the approval packet passes."
4295
+ };
4296
+ }
4297
+ if (CAMPAIGN_BUILDER_TERMINAL_STATUSES.has(status)) {
4298
+ return {
4299
+ kind: "terminal",
4300
+ label: "Terminal and reviewable",
4301
+ reviewable: true,
4302
+ terminal: true,
4303
+ stale: false,
4304
+ phase: input.phase,
4305
+ phaseStatus: input.phaseStatus,
4306
+ heartbeatAgeSeconds,
4307
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4308
+ terminalReason: input.terminalReason,
4309
+ staleReason: null,
4310
+ safeNextAction: input.nextAction || "Review rows, artifacts, scorecard, and approval locks before delivery."
4311
+ };
4312
+ }
4313
+ if (status === "running" || status === "queued" || status === "processing") {
4314
+ if (stale) {
4315
+ return {
4316
+ kind: "running_stale",
4317
+ label: "Running but stale",
4318
+ reviewable: false,
4319
+ terminal: false,
4320
+ stale: true,
4321
+ phase: input.phase,
4322
+ phaseStatus: input.phaseStatus,
4323
+ heartbeatAgeSeconds,
4324
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4325
+ terminalReason: null,
4326
+ staleReason: input.staleReason || "The active phase heartbeat is stale.",
4327
+ safeNextAction: input.nextAction || safeWatchAction
4328
+ };
4329
+ }
4330
+ return {
4331
+ kind: "running_healthy",
4332
+ label: "Running and healthy",
4333
+ reviewable: false,
4334
+ terminal: false,
4335
+ stale: false,
4336
+ phase: input.phase,
4337
+ phaseStatus: input.phaseStatus,
4338
+ heartbeatAgeSeconds,
4339
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4340
+ terminalReason: null,
4341
+ staleReason: null,
4342
+ safeNextAction: input.nextAction || safeWatchAction
4343
+ };
4344
+ }
4345
+ return {
4346
+ kind: "unknown",
4347
+ label: "Unknown build state",
4348
+ reviewable: false,
4349
+ terminal: false,
4350
+ stale,
4351
+ phase: input.phase,
4352
+ phaseStatus: input.phaseStatus,
4353
+ heartbeatAgeSeconds,
4354
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4355
+ terminalReason: input.terminalReason,
4356
+ staleReason: input.staleReason,
4357
+ safeNextAction: input.nextAction || "Read campaign build status again before making a launch or delivery decision."
4358
+ };
4359
+ }
3859
4360
  function mapAgentCampaignBuildStatus(data) {
4361
+ const customerRowCounts = mapAgentCustomerRowCounts(data.customer_row_counts);
4362
+ const phaseHealth = mapAgentCampaignPhaseHealth(data.phases);
4363
+ const terminalReason = stringValue(data.terminal_reason) ?? customerRowCounts?.terminalReason ?? null;
4364
+ const staleReason = stringValue(data.stale_reason) ?? stringValue(asRecord(data.diagnostics).stale_reason) ?? null;
4365
+ const nextPollAfterSeconds = numberOrNull2(data.next_poll_after_seconds ?? data.nextPollAfterSeconds);
4366
+ const terminalState = deriveCampaignBuildTerminalState({
4367
+ status: stringValue(data.status) ?? "unknown",
4368
+ phase: stringValue(data.current_phase) ?? null,
4369
+ phaseStatus: stringValue(data.current_phase_status) ?? null,
4370
+ staleRunningPhase: data.stale_running_phase === true,
4371
+ phaseAgeSeconds: numberOrNull2(data.phase_age_seconds),
4372
+ nextPollAfterSeconds,
4373
+ terminalReason,
4374
+ staleReason,
4375
+ nextAction: formatAgentNextAction(data.next_action),
4376
+ approvalAction: formatAgentNextAction(data.approval_action),
4377
+ triggerRunId: stringValue(data.trigger_run_id) ?? null
4378
+ });
3860
4379
  return {
3861
4380
  campaignBuildId: stringValue(data.campaign_build_id) ?? "",
3862
4381
  campaignId: stringValue(data.campaign_id) ?? null,
@@ -3873,17 +4392,68 @@ function mapAgentCampaignBuildStatus(data) {
3873
4392
  warnings: diagnosticMessages2(data.warnings),
3874
4393
  errors: diagnosticMessages2(data.errors),
3875
4394
  artifactCount: numberOrUndefined2(data.artifact_count) ?? 0,
4395
+ artifactDownloads: Array.isArray(data.artifact_downloads) ? data.artifact_downloads.map(mapAgentCampaignArtifactDownload) : [],
4396
+ customerRowCounts,
4397
+ phaseAttemptTotals: nullableRecord(data.phase_attempt_totals) ?? void 0,
3876
4398
  providerRoute: mapProviderRoute2(data),
3877
4399
  triggerRunId: stringValue(data.trigger_run_id) ?? null,
3878
4400
  staleRunningPhase: data.stale_running_phase === true,
3879
4401
  phaseAgeSeconds: numberOrNull2(data.phase_age_seconds),
4402
+ nextPollAfterSeconds,
4403
+ terminalReason,
4404
+ staleReason,
4405
+ safeNextAction: terminalState.safeNextAction,
4406
+ terminalState,
4407
+ phaseHealth,
3880
4408
  diagnostics: nullableRecord(data.diagnostics) ?? {},
3881
4409
  nextAction: formatAgentNextAction(data.next_action),
4410
+ approvalAction: formatAgentNextAction(data.approval_action),
3882
4411
  createdAt: stringValue(data.created_at) ?? "",
3883
4412
  updatedAt: stringValue(data.updated_at) ?? "",
3884
4413
  completedAt: stringValue(data.completed_at) ?? null
3885
4414
  };
3886
4415
  }
4416
+ function mapAgentCampaignArtifactDownload(data) {
4417
+ return {
4418
+ id: stringValue(data.id) ?? "",
4419
+ artifactType: stringValue(data.artifact_type) ?? null,
4420
+ format: stringValue(data.format) ?? null,
4421
+ rowCount: numberOrUndefined2(data.row_count) ?? 0,
4422
+ signedUrl: stringValue(data.signed_url) ?? null,
4423
+ downloadUrl: stringValue(data.download_url) ?? null,
4424
+ manifestUrl: stringValue(data.manifest_url) ?? null,
4425
+ expiresAt: stringValue(data.expires_at) ?? null
4426
+ };
4427
+ }
4428
+ function mapAgentCustomerRowCounts(value) {
4429
+ const record = nullableRecord(value);
4430
+ if (!record) return void 0;
4431
+ return {
4432
+ requestedTarget: numberOrNull2(record.requested_target),
4433
+ acquiredRows: numberOrUndefined2(record.acquired_rows),
4434
+ acceptedRows: numberOrUndefined2(record.accepted_rows),
4435
+ usableRows: numberOrUndefined2(record.usable_rows),
4436
+ copiedRows: numberOrUndefined2(record.copied_rows),
4437
+ approvalReadyRows: numberOrUndefined2(record.approval_ready_rows),
4438
+ qaReadyRows: numberOrUndefined2(record.qa_ready_rows),
4439
+ deliveredRows: numberOrUndefined2(record.delivered_rows),
4440
+ disqualifiedRows: numberOrUndefined2(record.disqualified_rows),
4441
+ reviewableRows: numberOrUndefined2(record.reviewable_rows),
4442
+ rowFailures: numberOrUndefined2(record.row_failures),
4443
+ acceptedShortfall: numberOrNull2(record.accepted_shortfall),
4444
+ usableShortfall: numberOrNull2(record.usable_shortfall),
4445
+ qaReadyShortfall: numberOrNull2(record.qa_ready_shortfall),
4446
+ deliveryShortfall: numberOrNull2(record.delivery_shortfall),
4447
+ fillRatio: numberOrNull2(record.fill_ratio),
4448
+ qaReadyFillRatio: numberOrNull2(record.qa_ready_fill_ratio),
4449
+ deliveredFillRatio: numberOrNull2(record.delivered_fill_ratio),
4450
+ terminalReason: stringValue(record.terminal_reason) ?? null,
4451
+ acceptedShortfallReason: stringValue(record.accepted_shortfall_reason) ?? null,
4452
+ usableShortfallReason: stringValue(record.usable_shortfall_reason) ?? null,
4453
+ qaReadyShortfallReason: stringValue(record.qa_ready_shortfall_reason) ?? null,
4454
+ deliveryShortfallReason: stringValue(record.delivery_shortfall_reason) ?? null
4455
+ };
4456
+ }
3887
4457
  function mapAgentCampaignRowsResult(data) {
3888
4458
  const rows = Array.isArray(data.rows) ? data.rows : [];
3889
4459
  return {
@@ -3911,74 +4481,296 @@ function mapAgentCampaignArtifact(data) {
3911
4481
  artifactType: stringValue(data.artifact_type) ?? "",
3912
4482
  destination: stringValue(data.destination) ?? "",
3913
4483
  storagePath: stringValue(data.storage_path) ?? "",
4484
+ format: stringValue(data.format) ?? null,
3914
4485
  signedUrl: stringValue(data.signed_url) ?? null,
4486
+ downloadUrl: stringValue(data.download_url) ?? null,
4487
+ manifestUrl: stringValue(data.manifest_url) ?? null,
4488
+ expiresAt: stringValue(data.expires_at) ?? null,
3915
4489
  rowCount: numberOrUndefined2(data.row_count) ?? 0,
3916
4490
  checksum: stringValue(data.checksum) ?? null,
3917
4491
  metadata: nullableRecord(data.metadata) ?? {},
3918
4492
  createdAt: stringValue(data.created_at) ?? ""
3919
4493
  };
3920
4494
  }
4495
+ var NORTH_STAR_BANNED_COPY_PHRASES = [
4496
+ "i have been following",
4497
+ "i saw that",
4498
+ "would you be opposed",
4499
+ "unlock new opportunities",
4500
+ "significant success",
4501
+ "we understand",
4502
+ "hope this finds you well"
4503
+ ];
4504
+ function campaignReviewGate(id, label, status, detail) {
4505
+ return {
4506
+ id,
4507
+ label,
4508
+ status,
4509
+ passed: status !== "blocked",
4510
+ detail
4511
+ };
4512
+ }
4513
+ function northStarMetric(id, label, status, detail, value, target) {
4514
+ return {
4515
+ id,
4516
+ label,
4517
+ status,
4518
+ detail,
4519
+ ...value !== void 0 ? { value } : {},
4520
+ ...target !== void 0 ? { target } : {}
4521
+ };
4522
+ }
4523
+ function createCampaignBuilderNorthStarScorecard(status, rows, artifacts, terminalState) {
4524
+ if (!terminalState.reviewable) {
4525
+ const pendingMetric = northStarMetric(
4526
+ "terminal_state",
4527
+ "Terminal-state contract",
4528
+ terminalState.kind === "running_stale" ? "blocked" : "pending",
4529
+ terminalState.kind === "running_stale" ? `Build is stale in ${terminalState.phase || "unknown phase"}; do not grade output yet.` : `Build is ${terminalState.label.toLowerCase()}; wait for terminal or approval state before grading.`,
4530
+ terminalState.kind,
4531
+ "terminal or pending_approval"
4532
+ );
4533
+ return {
4534
+ version: "campaign-builder-north-star.v1",
4535
+ status: pendingMetric.status,
4536
+ score: null,
4537
+ moatState: "scaffolding_ready",
4538
+ metrics: [pendingMetric],
4539
+ blockers: pendingMetric.status === "blocked" ? [pendingMetric.detail] : [],
4540
+ warnings: pendingMetric.status === "pending" ? [pendingMetric.detail] : [],
4541
+ nextAction: terminalState.safeNextAction
4542
+ };
4543
+ }
4544
+ const sampledRows = rows.rows.length;
4545
+ const rowData = rows.rows.map((row) => asRecord(row.data));
4546
+ const requestedTarget = status.customerRowCounts?.requestedTarget ?? status.recordsTotal ?? rows.count;
4547
+ const finalRows = status.customerRowCounts?.deliveredRows ?? status.customerRowCounts?.qaReadyRows ?? status.customerRowCounts?.copiedRows ?? status.customerRowCounts?.acceptedRows ?? rows.count;
4548
+ const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
4549
+ const verifiedEmailRows = rowData.filter(campaignReviewDataHasVerifiedEmail).length;
4550
+ const uniqueEmails = new Set(rowData.map(campaignReviewDataEmail).filter(Boolean)).size;
4551
+ const uniqueDomains = new Set(rowData.map(campaignReviewDataDomain).filter(Boolean)).size;
4552
+ const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
4553
+ const copyQaIssuesByRow = rowData.map(campaignReviewDataCopyQaIssues).filter((issues) => issues.length > 0);
4554
+ const copyQaIssueCounts = countCampaignReviewIssues(copyQaIssuesByRow);
4555
+ const signalBackedRows = rowData.filter(campaignReviewDataHasSignalBasis).length;
4556
+ const noSupportedSignalRows = rowData.filter(campaignReviewDataHasNoSupportedSignal).length;
4557
+ const unsupportedSignalClaims = rowData.filter(campaignReviewDataHasUnsupportedSignalClaim).length;
4558
+ const approvalLockedRows = rowData.filter(campaignReviewDataHasApprovalLock).length;
4559
+ const holdoutRows = rowData.filter(campaignReviewDataHasHoldoutAttribution).length;
4560
+ const artifactCount = artifacts.length || status.artifactCount || 0;
4561
+ const deliveredRows = status.customerRowCounts?.deliveredRows ?? 0;
4562
+ const feedbackReady = campaignReviewStatusHasFeedbackReadiness(status);
4563
+ const feedbackRequiredForApproval = terminalState.kind === "pending_approval";
4564
+ const moat = campaignReviewMoatState(status, holdoutRows);
4565
+ const metrics = [
4566
+ northStarMetric(
4567
+ "terminal_state",
4568
+ "Terminal-state contract",
4569
+ terminalState.kind === "blocked" ? "blocked" : "pass",
4570
+ terminalState.label,
4571
+ terminalState.kind,
4572
+ "terminal or pending_approval"
4573
+ ),
4574
+ northStarMetric(
4575
+ "target_fill",
4576
+ "Target fill",
4577
+ requestedTarget && finalRows < requestedTarget ? "review" : "pass",
4578
+ requestedTarget ? `${finalRows || 0}/${requestedTarget} final or reviewable row(s) are accounted for.` : "No requested target was reported; using sampled rows for review.",
4579
+ finalRows || sampledRows,
4580
+ requestedTarget || sampledRows
4581
+ ),
4582
+ northStarMetric(
4583
+ "unique_emails",
4584
+ "Unique emails",
4585
+ sampledRows === 0 ? "blocked" : uniqueEmails === rowsWithEmail ? "pass" : "blocked",
4586
+ sampledRows === 0 ? "No sampled rows were available for email uniqueness review." : `${uniqueEmails}/${rowsWithEmail} sampled email(s) are unique.`,
4587
+ uniqueEmails,
4588
+ rowsWithEmail
4589
+ ),
4590
+ northStarMetric(
4591
+ "unique_domains",
4592
+ "Unique domains",
4593
+ sampledRows === 0 ? "blocked" : uniqueDomains === sampledRows ? "pass" : "review",
4594
+ sampledRows === 0 ? "No sampled rows were available for domain uniqueness review." : `${uniqueDomains}/${sampledRows} sampled company domain(s) are unique.`,
4595
+ uniqueDomains,
4596
+ sampledRows
4597
+ ),
4598
+ northStarMetric(
4599
+ "verified_email_rows",
4600
+ "Verified email rows",
4601
+ sampledRows === 0 ? "blocked" : verifiedEmailRows === sampledRows ? "pass" : rowsWithEmail === sampledRows ? "review" : "blocked",
4602
+ sampledRows === 0 ? "No sampled rows were available for email verification review." : `${verifiedEmailRows}/${sampledRows} sampled row(s) carry verified-email evidence.`,
4603
+ verifiedEmailRows,
4604
+ sampledRows
4605
+ ),
4606
+ northStarMetric(
4607
+ "copy_completeness",
4608
+ "Copy completeness",
4609
+ sampledRows === 0 ? "blocked" : copyReadyRows === sampledRows ? "pass" : "blocked",
4610
+ sampledRows === 0 ? "No sampled rows were available for copy review." : `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.`,
4611
+ copyReadyRows,
4612
+ sampledRows
4613
+ ),
4614
+ northStarMetric(
4615
+ "copy_quality",
4616
+ "Copy QA",
4617
+ copyQaIssuesByRow.length === 0 ? "pass" : "blocked",
4618
+ copyQaIssuesByRow.length === 0 ? "No sampled copy QA blockers were detected." : `${copyQaIssuesByRow.length}/${sampledRows} sampled row(s) have copy QA issue(s): ${formatCampaignReviewIssueCounts(copyQaIssueCounts)}.`,
4619
+ copyQaIssuesByRow.length,
4620
+ 0
4621
+ ),
4622
+ northStarMetric(
4623
+ "signal_grounding",
4624
+ "Signal grounding",
4625
+ unsupportedSignalClaims > 0 ? "blocked" : signalBackedRows > 0 ? "pass" : "review",
4626
+ unsupportedSignalClaims > 0 ? `${unsupportedSignalClaims} sampled row(s) make unsupported signal claims.` : signalBackedRows > 0 ? `${signalBackedRows}/${sampledRows} sampled row(s) include supported signal or personalization basis.` : noSupportedSignalRows > 0 ? `${noSupportedSignalRows}/${sampledRows} sampled row(s) are explicitly marked no_supported_signal; copy should stay conservative.` : "No sampled rows include supported signal evidence; copy should stay conservative.",
4627
+ signalBackedRows,
4628
+ sampledRows
4629
+ ),
4630
+ northStarMetric(
4631
+ "approval_locks",
4632
+ "Approval locks",
4633
+ approvalLockedRows > 0 || deliveredRows === 0 ? "pass" : "review",
4634
+ approvalLockedRows > 0 ? `${approvalLockedRows}/${sampledRows} sampled row(s) show approval/export lock metadata.` : "Approval-lock metadata was not visible in sampled rows; preserve delivery approval gates.",
4635
+ approvalLockedRows,
4636
+ sampledRows
4637
+ ),
4638
+ northStarMetric(
4639
+ "export_send_safety",
4640
+ "Export/send safety",
4641
+ deliveredRows > 0 && status.status !== "completed" ? "blocked" : "pass",
4642
+ deliveredRows > 0 ? `${deliveredRows} row(s) are delivered; status is ${status.status}.` : "No delivered rows are reported before approval.",
4643
+ deliveredRows,
4644
+ 0
4645
+ ),
4646
+ northStarMetric(
4647
+ "holdout_attribution",
4648
+ "Holdout attribution",
4649
+ holdoutRows > 0 ? "pass" : "review",
4650
+ holdoutRows > 0 ? `${holdoutRows}/${sampledRows} sampled row(s) preserve holdout attribution.` : "No holdout attribution was visible in the sampled rows.",
4651
+ holdoutRows,
4652
+ sampledRows
4653
+ ),
4654
+ northStarMetric(
4655
+ "feedback_readiness",
4656
+ "Feedback readiness",
4657
+ feedbackReady ? "pass" : feedbackRequiredForApproval ? "blocked" : "review",
4658
+ feedbackReady ? "Feedback readiness is visible in status diagnostics." : feedbackRequiredForApproval ? "Feedback webhook or approved pull readiness is required before delivery approval." : "Feedback webhook or approved pull readiness was not visible; require it before real send approval.",
4659
+ feedbackReady,
4660
+ true
4661
+ ),
4662
+ northStarMetric(
4663
+ "causal_moat",
4664
+ "Causal moat",
4665
+ moat.state === "proved" ? "pass" : "review",
4666
+ moat.detail,
4667
+ moat.state,
4668
+ "proved"
4669
+ ),
4670
+ northStarMetric(
4671
+ "artifact_readback",
4672
+ "Artifact readback",
4673
+ artifactCount > 0 ? "pass" : "review",
4674
+ artifactCount > 0 ? `${artifactCount} artifact(s) are available for review.` : "No review artifact is available yet.",
4675
+ artifactCount,
4676
+ 1
4677
+ )
4678
+ ];
4679
+ const gradedMetrics = metrics.filter((metric) => metric.status !== "pending");
4680
+ const score = gradedMetrics.length === 0 ? null : Math.round(gradedMetrics.filter((metric) => metric.status === "pass").length / gradedMetrics.length * 100);
4681
+ const blockers = metrics.filter((metric) => metric.status === "blocked").map((metric) => metric.detail);
4682
+ const warnings = metrics.filter((metric) => metric.status === "review").map((metric) => metric.detail);
4683
+ const scorecardStatus = blockers.length > 0 ? "blocked" : warnings.length > 0 ? "review" : "pass";
4684
+ return {
4685
+ version: "campaign-builder-north-star.v1",
4686
+ status: scorecardStatus,
4687
+ score,
4688
+ moatState: moat.state,
4689
+ metrics,
4690
+ blockers,
4691
+ warnings,
4692
+ nextAction: scorecardStatus === "blocked" ? "Fix blocked North Star gates before approval or delivery." : terminalState.safeNextAction
4693
+ };
4694
+ }
3921
4695
  function createCampaignBuilderBuildReview(status, rows, artifacts) {
4696
+ const terminalState = status.terminalState ?? deriveCampaignBuildTerminalState({
4697
+ status: status.status,
4698
+ phase: status.currentPhase,
4699
+ phaseStatus: status.currentPhaseStatus,
4700
+ staleRunningPhase: status.staleRunningPhase === true,
4701
+ phaseAgeSeconds: status.phaseAgeSeconds ?? null,
4702
+ nextPollAfterSeconds: status.nextPollAfterSeconds ?? null,
4703
+ terminalReason: status.terminalReason ?? status.customerRowCounts?.terminalReason ?? null,
4704
+ staleReason: status.staleReason ?? null,
4705
+ nextAction: status.nextAction,
4706
+ approvalAction: status.approvalAction,
4707
+ triggerRunId: status.triggerRunId ?? null
4708
+ });
3922
4709
  const sampledRows = rows.rows.length;
3923
4710
  const availableRows = rows.count || sampledRows;
3924
4711
  const qualifiedRows = rows.rows.filter((row) => row.qualified !== false).length;
3925
4712
  const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
3926
4713
  const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
3927
4714
  const artifactCount = artifacts.length || status.artifactCount || 0;
4715
+ const northStarScorecard = createCampaignBuilderNorthStarScorecard(status, rows, artifacts, terminalState);
3928
4716
  const gates = [
3929
- {
3930
- id: "build_completed",
3931
- label: "Build completed",
3932
- passed: status.status === "completed",
3933
- detail: status.status === "completed" ? "The campaign build is completed." : `The campaign build is ${status.status}; review again after completion.`
3934
- },
3935
- {
3936
- id: "rows_available",
3937
- label: "Rows available",
3938
- passed: sampledRows > 0,
3939
- detail: sampledRows > 0 ? `${sampledRows} sampled row(s) are available for review.` : "No rows were returned for review."
3940
- },
3941
- {
3942
- id: "qualified_sample",
3943
- label: "Sample rows qualified",
3944
- passed: sampledRows > 0 && qualifiedRows === sampledRows,
3945
- detail: sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
3946
- },
3947
- {
3948
- id: "email_coverage",
3949
- label: "Email coverage",
3950
- passed: sampledRows > 0 && rowsWithEmail === sampledRows,
3951
- detail: sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
3952
- },
3953
- {
3954
- id: "artifact_available",
3955
- label: "Artifact available",
3956
- passed: artifactCount > 0,
3957
- detail: artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
3958
- },
3959
- {
3960
- id: "no_failed_rows",
3961
- label: "No failed rows",
3962
- passed: status.recordsFailed === 0,
3963
- detail: status.recordsFailed === 0 ? "The build reports zero failed rows." : `${status.recordsFailed} row(s) failed during the build.`
3964
- }
4717
+ campaignReviewGate(
4718
+ "build_reviewable",
4719
+ "Build reviewable",
4720
+ terminalState.kind === "running_healthy" ? "pending" : terminalState.kind === "running_stale" || terminalState.kind === "blocked" ? "blocked" : "pass",
4721
+ terminalState.label
4722
+ ),
4723
+ campaignReviewGate(
4724
+ "rows_available",
4725
+ "Rows available",
4726
+ sampledRows > 0 ? "pass" : terminalState.reviewable ? "blocked" : "pending",
4727
+ sampledRows > 0 ? `${sampledRows} sampled row(s) are available for review.` : terminalState.reviewable ? "No rows were returned for review." : "Rows are not graded until the build is reviewable."
4728
+ ),
4729
+ campaignReviewGate(
4730
+ "qualified_sample",
4731
+ "Sample rows qualified",
4732
+ sampledRows === 0 ? terminalState.reviewable ? "blocked" : "pending" : qualifiedRows === sampledRows ? "pass" : "blocked",
4733
+ sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
4734
+ ),
4735
+ campaignReviewGate(
4736
+ "email_coverage",
4737
+ "Email coverage",
4738
+ sampledRows === 0 ? terminalState.reviewable ? "blocked" : "pending" : rowsWithEmail === sampledRows ? "pass" : "blocked",
4739
+ sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
4740
+ ),
4741
+ campaignReviewGate(
4742
+ "artifact_available",
4743
+ "Artifact available",
4744
+ artifactCount > 0 ? "pass" : terminalState.reviewable ? "review" : "pending",
4745
+ artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
4746
+ ),
4747
+ campaignReviewGate(
4748
+ "no_failed_rows",
4749
+ "No failed rows",
4750
+ status.recordsFailed === 0 ? "pass" : "blocked",
4751
+ status.recordsFailed === 0 ? "The build reports zero failed rows." : `${status.recordsFailed} row(s) failed during the build.`
4752
+ )
3965
4753
  ];
3966
- const blockedReasons = gates.filter((gate) => !gate.passed).map((gate) => gate.detail);
4754
+ const blockedReasons = gates.filter((gate) => gate.status === "blocked" || !gate.passed).map((gate) => gate.detail);
3967
4755
  const warnings = [
3968
4756
  rows.hasMore ? "This review is based on a row sample; page through remaining rows before final approval." : void 0,
3969
4757
  sampledRows > 0 && copyReadyRows < sampledRows ? `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.` : void 0,
3970
4758
  ...status.warnings
3971
4759
  ].filter((item) => Boolean(item));
3972
- const readyForDeliveryApproval = blockedReasons.length === 0;
4760
+ const readyForDeliveryApproval = terminalState.reviewable && blockedReasons.length === 0 && northStarScorecard.status !== "blocked";
3973
4761
  return {
3974
4762
  campaignBuildId: status.campaignBuildId || rows.campaignBuildId,
3975
4763
  status,
3976
4764
  rows,
3977
4765
  artifacts,
3978
4766
  gates,
4767
+ northStarScorecard,
3979
4768
  summary: {
3980
4769
  status: status.status,
3981
4770
  phase: status.currentPhase,
4771
+ terminalState: terminalState.kind,
4772
+ scorecardStatus: northStarScorecard.status,
4773
+ scorecardScore: northStarScorecard.score,
3982
4774
  sampledRows,
3983
4775
  availableRows,
3984
4776
  qualifiedRows,
@@ -4032,6 +4824,184 @@ function campaignReviewRowHasCopy(row) {
4032
4824
  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)
4033
4825
  );
4034
4826
  }
4827
+ function campaignReviewDataEmail(data) {
4828
+ return stringValue(data.email) || stringValue(data.work_email) || stringValue(data.workEmail) || stringValue(asRecord(data.contact).email);
4829
+ }
4830
+ function campaignReviewDataDomain(data) {
4831
+ const domain = stringValue(data.company_domain) || stringValue(data.companyDomain) || stringValue(asRecord(data.company).domain);
4832
+ return domain?.toLowerCase();
4833
+ }
4834
+ function campaignReviewDataHasVerifiedEmail(data) {
4835
+ const status = String(data.email_status ?? data.emailStatus ?? asRecord(data.contact).email_status ?? "").toLowerCase();
4836
+ return data.email_verified === true || data.emailVerified === true || asRecord(data.contact).email_verified === true || ["valid", "validated", "verified"].includes(status);
4837
+ }
4838
+ function campaignReviewDataCopyParts(data) {
4839
+ const copy = asRecord(data.copy);
4840
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4841
+ return [
4842
+ stringValue(data.subject),
4843
+ stringValue(data.subject_line),
4844
+ stringValue(data.opener),
4845
+ stringValue(data.body),
4846
+ stringValue(data.cta),
4847
+ stringValue(copy.subject),
4848
+ stringValue(copy.opener),
4849
+ stringValue(copy.body),
4850
+ stringValue(copy.cta),
4851
+ stringValue(rawCopy.subject),
4852
+ stringValue(rawCopy.opener),
4853
+ stringValue(rawCopy.body),
4854
+ stringValue(rawCopy.cta)
4855
+ ].filter((part) => Boolean(part));
4856
+ }
4857
+ function campaignReviewDataHasBadGreeting(data) {
4858
+ return campaignReviewDataCopyParts(data).some(
4859
+ (part) => /\bhi\s*(?:,|\{\{|\[)|\bhello\s*(?:,|\{\{|\[)/i.test(part) || /\b(first_name|firstname|name)\b/i.test(part)
4860
+ );
4861
+ }
4862
+ function campaignReviewDataHasBannedPhrase(data) {
4863
+ const text = campaignReviewDataCopyParts(data).join(" ").toLowerCase();
4864
+ return NORTH_STAR_BANNED_COPY_PHRASES.some((phrase) => text.includes(phrase));
4865
+ }
4866
+ function campaignReviewDataSubject(data) {
4867
+ const copy = asRecord(data.copy);
4868
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4869
+ return stringValue(data.subject) || stringValue(data.subject_line) || stringValue(data.subjectLine) || stringValue(copy.subject) || stringValue(rawCopy.subject);
4870
+ }
4871
+ function campaignReviewDataCta(data) {
4872
+ const copy = asRecord(data.copy);
4873
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4874
+ return stringValue(data.cta) || stringValue(copy.cta) || stringValue(rawCopy.cta);
4875
+ }
4876
+ function countCampaignReviewIssues(issueRows) {
4877
+ return issueRows.flat().reduce((acc, issue) => {
4878
+ acc[issue] = (acc[issue] || 0) + 1;
4879
+ return acc;
4880
+ }, {});
4881
+ }
4882
+ function formatCampaignReviewIssueCounts(counts) {
4883
+ const entries = Object.entries(counts);
4884
+ return entries.length > 0 ? entries.map(([issue, count]) => `${issue}=${count}`).join(", ") : "none";
4885
+ }
4886
+ function campaignReviewDataCopyQaIssues(data) {
4887
+ const issues = /* @__PURE__ */ new Set();
4888
+ const copyParts = campaignReviewDataCopyParts(data);
4889
+ if (copyParts.length === 0) return [];
4890
+ if (campaignReviewDataHasBadGreeting(data)) issues.add("bad_greeting");
4891
+ if (campaignReviewDataHasBannedPhrase(data)) issues.add("banned_or_vague_phrase");
4892
+ if (campaignReviewDataHasUnsupportedSignalClaim(data)) issues.add("unsupported_signal_claim");
4893
+ if (campaignReviewDataPersonalizationBasis(data).length === 0) issues.add("missing_personalization_basis");
4894
+ if (!campaignReviewDataCta(data)) issues.add("missing_cta");
4895
+ const subject = campaignReviewDataSubject(data);
4896
+ if (subject && subject.length > 70) issues.add("overlong_subject");
4897
+ return [...issues];
4898
+ }
4899
+ function campaignReviewDataPersonalizationBasis(data) {
4900
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4901
+ const copy = asRecord(data.copy);
4902
+ const metadata = asRecord(data.metadata);
4903
+ const signalEvidence = asRecord(data.signal_evidence ?? metadata.signal_evidence ?? metadata.signalEvidence);
4904
+ return [
4905
+ ...arrayOfStrings(data.personalization_basis) ?? [],
4906
+ ...arrayOfStrings(data.personalizationBasis) ?? [],
4907
+ ...arrayOfStrings(copy.personalization_basis) ?? [],
4908
+ ...arrayOfStrings(rawCopy.personalization_basis) ?? [],
4909
+ ...arrayOfStrings(metadata.personalization_basis) ?? [],
4910
+ ...arrayOfStrings(metadata.personalizationBasis) ?? [],
4911
+ ...arrayOfStrings(signalEvidence.personalization_basis) ?? []
4912
+ ];
4913
+ }
4914
+ function campaignReviewDataHasNoSupportedSignal(data) {
4915
+ const metadata = asRecord(data.metadata);
4916
+ const copy = asRecord(data.copy);
4917
+ const state = [
4918
+ data.copy_signal_state,
4919
+ data.signal_basis,
4920
+ data.signal_type,
4921
+ metadata.copy_signal_state,
4922
+ metadata.signal_basis,
4923
+ metadata.signal_type,
4924
+ copy.copy_signal_state
4925
+ ].map((value) => stringValue(value)?.toLowerCase()).filter(Boolean);
4926
+ return data.no_supported_signal === true || metadata.no_supported_signal === true || state.includes("no_supported_signal");
4927
+ }
4928
+ function campaignReviewDataHasSignalBasis(data) {
4929
+ if (campaignReviewDataHasNoSupportedSignal(data)) return false;
4930
+ const metadata = asRecord(data.metadata);
4931
+ const signal = asRecord(data.signal);
4932
+ const signalEvidence = asRecord(data.signal_evidence ?? metadata.signal_evidence ?? metadata.signalEvidence);
4933
+ const basis = campaignReviewDataPersonalizationBasis(data);
4934
+ return Boolean(
4935
+ stringValue(data.signal_type) || stringValue(data.signalType) || stringValue(data.signal_summary) || stringValue(data.signalSummary) || stringValue(data.signal_source_url) || stringValue(data.signalSourceUrl) || stringValue(metadata.signal_type) || stringValue(metadata.signal_summary) || stringValue(metadata.signal_source_url) || stringValue(signal.type) || stringValue(signal.summary) || stringValue(signal.source_url) || stringValue(signalEvidence.signal_type) || stringValue(signalEvidence.summary) || stringValue(signalEvidence.source_url) || basis.some((item) => /\bsignal|hiring|funding|leadership|growth|launch|news|technology\b/i.test(item))
4936
+ );
4937
+ }
4938
+ function campaignReviewDataHasUnsupportedSignalClaim(data) {
4939
+ const copyText = campaignReviewDataCopyParts(data).join(" ").toLowerCase();
4940
+ const claimsSignal = /\b(hiring|funding|raised|launched|expanding|appointed|announced|recent news|new role|job posting)\b/.test(copyText);
4941
+ return claimsSignal && !campaignReviewDataHasSignalBasis(data);
4942
+ }
4943
+ function campaignReviewDataHasApprovalLock(data) {
4944
+ const metadata = asRecord(data.fit_metadata ?? data.metadata);
4945
+ const approval = String(data.approval_status ?? data.delivery_approval_status ?? metadata.approval_status ?? "").toLowerCase();
4946
+ const exportStatus = String(data.export_status ?? data.exportStatus ?? "").toLowerCase();
4947
+ return data.export_ready === false || data.exportReady === false || data.delivery_approved === false || data.deliveryApproved === false || ["pending", "pending_approval", "blocked", "approval_required", "held"].includes(approval) || ["pending", "blocked", "approval_required", "held"].includes(exportStatus);
4948
+ }
4949
+ function campaignReviewDataHasHoldoutAttribution(data) {
4950
+ const fitMetadata = asRecord(data.fit_metadata ?? data.fitMetadata);
4951
+ const metadata = asRecord(data.metadata);
4952
+ const holdout = asRecord(
4953
+ fitMetadata.learning_holdout ?? fitMetadata.learningHoldout ?? metadata.learning_holdout ?? metadata.learningHoldout ?? data.learning_holdout ?? data.learningHoldout
4954
+ );
4955
+ const experimentKey = stringValue(holdout.experiment_key ?? holdout.experimentKey ?? data.experiment_key ?? data.experimentKey);
4956
+ const cohort = stringValue(holdout.cohort_role ?? holdout.cohortRole ?? data.cohort_role ?? data.learning_cohort ?? data.learningCohort);
4957
+ const brainApplied = holdout.brain_applied ?? holdout.brainApplied ?? data.brain_applied ?? data.signaliz_brain_applied;
4958
+ return Boolean(experimentKey && cohort && brainApplied !== void 0);
4959
+ }
4960
+ function campaignReviewStatusHasFeedbackReadiness(status) {
4961
+ const diagnostics = asRecord(status.diagnostics);
4962
+ const feedbackReadiness = asRecord(diagnostics.feedback_readiness ?? diagnostics.feedbackReadiness);
4963
+ const feedbackSubstrate = asRecord(diagnostics.feedback_substrate ?? diagnostics.feedbackSubstrate);
4964
+ const evidenceCounts = asRecord(diagnostics.evidence_counts ?? diagnostics.evidenceCounts);
4965
+ return feedbackReadiness.ready === true || feedbackReadiness.webhook_configured === true || feedbackSubstrate.stage === "ready" || Number(evidenceCounts.feedback_events ?? 0) > 0;
4966
+ }
4967
+ function campaignReviewMoatState(status, holdoutRows) {
4968
+ const diagnostics = asRecord(status.diagnostics);
4969
+ const evidenceCounts = asRecord(diagnostics.evidence_counts ?? diagnostics.evidenceCounts);
4970
+ const embeddedHoldout = asRecord(diagnostics.embedded_holdout ?? diagnostics.embeddedHoldout);
4971
+ const holdoutLift = asRecord(diagnostics.holdout_lift ?? diagnostics.holdoutLift ?? diagnostics.holdout_measurement ?? diagnostics.holdoutMeasurement);
4972
+ const controlSample = Math.max(
4973
+ Number(evidenceCounts.holdout_control_sample_size ?? 0),
4974
+ Number(evidenceCounts.embedded_holdout_control_sample ?? 0),
4975
+ Number(embeddedHoldout.control_sample_size ?? 0)
4976
+ );
4977
+ const learnedSample = Math.max(
4978
+ Number(evidenceCounts.holdout_learned_sample_size ?? 0),
4979
+ Number(evidenceCounts.embedded_holdout_learned_sample ?? 0),
4980
+ Number(embeddedHoldout.learned_sample_size ?? 0)
4981
+ );
4982
+ const feedbackEvents = Math.max(
4983
+ Number(evidenceCounts.feedback_events ?? 0),
4984
+ Number(evidenceCounts.campaign_build_feedback_events ?? 0),
4985
+ Number(evidenceCounts.delivery_holdout_sent_outcomes ?? 0)
4986
+ );
4987
+ const proved = diagnostics.closed_loop_proof_ready === true || holdoutLift.honest_measurement === true && holdoutLift.positive_lift === true;
4988
+ if (proved) {
4989
+ return {
4990
+ state: "proved",
4991
+ detail: "Real attributed feedback shows positive lift for explicit control and learned cohorts."
4992
+ };
4993
+ }
4994
+ if (controlSample > 0 && learnedSample > 0 && feedbackEvents > 0) {
4995
+ return {
4996
+ state: "moved_needs_lift_volume",
4997
+ detail: `Control and learned cohorts have attributed feedback (${controlSample} control, ${learnedSample} learned, ${feedbackEvents} feedback event(s)); keep collecting lift volume before claiming proof.`
4998
+ };
4999
+ }
5000
+ return {
5001
+ state: "scaffolding_ready",
5002
+ detail: holdoutRows > 0 ? `${holdoutRows} sampled row(s) carry holdout attribution, but real control/learned feedback lift is not proved yet.` : "Holdout and feedback scaffolding may be ready, but no real control/learned feedback lift is proved yet."
5003
+ };
5004
+ }
4035
5005
  function createCampaignBuilderReadinessLane(route, plan) {
4036
5006
  const builtIn = campaignBuilderBuiltInForRoute(route);
4037
5007
  const customRoute = route.provider !== "signaliz" && route.provider !== "csv" && builtIn === void 0;
@@ -4073,6 +5043,8 @@ function readinessLaneLabel(route, builtIn) {
4073
5043
  const labels = {
4074
5044
  lead_generation: "Signaliz lead generation",
4075
5045
  local_leads: "Signaliz local leads",
5046
+ company_discovery: "Signaliz company discovery",
5047
+ people_discovery: "Signaliz people discovery",
4076
5048
  email_finding: "Signaliz email finding",
4077
5049
  email_verification: "Signaliz email verification",
4078
5050
  signals: "Signaliz signals"
@@ -4157,6 +5129,35 @@ function summarizeCampaignBuilderProofDryRun(dryRunResult) {
4157
5129
  plannedTargetCount: dryRunResult.plannedTargetCount ?? dryRunResult.planned_target_count ?? null
4158
5130
  };
4159
5131
  }
5132
+ function summarizeCampaignBuilderProofLearnBack(plan) {
5133
+ if (Object.keys(plan).length === 0) return { ready: false };
5134
+ const memoryPolicy = asRecord(plan.memory_write_policy);
5135
+ const memoryItemsPolicy = asRecord(memoryPolicy.gtm_memory_items);
5136
+ const sequence = Array.isArray(plan.post_build_sequence) ? plan.post_build_sequence.map((step) => {
5137
+ const record = asRecord(step);
5138
+ return compact({
5139
+ step: record.step,
5140
+ tool: stringValue(record.tool),
5141
+ approval_boundary: stringValue(record.approval_boundary),
5142
+ reason: stringValue(record.reason)
5143
+ });
5144
+ }).filter((step) => typeof step.tool === "string") : [];
5145
+ return {
5146
+ ready: sequence.length > 0,
5147
+ source_tool: firstNonEmptyString(plan.source_tool, plan.sourceTool),
5148
+ post_build_sequence: sequence,
5149
+ memory_write_policy: {
5150
+ target_tables: Array.isArray(memoryPolicy.target_tables) ? memoryPolicy.target_tables : [],
5151
+ gtm_memory_items: {
5152
+ shareable: memoryItemsPolicy.shareable === false ? false : memoryItemsPolicy.shareable ?? null,
5153
+ allowed_redaction_states: Array.isArray(memoryItemsPolicy.allowed_redaction_states) ? memoryItemsPolicy.allowed_redaction_states : [],
5154
+ raw_private_fields_withheld: Array.isArray(memoryItemsPolicy.raw_private_fields_withheld) ? memoryItemsPolicy.raw_private_fields_withheld : []
5155
+ }
5156
+ },
5157
+ approval_policy: firstNonEmptyString(plan.approval_policy, plan.approvalPolicy),
5158
+ privacy_policy: firstNonEmptyString(plan.privacy_policy, plan.privacyPolicy)
5159
+ };
5160
+ }
4160
5161
  function firstNonEmptyString(...values) {
4161
5162
  for (const value of values) {
4162
5163
  if (typeof value === "string" && value.trim()) return value;
@@ -4639,14 +5640,87 @@ var Ops = class {
4639
5640
  const body = typeof params === "string" ? { prompt: params } : buildOpsCreateBody(params);
4640
5641
  return normalizeOpsCreateResult(withExecutionRefs(await this.call("ops_create", body)));
4641
5642
  }
5643
+ async schedule(params) {
5644
+ const createBody = typeof params === "string" ? { prompt: params, cadence: "daily" } : { ...buildOpsCreateBody(params), cadence: params.cadence ?? "daily" };
5645
+ const { activate: _activate, ...body } = createBody;
5646
+ return normalizeOpsCreateResult(withExecutionRefs(await this.call("ops_schedule", body)));
5647
+ }
5648
+ async scheduleAndWait(params) {
5649
+ const options = typeof params === "string" ? { schedule: { prompt: params, cadence: "daily" }, run: {}, wait: {} } : buildOpsScheduleAndWaitOptions(params);
5650
+ const raw = await this.call("ops_schedule_and_wait", buildOpsScheduleAndWaitBody(options));
5651
+ return normalizeOpsScheduleAndWaitCallResult(raw, options.wait, {
5652
+ approvalMessage: "ops.scheduleAndWait requires an approved schedulable Op. Retry with confirmSpend=true after reviewing approval details.",
5653
+ notCreatedMessage: "ops.scheduleAndWait requires ops_schedule_and_wait to create an op_id. Use ops.schedule for approval review flows."
5654
+ });
5655
+ }
5656
+ /** Schedule a recurring Op that delivers through a customer-owned Nango API connection. */
5657
+ async scheduleNangoAction(input) {
5658
+ return normalizeOpsCreateResult(withExecutionRefs(await this.call("ops_nango_schedule", buildOpsNangoScheduleBody(input))));
5659
+ }
5660
+ /** Schedule a recurring Nango-backed Op, run the first tick, and return result readback. */
5661
+ async scheduleNangoActionAndWait(input) {
5662
+ const options = buildOpsNangoScheduleAndWaitOptions(input);
5663
+ const raw = await this.call("ops_nango_schedule_and_wait", buildOpsNangoScheduleAndWaitBody(options));
5664
+ return normalizeOpsScheduleAndWaitCallResult(raw, options.wait, {
5665
+ approvalMessage: "ops.scheduleNangoActionAndWait requires an approved Nango-backed Op. Retry with confirmSpend=true after reviewing approval details.",
5666
+ notCreatedMessage: "ops.scheduleNangoActionAndWait requires ops_nango_schedule_and_wait to create an op_id. Use ops.scheduleNangoAction for approval review flows."
5667
+ });
5668
+ }
4642
5669
  async execute(params) {
4643
5670
  const body = typeof params === "string" ? { prompt: params } : buildOpsExecuteBody(params);
4644
5671
  return normalizeOpsExecuteResult(withExecutionRefs(await this.call("ops_execute", body)));
4645
5672
  }
5673
+ async executeAndWait(params) {
5674
+ const options = typeof params === "string" ? { execute: { prompt: params, auto_run: true }, wait: {} } : buildOpsExecuteAndWaitOptions(params);
5675
+ const execute = await this.execute(options.execute);
5676
+ if (execute.approval_required || execute.error_code === "APPROVAL_REQUIRED") {
5677
+ throw new SignalizError({
5678
+ code: "APPROVAL_REQUIRED",
5679
+ errorType: "validation",
5680
+ message: "ops.executeAndWait requires an approved executable Op. Retry with confirmSpend=true after reviewing approval details.",
5681
+ details: { execute }
5682
+ });
5683
+ }
5684
+ if (!execute.op_id) {
5685
+ throw new SignalizError({
5686
+ code: "OP_NOT_CREATED",
5687
+ errorType: "validation",
5688
+ message: "ops.executeAndWait requires ops_execute to create an op_id. Use ops.execute for dry runs or approval review flows.",
5689
+ details: { execute }
5690
+ });
5691
+ }
5692
+ const waited = await this.waitForResults({
5693
+ ...options.wait,
5694
+ op_id: execute.op_id
5695
+ });
5696
+ const runId = execute.run_id ?? execute.runId ?? waited.status.run_id ?? waited.status.runId ?? null;
5697
+ return {
5698
+ ...waited,
5699
+ execute,
5700
+ run_id: runId,
5701
+ runId,
5702
+ execution_refs: mergeExecutionRefsFrom([execute, waited])
5703
+ };
5704
+ }
4646
5705
  async run(params) {
4647
5706
  const body = typeof params === "string" ? { op_id: params } : buildOpsRunBody(params);
4648
5707
  return normalizeOpsRunResult(withExecutionRefs(await this.call("ops_run", body)));
4649
5708
  }
5709
+ async runAndWait(params) {
5710
+ const options = typeof params === "string" ? { op_id: params } : buildOpsRunAndWaitOptions(params);
5711
+ if (!options.op_id) throw new Error("ops.runAndWait requires op_id or opId");
5712
+ const run = await this.run({
5713
+ op_id: options.op_id,
5714
+ instruction: options.instruction,
5715
+ force: options.force
5716
+ });
5717
+ const waited = await this.waitForResults(options);
5718
+ return {
5719
+ ...waited,
5720
+ run,
5721
+ execution_refs: mergeExecutionRefsFrom([run, waited])
5722
+ };
5723
+ }
4650
5724
  async status(params) {
4651
5725
  const body = typeof params === "string" ? { op_id: params } : buildOpsStatusBody(params);
4652
5726
  return normalizeOpsStatusResult(withExecutionRefs(await this.call("ops_status", body)));
@@ -4655,21 +5729,188 @@ var Ops = class {
4655
5729
  const body = typeof params === "string" ? { op_id: params } : buildOpsResultsBody(params);
4656
5730
  return normalizeOpsResultsResult(withExecutionRefs(await this.call("ops_results", body)));
4657
5731
  }
4658
- async proof(params = {}) {
4659
- const data = await this.call("ops_proof", {
4660
- window_hours: params.windowHours,
4661
- output_format: "json"
4662
- });
4663
- return normalizeOpsProofResult(data);
5732
+ async waitForResults(params) {
5733
+ const options = typeof params === "string" ? { op_id: params } : buildOpsWaitForResultsOptions(params);
5734
+ if (!options.op_id) throw new Error("ops.waitForResults requires op_id or opId");
5735
+ const waitResult = await this.call("ops_wait", buildOpsWaitBody(options));
5736
+ return normalizeOpsWaitForResultsResult(waitResult, options);
4664
5737
  }
4665
- async debug(params) {
4666
- const options = typeof params === "string" ? { op_id: params } : buildOpsDebugOptions(params);
4667
- const diagnosticErrors = [];
4668
- const status = await this.status(options.op_id);
4669
- const refs = /* @__PURE__ */ new Map();
4670
- const addRefs = (items) => {
4671
- for (const ref of items ?? []) refs.set(`${ref.kind}:${ref.id}`, ref);
4672
- };
5738
+ /** Create a short-lived Nango Connect session from the Ops namespace. */
5739
+ async createNangoConnectSession(input) {
5740
+ return this.call("nango_connect_session_create", {
5741
+ provider_id: input.providerId,
5742
+ integration_id: input.integrationId,
5743
+ provider_display_name: input.providerDisplayName,
5744
+ integration_display_name: input.integrationDisplayName,
5745
+ allowed_integrations: input.allowedIntegrations,
5746
+ surface: input.surface,
5747
+ source: input.source,
5748
+ user_email: input.userEmail,
5749
+ user_display_name: input.userDisplayName
5750
+ });
5751
+ }
5752
+ /** Prepare the full Nango provider search, connect, pull/export, Ops, and route flow. */
5753
+ async prepareNangoIntegrationFlow(input = {}) {
5754
+ return this.call("nango_integration_flow_prepare", {
5755
+ query: input.query,
5756
+ search: input.search,
5757
+ provider_id: input.providerId,
5758
+ provider: input.provider,
5759
+ integration_id: input.integrationId,
5760
+ provider_config_key: input.providerConfigKey,
5761
+ workspace_connection_id: input.workspaceConnectionId,
5762
+ connection_id: input.connectionId,
5763
+ nango_connection_id: input.nangoConnectionId,
5764
+ intent: input.intent,
5765
+ action_name: input.actionName,
5766
+ tool_name: input.toolName,
5767
+ proxy_path: input.proxyPath,
5768
+ method: input.method,
5769
+ input: input.input,
5770
+ cadence: input.cadence,
5771
+ field_map: input.fieldMap,
5772
+ required_fields: input.requiredFields,
5773
+ confirm_spend: input.confirmSpend,
5774
+ write_confirmed: input.writeConfirmed,
5775
+ layer: input.layer,
5776
+ campaign_id: input.campaignId,
5777
+ limit: input.limit,
5778
+ include_provider_catalog: input.includeProviderCatalog
5779
+ });
5780
+ }
5781
+ /** List Nango-backed action tools for a workspace connection without executing them. */
5782
+ async listNangoTools(options = {}) {
5783
+ return this.call("nango_mcp_tools_list", {
5784
+ workspace_connection_id: options.workspaceConnectionId,
5785
+ connection_id: options.connectionId,
5786
+ provider_config_key: options.providerConfigKey,
5787
+ integration_id: options.integrationId,
5788
+ nango_connection_id: options.nangoConnectionId,
5789
+ format: options.format,
5790
+ include_raw: options.includeRaw
5791
+ });
5792
+ }
5793
+ /** Audit connected Nango rows, action/proxy readiness, and read/write proof gaps. */
5794
+ async proveNangoReadWrite(options = {}) {
5795
+ return this.call("nango_mcp_read_write_audit", {
5796
+ workspace_connection_id: options.workspaceConnectionId,
5797
+ connection_id: options.connectionId,
5798
+ provider_config_key: options.providerConfigKey,
5799
+ integration_id: options.integrationId,
5800
+ nango_connection_id: options.nangoConnectionId,
5801
+ limit: options.limit,
5802
+ include_raw: options.includeRaw,
5803
+ read_proxy_path: options.readProxyPath,
5804
+ write_proxy_path: options.writeProxyPath,
5805
+ method: options.method,
5806
+ execute_read_probe: options.executeReadProbe,
5807
+ execute_write_probe: options.executeWriteProbe,
5808
+ confirm: options.confirm,
5809
+ confirm_write: options.confirmWrite,
5810
+ input: options.input,
5811
+ write_input: options.writeInput
5812
+ });
5813
+ }
5814
+ /** Dry-run or execute a Nango-backed action tool through the approval-aware MCP bridge. */
5815
+ async callNangoTool(input) {
5816
+ return this.call("nango_mcp_tool_call", {
5817
+ workspace_connection_id: input.workspaceConnectionId,
5818
+ connection_id: input.connectionId,
5819
+ provider_config_key: input.providerConfigKey,
5820
+ integration_id: input.integrationId,
5821
+ nango_connection_id: input.nangoConnectionId,
5822
+ action_name: input.actionName,
5823
+ tool_name: input.toolName,
5824
+ delivery_mode: input.deliveryMode,
5825
+ proxy_path: input.proxyPath,
5826
+ nango_proxy_path: input.nangoProxyPath,
5827
+ method: input.method,
5828
+ http_method: input.httpMethod,
5829
+ proxy_headers: input.proxyHeaders,
5830
+ input: input.input,
5831
+ async: input.async,
5832
+ max_retries: input.maxRetries,
5833
+ dry_run: input.dryRun,
5834
+ confirm: input.confirm,
5835
+ confirm_write: input.confirmWrite
5836
+ });
5837
+ }
5838
+ /** Execute a Nango action and poll its async result when a status URL/action id is returned. */
5839
+ async callNangoToolAndWait(input) {
5840
+ const result = await this.call("nango_mcp_tool_call_and_wait", {
5841
+ workspace_connection_id: input.workspaceConnectionId,
5842
+ connection_id: input.connectionId,
5843
+ provider_config_key: input.providerConfigKey,
5844
+ integration_id: input.integrationId,
5845
+ nango_connection_id: input.nangoConnectionId,
5846
+ action_name: input.actionName,
5847
+ tool_name: input.toolName,
5848
+ delivery_mode: input.deliveryMode,
5849
+ proxy_path: input.proxyPath,
5850
+ nango_proxy_path: input.nangoProxyPath,
5851
+ method: input.method,
5852
+ http_method: input.httpMethod,
5853
+ proxy_headers: input.proxyHeaders,
5854
+ input: input.input,
5855
+ async: input.async ?? true,
5856
+ max_retries: input.maxRetries,
5857
+ dry_run: input.dryRun,
5858
+ confirm: input.confirm,
5859
+ confirm_write: input.confirmWrite,
5860
+ interval_ms: input.intervalMs,
5861
+ max_polls: input.maxPolls,
5862
+ timeout_ms: input.timeoutMs
5863
+ });
5864
+ const packet = asRecord2(result);
5865
+ const actionId = optionalString(packet.actionId) ?? optionalString(packet.action_id);
5866
+ const statusUrl = optionalString(packet.statusUrl) ?? optionalString(packet.status_url);
5867
+ const maxPolls = Number(packet.maxPolls ?? packet.max_polls ?? 0);
5868
+ const intervalMs = Number(packet.intervalMs ?? packet.interval_ms ?? 0);
5869
+ const timeoutMs = Number(packet.timeoutMs ?? packet.timeout_ms ?? 0);
5870
+ const timedOut = Boolean(packet.timedOut ?? packet.timed_out);
5871
+ return {
5872
+ ...packet,
5873
+ success: typeof packet.success === "boolean" ? packet.success : void 0,
5874
+ call: packet.call,
5875
+ result: packet.result,
5876
+ status: optionalString(packet.status),
5877
+ action_id: actionId,
5878
+ actionId,
5879
+ status_url: statusUrl,
5880
+ statusUrl,
5881
+ polls: Number(packet.polls ?? 0),
5882
+ max_polls: maxPolls,
5883
+ maxPolls,
5884
+ interval_ms: intervalMs,
5885
+ intervalMs,
5886
+ timeout_ms: timeoutMs,
5887
+ timeoutMs,
5888
+ timed_out: timedOut,
5889
+ timedOut
5890
+ };
5891
+ }
5892
+ /** Poll an async Nango action result by action id or status URL. */
5893
+ async getNangoActionResult(input) {
5894
+ return this.call("nango_mcp_action_result_get", {
5895
+ action_id: input.actionId,
5896
+ status_url: input.statusUrl
5897
+ });
5898
+ }
5899
+ async proof(params = {}) {
5900
+ const data = await this.call("ops_proof", {
5901
+ window_hours: params.windowHours,
5902
+ output_format: "json"
5903
+ });
5904
+ return normalizeOpsProofResult(data);
5905
+ }
5906
+ async debug(params) {
5907
+ const options = typeof params === "string" ? { op_id: params } : buildOpsDebugOptions(params);
5908
+ const diagnosticErrors = [];
5909
+ const status = await this.status(options.op_id);
5910
+ const refs = /* @__PURE__ */ new Map();
5911
+ const addRefs = (items) => {
5912
+ for (const ref of items ?? []) refs.set(`${ref.kind}:${ref.id}`, ref);
5913
+ };
4673
5914
  const recordError = (step, err) => {
4674
5915
  diagnosticErrors.push({
4675
5916
  step,
@@ -4681,51 +5922,57 @@ var Ops = class {
4681
5922
  addRefs(collectExecutionReferences(status.latest_run));
4682
5923
  }
4683
5924
  const runRefs = Array.from(refs.values()).filter((ref) => ref.kind === "trigger_run" || ref.kind === "dataplane_run" || ref.kind === "replay_run");
4684
- const runStatuses = [];
4685
- for (const ref of runRefs) {
4686
- try {
4687
- const result = await this.getTriggerRunStatus(ref.id);
4688
- const runs = "runs" in result ? result.runs : [result];
4689
- runStatuses.push(...runs);
4690
- for (const run of runs) addRefs(run.execution_refs);
4691
- } catch (err) {
4692
- recordError(`run-status:${ref.id}`, err);
4693
- }
4694
- }
5925
+ let runStatuses = [];
4695
5926
  let queue;
4696
- if (options.include_queue !== false) {
4697
- try {
4698
- queue = await this.getQueueStatus({ summary: true });
4699
- addRefs(queue.execution_refs);
4700
- } catch (err) {
4701
- recordError("queue", err);
4702
- }
4703
- }
4704
5927
  let dashboardRecent;
4705
- if (options.include_dashboard !== false) {
4706
- try {
4707
- dashboardRecent = await this.getDashboard({
4708
- section: "recent",
4709
- limit: options.dashboard_limit ?? 10,
4710
- timeRangeDays: options.time_range_days
4711
- });
4712
- } catch (err) {
4713
- recordError("dashboard:recent", err);
4714
- }
4715
- }
4716
5928
  let results;
4717
- if (options.include_results) {
4718
- try {
4719
- results = await this.results({
4720
- op_id: options.op_id,
4721
- limit: options.results_limit ?? 25,
4722
- include_failed_runs: true
4723
- });
4724
- addRefs(results.execution_refs);
4725
- } catch (err) {
4726
- recordError("results", err);
4727
- }
4728
- }
5929
+ await Promise.all([
5930
+ (async () => {
5931
+ if (runRefs.length === 0) return;
5932
+ try {
5933
+ const result = await this.getTriggerRunStatus({ run_ids: runRefs.map((ref) => ref.id) });
5934
+ const runs = "runs" in result ? result.runs : [result];
5935
+ runStatuses = runs;
5936
+ for (const run of runs) addRefs(run.execution_refs);
5937
+ } catch (err) {
5938
+ recordError(runRefs.length === 1 ? `run-status:${runRefs[0].id}` : "run-status:batch", err);
5939
+ }
5940
+ })(),
5941
+ (async () => {
5942
+ if (options.include_queue === false) return;
5943
+ try {
5944
+ queue = await this.getQueueStatus({ summary: true });
5945
+ addRefs(queue.execution_refs);
5946
+ } catch (err) {
5947
+ recordError("queue", err);
5948
+ }
5949
+ })(),
5950
+ (async () => {
5951
+ if (options.include_dashboard === false) return;
5952
+ try {
5953
+ dashboardRecent = await this.getDashboard({
5954
+ section: "recent",
5955
+ limit: options.dashboard_limit ?? 10,
5956
+ timeRangeDays: options.time_range_days
5957
+ });
5958
+ } catch (err) {
5959
+ recordError("dashboard:recent", err);
5960
+ }
5961
+ })(),
5962
+ (async () => {
5963
+ if (!options.include_results) return;
5964
+ try {
5965
+ results = await this.results({
5966
+ op_id: options.op_id,
5967
+ limit: options.results_limit ?? 25,
5968
+ include_failed_runs: true
5969
+ });
5970
+ addRefs(results.execution_refs);
5971
+ } catch (err) {
5972
+ recordError("results", err);
5973
+ }
5974
+ })()
5975
+ ]);
4729
5976
  const executionRefs = Array.from(refs.values());
4730
5977
  const replayCandidates = executionRefs.filter((ref) => ref.kind === "execution_event");
4731
5978
  const nextActions = buildDebugNextActions(status, executionRefs, diagnosticErrors);
@@ -4927,6 +6174,69 @@ function mergeExecutionRefs(result) {
4927
6174
  for (const ref of collectExecutionReferences(result)) add(ref);
4928
6175
  return Array.from(refs.values());
4929
6176
  }
6177
+ function mergeExecutionRefsFrom(results) {
6178
+ const refs = /* @__PURE__ */ new Map();
6179
+ for (const result of results) {
6180
+ if (!result) continue;
6181
+ for (const ref of mergeExecutionRefs(result)) refs.set(`${ref.kind}:${ref.id}`, ref);
6182
+ }
6183
+ return Array.from(refs.values());
6184
+ }
6185
+ function normalizeOpsScheduleAndWaitCallResult(raw, waitOptions, messages) {
6186
+ if (raw.approval_required || raw.approvalRequired || raw.error_code === "APPROVAL_REQUIRED" || raw.errorCode === "APPROVAL_REQUIRED") {
6187
+ throw new SignalizError({
6188
+ code: "APPROVAL_REQUIRED",
6189
+ errorType: "validation",
6190
+ message: messages.approvalMessage,
6191
+ details: { schedule: raw }
6192
+ });
6193
+ }
6194
+ const opId = stringValue2(raw.op_id ?? raw.opId);
6195
+ if (!opId) {
6196
+ throw new SignalizError({
6197
+ code: "OP_NOT_CREATED",
6198
+ errorType: "validation",
6199
+ message: messages.notCreatedMessage,
6200
+ details: { schedule: raw }
6201
+ });
6202
+ }
6203
+ const scheduleRaw = asRecord2(raw.schedule ?? raw.schedule_result ?? raw.scheduleResult);
6204
+ const runRaw = asRecord2(raw.run ?? raw.run_result ?? raw.runResult);
6205
+ const schedule = normalizeOpsCreateResult(withExecutionRefs(
6206
+ Object.keys(scheduleRaw).length > 0 ? scheduleRaw : {
6207
+ ...raw,
6208
+ op_id: opId,
6209
+ routine_id: raw.routine_id ?? raw.routineId ?? opId,
6210
+ status: "ready",
6211
+ phase: "scheduled",
6212
+ next_action: raw.next_action ?? raw.nextAction
6213
+ }
6214
+ ));
6215
+ const run = normalizeOpsRunResult(withExecutionRefs(
6216
+ Object.keys(runRaw).length > 0 ? runRaw : {
6217
+ success: raw.success !== false,
6218
+ op_id: opId,
6219
+ routine_id: raw.routine_id ?? raw.routineId ?? opId,
6220
+ run_id: raw.run_id ?? raw.runId ?? null,
6221
+ status: "running",
6222
+ phase: "queued",
6223
+ next_action: raw.next_action ?? raw.nextAction,
6224
+ results_count: 0,
6225
+ results_url: raw.results_url ?? raw.resultsUrl,
6226
+ delivery_status: raw.delivery_status ?? raw.deliveryStatus
6227
+ }
6228
+ ));
6229
+ const waited = normalizeOpsWaitForResultsResult(raw, { ...waitOptions, op_id: opId });
6230
+ const runId = run.run_id ?? run.runId ?? waited.run_id ?? waited.runId ?? waited.status.run_id ?? waited.status.runId ?? null;
6231
+ return {
6232
+ ...waited,
6233
+ schedule,
6234
+ run,
6235
+ run_id: runId,
6236
+ runId,
6237
+ execution_refs: mergeExecutionRefsFrom([schedule, run, waited])
6238
+ };
6239
+ }
4930
6240
  function normalizeOpsPrimitiveRetryPolicy(policy = {}) {
4931
6241
  return {
4932
6242
  ...policy,
@@ -5002,8 +6312,122 @@ function normalizeOpsPrimitive(primitive) {
5002
6312
  observability: normalizeOpsPrimitiveObservabilityPolicy(primitive.observability)
5003
6313
  };
5004
6314
  }
6315
+ function normalizeOpsExecutionToolCall(rawCall) {
6316
+ const call = asRecord2(rawCall);
6317
+ return {
6318
+ ...call,
6319
+ tool: stringValue2(call.tool),
6320
+ arguments: asRecord2(call.arguments),
6321
+ purpose: stringValue2(call.purpose)
6322
+ };
6323
+ }
6324
+ function normalizeOpsExecutionScheduleContract(rawSchedule) {
6325
+ const schedule = asRecord2(rawSchedule);
6326
+ const wakeOnEvents = Array.isArray(schedule.wake_on_events) ? schedule.wake_on_events.map(String) : Array.isArray(schedule.wakeOnEvents) ? schedule.wakeOnEvents.map(String) : [];
6327
+ return {
6328
+ ...schedule,
6329
+ create_tool: stringValue2(schedule.create_tool ?? schedule.createTool),
6330
+ createTool: stringValue2(schedule.createTool ?? schedule.create_tool),
6331
+ activate_tool: stringValue2(schedule.activate_tool ?? schedule.activateTool),
6332
+ activateTool: stringValue2(schedule.activateTool ?? schedule.activate_tool),
6333
+ cadence: stringValue2(schedule.cadence, "manual"),
6334
+ recurrence: stringValue2(schedule.recurrence),
6335
+ wake_on_events: wakeOnEvents,
6336
+ wakeOnEvents
6337
+ };
6338
+ }
6339
+ function normalizeOpsExecutionApprovalContract(rawApproval) {
6340
+ const approval = asRecord2(rawApproval);
6341
+ return {
6342
+ ...approval,
6343
+ required: Boolean(approval.required),
6344
+ tool: stringValue2(approval.tool),
6345
+ reason: stringValue2(approval.reason)
6346
+ };
6347
+ }
6348
+ function normalizeOpsExecutionMonitorContract(rawMonitor) {
6349
+ const monitor = asRecord2(rawMonitor);
6350
+ const pollAfterMs = numberValue(monitor.poll_after_ms ?? monitor.pollAfterMs);
6351
+ return {
6352
+ ...monitor,
6353
+ status_tool: stringValue2(monitor.status_tool ?? monitor.statusTool),
6354
+ statusTool: stringValue2(monitor.statusTool ?? monitor.status_tool),
6355
+ wait_tool: stringValue2(monitor.wait_tool ?? monitor.waitTool),
6356
+ waitTool: stringValue2(monitor.waitTool ?? monitor.wait_tool),
6357
+ results_tool: stringValue2(monitor.results_tool ?? monitor.resultsTool),
6358
+ resultsTool: stringValue2(monitor.resultsTool ?? monitor.results_tool),
6359
+ poll_after_ms: pollAfterMs,
6360
+ pollAfterMs
6361
+ };
6362
+ }
6363
+ function normalizeOpsExecutionResultContract(rawResult) {
6364
+ const result = asRecord2(rawResult);
6365
+ const shape = Array.isArray(result.shape) ? result.shape.map(String) : [];
6366
+ return {
6367
+ ...result,
6368
+ tool: stringValue2(result.tool),
6369
+ wait_tool: stringValue2(result.wait_tool ?? result.waitTool),
6370
+ waitTool: stringValue2(result.waitTool ?? result.wait_tool),
6371
+ shape,
6372
+ empty_result_recovery: stringValue2(result.empty_result_recovery ?? result.emptyResultRecovery),
6373
+ emptyResultRecovery: stringValue2(result.emptyResultRecovery ?? result.empty_result_recovery)
6374
+ };
6375
+ }
6376
+ function normalizeOpsExecutionNangoRouteContract(rawRoute) {
6377
+ if (!rawRoute || typeof rawRoute !== "object" || Array.isArray(rawRoute)) return void 0;
6378
+ const route = asRecord2(rawRoute);
6379
+ const requiredFields = Array.isArray(route.required_fields) ? route.required_fields.map(String) : Array.isArray(route.requiredFields) ? route.requiredFields.map(String) : [];
6380
+ return {
6381
+ ...route,
6382
+ enabled: route.enabled !== false,
6383
+ connect_tool: stringValue2(route.connect_tool ?? route.connectTool),
6384
+ connectTool: stringValue2(route.connectTool ?? route.connect_tool),
6385
+ discover_tool: stringValue2(route.discover_tool ?? route.discoverTool),
6386
+ discoverTool: stringValue2(route.discoverTool ?? route.discover_tool),
6387
+ dry_run_tool: stringValue2(route.dry_run_tool ?? route.dryRunTool),
6388
+ dryRunTool: stringValue2(route.dryRunTool ?? route.dry_run_tool),
6389
+ execute_tool: stringValue2(route.execute_tool ?? route.executeTool),
6390
+ executeTool: stringValue2(route.executeTool ?? route.execute_tool),
6391
+ execute_and_wait_tool: stringValue2(route.execute_and_wait_tool ?? route.executeAndWaitTool),
6392
+ executeAndWaitTool: stringValue2(route.executeAndWaitTool ?? route.execute_and_wait_tool),
6393
+ schedule_tool: stringValue2(route.schedule_tool ?? route.scheduleTool),
6394
+ scheduleTool: stringValue2(route.scheduleTool ?? route.schedule_tool),
6395
+ schedule_and_wait_tool: stringValue2(route.schedule_and_wait_tool ?? route.scheduleAndWaitTool),
6396
+ scheduleAndWaitTool: stringValue2(route.scheduleAndWaitTool ?? route.schedule_and_wait_tool),
6397
+ result_tool: stringValue2(route.result_tool ?? route.resultTool),
6398
+ resultTool: stringValue2(route.resultTool ?? route.result_tool),
6399
+ write_gate: stringValue2(route.write_gate ?? route.writeGate),
6400
+ writeGate: stringValue2(route.writeGate ?? route.write_gate),
6401
+ required_fields: requiredFields,
6402
+ requiredFields,
6403
+ schedule_arguments: asRecord2(route.schedule_arguments ?? route.scheduleArguments),
6404
+ scheduleArguments: asRecord2(route.scheduleArguments ?? route.schedule_arguments),
6405
+ schedule_and_wait_arguments: asRecord2(route.schedule_and_wait_arguments ?? route.scheduleAndWaitArguments),
6406
+ scheduleAndWaitArguments: asRecord2(route.scheduleAndWaitArguments ?? route.schedule_and_wait_arguments)
6407
+ };
6408
+ }
6409
+ function normalizeOpsExecutionContract(rawContract) {
6410
+ if (!rawContract || typeof rawContract !== "object" || Array.isArray(rawContract)) return void 0;
6411
+ const contract = asRecord2(rawContract);
6412
+ const nangoRoute = normalizeOpsExecutionNangoRouteContract(contract.nango_route ?? contract.nangoRoute);
6413
+ const sequence = Array.isArray(contract.sequence) ? contract.sequence.map(normalizeOpsExecutionToolCall) : [];
6414
+ return {
6415
+ ...contract,
6416
+ mode: stringValue2(contract.mode, "run_once"),
6417
+ run_now: normalizeOpsExecutionToolCall(contract.run_now ?? contract.runNow),
6418
+ runNow: normalizeOpsExecutionToolCall(contract.runNow ?? contract.run_now),
6419
+ schedule: normalizeOpsExecutionScheduleContract(contract.schedule),
6420
+ approval: normalizeOpsExecutionApprovalContract(contract.approval),
6421
+ monitor: normalizeOpsExecutionMonitorContract(contract.monitor),
6422
+ result_contract: normalizeOpsExecutionResultContract(contract.result_contract ?? contract.resultContract),
6423
+ resultContract: normalizeOpsExecutionResultContract(contract.resultContract ?? contract.result_contract),
6424
+ ...nangoRoute ? { nango_route: nangoRoute, nangoRoute } : {},
6425
+ sequence
6426
+ };
6427
+ }
5005
6428
  function normalizeOpsPlanResult(result) {
5006
6429
  const primitiveGraph = result.primitive_graph ?? result.primitiveGraph;
6430
+ const executionContract = normalizeOpsExecutionContract(result.execution_contract ?? result.executionContract);
5007
6431
  return {
5008
6432
  ...result,
5009
6433
  plan_id: result.plan_id ?? result.planId,
@@ -5027,7 +6451,8 @@ function normalizeOpsPlanResult(result) {
5027
6451
  destination_status: result.destination_status ?? result.destinationStatus,
5028
6452
  destinationStatus: result.destinationStatus ?? result.destination_status,
5029
6453
  primitive_graph: primitiveGraph?.map(normalizeOpsPrimitive),
5030
- primitiveGraph: primitiveGraph?.map(normalizeOpsPrimitive)
6454
+ primitiveGraph: primitiveGraph?.map(normalizeOpsPrimitive),
6455
+ ...executionContract ? { execution_contract: executionContract, executionContract } : {}
5031
6456
  };
5032
6457
  }
5033
6458
  function normalizeOpsCreateResult(result) {
@@ -5049,7 +6474,18 @@ function normalizeOpsCreateResult(result) {
5049
6474
  deliveryStatus: result.deliveryStatus ?? result.delivery_status,
5050
6475
  estimated_credits: result.estimated_credits ?? result.estimatedCredits,
5051
6476
  estimatedCredits: result.estimatedCredits ?? result.estimated_credits,
5052
- plan: result.plan ? normalizeOpsPlanResult(result.plan) : result.plan
6477
+ error_code: result.error_code ?? result.errorCode,
6478
+ errorCode: result.errorCode ?? result.error_code,
6479
+ approval_required: result.approval_required ?? result.approvalRequired,
6480
+ approvalRequired: result.approvalRequired ?? result.approval_required,
6481
+ retry_arguments: result.retry_arguments ?? result.retryArguments,
6482
+ retryArguments: result.retryArguments ?? result.retry_arguments,
6483
+ blockers: result.blockers,
6484
+ plan: result.plan ? normalizeOpsPlanResult(result.plan) : result.plan,
6485
+ next_step: result.next_step ?? result.nextStep,
6486
+ nextStep: result.nextStep ?? result.next_step,
6487
+ next_steps: result.next_steps ?? result.nextSteps,
6488
+ nextSteps: result.nextSteps ?? result.next_steps
5053
6489
  };
5054
6490
  }
5055
6491
  function normalizeOpsExecuteResult(result) {
@@ -5177,6 +6613,40 @@ function normalizeOpsStatusResult(result) {
5177
6613
  diagnosis: result.diagnosis ? normalizeOpsDebugDiagnosis(result.diagnosis) : void 0
5178
6614
  };
5179
6615
  }
6616
+ function normalizeOpsResultsSummary(value) {
6617
+ const summary = asRecord2(value);
6618
+ if (Object.keys(summary).length === 0) return void 0;
6619
+ const resultsTotal = summary.results_total ?? summary.resultsTotal;
6620
+ const nextCursor = summary.next_cursor ?? summary.nextCursor ?? null;
6621
+ const resultsUrl = summary.results_url ?? summary.resultsUrl;
6622
+ const nextAction = summary.next_action ?? summary.nextAction;
6623
+ const runId = summary.run_id ?? summary.runId ?? null;
6624
+ const creditsUsed = summary.credits_used ?? summary.creditsUsed;
6625
+ return {
6626
+ ...summary,
6627
+ label: optionalString(summary.label),
6628
+ status: optionalString(summary.status),
6629
+ phase: optionalString(summary.phase),
6630
+ results_count: numberValue(summary.results_count ?? summary.resultsCount),
6631
+ resultsCount: numberValue(summary.results_count ?? summary.resultsCount),
6632
+ results_total: resultsTotal === null || resultsTotal === void 0 ? resultsTotal : numberValue(resultsTotal),
6633
+ resultsTotal: resultsTotal === null || resultsTotal === void 0 ? resultsTotal : numberValue(resultsTotal),
6634
+ delivery_status: optionalString(summary.delivery_status ?? summary.deliveryStatus),
6635
+ deliveryStatus: optionalString(summary.delivery_status ?? summary.deliveryStatus),
6636
+ has_more: Boolean(summary.has_more ?? summary.hasMore),
6637
+ hasMore: Boolean(summary.has_more ?? summary.hasMore),
6638
+ next_cursor: nextCursor === null ? null : optionalString(nextCursor),
6639
+ nextCursor: nextCursor === null ? null : optionalString(nextCursor),
6640
+ results_url: optionalString(resultsUrl),
6641
+ resultsUrl: optionalString(resultsUrl),
6642
+ next_action: optionalString(nextAction),
6643
+ nextAction: optionalString(nextAction),
6644
+ run_id: runId === null ? null : optionalString(runId),
6645
+ runId: runId === null ? null : optionalString(runId),
6646
+ credits_used: creditsUsed === void 0 ? void 0 : numberValue(creditsUsed),
6647
+ creditsUsed: creditsUsed === void 0 ? void 0 : numberValue(creditsUsed)
6648
+ };
6649
+ }
5180
6650
  function normalizeOpsResultsResult(result) {
5181
6651
  const nextCursor = result.next_cursor ?? result.nextCursor ?? null;
5182
6652
  const hasMore = result.has_more ?? result.hasMore ?? false;
@@ -5201,7 +6671,104 @@ function normalizeOpsResultsResult(result) {
5201
6671
  results_url: result.results_url ?? result.resultsUrl,
5202
6672
  resultsUrl: result.resultsUrl ?? result.results_url,
5203
6673
  delivery_status: result.delivery_status ?? result.deliveryStatus,
5204
- deliveryStatus: result.deliveryStatus ?? result.delivery_status
6674
+ deliveryStatus: result.deliveryStatus ?? result.delivery_status,
6675
+ summary: normalizeOpsResultsSummary(result.summary)
6676
+ };
6677
+ }
6678
+ function normalizeOpsWaitForResultsPoll(value, fallbackPoll) {
6679
+ const raw = asRecord2(value);
6680
+ const checkedAt = stringValue2(raw.checked_at ?? raw.checkedAt);
6681
+ const resultsCount = raw.results_count ?? raw.resultsCount;
6682
+ const runId = raw.run_id ?? raw.runId ?? null;
6683
+ const nextAction = raw.next_action ?? raw.nextAction;
6684
+ return {
6685
+ ...raw,
6686
+ poll: numberValue(raw.poll) || fallbackPoll,
6687
+ checked_at: checkedAt,
6688
+ checkedAt,
6689
+ status: stringValue2(raw.status, "unknown"),
6690
+ phase: optionalString(raw.phase),
6691
+ results_count: resultsCount === void 0 ? void 0 : numberValue(resultsCount),
6692
+ resultsCount: resultsCount === void 0 ? void 0 : numberValue(resultsCount),
6693
+ run_id: runId === null ? null : optionalString(runId),
6694
+ runId: runId === null ? null : optionalString(runId),
6695
+ next_action: optionalString(nextAction),
6696
+ nextAction: optionalString(nextAction)
6697
+ };
6698
+ }
6699
+ function normalizeOpsWaitForResultsResult(result, options) {
6700
+ const opId = stringValue2(result.op_id ?? result.opId ?? options.op_id);
6701
+ const routineId = stringValue2(result.routine_id ?? result.routineId ?? opId);
6702
+ const runId = result.run_id ?? result.runId ?? null;
6703
+ const timedOut = Boolean(result.timed_out ?? result.timedOut);
6704
+ const statusRaw = asRecord2(result.status_result ?? result.statusResult);
6705
+ const resultsRaw = asRecord2(result.results_result ?? result.resultsResult);
6706
+ const historySource = Array.isArray(result.poll_history) ? result.poll_history : Array.isArray(result.history) ? result.history : [];
6707
+ const history = historySource.map((item, index) => normalizeOpsWaitForResultsPoll(item, index + 1));
6708
+ const status = normalizeOpsStatusResult(withExecutionRefs({
6709
+ success: result.success !== false,
6710
+ op_id: opId,
6711
+ routine_id: routineId,
6712
+ run_id: runId,
6713
+ status: stringValue2(result.status, "unknown"),
6714
+ phase: stringValue2(result.phase, "unknown"),
6715
+ next_action: stringValue2(result.next_action ?? result.nextAction),
6716
+ results_count: numberValue(result.results_count ?? result.resultsCount),
6717
+ results_url: optionalString(result.results_url ?? result.resultsUrl),
6718
+ delivery_status: optionalString(result.delivery_status ?? result.deliveryStatus),
6719
+ ...statusRaw
6720
+ }));
6721
+ const includeResults = options.include_results !== false && options.includeResults !== false;
6722
+ const hasResultsPacket = Object.keys(resultsRaw).length > 0 || Array.isArray(result.results);
6723
+ const results = includeResults && hasResultsPacket ? normalizeOpsResultsResult(withExecutionRefs({
6724
+ success: result.success !== false,
6725
+ op_id: opId,
6726
+ routine_id: routineId,
6727
+ run_id: runId,
6728
+ status: stringValue2(result.status, status.status),
6729
+ phase: stringValue2(result.phase, status.phase),
6730
+ results_count: numberValue(result.results_count ?? result.resultsCount),
6731
+ results_total: result.results_total ?? result.resultsTotal ?? null,
6732
+ results: Array.isArray(result.results) ? result.results : [],
6733
+ next_cursor: result.next_cursor ?? result.nextCursor ?? null,
6734
+ has_more: Boolean(result.has_more ?? result.hasMore),
6735
+ next_action: stringValue2(result.next_action ?? result.nextAction),
6736
+ results_url: optionalString(result.results_url ?? result.resultsUrl),
6737
+ delivery_status: optionalString(result.delivery_status ?? result.deliveryStatus),
6738
+ ...resultsRaw
6739
+ })) : void 0;
6740
+ const polls = numberValue(result.polls) || history.length;
6741
+ const maxPolls = result.max_polls ?? result.maxPolls;
6742
+ const intervalMs = result.interval_ms ?? result.intervalMs;
6743
+ const timeoutMs = result.timeout_ms ?? result.timeoutMs;
6744
+ const stopReason = result.stop_reason ?? result.stopReason;
6745
+ const nextAction = result.next_action ?? result.nextAction ?? results?.next_action ?? status.next_action;
6746
+ return {
6747
+ ...result,
6748
+ success: result.success !== false && !timedOut,
6749
+ op_id: opId,
6750
+ opId,
6751
+ routine_id: routineId,
6752
+ routineId,
6753
+ run_id: runId === null ? null : optionalString(runId),
6754
+ runId: runId === null ? null : optionalString(runId),
6755
+ status,
6756
+ results,
6757
+ timed_out: timedOut,
6758
+ timedOut,
6759
+ polls,
6760
+ max_polls: maxPolls === void 0 ? void 0 : numberValue(maxPolls),
6761
+ maxPolls: maxPolls === void 0 ? void 0 : numberValue(maxPolls),
6762
+ interval_ms: intervalMs === void 0 ? void 0 : numberValue(intervalMs),
6763
+ intervalMs: intervalMs === void 0 ? void 0 : numberValue(intervalMs),
6764
+ timeout_ms: timeoutMs === void 0 ? void 0 : numberValue(timeoutMs),
6765
+ timeoutMs: timeoutMs === void 0 ? void 0 : numberValue(timeoutMs),
6766
+ stop_reason: optionalString(stopReason),
6767
+ stopReason: optionalString(stopReason),
6768
+ history,
6769
+ next_action: optionalString(nextAction),
6770
+ nextAction: optionalString(nextAction),
6771
+ execution_refs: mergeExecutionRefsFrom([result, status, results, { history }])
5205
6772
  };
5206
6773
  }
5207
6774
  function normalizeOpsProofResult(data) {
@@ -5281,11 +6848,107 @@ function normalizeOpsProofResult(data) {
5281
6848
  raw: data
5282
6849
  };
5283
6850
  }
6851
+ function normalizeOpsAutopilotSchedulePacket(rawPacket) {
6852
+ const packet = asRecord2(rawPacket);
6853
+ return {
6854
+ ...packet,
6855
+ cadence: stringValue2(packet.cadence, "manual"),
6856
+ recurrence: stringValue2(packet.recurrence),
6857
+ activate_with: stringValue2(packet.activate_with ?? packet.activateWith),
6858
+ activateWith: stringValue2(packet.activate_with ?? packet.activateWith),
6859
+ run_now_with: stringValue2(packet.run_now_with ?? packet.runNowWith),
6860
+ runNowWith: stringValue2(packet.run_now_with ?? packet.runNowWith),
6861
+ monitor_with: stringValue2(packet.monitor_with ?? packet.monitorWith),
6862
+ monitorWith: stringValue2(packet.monitor_with ?? packet.monitorWith),
6863
+ retrieve_with: stringValue2(packet.retrieve_with ?? packet.retrieveWith),
6864
+ retrieveWith: stringValue2(packet.retrieve_with ?? packet.retrieveWith)
6865
+ };
6866
+ }
6867
+ function normalizeOpsAutopilotNangoRoutePacket(rawPacket) {
6868
+ if (!rawPacket || typeof rawPacket !== "object" || Array.isArray(rawPacket)) return void 0;
6869
+ const packet = asRecord2(rawPacket);
6870
+ const requiredFields = Array.isArray(packet.required_fields) ? packet.required_fields.map(String) : Array.isArray(packet.requiredFields) ? packet.requiredFields.map(String) : [];
6871
+ return {
6872
+ ...packet,
6873
+ enabled: packet.enabled !== false,
6874
+ route_label: stringValue2(packet.route_label ?? packet.routeLabel),
6875
+ routeLabel: stringValue2(packet.route_label ?? packet.routeLabel),
6876
+ connect_tool: stringValue2(packet.connect_tool ?? packet.connectTool),
6877
+ connectTool: stringValue2(packet.connect_tool ?? packet.connectTool),
6878
+ discover_tool: stringValue2(packet.discover_tool ?? packet.discoverTool),
6879
+ discoverTool: stringValue2(packet.discover_tool ?? packet.discoverTool),
6880
+ dry_run_tool: stringValue2(packet.dry_run_tool ?? packet.dryRunTool),
6881
+ dryRunTool: stringValue2(packet.dry_run_tool ?? packet.dryRunTool),
6882
+ execute_tool: stringValue2(packet.execute_tool ?? packet.executeTool),
6883
+ executeTool: stringValue2(packet.execute_tool ?? packet.executeTool),
6884
+ execute_and_wait_tool: stringValue2(packet.execute_and_wait_tool ?? packet.executeAndWaitTool),
6885
+ executeAndWaitTool: stringValue2(packet.execute_and_wait_tool ?? packet.executeAndWaitTool),
6886
+ schedule_tool: stringValue2(packet.schedule_tool ?? packet.scheduleTool),
6887
+ scheduleTool: stringValue2(packet.schedule_tool ?? packet.scheduleTool),
6888
+ schedule_and_wait_tool: stringValue2(packet.schedule_and_wait_tool ?? packet.scheduleAndWaitTool),
6889
+ scheduleAndWaitTool: stringValue2(packet.schedule_and_wait_tool ?? packet.scheduleAndWaitTool),
6890
+ result_tool: stringValue2(packet.result_tool ?? packet.resultTool),
6891
+ resultTool: stringValue2(packet.result_tool ?? packet.resultTool),
6892
+ write_gate: stringValue2(packet.write_gate ?? packet.writeGate),
6893
+ writeGate: stringValue2(packet.write_gate ?? packet.writeGate),
6894
+ required_fields: requiredFields,
6895
+ requiredFields,
6896
+ placeholders: asRecord2(packet.placeholders),
6897
+ dry_run_arguments: asRecord2(packet.dry_run_arguments ?? packet.dryRunArguments),
6898
+ dryRunArguments: asRecord2(packet.dry_run_arguments ?? packet.dryRunArguments),
6899
+ execute_arguments: asRecord2(packet.execute_arguments ?? packet.executeArguments),
6900
+ executeArguments: asRecord2(packet.execute_arguments ?? packet.executeArguments),
6901
+ execute_and_wait_arguments: asRecord2(packet.execute_and_wait_arguments ?? packet.executeAndWaitArguments),
6902
+ executeAndWaitArguments: asRecord2(packet.execute_and_wait_arguments ?? packet.executeAndWaitArguments),
6903
+ schedule_arguments: asRecord2(packet.schedule_arguments ?? packet.scheduleArguments),
6904
+ scheduleArguments: asRecord2(packet.schedule_arguments ?? packet.scheduleArguments),
6905
+ schedule_and_wait_arguments: asRecord2(packet.schedule_and_wait_arguments ?? packet.scheduleAndWaitArguments),
6906
+ scheduleAndWaitArguments: asRecord2(packet.schedule_and_wait_arguments ?? packet.scheduleAndWaitArguments),
6907
+ result_arguments: asRecord2(packet.result_arguments ?? packet.resultArguments),
6908
+ resultArguments: asRecord2(packet.result_arguments ?? packet.resultArguments)
6909
+ };
6910
+ }
6911
+ function normalizeOpsAutopilotResultPacket(rawPacket) {
6912
+ const packet = asRecord2(rawPacket);
6913
+ const resultShape = Array.isArray(packet.result_shape) ? packet.result_shape.map(String) : Array.isArray(packet.resultShape) ? packet.resultShape.map(String) : [];
6914
+ const deliveryReceipts = Array.isArray(packet.delivery_receipts) ? packet.delivery_receipts.map(String) : Array.isArray(packet.deliveryReceipts) ? packet.deliveryReceipts.map(String) : [];
6915
+ return {
6916
+ ...packet,
6917
+ retrieve_tool: stringValue2(packet.retrieve_tool ?? packet.retrieveTool),
6918
+ retrieveTool: stringValue2(packet.retrieve_tool ?? packet.retrieveTool),
6919
+ result_shape: resultShape,
6920
+ resultShape,
6921
+ delivery_receipts: deliveryReceipts,
6922
+ deliveryReceipts,
6923
+ empty_result_recovery: stringValue2(packet.empty_result_recovery ?? packet.emptyResultRecovery),
6924
+ emptyResultRecovery: stringValue2(packet.empty_result_recovery ?? packet.emptyResultRecovery)
6925
+ };
6926
+ }
6927
+ function normalizeOpsAutopilotOperatingPacket(rawPacket) {
6928
+ if (!rawPacket || typeof rawPacket !== "object" || Array.isArray(rawPacket)) return void 0;
6929
+ const packet = asRecord2(rawPacket);
6930
+ const nangoRoute = normalizeOpsAutopilotNangoRoutePacket(packet.nango_route ?? packet.nangoRoute);
6931
+ const approvalBoundaries = Array.isArray(packet.approval_boundaries) ? packet.approval_boundaries.map(String) : Array.isArray(packet.approvalBoundaries) ? packet.approvalBoundaries.map(String) : [];
6932
+ return {
6933
+ ...packet,
6934
+ north_star: stringValue2(packet.north_star ?? packet.northStar),
6935
+ northStar: stringValue2(packet.north_star ?? packet.northStar),
6936
+ schedule: normalizeOpsAutopilotSchedulePacket(packet.schedule),
6937
+ ...nangoRoute ? { nango_route: nangoRoute, nangoRoute } : {},
6938
+ result_contract: normalizeOpsAutopilotResultPacket(packet.result_contract ?? packet.resultContract),
6939
+ resultContract: normalizeOpsAutopilotResultPacket(packet.result_contract ?? packet.resultContract),
6940
+ approval_boundaries: approvalBoundaries,
6941
+ approvalBoundaries,
6942
+ saved_command_hint: stringValue2(packet.saved_command_hint ?? packet.savedCommandHint),
6943
+ savedCommandHint: stringValue2(packet.saved_command_hint ?? packet.savedCommandHint)
6944
+ };
6945
+ }
5284
6946
  function normalizeOpsAutopilotMotion(rawMotion) {
5285
6947
  const motion = asRecord2(rawMotion);
5286
6948
  const targetCount = numberValue(motion.target_count ?? motion.targetCount);
5287
6949
  const estimatedCredits = numberValue(motion.estimated_credits ?? motion.estimatedCredits);
5288
6950
  const mcpSequence = Array.isArray(motion.mcp_sequence) ? motion.mcp_sequence : Array.isArray(motion.mcpSequence) ? motion.mcpSequence : [];
6951
+ const operatingPacket = normalizeOpsAutopilotOperatingPacket(motion.operating_packet ?? motion.operatingPacket);
5289
6952
  return {
5290
6953
  ...motion,
5291
6954
  id: stringValue2(motion.id),
@@ -5308,12 +6971,16 @@ function normalizeOpsAutopilotMotion(rawMotion) {
5308
6971
  cli_command: optionalString(motion.cli_command ?? motion.cliCommand),
5309
6972
  cliCommand: optionalString(motion.cli_command ?? motion.cliCommand),
5310
6973
  mcp_sequence: mcpSequence,
5311
- mcpSequence
6974
+ mcpSequence,
6975
+ ...operatingPacket ? { operating_packet: operatingPacket, operatingPacket } : {}
5312
6976
  };
5313
6977
  }
5314
6978
  function normalizeOpsAutopilotResult(result) {
5315
6979
  const primary = normalizeOpsAutopilotMotion(result.primary_motion ?? result.primaryMotion);
5316
6980
  const motions = Array.isArray(result.motions) ? result.motions.map(normalizeOpsAutopilotMotion) : [];
6981
+ const operatingPacket = normalizeOpsAutopilotOperatingPacket(
6982
+ result.operating_packet ?? result.operatingPacket ?? primary.operating_packet ?? primary.operatingPacket
6983
+ );
5317
6984
  const queryErrors = Array.isArray(result.query_errors) ? result.query_errors.map(String) : Array.isArray(result.queryErrors) ? result.queryErrors.map(String) : [];
5318
6985
  return {
5319
6986
  ...result,
@@ -5329,6 +6996,7 @@ function normalizeOpsAutopilotResult(result) {
5329
6996
  scaleRule: optionalString(result.scale_rule ?? result.scaleRule),
5330
6997
  agent_handoff: optionalString(result.agent_handoff ?? result.agentHandoff),
5331
6998
  agentHandoff: optionalString(result.agent_handoff ?? result.agentHandoff),
6999
+ ...operatingPacket ? { operating_packet: operatingPacket, operatingPacket } : {},
5332
7000
  proof_state: optionalString(result.proof_state ?? result.proofState),
5333
7001
  proofState: optionalString(result.proof_state ?? result.proofState),
5334
7002
  proof_summary: result.proof_summary ?? result.proofSummary,
@@ -5593,11 +7261,125 @@ function normalizeOpsRoutineTickItemsResult(result) {
5593
7261
  hasMore
5594
7262
  };
5595
7263
  }
7264
+ function buildOpsNangoScheduleBody(input) {
7265
+ const {
7266
+ workspaceConnectionId,
7267
+ workspace_connection_id,
7268
+ connectionId,
7269
+ connection_id,
7270
+ providerConfigKey,
7271
+ provider_config_key,
7272
+ integrationId,
7273
+ integration_id,
7274
+ nangoConnectionId,
7275
+ nango_connection_id,
7276
+ actionName,
7277
+ action_name,
7278
+ toolName,
7279
+ tool_name,
7280
+ proxyPath,
7281
+ proxy_path,
7282
+ nangoProxyPath,
7283
+ nango_proxy_path,
7284
+ method,
7285
+ httpMethod,
7286
+ http_method,
7287
+ input: nangoInput,
7288
+ arguments: nangoArguments,
7289
+ fieldMap,
7290
+ field_map,
7291
+ requiredFields,
7292
+ required_fields,
7293
+ writeConfirmed,
7294
+ write_confirmed,
7295
+ agentWriteConfirmed,
7296
+ agent_write_confirmed,
7297
+ config,
7298
+ destinations: _destinations,
7299
+ ...schedule
7300
+ } = input;
7301
+ return {
7302
+ ...buildOpsCreateBody({
7303
+ ...schedule,
7304
+ cadence: schedule.cadence ?? "daily"
7305
+ }),
7306
+ workspace_connection_id: workspace_connection_id ?? workspaceConnectionId,
7307
+ connection_id: connection_id ?? connectionId,
7308
+ provider_config_key: provider_config_key ?? providerConfigKey,
7309
+ integration_id: integration_id ?? integrationId,
7310
+ nango_connection_id: nango_connection_id ?? nangoConnectionId,
7311
+ action_name: action_name ?? actionName,
7312
+ tool_name: tool_name ?? toolName,
7313
+ proxy_path: proxy_path ?? proxyPath ?? nango_proxy_path ?? nangoProxyPath,
7314
+ nango_proxy_path: nango_proxy_path ?? nangoProxyPath,
7315
+ method: method ?? http_method ?? httpMethod,
7316
+ input: nangoInput ?? nangoArguments,
7317
+ field_map: field_map ?? fieldMap,
7318
+ required_fields: required_fields ?? requiredFields,
7319
+ write_confirmed: write_confirmed ?? writeConfirmed,
7320
+ agent_write_confirmed: agent_write_confirmed ?? agentWriteConfirmed,
7321
+ config
7322
+ };
7323
+ }
7324
+ function buildOpsNangoScheduleAndWaitOptions(params) {
7325
+ const {
7326
+ force,
7327
+ instruction,
7328
+ nextCursor,
7329
+ includeFailedRuns,
7330
+ intervalMs,
7331
+ maxPolls,
7332
+ timeoutMs,
7333
+ includeResults,
7334
+ cursor,
7335
+ state,
7336
+ limit,
7337
+ include_failed_runs,
7338
+ interval_ms,
7339
+ max_polls,
7340
+ timeout_ms,
7341
+ include_results,
7342
+ ...schedule
7343
+ } = params;
7344
+ return {
7345
+ schedule: {
7346
+ ...schedule,
7347
+ cadence: schedule.cadence ?? "daily"
7348
+ },
7349
+ run: {
7350
+ force,
7351
+ instruction
7352
+ },
7353
+ wait: {
7354
+ cursor: cursor ?? nextCursor,
7355
+ state,
7356
+ limit,
7357
+ include_failed_runs: include_failed_runs ?? includeFailedRuns,
7358
+ interval_ms: interval_ms ?? intervalMs,
7359
+ max_polls: max_polls ?? maxPolls,
7360
+ timeout_ms: timeout_ms ?? timeoutMs,
7361
+ include_results: include_results ?? includeResults
7362
+ }
7363
+ };
7364
+ }
7365
+ function buildOpsNangoScheduleAndWaitBody(options) {
7366
+ const waitBody = buildOpsWaitBody(options.wait);
7367
+ const { op_id: _opId, ...waitWithoutOp } = waitBody;
7368
+ return {
7369
+ ...buildOpsNangoScheduleBody(options.schedule),
7370
+ ...waitWithoutOp,
7371
+ instruction: options.run.instruction,
7372
+ force: options.run.force
7373
+ };
7374
+ }
5596
7375
  function buildOpsPlanBody(params) {
5597
- const { targetCount, companyDomains, customAiPrompt, signalPrompt, outputPrompt, ...rest } = params;
7376
+ const { targetCount, wakeOnEvents, companyDomains, customAiPrompt, signalPrompt, outputPrompt, ...rest } = params;
7377
+ const destinations = Array.isArray(rest.destinations) ? rest.destinations.map(normalizeOpsDestinationRequest) : rest.destinations;
5598
7378
  return {
5599
7379
  ...rest,
7380
+ destinations,
5600
7381
  target_count: rest.target_count ?? targetCount,
7382
+ wake_on_events: rest.wake_on_events ?? wakeOnEvents,
5601
7383
  company_domains: rest.company_domains ?? companyDomains,
5602
7384
  custom_ai_prompt: rest.custom_ai_prompt ?? customAiPrompt,
5603
7385
  signal_prompt: rest.signal_prompt ?? signalPrompt,
@@ -5620,6 +7402,95 @@ function buildOpsExecuteBody(params) {
5620
7402
  output_format: rest.output_format ?? outputFormat
5621
7403
  };
5622
7404
  }
7405
+ function buildOpsExecuteAndWaitOptions(params) {
7406
+ const {
7407
+ nextCursor,
7408
+ includeFailedRuns,
7409
+ intervalMs,
7410
+ maxPolls,
7411
+ timeoutMs,
7412
+ includeResults,
7413
+ cursor,
7414
+ state,
7415
+ limit,
7416
+ include_failed_runs,
7417
+ interval_ms,
7418
+ max_polls,
7419
+ timeout_ms,
7420
+ include_results,
7421
+ ...execute
7422
+ } = params;
7423
+ return {
7424
+ execute: {
7425
+ ...execute,
7426
+ auto_run: execute.auto_run ?? execute.autoRun ?? true
7427
+ },
7428
+ wait: {
7429
+ cursor: cursor ?? nextCursor,
7430
+ state,
7431
+ limit,
7432
+ include_failed_runs: include_failed_runs ?? includeFailedRuns,
7433
+ interval_ms: interval_ms ?? intervalMs,
7434
+ max_polls: max_polls ?? maxPolls,
7435
+ timeout_ms: timeout_ms ?? timeoutMs,
7436
+ include_results: include_results ?? includeResults
7437
+ }
7438
+ };
7439
+ }
7440
+ function buildOpsScheduleAndWaitOptions(params) {
7441
+ const {
7442
+ force,
7443
+ instruction,
7444
+ nextCursor,
7445
+ includeFailedRuns,
7446
+ intervalMs,
7447
+ maxPolls,
7448
+ timeoutMs,
7449
+ includeResults,
7450
+ cursor,
7451
+ state,
7452
+ limit,
7453
+ include_failed_runs,
7454
+ interval_ms,
7455
+ max_polls,
7456
+ timeout_ms,
7457
+ include_results,
7458
+ ...schedule
7459
+ } = params;
7460
+ return {
7461
+ schedule: {
7462
+ ...schedule,
7463
+ cadence: schedule.cadence ?? "daily"
7464
+ },
7465
+ run: {
7466
+ force,
7467
+ instruction
7468
+ },
7469
+ wait: {
7470
+ cursor: cursor ?? nextCursor,
7471
+ state,
7472
+ limit,
7473
+ include_failed_runs: include_failed_runs ?? includeFailedRuns,
7474
+ interval_ms: interval_ms ?? intervalMs,
7475
+ max_polls: max_polls ?? maxPolls,
7476
+ timeout_ms: timeout_ms ?? timeoutMs,
7477
+ include_results: include_results ?? includeResults
7478
+ }
7479
+ };
7480
+ }
7481
+ function buildOpsScheduleAndWaitBody(options) {
7482
+ const waitBody = buildOpsWaitBody(options.wait);
7483
+ const { op_id: _opId, ...waitWithoutOp } = waitBody;
7484
+ return {
7485
+ ...buildOpsCreateBody({
7486
+ ...options.schedule,
7487
+ cadence: options.schedule.cadence ?? "daily"
7488
+ }),
7489
+ ...waitWithoutOp,
7490
+ instruction: options.run.instruction,
7491
+ force: options.run.force
7492
+ };
7493
+ }
5623
7494
  function buildOpsRunBody(params) {
5624
7495
  const { opId, ...rest } = params;
5625
7496
  return {
@@ -5627,6 +7498,28 @@ function buildOpsRunBody(params) {
5627
7498
  op_id: rest.op_id ?? opId
5628
7499
  };
5629
7500
  }
7501
+ function buildOpsRunAndWaitOptions(params) {
7502
+ const {
7503
+ opId,
7504
+ nextCursor,
7505
+ includeFailedRuns,
7506
+ intervalMs,
7507
+ maxPolls,
7508
+ timeoutMs,
7509
+ includeResults,
7510
+ ...rest
7511
+ } = params;
7512
+ return {
7513
+ ...rest,
7514
+ op_id: rest.op_id ?? opId,
7515
+ cursor: rest.cursor ?? nextCursor,
7516
+ include_failed_runs: rest.include_failed_runs ?? includeFailedRuns,
7517
+ interval_ms: rest.interval_ms ?? intervalMs,
7518
+ max_polls: rest.max_polls ?? maxPolls,
7519
+ timeout_ms: rest.timeout_ms ?? timeoutMs,
7520
+ include_results: rest.include_results ?? includeResults
7521
+ };
7522
+ }
5630
7523
  function buildOpsStatusBody(params) {
5631
7524
  const { opId, ...rest } = params;
5632
7525
  return {
@@ -5643,6 +7536,50 @@ function buildOpsResultsBody(params) {
5643
7536
  include_failed_runs: rest.include_failed_runs ?? includeFailedRuns
5644
7537
  };
5645
7538
  }
7539
+ function buildOpsWaitForResultsOptions(params) {
7540
+ const {
7541
+ opId,
7542
+ nextCursor,
7543
+ includeFailedRuns,
7544
+ intervalMs,
7545
+ maxPolls,
7546
+ timeoutMs,
7547
+ includeResults,
7548
+ ...rest
7549
+ } = params;
7550
+ return {
7551
+ ...rest,
7552
+ op_id: rest.op_id ?? opId,
7553
+ cursor: rest.cursor ?? nextCursor,
7554
+ include_failed_runs: rest.include_failed_runs ?? includeFailedRuns,
7555
+ interval_ms: rest.interval_ms ?? intervalMs,
7556
+ max_polls: rest.max_polls ?? maxPolls,
7557
+ timeout_ms: rest.timeout_ms ?? timeoutMs,
7558
+ include_results: rest.include_results ?? includeResults
7559
+ };
7560
+ }
7561
+ function buildOpsWaitBody(params) {
7562
+ const {
7563
+ opId,
7564
+ nextCursor,
7565
+ includeFailedRuns,
7566
+ intervalMs,
7567
+ maxPolls,
7568
+ timeoutMs,
7569
+ includeResults,
7570
+ ...rest
7571
+ } = params;
7572
+ return {
7573
+ ...rest,
7574
+ op_id: rest.op_id ?? opId,
7575
+ cursor: rest.cursor ?? nextCursor,
7576
+ include_failed_runs: rest.include_failed_runs ?? includeFailedRuns,
7577
+ interval_ms: rest.interval_ms ?? intervalMs,
7578
+ max_polls: rest.max_polls ?? maxPolls,
7579
+ timeout_ms: rest.timeout_ms ?? timeoutMs,
7580
+ include_results: rest.include_results ?? includeResults
7581
+ };
7582
+ }
5646
7583
  function buildOpsDebugOptions(params) {
5647
7584
  const {
5648
7585
  opId,
@@ -5720,6 +7657,8 @@ function normalizeOpsSinkRequest(sink) {
5720
7657
  nangoAction,
5721
7658
  proxyPath,
5722
7659
  nangoProxyPath,
7660
+ method,
7661
+ httpMethod,
5723
7662
  fieldMap,
5724
7663
  requiredFields,
5725
7664
  writeConfirmed,
@@ -5738,6 +7677,7 @@ function normalizeOpsSinkRequest(sink) {
5738
7677
  const nango_action = rest.nango_action ?? nangoAction;
5739
7678
  const proxy_path = rest.proxy_path ?? proxyPath;
5740
7679
  const nango_proxy_path = rest.nango_proxy_path ?? nangoProxyPath;
7680
+ const http_method = rest.http_method ?? httpMethod;
5741
7681
  const field_map = rest.field_map ?? fieldMap;
5742
7682
  const required_fields = rest.required_fields ?? requiredFields;
5743
7683
  const write_confirmed = rest.write_confirmed ?? writeConfirmed;
@@ -5752,6 +7692,9 @@ function normalizeOpsSinkRequest(sink) {
5752
7692
  if (nango_action !== void 0 && normalizedConfig.nango_action === void 0) normalizedConfig.nango_action = nango_action;
5753
7693
  if (proxy_path !== void 0 && normalizedConfig.proxy_path === void 0) normalizedConfig.proxy_path = proxy_path;
5754
7694
  if (nango_proxy_path !== void 0 && normalizedConfig.nango_proxy_path === void 0) normalizedConfig.nango_proxy_path = nango_proxy_path;
7695
+ if (method !== void 0 && normalizedConfig.method === void 0) normalizedConfig.method = method;
7696
+ if (http_method !== void 0 && normalizedConfig.http_method === void 0) normalizedConfig.http_method = http_method;
7697
+ if (normalizedConfig.method === void 0 && http_method !== void 0) normalizedConfig.method = http_method;
5755
7698
  if (field_map !== void 0 && normalizedConfig.field_map === void 0) normalizedConfig.field_map = field_map;
5756
7699
  if (required_fields !== void 0 && normalizedConfig.required_fields === void 0) normalizedConfig.required_fields = required_fields;
5757
7700
  if (write_confirmed !== void 0 && normalizedConfig.write_confirmed === void 0) normalizedConfig.write_confirmed = write_confirmed;
@@ -5771,6 +7714,8 @@ function normalizeOpsSinkRequest(sink) {
5771
7714
  nango_action,
5772
7715
  proxy_path,
5773
7716
  nango_proxy_path,
7717
+ method,
7718
+ http_method,
5774
7719
  field_map,
5775
7720
  required_fields,
5776
7721
  write_confirmed,
@@ -5778,6 +7723,10 @@ function normalizeOpsSinkRequest(sink) {
5778
7723
  config: normalizedConfig
5779
7724
  };
5780
7725
  }
7726
+ function normalizeOpsDestinationRequest(destination) {
7727
+ if (!destination || typeof destination !== "object" || Array.isArray(destination)) return destination;
7728
+ return normalizeOpsSinkRequest(destination);
7729
+ }
5781
7730
  function normalizeOpsSinkConfigRequest(config) {
5782
7731
  const out = { ...config ?? {} };
5783
7732
  if (out.connection_id === void 0 && out.connectionId !== void 0) out.connection_id = out.connectionId;
@@ -5791,6 +7740,8 @@ function normalizeOpsSinkConfigRequest(config) {
5791
7740
  if (out.nango_action === void 0 && out.nangoAction !== void 0) out.nango_action = out.nangoAction;
5792
7741
  if (out.proxy_path === void 0 && out.proxyPath !== void 0) out.proxy_path = out.proxyPath;
5793
7742
  if (out.nango_proxy_path === void 0 && out.nangoProxyPath !== void 0) out.nango_proxy_path = out.nangoProxyPath;
7743
+ if (out.http_method === void 0 && out.httpMethod !== void 0) out.http_method = out.httpMethod;
7744
+ if (out.method === void 0 && out.http_method !== void 0) out.method = out.http_method;
5794
7745
  if (out.field_map === void 0 && out.fieldMap !== void 0) out.field_map = out.fieldMap;
5795
7746
  if (out.required_fields === void 0 && out.requiredFields !== void 0) out.required_fields = out.requiredFields;
5796
7747
  if (out.write_confirmed === void 0 && out.writeConfirmed !== void 0) out.write_confirmed = out.writeConfirmed;
@@ -5864,7 +7815,6 @@ function toSnakeConfig(params) {
5864
7815
  return {
5865
7816
  system_prompt: params.systemPrompt || params.system_prompt || "You are a senior business research analyst. Use multi-model evidence carefully and return concise structured enrichment.",
5866
7817
  user_template: params.userTemplate || params.user_template || params.prompt,
5867
- model: params.model,
5868
7818
  temperature: params.temperature,
5869
7819
  max_concurrency: params.maxConcurrency || params.max_concurrency,
5870
7820
  max_tokens: params.maxTokens || params.max_tokens,
@@ -5888,13 +7838,10 @@ var Ai = class {
5888
7838
  /**
5889
7839
  * Run Custom AI Enrichment - Multi Model.
5890
7840
  *
5891
- * Uses OpenRouter Fusion for multi-model analysis with web search/fetch enabled
5892
- * inside Fusion, then synthesizes the requested structured output fields.
7841
+ * Uses the hosted default model with bounded synthesis, then returns the
7842
+ * requested structured output fields.
5893
7843
  */
5894
7844
  async multiModel(params) {
5895
- if (!params.model) {
5896
- throw new Error('model is required. Use an OpenRouter id such as "anthropic/claude-sonnet-4" or "google/gemini-2.5-flash".');
5897
- }
5898
7845
  if (!params.prompt && !params.userTemplate && !params.user_template) {
5899
7846
  throw new Error("prompt or userTemplate is required.");
5900
7847
  }
@@ -6111,6 +8058,10 @@ var GtmKernel = class {
6111
8058
  days: options.days,
6112
8059
  network_days: options.networkDays,
6113
8060
  min_sample_size: options.minSampleSize,
8061
+ holdout_min_sample_size: options.holdoutMinSampleSize,
8062
+ predictive_min_labeled_outcomes: options.predictiveMinLabeledOutcomes,
8063
+ predictive_min_positive_outcomes: options.predictiveMinPositiveOutcomes,
8064
+ predictive_min_memory_sample_size: options.predictiveMinMemorySampleSize,
6114
8065
  min_workspace_count: options.minWorkspaceCount,
6115
8066
  min_privacy_k: options.minPrivacyK,
6116
8067
  include_memory: options.includeMemory,
@@ -6177,9 +8128,11 @@ var GtmKernel = class {
6177
8128
  write_routine_outcomes: input.writeRoutineOutcomes
6178
8129
  });
6179
8130
  }
6180
- /** Start a background Instantly webhook-events pull and route results into the GTM feedback loop. */
8131
+ /** Start a background Instantly webhook-events or email-history pull and route results into the GTM feedback loop. */
6181
8132
  async pullInstantlyFeedback(input) {
6182
8133
  return this.callMcp("gtm_instantly_feedback_pull", {
8134
+ source: input.source,
8135
+ instantly_profile: input.instantlyProfile,
6183
8136
  campaign_id: input.campaignId,
6184
8137
  campaign_build_id: input.campaignBuildId,
6185
8138
  provider_link_id: input.providerLinkId,
@@ -6191,6 +8144,9 @@ var GtmKernel = class {
6191
8144
  to: input.to,
6192
8145
  limit: input.limit,
6193
8146
  max_pages: input.maxPages,
8147
+ email_type: input.emailType,
8148
+ mode: input.mode,
8149
+ sort_order: input.sortOrder,
6194
8150
  create_memory: input.createMemory,
6195
8151
  write_routine_outcomes: input.writeRoutineOutcomes,
6196
8152
  dry_run: input.dryRun,
@@ -6261,6 +8217,16 @@ var GtmKernel = class {
6261
8217
  min_privacy_k: input.minPrivacyK
6262
8218
  });
6263
8219
  }
8220
+ /** Queue a dry-run-first bridge from imported historical feedback to attribution-only Campaign Build surrogates. */
8221
+ async runHistoricalCampaignBuildBridge(input = {}) {
8222
+ return this.callMcp("gtm_historical_campaign_build_bridge_run", {
8223
+ dry_run: input.dryRun,
8224
+ write_approved: input.writeApproved,
8225
+ max_events: input.maxEvents,
8226
+ max_campaigns: input.maxCampaigns,
8227
+ min_feedback_events: input.minFeedbackEvents
8228
+ });
8229
+ }
6264
8230
  /** Prepare a provider-agnostic feedback webhook URL for Smartlead, HeyReach, Airbyte, or custom sender events. */
6265
8231
  async prepareFeedbackWebhook(input) {
6266
8232
  return this.callMcp("gtm_feedback_webhook_prepare", {
@@ -6320,6 +8286,7 @@ var GtmKernel = class {
6320
8286
  days: input.days,
6321
8287
  network_days: input.networkDays,
6322
8288
  min_sample_size: input.minSampleSize,
8289
+ holdout_min_sample_size: input.holdoutMinSampleSize,
6323
8290
  min_workspace_count: input.minWorkspaceCount,
6324
8291
  min_privacy_k: input.minPrivacyK,
6325
8292
  include_memory: input.includeMemory,
@@ -6340,6 +8307,7 @@ var GtmKernel = class {
6340
8307
  days: input.days,
6341
8308
  network_days: input.networkDays,
6342
8309
  min_sample_size: input.minSampleSize,
8310
+ holdout_min_sample_size: input.holdoutMinSampleSize,
6343
8311
  min_workspace_count: input.minWorkspaceCount,
6344
8312
  min_privacy_k: input.minPrivacyK,
6345
8313
  include_memory: input.includeMemory,
@@ -6464,6 +8432,29 @@ var GtmKernel = class {
6464
8432
  include_details: options.includeDetails
6465
8433
  });
6466
8434
  }
8435
+ /** Search the Agency Autopilot safe-action catalog for agency-style lead lists, Clay tables, and GTM motions. */
8436
+ async agencyAutopilotCapabilityCatalog(options = {}) {
8437
+ return this.callMcp("gtm_agency_autopilot_capability_catalog", {
8438
+ query: options.query,
8439
+ family: options.family,
8440
+ phase: options.phase,
8441
+ account_label: options.accountLabel,
8442
+ workspace_label: options.workspaceLabel,
8443
+ target_count: options.targetCount,
8444
+ limit: options.limit,
8445
+ include_agent_prompts: options.includeAgentPrompts
8446
+ });
8447
+ }
8448
+ /** Search agency operating playbooks Agency Autopilot can use for lead lists, Clay tables, GTM motions, and proof gates. */
8449
+ async agencyAutopilotPlaybookCatalog(options = {}) {
8450
+ return this.callMcp("gtm_agency_autopilot_playbook_catalog", {
8451
+ query: options.query,
8452
+ playbook_slug: options.playbookSlug,
8453
+ outcome_type: options.outcomeType,
8454
+ limit: options.limit,
8455
+ include_required_fields: options.includeRequiredFields
8456
+ });
8457
+ }
6467
8458
  /** Build the read-only Memory, Brain, risk, and provider-route runbook needed before planning a campaign. */
6468
8459
  async campaignStartContext(input = {}) {
6469
8460
  return this.callMcp("gtm_campaign_start_context", {
@@ -6822,6 +8813,35 @@ var GtmKernel = class {
6822
8813
  user_display_name: input.userDisplayName
6823
8814
  });
6824
8815
  }
8816
+ /** Prepare the full Nango provider search, connect, pull/export, Ops, and campaign-route flow. */
8817
+ async prepareNangoIntegrationFlow(input = {}) {
8818
+ return this.callMcp("nango_integration_flow_prepare", {
8819
+ query: input.query,
8820
+ search: input.search,
8821
+ provider_id: input.providerId,
8822
+ provider: input.provider,
8823
+ integration_id: input.integrationId,
8824
+ provider_config_key: input.providerConfigKey,
8825
+ workspace_connection_id: input.workspaceConnectionId,
8826
+ connection_id: input.connectionId,
8827
+ nango_connection_id: input.nangoConnectionId,
8828
+ intent: input.intent,
8829
+ action_name: input.actionName,
8830
+ tool_name: input.toolName,
8831
+ proxy_path: input.proxyPath,
8832
+ method: input.method,
8833
+ input: input.input,
8834
+ cadence: input.cadence,
8835
+ field_map: input.fieldMap,
8836
+ required_fields: input.requiredFields,
8837
+ confirm_spend: input.confirmSpend,
8838
+ write_confirmed: input.writeConfirmed,
8839
+ layer: input.layer,
8840
+ campaign_id: input.campaignId,
8841
+ limit: input.limit,
8842
+ include_provider_catalog: input.includeProviderCatalog
8843
+ });
8844
+ }
6825
8845
  /** List Nango-backed action tools for a workspace connection without executing them. */
6826
8846
  async listNangoTools(options = {}) {
6827
8847
  return this.callMcp("nango_mcp_tools_list", {
@@ -6844,6 +8864,12 @@ var GtmKernel = class {
6844
8864
  nango_connection_id: input.nangoConnectionId,
6845
8865
  action_name: input.actionName,
6846
8866
  tool_name: input.toolName,
8867
+ delivery_mode: input.deliveryMode,
8868
+ proxy_path: input.proxyPath,
8869
+ nango_proxy_path: input.nangoProxyPath,
8870
+ method: input.method,
8871
+ http_method: input.httpMethod,
8872
+ proxy_headers: input.proxyHeaders,
6847
8873
  input: input.input,
6848
8874
  async: input.async,
6849
8875
  max_retries: input.maxRetries,
@@ -6993,6 +9019,7 @@ function compact2(value) {
6993
9019
  }
6994
9020
 
6995
9021
  // src/index.ts
9022
+ var MCP_TOOL_CACHE_TTL_MS = 3e4;
6996
9023
  function inferMcpToolCategory(toolName) {
6997
9024
  if (typeof toolName !== "string" || !toolName) return void 0;
6998
9025
  if (toolName.startsWith("ops_") || toolName === "get_ops_readiness" || toolName === "get_gtm_ops_readiness" || toolName.startsWith("gtm_") || [
@@ -7124,6 +9151,21 @@ var Signaliz = class {
7124
9151
  }
7125
9152
  /** List all available MCP tools */
7126
9153
  async listTools() {
9154
+ const now = Date.now();
9155
+ if (this.toolListCache && this.toolListCache.expiresAt > now) {
9156
+ return this.toolListCache.promise;
9157
+ }
9158
+ const promise = this.fetchTools().catch((error) => {
9159
+ if (this.toolListCache?.promise === promise) this.toolListCache = void 0;
9160
+ throw error;
9161
+ });
9162
+ this.toolListCache = {
9163
+ expiresAt: now + MCP_TOOL_CACHE_TTL_MS,
9164
+ promise
9165
+ };
9166
+ return promise;
9167
+ }
9168
+ async fetchTools() {
7127
9169
  try {
7128
9170
  const manifest = await this.client.mcp("tools/call", {
7129
9171
  name: "get_tool_manifest",