@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.
@@ -243,25 +243,31 @@ var HttpClient = class {
243
243
  async getToken() {
244
244
  if (this.apiKey) return this.apiKey;
245
245
  if (this.accessToken) return this.accessToken;
246
- const res = await fetch("https://api.signaliz.com/token", {
247
- method: "POST",
248
- headers: { "Content-Type": "application/json" },
249
- body: JSON.stringify({
250
- grant_type: "client_credentials",
251
- client_id: this.clientId,
252
- client_secret: this.clientSecret
253
- })
254
- });
255
- if (!res.ok) {
256
- throw new SignalizError({
257
- code: "AUTH_FAILED",
258
- message: "Failed to obtain access token",
259
- errorType: "auth_expired"
246
+ if (this.accessTokenPromise) return this.accessTokenPromise;
247
+ this.accessTokenPromise = (async () => {
248
+ const res = await fetch("https://api.signaliz.com/token", {
249
+ method: "POST",
250
+ headers: { "Content-Type": "application/json" },
251
+ body: JSON.stringify({
252
+ grant_type: "client_credentials",
253
+ client_id: this.clientId,
254
+ client_secret: this.clientSecret
255
+ })
260
256
  });
261
- }
262
- const data = await res.json();
263
- this.accessToken = data.access_token;
264
- return this.accessToken;
257
+ if (!res.ok) {
258
+ throw new SignalizError({
259
+ code: "AUTH_FAILED",
260
+ message: "Failed to obtain access token",
261
+ errorType: "auth_expired"
262
+ });
263
+ }
264
+ const data = await res.json();
265
+ this.accessToken = data.access_token;
266
+ return this.accessToken;
267
+ })().finally(() => {
268
+ this.accessTokenPromise = void 0;
269
+ });
270
+ return this.accessTokenPromise;
265
271
  }
266
272
  };
267
273
  function sleep(ms) {
@@ -510,7 +516,6 @@ var Signals = class {
510
516
  if (params.online !== void 0) args.online = params.online;
511
517
  if (params.targetSignalCount) args.target_signal_count = params.targetSignalCount;
512
518
  if (params.lookbackDays) args.lookback_days = params.lookbackDays;
513
- if (params.model) args.model = params.model;
514
519
  if (params.enableDeepSearch) args.enable_deep_search = params.enableDeepSearch;
515
520
  if (params.enableOutreachIntelligence) args.enable_outreach_intelligence = params.enableOutreachIntelligence;
516
521
  if (params.enablePredictiveIntelligence) args.enable_predictive_intelligence = params.enablePredictiveIntelligence;
@@ -805,6 +810,10 @@ var Campaigns = class {
805
810
  async artifacts(campaignBuildId) {
806
811
  return this.listCampaignBuildArtifacts(campaignBuildId);
807
812
  }
813
+ /** Generate fresh signed URLs for a downloadable campaign artifact. */
814
+ async downloadArtifact(campaignBuildId, options) {
815
+ return this.downloadCampaignArtifact(campaignBuildId, options);
816
+ }
808
817
  /** Cancel a queued or running build. */
809
818
  async cancel(campaignBuildId, reason) {
810
819
  return this.cancelCampaignBuild(campaignBuildId, reason);
@@ -849,13 +858,21 @@ var Campaigns = class {
849
858
  dryRun: isDryRun,
850
859
  requestedTargetCount: data.requested_target_count,
851
860
  plannedTargetCount: data.planned_target_count,
861
+ acquisitionMode: data.acquisition_mode,
862
+ acquisitionSource: data.acquisition_source,
863
+ cacheReusePolicy: data.cache_reuse_policy,
852
864
  estimatedCredits: data.estimated_credits,
853
865
  estimatedDurationSeconds: data.estimated_duration_seconds,
854
866
  targetLimitApplied: data.target_limit_applied,
855
867
  targetLimitReason: data.target_limit_reason,
856
868
  maxSupportedTargetCount: data.max_supported_target_count,
857
869
  brainContext: data.brain_context ?? {},
858
- providerRoute: mapProviderRoute(data)
870
+ learningHoldout: data.learning_holdout ?? {},
871
+ providerRoute: mapProviderRoute(data),
872
+ effectiveSourceRouting: recordOrNull(data.effective_source_routing ?? data.source_routing),
873
+ sourcePlan: recordOrNull(data.source_plan ?? data.plan?.source_plan),
874
+ sourceRoutes: recordArray(data.source_routes ?? data.source_plan?.routes ?? data.plan?.source_plan?.routes),
875
+ sourceShards: recordArray(data.source_shards ?? data.source_plan?.shards ?? data.plan?.source_plan?.shards)
859
876
  };
860
877
  }
861
878
  async getCampaignBuildStatus(campaignBuildId) {
@@ -870,14 +887,24 @@ var Campaigns = class {
870
887
  });
871
888
  return (data.artifacts ?? []).map(mapArtifact);
872
889
  }
890
+ async downloadCampaignArtifact(campaignBuildId, options) {
891
+ const args = { campaign_build_id: campaignBuildId };
892
+ if (options?.format) args.format = options.format;
893
+ const data = await this.callMcp("download_campaign_artifact", args);
894
+ return mapArtifactDownloadResult(data);
895
+ }
873
896
  async getCampaignBuildRows(campaignBuildId, options) {
874
897
  const args = { campaign_build_id: campaignBuildId };
875
898
  if (options?.limit) args.page_size = options.limit;
876
899
  if (options?.cursor) args.cursor = options.cursor;
900
+ if (options?.includeData !== void 0) args.include_data = options.includeData;
901
+ if (options?.includeRaw !== void 0) args.include_raw = options.includeRaw;
877
902
  const filters = {};
878
903
  if (options?.segment) filters.segment = options.segment;
879
904
  if (options?.rowStatus) filters.row_status = options.rowStatus;
880
905
  if (options?.qualified !== void 0) filters.qualified = options.qualified;
906
+ if (options?.cached !== void 0) filters.cached = options.cached;
907
+ if (options?.exportReady !== void 0) filters.export_ready = options.exportReady;
881
908
  if (Object.keys(filters).length > 0) args.filters = filters;
882
909
  const data = await this.callMcp("get_campaign_build_rows", args);
883
910
  return {
@@ -902,6 +929,7 @@ var Campaigns = class {
902
929
  };
903
930
  if (options?.destinationId) args.destination_id = options.destinationId;
904
931
  if (options?.destinationConfig) args.destination_config = options.destinationConfig;
932
+ if (options?.allowPartialDelivery === true) args.allow_partial_delivery = true;
905
933
  const data = await this.callMcp("approve_campaign_delivery", args);
906
934
  return {
907
935
  campaignBuildId: data.campaign_build_id,
@@ -910,7 +938,10 @@ var Campaigns = class {
910
938
  message: data.message ?? "",
911
939
  deliveryId: data.delivery_id,
912
940
  deliveryIds: data.delivery_ids,
913
- approvedCount: data.approved_count
941
+ approvedCount: data.approved_count,
942
+ partialDelivery: data.partial_delivery,
943
+ effectiveTargetCount: data.effective_target_count,
944
+ requestedTargetCount: data.requested_target_count
914
945
  };
915
946
  }
916
947
  async cancelCampaignBuild(campaignBuildId, reason) {
@@ -963,17 +994,50 @@ function mapStatus(data) {
963
994
  warnings: diagnosticMessages(data.warnings),
964
995
  errors: diagnosticMessages(data.errors),
965
996
  artifactCount: data.artifact_count ?? 0,
997
+ artifactDownloads: Array.isArray(data.artifact_downloads) ? data.artifact_downloads.map(mapArtifactDownload) : [],
998
+ customerRowCounts: mapCustomerRowCounts(data.customer_row_counts),
999
+ phaseAttemptTotals: recordOrNull(data.phase_attempt_totals) ?? void 0,
966
1000
  providerRoute: mapProviderRoute(data),
967
1001
  triggerRunId: typeof data.trigger_run_id === "string" ? data.trigger_run_id : null,
968
1002
  staleRunningPhase: data.stale_running_phase === true,
969
1003
  phaseAgeSeconds: numberOrNull(data.phase_age_seconds),
970
1004
  diagnostics: recordOrNull(data.diagnostics) ?? {},
971
1005
  nextAction: formatNextAction(data.next_action),
1006
+ approvalAction: formatNextAction(data.approval_action),
972
1007
  createdAt: data.created_at,
973
1008
  updatedAt: data.updated_at,
974
1009
  completedAt: data.completed_at
975
1010
  };
976
1011
  }
1012
+ function mapCustomerRowCounts(value) {
1013
+ const record = recordOrNull(value);
1014
+ if (!record) return void 0;
1015
+ return {
1016
+ requestedTarget: numberOrNull(record.requested_target),
1017
+ acquiredRows: numberOrNull(record.acquired_rows) ?? void 0,
1018
+ acceptedRows: numberOrNull(record.accepted_rows) ?? void 0,
1019
+ usableRows: numberOrNull(record.usable_rows) ?? void 0,
1020
+ copiedRows: numberOrNull(record.copied_rows) ?? void 0,
1021
+ approvalReadyRows: numberOrNull(record.approval_ready_rows) ?? void 0,
1022
+ qaReadyRows: numberOrNull(record.qa_ready_rows) ?? void 0,
1023
+ deliveredRows: numberOrNull(record.delivered_rows) ?? void 0,
1024
+ disqualifiedRows: numberOrNull(record.disqualified_rows) ?? void 0,
1025
+ reviewableRows: numberOrNull(record.reviewable_rows) ?? void 0,
1026
+ rowFailures: numberOrNull(record.row_failures) ?? void 0,
1027
+ acceptedShortfall: numberOrNull(record.accepted_shortfall),
1028
+ usableShortfall: numberOrNull(record.usable_shortfall),
1029
+ qaReadyShortfall: numberOrNull(record.qa_ready_shortfall),
1030
+ deliveryShortfall: numberOrNull(record.delivery_shortfall),
1031
+ fillRatio: numberOrNull(record.fill_ratio),
1032
+ qaReadyFillRatio: numberOrNull(record.qa_ready_fill_ratio),
1033
+ deliveredFillRatio: numberOrNull(record.delivered_fill_ratio),
1034
+ terminalReason: typeof record.terminal_reason === "string" ? record.terminal_reason : null,
1035
+ acceptedShortfallReason: typeof record.accepted_shortfall_reason === "string" ? record.accepted_shortfall_reason : null,
1036
+ usableShortfallReason: typeof record.usable_shortfall_reason === "string" ? record.usable_shortfall_reason : null,
1037
+ qaReadyShortfallReason: typeof record.qa_ready_shortfall_reason === "string" ? record.qa_ready_shortfall_reason : null,
1038
+ deliveryShortfallReason: typeof record.delivery_shortfall_reason === "string" ? record.delivery_shortfall_reason : null
1039
+ };
1040
+ }
977
1041
  function mapProviderRoute(data) {
978
1042
  const brainContext = recordOrNull(data?.brain_context);
979
1043
  const plan = recordOrNull(data?.provider_route_plan) ?? recordOrNull(brainContext?.provider_route_plan);
@@ -1000,6 +1064,9 @@ function recordOrNull(value) {
1000
1064
  function stringArray(value) {
1001
1065
  return Array.isArray(value) ? value.map((item) => String(item || "").trim()).filter(Boolean) : [];
1002
1066
  }
1067
+ function recordArray(value) {
1068
+ return Array.isArray(value) ? value.map(recordOrNull).filter((item) => Boolean(item)) : [];
1069
+ }
1003
1070
  function diagnosticMessages(value) {
1004
1071
  if (!Array.isArray(value)) return [];
1005
1072
  return value.map((item) => {
@@ -1051,13 +1118,56 @@ function mapArtifact(a) {
1051
1118
  artifactType: a.artifact_type,
1052
1119
  destination: a.destination,
1053
1120
  storagePath: a.storage_path,
1121
+ format: a.format ?? null,
1054
1122
  signedUrl: a.signed_url,
1123
+ downloadUrl: a.download_url ?? null,
1124
+ manifestUrl: a.manifest_url ?? null,
1125
+ expiresAt: a.expires_at ?? null,
1055
1126
  rowCount: a.row_count ?? 0,
1056
1127
  checksum: a.checksum,
1057
1128
  metadata: a.metadata ?? {},
1058
1129
  createdAt: a.created_at
1059
1130
  };
1060
1131
  }
1132
+ function mapArtifactDownload(data) {
1133
+ return {
1134
+ id: data.id,
1135
+ artifactType: data.artifact_type ?? null,
1136
+ format: data.format ?? null,
1137
+ rowCount: data.row_count ?? 0,
1138
+ signedUrl: data.signed_url ?? null,
1139
+ downloadUrl: data.download_url ?? null,
1140
+ manifestUrl: data.manifest_url ?? null,
1141
+ expiresAt: data.expires_at ?? null
1142
+ };
1143
+ }
1144
+ function mapArtifactDownloadResult(data) {
1145
+ return {
1146
+ campaignBuildId: data.campaign_build_id,
1147
+ artifactId: data.artifact_id,
1148
+ format: data.format,
1149
+ rowCount: data.row_count ?? 0,
1150
+ byteSize: numberOrNull(data.byte_size),
1151
+ partCount: data.part_count ?? 0,
1152
+ checksum: data.checksum ?? null,
1153
+ signedUrl: data.signed_url ?? null,
1154
+ downloadUrl: data.download_url ?? null,
1155
+ manifestUrl: data.manifest_url ?? null,
1156
+ expiresAt: data.expires_at ?? null,
1157
+ parts: Array.isArray(data.parts) ? data.parts.map(mapArtifactDownloadPart) : [],
1158
+ downloadCommands: recordOrNull(data.download_commands) ?? {},
1159
+ expiresInSeconds: numberOrNull(data.expires_in_seconds)
1160
+ };
1161
+ }
1162
+ function mapArtifactDownloadPart(data) {
1163
+ return {
1164
+ partIndex: data.part_index ?? 0,
1165
+ rowCount: data.row_count ?? 0,
1166
+ byteSize: numberOrNull(data.byte_size),
1167
+ checksum: data.checksum ?? null,
1168
+ signedUrl: data.signed_url ?? null
1169
+ };
1170
+ }
1061
1171
  function mapScope(data) {
1062
1172
  return {
1063
1173
  campaignScopeId: data.campaign_scope_id,
@@ -1111,8 +1221,30 @@ function buildArgs(request) {
1111
1221
  if (request.allowDownscale !== void 0) args.allow_downscale = request.allowDownscale;
1112
1222
  if (request.confirmSpend !== void 0) args.confirm_spend = request.confirmSpend;
1113
1223
  if (request.dedupKeys) args.dedup_keys = request.dedupKeys;
1224
+ if (request.acquisitionMode) args.acquisition_mode = request.acquisitionMode;
1225
+ if (request.cacheReusePolicy) {
1226
+ args.cache_reuse_policy = {
1227
+ allow_verified_cache: request.cacheReusePolicy.allowVerifiedCache,
1228
+ suppress_prior_campaign_ids: request.cacheReusePolicy.suppressPriorCampaignIds,
1229
+ suppress_prior_list_ids: request.cacheReusePolicy.suppressPriorListIds,
1230
+ uniqueness: request.cacheReusePolicy.uniqueness,
1231
+ require_usage_metadata: request.cacheReusePolicy.requireUsageMetadata,
1232
+ verified_cache_ttl_days: request.cacheReusePolicy.verifiedCacheTtlDays
1233
+ };
1234
+ }
1114
1235
  if (request.brainPreflight) args.brain_preflight = request.brainPreflight;
1115
1236
  if (request.brainDefaults) args.brain_defaults = request.brainDefaults;
1237
+ if (request.learningHoldout) {
1238
+ args.learning_holdout = {
1239
+ enabled: request.learningHoldout.enabled,
1240
+ control_percentage: request.learningHoldout.controlPercentage,
1241
+ min_control_size: request.learningHoldout.minControlSize,
1242
+ experiment_key: request.learningHoldout.experimentKey,
1243
+ source_campaign_id: request.learningHoldout.sourceCampaignId,
1244
+ primary_metric: request.learningHoldout.primaryMetric
1245
+ };
1246
+ }
1247
+ if (request.campaignStrategyContext) args.campaign_strategy_context = request.campaignStrategyContext;
1116
1248
  if (request.deliveryRisk) args.delivery_risk = request.deliveryRisk;
1117
1249
  if (request.icp) {
1118
1250
  args.icp = {
@@ -1154,7 +1286,17 @@ function buildArgs(request) {
1154
1286
  max_body_words: request.copy.maxBodyWords,
1155
1287
  sender_context: request.copy.senderContext,
1156
1288
  offer_context: request.copy.offerContext,
1157
- model: request.copy.model
1289
+ model: request.copy.model,
1290
+ style_source: request.copy.styleSource ? {
1291
+ sample_text: request.copy.styleSource.sampleText,
1292
+ campaign_ids: request.copy.styleSource.campaignIds,
1293
+ artifact_ids: request.copy.styleSource.artifactIds
1294
+ } : void 0,
1295
+ sequence_steps: request.copy.sequenceSteps,
1296
+ copy_schema: request.copy.copySchema,
1297
+ banned_phrases: request.copy.bannedPhrases,
1298
+ approval_required: request.copy.approvalRequired,
1299
+ quality_tier: request.copy.qualityTier
1158
1300
  };
1159
1301
  }
1160
1302
  if (request.delivery) {
@@ -1217,6 +1359,8 @@ var DEFAULT_SIGNALIZ_CAMPAIGN_BUILDER_DEFAULTS = {
1217
1359
  }
1218
1360
  };
1219
1361
  var DEFAULT_CAMPAIGN_BUILDER_BUILT_INS = [
1362
+ "company_discovery",
1363
+ "people_discovery",
1220
1364
  "lead_generation",
1221
1365
  "email_finding",
1222
1366
  "email_verification",
@@ -1225,6 +1369,8 @@ var DEFAULT_CAMPAIGN_BUILDER_BUILT_INS = [
1225
1369
  var CAMPAIGN_BUILDER_BUILT_IN_ROUTE_IDS = {
1226
1370
  lead_generation: "signaliz-lead-generation",
1227
1371
  local_leads: "signaliz-local-leads",
1372
+ company_discovery: "signaliz-company-discovery",
1373
+ people_discovery: "signaliz-people-discovery",
1228
1374
  email_finding: "signaliz-email-finding",
1229
1375
  email_verification: "signaliz-email-verification",
1230
1376
  signals: "signaliz-signals"
@@ -1242,8 +1388,8 @@ var CAMPAIGN_BUILDER_OPERATING_PLAYBOOKS = [
1242
1388
  slug: "cache-first-large-list",
1243
1389
  label: "Cache-First Large List",
1244
1390
  whenToUse: [
1245
- "The target count is high and reusable workspace lead or cache coverage may exist.",
1246
- "The deliverable is a verified list or top-up rather than a deeply custom one-off workflow."
1391
+ "The operator explicitly references a previous campaign, prior list, seed list, or suppression baseline.",
1392
+ "The deliverable is a top-up, continuation, or backfill where prior campaign context should guide reuse."
1247
1393
  ],
1248
1394
  sequence: [
1249
1395
  "Inventory reusable cache before paid sourcing.",
@@ -1449,7 +1595,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1449
1595
  ],
1450
1596
  requireVerifiedEmail: true
1451
1597
  },
1452
- builtIns: ["lead_generation", "email_finding", "email_verification", "signals"],
1598
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "email_finding", "email_verification", "signals"],
1453
1599
  preferredProviders: ["signaliz_native"],
1454
1600
  memoryQueries: [{
1455
1601
  query: "industrial OT net-new prior email suppression verified manufacturing embedded systems field service cyber resilience business continuity",
@@ -1523,7 +1669,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1523
1669
  ],
1524
1670
  requireVerifiedEmail: true
1525
1671
  },
1526
- builtIns: ["lead_generation", "local_leads", "email_finding", "email_verification", "signals"],
1672
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "local_leads", "email_finding", "email_verification", "signals"],
1527
1673
  preferredProviders: ["signaliz_native", "octave", "clay_webhook", "apify"],
1528
1674
  memoryQueries: [{
1529
1675
  query: "mature non-medical home care agencies billable hours franchise caregiver recruiting proof gate qualification export approval",
@@ -1589,7 +1735,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1589
1735
  exclusions: ["enterprise holding company", "staffing", "recruiting", "software-only vendor", "freelancer-only profile"],
1590
1736
  requireVerifiedEmail: true
1591
1737
  },
1592
- builtIns: ["lead_generation", "email_finding", "email_verification", "signals"],
1738
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "email_finding", "email_verification", "signals"],
1593
1739
  preferredProviders: ["signaliz_native", "octave"],
1594
1740
  memoryQueries: [{
1595
1741
  query: "agency founder owner CEO marketing services suppression domain pool email verification final approval gate",
@@ -1671,7 +1817,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1671
1817
  ],
1672
1818
  requireVerifiedEmail: true
1673
1819
  },
1674
- builtIns: ["lead_generation", "email_finding", "email_verification", "signals"],
1820
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "email_finding", "email_verification", "signals"],
1675
1821
  preferredProviders: ["signaliz_native", "octave"],
1676
1822
  memoryQueries: [{
1677
1823
  query: "cloud hosting infrastructure VMware Broadcom qualification verified email persona trigger SKU mapping",
@@ -1704,25 +1850,21 @@ var CampaignBuilderAgent = class {
1704
1850
  async createPlan(request, options = {}) {
1705
1851
  const resolvedRequest = applyCampaignBuilderStrategyTemplate(request);
1706
1852
  const warnings = [];
1707
- const workspaceContext = resolvedRequest.workspaceContext ?? await this.getWorkspaceContext(warnings);
1708
- const scopeResult = options.scopeCampaign === false ? void 0 : await this.scopeCampaign(resolvedRequest, warnings);
1709
- if (options.discoverCapabilities !== false) {
1710
- await this.discoverPlannedRoutes(resolvedRequest, warnings);
1711
- }
1853
+ const [workspaceContext, scopeResult] = await Promise.all([
1854
+ resolvedRequest.workspaceContext ? Promise.resolve(resolvedRequest.workspaceContext) : this.getWorkspaceContext(warnings),
1855
+ options.scopeCampaign === false ? Promise.resolve(void 0) : this.scopeCampaign(resolvedRequest, warnings),
1856
+ options.discoverCapabilities === false ? Promise.resolve(void 0) : this.discoverPlannedRoutes(resolvedRequest, warnings)
1857
+ ]);
1712
1858
  const plan = createCampaignBuilderAgentPlan(resolvedRequest, {
1713
1859
  workspaceContext,
1714
1860
  scopeBuildArgs: asRecord(scopeResult?.build_campaign_args ?? scopeResult?.buildCampaignArgs),
1715
1861
  warnings
1716
1862
  });
1717
- if (options.includeStrategyMemoryStatus !== false) {
1718
- await this.attachStrategyMemoryStatus(plan, warnings);
1719
- }
1720
- if (options.includeKernelPlan !== false) {
1721
- await this.attachKernelCampaignBuildPlan(plan, warnings);
1722
- }
1723
- if (options.includeDeliveryRisk !== false) {
1724
- await this.attachDeliveryRiskPreflight(plan, warnings);
1725
- }
1863
+ await Promise.all([
1864
+ options.includeStrategyMemoryStatus === false ? Promise.resolve() : this.attachStrategyMemoryStatus(plan, warnings),
1865
+ options.includeKernelPlan === false ? Promise.resolve() : this.attachKernelCampaignBuildPlan(plan, warnings),
1866
+ options.includeDeliveryRisk === false ? Promise.resolve() : this.attachDeliveryRiskPreflight(plan, warnings)
1867
+ ]);
1726
1868
  return plan;
1727
1869
  }
1728
1870
  async readiness(request, options = {}) {
@@ -1770,6 +1912,8 @@ var CampaignBuilderAgent = class {
1770
1912
  },
1771
1913
  brain_defaults: plan.buildRequest.brainDefaults,
1772
1914
  brain_preflight: plan.buildRequest.brainPreflight,
1915
+ learning_holdout: plan.buildRequest.learningHoldout,
1916
+ campaign_strategy_context: plan.buildRequest.campaignStrategyContext,
1773
1917
  delivery_risk_preflight: plan.buildRequest.deliveryRisk,
1774
1918
  delivery_risk: plan.buildRequest.deliveryRisk
1775
1919
  }),
@@ -1782,7 +1926,8 @@ var CampaignBuilderAgent = class {
1782
1926
  estimated_credits: plan.estimatedCredits
1783
1927
  },
1784
1928
  brain_delivery_risk_preflight: plan.buildRequest.deliveryRisk,
1785
- delivery_risk: plan.buildRequest.deliveryRisk
1929
+ delivery_risk: plan.buildRequest.deliveryRisk,
1930
+ learning_holdout: plan.buildRequest.learningHoldout
1786
1931
  },
1787
1932
  approval_required: plan.approvals.some((approval) => approval.blocking),
1788
1933
  actor_type: "agent",
@@ -1891,7 +2036,8 @@ var CampaignBuilderAgent = class {
1891
2036
  const destinationType = options.deliveryDestinationType ?? plan.buildRequest.delivery?.destinationType ?? "json";
1892
2037
  result.deliveryApproval = await this.approveCampaignDelivery(campaignBuildId, destinationType, {
1893
2038
  destinationId: options.deliveryDestinationId,
1894
- destinationConfig: options.deliveryDestinationConfig ?? plan.buildRequest.delivery?.destinationConfig
2039
+ destinationConfig: options.deliveryDestinationConfig ?? plan.buildRequest.delivery?.destinationConfig,
2040
+ allowPartialDelivery: options.allowPartialDelivery
1895
2041
  });
1896
2042
  finalStatus = await this.waitForCampaignBuildStatus(
1897
2043
  campaignBuildId,
@@ -1914,8 +2060,12 @@ var CampaignBuilderAgent = class {
1914
2060
  const args = compact({
1915
2061
  campaign_build_id: campaignBuildId,
1916
2062
  page_size: options.limit,
1917
- cursor: options.cursor
2063
+ cursor: options.cursor,
2064
+ include_data: options.includeData,
2065
+ include_raw: options.includeRaw
1918
2066
  });
2067
+ if (options.cached !== void 0) filters.cached = options.cached;
2068
+ if (options.exportReady !== void 0) filters.export_ready = options.exportReady;
1919
2069
  if (Object.keys(filters).length > 0) args.filters = filters;
1920
2070
  const data = await this.callMcp("get_campaign_build_rows", args);
1921
2071
  return mapAgentCampaignRowsResult(data);
@@ -1930,12 +2080,14 @@ var CampaignBuilderAgent = class {
1930
2080
  return this.approveCampaignDelivery(campaignBuildId, destinationType, options);
1931
2081
  }
1932
2082
  async reviewBuild(campaignBuildId, options = {}) {
1933
- const status = await this.getBuildStatus(campaignBuildId);
1934
- const rows = await this.getBuildRows(campaignBuildId, {
1935
- ...options,
1936
- limit: options.limit ?? 100
1937
- });
1938
- const artifacts = await this.listBuildArtifacts(campaignBuildId);
2083
+ const [status, rows, artifacts] = await Promise.all([
2084
+ this.getBuildStatus(campaignBuildId),
2085
+ this.getBuildRows(campaignBuildId, {
2086
+ ...options,
2087
+ limit: options.limit ?? 100
2088
+ }),
2089
+ this.listBuildArtifacts(campaignBuildId)
2090
+ ]);
1939
2091
  return createCampaignBuilderBuildReview(status, rows, artifacts);
1940
2092
  }
1941
2093
  async getCampaignBuildStatus(campaignBuildId) {
@@ -1963,7 +2115,8 @@ var CampaignBuilderAgent = class {
1963
2115
  campaign_build_id: campaignBuildId,
1964
2116
  destination_type: destinationType,
1965
2117
  destination_id: options?.destinationId,
1966
- destination_config: options?.destinationConfig
2118
+ destination_config: options?.destinationConfig,
2119
+ allow_partial_delivery: options?.allowPartialDelivery === true ? true : void 0
1967
2120
  });
1968
2121
  const data = await this.callMcp("approve_campaign_delivery", args);
1969
2122
  return {
@@ -1973,7 +2126,10 @@ var CampaignBuilderAgent = class {
1973
2126
  message: data.message ?? "",
1974
2127
  deliveryId: data.delivery_id,
1975
2128
  deliveryIds: data.delivery_ids,
1976
- approvedCount: data.approved_count
2129
+ approvedCount: data.approved_count,
2130
+ partialDelivery: data.partial_delivery,
2131
+ effectiveTargetCount: data.effective_target_count,
2132
+ requestedTargetCount: data.requested_target_count
1977
2133
  };
1978
2134
  }
1979
2135
  async getWorkspaceContext(warnings) {
@@ -2009,15 +2165,19 @@ var CampaignBuilderAgent = class {
2009
2165
  const providers = new Set(
2010
2166
  [...request.integrations ?? [], ...routesFromCustomerTools(request.customerTools ?? [])].filter((route) => route.provider !== "signaliz").map((route) => route.provider)
2011
2167
  );
2012
- for (const provider of providers) {
2168
+ const warningsByProvider = await Promise.all(Array.from(providers, async (provider) => {
2013
2169
  try {
2014
2170
  await this.callMcp("discover_capabilities", {
2015
2171
  query: `campaign builder ${provider} ${request.goal}`,
2016
2172
  category: "campaign"
2017
2173
  });
2174
+ return void 0;
2018
2175
  } catch (error) {
2019
- warnings.push(`Capability discovery for ${provider} failed: ${errorMessage(error)}`);
2176
+ return `Capability discovery for ${provider} failed: ${errorMessage(error)}`;
2020
2177
  }
2178
+ }));
2179
+ for (const warning of warningsByProvider) {
2180
+ if (warning) warnings.push(warning);
2021
2181
  }
2022
2182
  }
2023
2183
  async attachStrategyMemoryStatus(plan, warnings) {
@@ -2041,8 +2201,24 @@ var CampaignBuilderAgent = class {
2041
2201
  const kernelPlan = await this.callMcp("gtm_campaign_build_plan", asRecord(plannerStep.arguments));
2042
2202
  plan.kernelPlan = asRecord(kernelPlan);
2043
2203
  const brainDefaults = asRecord(plan.kernelPlan.brain_defaults ?? plan.kernelPlan.brainDefaults);
2204
+ const corpusContext = asRecord(plan.kernelPlan.corpus_context ?? plan.kernelPlan.corpusContext);
2205
+ const campaignStrategyContext = asRecord(plan.kernelPlan.campaign_strategy_context ?? plan.kernelPlan.campaignStrategyContext);
2206
+ 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) : [];
2044
2207
  if (Object.keys(brainDefaults).length > 0 && Object.keys(asRecord(plan.buildRequest.brainDefaults)).length === 0) {
2045
2208
  plan.buildRequest.brainDefaults = brainDefaults;
2209
+ }
2210
+ if (Object.keys(campaignStrategyContext).length > 0) {
2211
+ plan.buildRequest.campaignStrategyContext = campaignStrategyContext;
2212
+ }
2213
+ if (Object.keys(corpusContext).length > 0 || memoryExamples.length > 0 || Object.keys(brainDefaults).length > 0 || Object.keys(campaignStrategyContext).length > 0) {
2214
+ plan.buildRequest.brainPreflight = compact({
2215
+ ...asRecord(plan.buildRequest.brainPreflight),
2216
+ corpus_context: Object.keys(corpusContext).length > 0 ? corpusContext : void 0,
2217
+ memory_examples: memoryExamples.length > 0 ? memoryExamples : void 0,
2218
+ brain_defaults: Object.keys(brainDefaults).length > 0 ? brainDefaults : void 0,
2219
+ campaign_strategy_context: Object.keys(campaignStrategyContext).length > 0 ? campaignStrategyContext : void 0
2220
+ });
2221
+ plan.brainPreflight = asRecord(plan.buildRequest.brainPreflight);
2046
2222
  refreshBuildStepArguments(plan);
2047
2223
  }
2048
2224
  } catch (error) {
@@ -2223,6 +2399,9 @@ function createCampaignBuilderProofReceipt(plan, result) {
2223
2399
  campaign_build_id: dryRunResult.campaignBuildId ?? dryRunResult.campaign_build_id ?? null
2224
2400
  }
2225
2401
  ];
2402
+ const learnBackPlan = summarizeCampaignBuilderProofLearnBack(
2403
+ asRecord(asRecord(plan.buildRequest.brainPreflight).learn_back_plan ?? asRecord(plan.brainPreflight).learn_back_plan)
2404
+ );
2226
2405
  return {
2227
2406
  receipt_version: "campaign-agent-proof.v1",
2228
2407
  source: "signaliz.campaignBuilderAgent.proof",
@@ -2245,6 +2424,7 @@ function createCampaignBuilderProofReceipt(plan, result) {
2245
2424
  gates,
2246
2425
  strategy_memory_status: summarizeCampaignBuilderProofStrategyMemory(strategyMemory),
2247
2426
  dry_run_result: summarizeCampaignBuilderProofDryRun(dryRunResult),
2427
+ learn_back_plan: learnBackPlan,
2248
2428
  recommended_next_actions: [
2249
2429
  "Review the plan, approvals, and dry-run result.",
2250
2430
  "Use campaign-agent commit-plan --confirm-write only after review.",
@@ -2794,6 +2974,13 @@ function campaignBuilderMemoryKitSdkExample(request, seedManifestFile) {
2794
2974
  function campaignBuilderMemoryKitSource(source) {
2795
2975
  const normalized = normalizeTemplateKey(source || "agency");
2796
2976
  const catalog = {
2977
+ northStar: {
2978
+ id: "north_star_campaign_builder",
2979
+ label: "North Star campaign-builder acceptance patterns",
2980
+ seed_source: "north-star",
2981
+ scope: "redacted_acceptance_seed_packs",
2982
+ privacy: "redacted aggregate counts, QA gates, and fixture shape only"
2983
+ },
2797
2984
  agency: {
2798
2985
  id: "agency_campaign_builder",
2799
2986
  label: "Agency campaign-builder strategy patterns",
@@ -2819,6 +3006,10 @@ function campaignBuilderMemoryKitSource(source) {
2819
3006
  const sourceMap = {
2820
3007
  agency: "agency",
2821
3008
  "campaign-builder": "agency",
3009
+ "north-star": "northStar",
3010
+ northstar: "northStar",
3011
+ "campaign-builder-north-star": "northStar",
3012
+ acceptance: "northStar",
2822
3013
  "strategy-memory": "agency",
2823
3014
  "workflow-patterns": "workflow",
2824
3015
  workflow: "workflow",
@@ -2834,13 +3025,13 @@ function campaignBuilderMemoryKitSource(source) {
2834
3025
  return {
2835
3026
  seedSource: "all",
2836
3027
  verifyScript: null,
2837
- memorySources: [catalog.agency, catalog.workflow, catalog.feedback]
3028
+ memorySources: [catalog.northStar, catalog.agency, catalog.workflow, catalog.feedback]
2838
3029
  };
2839
3030
  }
2840
3031
  const item = catalog[selected];
2841
3032
  return {
2842
3033
  seedSource: item.seed_source,
2843
- 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,
3034
+ 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,
2844
3035
  memorySources: [item]
2845
3036
  };
2846
3037
  }
@@ -3020,11 +3211,11 @@ function builtInRoutesFor(request) {
3020
3211
  routes.push({
3021
3212
  id: "signaliz-lead-generation",
3022
3213
  layer: "source",
3023
- gtmLayers: ["find_company", "find_people", "lead_generation"],
3214
+ gtmLayer: "lead_generation",
3024
3215
  provider: "signaliz",
3025
3216
  mode: "required",
3026
3217
  toolName: "generate_leads",
3027
- rationale: "Use Signaliz fresh workspace acquisition as the default source of truth."
3218
+ rationale: "Inventory verified cache first, then use Signaliz fresh workspace acquisition for the remaining approved gap."
3028
3219
  });
3029
3220
  }
3030
3221
  if (builtIns.has("local_leads")) {
@@ -3038,6 +3229,28 @@ function builtInRoutesFor(request) {
3038
3229
  rationale: "Use Signaliz local-leads sourcing for geography-bound local business campaigns."
3039
3230
  });
3040
3231
  }
3232
+ if (builtIns.has("company_discovery")) {
3233
+ routes.push({
3234
+ id: "signaliz-company-discovery",
3235
+ layer: "source",
3236
+ gtmLayer: "find_company",
3237
+ provider: "signaliz",
3238
+ mode: "if_available",
3239
+ toolName: "find_companies_signaliz",
3240
+ rationale: "Find and qualify company domains before people/email fill for account-led campaigns."
3241
+ });
3242
+ }
3243
+ if (builtIns.has("people_discovery")) {
3244
+ routes.push({
3245
+ id: "signaliz-people-discovery",
3246
+ layer: "source",
3247
+ gtmLayer: "find_people",
3248
+ provider: "signaliz",
3249
+ mode: "if_available",
3250
+ toolName: "find_people_signaliz",
3251
+ rationale: "Find people directly when the brief is contact research or LinkedIn-style prospecting."
3252
+ });
3253
+ }
3041
3254
  if (builtIns.has("email_finding")) {
3042
3255
  routes.push({
3043
3256
  id: "signaliz-email-finding",
@@ -3082,7 +3295,7 @@ function normalizeBuiltIns(request) {
3082
3295
  return [...builtIns].filter(isCampaignBuilderBuiltInTool);
3083
3296
  }
3084
3297
  function isCampaignBuilderBuiltInTool(value) {
3085
- return ["lead_generation", "local_leads", "email_finding", "email_verification", "signals"].includes(String(value));
3298
+ return ["lead_generation", "local_leads", "company_discovery", "people_discovery", "email_finding", "email_verification", "signals"].includes(String(value));
3086
3299
  }
3087
3300
  function looksLikeLocalLeadCampaign(request) {
3088
3301
  const text = [
@@ -3113,6 +3326,21 @@ function routesFromCustomerTools(tools) {
3113
3326
  }))
3114
3327
  );
3115
3328
  }
3329
+ function normalizeCampaignBuilderLearningHoldout(input, gtmCampaignId, campaignName) {
3330
+ const source = asRecord(input);
3331
+ if (source.enabled === false) return { enabled: false };
3332
+ const controlPercentage = numberOrUndefined2(source.control_percentage ?? source.controlPercentage);
3333
+ const minControlSize = numberOrUndefined2(source.min_control_size ?? source.minControlSize);
3334
+ const experimentKey = stringValue(source.experiment_key ?? source.experimentKey) ?? (gtmCampaignId ? `gtm_campaign:${gtmCampaignId}:learning_holdout` : `campaign_builder_agent:${hashString(campaignName)}:learning_holdout`);
3335
+ return {
3336
+ enabled: true,
3337
+ controlPercentage: controlPercentage ?? 0.2,
3338
+ minControlSize: minControlSize ?? 50,
3339
+ experimentKey,
3340
+ sourceCampaignId: stringValue(source.source_campaign_id ?? source.sourceCampaignId) ?? gtmCampaignId,
3341
+ primaryMetric: stringValue(source.primary_metric ?? source.primaryMetric) ?? "positive_reply_rate"
3342
+ };
3343
+ }
3116
3344
  function createBuildRequest(request, defaults, targetCount, campaignName, scopeBuildArgs, routes = [], memory, scopeBrainPreflight) {
3117
3345
  const buildRequest = {
3118
3346
  name: campaignName,
@@ -3137,10 +3365,19 @@ function createBuildRequest(request, defaults, targetCount, campaignName, scopeB
3137
3365
  qualification: defaults.qualification,
3138
3366
  copy: defaults.copy,
3139
3367
  delivery: defaults.delivery,
3140
- enhancers: enhancerConfig(request)
3368
+ enhancers: enhancerConfig(request),
3369
+ sourceRouting: request.sourceRouting ?? {
3370
+ mode: looksLikeLocalLeadCampaign(request) ? "local_leads" : "auto",
3371
+ fillPolicy: "aggressive"
3372
+ }
3141
3373
  };
3142
3374
  const scoped = asRecord(scopeBuildArgs);
3143
3375
  buildRequest.gtmCampaignId = request.gtmCampaignId ?? stringValue(scoped.gtm_campaign_id ?? scoped.gtmCampaignId);
3376
+ buildRequest.learningHoldout = normalizeCampaignBuilderLearningHoldout(
3377
+ request.learningHoldout ?? scoped.learning_holdout ?? scoped.learningHoldout,
3378
+ buildRequest.gtmCampaignId,
3379
+ campaignName
3380
+ );
3144
3381
  if (typeof scoped.name === "string") buildRequest.name = scoped.name;
3145
3382
  if (typeof scoped.prompt === "string") buildRequest.prompt = scoped.prompt;
3146
3383
  if (typeof scoped.target_count === "number") buildRequest.targetCount = scoped.target_count;
@@ -3215,13 +3452,137 @@ function createBrainPreflight(request, buildRequest, routes, memory) {
3215
3452
  target_icp: targetIcp,
3216
3453
  provider_chain: providerChain,
3217
3454
  lead_source: providerChain[0] ?? "signaliz",
3455
+ learning_holdout: buildRequest.learningHoldout,
3218
3456
  tool_sequence: [
3219
3457
  { tool: "gtm_brain_learning_cycle_plan", purpose: "Plan the Brain learning loop for this campaign.", arguments: learningArgs },
3220
3458
  { tool: "gtm_brain_seed_defaults", purpose: "Seed campaign defaults from workspace memory and safe aggregate patterns.", arguments: defaultsArgs },
3221
3459
  { tool: "gtm_brain_delivery_risk", purpose: "Check deliverability risk before launch or external writes.", arguments: riskArgs }
3222
- ]
3460
+ ],
3461
+ learn_back_plan: createCampaignBuilderLearnBackPlan(request, buildRequest)
3462
+ };
3463
+ }
3464
+ function createCampaignBuilderLearnBackPlan(request, buildRequest) {
3465
+ const instantlyFeedbackContext = campaignBuilderProviderFeedbackContext(request, buildRequest, "instantly");
3466
+ const postBuildSequence = [
3467
+ {
3468
+ tool: "get_campaign_build_status",
3469
+ approval_boundary: "read_only",
3470
+ arguments_template: { campaign_build_id: "<build_campaign.campaign_build_id>" },
3471
+ reason: "Wait for the Campaign Builder run to finish before deriving memory."
3472
+ },
3473
+ {
3474
+ tool: "gtm_campaign_build_backfill_preview",
3475
+ approval_boundary: "read_only",
3476
+ arguments_template: { campaign_build_id: "<build_campaign.campaign_build_id>", create_memory: true },
3477
+ reason: "Preview the provider-neutral Kernel import payload without writing rows."
3478
+ },
3479
+ {
3480
+ tool: "gtm_campaign_build_backfill_run",
3481
+ approval_boundary: "dry_run",
3482
+ arguments_template: { campaign_build_ids: ["<build_campaign.campaign_build_id>"], dry_run: true, create_memory: true, run_brain_cycle: false },
3483
+ reason: "Dry-run the Kernel history import before creating campaign memory."
3484
+ },
3485
+ {
3486
+ tool: "gtm_campaign_build_backfill_run",
3487
+ approval_boundary: "explicit_write_approval",
3488
+ 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" },
3489
+ reason: "After review, queue the Kernel history import; Brain learning remains dry-run unless separately approved."
3490
+ }
3491
+ ];
3492
+ if (instantlyFeedbackContext) {
3493
+ postBuildSequence.push({
3494
+ tool: "gtm_instantly_feedback_pull",
3495
+ approval_boundary: "dry_run_first_explicit_write_approval",
3496
+ arguments_template: {
3497
+ campaign_id: buildRequest.gtmCampaignId || "<gtm_campaign_id_after_commit>",
3498
+ campaign_build_id: "<build_campaign.campaign_build_id>",
3499
+ source: "emails",
3500
+ email_type: "received",
3501
+ ...instantlyFeedbackContext,
3502
+ dry_run: true,
3503
+ create_memory: true,
3504
+ write_routine_outcomes: true,
3505
+ run_brain_cycle: true,
3506
+ brain_cycle_write_mode: "dry_run"
3507
+ },
3508
+ reason: "After Instantly delivery has live outcomes, pull reply and bounce history into private feedback memory before Brain learning."
3509
+ });
3510
+ }
3511
+ postBuildSequence.push(
3512
+ {
3513
+ tool: "gtm_campaign_learning_status",
3514
+ approval_boundary: "read_only",
3515
+ arguments_template: {
3516
+ campaign_id: buildRequest.gtmCampaignId || "<gtm_campaign_id_after_commit>",
3517
+ campaign_build_id: "<build_campaign.campaign_build_id>",
3518
+ write_mode: "dry_run"
3519
+ },
3520
+ reason: "Confirm feedback, memory, and Brain learning readiness after the import."
3521
+ },
3522
+ {
3523
+ tool: "gtm_memory_search",
3524
+ approval_boundary: "read_only",
3525
+ arguments_template: {
3526
+ query: request.goal,
3527
+ ...buildRequest.gtmCampaignId ? { campaign_id: buildRequest.gtmCampaignId } : {},
3528
+ limit: 10
3529
+ },
3530
+ reason: "Verify the new private memory is retrievable for future campaign planning."
3531
+ },
3532
+ {
3533
+ tool: "gtm_brain_learning_cycle_plan",
3534
+ approval_boundary: "read_only",
3535
+ arguments_template: {
3536
+ ...buildRequest.gtmCampaignId ? { campaign_id: buildRequest.gtmCampaignId } : {},
3537
+ include_memory: true,
3538
+ write_mode: "dry_run"
3539
+ },
3540
+ reason: "Plan the Brain phases from the imported outcomes before any pattern writes."
3541
+ }
3542
+ );
3543
+ return {
3544
+ source_tool: "campaign-builder-agent",
3545
+ gtm_campaign_id: buildRequest.gtmCampaignId ?? null,
3546
+ trigger: "After build_campaign returns a campaign_build_id and the build reaches a terminal reviewed state.",
3547
+ build_result_placeholders: {
3548
+ campaign_build_id: "<build_campaign.campaign_build_id>"
3549
+ },
3550
+ memory_write_policy: {
3551
+ target_tables: ["gtm_campaigns", "gtm_campaign_provider_links", "gtm_campaign_feedback_events", "gtm_memory_items", "gtm_brain_patterns"],
3552
+ gtm_memory_items: {
3553
+ shareable: false,
3554
+ allowed_redaction_states: ["redacted", "abstracted"],
3555
+ raw_private_fields_withheld: ["reply_text", "copy_body", "lead_email", "lead_domain", "company_domain", "provider_campaign_id", "provider_payload", "webhook_url", "secret"]
3556
+ },
3557
+ gtm_brain_patterns: "Workspace-scoped by default. Global promotion requires abstracted_only=true, shared opt-in, and privacy thresholds."
3558
+ },
3559
+ post_build_sequence: postBuildSequence.map((item, index) => ({ step: index + 1, ...item })),
3560
+ 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.",
3561
+ 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."
3223
3562
  };
3224
3563
  }
3564
+ function campaignBuilderProviderFeedbackContext(request, buildRequest, provider) {
3565
+ const normalizedProvider = String(provider).toLowerCase();
3566
+ const routes = [...request.integrations ?? [], ...routesFromCustomerTools(request.customerTools ?? [])];
3567
+ const providerRoute = routes.find((route) => String(route.provider).toLowerCase() === normalizedProvider);
3568
+ const routeConfig = asRecord(providerRoute?.config);
3569
+ const deliveryConfig = asRecord(buildRequest.delivery?.destinationConfig);
3570
+ const hasProviderContext = [
3571
+ ...request.preferredProviders ?? [],
3572
+ ...routes.map((route) => route.provider),
3573
+ stringValue(deliveryConfig.provider),
3574
+ stringValue(deliveryConfig.provider_id ?? deliveryConfig.providerId),
3575
+ stringValue(deliveryConfig.destination_provider ?? deliveryConfig.destinationProvider),
3576
+ stringValue(deliveryConfig.sink ?? deliveryConfig.sink_type ?? deliveryConfig.sinkType)
3577
+ ].some((value) => String(value).toLowerCase() === normalizedProvider);
3578
+ if (!hasProviderContext) return void 0;
3579
+ return compact({
3580
+ provider_link_id: stringValue(routeConfig.provider_link_id ?? routeConfig.providerLinkId ?? deliveryConfig.provider_link_id ?? deliveryConfig.providerLinkId),
3581
+ 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),
3582
+ integration_id: stringValue(routeConfig.integration_id ?? routeConfig.integrationId ?? deliveryConfig.integration_id ?? deliveryConfig.integrationId),
3583
+ instantly_profile: stringValue(routeConfig.instantly_profile ?? routeConfig.instantlyProfile ?? routeConfig.profile ?? deliveryConfig.instantly_profile ?? deliveryConfig.instantlyProfile ?? deliveryConfig.profile)
3584
+ });
3585
+ }
3225
3586
  function gtmLayersForBuilderLayer(layer) {
3226
3587
  const map = {
3227
3588
  workspace_context: ["customer_data"],
@@ -3626,6 +3987,7 @@ function buildFallbackPlanSnapshot(plan) {
3626
3987
  target_count: plan.targetCount,
3627
3988
  approvals: plan.approvals,
3628
3989
  brain_preflight: plan.brainPreflight,
3990
+ campaign_strategy_context: plan.buildRequest.campaignStrategyContext,
3629
3991
  operating_playbooks: plan.operatingPlaybooks
3630
3992
  });
3631
3993
  }
@@ -3758,6 +4120,17 @@ function buildCampaignArgs(request) {
3758
4120
  if (request.dedupKeys) args.dedup_keys = request.dedupKeys;
3759
4121
  if (request.brainPreflight) args.brain_preflight = request.brainPreflight;
3760
4122
  if (request.brainDefaults) args.brain_defaults = request.brainDefaults;
4123
+ if (request.learningHoldout) {
4124
+ args.learning_holdout = compact({
4125
+ enabled: request.learningHoldout.enabled,
4126
+ control_percentage: request.learningHoldout.controlPercentage,
4127
+ min_control_size: request.learningHoldout.minControlSize,
4128
+ experiment_key: request.learningHoldout.experimentKey,
4129
+ source_campaign_id: request.learningHoldout.sourceCampaignId,
4130
+ primary_metric: request.learningHoldout.primaryMetric
4131
+ });
4132
+ }
4133
+ if (request.campaignStrategyContext) args.campaign_strategy_context = request.campaignStrategyContext;
3761
4134
  if (request.deliveryRisk) args.delivery_risk = request.deliveryRisk;
3762
4135
  if (request.icp) {
3763
4136
  args.icp = compact({
@@ -3777,6 +4150,15 @@ function buildCampaignArgs(request) {
3777
4150
  model: request.policy.model
3778
4151
  });
3779
4152
  }
4153
+ if (request.sourceRouting) {
4154
+ args.source_routing = compact({
4155
+ mode: request.sourceRouting.mode,
4156
+ fill_policy: request.sourceRouting.fillPolicy,
4157
+ shard_size: request.sourceRouting.shardSize,
4158
+ max_source_shard_size: request.sourceRouting.maxSourceShardSize,
4159
+ max_local_source_shard_size: request.sourceRouting.maxLocalSourceShardSize
4160
+ });
4161
+ }
3780
4162
  if (request.signals) {
3781
4163
  args.signals = compact({
3782
4164
  enabled: request.signals.enabled,
@@ -3844,11 +4226,13 @@ function buildExecutionPrepareArgs(request, dryRun, confirmSpend) {
3844
4226
  delivery_risk: buildArgs2.delivery_risk,
3845
4227
  dedup_keys: buildArgs2.dedup_keys,
3846
4228
  policy: buildArgs2.policy,
4229
+ learning_holdout: buildArgs2.learning_holdout,
3847
4230
  signals: buildArgs2.signals,
3848
4231
  qualification: buildArgs2.qualification,
3849
4232
  copy: buildArgs2.copy,
3850
4233
  delivery: buildArgs2.delivery,
3851
- enhancers: buildArgs2.enhancers
4234
+ enhancers: buildArgs2.enhancers,
4235
+ source_routing: buildArgs2.source_routing
3852
4236
  });
3853
4237
  }
3854
4238
  function buildDeliveryApprovalArgs(request) {
@@ -3877,10 +4261,145 @@ function mapBuildResult(data, dryRun) {
3877
4261
  targetLimitReason: data.target_limit_reason,
3878
4262
  maxSupportedTargetCount: data.max_supported_target_count,
3879
4263
  brainContext: data.brain_context ?? {},
4264
+ learningHoldout: data.learning_holdout ?? {},
3880
4265
  providerRoute: mapProviderRoute2(data)
3881
4266
  };
3882
4267
  }
4268
+ var CAMPAIGN_BUILDER_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "canceled", "cancelled"]);
4269
+ var CAMPAIGN_BUILDER_BLOCKED_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["failed", "canceled", "cancelled"]);
4270
+ function mapAgentCampaignPhaseHealth(value) {
4271
+ if (!Array.isArray(value)) return [];
4272
+ return value.map((phase) => {
4273
+ const record = asRecord(phase);
4274
+ return {
4275
+ phase: stringValue(record.phase) ?? "unknown",
4276
+ status: stringValue(record.status) ?? "unknown",
4277
+ recordsProcessed: numberOrNull2(record.records_processed ?? record.recordsProcessed),
4278
+ recordsSucceeded: numberOrNull2(record.records_succeeded ?? record.recordsSucceeded),
4279
+ recordsFailed: numberOrNull2(record.records_failed ?? record.recordsFailed),
4280
+ heartbeatAgeSeconds: numberOrNull2(record.heartbeat_age_seconds ?? record.heartbeatAgeSeconds ?? record.phase_age_seconds)
4281
+ };
4282
+ });
4283
+ }
4284
+ function deriveCampaignBuildTerminalState(input) {
4285
+ const status = input.status.toLowerCase();
4286
+ const heartbeatAgeSeconds = input.phaseAgeSeconds;
4287
+ const stale = input.staleRunningPhase === true;
4288
+ const safeWatchAction = input.triggerRunId ? `signaliz ops run-status ${input.triggerRunId} --watch` : "signaliz campaign-agent status <campaign_build_id>";
4289
+ if (CAMPAIGN_BUILDER_BLOCKED_TERMINAL_STATUSES.has(status)) {
4290
+ return {
4291
+ kind: "blocked",
4292
+ label: "Blocked terminal state",
4293
+ reviewable: true,
4294
+ terminal: true,
4295
+ stale: false,
4296
+ phase: input.phase,
4297
+ phaseStatus: input.phaseStatus,
4298
+ heartbeatAgeSeconds,
4299
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4300
+ terminalReason: input.terminalReason,
4301
+ staleReason: null,
4302
+ safeNextAction: input.nextAction || "Review errors and rerun only after the blocker is resolved."
4303
+ };
4304
+ }
4305
+ if (status === "pending_approval") {
4306
+ return {
4307
+ kind: "pending_approval",
4308
+ label: "Pending delivery approval",
4309
+ reviewable: true,
4310
+ terminal: false,
4311
+ stale: false,
4312
+ phase: input.phase,
4313
+ phaseStatus: input.phaseStatus,
4314
+ heartbeatAgeSeconds,
4315
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4316
+ terminalReason: input.terminalReason,
4317
+ staleReason: null,
4318
+ safeNextAction: input.approvalAction || input.nextAction || "Review rows and approve delivery only after the approval packet passes."
4319
+ };
4320
+ }
4321
+ if (CAMPAIGN_BUILDER_TERMINAL_STATUSES.has(status)) {
4322
+ return {
4323
+ kind: "terminal",
4324
+ label: "Terminal and reviewable",
4325
+ reviewable: true,
4326
+ terminal: true,
4327
+ stale: false,
4328
+ phase: input.phase,
4329
+ phaseStatus: input.phaseStatus,
4330
+ heartbeatAgeSeconds,
4331
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4332
+ terminalReason: input.terminalReason,
4333
+ staleReason: null,
4334
+ safeNextAction: input.nextAction || "Review rows, artifacts, scorecard, and approval locks before delivery."
4335
+ };
4336
+ }
4337
+ if (status === "running" || status === "queued" || status === "processing") {
4338
+ if (stale) {
4339
+ return {
4340
+ kind: "running_stale",
4341
+ label: "Running but stale",
4342
+ reviewable: false,
4343
+ terminal: false,
4344
+ stale: true,
4345
+ phase: input.phase,
4346
+ phaseStatus: input.phaseStatus,
4347
+ heartbeatAgeSeconds,
4348
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4349
+ terminalReason: null,
4350
+ staleReason: input.staleReason || "The active phase heartbeat is stale.",
4351
+ safeNextAction: input.nextAction || safeWatchAction
4352
+ };
4353
+ }
4354
+ return {
4355
+ kind: "running_healthy",
4356
+ label: "Running and healthy",
4357
+ reviewable: false,
4358
+ terminal: false,
4359
+ stale: false,
4360
+ phase: input.phase,
4361
+ phaseStatus: input.phaseStatus,
4362
+ heartbeatAgeSeconds,
4363
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4364
+ terminalReason: null,
4365
+ staleReason: null,
4366
+ safeNextAction: input.nextAction || safeWatchAction
4367
+ };
4368
+ }
4369
+ return {
4370
+ kind: "unknown",
4371
+ label: "Unknown build state",
4372
+ reviewable: false,
4373
+ terminal: false,
4374
+ stale,
4375
+ phase: input.phase,
4376
+ phaseStatus: input.phaseStatus,
4377
+ heartbeatAgeSeconds,
4378
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4379
+ terminalReason: input.terminalReason,
4380
+ staleReason: input.staleReason,
4381
+ safeNextAction: input.nextAction || "Read campaign build status again before making a launch or delivery decision."
4382
+ };
4383
+ }
3883
4384
  function mapAgentCampaignBuildStatus(data) {
4385
+ const customerRowCounts = mapAgentCustomerRowCounts(data.customer_row_counts);
4386
+ const phaseHealth = mapAgentCampaignPhaseHealth(data.phases);
4387
+ const terminalReason = stringValue(data.terminal_reason) ?? customerRowCounts?.terminalReason ?? null;
4388
+ const staleReason = stringValue(data.stale_reason) ?? stringValue(asRecord(data.diagnostics).stale_reason) ?? null;
4389
+ const nextPollAfterSeconds = numberOrNull2(data.next_poll_after_seconds ?? data.nextPollAfterSeconds);
4390
+ const terminalState = deriveCampaignBuildTerminalState({
4391
+ status: stringValue(data.status) ?? "unknown",
4392
+ phase: stringValue(data.current_phase) ?? null,
4393
+ phaseStatus: stringValue(data.current_phase_status) ?? null,
4394
+ staleRunningPhase: data.stale_running_phase === true,
4395
+ phaseAgeSeconds: numberOrNull2(data.phase_age_seconds),
4396
+ nextPollAfterSeconds,
4397
+ terminalReason,
4398
+ staleReason,
4399
+ nextAction: formatAgentNextAction(data.next_action),
4400
+ approvalAction: formatAgentNextAction(data.approval_action),
4401
+ triggerRunId: stringValue(data.trigger_run_id) ?? null
4402
+ });
3884
4403
  return {
3885
4404
  campaignBuildId: stringValue(data.campaign_build_id) ?? "",
3886
4405
  campaignId: stringValue(data.campaign_id) ?? null,
@@ -3897,17 +4416,68 @@ function mapAgentCampaignBuildStatus(data) {
3897
4416
  warnings: diagnosticMessages2(data.warnings),
3898
4417
  errors: diagnosticMessages2(data.errors),
3899
4418
  artifactCount: numberOrUndefined2(data.artifact_count) ?? 0,
4419
+ artifactDownloads: Array.isArray(data.artifact_downloads) ? data.artifact_downloads.map(mapAgentCampaignArtifactDownload) : [],
4420
+ customerRowCounts,
4421
+ phaseAttemptTotals: nullableRecord(data.phase_attempt_totals) ?? void 0,
3900
4422
  providerRoute: mapProviderRoute2(data),
3901
4423
  triggerRunId: stringValue(data.trigger_run_id) ?? null,
3902
4424
  staleRunningPhase: data.stale_running_phase === true,
3903
4425
  phaseAgeSeconds: numberOrNull2(data.phase_age_seconds),
4426
+ nextPollAfterSeconds,
4427
+ terminalReason,
4428
+ staleReason,
4429
+ safeNextAction: terminalState.safeNextAction,
4430
+ terminalState,
4431
+ phaseHealth,
3904
4432
  diagnostics: nullableRecord(data.diagnostics) ?? {},
3905
4433
  nextAction: formatAgentNextAction(data.next_action),
4434
+ approvalAction: formatAgentNextAction(data.approval_action),
3906
4435
  createdAt: stringValue(data.created_at) ?? "",
3907
4436
  updatedAt: stringValue(data.updated_at) ?? "",
3908
4437
  completedAt: stringValue(data.completed_at) ?? null
3909
4438
  };
3910
4439
  }
4440
+ function mapAgentCampaignArtifactDownload(data) {
4441
+ return {
4442
+ id: stringValue(data.id) ?? "",
4443
+ artifactType: stringValue(data.artifact_type) ?? null,
4444
+ format: stringValue(data.format) ?? null,
4445
+ rowCount: numberOrUndefined2(data.row_count) ?? 0,
4446
+ signedUrl: stringValue(data.signed_url) ?? null,
4447
+ downloadUrl: stringValue(data.download_url) ?? null,
4448
+ manifestUrl: stringValue(data.manifest_url) ?? null,
4449
+ expiresAt: stringValue(data.expires_at) ?? null
4450
+ };
4451
+ }
4452
+ function mapAgentCustomerRowCounts(value) {
4453
+ const record = nullableRecord(value);
4454
+ if (!record) return void 0;
4455
+ return {
4456
+ requestedTarget: numberOrNull2(record.requested_target),
4457
+ acquiredRows: numberOrUndefined2(record.acquired_rows),
4458
+ acceptedRows: numberOrUndefined2(record.accepted_rows),
4459
+ usableRows: numberOrUndefined2(record.usable_rows),
4460
+ copiedRows: numberOrUndefined2(record.copied_rows),
4461
+ approvalReadyRows: numberOrUndefined2(record.approval_ready_rows),
4462
+ qaReadyRows: numberOrUndefined2(record.qa_ready_rows),
4463
+ deliveredRows: numberOrUndefined2(record.delivered_rows),
4464
+ disqualifiedRows: numberOrUndefined2(record.disqualified_rows),
4465
+ reviewableRows: numberOrUndefined2(record.reviewable_rows),
4466
+ rowFailures: numberOrUndefined2(record.row_failures),
4467
+ acceptedShortfall: numberOrNull2(record.accepted_shortfall),
4468
+ usableShortfall: numberOrNull2(record.usable_shortfall),
4469
+ qaReadyShortfall: numberOrNull2(record.qa_ready_shortfall),
4470
+ deliveryShortfall: numberOrNull2(record.delivery_shortfall),
4471
+ fillRatio: numberOrNull2(record.fill_ratio),
4472
+ qaReadyFillRatio: numberOrNull2(record.qa_ready_fill_ratio),
4473
+ deliveredFillRatio: numberOrNull2(record.delivered_fill_ratio),
4474
+ terminalReason: stringValue(record.terminal_reason) ?? null,
4475
+ acceptedShortfallReason: stringValue(record.accepted_shortfall_reason) ?? null,
4476
+ usableShortfallReason: stringValue(record.usable_shortfall_reason) ?? null,
4477
+ qaReadyShortfallReason: stringValue(record.qa_ready_shortfall_reason) ?? null,
4478
+ deliveryShortfallReason: stringValue(record.delivery_shortfall_reason) ?? null
4479
+ };
4480
+ }
3911
4481
  function mapAgentCampaignRowsResult(data) {
3912
4482
  const rows = Array.isArray(data.rows) ? data.rows : [];
3913
4483
  return {
@@ -3935,74 +4505,296 @@ function mapAgentCampaignArtifact(data) {
3935
4505
  artifactType: stringValue(data.artifact_type) ?? "",
3936
4506
  destination: stringValue(data.destination) ?? "",
3937
4507
  storagePath: stringValue(data.storage_path) ?? "",
4508
+ format: stringValue(data.format) ?? null,
3938
4509
  signedUrl: stringValue(data.signed_url) ?? null,
4510
+ downloadUrl: stringValue(data.download_url) ?? null,
4511
+ manifestUrl: stringValue(data.manifest_url) ?? null,
4512
+ expiresAt: stringValue(data.expires_at) ?? null,
3939
4513
  rowCount: numberOrUndefined2(data.row_count) ?? 0,
3940
4514
  checksum: stringValue(data.checksum) ?? null,
3941
4515
  metadata: nullableRecord(data.metadata) ?? {},
3942
4516
  createdAt: stringValue(data.created_at) ?? ""
3943
4517
  };
3944
4518
  }
4519
+ var NORTH_STAR_BANNED_COPY_PHRASES = [
4520
+ "i have been following",
4521
+ "i saw that",
4522
+ "would you be opposed",
4523
+ "unlock new opportunities",
4524
+ "significant success",
4525
+ "we understand",
4526
+ "hope this finds you well"
4527
+ ];
4528
+ function campaignReviewGate(id, label, status, detail) {
4529
+ return {
4530
+ id,
4531
+ label,
4532
+ status,
4533
+ passed: status !== "blocked",
4534
+ detail
4535
+ };
4536
+ }
4537
+ function northStarMetric(id, label, status, detail, value, target) {
4538
+ return {
4539
+ id,
4540
+ label,
4541
+ status,
4542
+ detail,
4543
+ ...value !== void 0 ? { value } : {},
4544
+ ...target !== void 0 ? { target } : {}
4545
+ };
4546
+ }
4547
+ function createCampaignBuilderNorthStarScorecard(status, rows, artifacts, terminalState) {
4548
+ if (!terminalState.reviewable) {
4549
+ const pendingMetric = northStarMetric(
4550
+ "terminal_state",
4551
+ "Terminal-state contract",
4552
+ terminalState.kind === "running_stale" ? "blocked" : "pending",
4553
+ 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.`,
4554
+ terminalState.kind,
4555
+ "terminal or pending_approval"
4556
+ );
4557
+ return {
4558
+ version: "campaign-builder-north-star.v1",
4559
+ status: pendingMetric.status,
4560
+ score: null,
4561
+ moatState: "scaffolding_ready",
4562
+ metrics: [pendingMetric],
4563
+ blockers: pendingMetric.status === "blocked" ? [pendingMetric.detail] : [],
4564
+ warnings: pendingMetric.status === "pending" ? [pendingMetric.detail] : [],
4565
+ nextAction: terminalState.safeNextAction
4566
+ };
4567
+ }
4568
+ const sampledRows = rows.rows.length;
4569
+ const rowData = rows.rows.map((row) => asRecord(row.data));
4570
+ const requestedTarget = status.customerRowCounts?.requestedTarget ?? status.recordsTotal ?? rows.count;
4571
+ const finalRows = status.customerRowCounts?.deliveredRows ?? status.customerRowCounts?.qaReadyRows ?? status.customerRowCounts?.copiedRows ?? status.customerRowCounts?.acceptedRows ?? rows.count;
4572
+ const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
4573
+ const verifiedEmailRows = rowData.filter(campaignReviewDataHasVerifiedEmail).length;
4574
+ const uniqueEmails = new Set(rowData.map(campaignReviewDataEmail).filter(Boolean)).size;
4575
+ const uniqueDomains = new Set(rowData.map(campaignReviewDataDomain).filter(Boolean)).size;
4576
+ const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
4577
+ const copyQaIssuesByRow = rowData.map(campaignReviewDataCopyQaIssues).filter((issues) => issues.length > 0);
4578
+ const copyQaIssueCounts = countCampaignReviewIssues(copyQaIssuesByRow);
4579
+ const signalBackedRows = rowData.filter(campaignReviewDataHasSignalBasis).length;
4580
+ const noSupportedSignalRows = rowData.filter(campaignReviewDataHasNoSupportedSignal).length;
4581
+ const unsupportedSignalClaims = rowData.filter(campaignReviewDataHasUnsupportedSignalClaim).length;
4582
+ const approvalLockedRows = rowData.filter(campaignReviewDataHasApprovalLock).length;
4583
+ const holdoutRows = rowData.filter(campaignReviewDataHasHoldoutAttribution).length;
4584
+ const artifactCount = artifacts.length || status.artifactCount || 0;
4585
+ const deliveredRows = status.customerRowCounts?.deliveredRows ?? 0;
4586
+ const feedbackReady = campaignReviewStatusHasFeedbackReadiness(status);
4587
+ const feedbackRequiredForApproval = terminalState.kind === "pending_approval";
4588
+ const moat = campaignReviewMoatState(status, holdoutRows);
4589
+ const metrics = [
4590
+ northStarMetric(
4591
+ "terminal_state",
4592
+ "Terminal-state contract",
4593
+ terminalState.kind === "blocked" ? "blocked" : "pass",
4594
+ terminalState.label,
4595
+ terminalState.kind,
4596
+ "terminal or pending_approval"
4597
+ ),
4598
+ northStarMetric(
4599
+ "target_fill",
4600
+ "Target fill",
4601
+ requestedTarget && finalRows < requestedTarget ? "review" : "pass",
4602
+ requestedTarget ? `${finalRows || 0}/${requestedTarget} final or reviewable row(s) are accounted for.` : "No requested target was reported; using sampled rows for review.",
4603
+ finalRows || sampledRows,
4604
+ requestedTarget || sampledRows
4605
+ ),
4606
+ northStarMetric(
4607
+ "unique_emails",
4608
+ "Unique emails",
4609
+ sampledRows === 0 ? "blocked" : uniqueEmails === rowsWithEmail ? "pass" : "blocked",
4610
+ sampledRows === 0 ? "No sampled rows were available for email uniqueness review." : `${uniqueEmails}/${rowsWithEmail} sampled email(s) are unique.`,
4611
+ uniqueEmails,
4612
+ rowsWithEmail
4613
+ ),
4614
+ northStarMetric(
4615
+ "unique_domains",
4616
+ "Unique domains",
4617
+ sampledRows === 0 ? "blocked" : uniqueDomains === sampledRows ? "pass" : "review",
4618
+ sampledRows === 0 ? "No sampled rows were available for domain uniqueness review." : `${uniqueDomains}/${sampledRows} sampled company domain(s) are unique.`,
4619
+ uniqueDomains,
4620
+ sampledRows
4621
+ ),
4622
+ northStarMetric(
4623
+ "verified_email_rows",
4624
+ "Verified email rows",
4625
+ sampledRows === 0 ? "blocked" : verifiedEmailRows === sampledRows ? "pass" : rowsWithEmail === sampledRows ? "review" : "blocked",
4626
+ sampledRows === 0 ? "No sampled rows were available for email verification review." : `${verifiedEmailRows}/${sampledRows} sampled row(s) carry verified-email evidence.`,
4627
+ verifiedEmailRows,
4628
+ sampledRows
4629
+ ),
4630
+ northStarMetric(
4631
+ "copy_completeness",
4632
+ "Copy completeness",
4633
+ sampledRows === 0 ? "blocked" : copyReadyRows === sampledRows ? "pass" : "blocked",
4634
+ sampledRows === 0 ? "No sampled rows were available for copy review." : `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.`,
4635
+ copyReadyRows,
4636
+ sampledRows
4637
+ ),
4638
+ northStarMetric(
4639
+ "copy_quality",
4640
+ "Copy QA",
4641
+ copyQaIssuesByRow.length === 0 ? "pass" : "blocked",
4642
+ copyQaIssuesByRow.length === 0 ? "No sampled copy QA blockers were detected." : `${copyQaIssuesByRow.length}/${sampledRows} sampled row(s) have copy QA issue(s): ${formatCampaignReviewIssueCounts(copyQaIssueCounts)}.`,
4643
+ copyQaIssuesByRow.length,
4644
+ 0
4645
+ ),
4646
+ northStarMetric(
4647
+ "signal_grounding",
4648
+ "Signal grounding",
4649
+ unsupportedSignalClaims > 0 ? "blocked" : signalBackedRows > 0 ? "pass" : "review",
4650
+ 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.",
4651
+ signalBackedRows,
4652
+ sampledRows
4653
+ ),
4654
+ northStarMetric(
4655
+ "approval_locks",
4656
+ "Approval locks",
4657
+ approvalLockedRows > 0 || deliveredRows === 0 ? "pass" : "review",
4658
+ 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.",
4659
+ approvalLockedRows,
4660
+ sampledRows
4661
+ ),
4662
+ northStarMetric(
4663
+ "export_send_safety",
4664
+ "Export/send safety",
4665
+ deliveredRows > 0 && status.status !== "completed" ? "blocked" : "pass",
4666
+ deliveredRows > 0 ? `${deliveredRows} row(s) are delivered; status is ${status.status}.` : "No delivered rows are reported before approval.",
4667
+ deliveredRows,
4668
+ 0
4669
+ ),
4670
+ northStarMetric(
4671
+ "holdout_attribution",
4672
+ "Holdout attribution",
4673
+ holdoutRows > 0 ? "pass" : "review",
4674
+ holdoutRows > 0 ? `${holdoutRows}/${sampledRows} sampled row(s) preserve holdout attribution.` : "No holdout attribution was visible in the sampled rows.",
4675
+ holdoutRows,
4676
+ sampledRows
4677
+ ),
4678
+ northStarMetric(
4679
+ "feedback_readiness",
4680
+ "Feedback readiness",
4681
+ feedbackReady ? "pass" : feedbackRequiredForApproval ? "blocked" : "review",
4682
+ 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.",
4683
+ feedbackReady,
4684
+ true
4685
+ ),
4686
+ northStarMetric(
4687
+ "causal_moat",
4688
+ "Causal moat",
4689
+ moat.state === "proved" ? "pass" : "review",
4690
+ moat.detail,
4691
+ moat.state,
4692
+ "proved"
4693
+ ),
4694
+ northStarMetric(
4695
+ "artifact_readback",
4696
+ "Artifact readback",
4697
+ artifactCount > 0 ? "pass" : "review",
4698
+ artifactCount > 0 ? `${artifactCount} artifact(s) are available for review.` : "No review artifact is available yet.",
4699
+ artifactCount,
4700
+ 1
4701
+ )
4702
+ ];
4703
+ const gradedMetrics = metrics.filter((metric) => metric.status !== "pending");
4704
+ const score = gradedMetrics.length === 0 ? null : Math.round(gradedMetrics.filter((metric) => metric.status === "pass").length / gradedMetrics.length * 100);
4705
+ const blockers = metrics.filter((metric) => metric.status === "blocked").map((metric) => metric.detail);
4706
+ const warnings = metrics.filter((metric) => metric.status === "review").map((metric) => metric.detail);
4707
+ const scorecardStatus = blockers.length > 0 ? "blocked" : warnings.length > 0 ? "review" : "pass";
4708
+ return {
4709
+ version: "campaign-builder-north-star.v1",
4710
+ status: scorecardStatus,
4711
+ score,
4712
+ moatState: moat.state,
4713
+ metrics,
4714
+ blockers,
4715
+ warnings,
4716
+ nextAction: scorecardStatus === "blocked" ? "Fix blocked North Star gates before approval or delivery." : terminalState.safeNextAction
4717
+ };
4718
+ }
3945
4719
  function createCampaignBuilderBuildReview(status, rows, artifacts) {
4720
+ const terminalState = status.terminalState ?? deriveCampaignBuildTerminalState({
4721
+ status: status.status,
4722
+ phase: status.currentPhase,
4723
+ phaseStatus: status.currentPhaseStatus,
4724
+ staleRunningPhase: status.staleRunningPhase === true,
4725
+ phaseAgeSeconds: status.phaseAgeSeconds ?? null,
4726
+ nextPollAfterSeconds: status.nextPollAfterSeconds ?? null,
4727
+ terminalReason: status.terminalReason ?? status.customerRowCounts?.terminalReason ?? null,
4728
+ staleReason: status.staleReason ?? null,
4729
+ nextAction: status.nextAction,
4730
+ approvalAction: status.approvalAction,
4731
+ triggerRunId: status.triggerRunId ?? null
4732
+ });
3946
4733
  const sampledRows = rows.rows.length;
3947
4734
  const availableRows = rows.count || sampledRows;
3948
4735
  const qualifiedRows = rows.rows.filter((row) => row.qualified !== false).length;
3949
4736
  const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
3950
4737
  const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
3951
4738
  const artifactCount = artifacts.length || status.artifactCount || 0;
4739
+ const northStarScorecard = createCampaignBuilderNorthStarScorecard(status, rows, artifacts, terminalState);
3952
4740
  const gates = [
3953
- {
3954
- id: "build_completed",
3955
- label: "Build completed",
3956
- passed: status.status === "completed",
3957
- detail: status.status === "completed" ? "The campaign build is completed." : `The campaign build is ${status.status}; review again after completion.`
3958
- },
3959
- {
3960
- id: "rows_available",
3961
- label: "Rows available",
3962
- passed: sampledRows > 0,
3963
- detail: sampledRows > 0 ? `${sampledRows} sampled row(s) are available for review.` : "No rows were returned for review."
3964
- },
3965
- {
3966
- id: "qualified_sample",
3967
- label: "Sample rows qualified",
3968
- passed: sampledRows > 0 && qualifiedRows === sampledRows,
3969
- detail: sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
3970
- },
3971
- {
3972
- id: "email_coverage",
3973
- label: "Email coverage",
3974
- passed: sampledRows > 0 && rowsWithEmail === sampledRows,
3975
- detail: sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
3976
- },
3977
- {
3978
- id: "artifact_available",
3979
- label: "Artifact available",
3980
- passed: artifactCount > 0,
3981
- detail: artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
3982
- },
3983
- {
3984
- id: "no_failed_rows",
3985
- label: "No failed rows",
3986
- passed: status.recordsFailed === 0,
3987
- detail: status.recordsFailed === 0 ? "The build reports zero failed rows." : `${status.recordsFailed} row(s) failed during the build.`
3988
- }
4741
+ campaignReviewGate(
4742
+ "build_reviewable",
4743
+ "Build reviewable",
4744
+ terminalState.kind === "running_healthy" ? "pending" : terminalState.kind === "running_stale" || terminalState.kind === "blocked" ? "blocked" : "pass",
4745
+ terminalState.label
4746
+ ),
4747
+ campaignReviewGate(
4748
+ "rows_available",
4749
+ "Rows available",
4750
+ sampledRows > 0 ? "pass" : terminalState.reviewable ? "blocked" : "pending",
4751
+ 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."
4752
+ ),
4753
+ campaignReviewGate(
4754
+ "qualified_sample",
4755
+ "Sample rows qualified",
4756
+ sampledRows === 0 ? terminalState.reviewable ? "blocked" : "pending" : qualifiedRows === sampledRows ? "pass" : "blocked",
4757
+ sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
4758
+ ),
4759
+ campaignReviewGate(
4760
+ "email_coverage",
4761
+ "Email coverage",
4762
+ sampledRows === 0 ? terminalState.reviewable ? "blocked" : "pending" : rowsWithEmail === sampledRows ? "pass" : "blocked",
4763
+ sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
4764
+ ),
4765
+ campaignReviewGate(
4766
+ "artifact_available",
4767
+ "Artifact available",
4768
+ artifactCount > 0 ? "pass" : terminalState.reviewable ? "review" : "pending",
4769
+ artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
4770
+ ),
4771
+ campaignReviewGate(
4772
+ "no_failed_rows",
4773
+ "No failed rows",
4774
+ status.recordsFailed === 0 ? "pass" : "blocked",
4775
+ status.recordsFailed === 0 ? "The build reports zero failed rows." : `${status.recordsFailed} row(s) failed during the build.`
4776
+ )
3989
4777
  ];
3990
- const blockedReasons = gates.filter((gate) => !gate.passed).map((gate) => gate.detail);
4778
+ const blockedReasons = gates.filter((gate) => gate.status === "blocked" || !gate.passed).map((gate) => gate.detail);
3991
4779
  const warnings = [
3992
4780
  rows.hasMore ? "This review is based on a row sample; page through remaining rows before final approval." : void 0,
3993
4781
  sampledRows > 0 && copyReadyRows < sampledRows ? `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.` : void 0,
3994
4782
  ...status.warnings
3995
4783
  ].filter((item) => Boolean(item));
3996
- const readyForDeliveryApproval = blockedReasons.length === 0;
4784
+ const readyForDeliveryApproval = terminalState.reviewable && blockedReasons.length === 0 && northStarScorecard.status !== "blocked";
3997
4785
  return {
3998
4786
  campaignBuildId: status.campaignBuildId || rows.campaignBuildId,
3999
4787
  status,
4000
4788
  rows,
4001
4789
  artifacts,
4002
4790
  gates,
4791
+ northStarScorecard,
4003
4792
  summary: {
4004
4793
  status: status.status,
4005
4794
  phase: status.currentPhase,
4795
+ terminalState: terminalState.kind,
4796
+ scorecardStatus: northStarScorecard.status,
4797
+ scorecardScore: northStarScorecard.score,
4006
4798
  sampledRows,
4007
4799
  availableRows,
4008
4800
  qualifiedRows,
@@ -4056,6 +4848,184 @@ function campaignReviewRowHasCopy(row) {
4056
4848
  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)
4057
4849
  );
4058
4850
  }
4851
+ function campaignReviewDataEmail(data) {
4852
+ return stringValue(data.email) || stringValue(data.work_email) || stringValue(data.workEmail) || stringValue(asRecord(data.contact).email);
4853
+ }
4854
+ function campaignReviewDataDomain(data) {
4855
+ const domain = stringValue(data.company_domain) || stringValue(data.companyDomain) || stringValue(asRecord(data.company).domain);
4856
+ return domain?.toLowerCase();
4857
+ }
4858
+ function campaignReviewDataHasVerifiedEmail(data) {
4859
+ const status = String(data.email_status ?? data.emailStatus ?? asRecord(data.contact).email_status ?? "").toLowerCase();
4860
+ return data.email_verified === true || data.emailVerified === true || asRecord(data.contact).email_verified === true || ["valid", "validated", "verified"].includes(status);
4861
+ }
4862
+ function campaignReviewDataCopyParts(data) {
4863
+ const copy = asRecord(data.copy);
4864
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4865
+ return [
4866
+ stringValue(data.subject),
4867
+ stringValue(data.subject_line),
4868
+ stringValue(data.opener),
4869
+ stringValue(data.body),
4870
+ stringValue(data.cta),
4871
+ stringValue(copy.subject),
4872
+ stringValue(copy.opener),
4873
+ stringValue(copy.body),
4874
+ stringValue(copy.cta),
4875
+ stringValue(rawCopy.subject),
4876
+ stringValue(rawCopy.opener),
4877
+ stringValue(rawCopy.body),
4878
+ stringValue(rawCopy.cta)
4879
+ ].filter((part) => Boolean(part));
4880
+ }
4881
+ function campaignReviewDataHasBadGreeting(data) {
4882
+ return campaignReviewDataCopyParts(data).some(
4883
+ (part) => /\bhi\s*(?:,|\{\{|\[)|\bhello\s*(?:,|\{\{|\[)/i.test(part) || /\b(first_name|firstname|name)\b/i.test(part)
4884
+ );
4885
+ }
4886
+ function campaignReviewDataHasBannedPhrase(data) {
4887
+ const text = campaignReviewDataCopyParts(data).join(" ").toLowerCase();
4888
+ return NORTH_STAR_BANNED_COPY_PHRASES.some((phrase) => text.includes(phrase));
4889
+ }
4890
+ function campaignReviewDataSubject(data) {
4891
+ const copy = asRecord(data.copy);
4892
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4893
+ return stringValue(data.subject) || stringValue(data.subject_line) || stringValue(data.subjectLine) || stringValue(copy.subject) || stringValue(rawCopy.subject);
4894
+ }
4895
+ function campaignReviewDataCta(data) {
4896
+ const copy = asRecord(data.copy);
4897
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4898
+ return stringValue(data.cta) || stringValue(copy.cta) || stringValue(rawCopy.cta);
4899
+ }
4900
+ function countCampaignReviewIssues(issueRows) {
4901
+ return issueRows.flat().reduce((acc, issue) => {
4902
+ acc[issue] = (acc[issue] || 0) + 1;
4903
+ return acc;
4904
+ }, {});
4905
+ }
4906
+ function formatCampaignReviewIssueCounts(counts) {
4907
+ const entries = Object.entries(counts);
4908
+ return entries.length > 0 ? entries.map(([issue, count]) => `${issue}=${count}`).join(", ") : "none";
4909
+ }
4910
+ function campaignReviewDataCopyQaIssues(data) {
4911
+ const issues = /* @__PURE__ */ new Set();
4912
+ const copyParts = campaignReviewDataCopyParts(data);
4913
+ if (copyParts.length === 0) return [];
4914
+ if (campaignReviewDataHasBadGreeting(data)) issues.add("bad_greeting");
4915
+ if (campaignReviewDataHasBannedPhrase(data)) issues.add("banned_or_vague_phrase");
4916
+ if (campaignReviewDataHasUnsupportedSignalClaim(data)) issues.add("unsupported_signal_claim");
4917
+ if (campaignReviewDataPersonalizationBasis(data).length === 0) issues.add("missing_personalization_basis");
4918
+ if (!campaignReviewDataCta(data)) issues.add("missing_cta");
4919
+ const subject = campaignReviewDataSubject(data);
4920
+ if (subject && subject.length > 70) issues.add("overlong_subject");
4921
+ return [...issues];
4922
+ }
4923
+ function campaignReviewDataPersonalizationBasis(data) {
4924
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4925
+ const copy = asRecord(data.copy);
4926
+ const metadata = asRecord(data.metadata);
4927
+ const signalEvidence = asRecord(data.signal_evidence ?? metadata.signal_evidence ?? metadata.signalEvidence);
4928
+ return [
4929
+ ...arrayOfStrings(data.personalization_basis) ?? [],
4930
+ ...arrayOfStrings(data.personalizationBasis) ?? [],
4931
+ ...arrayOfStrings(copy.personalization_basis) ?? [],
4932
+ ...arrayOfStrings(rawCopy.personalization_basis) ?? [],
4933
+ ...arrayOfStrings(metadata.personalization_basis) ?? [],
4934
+ ...arrayOfStrings(metadata.personalizationBasis) ?? [],
4935
+ ...arrayOfStrings(signalEvidence.personalization_basis) ?? []
4936
+ ];
4937
+ }
4938
+ function campaignReviewDataHasNoSupportedSignal(data) {
4939
+ const metadata = asRecord(data.metadata);
4940
+ const copy = asRecord(data.copy);
4941
+ const state = [
4942
+ data.copy_signal_state,
4943
+ data.signal_basis,
4944
+ data.signal_type,
4945
+ metadata.copy_signal_state,
4946
+ metadata.signal_basis,
4947
+ metadata.signal_type,
4948
+ copy.copy_signal_state
4949
+ ].map((value) => stringValue(value)?.toLowerCase()).filter(Boolean);
4950
+ return data.no_supported_signal === true || metadata.no_supported_signal === true || state.includes("no_supported_signal");
4951
+ }
4952
+ function campaignReviewDataHasSignalBasis(data) {
4953
+ if (campaignReviewDataHasNoSupportedSignal(data)) return false;
4954
+ const metadata = asRecord(data.metadata);
4955
+ const signal = asRecord(data.signal);
4956
+ const signalEvidence = asRecord(data.signal_evidence ?? metadata.signal_evidence ?? metadata.signalEvidence);
4957
+ const basis = campaignReviewDataPersonalizationBasis(data);
4958
+ return Boolean(
4959
+ 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))
4960
+ );
4961
+ }
4962
+ function campaignReviewDataHasUnsupportedSignalClaim(data) {
4963
+ const copyText = campaignReviewDataCopyParts(data).join(" ").toLowerCase();
4964
+ const claimsSignal = /\b(hiring|funding|raised|launched|expanding|appointed|announced|recent news|new role|job posting)\b/.test(copyText);
4965
+ return claimsSignal && !campaignReviewDataHasSignalBasis(data);
4966
+ }
4967
+ function campaignReviewDataHasApprovalLock(data) {
4968
+ const metadata = asRecord(data.fit_metadata ?? data.metadata);
4969
+ const approval = String(data.approval_status ?? data.delivery_approval_status ?? metadata.approval_status ?? "").toLowerCase();
4970
+ const exportStatus = String(data.export_status ?? data.exportStatus ?? "").toLowerCase();
4971
+ 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);
4972
+ }
4973
+ function campaignReviewDataHasHoldoutAttribution(data) {
4974
+ const fitMetadata = asRecord(data.fit_metadata ?? data.fitMetadata);
4975
+ const metadata = asRecord(data.metadata);
4976
+ const holdout = asRecord(
4977
+ fitMetadata.learning_holdout ?? fitMetadata.learningHoldout ?? metadata.learning_holdout ?? metadata.learningHoldout ?? data.learning_holdout ?? data.learningHoldout
4978
+ );
4979
+ const experimentKey = stringValue(holdout.experiment_key ?? holdout.experimentKey ?? data.experiment_key ?? data.experimentKey);
4980
+ const cohort = stringValue(holdout.cohort_role ?? holdout.cohortRole ?? data.cohort_role ?? data.learning_cohort ?? data.learningCohort);
4981
+ const brainApplied = holdout.brain_applied ?? holdout.brainApplied ?? data.brain_applied ?? data.signaliz_brain_applied;
4982
+ return Boolean(experimentKey && cohort && brainApplied !== void 0);
4983
+ }
4984
+ function campaignReviewStatusHasFeedbackReadiness(status) {
4985
+ const diagnostics = asRecord(status.diagnostics);
4986
+ const feedbackReadiness = asRecord(diagnostics.feedback_readiness ?? diagnostics.feedbackReadiness);
4987
+ const feedbackSubstrate = asRecord(diagnostics.feedback_substrate ?? diagnostics.feedbackSubstrate);
4988
+ const evidenceCounts = asRecord(diagnostics.evidence_counts ?? diagnostics.evidenceCounts);
4989
+ return feedbackReadiness.ready === true || feedbackReadiness.webhook_configured === true || feedbackSubstrate.stage === "ready" || Number(evidenceCounts.feedback_events ?? 0) > 0;
4990
+ }
4991
+ function campaignReviewMoatState(status, holdoutRows) {
4992
+ const diagnostics = asRecord(status.diagnostics);
4993
+ const evidenceCounts = asRecord(diagnostics.evidence_counts ?? diagnostics.evidenceCounts);
4994
+ const embeddedHoldout = asRecord(diagnostics.embedded_holdout ?? diagnostics.embeddedHoldout);
4995
+ const holdoutLift = asRecord(diagnostics.holdout_lift ?? diagnostics.holdoutLift ?? diagnostics.holdout_measurement ?? diagnostics.holdoutMeasurement);
4996
+ const controlSample = Math.max(
4997
+ Number(evidenceCounts.holdout_control_sample_size ?? 0),
4998
+ Number(evidenceCounts.embedded_holdout_control_sample ?? 0),
4999
+ Number(embeddedHoldout.control_sample_size ?? 0)
5000
+ );
5001
+ const learnedSample = Math.max(
5002
+ Number(evidenceCounts.holdout_learned_sample_size ?? 0),
5003
+ Number(evidenceCounts.embedded_holdout_learned_sample ?? 0),
5004
+ Number(embeddedHoldout.learned_sample_size ?? 0)
5005
+ );
5006
+ const feedbackEvents = Math.max(
5007
+ Number(evidenceCounts.feedback_events ?? 0),
5008
+ Number(evidenceCounts.campaign_build_feedback_events ?? 0),
5009
+ Number(evidenceCounts.delivery_holdout_sent_outcomes ?? 0)
5010
+ );
5011
+ const proved = diagnostics.closed_loop_proof_ready === true || holdoutLift.honest_measurement === true && holdoutLift.positive_lift === true;
5012
+ if (proved) {
5013
+ return {
5014
+ state: "proved",
5015
+ detail: "Real attributed feedback shows positive lift for explicit control and learned cohorts."
5016
+ };
5017
+ }
5018
+ if (controlSample > 0 && learnedSample > 0 && feedbackEvents > 0) {
5019
+ return {
5020
+ state: "moved_needs_lift_volume",
5021
+ detail: `Control and learned cohorts have attributed feedback (${controlSample} control, ${learnedSample} learned, ${feedbackEvents} feedback event(s)); keep collecting lift volume before claiming proof.`
5022
+ };
5023
+ }
5024
+ return {
5025
+ state: "scaffolding_ready",
5026
+ 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."
5027
+ };
5028
+ }
4059
5029
  function createCampaignBuilderReadinessLane(route, plan) {
4060
5030
  const builtIn = campaignBuilderBuiltInForRoute(route);
4061
5031
  const customRoute = route.provider !== "signaliz" && route.provider !== "csv" && builtIn === void 0;
@@ -4097,6 +5067,8 @@ function readinessLaneLabel(route, builtIn) {
4097
5067
  const labels = {
4098
5068
  lead_generation: "Signaliz lead generation",
4099
5069
  local_leads: "Signaliz local leads",
5070
+ company_discovery: "Signaliz company discovery",
5071
+ people_discovery: "Signaliz people discovery",
4100
5072
  email_finding: "Signaliz email finding",
4101
5073
  email_verification: "Signaliz email verification",
4102
5074
  signals: "Signaliz signals"
@@ -4181,6 +5153,35 @@ function summarizeCampaignBuilderProofDryRun(dryRunResult) {
4181
5153
  plannedTargetCount: dryRunResult.plannedTargetCount ?? dryRunResult.planned_target_count ?? null
4182
5154
  };
4183
5155
  }
5156
+ function summarizeCampaignBuilderProofLearnBack(plan) {
5157
+ if (Object.keys(plan).length === 0) return { ready: false };
5158
+ const memoryPolicy = asRecord(plan.memory_write_policy);
5159
+ const memoryItemsPolicy = asRecord(memoryPolicy.gtm_memory_items);
5160
+ const sequence = Array.isArray(plan.post_build_sequence) ? plan.post_build_sequence.map((step) => {
5161
+ const record = asRecord(step);
5162
+ return compact({
5163
+ step: record.step,
5164
+ tool: stringValue(record.tool),
5165
+ approval_boundary: stringValue(record.approval_boundary),
5166
+ reason: stringValue(record.reason)
5167
+ });
5168
+ }).filter((step) => typeof step.tool === "string") : [];
5169
+ return {
5170
+ ready: sequence.length > 0,
5171
+ source_tool: firstNonEmptyString(plan.source_tool, plan.sourceTool),
5172
+ post_build_sequence: sequence,
5173
+ memory_write_policy: {
5174
+ target_tables: Array.isArray(memoryPolicy.target_tables) ? memoryPolicy.target_tables : [],
5175
+ gtm_memory_items: {
5176
+ shareable: memoryItemsPolicy.shareable === false ? false : memoryItemsPolicy.shareable ?? null,
5177
+ allowed_redaction_states: Array.isArray(memoryItemsPolicy.allowed_redaction_states) ? memoryItemsPolicy.allowed_redaction_states : [],
5178
+ raw_private_fields_withheld: Array.isArray(memoryItemsPolicy.raw_private_fields_withheld) ? memoryItemsPolicy.raw_private_fields_withheld : []
5179
+ }
5180
+ },
5181
+ approval_policy: firstNonEmptyString(plan.approval_policy, plan.approvalPolicy),
5182
+ privacy_policy: firstNonEmptyString(plan.privacy_policy, plan.privacyPolicy)
5183
+ };
5184
+ }
4184
5185
  function firstNonEmptyString(...values) {
4185
5186
  for (const value of values) {
4186
5187
  if (typeof value === "string" && value.trim()) return value;
@@ -4663,14 +5664,87 @@ var Ops = class {
4663
5664
  const body = typeof params === "string" ? { prompt: params } : buildOpsCreateBody(params);
4664
5665
  return normalizeOpsCreateResult(withExecutionRefs(await this.call("ops_create", body)));
4665
5666
  }
5667
+ async schedule(params) {
5668
+ const createBody = typeof params === "string" ? { prompt: params, cadence: "daily" } : { ...buildOpsCreateBody(params), cadence: params.cadence ?? "daily" };
5669
+ const { activate: _activate, ...body } = createBody;
5670
+ return normalizeOpsCreateResult(withExecutionRefs(await this.call("ops_schedule", body)));
5671
+ }
5672
+ async scheduleAndWait(params) {
5673
+ const options = typeof params === "string" ? { schedule: { prompt: params, cadence: "daily" }, run: {}, wait: {} } : buildOpsScheduleAndWaitOptions(params);
5674
+ const raw = await this.call("ops_schedule_and_wait", buildOpsScheduleAndWaitBody(options));
5675
+ return normalizeOpsScheduleAndWaitCallResult(raw, options.wait, {
5676
+ approvalMessage: "ops.scheduleAndWait requires an approved schedulable Op. Retry with confirmSpend=true after reviewing approval details.",
5677
+ notCreatedMessage: "ops.scheduleAndWait requires ops_schedule_and_wait to create an op_id. Use ops.schedule for approval review flows."
5678
+ });
5679
+ }
5680
+ /** Schedule a recurring Op that delivers through a customer-owned Nango API connection. */
5681
+ async scheduleNangoAction(input) {
5682
+ return normalizeOpsCreateResult(withExecutionRefs(await this.call("ops_nango_schedule", buildOpsNangoScheduleBody(input))));
5683
+ }
5684
+ /** Schedule a recurring Nango-backed Op, run the first tick, and return result readback. */
5685
+ async scheduleNangoActionAndWait(input) {
5686
+ const options = buildOpsNangoScheduleAndWaitOptions(input);
5687
+ const raw = await this.call("ops_nango_schedule_and_wait", buildOpsNangoScheduleAndWaitBody(options));
5688
+ return normalizeOpsScheduleAndWaitCallResult(raw, options.wait, {
5689
+ approvalMessage: "ops.scheduleNangoActionAndWait requires an approved Nango-backed Op. Retry with confirmSpend=true after reviewing approval details.",
5690
+ notCreatedMessage: "ops.scheduleNangoActionAndWait requires ops_nango_schedule_and_wait to create an op_id. Use ops.scheduleNangoAction for approval review flows."
5691
+ });
5692
+ }
4666
5693
  async execute(params) {
4667
5694
  const body = typeof params === "string" ? { prompt: params } : buildOpsExecuteBody(params);
4668
5695
  return normalizeOpsExecuteResult(withExecutionRefs(await this.call("ops_execute", body)));
4669
5696
  }
5697
+ async executeAndWait(params) {
5698
+ const options = typeof params === "string" ? { execute: { prompt: params, auto_run: true }, wait: {} } : buildOpsExecuteAndWaitOptions(params);
5699
+ const execute = await this.execute(options.execute);
5700
+ if (execute.approval_required || execute.error_code === "APPROVAL_REQUIRED") {
5701
+ throw new SignalizError({
5702
+ code: "APPROVAL_REQUIRED",
5703
+ errorType: "validation",
5704
+ message: "ops.executeAndWait requires an approved executable Op. Retry with confirmSpend=true after reviewing approval details.",
5705
+ details: { execute }
5706
+ });
5707
+ }
5708
+ if (!execute.op_id) {
5709
+ throw new SignalizError({
5710
+ code: "OP_NOT_CREATED",
5711
+ errorType: "validation",
5712
+ message: "ops.executeAndWait requires ops_execute to create an op_id. Use ops.execute for dry runs or approval review flows.",
5713
+ details: { execute }
5714
+ });
5715
+ }
5716
+ const waited = await this.waitForResults({
5717
+ ...options.wait,
5718
+ op_id: execute.op_id
5719
+ });
5720
+ const runId = execute.run_id ?? execute.runId ?? waited.status.run_id ?? waited.status.runId ?? null;
5721
+ return {
5722
+ ...waited,
5723
+ execute,
5724
+ run_id: runId,
5725
+ runId,
5726
+ execution_refs: mergeExecutionRefsFrom([execute, waited])
5727
+ };
5728
+ }
4670
5729
  async run(params) {
4671
5730
  const body = typeof params === "string" ? { op_id: params } : buildOpsRunBody(params);
4672
5731
  return normalizeOpsRunResult(withExecutionRefs(await this.call("ops_run", body)));
4673
5732
  }
5733
+ async runAndWait(params) {
5734
+ const options = typeof params === "string" ? { op_id: params } : buildOpsRunAndWaitOptions(params);
5735
+ if (!options.op_id) throw new Error("ops.runAndWait requires op_id or opId");
5736
+ const run = await this.run({
5737
+ op_id: options.op_id,
5738
+ instruction: options.instruction,
5739
+ force: options.force
5740
+ });
5741
+ const waited = await this.waitForResults(options);
5742
+ return {
5743
+ ...waited,
5744
+ run,
5745
+ execution_refs: mergeExecutionRefsFrom([run, waited])
5746
+ };
5747
+ }
4674
5748
  async status(params) {
4675
5749
  const body = typeof params === "string" ? { op_id: params } : buildOpsStatusBody(params);
4676
5750
  return normalizeOpsStatusResult(withExecutionRefs(await this.call("ops_status", body)));
@@ -4679,21 +5753,188 @@ var Ops = class {
4679
5753
  const body = typeof params === "string" ? { op_id: params } : buildOpsResultsBody(params);
4680
5754
  return normalizeOpsResultsResult(withExecutionRefs(await this.call("ops_results", body)));
4681
5755
  }
4682
- async proof(params = {}) {
4683
- const data = await this.call("ops_proof", {
4684
- window_hours: params.windowHours,
4685
- output_format: "json"
4686
- });
4687
- return normalizeOpsProofResult(data);
5756
+ async waitForResults(params) {
5757
+ const options = typeof params === "string" ? { op_id: params } : buildOpsWaitForResultsOptions(params);
5758
+ if (!options.op_id) throw new Error("ops.waitForResults requires op_id or opId");
5759
+ const waitResult = await this.call("ops_wait", buildOpsWaitBody(options));
5760
+ return normalizeOpsWaitForResultsResult(waitResult, options);
4688
5761
  }
4689
- async debug(params) {
4690
- const options = typeof params === "string" ? { op_id: params } : buildOpsDebugOptions(params);
4691
- const diagnosticErrors = [];
4692
- const status = await this.status(options.op_id);
4693
- const refs = /* @__PURE__ */ new Map();
4694
- const addRefs = (items) => {
4695
- for (const ref of items ?? []) refs.set(`${ref.kind}:${ref.id}`, ref);
4696
- };
5762
+ /** Create a short-lived Nango Connect session from the Ops namespace. */
5763
+ async createNangoConnectSession(input) {
5764
+ return this.call("nango_connect_session_create", {
5765
+ provider_id: input.providerId,
5766
+ integration_id: input.integrationId,
5767
+ provider_display_name: input.providerDisplayName,
5768
+ integration_display_name: input.integrationDisplayName,
5769
+ allowed_integrations: input.allowedIntegrations,
5770
+ surface: input.surface,
5771
+ source: input.source,
5772
+ user_email: input.userEmail,
5773
+ user_display_name: input.userDisplayName
5774
+ });
5775
+ }
5776
+ /** Prepare the full Nango provider search, connect, pull/export, Ops, and route flow. */
5777
+ async prepareNangoIntegrationFlow(input = {}) {
5778
+ return this.call("nango_integration_flow_prepare", {
5779
+ query: input.query,
5780
+ search: input.search,
5781
+ provider_id: input.providerId,
5782
+ provider: input.provider,
5783
+ integration_id: input.integrationId,
5784
+ provider_config_key: input.providerConfigKey,
5785
+ workspace_connection_id: input.workspaceConnectionId,
5786
+ connection_id: input.connectionId,
5787
+ nango_connection_id: input.nangoConnectionId,
5788
+ intent: input.intent,
5789
+ action_name: input.actionName,
5790
+ tool_name: input.toolName,
5791
+ proxy_path: input.proxyPath,
5792
+ method: input.method,
5793
+ input: input.input,
5794
+ cadence: input.cadence,
5795
+ field_map: input.fieldMap,
5796
+ required_fields: input.requiredFields,
5797
+ confirm_spend: input.confirmSpend,
5798
+ write_confirmed: input.writeConfirmed,
5799
+ layer: input.layer,
5800
+ campaign_id: input.campaignId,
5801
+ limit: input.limit,
5802
+ include_provider_catalog: input.includeProviderCatalog
5803
+ });
5804
+ }
5805
+ /** List Nango-backed action tools for a workspace connection without executing them. */
5806
+ async listNangoTools(options = {}) {
5807
+ return this.call("nango_mcp_tools_list", {
5808
+ workspace_connection_id: options.workspaceConnectionId,
5809
+ connection_id: options.connectionId,
5810
+ provider_config_key: options.providerConfigKey,
5811
+ integration_id: options.integrationId,
5812
+ nango_connection_id: options.nangoConnectionId,
5813
+ format: options.format,
5814
+ include_raw: options.includeRaw
5815
+ });
5816
+ }
5817
+ /** Audit connected Nango rows, action/proxy readiness, and read/write proof gaps. */
5818
+ async proveNangoReadWrite(options = {}) {
5819
+ return this.call("nango_mcp_read_write_audit", {
5820
+ workspace_connection_id: options.workspaceConnectionId,
5821
+ connection_id: options.connectionId,
5822
+ provider_config_key: options.providerConfigKey,
5823
+ integration_id: options.integrationId,
5824
+ nango_connection_id: options.nangoConnectionId,
5825
+ limit: options.limit,
5826
+ include_raw: options.includeRaw,
5827
+ read_proxy_path: options.readProxyPath,
5828
+ write_proxy_path: options.writeProxyPath,
5829
+ method: options.method,
5830
+ execute_read_probe: options.executeReadProbe,
5831
+ execute_write_probe: options.executeWriteProbe,
5832
+ confirm: options.confirm,
5833
+ confirm_write: options.confirmWrite,
5834
+ input: options.input,
5835
+ write_input: options.writeInput
5836
+ });
5837
+ }
5838
+ /** Dry-run or execute a Nango-backed action tool through the approval-aware MCP bridge. */
5839
+ async callNangoTool(input) {
5840
+ return this.call("nango_mcp_tool_call", {
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,
5856
+ max_retries: input.maxRetries,
5857
+ dry_run: input.dryRun,
5858
+ confirm: input.confirm,
5859
+ confirm_write: input.confirmWrite
5860
+ });
5861
+ }
5862
+ /** Execute a Nango action and poll its async result when a status URL/action id is returned. */
5863
+ async callNangoToolAndWait(input) {
5864
+ const result = await this.call("nango_mcp_tool_call_and_wait", {
5865
+ workspace_connection_id: input.workspaceConnectionId,
5866
+ connection_id: input.connectionId,
5867
+ provider_config_key: input.providerConfigKey,
5868
+ integration_id: input.integrationId,
5869
+ nango_connection_id: input.nangoConnectionId,
5870
+ action_name: input.actionName,
5871
+ tool_name: input.toolName,
5872
+ delivery_mode: input.deliveryMode,
5873
+ proxy_path: input.proxyPath,
5874
+ nango_proxy_path: input.nangoProxyPath,
5875
+ method: input.method,
5876
+ http_method: input.httpMethod,
5877
+ proxy_headers: input.proxyHeaders,
5878
+ input: input.input,
5879
+ async: input.async ?? true,
5880
+ max_retries: input.maxRetries,
5881
+ dry_run: input.dryRun,
5882
+ confirm: input.confirm,
5883
+ confirm_write: input.confirmWrite,
5884
+ interval_ms: input.intervalMs,
5885
+ max_polls: input.maxPolls,
5886
+ timeout_ms: input.timeoutMs
5887
+ });
5888
+ const packet = asRecord2(result);
5889
+ const actionId = optionalString(packet.actionId) ?? optionalString(packet.action_id);
5890
+ const statusUrl = optionalString(packet.statusUrl) ?? optionalString(packet.status_url);
5891
+ const maxPolls = Number(packet.maxPolls ?? packet.max_polls ?? 0);
5892
+ const intervalMs = Number(packet.intervalMs ?? packet.interval_ms ?? 0);
5893
+ const timeoutMs = Number(packet.timeoutMs ?? packet.timeout_ms ?? 0);
5894
+ const timedOut = Boolean(packet.timedOut ?? packet.timed_out);
5895
+ return {
5896
+ ...packet,
5897
+ success: typeof packet.success === "boolean" ? packet.success : void 0,
5898
+ call: packet.call,
5899
+ result: packet.result,
5900
+ status: optionalString(packet.status),
5901
+ action_id: actionId,
5902
+ actionId,
5903
+ status_url: statusUrl,
5904
+ statusUrl,
5905
+ polls: Number(packet.polls ?? 0),
5906
+ max_polls: maxPolls,
5907
+ maxPolls,
5908
+ interval_ms: intervalMs,
5909
+ intervalMs,
5910
+ timeout_ms: timeoutMs,
5911
+ timeoutMs,
5912
+ timed_out: timedOut,
5913
+ timedOut
5914
+ };
5915
+ }
5916
+ /** Poll an async Nango action result by action id or status URL. */
5917
+ async getNangoActionResult(input) {
5918
+ return this.call("nango_mcp_action_result_get", {
5919
+ action_id: input.actionId,
5920
+ status_url: input.statusUrl
5921
+ });
5922
+ }
5923
+ async proof(params = {}) {
5924
+ const data = await this.call("ops_proof", {
5925
+ window_hours: params.windowHours,
5926
+ output_format: "json"
5927
+ });
5928
+ return normalizeOpsProofResult(data);
5929
+ }
5930
+ async debug(params) {
5931
+ const options = typeof params === "string" ? { op_id: params } : buildOpsDebugOptions(params);
5932
+ const diagnosticErrors = [];
5933
+ const status = await this.status(options.op_id);
5934
+ const refs = /* @__PURE__ */ new Map();
5935
+ const addRefs = (items) => {
5936
+ for (const ref of items ?? []) refs.set(`${ref.kind}:${ref.id}`, ref);
5937
+ };
4697
5938
  const recordError = (step, err) => {
4698
5939
  diagnosticErrors.push({
4699
5940
  step,
@@ -4705,51 +5946,57 @@ var Ops = class {
4705
5946
  addRefs(collectExecutionReferences(status.latest_run));
4706
5947
  }
4707
5948
  const runRefs = Array.from(refs.values()).filter((ref) => ref.kind === "trigger_run" || ref.kind === "dataplane_run" || ref.kind === "replay_run");
4708
- const runStatuses = [];
4709
- for (const ref of runRefs) {
4710
- try {
4711
- const result = await this.getTriggerRunStatus(ref.id);
4712
- const runs = "runs" in result ? result.runs : [result];
4713
- runStatuses.push(...runs);
4714
- for (const run of runs) addRefs(run.execution_refs);
4715
- } catch (err) {
4716
- recordError(`run-status:${ref.id}`, err);
4717
- }
4718
- }
5949
+ let runStatuses = [];
4719
5950
  let queue;
4720
- if (options.include_queue !== false) {
4721
- try {
4722
- queue = await this.getQueueStatus({ summary: true });
4723
- addRefs(queue.execution_refs);
4724
- } catch (err) {
4725
- recordError("queue", err);
4726
- }
4727
- }
4728
5951
  let dashboardRecent;
4729
- if (options.include_dashboard !== false) {
4730
- try {
4731
- dashboardRecent = await this.getDashboard({
4732
- section: "recent",
4733
- limit: options.dashboard_limit ?? 10,
4734
- timeRangeDays: options.time_range_days
4735
- });
4736
- } catch (err) {
4737
- recordError("dashboard:recent", err);
4738
- }
4739
- }
4740
5952
  let results;
4741
- if (options.include_results) {
4742
- try {
4743
- results = await this.results({
4744
- op_id: options.op_id,
4745
- limit: options.results_limit ?? 25,
4746
- include_failed_runs: true
4747
- });
4748
- addRefs(results.execution_refs);
4749
- } catch (err) {
4750
- recordError("results", err);
4751
- }
4752
- }
5953
+ await Promise.all([
5954
+ (async () => {
5955
+ if (runRefs.length === 0) return;
5956
+ try {
5957
+ const result = await this.getTriggerRunStatus({ run_ids: runRefs.map((ref) => ref.id) });
5958
+ const runs = "runs" in result ? result.runs : [result];
5959
+ runStatuses = runs;
5960
+ for (const run of runs) addRefs(run.execution_refs);
5961
+ } catch (err) {
5962
+ recordError(runRefs.length === 1 ? `run-status:${runRefs[0].id}` : "run-status:batch", err);
5963
+ }
5964
+ })(),
5965
+ (async () => {
5966
+ if (options.include_queue === false) return;
5967
+ try {
5968
+ queue = await this.getQueueStatus({ summary: true });
5969
+ addRefs(queue.execution_refs);
5970
+ } catch (err) {
5971
+ recordError("queue", err);
5972
+ }
5973
+ })(),
5974
+ (async () => {
5975
+ if (options.include_dashboard === false) return;
5976
+ try {
5977
+ dashboardRecent = await this.getDashboard({
5978
+ section: "recent",
5979
+ limit: options.dashboard_limit ?? 10,
5980
+ timeRangeDays: options.time_range_days
5981
+ });
5982
+ } catch (err) {
5983
+ recordError("dashboard:recent", err);
5984
+ }
5985
+ })(),
5986
+ (async () => {
5987
+ if (!options.include_results) return;
5988
+ try {
5989
+ results = await this.results({
5990
+ op_id: options.op_id,
5991
+ limit: options.results_limit ?? 25,
5992
+ include_failed_runs: true
5993
+ });
5994
+ addRefs(results.execution_refs);
5995
+ } catch (err) {
5996
+ recordError("results", err);
5997
+ }
5998
+ })()
5999
+ ]);
4753
6000
  const executionRefs = Array.from(refs.values());
4754
6001
  const replayCandidates = executionRefs.filter((ref) => ref.kind === "execution_event");
4755
6002
  const nextActions = buildDebugNextActions(status, executionRefs, diagnosticErrors);
@@ -4951,6 +6198,69 @@ function mergeExecutionRefs(result) {
4951
6198
  for (const ref of collectExecutionReferences(result)) add(ref);
4952
6199
  return Array.from(refs.values());
4953
6200
  }
6201
+ function mergeExecutionRefsFrom(results) {
6202
+ const refs = /* @__PURE__ */ new Map();
6203
+ for (const result of results) {
6204
+ if (!result) continue;
6205
+ for (const ref of mergeExecutionRefs(result)) refs.set(`${ref.kind}:${ref.id}`, ref);
6206
+ }
6207
+ return Array.from(refs.values());
6208
+ }
6209
+ function normalizeOpsScheduleAndWaitCallResult(raw, waitOptions, messages) {
6210
+ if (raw.approval_required || raw.approvalRequired || raw.error_code === "APPROVAL_REQUIRED" || raw.errorCode === "APPROVAL_REQUIRED") {
6211
+ throw new SignalizError({
6212
+ code: "APPROVAL_REQUIRED",
6213
+ errorType: "validation",
6214
+ message: messages.approvalMessage,
6215
+ details: { schedule: raw }
6216
+ });
6217
+ }
6218
+ const opId = stringValue2(raw.op_id ?? raw.opId);
6219
+ if (!opId) {
6220
+ throw new SignalizError({
6221
+ code: "OP_NOT_CREATED",
6222
+ errorType: "validation",
6223
+ message: messages.notCreatedMessage,
6224
+ details: { schedule: raw }
6225
+ });
6226
+ }
6227
+ const scheduleRaw = asRecord2(raw.schedule ?? raw.schedule_result ?? raw.scheduleResult);
6228
+ const runRaw = asRecord2(raw.run ?? raw.run_result ?? raw.runResult);
6229
+ const schedule = normalizeOpsCreateResult(withExecutionRefs(
6230
+ Object.keys(scheduleRaw).length > 0 ? scheduleRaw : {
6231
+ ...raw,
6232
+ op_id: opId,
6233
+ routine_id: raw.routine_id ?? raw.routineId ?? opId,
6234
+ status: "ready",
6235
+ phase: "scheduled",
6236
+ next_action: raw.next_action ?? raw.nextAction
6237
+ }
6238
+ ));
6239
+ const run = normalizeOpsRunResult(withExecutionRefs(
6240
+ Object.keys(runRaw).length > 0 ? runRaw : {
6241
+ success: raw.success !== false,
6242
+ op_id: opId,
6243
+ routine_id: raw.routine_id ?? raw.routineId ?? opId,
6244
+ run_id: raw.run_id ?? raw.runId ?? null,
6245
+ status: "running",
6246
+ phase: "queued",
6247
+ next_action: raw.next_action ?? raw.nextAction,
6248
+ results_count: 0,
6249
+ results_url: raw.results_url ?? raw.resultsUrl,
6250
+ delivery_status: raw.delivery_status ?? raw.deliveryStatus
6251
+ }
6252
+ ));
6253
+ const waited = normalizeOpsWaitForResultsResult(raw, { ...waitOptions, op_id: opId });
6254
+ const runId = run.run_id ?? run.runId ?? waited.run_id ?? waited.runId ?? waited.status.run_id ?? waited.status.runId ?? null;
6255
+ return {
6256
+ ...waited,
6257
+ schedule,
6258
+ run,
6259
+ run_id: runId,
6260
+ runId,
6261
+ execution_refs: mergeExecutionRefsFrom([schedule, run, waited])
6262
+ };
6263
+ }
4954
6264
  function normalizeOpsPrimitiveRetryPolicy(policy = {}) {
4955
6265
  return {
4956
6266
  ...policy,
@@ -5026,8 +6336,122 @@ function normalizeOpsPrimitive(primitive) {
5026
6336
  observability: normalizeOpsPrimitiveObservabilityPolicy(primitive.observability)
5027
6337
  };
5028
6338
  }
6339
+ function normalizeOpsExecutionToolCall(rawCall) {
6340
+ const call = asRecord2(rawCall);
6341
+ return {
6342
+ ...call,
6343
+ tool: stringValue2(call.tool),
6344
+ arguments: asRecord2(call.arguments),
6345
+ purpose: stringValue2(call.purpose)
6346
+ };
6347
+ }
6348
+ function normalizeOpsExecutionScheduleContract(rawSchedule) {
6349
+ const schedule = asRecord2(rawSchedule);
6350
+ const wakeOnEvents = Array.isArray(schedule.wake_on_events) ? schedule.wake_on_events.map(String) : Array.isArray(schedule.wakeOnEvents) ? schedule.wakeOnEvents.map(String) : [];
6351
+ return {
6352
+ ...schedule,
6353
+ create_tool: stringValue2(schedule.create_tool ?? schedule.createTool),
6354
+ createTool: stringValue2(schedule.createTool ?? schedule.create_tool),
6355
+ activate_tool: stringValue2(schedule.activate_tool ?? schedule.activateTool),
6356
+ activateTool: stringValue2(schedule.activateTool ?? schedule.activate_tool),
6357
+ cadence: stringValue2(schedule.cadence, "manual"),
6358
+ recurrence: stringValue2(schedule.recurrence),
6359
+ wake_on_events: wakeOnEvents,
6360
+ wakeOnEvents
6361
+ };
6362
+ }
6363
+ function normalizeOpsExecutionApprovalContract(rawApproval) {
6364
+ const approval = asRecord2(rawApproval);
6365
+ return {
6366
+ ...approval,
6367
+ required: Boolean(approval.required),
6368
+ tool: stringValue2(approval.tool),
6369
+ reason: stringValue2(approval.reason)
6370
+ };
6371
+ }
6372
+ function normalizeOpsExecutionMonitorContract(rawMonitor) {
6373
+ const monitor = asRecord2(rawMonitor);
6374
+ const pollAfterMs = numberValue(monitor.poll_after_ms ?? monitor.pollAfterMs);
6375
+ return {
6376
+ ...monitor,
6377
+ status_tool: stringValue2(monitor.status_tool ?? monitor.statusTool),
6378
+ statusTool: stringValue2(monitor.statusTool ?? monitor.status_tool),
6379
+ wait_tool: stringValue2(monitor.wait_tool ?? monitor.waitTool),
6380
+ waitTool: stringValue2(monitor.waitTool ?? monitor.wait_tool),
6381
+ results_tool: stringValue2(monitor.results_tool ?? monitor.resultsTool),
6382
+ resultsTool: stringValue2(monitor.resultsTool ?? monitor.results_tool),
6383
+ poll_after_ms: pollAfterMs,
6384
+ pollAfterMs
6385
+ };
6386
+ }
6387
+ function normalizeOpsExecutionResultContract(rawResult) {
6388
+ const result = asRecord2(rawResult);
6389
+ const shape = Array.isArray(result.shape) ? result.shape.map(String) : [];
6390
+ return {
6391
+ ...result,
6392
+ tool: stringValue2(result.tool),
6393
+ wait_tool: stringValue2(result.wait_tool ?? result.waitTool),
6394
+ waitTool: stringValue2(result.waitTool ?? result.wait_tool),
6395
+ shape,
6396
+ empty_result_recovery: stringValue2(result.empty_result_recovery ?? result.emptyResultRecovery),
6397
+ emptyResultRecovery: stringValue2(result.emptyResultRecovery ?? result.empty_result_recovery)
6398
+ };
6399
+ }
6400
+ function normalizeOpsExecutionNangoRouteContract(rawRoute) {
6401
+ if (!rawRoute || typeof rawRoute !== "object" || Array.isArray(rawRoute)) return void 0;
6402
+ const route = asRecord2(rawRoute);
6403
+ const requiredFields = Array.isArray(route.required_fields) ? route.required_fields.map(String) : Array.isArray(route.requiredFields) ? route.requiredFields.map(String) : [];
6404
+ return {
6405
+ ...route,
6406
+ enabled: route.enabled !== false,
6407
+ connect_tool: stringValue2(route.connect_tool ?? route.connectTool),
6408
+ connectTool: stringValue2(route.connectTool ?? route.connect_tool),
6409
+ discover_tool: stringValue2(route.discover_tool ?? route.discoverTool),
6410
+ discoverTool: stringValue2(route.discoverTool ?? route.discover_tool),
6411
+ dry_run_tool: stringValue2(route.dry_run_tool ?? route.dryRunTool),
6412
+ dryRunTool: stringValue2(route.dryRunTool ?? route.dry_run_tool),
6413
+ execute_tool: stringValue2(route.execute_tool ?? route.executeTool),
6414
+ executeTool: stringValue2(route.executeTool ?? route.execute_tool),
6415
+ execute_and_wait_tool: stringValue2(route.execute_and_wait_tool ?? route.executeAndWaitTool),
6416
+ executeAndWaitTool: stringValue2(route.executeAndWaitTool ?? route.execute_and_wait_tool),
6417
+ schedule_tool: stringValue2(route.schedule_tool ?? route.scheduleTool),
6418
+ scheduleTool: stringValue2(route.scheduleTool ?? route.schedule_tool),
6419
+ schedule_and_wait_tool: stringValue2(route.schedule_and_wait_tool ?? route.scheduleAndWaitTool),
6420
+ scheduleAndWaitTool: stringValue2(route.scheduleAndWaitTool ?? route.schedule_and_wait_tool),
6421
+ result_tool: stringValue2(route.result_tool ?? route.resultTool),
6422
+ resultTool: stringValue2(route.resultTool ?? route.result_tool),
6423
+ write_gate: stringValue2(route.write_gate ?? route.writeGate),
6424
+ writeGate: stringValue2(route.writeGate ?? route.write_gate),
6425
+ required_fields: requiredFields,
6426
+ requiredFields,
6427
+ schedule_arguments: asRecord2(route.schedule_arguments ?? route.scheduleArguments),
6428
+ scheduleArguments: asRecord2(route.scheduleArguments ?? route.schedule_arguments),
6429
+ schedule_and_wait_arguments: asRecord2(route.schedule_and_wait_arguments ?? route.scheduleAndWaitArguments),
6430
+ scheduleAndWaitArguments: asRecord2(route.scheduleAndWaitArguments ?? route.schedule_and_wait_arguments)
6431
+ };
6432
+ }
6433
+ function normalizeOpsExecutionContract(rawContract) {
6434
+ if (!rawContract || typeof rawContract !== "object" || Array.isArray(rawContract)) return void 0;
6435
+ const contract = asRecord2(rawContract);
6436
+ const nangoRoute = normalizeOpsExecutionNangoRouteContract(contract.nango_route ?? contract.nangoRoute);
6437
+ const sequence = Array.isArray(contract.sequence) ? contract.sequence.map(normalizeOpsExecutionToolCall) : [];
6438
+ return {
6439
+ ...contract,
6440
+ mode: stringValue2(contract.mode, "run_once"),
6441
+ run_now: normalizeOpsExecutionToolCall(contract.run_now ?? contract.runNow),
6442
+ runNow: normalizeOpsExecutionToolCall(contract.runNow ?? contract.run_now),
6443
+ schedule: normalizeOpsExecutionScheduleContract(contract.schedule),
6444
+ approval: normalizeOpsExecutionApprovalContract(contract.approval),
6445
+ monitor: normalizeOpsExecutionMonitorContract(contract.monitor),
6446
+ result_contract: normalizeOpsExecutionResultContract(contract.result_contract ?? contract.resultContract),
6447
+ resultContract: normalizeOpsExecutionResultContract(contract.resultContract ?? contract.result_contract),
6448
+ ...nangoRoute ? { nango_route: nangoRoute, nangoRoute } : {},
6449
+ sequence
6450
+ };
6451
+ }
5029
6452
  function normalizeOpsPlanResult(result) {
5030
6453
  const primitiveGraph = result.primitive_graph ?? result.primitiveGraph;
6454
+ const executionContract = normalizeOpsExecutionContract(result.execution_contract ?? result.executionContract);
5031
6455
  return {
5032
6456
  ...result,
5033
6457
  plan_id: result.plan_id ?? result.planId,
@@ -5051,7 +6475,8 @@ function normalizeOpsPlanResult(result) {
5051
6475
  destination_status: result.destination_status ?? result.destinationStatus,
5052
6476
  destinationStatus: result.destinationStatus ?? result.destination_status,
5053
6477
  primitive_graph: primitiveGraph?.map(normalizeOpsPrimitive),
5054
- primitiveGraph: primitiveGraph?.map(normalizeOpsPrimitive)
6478
+ primitiveGraph: primitiveGraph?.map(normalizeOpsPrimitive),
6479
+ ...executionContract ? { execution_contract: executionContract, executionContract } : {}
5055
6480
  };
5056
6481
  }
5057
6482
  function normalizeOpsCreateResult(result) {
@@ -5073,7 +6498,18 @@ function normalizeOpsCreateResult(result) {
5073
6498
  deliveryStatus: result.deliveryStatus ?? result.delivery_status,
5074
6499
  estimated_credits: result.estimated_credits ?? result.estimatedCredits,
5075
6500
  estimatedCredits: result.estimatedCredits ?? result.estimated_credits,
5076
- plan: result.plan ? normalizeOpsPlanResult(result.plan) : result.plan
6501
+ error_code: result.error_code ?? result.errorCode,
6502
+ errorCode: result.errorCode ?? result.error_code,
6503
+ approval_required: result.approval_required ?? result.approvalRequired,
6504
+ approvalRequired: result.approvalRequired ?? result.approval_required,
6505
+ retry_arguments: result.retry_arguments ?? result.retryArguments,
6506
+ retryArguments: result.retryArguments ?? result.retry_arguments,
6507
+ blockers: result.blockers,
6508
+ plan: result.plan ? normalizeOpsPlanResult(result.plan) : result.plan,
6509
+ next_step: result.next_step ?? result.nextStep,
6510
+ nextStep: result.nextStep ?? result.next_step,
6511
+ next_steps: result.next_steps ?? result.nextSteps,
6512
+ nextSteps: result.nextSteps ?? result.next_steps
5077
6513
  };
5078
6514
  }
5079
6515
  function normalizeOpsExecuteResult(result) {
@@ -5201,6 +6637,40 @@ function normalizeOpsStatusResult(result) {
5201
6637
  diagnosis: result.diagnosis ? normalizeOpsDebugDiagnosis(result.diagnosis) : void 0
5202
6638
  };
5203
6639
  }
6640
+ function normalizeOpsResultsSummary(value) {
6641
+ const summary = asRecord2(value);
6642
+ if (Object.keys(summary).length === 0) return void 0;
6643
+ const resultsTotal = summary.results_total ?? summary.resultsTotal;
6644
+ const nextCursor = summary.next_cursor ?? summary.nextCursor ?? null;
6645
+ const resultsUrl = summary.results_url ?? summary.resultsUrl;
6646
+ const nextAction = summary.next_action ?? summary.nextAction;
6647
+ const runId = summary.run_id ?? summary.runId ?? null;
6648
+ const creditsUsed = summary.credits_used ?? summary.creditsUsed;
6649
+ return {
6650
+ ...summary,
6651
+ label: optionalString(summary.label),
6652
+ status: optionalString(summary.status),
6653
+ phase: optionalString(summary.phase),
6654
+ results_count: numberValue(summary.results_count ?? summary.resultsCount),
6655
+ resultsCount: numberValue(summary.results_count ?? summary.resultsCount),
6656
+ results_total: resultsTotal === null || resultsTotal === void 0 ? resultsTotal : numberValue(resultsTotal),
6657
+ resultsTotal: resultsTotal === null || resultsTotal === void 0 ? resultsTotal : numberValue(resultsTotal),
6658
+ delivery_status: optionalString(summary.delivery_status ?? summary.deliveryStatus),
6659
+ deliveryStatus: optionalString(summary.delivery_status ?? summary.deliveryStatus),
6660
+ has_more: Boolean(summary.has_more ?? summary.hasMore),
6661
+ hasMore: Boolean(summary.has_more ?? summary.hasMore),
6662
+ next_cursor: nextCursor === null ? null : optionalString(nextCursor),
6663
+ nextCursor: nextCursor === null ? null : optionalString(nextCursor),
6664
+ results_url: optionalString(resultsUrl),
6665
+ resultsUrl: optionalString(resultsUrl),
6666
+ next_action: optionalString(nextAction),
6667
+ nextAction: optionalString(nextAction),
6668
+ run_id: runId === null ? null : optionalString(runId),
6669
+ runId: runId === null ? null : optionalString(runId),
6670
+ credits_used: creditsUsed === void 0 ? void 0 : numberValue(creditsUsed),
6671
+ creditsUsed: creditsUsed === void 0 ? void 0 : numberValue(creditsUsed)
6672
+ };
6673
+ }
5204
6674
  function normalizeOpsResultsResult(result) {
5205
6675
  const nextCursor = result.next_cursor ?? result.nextCursor ?? null;
5206
6676
  const hasMore = result.has_more ?? result.hasMore ?? false;
@@ -5225,7 +6695,104 @@ function normalizeOpsResultsResult(result) {
5225
6695
  results_url: result.results_url ?? result.resultsUrl,
5226
6696
  resultsUrl: result.resultsUrl ?? result.results_url,
5227
6697
  delivery_status: result.delivery_status ?? result.deliveryStatus,
5228
- deliveryStatus: result.deliveryStatus ?? result.delivery_status
6698
+ deliveryStatus: result.deliveryStatus ?? result.delivery_status,
6699
+ summary: normalizeOpsResultsSummary(result.summary)
6700
+ };
6701
+ }
6702
+ function normalizeOpsWaitForResultsPoll(value, fallbackPoll) {
6703
+ const raw = asRecord2(value);
6704
+ const checkedAt = stringValue2(raw.checked_at ?? raw.checkedAt);
6705
+ const resultsCount = raw.results_count ?? raw.resultsCount;
6706
+ const runId = raw.run_id ?? raw.runId ?? null;
6707
+ const nextAction = raw.next_action ?? raw.nextAction;
6708
+ return {
6709
+ ...raw,
6710
+ poll: numberValue(raw.poll) || fallbackPoll,
6711
+ checked_at: checkedAt,
6712
+ checkedAt,
6713
+ status: stringValue2(raw.status, "unknown"),
6714
+ phase: optionalString(raw.phase),
6715
+ results_count: resultsCount === void 0 ? void 0 : numberValue(resultsCount),
6716
+ resultsCount: resultsCount === void 0 ? void 0 : numberValue(resultsCount),
6717
+ run_id: runId === null ? null : optionalString(runId),
6718
+ runId: runId === null ? null : optionalString(runId),
6719
+ next_action: optionalString(nextAction),
6720
+ nextAction: optionalString(nextAction)
6721
+ };
6722
+ }
6723
+ function normalizeOpsWaitForResultsResult(result, options) {
6724
+ const opId = stringValue2(result.op_id ?? result.opId ?? options.op_id);
6725
+ const routineId = stringValue2(result.routine_id ?? result.routineId ?? opId);
6726
+ const runId = result.run_id ?? result.runId ?? null;
6727
+ const timedOut = Boolean(result.timed_out ?? result.timedOut);
6728
+ const statusRaw = asRecord2(result.status_result ?? result.statusResult);
6729
+ const resultsRaw = asRecord2(result.results_result ?? result.resultsResult);
6730
+ const historySource = Array.isArray(result.poll_history) ? result.poll_history : Array.isArray(result.history) ? result.history : [];
6731
+ const history = historySource.map((item, index) => normalizeOpsWaitForResultsPoll(item, index + 1));
6732
+ const status = normalizeOpsStatusResult(withExecutionRefs({
6733
+ success: result.success !== false,
6734
+ op_id: opId,
6735
+ routine_id: routineId,
6736
+ run_id: runId,
6737
+ status: stringValue2(result.status, "unknown"),
6738
+ phase: stringValue2(result.phase, "unknown"),
6739
+ next_action: stringValue2(result.next_action ?? result.nextAction),
6740
+ results_count: numberValue(result.results_count ?? result.resultsCount),
6741
+ results_url: optionalString(result.results_url ?? result.resultsUrl),
6742
+ delivery_status: optionalString(result.delivery_status ?? result.deliveryStatus),
6743
+ ...statusRaw
6744
+ }));
6745
+ const includeResults = options.include_results !== false && options.includeResults !== false;
6746
+ const hasResultsPacket = Object.keys(resultsRaw).length > 0 || Array.isArray(result.results);
6747
+ const results = includeResults && hasResultsPacket ? normalizeOpsResultsResult(withExecutionRefs({
6748
+ success: result.success !== false,
6749
+ op_id: opId,
6750
+ routine_id: routineId,
6751
+ run_id: runId,
6752
+ status: stringValue2(result.status, status.status),
6753
+ phase: stringValue2(result.phase, status.phase),
6754
+ results_count: numberValue(result.results_count ?? result.resultsCount),
6755
+ results_total: result.results_total ?? result.resultsTotal ?? null,
6756
+ results: Array.isArray(result.results) ? result.results : [],
6757
+ next_cursor: result.next_cursor ?? result.nextCursor ?? null,
6758
+ has_more: Boolean(result.has_more ?? result.hasMore),
6759
+ next_action: stringValue2(result.next_action ?? result.nextAction),
6760
+ results_url: optionalString(result.results_url ?? result.resultsUrl),
6761
+ delivery_status: optionalString(result.delivery_status ?? result.deliveryStatus),
6762
+ ...resultsRaw
6763
+ })) : void 0;
6764
+ const polls = numberValue(result.polls) || history.length;
6765
+ const maxPolls = result.max_polls ?? result.maxPolls;
6766
+ const intervalMs = result.interval_ms ?? result.intervalMs;
6767
+ const timeoutMs = result.timeout_ms ?? result.timeoutMs;
6768
+ const stopReason = result.stop_reason ?? result.stopReason;
6769
+ const nextAction = result.next_action ?? result.nextAction ?? results?.next_action ?? status.next_action;
6770
+ return {
6771
+ ...result,
6772
+ success: result.success !== false && !timedOut,
6773
+ op_id: opId,
6774
+ opId,
6775
+ routine_id: routineId,
6776
+ routineId,
6777
+ run_id: runId === null ? null : optionalString(runId),
6778
+ runId: runId === null ? null : optionalString(runId),
6779
+ status,
6780
+ results,
6781
+ timed_out: timedOut,
6782
+ timedOut,
6783
+ polls,
6784
+ max_polls: maxPolls === void 0 ? void 0 : numberValue(maxPolls),
6785
+ maxPolls: maxPolls === void 0 ? void 0 : numberValue(maxPolls),
6786
+ interval_ms: intervalMs === void 0 ? void 0 : numberValue(intervalMs),
6787
+ intervalMs: intervalMs === void 0 ? void 0 : numberValue(intervalMs),
6788
+ timeout_ms: timeoutMs === void 0 ? void 0 : numberValue(timeoutMs),
6789
+ timeoutMs: timeoutMs === void 0 ? void 0 : numberValue(timeoutMs),
6790
+ stop_reason: optionalString(stopReason),
6791
+ stopReason: optionalString(stopReason),
6792
+ history,
6793
+ next_action: optionalString(nextAction),
6794
+ nextAction: optionalString(nextAction),
6795
+ execution_refs: mergeExecutionRefsFrom([result, status, results, { history }])
5229
6796
  };
5230
6797
  }
5231
6798
  function normalizeOpsProofResult(data) {
@@ -5305,11 +6872,107 @@ function normalizeOpsProofResult(data) {
5305
6872
  raw: data
5306
6873
  };
5307
6874
  }
6875
+ function normalizeOpsAutopilotSchedulePacket(rawPacket) {
6876
+ const packet = asRecord2(rawPacket);
6877
+ return {
6878
+ ...packet,
6879
+ cadence: stringValue2(packet.cadence, "manual"),
6880
+ recurrence: stringValue2(packet.recurrence),
6881
+ activate_with: stringValue2(packet.activate_with ?? packet.activateWith),
6882
+ activateWith: stringValue2(packet.activate_with ?? packet.activateWith),
6883
+ run_now_with: stringValue2(packet.run_now_with ?? packet.runNowWith),
6884
+ runNowWith: stringValue2(packet.run_now_with ?? packet.runNowWith),
6885
+ monitor_with: stringValue2(packet.monitor_with ?? packet.monitorWith),
6886
+ monitorWith: stringValue2(packet.monitor_with ?? packet.monitorWith),
6887
+ retrieve_with: stringValue2(packet.retrieve_with ?? packet.retrieveWith),
6888
+ retrieveWith: stringValue2(packet.retrieve_with ?? packet.retrieveWith)
6889
+ };
6890
+ }
6891
+ function normalizeOpsAutopilotNangoRoutePacket(rawPacket) {
6892
+ if (!rawPacket || typeof rawPacket !== "object" || Array.isArray(rawPacket)) return void 0;
6893
+ const packet = asRecord2(rawPacket);
6894
+ const requiredFields = Array.isArray(packet.required_fields) ? packet.required_fields.map(String) : Array.isArray(packet.requiredFields) ? packet.requiredFields.map(String) : [];
6895
+ return {
6896
+ ...packet,
6897
+ enabled: packet.enabled !== false,
6898
+ route_label: stringValue2(packet.route_label ?? packet.routeLabel),
6899
+ routeLabel: stringValue2(packet.route_label ?? packet.routeLabel),
6900
+ connect_tool: stringValue2(packet.connect_tool ?? packet.connectTool),
6901
+ connectTool: stringValue2(packet.connect_tool ?? packet.connectTool),
6902
+ discover_tool: stringValue2(packet.discover_tool ?? packet.discoverTool),
6903
+ discoverTool: stringValue2(packet.discover_tool ?? packet.discoverTool),
6904
+ dry_run_tool: stringValue2(packet.dry_run_tool ?? packet.dryRunTool),
6905
+ dryRunTool: stringValue2(packet.dry_run_tool ?? packet.dryRunTool),
6906
+ execute_tool: stringValue2(packet.execute_tool ?? packet.executeTool),
6907
+ executeTool: stringValue2(packet.execute_tool ?? packet.executeTool),
6908
+ execute_and_wait_tool: stringValue2(packet.execute_and_wait_tool ?? packet.executeAndWaitTool),
6909
+ executeAndWaitTool: stringValue2(packet.execute_and_wait_tool ?? packet.executeAndWaitTool),
6910
+ schedule_tool: stringValue2(packet.schedule_tool ?? packet.scheduleTool),
6911
+ scheduleTool: stringValue2(packet.schedule_tool ?? packet.scheduleTool),
6912
+ schedule_and_wait_tool: stringValue2(packet.schedule_and_wait_tool ?? packet.scheduleAndWaitTool),
6913
+ scheduleAndWaitTool: stringValue2(packet.schedule_and_wait_tool ?? packet.scheduleAndWaitTool),
6914
+ result_tool: stringValue2(packet.result_tool ?? packet.resultTool),
6915
+ resultTool: stringValue2(packet.result_tool ?? packet.resultTool),
6916
+ write_gate: stringValue2(packet.write_gate ?? packet.writeGate),
6917
+ writeGate: stringValue2(packet.write_gate ?? packet.writeGate),
6918
+ required_fields: requiredFields,
6919
+ requiredFields,
6920
+ placeholders: asRecord2(packet.placeholders),
6921
+ dry_run_arguments: asRecord2(packet.dry_run_arguments ?? packet.dryRunArguments),
6922
+ dryRunArguments: asRecord2(packet.dry_run_arguments ?? packet.dryRunArguments),
6923
+ execute_arguments: asRecord2(packet.execute_arguments ?? packet.executeArguments),
6924
+ executeArguments: asRecord2(packet.execute_arguments ?? packet.executeArguments),
6925
+ execute_and_wait_arguments: asRecord2(packet.execute_and_wait_arguments ?? packet.executeAndWaitArguments),
6926
+ executeAndWaitArguments: asRecord2(packet.execute_and_wait_arguments ?? packet.executeAndWaitArguments),
6927
+ schedule_arguments: asRecord2(packet.schedule_arguments ?? packet.scheduleArguments),
6928
+ scheduleArguments: asRecord2(packet.schedule_arguments ?? packet.scheduleArguments),
6929
+ schedule_and_wait_arguments: asRecord2(packet.schedule_and_wait_arguments ?? packet.scheduleAndWaitArguments),
6930
+ scheduleAndWaitArguments: asRecord2(packet.schedule_and_wait_arguments ?? packet.scheduleAndWaitArguments),
6931
+ result_arguments: asRecord2(packet.result_arguments ?? packet.resultArguments),
6932
+ resultArguments: asRecord2(packet.result_arguments ?? packet.resultArguments)
6933
+ };
6934
+ }
6935
+ function normalizeOpsAutopilotResultPacket(rawPacket) {
6936
+ const packet = asRecord2(rawPacket);
6937
+ const resultShape = Array.isArray(packet.result_shape) ? packet.result_shape.map(String) : Array.isArray(packet.resultShape) ? packet.resultShape.map(String) : [];
6938
+ const deliveryReceipts = Array.isArray(packet.delivery_receipts) ? packet.delivery_receipts.map(String) : Array.isArray(packet.deliveryReceipts) ? packet.deliveryReceipts.map(String) : [];
6939
+ return {
6940
+ ...packet,
6941
+ retrieve_tool: stringValue2(packet.retrieve_tool ?? packet.retrieveTool),
6942
+ retrieveTool: stringValue2(packet.retrieve_tool ?? packet.retrieveTool),
6943
+ result_shape: resultShape,
6944
+ resultShape,
6945
+ delivery_receipts: deliveryReceipts,
6946
+ deliveryReceipts,
6947
+ empty_result_recovery: stringValue2(packet.empty_result_recovery ?? packet.emptyResultRecovery),
6948
+ emptyResultRecovery: stringValue2(packet.empty_result_recovery ?? packet.emptyResultRecovery)
6949
+ };
6950
+ }
6951
+ function normalizeOpsAutopilotOperatingPacket(rawPacket) {
6952
+ if (!rawPacket || typeof rawPacket !== "object" || Array.isArray(rawPacket)) return void 0;
6953
+ const packet = asRecord2(rawPacket);
6954
+ const nangoRoute = normalizeOpsAutopilotNangoRoutePacket(packet.nango_route ?? packet.nangoRoute);
6955
+ const approvalBoundaries = Array.isArray(packet.approval_boundaries) ? packet.approval_boundaries.map(String) : Array.isArray(packet.approvalBoundaries) ? packet.approvalBoundaries.map(String) : [];
6956
+ return {
6957
+ ...packet,
6958
+ north_star: stringValue2(packet.north_star ?? packet.northStar),
6959
+ northStar: stringValue2(packet.north_star ?? packet.northStar),
6960
+ schedule: normalizeOpsAutopilotSchedulePacket(packet.schedule),
6961
+ ...nangoRoute ? { nango_route: nangoRoute, nangoRoute } : {},
6962
+ result_contract: normalizeOpsAutopilotResultPacket(packet.result_contract ?? packet.resultContract),
6963
+ resultContract: normalizeOpsAutopilotResultPacket(packet.result_contract ?? packet.resultContract),
6964
+ approval_boundaries: approvalBoundaries,
6965
+ approvalBoundaries,
6966
+ saved_command_hint: stringValue2(packet.saved_command_hint ?? packet.savedCommandHint),
6967
+ savedCommandHint: stringValue2(packet.saved_command_hint ?? packet.savedCommandHint)
6968
+ };
6969
+ }
5308
6970
  function normalizeOpsAutopilotMotion(rawMotion) {
5309
6971
  const motion = asRecord2(rawMotion);
5310
6972
  const targetCount = numberValue(motion.target_count ?? motion.targetCount);
5311
6973
  const estimatedCredits = numberValue(motion.estimated_credits ?? motion.estimatedCredits);
5312
6974
  const mcpSequence = Array.isArray(motion.mcp_sequence) ? motion.mcp_sequence : Array.isArray(motion.mcpSequence) ? motion.mcpSequence : [];
6975
+ const operatingPacket = normalizeOpsAutopilotOperatingPacket(motion.operating_packet ?? motion.operatingPacket);
5313
6976
  return {
5314
6977
  ...motion,
5315
6978
  id: stringValue2(motion.id),
@@ -5332,12 +6995,16 @@ function normalizeOpsAutopilotMotion(rawMotion) {
5332
6995
  cli_command: optionalString(motion.cli_command ?? motion.cliCommand),
5333
6996
  cliCommand: optionalString(motion.cli_command ?? motion.cliCommand),
5334
6997
  mcp_sequence: mcpSequence,
5335
- mcpSequence
6998
+ mcpSequence,
6999
+ ...operatingPacket ? { operating_packet: operatingPacket, operatingPacket } : {}
5336
7000
  };
5337
7001
  }
5338
7002
  function normalizeOpsAutopilotResult(result) {
5339
7003
  const primary = normalizeOpsAutopilotMotion(result.primary_motion ?? result.primaryMotion);
5340
7004
  const motions = Array.isArray(result.motions) ? result.motions.map(normalizeOpsAutopilotMotion) : [];
7005
+ const operatingPacket = normalizeOpsAutopilotOperatingPacket(
7006
+ result.operating_packet ?? result.operatingPacket ?? primary.operating_packet ?? primary.operatingPacket
7007
+ );
5341
7008
  const queryErrors = Array.isArray(result.query_errors) ? result.query_errors.map(String) : Array.isArray(result.queryErrors) ? result.queryErrors.map(String) : [];
5342
7009
  return {
5343
7010
  ...result,
@@ -5353,6 +7020,7 @@ function normalizeOpsAutopilotResult(result) {
5353
7020
  scaleRule: optionalString(result.scale_rule ?? result.scaleRule),
5354
7021
  agent_handoff: optionalString(result.agent_handoff ?? result.agentHandoff),
5355
7022
  agentHandoff: optionalString(result.agent_handoff ?? result.agentHandoff),
7023
+ ...operatingPacket ? { operating_packet: operatingPacket, operatingPacket } : {},
5356
7024
  proof_state: optionalString(result.proof_state ?? result.proofState),
5357
7025
  proofState: optionalString(result.proof_state ?? result.proofState),
5358
7026
  proof_summary: result.proof_summary ?? result.proofSummary,
@@ -5617,11 +7285,125 @@ function normalizeOpsRoutineTickItemsResult(result) {
5617
7285
  hasMore
5618
7286
  };
5619
7287
  }
7288
+ function buildOpsNangoScheduleBody(input) {
7289
+ const {
7290
+ workspaceConnectionId,
7291
+ workspace_connection_id,
7292
+ connectionId,
7293
+ connection_id,
7294
+ providerConfigKey,
7295
+ provider_config_key,
7296
+ integrationId,
7297
+ integration_id,
7298
+ nangoConnectionId,
7299
+ nango_connection_id,
7300
+ actionName,
7301
+ action_name,
7302
+ toolName,
7303
+ tool_name,
7304
+ proxyPath,
7305
+ proxy_path,
7306
+ nangoProxyPath,
7307
+ nango_proxy_path,
7308
+ method,
7309
+ httpMethod,
7310
+ http_method,
7311
+ input: nangoInput,
7312
+ arguments: nangoArguments,
7313
+ fieldMap,
7314
+ field_map,
7315
+ requiredFields,
7316
+ required_fields,
7317
+ writeConfirmed,
7318
+ write_confirmed,
7319
+ agentWriteConfirmed,
7320
+ agent_write_confirmed,
7321
+ config,
7322
+ destinations: _destinations,
7323
+ ...schedule
7324
+ } = input;
7325
+ return {
7326
+ ...buildOpsCreateBody({
7327
+ ...schedule,
7328
+ cadence: schedule.cadence ?? "daily"
7329
+ }),
7330
+ workspace_connection_id: workspace_connection_id ?? workspaceConnectionId,
7331
+ connection_id: connection_id ?? connectionId,
7332
+ provider_config_key: provider_config_key ?? providerConfigKey,
7333
+ integration_id: integration_id ?? integrationId,
7334
+ nango_connection_id: nango_connection_id ?? nangoConnectionId,
7335
+ action_name: action_name ?? actionName,
7336
+ tool_name: tool_name ?? toolName,
7337
+ proxy_path: proxy_path ?? proxyPath ?? nango_proxy_path ?? nangoProxyPath,
7338
+ nango_proxy_path: nango_proxy_path ?? nangoProxyPath,
7339
+ method: method ?? http_method ?? httpMethod,
7340
+ input: nangoInput ?? nangoArguments,
7341
+ field_map: field_map ?? fieldMap,
7342
+ required_fields: required_fields ?? requiredFields,
7343
+ write_confirmed: write_confirmed ?? writeConfirmed,
7344
+ agent_write_confirmed: agent_write_confirmed ?? agentWriteConfirmed,
7345
+ config
7346
+ };
7347
+ }
7348
+ function buildOpsNangoScheduleAndWaitOptions(params) {
7349
+ const {
7350
+ force,
7351
+ instruction,
7352
+ nextCursor,
7353
+ includeFailedRuns,
7354
+ intervalMs,
7355
+ maxPolls,
7356
+ timeoutMs,
7357
+ includeResults,
7358
+ cursor,
7359
+ state,
7360
+ limit,
7361
+ include_failed_runs,
7362
+ interval_ms,
7363
+ max_polls,
7364
+ timeout_ms,
7365
+ include_results,
7366
+ ...schedule
7367
+ } = params;
7368
+ return {
7369
+ schedule: {
7370
+ ...schedule,
7371
+ cadence: schedule.cadence ?? "daily"
7372
+ },
7373
+ run: {
7374
+ force,
7375
+ instruction
7376
+ },
7377
+ wait: {
7378
+ cursor: cursor ?? nextCursor,
7379
+ state,
7380
+ limit,
7381
+ include_failed_runs: include_failed_runs ?? includeFailedRuns,
7382
+ interval_ms: interval_ms ?? intervalMs,
7383
+ max_polls: max_polls ?? maxPolls,
7384
+ timeout_ms: timeout_ms ?? timeoutMs,
7385
+ include_results: include_results ?? includeResults
7386
+ }
7387
+ };
7388
+ }
7389
+ function buildOpsNangoScheduleAndWaitBody(options) {
7390
+ const waitBody = buildOpsWaitBody(options.wait);
7391
+ const { op_id: _opId, ...waitWithoutOp } = waitBody;
7392
+ return {
7393
+ ...buildOpsNangoScheduleBody(options.schedule),
7394
+ ...waitWithoutOp,
7395
+ instruction: options.run.instruction,
7396
+ force: options.run.force
7397
+ };
7398
+ }
5620
7399
  function buildOpsPlanBody(params) {
5621
- const { targetCount, companyDomains, customAiPrompt, signalPrompt, outputPrompt, ...rest } = params;
7400
+ const { targetCount, wakeOnEvents, companyDomains, customAiPrompt, signalPrompt, outputPrompt, ...rest } = params;
7401
+ const destinations = Array.isArray(rest.destinations) ? rest.destinations.map(normalizeOpsDestinationRequest) : rest.destinations;
5622
7402
  return {
5623
7403
  ...rest,
7404
+ destinations,
5624
7405
  target_count: rest.target_count ?? targetCount,
7406
+ wake_on_events: rest.wake_on_events ?? wakeOnEvents,
5625
7407
  company_domains: rest.company_domains ?? companyDomains,
5626
7408
  custom_ai_prompt: rest.custom_ai_prompt ?? customAiPrompt,
5627
7409
  signal_prompt: rest.signal_prompt ?? signalPrompt,
@@ -5644,6 +7426,95 @@ function buildOpsExecuteBody(params) {
5644
7426
  output_format: rest.output_format ?? outputFormat
5645
7427
  };
5646
7428
  }
7429
+ function buildOpsExecuteAndWaitOptions(params) {
7430
+ const {
7431
+ nextCursor,
7432
+ includeFailedRuns,
7433
+ intervalMs,
7434
+ maxPolls,
7435
+ timeoutMs,
7436
+ includeResults,
7437
+ cursor,
7438
+ state,
7439
+ limit,
7440
+ include_failed_runs,
7441
+ interval_ms,
7442
+ max_polls,
7443
+ timeout_ms,
7444
+ include_results,
7445
+ ...execute
7446
+ } = params;
7447
+ return {
7448
+ execute: {
7449
+ ...execute,
7450
+ auto_run: execute.auto_run ?? execute.autoRun ?? true
7451
+ },
7452
+ wait: {
7453
+ cursor: cursor ?? nextCursor,
7454
+ state,
7455
+ limit,
7456
+ include_failed_runs: include_failed_runs ?? includeFailedRuns,
7457
+ interval_ms: interval_ms ?? intervalMs,
7458
+ max_polls: max_polls ?? maxPolls,
7459
+ timeout_ms: timeout_ms ?? timeoutMs,
7460
+ include_results: include_results ?? includeResults
7461
+ }
7462
+ };
7463
+ }
7464
+ function buildOpsScheduleAndWaitOptions(params) {
7465
+ const {
7466
+ force,
7467
+ instruction,
7468
+ nextCursor,
7469
+ includeFailedRuns,
7470
+ intervalMs,
7471
+ maxPolls,
7472
+ timeoutMs,
7473
+ includeResults,
7474
+ cursor,
7475
+ state,
7476
+ limit,
7477
+ include_failed_runs,
7478
+ interval_ms,
7479
+ max_polls,
7480
+ timeout_ms,
7481
+ include_results,
7482
+ ...schedule
7483
+ } = params;
7484
+ return {
7485
+ schedule: {
7486
+ ...schedule,
7487
+ cadence: schedule.cadence ?? "daily"
7488
+ },
7489
+ run: {
7490
+ force,
7491
+ instruction
7492
+ },
7493
+ wait: {
7494
+ cursor: cursor ?? nextCursor,
7495
+ state,
7496
+ limit,
7497
+ include_failed_runs: include_failed_runs ?? includeFailedRuns,
7498
+ interval_ms: interval_ms ?? intervalMs,
7499
+ max_polls: max_polls ?? maxPolls,
7500
+ timeout_ms: timeout_ms ?? timeoutMs,
7501
+ include_results: include_results ?? includeResults
7502
+ }
7503
+ };
7504
+ }
7505
+ function buildOpsScheduleAndWaitBody(options) {
7506
+ const waitBody = buildOpsWaitBody(options.wait);
7507
+ const { op_id: _opId, ...waitWithoutOp } = waitBody;
7508
+ return {
7509
+ ...buildOpsCreateBody({
7510
+ ...options.schedule,
7511
+ cadence: options.schedule.cadence ?? "daily"
7512
+ }),
7513
+ ...waitWithoutOp,
7514
+ instruction: options.run.instruction,
7515
+ force: options.run.force
7516
+ };
7517
+ }
5647
7518
  function buildOpsRunBody(params) {
5648
7519
  const { opId, ...rest } = params;
5649
7520
  return {
@@ -5651,6 +7522,28 @@ function buildOpsRunBody(params) {
5651
7522
  op_id: rest.op_id ?? opId
5652
7523
  };
5653
7524
  }
7525
+ function buildOpsRunAndWaitOptions(params) {
7526
+ const {
7527
+ opId,
7528
+ nextCursor,
7529
+ includeFailedRuns,
7530
+ intervalMs,
7531
+ maxPolls,
7532
+ timeoutMs,
7533
+ includeResults,
7534
+ ...rest
7535
+ } = params;
7536
+ return {
7537
+ ...rest,
7538
+ op_id: rest.op_id ?? opId,
7539
+ cursor: rest.cursor ?? nextCursor,
7540
+ include_failed_runs: rest.include_failed_runs ?? includeFailedRuns,
7541
+ interval_ms: rest.interval_ms ?? intervalMs,
7542
+ max_polls: rest.max_polls ?? maxPolls,
7543
+ timeout_ms: rest.timeout_ms ?? timeoutMs,
7544
+ include_results: rest.include_results ?? includeResults
7545
+ };
7546
+ }
5654
7547
  function buildOpsStatusBody(params) {
5655
7548
  const { opId, ...rest } = params;
5656
7549
  return {
@@ -5667,6 +7560,50 @@ function buildOpsResultsBody(params) {
5667
7560
  include_failed_runs: rest.include_failed_runs ?? includeFailedRuns
5668
7561
  };
5669
7562
  }
7563
+ function buildOpsWaitForResultsOptions(params) {
7564
+ const {
7565
+ opId,
7566
+ nextCursor,
7567
+ includeFailedRuns,
7568
+ intervalMs,
7569
+ maxPolls,
7570
+ timeoutMs,
7571
+ includeResults,
7572
+ ...rest
7573
+ } = params;
7574
+ return {
7575
+ ...rest,
7576
+ op_id: rest.op_id ?? opId,
7577
+ cursor: rest.cursor ?? nextCursor,
7578
+ include_failed_runs: rest.include_failed_runs ?? includeFailedRuns,
7579
+ interval_ms: rest.interval_ms ?? intervalMs,
7580
+ max_polls: rest.max_polls ?? maxPolls,
7581
+ timeout_ms: rest.timeout_ms ?? timeoutMs,
7582
+ include_results: rest.include_results ?? includeResults
7583
+ };
7584
+ }
7585
+ function buildOpsWaitBody(params) {
7586
+ const {
7587
+ opId,
7588
+ nextCursor,
7589
+ includeFailedRuns,
7590
+ intervalMs,
7591
+ maxPolls,
7592
+ timeoutMs,
7593
+ includeResults,
7594
+ ...rest
7595
+ } = params;
7596
+ return {
7597
+ ...rest,
7598
+ op_id: rest.op_id ?? opId,
7599
+ cursor: rest.cursor ?? nextCursor,
7600
+ include_failed_runs: rest.include_failed_runs ?? includeFailedRuns,
7601
+ interval_ms: rest.interval_ms ?? intervalMs,
7602
+ max_polls: rest.max_polls ?? maxPolls,
7603
+ timeout_ms: rest.timeout_ms ?? timeoutMs,
7604
+ include_results: rest.include_results ?? includeResults
7605
+ };
7606
+ }
5670
7607
  function buildOpsDebugOptions(params) {
5671
7608
  const {
5672
7609
  opId,
@@ -5744,6 +7681,8 @@ function normalizeOpsSinkRequest(sink) {
5744
7681
  nangoAction,
5745
7682
  proxyPath,
5746
7683
  nangoProxyPath,
7684
+ method,
7685
+ httpMethod,
5747
7686
  fieldMap,
5748
7687
  requiredFields,
5749
7688
  writeConfirmed,
@@ -5762,6 +7701,7 @@ function normalizeOpsSinkRequest(sink) {
5762
7701
  const nango_action = rest.nango_action ?? nangoAction;
5763
7702
  const proxy_path = rest.proxy_path ?? proxyPath;
5764
7703
  const nango_proxy_path = rest.nango_proxy_path ?? nangoProxyPath;
7704
+ const http_method = rest.http_method ?? httpMethod;
5765
7705
  const field_map = rest.field_map ?? fieldMap;
5766
7706
  const required_fields = rest.required_fields ?? requiredFields;
5767
7707
  const write_confirmed = rest.write_confirmed ?? writeConfirmed;
@@ -5776,6 +7716,9 @@ function normalizeOpsSinkRequest(sink) {
5776
7716
  if (nango_action !== void 0 && normalizedConfig.nango_action === void 0) normalizedConfig.nango_action = nango_action;
5777
7717
  if (proxy_path !== void 0 && normalizedConfig.proxy_path === void 0) normalizedConfig.proxy_path = proxy_path;
5778
7718
  if (nango_proxy_path !== void 0 && normalizedConfig.nango_proxy_path === void 0) normalizedConfig.nango_proxy_path = nango_proxy_path;
7719
+ if (method !== void 0 && normalizedConfig.method === void 0) normalizedConfig.method = method;
7720
+ if (http_method !== void 0 && normalizedConfig.http_method === void 0) normalizedConfig.http_method = http_method;
7721
+ if (normalizedConfig.method === void 0 && http_method !== void 0) normalizedConfig.method = http_method;
5779
7722
  if (field_map !== void 0 && normalizedConfig.field_map === void 0) normalizedConfig.field_map = field_map;
5780
7723
  if (required_fields !== void 0 && normalizedConfig.required_fields === void 0) normalizedConfig.required_fields = required_fields;
5781
7724
  if (write_confirmed !== void 0 && normalizedConfig.write_confirmed === void 0) normalizedConfig.write_confirmed = write_confirmed;
@@ -5795,6 +7738,8 @@ function normalizeOpsSinkRequest(sink) {
5795
7738
  nango_action,
5796
7739
  proxy_path,
5797
7740
  nango_proxy_path,
7741
+ method,
7742
+ http_method,
5798
7743
  field_map,
5799
7744
  required_fields,
5800
7745
  write_confirmed,
@@ -5802,6 +7747,10 @@ function normalizeOpsSinkRequest(sink) {
5802
7747
  config: normalizedConfig
5803
7748
  };
5804
7749
  }
7750
+ function normalizeOpsDestinationRequest(destination) {
7751
+ if (!destination || typeof destination !== "object" || Array.isArray(destination)) return destination;
7752
+ return normalizeOpsSinkRequest(destination);
7753
+ }
5805
7754
  function normalizeOpsSinkConfigRequest(config) {
5806
7755
  const out = { ...config ?? {} };
5807
7756
  if (out.connection_id === void 0 && out.connectionId !== void 0) out.connection_id = out.connectionId;
@@ -5815,6 +7764,8 @@ function normalizeOpsSinkConfigRequest(config) {
5815
7764
  if (out.nango_action === void 0 && out.nangoAction !== void 0) out.nango_action = out.nangoAction;
5816
7765
  if (out.proxy_path === void 0 && out.proxyPath !== void 0) out.proxy_path = out.proxyPath;
5817
7766
  if (out.nango_proxy_path === void 0 && out.nangoProxyPath !== void 0) out.nango_proxy_path = out.nangoProxyPath;
7767
+ if (out.http_method === void 0 && out.httpMethod !== void 0) out.http_method = out.httpMethod;
7768
+ if (out.method === void 0 && out.http_method !== void 0) out.method = out.http_method;
5818
7769
  if (out.field_map === void 0 && out.fieldMap !== void 0) out.field_map = out.fieldMap;
5819
7770
  if (out.required_fields === void 0 && out.requiredFields !== void 0) out.required_fields = out.requiredFields;
5820
7771
  if (out.write_confirmed === void 0 && out.writeConfirmed !== void 0) out.write_confirmed = out.writeConfirmed;
@@ -5888,7 +7839,6 @@ function toSnakeConfig(params) {
5888
7839
  return {
5889
7840
  system_prompt: params.systemPrompt || params.system_prompt || "You are a senior business research analyst. Use multi-model evidence carefully and return concise structured enrichment.",
5890
7841
  user_template: params.userTemplate || params.user_template || params.prompt,
5891
- model: params.model,
5892
7842
  temperature: params.temperature,
5893
7843
  max_concurrency: params.maxConcurrency || params.max_concurrency,
5894
7844
  max_tokens: params.maxTokens || params.max_tokens,
@@ -5912,13 +7862,10 @@ var Ai = class {
5912
7862
  /**
5913
7863
  * Run Custom AI Enrichment - Multi Model.
5914
7864
  *
5915
- * Uses OpenRouter Fusion for multi-model analysis with web search/fetch enabled
5916
- * inside Fusion, then synthesizes the requested structured output fields.
7865
+ * Uses the hosted default model with bounded synthesis, then returns the
7866
+ * requested structured output fields.
5917
7867
  */
5918
7868
  async multiModel(params) {
5919
- if (!params.model) {
5920
- throw new Error('model is required. Use an OpenRouter id such as "anthropic/claude-sonnet-4" or "google/gemini-2.5-flash".');
5921
- }
5922
7869
  if (!params.prompt && !params.userTemplate && !params.user_template) {
5923
7870
  throw new Error("prompt or userTemplate is required.");
5924
7871
  }
@@ -6135,6 +8082,10 @@ var GtmKernel = class {
6135
8082
  days: options.days,
6136
8083
  network_days: options.networkDays,
6137
8084
  min_sample_size: options.minSampleSize,
8085
+ holdout_min_sample_size: options.holdoutMinSampleSize,
8086
+ predictive_min_labeled_outcomes: options.predictiveMinLabeledOutcomes,
8087
+ predictive_min_positive_outcomes: options.predictiveMinPositiveOutcomes,
8088
+ predictive_min_memory_sample_size: options.predictiveMinMemorySampleSize,
6138
8089
  min_workspace_count: options.minWorkspaceCount,
6139
8090
  min_privacy_k: options.minPrivacyK,
6140
8091
  include_memory: options.includeMemory,
@@ -6201,9 +8152,11 @@ var GtmKernel = class {
6201
8152
  write_routine_outcomes: input.writeRoutineOutcomes
6202
8153
  });
6203
8154
  }
6204
- /** Start a background Instantly webhook-events pull and route results into the GTM feedback loop. */
8155
+ /** Start a background Instantly webhook-events or email-history pull and route results into the GTM feedback loop. */
6205
8156
  async pullInstantlyFeedback(input) {
6206
8157
  return this.callMcp("gtm_instantly_feedback_pull", {
8158
+ source: input.source,
8159
+ instantly_profile: input.instantlyProfile,
6207
8160
  campaign_id: input.campaignId,
6208
8161
  campaign_build_id: input.campaignBuildId,
6209
8162
  provider_link_id: input.providerLinkId,
@@ -6215,6 +8168,9 @@ var GtmKernel = class {
6215
8168
  to: input.to,
6216
8169
  limit: input.limit,
6217
8170
  max_pages: input.maxPages,
8171
+ email_type: input.emailType,
8172
+ mode: input.mode,
8173
+ sort_order: input.sortOrder,
6218
8174
  create_memory: input.createMemory,
6219
8175
  write_routine_outcomes: input.writeRoutineOutcomes,
6220
8176
  dry_run: input.dryRun,
@@ -6285,6 +8241,16 @@ var GtmKernel = class {
6285
8241
  min_privacy_k: input.minPrivacyK
6286
8242
  });
6287
8243
  }
8244
+ /** Queue a dry-run-first bridge from imported historical feedback to attribution-only Campaign Build surrogates. */
8245
+ async runHistoricalCampaignBuildBridge(input = {}) {
8246
+ return this.callMcp("gtm_historical_campaign_build_bridge_run", {
8247
+ dry_run: input.dryRun,
8248
+ write_approved: input.writeApproved,
8249
+ max_events: input.maxEvents,
8250
+ max_campaigns: input.maxCampaigns,
8251
+ min_feedback_events: input.minFeedbackEvents
8252
+ });
8253
+ }
6288
8254
  /** Prepare a provider-agnostic feedback webhook URL for Smartlead, HeyReach, Airbyte, or custom sender events. */
6289
8255
  async prepareFeedbackWebhook(input) {
6290
8256
  return this.callMcp("gtm_feedback_webhook_prepare", {
@@ -6344,6 +8310,7 @@ var GtmKernel = class {
6344
8310
  days: input.days,
6345
8311
  network_days: input.networkDays,
6346
8312
  min_sample_size: input.minSampleSize,
8313
+ holdout_min_sample_size: input.holdoutMinSampleSize,
6347
8314
  min_workspace_count: input.minWorkspaceCount,
6348
8315
  min_privacy_k: input.minPrivacyK,
6349
8316
  include_memory: input.includeMemory,
@@ -6364,6 +8331,7 @@ var GtmKernel = class {
6364
8331
  days: input.days,
6365
8332
  network_days: input.networkDays,
6366
8333
  min_sample_size: input.minSampleSize,
8334
+ holdout_min_sample_size: input.holdoutMinSampleSize,
6367
8335
  min_workspace_count: input.minWorkspaceCount,
6368
8336
  min_privacy_k: input.minPrivacyK,
6369
8337
  include_memory: input.includeMemory,
@@ -6488,6 +8456,29 @@ var GtmKernel = class {
6488
8456
  include_details: options.includeDetails
6489
8457
  });
6490
8458
  }
8459
+ /** Search the Agency Autopilot safe-action catalog for agency-style lead lists, Clay tables, and GTM motions. */
8460
+ async agencyAutopilotCapabilityCatalog(options = {}) {
8461
+ return this.callMcp("gtm_agency_autopilot_capability_catalog", {
8462
+ query: options.query,
8463
+ family: options.family,
8464
+ phase: options.phase,
8465
+ account_label: options.accountLabel,
8466
+ workspace_label: options.workspaceLabel,
8467
+ target_count: options.targetCount,
8468
+ limit: options.limit,
8469
+ include_agent_prompts: options.includeAgentPrompts
8470
+ });
8471
+ }
8472
+ /** Search agency operating playbooks Agency Autopilot can use for lead lists, Clay tables, GTM motions, and proof gates. */
8473
+ async agencyAutopilotPlaybookCatalog(options = {}) {
8474
+ return this.callMcp("gtm_agency_autopilot_playbook_catalog", {
8475
+ query: options.query,
8476
+ playbook_slug: options.playbookSlug,
8477
+ outcome_type: options.outcomeType,
8478
+ limit: options.limit,
8479
+ include_required_fields: options.includeRequiredFields
8480
+ });
8481
+ }
6491
8482
  /** Build the read-only Memory, Brain, risk, and provider-route runbook needed before planning a campaign. */
6492
8483
  async campaignStartContext(input = {}) {
6493
8484
  return this.callMcp("gtm_campaign_start_context", {
@@ -6846,6 +8837,35 @@ var GtmKernel = class {
6846
8837
  user_display_name: input.userDisplayName
6847
8838
  });
6848
8839
  }
8840
+ /** Prepare the full Nango provider search, connect, pull/export, Ops, and campaign-route flow. */
8841
+ async prepareNangoIntegrationFlow(input = {}) {
8842
+ return this.callMcp("nango_integration_flow_prepare", {
8843
+ query: input.query,
8844
+ search: input.search,
8845
+ provider_id: input.providerId,
8846
+ provider: input.provider,
8847
+ integration_id: input.integrationId,
8848
+ provider_config_key: input.providerConfigKey,
8849
+ workspace_connection_id: input.workspaceConnectionId,
8850
+ connection_id: input.connectionId,
8851
+ nango_connection_id: input.nangoConnectionId,
8852
+ intent: input.intent,
8853
+ action_name: input.actionName,
8854
+ tool_name: input.toolName,
8855
+ proxy_path: input.proxyPath,
8856
+ method: input.method,
8857
+ input: input.input,
8858
+ cadence: input.cadence,
8859
+ field_map: input.fieldMap,
8860
+ required_fields: input.requiredFields,
8861
+ confirm_spend: input.confirmSpend,
8862
+ write_confirmed: input.writeConfirmed,
8863
+ layer: input.layer,
8864
+ campaign_id: input.campaignId,
8865
+ limit: input.limit,
8866
+ include_provider_catalog: input.includeProviderCatalog
8867
+ });
8868
+ }
6849
8869
  /** List Nango-backed action tools for a workspace connection without executing them. */
6850
8870
  async listNangoTools(options = {}) {
6851
8871
  return this.callMcp("nango_mcp_tools_list", {
@@ -6868,6 +8888,12 @@ var GtmKernel = class {
6868
8888
  nango_connection_id: input.nangoConnectionId,
6869
8889
  action_name: input.actionName,
6870
8890
  tool_name: input.toolName,
8891
+ delivery_mode: input.deliveryMode,
8892
+ proxy_path: input.proxyPath,
8893
+ nango_proxy_path: input.nangoProxyPath,
8894
+ method: input.method,
8895
+ http_method: input.httpMethod,
8896
+ proxy_headers: input.proxyHeaders,
6871
8897
  input: input.input,
6872
8898
  async: input.async,
6873
8899
  max_retries: input.maxRetries,
@@ -7017,6 +9043,7 @@ function compact2(value) {
7017
9043
  }
7018
9044
 
7019
9045
  // src/index.ts
9046
+ var MCP_TOOL_CACHE_TTL_MS = 3e4;
7020
9047
  function inferMcpToolCategory(toolName) {
7021
9048
  if (typeof toolName !== "string" || !toolName) return void 0;
7022
9049
  if (toolName.startsWith("ops_") || toolName === "get_ops_readiness" || toolName === "get_gtm_ops_readiness" || toolName.startsWith("gtm_") || [
@@ -7148,6 +9175,21 @@ var Signaliz = class {
7148
9175
  }
7149
9176
  /** List all available MCP tools */
7150
9177
  async listTools() {
9178
+ const now = Date.now();
9179
+ if (this.toolListCache && this.toolListCache.expiresAt > now) {
9180
+ return this.toolListCache.promise;
9181
+ }
9182
+ const promise = this.fetchTools().catch((error) => {
9183
+ if (this.toolListCache?.promise === promise) this.toolListCache = void 0;
9184
+ throw error;
9185
+ });
9186
+ this.toolListCache = {
9187
+ expiresAt: now + MCP_TOOL_CACHE_TTL_MS,
9188
+ promise
9189
+ };
9190
+ return promise;
9191
+ }
9192
+ async fetchTools() {
7151
9193
  try {
7152
9194
  const manifest = await this.client.mcp("tools/call", {
7153
9195
  name: "get_tool_manifest",