@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.
package/dist/index.js CHANGED
@@ -265,25 +265,31 @@ var HttpClient = class {
265
265
  async getToken() {
266
266
  if (this.apiKey) return this.apiKey;
267
267
  if (this.accessToken) return this.accessToken;
268
- const res = await fetch("https://api.signaliz.com/token", {
269
- method: "POST",
270
- headers: { "Content-Type": "application/json" },
271
- body: JSON.stringify({
272
- grant_type: "client_credentials",
273
- client_id: this.clientId,
274
- client_secret: this.clientSecret
275
- })
276
- });
277
- if (!res.ok) {
278
- throw new SignalizError({
279
- code: "AUTH_FAILED",
280
- message: "Failed to obtain access token",
281
- errorType: "auth_expired"
268
+ if (this.accessTokenPromise) return this.accessTokenPromise;
269
+ this.accessTokenPromise = (async () => {
270
+ const res = await fetch("https://api.signaliz.com/token", {
271
+ method: "POST",
272
+ headers: { "Content-Type": "application/json" },
273
+ body: JSON.stringify({
274
+ grant_type: "client_credentials",
275
+ client_id: this.clientId,
276
+ client_secret: this.clientSecret
277
+ })
282
278
  });
283
- }
284
- const data = await res.json();
285
- this.accessToken = data.access_token;
286
- return this.accessToken;
279
+ if (!res.ok) {
280
+ throw new SignalizError({
281
+ code: "AUTH_FAILED",
282
+ message: "Failed to obtain access token",
283
+ errorType: "auth_expired"
284
+ });
285
+ }
286
+ const data = await res.json();
287
+ this.accessToken = data.access_token;
288
+ return this.accessToken;
289
+ })().finally(() => {
290
+ this.accessTokenPromise = void 0;
291
+ });
292
+ return this.accessTokenPromise;
287
293
  }
288
294
  };
289
295
  function sleep(ms) {
@@ -532,7 +538,6 @@ var Signals = class {
532
538
  if (params.online !== void 0) args.online = params.online;
533
539
  if (params.targetSignalCount) args.target_signal_count = params.targetSignalCount;
534
540
  if (params.lookbackDays) args.lookback_days = params.lookbackDays;
535
- if (params.model) args.model = params.model;
536
541
  if (params.enableDeepSearch) args.enable_deep_search = params.enableDeepSearch;
537
542
  if (params.enableOutreachIntelligence) args.enable_outreach_intelligence = params.enableOutreachIntelligence;
538
543
  if (params.enablePredictiveIntelligence) args.enable_predictive_intelligence = params.enablePredictiveIntelligence;
@@ -827,6 +832,10 @@ var Campaigns = class {
827
832
  async artifacts(campaignBuildId) {
828
833
  return this.listCampaignBuildArtifacts(campaignBuildId);
829
834
  }
835
+ /** Generate fresh signed URLs for a downloadable campaign artifact. */
836
+ async downloadArtifact(campaignBuildId, options) {
837
+ return this.downloadCampaignArtifact(campaignBuildId, options);
838
+ }
830
839
  /** Cancel a queued or running build. */
831
840
  async cancel(campaignBuildId, reason) {
832
841
  return this.cancelCampaignBuild(campaignBuildId, reason);
@@ -871,13 +880,21 @@ var Campaigns = class {
871
880
  dryRun: isDryRun,
872
881
  requestedTargetCount: data.requested_target_count,
873
882
  plannedTargetCount: data.planned_target_count,
883
+ acquisitionMode: data.acquisition_mode,
884
+ acquisitionSource: data.acquisition_source,
885
+ cacheReusePolicy: data.cache_reuse_policy,
874
886
  estimatedCredits: data.estimated_credits,
875
887
  estimatedDurationSeconds: data.estimated_duration_seconds,
876
888
  targetLimitApplied: data.target_limit_applied,
877
889
  targetLimitReason: data.target_limit_reason,
878
890
  maxSupportedTargetCount: data.max_supported_target_count,
879
891
  brainContext: data.brain_context ?? {},
880
- providerRoute: mapProviderRoute(data)
892
+ learningHoldout: data.learning_holdout ?? {},
893
+ providerRoute: mapProviderRoute(data),
894
+ effectiveSourceRouting: recordOrNull(data.effective_source_routing ?? data.source_routing),
895
+ sourcePlan: recordOrNull(data.source_plan ?? data.plan?.source_plan),
896
+ sourceRoutes: recordArray(data.source_routes ?? data.source_plan?.routes ?? data.plan?.source_plan?.routes),
897
+ sourceShards: recordArray(data.source_shards ?? data.source_plan?.shards ?? data.plan?.source_plan?.shards)
881
898
  };
882
899
  }
883
900
  async getCampaignBuildStatus(campaignBuildId) {
@@ -892,14 +909,24 @@ var Campaigns = class {
892
909
  });
893
910
  return (data.artifacts ?? []).map(mapArtifact);
894
911
  }
912
+ async downloadCampaignArtifact(campaignBuildId, options) {
913
+ const args = { campaign_build_id: campaignBuildId };
914
+ if (options?.format) args.format = options.format;
915
+ const data = await this.callMcp("download_campaign_artifact", args);
916
+ return mapArtifactDownloadResult(data);
917
+ }
895
918
  async getCampaignBuildRows(campaignBuildId, options) {
896
919
  const args = { campaign_build_id: campaignBuildId };
897
920
  if (options?.limit) args.page_size = options.limit;
898
921
  if (options?.cursor) args.cursor = options.cursor;
922
+ if (options?.includeData !== void 0) args.include_data = options.includeData;
923
+ if (options?.includeRaw !== void 0) args.include_raw = options.includeRaw;
899
924
  const filters = {};
900
925
  if (options?.segment) filters.segment = options.segment;
901
926
  if (options?.rowStatus) filters.row_status = options.rowStatus;
902
927
  if (options?.qualified !== void 0) filters.qualified = options.qualified;
928
+ if (options?.cached !== void 0) filters.cached = options.cached;
929
+ if (options?.exportReady !== void 0) filters.export_ready = options.exportReady;
903
930
  if (Object.keys(filters).length > 0) args.filters = filters;
904
931
  const data = await this.callMcp("get_campaign_build_rows", args);
905
932
  return {
@@ -924,6 +951,7 @@ var Campaigns = class {
924
951
  };
925
952
  if (options?.destinationId) args.destination_id = options.destinationId;
926
953
  if (options?.destinationConfig) args.destination_config = options.destinationConfig;
954
+ if (options?.allowPartialDelivery === true) args.allow_partial_delivery = true;
927
955
  const data = await this.callMcp("approve_campaign_delivery", args);
928
956
  return {
929
957
  campaignBuildId: data.campaign_build_id,
@@ -932,7 +960,10 @@ var Campaigns = class {
932
960
  message: data.message ?? "",
933
961
  deliveryId: data.delivery_id,
934
962
  deliveryIds: data.delivery_ids,
935
- approvedCount: data.approved_count
963
+ approvedCount: data.approved_count,
964
+ partialDelivery: data.partial_delivery,
965
+ effectiveTargetCount: data.effective_target_count,
966
+ requestedTargetCount: data.requested_target_count
936
967
  };
937
968
  }
938
969
  async cancelCampaignBuild(campaignBuildId, reason) {
@@ -985,17 +1016,50 @@ function mapStatus(data) {
985
1016
  warnings: diagnosticMessages(data.warnings),
986
1017
  errors: diagnosticMessages(data.errors),
987
1018
  artifactCount: data.artifact_count ?? 0,
1019
+ artifactDownloads: Array.isArray(data.artifact_downloads) ? data.artifact_downloads.map(mapArtifactDownload) : [],
1020
+ customerRowCounts: mapCustomerRowCounts(data.customer_row_counts),
1021
+ phaseAttemptTotals: recordOrNull(data.phase_attempt_totals) ?? void 0,
988
1022
  providerRoute: mapProviderRoute(data),
989
1023
  triggerRunId: typeof data.trigger_run_id === "string" ? data.trigger_run_id : null,
990
1024
  staleRunningPhase: data.stale_running_phase === true,
991
1025
  phaseAgeSeconds: numberOrNull(data.phase_age_seconds),
992
1026
  diagnostics: recordOrNull(data.diagnostics) ?? {},
993
1027
  nextAction: formatNextAction(data.next_action),
1028
+ approvalAction: formatNextAction(data.approval_action),
994
1029
  createdAt: data.created_at,
995
1030
  updatedAt: data.updated_at,
996
1031
  completedAt: data.completed_at
997
1032
  };
998
1033
  }
1034
+ function mapCustomerRowCounts(value) {
1035
+ const record = recordOrNull(value);
1036
+ if (!record) return void 0;
1037
+ return {
1038
+ requestedTarget: numberOrNull(record.requested_target),
1039
+ acquiredRows: numberOrNull(record.acquired_rows) ?? void 0,
1040
+ acceptedRows: numberOrNull(record.accepted_rows) ?? void 0,
1041
+ usableRows: numberOrNull(record.usable_rows) ?? void 0,
1042
+ copiedRows: numberOrNull(record.copied_rows) ?? void 0,
1043
+ approvalReadyRows: numberOrNull(record.approval_ready_rows) ?? void 0,
1044
+ qaReadyRows: numberOrNull(record.qa_ready_rows) ?? void 0,
1045
+ deliveredRows: numberOrNull(record.delivered_rows) ?? void 0,
1046
+ disqualifiedRows: numberOrNull(record.disqualified_rows) ?? void 0,
1047
+ reviewableRows: numberOrNull(record.reviewable_rows) ?? void 0,
1048
+ rowFailures: numberOrNull(record.row_failures) ?? void 0,
1049
+ acceptedShortfall: numberOrNull(record.accepted_shortfall),
1050
+ usableShortfall: numberOrNull(record.usable_shortfall),
1051
+ qaReadyShortfall: numberOrNull(record.qa_ready_shortfall),
1052
+ deliveryShortfall: numberOrNull(record.delivery_shortfall),
1053
+ fillRatio: numberOrNull(record.fill_ratio),
1054
+ qaReadyFillRatio: numberOrNull(record.qa_ready_fill_ratio),
1055
+ deliveredFillRatio: numberOrNull(record.delivered_fill_ratio),
1056
+ terminalReason: typeof record.terminal_reason === "string" ? record.terminal_reason : null,
1057
+ acceptedShortfallReason: typeof record.accepted_shortfall_reason === "string" ? record.accepted_shortfall_reason : null,
1058
+ usableShortfallReason: typeof record.usable_shortfall_reason === "string" ? record.usable_shortfall_reason : null,
1059
+ qaReadyShortfallReason: typeof record.qa_ready_shortfall_reason === "string" ? record.qa_ready_shortfall_reason : null,
1060
+ deliveryShortfallReason: typeof record.delivery_shortfall_reason === "string" ? record.delivery_shortfall_reason : null
1061
+ };
1062
+ }
999
1063
  function mapProviderRoute(data) {
1000
1064
  const brainContext = recordOrNull(data?.brain_context);
1001
1065
  const plan = recordOrNull(data?.provider_route_plan) ?? recordOrNull(brainContext?.provider_route_plan);
@@ -1022,6 +1086,9 @@ function recordOrNull(value) {
1022
1086
  function stringArray(value) {
1023
1087
  return Array.isArray(value) ? value.map((item) => String(item || "").trim()).filter(Boolean) : [];
1024
1088
  }
1089
+ function recordArray(value) {
1090
+ return Array.isArray(value) ? value.map(recordOrNull).filter((item) => Boolean(item)) : [];
1091
+ }
1025
1092
  function diagnosticMessages(value) {
1026
1093
  if (!Array.isArray(value)) return [];
1027
1094
  return value.map((item) => {
@@ -1073,13 +1140,56 @@ function mapArtifact(a) {
1073
1140
  artifactType: a.artifact_type,
1074
1141
  destination: a.destination,
1075
1142
  storagePath: a.storage_path,
1143
+ format: a.format ?? null,
1076
1144
  signedUrl: a.signed_url,
1145
+ downloadUrl: a.download_url ?? null,
1146
+ manifestUrl: a.manifest_url ?? null,
1147
+ expiresAt: a.expires_at ?? null,
1077
1148
  rowCount: a.row_count ?? 0,
1078
1149
  checksum: a.checksum,
1079
1150
  metadata: a.metadata ?? {},
1080
1151
  createdAt: a.created_at
1081
1152
  };
1082
1153
  }
1154
+ function mapArtifactDownload(data) {
1155
+ return {
1156
+ id: data.id,
1157
+ artifactType: data.artifact_type ?? null,
1158
+ format: data.format ?? null,
1159
+ rowCount: data.row_count ?? 0,
1160
+ signedUrl: data.signed_url ?? null,
1161
+ downloadUrl: data.download_url ?? null,
1162
+ manifestUrl: data.manifest_url ?? null,
1163
+ expiresAt: data.expires_at ?? null
1164
+ };
1165
+ }
1166
+ function mapArtifactDownloadResult(data) {
1167
+ return {
1168
+ campaignBuildId: data.campaign_build_id,
1169
+ artifactId: data.artifact_id,
1170
+ format: data.format,
1171
+ rowCount: data.row_count ?? 0,
1172
+ byteSize: numberOrNull(data.byte_size),
1173
+ partCount: data.part_count ?? 0,
1174
+ checksum: data.checksum ?? null,
1175
+ signedUrl: data.signed_url ?? null,
1176
+ downloadUrl: data.download_url ?? null,
1177
+ manifestUrl: data.manifest_url ?? null,
1178
+ expiresAt: data.expires_at ?? null,
1179
+ parts: Array.isArray(data.parts) ? data.parts.map(mapArtifactDownloadPart) : [],
1180
+ downloadCommands: recordOrNull(data.download_commands) ?? {},
1181
+ expiresInSeconds: numberOrNull(data.expires_in_seconds)
1182
+ };
1183
+ }
1184
+ function mapArtifactDownloadPart(data) {
1185
+ return {
1186
+ partIndex: data.part_index ?? 0,
1187
+ rowCount: data.row_count ?? 0,
1188
+ byteSize: numberOrNull(data.byte_size),
1189
+ checksum: data.checksum ?? null,
1190
+ signedUrl: data.signed_url ?? null
1191
+ };
1192
+ }
1083
1193
  function mapScope(data) {
1084
1194
  return {
1085
1195
  campaignScopeId: data.campaign_scope_id,
@@ -1133,8 +1243,30 @@ function buildArgs(request) {
1133
1243
  if (request.allowDownscale !== void 0) args.allow_downscale = request.allowDownscale;
1134
1244
  if (request.confirmSpend !== void 0) args.confirm_spend = request.confirmSpend;
1135
1245
  if (request.dedupKeys) args.dedup_keys = request.dedupKeys;
1246
+ if (request.acquisitionMode) args.acquisition_mode = request.acquisitionMode;
1247
+ if (request.cacheReusePolicy) {
1248
+ args.cache_reuse_policy = {
1249
+ allow_verified_cache: request.cacheReusePolicy.allowVerifiedCache,
1250
+ suppress_prior_campaign_ids: request.cacheReusePolicy.suppressPriorCampaignIds,
1251
+ suppress_prior_list_ids: request.cacheReusePolicy.suppressPriorListIds,
1252
+ uniqueness: request.cacheReusePolicy.uniqueness,
1253
+ require_usage_metadata: request.cacheReusePolicy.requireUsageMetadata,
1254
+ verified_cache_ttl_days: request.cacheReusePolicy.verifiedCacheTtlDays
1255
+ };
1256
+ }
1136
1257
  if (request.brainPreflight) args.brain_preflight = request.brainPreflight;
1137
1258
  if (request.brainDefaults) args.brain_defaults = request.brainDefaults;
1259
+ if (request.learningHoldout) {
1260
+ args.learning_holdout = {
1261
+ enabled: request.learningHoldout.enabled,
1262
+ control_percentage: request.learningHoldout.controlPercentage,
1263
+ min_control_size: request.learningHoldout.minControlSize,
1264
+ experiment_key: request.learningHoldout.experimentKey,
1265
+ source_campaign_id: request.learningHoldout.sourceCampaignId,
1266
+ primary_metric: request.learningHoldout.primaryMetric
1267
+ };
1268
+ }
1269
+ if (request.campaignStrategyContext) args.campaign_strategy_context = request.campaignStrategyContext;
1138
1270
  if (request.deliveryRisk) args.delivery_risk = request.deliveryRisk;
1139
1271
  if (request.icp) {
1140
1272
  args.icp = {
@@ -1176,7 +1308,17 @@ function buildArgs(request) {
1176
1308
  max_body_words: request.copy.maxBodyWords,
1177
1309
  sender_context: request.copy.senderContext,
1178
1310
  offer_context: request.copy.offerContext,
1179
- model: request.copy.model
1311
+ model: request.copy.model,
1312
+ style_source: request.copy.styleSource ? {
1313
+ sample_text: request.copy.styleSource.sampleText,
1314
+ campaign_ids: request.copy.styleSource.campaignIds,
1315
+ artifact_ids: request.copy.styleSource.artifactIds
1316
+ } : void 0,
1317
+ sequence_steps: request.copy.sequenceSteps,
1318
+ copy_schema: request.copy.copySchema,
1319
+ banned_phrases: request.copy.bannedPhrases,
1320
+ approval_required: request.copy.approvalRequired,
1321
+ quality_tier: request.copy.qualityTier
1180
1322
  };
1181
1323
  }
1182
1324
  if (request.delivery) {
@@ -1239,6 +1381,8 @@ var DEFAULT_SIGNALIZ_CAMPAIGN_BUILDER_DEFAULTS = {
1239
1381
  }
1240
1382
  };
1241
1383
  var DEFAULT_CAMPAIGN_BUILDER_BUILT_INS = [
1384
+ "company_discovery",
1385
+ "people_discovery",
1242
1386
  "lead_generation",
1243
1387
  "email_finding",
1244
1388
  "email_verification",
@@ -1247,6 +1391,8 @@ var DEFAULT_CAMPAIGN_BUILDER_BUILT_INS = [
1247
1391
  var CAMPAIGN_BUILDER_BUILT_IN_ROUTE_IDS = {
1248
1392
  lead_generation: "signaliz-lead-generation",
1249
1393
  local_leads: "signaliz-local-leads",
1394
+ company_discovery: "signaliz-company-discovery",
1395
+ people_discovery: "signaliz-people-discovery",
1250
1396
  email_finding: "signaliz-email-finding",
1251
1397
  email_verification: "signaliz-email-verification",
1252
1398
  signals: "signaliz-signals"
@@ -1264,8 +1410,8 @@ var CAMPAIGN_BUILDER_OPERATING_PLAYBOOKS = [
1264
1410
  slug: "cache-first-large-list",
1265
1411
  label: "Cache-First Large List",
1266
1412
  whenToUse: [
1267
- "The target count is high and reusable workspace lead or cache coverage may exist.",
1268
- "The deliverable is a verified list or top-up rather than a deeply custom one-off workflow."
1413
+ "The operator explicitly references a previous campaign, prior list, seed list, or suppression baseline.",
1414
+ "The deliverable is a top-up, continuation, or backfill where prior campaign context should guide reuse."
1269
1415
  ],
1270
1416
  sequence: [
1271
1417
  "Inventory reusable cache before paid sourcing.",
@@ -1471,7 +1617,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1471
1617
  ],
1472
1618
  requireVerifiedEmail: true
1473
1619
  },
1474
- builtIns: ["lead_generation", "email_finding", "email_verification", "signals"],
1620
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "email_finding", "email_verification", "signals"],
1475
1621
  preferredProviders: ["signaliz_native"],
1476
1622
  memoryQueries: [{
1477
1623
  query: "industrial OT net-new prior email suppression verified manufacturing embedded systems field service cyber resilience business continuity",
@@ -1545,7 +1691,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1545
1691
  ],
1546
1692
  requireVerifiedEmail: true
1547
1693
  },
1548
- builtIns: ["lead_generation", "local_leads", "email_finding", "email_verification", "signals"],
1694
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "local_leads", "email_finding", "email_verification", "signals"],
1549
1695
  preferredProviders: ["signaliz_native", "octave", "clay_webhook", "apify"],
1550
1696
  memoryQueries: [{
1551
1697
  query: "mature non-medical home care agencies billable hours franchise caregiver recruiting proof gate qualification export approval",
@@ -1611,7 +1757,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1611
1757
  exclusions: ["enterprise holding company", "staffing", "recruiting", "software-only vendor", "freelancer-only profile"],
1612
1758
  requireVerifiedEmail: true
1613
1759
  },
1614
- builtIns: ["lead_generation", "email_finding", "email_verification", "signals"],
1760
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "email_finding", "email_verification", "signals"],
1615
1761
  preferredProviders: ["signaliz_native", "octave"],
1616
1762
  memoryQueries: [{
1617
1763
  query: "agency founder owner CEO marketing services suppression domain pool email verification final approval gate",
@@ -1693,7 +1839,7 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
1693
1839
  ],
1694
1840
  requireVerifiedEmail: true
1695
1841
  },
1696
- builtIns: ["lead_generation", "email_finding", "email_verification", "signals"],
1842
+ builtIns: ["company_discovery", "people_discovery", "lead_generation", "email_finding", "email_verification", "signals"],
1697
1843
  preferredProviders: ["signaliz_native", "octave"],
1698
1844
  memoryQueries: [{
1699
1845
  query: "cloud hosting infrastructure VMware Broadcom qualification verified email persona trigger SKU mapping",
@@ -1726,25 +1872,21 @@ var CampaignBuilderAgent = class {
1726
1872
  async createPlan(request, options = {}) {
1727
1873
  const resolvedRequest = applyCampaignBuilderStrategyTemplate(request);
1728
1874
  const warnings = [];
1729
- const workspaceContext = resolvedRequest.workspaceContext ?? await this.getWorkspaceContext(warnings);
1730
- const scopeResult = options.scopeCampaign === false ? void 0 : await this.scopeCampaign(resolvedRequest, warnings);
1731
- if (options.discoverCapabilities !== false) {
1732
- await this.discoverPlannedRoutes(resolvedRequest, warnings);
1733
- }
1875
+ const [workspaceContext, scopeResult] = await Promise.all([
1876
+ resolvedRequest.workspaceContext ? Promise.resolve(resolvedRequest.workspaceContext) : this.getWorkspaceContext(warnings),
1877
+ options.scopeCampaign === false ? Promise.resolve(void 0) : this.scopeCampaign(resolvedRequest, warnings),
1878
+ options.discoverCapabilities === false ? Promise.resolve(void 0) : this.discoverPlannedRoutes(resolvedRequest, warnings)
1879
+ ]);
1734
1880
  const plan = createCampaignBuilderAgentPlan(resolvedRequest, {
1735
1881
  workspaceContext,
1736
1882
  scopeBuildArgs: asRecord(scopeResult?.build_campaign_args ?? scopeResult?.buildCampaignArgs),
1737
1883
  warnings
1738
1884
  });
1739
- if (options.includeStrategyMemoryStatus !== false) {
1740
- await this.attachStrategyMemoryStatus(plan, warnings);
1741
- }
1742
- if (options.includeKernelPlan !== false) {
1743
- await this.attachKernelCampaignBuildPlan(plan, warnings);
1744
- }
1745
- if (options.includeDeliveryRisk !== false) {
1746
- await this.attachDeliveryRiskPreflight(plan, warnings);
1747
- }
1885
+ await Promise.all([
1886
+ options.includeStrategyMemoryStatus === false ? Promise.resolve() : this.attachStrategyMemoryStatus(plan, warnings),
1887
+ options.includeKernelPlan === false ? Promise.resolve() : this.attachKernelCampaignBuildPlan(plan, warnings),
1888
+ options.includeDeliveryRisk === false ? Promise.resolve() : this.attachDeliveryRiskPreflight(plan, warnings)
1889
+ ]);
1748
1890
  return plan;
1749
1891
  }
1750
1892
  async readiness(request, options = {}) {
@@ -1792,6 +1934,8 @@ var CampaignBuilderAgent = class {
1792
1934
  },
1793
1935
  brain_defaults: plan.buildRequest.brainDefaults,
1794
1936
  brain_preflight: plan.buildRequest.brainPreflight,
1937
+ learning_holdout: plan.buildRequest.learningHoldout,
1938
+ campaign_strategy_context: plan.buildRequest.campaignStrategyContext,
1795
1939
  delivery_risk_preflight: plan.buildRequest.deliveryRisk,
1796
1940
  delivery_risk: plan.buildRequest.deliveryRisk
1797
1941
  }),
@@ -1804,7 +1948,8 @@ var CampaignBuilderAgent = class {
1804
1948
  estimated_credits: plan.estimatedCredits
1805
1949
  },
1806
1950
  brain_delivery_risk_preflight: plan.buildRequest.deliveryRisk,
1807
- delivery_risk: plan.buildRequest.deliveryRisk
1951
+ delivery_risk: plan.buildRequest.deliveryRisk,
1952
+ learning_holdout: plan.buildRequest.learningHoldout
1808
1953
  },
1809
1954
  approval_required: plan.approvals.some((approval) => approval.blocking),
1810
1955
  actor_type: "agent",
@@ -1913,7 +2058,8 @@ var CampaignBuilderAgent = class {
1913
2058
  const destinationType = options.deliveryDestinationType ?? plan.buildRequest.delivery?.destinationType ?? "json";
1914
2059
  result.deliveryApproval = await this.approveCampaignDelivery(campaignBuildId, destinationType, {
1915
2060
  destinationId: options.deliveryDestinationId,
1916
- destinationConfig: options.deliveryDestinationConfig ?? plan.buildRequest.delivery?.destinationConfig
2061
+ destinationConfig: options.deliveryDestinationConfig ?? plan.buildRequest.delivery?.destinationConfig,
2062
+ allowPartialDelivery: options.allowPartialDelivery
1917
2063
  });
1918
2064
  finalStatus = await this.waitForCampaignBuildStatus(
1919
2065
  campaignBuildId,
@@ -1936,8 +2082,12 @@ var CampaignBuilderAgent = class {
1936
2082
  const args = compact({
1937
2083
  campaign_build_id: campaignBuildId,
1938
2084
  page_size: options.limit,
1939
- cursor: options.cursor
2085
+ cursor: options.cursor,
2086
+ include_data: options.includeData,
2087
+ include_raw: options.includeRaw
1940
2088
  });
2089
+ if (options.cached !== void 0) filters.cached = options.cached;
2090
+ if (options.exportReady !== void 0) filters.export_ready = options.exportReady;
1941
2091
  if (Object.keys(filters).length > 0) args.filters = filters;
1942
2092
  const data = await this.callMcp("get_campaign_build_rows", args);
1943
2093
  return mapAgentCampaignRowsResult(data);
@@ -1952,12 +2102,14 @@ var CampaignBuilderAgent = class {
1952
2102
  return this.approveCampaignDelivery(campaignBuildId, destinationType, options);
1953
2103
  }
1954
2104
  async reviewBuild(campaignBuildId, options = {}) {
1955
- const status = await this.getBuildStatus(campaignBuildId);
1956
- const rows = await this.getBuildRows(campaignBuildId, {
1957
- ...options,
1958
- limit: options.limit ?? 100
1959
- });
1960
- const artifacts = await this.listBuildArtifacts(campaignBuildId);
2105
+ const [status, rows, artifacts] = await Promise.all([
2106
+ this.getBuildStatus(campaignBuildId),
2107
+ this.getBuildRows(campaignBuildId, {
2108
+ ...options,
2109
+ limit: options.limit ?? 100
2110
+ }),
2111
+ this.listBuildArtifacts(campaignBuildId)
2112
+ ]);
1961
2113
  return createCampaignBuilderBuildReview(status, rows, artifacts);
1962
2114
  }
1963
2115
  async getCampaignBuildStatus(campaignBuildId) {
@@ -1985,7 +2137,8 @@ var CampaignBuilderAgent = class {
1985
2137
  campaign_build_id: campaignBuildId,
1986
2138
  destination_type: destinationType,
1987
2139
  destination_id: options?.destinationId,
1988
- destination_config: options?.destinationConfig
2140
+ destination_config: options?.destinationConfig,
2141
+ allow_partial_delivery: options?.allowPartialDelivery === true ? true : void 0
1989
2142
  });
1990
2143
  const data = await this.callMcp("approve_campaign_delivery", args);
1991
2144
  return {
@@ -1995,7 +2148,10 @@ var CampaignBuilderAgent = class {
1995
2148
  message: data.message ?? "",
1996
2149
  deliveryId: data.delivery_id,
1997
2150
  deliveryIds: data.delivery_ids,
1998
- approvedCount: data.approved_count
2151
+ approvedCount: data.approved_count,
2152
+ partialDelivery: data.partial_delivery,
2153
+ effectiveTargetCount: data.effective_target_count,
2154
+ requestedTargetCount: data.requested_target_count
1999
2155
  };
2000
2156
  }
2001
2157
  async getWorkspaceContext(warnings) {
@@ -2031,15 +2187,19 @@ var CampaignBuilderAgent = class {
2031
2187
  const providers = new Set(
2032
2188
  [...request.integrations ?? [], ...routesFromCustomerTools(request.customerTools ?? [])].filter((route) => route.provider !== "signaliz").map((route) => route.provider)
2033
2189
  );
2034
- for (const provider of providers) {
2190
+ const warningsByProvider = await Promise.all(Array.from(providers, async (provider) => {
2035
2191
  try {
2036
2192
  await this.callMcp("discover_capabilities", {
2037
2193
  query: `campaign builder ${provider} ${request.goal}`,
2038
2194
  category: "campaign"
2039
2195
  });
2196
+ return void 0;
2040
2197
  } catch (error) {
2041
- warnings.push(`Capability discovery for ${provider} failed: ${errorMessage(error)}`);
2198
+ return `Capability discovery for ${provider} failed: ${errorMessage(error)}`;
2042
2199
  }
2200
+ }));
2201
+ for (const warning of warningsByProvider) {
2202
+ if (warning) warnings.push(warning);
2043
2203
  }
2044
2204
  }
2045
2205
  async attachStrategyMemoryStatus(plan, warnings) {
@@ -2063,8 +2223,24 @@ var CampaignBuilderAgent = class {
2063
2223
  const kernelPlan = await this.callMcp("gtm_campaign_build_plan", asRecord(plannerStep.arguments));
2064
2224
  plan.kernelPlan = asRecord(kernelPlan);
2065
2225
  const brainDefaults = asRecord(plan.kernelPlan.brain_defaults ?? plan.kernelPlan.brainDefaults);
2226
+ const corpusContext = asRecord(plan.kernelPlan.corpus_context ?? plan.kernelPlan.corpusContext);
2227
+ const campaignStrategyContext = asRecord(plan.kernelPlan.campaign_strategy_context ?? plan.kernelPlan.campaignStrategyContext);
2228
+ 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) : [];
2066
2229
  if (Object.keys(brainDefaults).length > 0 && Object.keys(asRecord(plan.buildRequest.brainDefaults)).length === 0) {
2067
2230
  plan.buildRequest.brainDefaults = brainDefaults;
2231
+ }
2232
+ if (Object.keys(campaignStrategyContext).length > 0) {
2233
+ plan.buildRequest.campaignStrategyContext = campaignStrategyContext;
2234
+ }
2235
+ if (Object.keys(corpusContext).length > 0 || memoryExamples.length > 0 || Object.keys(brainDefaults).length > 0 || Object.keys(campaignStrategyContext).length > 0) {
2236
+ plan.buildRequest.brainPreflight = compact({
2237
+ ...asRecord(plan.buildRequest.brainPreflight),
2238
+ corpus_context: Object.keys(corpusContext).length > 0 ? corpusContext : void 0,
2239
+ memory_examples: memoryExamples.length > 0 ? memoryExamples : void 0,
2240
+ brain_defaults: Object.keys(brainDefaults).length > 0 ? brainDefaults : void 0,
2241
+ campaign_strategy_context: Object.keys(campaignStrategyContext).length > 0 ? campaignStrategyContext : void 0
2242
+ });
2243
+ plan.brainPreflight = asRecord(plan.buildRequest.brainPreflight);
2068
2244
  refreshBuildStepArguments(plan);
2069
2245
  }
2070
2246
  } catch (error) {
@@ -2245,6 +2421,9 @@ function createCampaignBuilderProofReceipt(plan, result) {
2245
2421
  campaign_build_id: dryRunResult.campaignBuildId ?? dryRunResult.campaign_build_id ?? null
2246
2422
  }
2247
2423
  ];
2424
+ const learnBackPlan = summarizeCampaignBuilderProofLearnBack(
2425
+ asRecord(asRecord(plan.buildRequest.brainPreflight).learn_back_plan ?? asRecord(plan.brainPreflight).learn_back_plan)
2426
+ );
2248
2427
  return {
2249
2428
  receipt_version: "campaign-agent-proof.v1",
2250
2429
  source: "signaliz.campaignBuilderAgent.proof",
@@ -2267,6 +2446,7 @@ function createCampaignBuilderProofReceipt(plan, result) {
2267
2446
  gates,
2268
2447
  strategy_memory_status: summarizeCampaignBuilderProofStrategyMemory(strategyMemory),
2269
2448
  dry_run_result: summarizeCampaignBuilderProofDryRun(dryRunResult),
2449
+ learn_back_plan: learnBackPlan,
2270
2450
  recommended_next_actions: [
2271
2451
  "Review the plan, approvals, and dry-run result.",
2272
2452
  "Use campaign-agent commit-plan --confirm-write only after review.",
@@ -2823,6 +3003,13 @@ function campaignBuilderMemoryKitSdkExample(request, seedManifestFile) {
2823
3003
  function campaignBuilderMemoryKitSource(source) {
2824
3004
  const normalized = normalizeTemplateKey(source || "agency");
2825
3005
  const catalog = {
3006
+ northStar: {
3007
+ id: "north_star_campaign_builder",
3008
+ label: "North Star campaign-builder acceptance patterns",
3009
+ seed_source: "north-star",
3010
+ scope: "redacted_acceptance_seed_packs",
3011
+ privacy: "redacted aggregate counts, QA gates, and fixture shape only"
3012
+ },
2826
3013
  agency: {
2827
3014
  id: "agency_campaign_builder",
2828
3015
  label: "Agency campaign-builder strategy patterns",
@@ -2848,6 +3035,10 @@ function campaignBuilderMemoryKitSource(source) {
2848
3035
  const sourceMap = {
2849
3036
  agency: "agency",
2850
3037
  "campaign-builder": "agency",
3038
+ "north-star": "northStar",
3039
+ northstar: "northStar",
3040
+ "campaign-builder-north-star": "northStar",
3041
+ acceptance: "northStar",
2851
3042
  "strategy-memory": "agency",
2852
3043
  "workflow-patterns": "workflow",
2853
3044
  workflow: "workflow",
@@ -2863,13 +3054,13 @@ function campaignBuilderMemoryKitSource(source) {
2863
3054
  return {
2864
3055
  seedSource: "all",
2865
3056
  verifyScript: null,
2866
- memorySources: [catalog.agency, catalog.workflow, catalog.feedback]
3057
+ memorySources: [catalog.northStar, catalog.agency, catalog.workflow, catalog.feedback]
2867
3058
  };
2868
3059
  }
2869
3060
  const item = catalog[selected];
2870
3061
  return {
2871
3062
  seedSource: item.seed_source,
2872
- 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,
3063
+ 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,
2873
3064
  memorySources: [item]
2874
3065
  };
2875
3066
  }
@@ -3049,11 +3240,11 @@ function builtInRoutesFor(request) {
3049
3240
  routes.push({
3050
3241
  id: "signaliz-lead-generation",
3051
3242
  layer: "source",
3052
- gtmLayers: ["find_company", "find_people", "lead_generation"],
3243
+ gtmLayer: "lead_generation",
3053
3244
  provider: "signaliz",
3054
3245
  mode: "required",
3055
3246
  toolName: "generate_leads",
3056
- rationale: "Use Signaliz fresh workspace acquisition as the default source of truth."
3247
+ rationale: "Inventory verified cache first, then use Signaliz fresh workspace acquisition for the remaining approved gap."
3057
3248
  });
3058
3249
  }
3059
3250
  if (builtIns.has("local_leads")) {
@@ -3067,6 +3258,28 @@ function builtInRoutesFor(request) {
3067
3258
  rationale: "Use Signaliz local-leads sourcing for geography-bound local business campaigns."
3068
3259
  });
3069
3260
  }
3261
+ if (builtIns.has("company_discovery")) {
3262
+ routes.push({
3263
+ id: "signaliz-company-discovery",
3264
+ layer: "source",
3265
+ gtmLayer: "find_company",
3266
+ provider: "signaliz",
3267
+ mode: "if_available",
3268
+ toolName: "find_companies_signaliz",
3269
+ rationale: "Find and qualify company domains before people/email fill for account-led campaigns."
3270
+ });
3271
+ }
3272
+ if (builtIns.has("people_discovery")) {
3273
+ routes.push({
3274
+ id: "signaliz-people-discovery",
3275
+ layer: "source",
3276
+ gtmLayer: "find_people",
3277
+ provider: "signaliz",
3278
+ mode: "if_available",
3279
+ toolName: "find_people_signaliz",
3280
+ rationale: "Find people directly when the brief is contact research or LinkedIn-style prospecting."
3281
+ });
3282
+ }
3070
3283
  if (builtIns.has("email_finding")) {
3071
3284
  routes.push({
3072
3285
  id: "signaliz-email-finding",
@@ -3111,7 +3324,7 @@ function normalizeBuiltIns(request) {
3111
3324
  return [...builtIns].filter(isCampaignBuilderBuiltInTool);
3112
3325
  }
3113
3326
  function isCampaignBuilderBuiltInTool(value) {
3114
- return ["lead_generation", "local_leads", "email_finding", "email_verification", "signals"].includes(String(value));
3327
+ return ["lead_generation", "local_leads", "company_discovery", "people_discovery", "email_finding", "email_verification", "signals"].includes(String(value));
3115
3328
  }
3116
3329
  function looksLikeLocalLeadCampaign(request) {
3117
3330
  const text = [
@@ -3142,6 +3355,21 @@ function routesFromCustomerTools(tools) {
3142
3355
  }))
3143
3356
  );
3144
3357
  }
3358
+ function normalizeCampaignBuilderLearningHoldout(input, gtmCampaignId, campaignName) {
3359
+ const source = asRecord(input);
3360
+ if (source.enabled === false) return { enabled: false };
3361
+ const controlPercentage = numberOrUndefined2(source.control_percentage ?? source.controlPercentage);
3362
+ const minControlSize = numberOrUndefined2(source.min_control_size ?? source.minControlSize);
3363
+ const experimentKey = stringValue(source.experiment_key ?? source.experimentKey) ?? (gtmCampaignId ? `gtm_campaign:${gtmCampaignId}:learning_holdout` : `campaign_builder_agent:${hashString(campaignName)}:learning_holdout`);
3364
+ return {
3365
+ enabled: true,
3366
+ controlPercentage: controlPercentage ?? 0.2,
3367
+ minControlSize: minControlSize ?? 50,
3368
+ experimentKey,
3369
+ sourceCampaignId: stringValue(source.source_campaign_id ?? source.sourceCampaignId) ?? gtmCampaignId,
3370
+ primaryMetric: stringValue(source.primary_metric ?? source.primaryMetric) ?? "positive_reply_rate"
3371
+ };
3372
+ }
3145
3373
  function createBuildRequest(request, defaults, targetCount, campaignName, scopeBuildArgs, routes = [], memory, scopeBrainPreflight) {
3146
3374
  const buildRequest = {
3147
3375
  name: campaignName,
@@ -3166,10 +3394,19 @@ function createBuildRequest(request, defaults, targetCount, campaignName, scopeB
3166
3394
  qualification: defaults.qualification,
3167
3395
  copy: defaults.copy,
3168
3396
  delivery: defaults.delivery,
3169
- enhancers: enhancerConfig(request)
3397
+ enhancers: enhancerConfig(request),
3398
+ sourceRouting: request.sourceRouting ?? {
3399
+ mode: looksLikeLocalLeadCampaign(request) ? "local_leads" : "auto",
3400
+ fillPolicy: "aggressive"
3401
+ }
3170
3402
  };
3171
3403
  const scoped = asRecord(scopeBuildArgs);
3172
3404
  buildRequest.gtmCampaignId = request.gtmCampaignId ?? stringValue(scoped.gtm_campaign_id ?? scoped.gtmCampaignId);
3405
+ buildRequest.learningHoldout = normalizeCampaignBuilderLearningHoldout(
3406
+ request.learningHoldout ?? scoped.learning_holdout ?? scoped.learningHoldout,
3407
+ buildRequest.gtmCampaignId,
3408
+ campaignName
3409
+ );
3173
3410
  if (typeof scoped.name === "string") buildRequest.name = scoped.name;
3174
3411
  if (typeof scoped.prompt === "string") buildRequest.prompt = scoped.prompt;
3175
3412
  if (typeof scoped.target_count === "number") buildRequest.targetCount = scoped.target_count;
@@ -3244,13 +3481,137 @@ function createBrainPreflight(request, buildRequest, routes, memory) {
3244
3481
  target_icp: targetIcp,
3245
3482
  provider_chain: providerChain,
3246
3483
  lead_source: providerChain[0] ?? "signaliz",
3484
+ learning_holdout: buildRequest.learningHoldout,
3247
3485
  tool_sequence: [
3248
3486
  { tool: "gtm_brain_learning_cycle_plan", purpose: "Plan the Brain learning loop for this campaign.", arguments: learningArgs },
3249
3487
  { tool: "gtm_brain_seed_defaults", purpose: "Seed campaign defaults from workspace memory and safe aggregate patterns.", arguments: defaultsArgs },
3250
3488
  { tool: "gtm_brain_delivery_risk", purpose: "Check deliverability risk before launch or external writes.", arguments: riskArgs }
3251
- ]
3489
+ ],
3490
+ learn_back_plan: createCampaignBuilderLearnBackPlan(request, buildRequest)
3491
+ };
3492
+ }
3493
+ function createCampaignBuilderLearnBackPlan(request, buildRequest) {
3494
+ const instantlyFeedbackContext = campaignBuilderProviderFeedbackContext(request, buildRequest, "instantly");
3495
+ const postBuildSequence = [
3496
+ {
3497
+ tool: "get_campaign_build_status",
3498
+ approval_boundary: "read_only",
3499
+ arguments_template: { campaign_build_id: "<build_campaign.campaign_build_id>" },
3500
+ reason: "Wait for the Campaign Builder run to finish before deriving memory."
3501
+ },
3502
+ {
3503
+ tool: "gtm_campaign_build_backfill_preview",
3504
+ approval_boundary: "read_only",
3505
+ arguments_template: { campaign_build_id: "<build_campaign.campaign_build_id>", create_memory: true },
3506
+ reason: "Preview the provider-neutral Kernel import payload without writing rows."
3507
+ },
3508
+ {
3509
+ tool: "gtm_campaign_build_backfill_run",
3510
+ approval_boundary: "dry_run",
3511
+ arguments_template: { campaign_build_ids: ["<build_campaign.campaign_build_id>"], dry_run: true, create_memory: true, run_brain_cycle: false },
3512
+ reason: "Dry-run the Kernel history import before creating campaign memory."
3513
+ },
3514
+ {
3515
+ tool: "gtm_campaign_build_backfill_run",
3516
+ approval_boundary: "explicit_write_approval",
3517
+ 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" },
3518
+ reason: "After review, queue the Kernel history import; Brain learning remains dry-run unless separately approved."
3519
+ }
3520
+ ];
3521
+ if (instantlyFeedbackContext) {
3522
+ postBuildSequence.push({
3523
+ tool: "gtm_instantly_feedback_pull",
3524
+ approval_boundary: "dry_run_first_explicit_write_approval",
3525
+ arguments_template: {
3526
+ campaign_id: buildRequest.gtmCampaignId || "<gtm_campaign_id_after_commit>",
3527
+ campaign_build_id: "<build_campaign.campaign_build_id>",
3528
+ source: "emails",
3529
+ email_type: "received",
3530
+ ...instantlyFeedbackContext,
3531
+ dry_run: true,
3532
+ create_memory: true,
3533
+ write_routine_outcomes: true,
3534
+ run_brain_cycle: true,
3535
+ brain_cycle_write_mode: "dry_run"
3536
+ },
3537
+ reason: "After Instantly delivery has live outcomes, pull reply and bounce history into private feedback memory before Brain learning."
3538
+ });
3539
+ }
3540
+ postBuildSequence.push(
3541
+ {
3542
+ tool: "gtm_campaign_learning_status",
3543
+ approval_boundary: "read_only",
3544
+ arguments_template: {
3545
+ campaign_id: buildRequest.gtmCampaignId || "<gtm_campaign_id_after_commit>",
3546
+ campaign_build_id: "<build_campaign.campaign_build_id>",
3547
+ write_mode: "dry_run"
3548
+ },
3549
+ reason: "Confirm feedback, memory, and Brain learning readiness after the import."
3550
+ },
3551
+ {
3552
+ tool: "gtm_memory_search",
3553
+ approval_boundary: "read_only",
3554
+ arguments_template: {
3555
+ query: request.goal,
3556
+ ...buildRequest.gtmCampaignId ? { campaign_id: buildRequest.gtmCampaignId } : {},
3557
+ limit: 10
3558
+ },
3559
+ reason: "Verify the new private memory is retrievable for future campaign planning."
3560
+ },
3561
+ {
3562
+ tool: "gtm_brain_learning_cycle_plan",
3563
+ approval_boundary: "read_only",
3564
+ arguments_template: {
3565
+ ...buildRequest.gtmCampaignId ? { campaign_id: buildRequest.gtmCampaignId } : {},
3566
+ include_memory: true,
3567
+ write_mode: "dry_run"
3568
+ },
3569
+ reason: "Plan the Brain phases from the imported outcomes before any pattern writes."
3570
+ }
3571
+ );
3572
+ return {
3573
+ source_tool: "campaign-builder-agent",
3574
+ gtm_campaign_id: buildRequest.gtmCampaignId ?? null,
3575
+ trigger: "After build_campaign returns a campaign_build_id and the build reaches a terminal reviewed state.",
3576
+ build_result_placeholders: {
3577
+ campaign_build_id: "<build_campaign.campaign_build_id>"
3578
+ },
3579
+ memory_write_policy: {
3580
+ target_tables: ["gtm_campaigns", "gtm_campaign_provider_links", "gtm_campaign_feedback_events", "gtm_memory_items", "gtm_brain_patterns"],
3581
+ gtm_memory_items: {
3582
+ shareable: false,
3583
+ allowed_redaction_states: ["redacted", "abstracted"],
3584
+ raw_private_fields_withheld: ["reply_text", "copy_body", "lead_email", "lead_domain", "company_domain", "provider_campaign_id", "provider_payload", "webhook_url", "secret"]
3585
+ },
3586
+ gtm_brain_patterns: "Workspace-scoped by default. Global promotion requires abstracted_only=true, shared opt-in, and privacy thresholds."
3587
+ },
3588
+ post_build_sequence: postBuildSequence.map((item, index) => ({ step: index + 1, ...item })),
3589
+ 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.",
3590
+ 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."
3252
3591
  };
3253
3592
  }
3593
+ function campaignBuilderProviderFeedbackContext(request, buildRequest, provider) {
3594
+ const normalizedProvider = String(provider).toLowerCase();
3595
+ const routes = [...request.integrations ?? [], ...routesFromCustomerTools(request.customerTools ?? [])];
3596
+ const providerRoute = routes.find((route) => String(route.provider).toLowerCase() === normalizedProvider);
3597
+ const routeConfig = asRecord(providerRoute?.config);
3598
+ const deliveryConfig = asRecord(buildRequest.delivery?.destinationConfig);
3599
+ const hasProviderContext = [
3600
+ ...request.preferredProviders ?? [],
3601
+ ...routes.map((route) => route.provider),
3602
+ stringValue(deliveryConfig.provider),
3603
+ stringValue(deliveryConfig.provider_id ?? deliveryConfig.providerId),
3604
+ stringValue(deliveryConfig.destination_provider ?? deliveryConfig.destinationProvider),
3605
+ stringValue(deliveryConfig.sink ?? deliveryConfig.sink_type ?? deliveryConfig.sinkType)
3606
+ ].some((value) => String(value).toLowerCase() === normalizedProvider);
3607
+ if (!hasProviderContext) return void 0;
3608
+ return compact({
3609
+ provider_link_id: stringValue(routeConfig.provider_link_id ?? routeConfig.providerLinkId ?? deliveryConfig.provider_link_id ?? deliveryConfig.providerLinkId),
3610
+ 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),
3611
+ integration_id: stringValue(routeConfig.integration_id ?? routeConfig.integrationId ?? deliveryConfig.integration_id ?? deliveryConfig.integrationId),
3612
+ instantly_profile: stringValue(routeConfig.instantly_profile ?? routeConfig.instantlyProfile ?? routeConfig.profile ?? deliveryConfig.instantly_profile ?? deliveryConfig.instantlyProfile ?? deliveryConfig.profile)
3613
+ });
3614
+ }
3254
3615
  function gtmLayersForBuilderLayer(layer) {
3255
3616
  const map = {
3256
3617
  workspace_context: ["customer_data"],
@@ -3655,6 +4016,7 @@ function buildFallbackPlanSnapshot(plan) {
3655
4016
  target_count: plan.targetCount,
3656
4017
  approvals: plan.approvals,
3657
4018
  brain_preflight: plan.brainPreflight,
4019
+ campaign_strategy_context: plan.buildRequest.campaignStrategyContext,
3658
4020
  operating_playbooks: plan.operatingPlaybooks
3659
4021
  });
3660
4022
  }
@@ -3787,6 +4149,17 @@ function buildCampaignArgs(request) {
3787
4149
  if (request.dedupKeys) args.dedup_keys = request.dedupKeys;
3788
4150
  if (request.brainPreflight) args.brain_preflight = request.brainPreflight;
3789
4151
  if (request.brainDefaults) args.brain_defaults = request.brainDefaults;
4152
+ if (request.learningHoldout) {
4153
+ args.learning_holdout = compact({
4154
+ enabled: request.learningHoldout.enabled,
4155
+ control_percentage: request.learningHoldout.controlPercentage,
4156
+ min_control_size: request.learningHoldout.minControlSize,
4157
+ experiment_key: request.learningHoldout.experimentKey,
4158
+ source_campaign_id: request.learningHoldout.sourceCampaignId,
4159
+ primary_metric: request.learningHoldout.primaryMetric
4160
+ });
4161
+ }
4162
+ if (request.campaignStrategyContext) args.campaign_strategy_context = request.campaignStrategyContext;
3790
4163
  if (request.deliveryRisk) args.delivery_risk = request.deliveryRisk;
3791
4164
  if (request.icp) {
3792
4165
  args.icp = compact({
@@ -3806,6 +4179,15 @@ function buildCampaignArgs(request) {
3806
4179
  model: request.policy.model
3807
4180
  });
3808
4181
  }
4182
+ if (request.sourceRouting) {
4183
+ args.source_routing = compact({
4184
+ mode: request.sourceRouting.mode,
4185
+ fill_policy: request.sourceRouting.fillPolicy,
4186
+ shard_size: request.sourceRouting.shardSize,
4187
+ max_source_shard_size: request.sourceRouting.maxSourceShardSize,
4188
+ max_local_source_shard_size: request.sourceRouting.maxLocalSourceShardSize
4189
+ });
4190
+ }
3809
4191
  if (request.signals) {
3810
4192
  args.signals = compact({
3811
4193
  enabled: request.signals.enabled,
@@ -3873,11 +4255,13 @@ function buildExecutionPrepareArgs(request, dryRun, confirmSpend) {
3873
4255
  delivery_risk: buildArgs2.delivery_risk,
3874
4256
  dedup_keys: buildArgs2.dedup_keys,
3875
4257
  policy: buildArgs2.policy,
4258
+ learning_holdout: buildArgs2.learning_holdout,
3876
4259
  signals: buildArgs2.signals,
3877
4260
  qualification: buildArgs2.qualification,
3878
4261
  copy: buildArgs2.copy,
3879
4262
  delivery: buildArgs2.delivery,
3880
- enhancers: buildArgs2.enhancers
4263
+ enhancers: buildArgs2.enhancers,
4264
+ source_routing: buildArgs2.source_routing
3881
4265
  });
3882
4266
  }
3883
4267
  function buildDeliveryApprovalArgs(request) {
@@ -3906,10 +4290,145 @@ function mapBuildResult(data, dryRun) {
3906
4290
  targetLimitReason: data.target_limit_reason,
3907
4291
  maxSupportedTargetCount: data.max_supported_target_count,
3908
4292
  brainContext: data.brain_context ?? {},
4293
+ learningHoldout: data.learning_holdout ?? {},
3909
4294
  providerRoute: mapProviderRoute2(data)
3910
4295
  };
3911
4296
  }
4297
+ var CAMPAIGN_BUILDER_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "canceled", "cancelled"]);
4298
+ var CAMPAIGN_BUILDER_BLOCKED_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["failed", "canceled", "cancelled"]);
4299
+ function mapAgentCampaignPhaseHealth(value) {
4300
+ if (!Array.isArray(value)) return [];
4301
+ return value.map((phase) => {
4302
+ const record = asRecord(phase);
4303
+ return {
4304
+ phase: stringValue(record.phase) ?? "unknown",
4305
+ status: stringValue(record.status) ?? "unknown",
4306
+ recordsProcessed: numberOrNull2(record.records_processed ?? record.recordsProcessed),
4307
+ recordsSucceeded: numberOrNull2(record.records_succeeded ?? record.recordsSucceeded),
4308
+ recordsFailed: numberOrNull2(record.records_failed ?? record.recordsFailed),
4309
+ heartbeatAgeSeconds: numberOrNull2(record.heartbeat_age_seconds ?? record.heartbeatAgeSeconds ?? record.phase_age_seconds)
4310
+ };
4311
+ });
4312
+ }
4313
+ function deriveCampaignBuildTerminalState(input) {
4314
+ const status = input.status.toLowerCase();
4315
+ const heartbeatAgeSeconds = input.phaseAgeSeconds;
4316
+ const stale = input.staleRunningPhase === true;
4317
+ const safeWatchAction = input.triggerRunId ? `signaliz ops run-status ${input.triggerRunId} --watch` : "signaliz campaign-agent status <campaign_build_id>";
4318
+ if (CAMPAIGN_BUILDER_BLOCKED_TERMINAL_STATUSES.has(status)) {
4319
+ return {
4320
+ kind: "blocked",
4321
+ label: "Blocked terminal state",
4322
+ reviewable: true,
4323
+ terminal: true,
4324
+ stale: false,
4325
+ phase: input.phase,
4326
+ phaseStatus: input.phaseStatus,
4327
+ heartbeatAgeSeconds,
4328
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4329
+ terminalReason: input.terminalReason,
4330
+ staleReason: null,
4331
+ safeNextAction: input.nextAction || "Review errors and rerun only after the blocker is resolved."
4332
+ };
4333
+ }
4334
+ if (status === "pending_approval") {
4335
+ return {
4336
+ kind: "pending_approval",
4337
+ label: "Pending delivery approval",
4338
+ reviewable: true,
4339
+ terminal: false,
4340
+ stale: false,
4341
+ phase: input.phase,
4342
+ phaseStatus: input.phaseStatus,
4343
+ heartbeatAgeSeconds,
4344
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4345
+ terminalReason: input.terminalReason,
4346
+ staleReason: null,
4347
+ safeNextAction: input.approvalAction || input.nextAction || "Review rows and approve delivery only after the approval packet passes."
4348
+ };
4349
+ }
4350
+ if (CAMPAIGN_BUILDER_TERMINAL_STATUSES.has(status)) {
4351
+ return {
4352
+ kind: "terminal",
4353
+ label: "Terminal and reviewable",
4354
+ reviewable: true,
4355
+ terminal: true,
4356
+ stale: false,
4357
+ phase: input.phase,
4358
+ phaseStatus: input.phaseStatus,
4359
+ heartbeatAgeSeconds,
4360
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4361
+ terminalReason: input.terminalReason,
4362
+ staleReason: null,
4363
+ safeNextAction: input.nextAction || "Review rows, artifacts, scorecard, and approval locks before delivery."
4364
+ };
4365
+ }
4366
+ if (status === "running" || status === "queued" || status === "processing") {
4367
+ if (stale) {
4368
+ return {
4369
+ kind: "running_stale",
4370
+ label: "Running but stale",
4371
+ reviewable: false,
4372
+ terminal: false,
4373
+ stale: true,
4374
+ phase: input.phase,
4375
+ phaseStatus: input.phaseStatus,
4376
+ heartbeatAgeSeconds,
4377
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4378
+ terminalReason: null,
4379
+ staleReason: input.staleReason || "The active phase heartbeat is stale.",
4380
+ safeNextAction: input.nextAction || safeWatchAction
4381
+ };
4382
+ }
4383
+ return {
4384
+ kind: "running_healthy",
4385
+ label: "Running and healthy",
4386
+ reviewable: false,
4387
+ terminal: false,
4388
+ stale: false,
4389
+ phase: input.phase,
4390
+ phaseStatus: input.phaseStatus,
4391
+ heartbeatAgeSeconds,
4392
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4393
+ terminalReason: null,
4394
+ staleReason: null,
4395
+ safeNextAction: input.nextAction || safeWatchAction
4396
+ };
4397
+ }
4398
+ return {
4399
+ kind: "unknown",
4400
+ label: "Unknown build state",
4401
+ reviewable: false,
4402
+ terminal: false,
4403
+ stale,
4404
+ phase: input.phase,
4405
+ phaseStatus: input.phaseStatus,
4406
+ heartbeatAgeSeconds,
4407
+ nextPollAfterSeconds: input.nextPollAfterSeconds,
4408
+ terminalReason: input.terminalReason,
4409
+ staleReason: input.staleReason,
4410
+ safeNextAction: input.nextAction || "Read campaign build status again before making a launch or delivery decision."
4411
+ };
4412
+ }
3912
4413
  function mapAgentCampaignBuildStatus(data) {
4414
+ const customerRowCounts = mapAgentCustomerRowCounts(data.customer_row_counts);
4415
+ const phaseHealth = mapAgentCampaignPhaseHealth(data.phases);
4416
+ const terminalReason = stringValue(data.terminal_reason) ?? customerRowCounts?.terminalReason ?? null;
4417
+ const staleReason = stringValue(data.stale_reason) ?? stringValue(asRecord(data.diagnostics).stale_reason) ?? null;
4418
+ const nextPollAfterSeconds = numberOrNull2(data.next_poll_after_seconds ?? data.nextPollAfterSeconds);
4419
+ const terminalState = deriveCampaignBuildTerminalState({
4420
+ status: stringValue(data.status) ?? "unknown",
4421
+ phase: stringValue(data.current_phase) ?? null,
4422
+ phaseStatus: stringValue(data.current_phase_status) ?? null,
4423
+ staleRunningPhase: data.stale_running_phase === true,
4424
+ phaseAgeSeconds: numberOrNull2(data.phase_age_seconds),
4425
+ nextPollAfterSeconds,
4426
+ terminalReason,
4427
+ staleReason,
4428
+ nextAction: formatAgentNextAction(data.next_action),
4429
+ approvalAction: formatAgentNextAction(data.approval_action),
4430
+ triggerRunId: stringValue(data.trigger_run_id) ?? null
4431
+ });
3913
4432
  return {
3914
4433
  campaignBuildId: stringValue(data.campaign_build_id) ?? "",
3915
4434
  campaignId: stringValue(data.campaign_id) ?? null,
@@ -3926,17 +4445,68 @@ function mapAgentCampaignBuildStatus(data) {
3926
4445
  warnings: diagnosticMessages2(data.warnings),
3927
4446
  errors: diagnosticMessages2(data.errors),
3928
4447
  artifactCount: numberOrUndefined2(data.artifact_count) ?? 0,
4448
+ artifactDownloads: Array.isArray(data.artifact_downloads) ? data.artifact_downloads.map(mapAgentCampaignArtifactDownload) : [],
4449
+ customerRowCounts,
4450
+ phaseAttemptTotals: nullableRecord(data.phase_attempt_totals) ?? void 0,
3929
4451
  providerRoute: mapProviderRoute2(data),
3930
4452
  triggerRunId: stringValue(data.trigger_run_id) ?? null,
3931
4453
  staleRunningPhase: data.stale_running_phase === true,
3932
4454
  phaseAgeSeconds: numberOrNull2(data.phase_age_seconds),
4455
+ nextPollAfterSeconds,
4456
+ terminalReason,
4457
+ staleReason,
4458
+ safeNextAction: terminalState.safeNextAction,
4459
+ terminalState,
4460
+ phaseHealth,
3933
4461
  diagnostics: nullableRecord(data.diagnostics) ?? {},
3934
4462
  nextAction: formatAgentNextAction(data.next_action),
4463
+ approvalAction: formatAgentNextAction(data.approval_action),
3935
4464
  createdAt: stringValue(data.created_at) ?? "",
3936
4465
  updatedAt: stringValue(data.updated_at) ?? "",
3937
4466
  completedAt: stringValue(data.completed_at) ?? null
3938
4467
  };
3939
4468
  }
4469
+ function mapAgentCampaignArtifactDownload(data) {
4470
+ return {
4471
+ id: stringValue(data.id) ?? "",
4472
+ artifactType: stringValue(data.artifact_type) ?? null,
4473
+ format: stringValue(data.format) ?? null,
4474
+ rowCount: numberOrUndefined2(data.row_count) ?? 0,
4475
+ signedUrl: stringValue(data.signed_url) ?? null,
4476
+ downloadUrl: stringValue(data.download_url) ?? null,
4477
+ manifestUrl: stringValue(data.manifest_url) ?? null,
4478
+ expiresAt: stringValue(data.expires_at) ?? null
4479
+ };
4480
+ }
4481
+ function mapAgentCustomerRowCounts(value) {
4482
+ const record = nullableRecord(value);
4483
+ if (!record) return void 0;
4484
+ return {
4485
+ requestedTarget: numberOrNull2(record.requested_target),
4486
+ acquiredRows: numberOrUndefined2(record.acquired_rows),
4487
+ acceptedRows: numberOrUndefined2(record.accepted_rows),
4488
+ usableRows: numberOrUndefined2(record.usable_rows),
4489
+ copiedRows: numberOrUndefined2(record.copied_rows),
4490
+ approvalReadyRows: numberOrUndefined2(record.approval_ready_rows),
4491
+ qaReadyRows: numberOrUndefined2(record.qa_ready_rows),
4492
+ deliveredRows: numberOrUndefined2(record.delivered_rows),
4493
+ disqualifiedRows: numberOrUndefined2(record.disqualified_rows),
4494
+ reviewableRows: numberOrUndefined2(record.reviewable_rows),
4495
+ rowFailures: numberOrUndefined2(record.row_failures),
4496
+ acceptedShortfall: numberOrNull2(record.accepted_shortfall),
4497
+ usableShortfall: numberOrNull2(record.usable_shortfall),
4498
+ qaReadyShortfall: numberOrNull2(record.qa_ready_shortfall),
4499
+ deliveryShortfall: numberOrNull2(record.delivery_shortfall),
4500
+ fillRatio: numberOrNull2(record.fill_ratio),
4501
+ qaReadyFillRatio: numberOrNull2(record.qa_ready_fill_ratio),
4502
+ deliveredFillRatio: numberOrNull2(record.delivered_fill_ratio),
4503
+ terminalReason: stringValue(record.terminal_reason) ?? null,
4504
+ acceptedShortfallReason: stringValue(record.accepted_shortfall_reason) ?? null,
4505
+ usableShortfallReason: stringValue(record.usable_shortfall_reason) ?? null,
4506
+ qaReadyShortfallReason: stringValue(record.qa_ready_shortfall_reason) ?? null,
4507
+ deliveryShortfallReason: stringValue(record.delivery_shortfall_reason) ?? null
4508
+ };
4509
+ }
3940
4510
  function mapAgentCampaignRowsResult(data) {
3941
4511
  const rows = Array.isArray(data.rows) ? data.rows : [];
3942
4512
  return {
@@ -3964,74 +4534,296 @@ function mapAgentCampaignArtifact(data) {
3964
4534
  artifactType: stringValue(data.artifact_type) ?? "",
3965
4535
  destination: stringValue(data.destination) ?? "",
3966
4536
  storagePath: stringValue(data.storage_path) ?? "",
4537
+ format: stringValue(data.format) ?? null,
3967
4538
  signedUrl: stringValue(data.signed_url) ?? null,
4539
+ downloadUrl: stringValue(data.download_url) ?? null,
4540
+ manifestUrl: stringValue(data.manifest_url) ?? null,
4541
+ expiresAt: stringValue(data.expires_at) ?? null,
3968
4542
  rowCount: numberOrUndefined2(data.row_count) ?? 0,
3969
4543
  checksum: stringValue(data.checksum) ?? null,
3970
4544
  metadata: nullableRecord(data.metadata) ?? {},
3971
4545
  createdAt: stringValue(data.created_at) ?? ""
3972
4546
  };
3973
4547
  }
4548
+ var NORTH_STAR_BANNED_COPY_PHRASES = [
4549
+ "i have been following",
4550
+ "i saw that",
4551
+ "would you be opposed",
4552
+ "unlock new opportunities",
4553
+ "significant success",
4554
+ "we understand",
4555
+ "hope this finds you well"
4556
+ ];
4557
+ function campaignReviewGate(id, label, status, detail) {
4558
+ return {
4559
+ id,
4560
+ label,
4561
+ status,
4562
+ passed: status !== "blocked",
4563
+ detail
4564
+ };
4565
+ }
4566
+ function northStarMetric(id, label, status, detail, value, target) {
4567
+ return {
4568
+ id,
4569
+ label,
4570
+ status,
4571
+ detail,
4572
+ ...value !== void 0 ? { value } : {},
4573
+ ...target !== void 0 ? { target } : {}
4574
+ };
4575
+ }
4576
+ function createCampaignBuilderNorthStarScorecard(status, rows, artifacts, terminalState) {
4577
+ if (!terminalState.reviewable) {
4578
+ const pendingMetric = northStarMetric(
4579
+ "terminal_state",
4580
+ "Terminal-state contract",
4581
+ terminalState.kind === "running_stale" ? "blocked" : "pending",
4582
+ 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.`,
4583
+ terminalState.kind,
4584
+ "terminal or pending_approval"
4585
+ );
4586
+ return {
4587
+ version: "campaign-builder-north-star.v1",
4588
+ status: pendingMetric.status,
4589
+ score: null,
4590
+ moatState: "scaffolding_ready",
4591
+ metrics: [pendingMetric],
4592
+ blockers: pendingMetric.status === "blocked" ? [pendingMetric.detail] : [],
4593
+ warnings: pendingMetric.status === "pending" ? [pendingMetric.detail] : [],
4594
+ nextAction: terminalState.safeNextAction
4595
+ };
4596
+ }
4597
+ const sampledRows = rows.rows.length;
4598
+ const rowData = rows.rows.map((row) => asRecord(row.data));
4599
+ const requestedTarget = status.customerRowCounts?.requestedTarget ?? status.recordsTotal ?? rows.count;
4600
+ const finalRows = status.customerRowCounts?.deliveredRows ?? status.customerRowCounts?.qaReadyRows ?? status.customerRowCounts?.copiedRows ?? status.customerRowCounts?.acceptedRows ?? rows.count;
4601
+ const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
4602
+ const verifiedEmailRows = rowData.filter(campaignReviewDataHasVerifiedEmail).length;
4603
+ const uniqueEmails = new Set(rowData.map(campaignReviewDataEmail).filter(Boolean)).size;
4604
+ const uniqueDomains = new Set(rowData.map(campaignReviewDataDomain).filter(Boolean)).size;
4605
+ const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
4606
+ const copyQaIssuesByRow = rowData.map(campaignReviewDataCopyQaIssues).filter((issues) => issues.length > 0);
4607
+ const copyQaIssueCounts = countCampaignReviewIssues(copyQaIssuesByRow);
4608
+ const signalBackedRows = rowData.filter(campaignReviewDataHasSignalBasis).length;
4609
+ const noSupportedSignalRows = rowData.filter(campaignReviewDataHasNoSupportedSignal).length;
4610
+ const unsupportedSignalClaims = rowData.filter(campaignReviewDataHasUnsupportedSignalClaim).length;
4611
+ const approvalLockedRows = rowData.filter(campaignReviewDataHasApprovalLock).length;
4612
+ const holdoutRows = rowData.filter(campaignReviewDataHasHoldoutAttribution).length;
4613
+ const artifactCount = artifacts.length || status.artifactCount || 0;
4614
+ const deliveredRows = status.customerRowCounts?.deliveredRows ?? 0;
4615
+ const feedbackReady = campaignReviewStatusHasFeedbackReadiness(status);
4616
+ const feedbackRequiredForApproval = terminalState.kind === "pending_approval";
4617
+ const moat = campaignReviewMoatState(status, holdoutRows);
4618
+ const metrics = [
4619
+ northStarMetric(
4620
+ "terminal_state",
4621
+ "Terminal-state contract",
4622
+ terminalState.kind === "blocked" ? "blocked" : "pass",
4623
+ terminalState.label,
4624
+ terminalState.kind,
4625
+ "terminal or pending_approval"
4626
+ ),
4627
+ northStarMetric(
4628
+ "target_fill",
4629
+ "Target fill",
4630
+ requestedTarget && finalRows < requestedTarget ? "review" : "pass",
4631
+ requestedTarget ? `${finalRows || 0}/${requestedTarget} final or reviewable row(s) are accounted for.` : "No requested target was reported; using sampled rows for review.",
4632
+ finalRows || sampledRows,
4633
+ requestedTarget || sampledRows
4634
+ ),
4635
+ northStarMetric(
4636
+ "unique_emails",
4637
+ "Unique emails",
4638
+ sampledRows === 0 ? "blocked" : uniqueEmails === rowsWithEmail ? "pass" : "blocked",
4639
+ sampledRows === 0 ? "No sampled rows were available for email uniqueness review." : `${uniqueEmails}/${rowsWithEmail} sampled email(s) are unique.`,
4640
+ uniqueEmails,
4641
+ rowsWithEmail
4642
+ ),
4643
+ northStarMetric(
4644
+ "unique_domains",
4645
+ "Unique domains",
4646
+ sampledRows === 0 ? "blocked" : uniqueDomains === sampledRows ? "pass" : "review",
4647
+ sampledRows === 0 ? "No sampled rows were available for domain uniqueness review." : `${uniqueDomains}/${sampledRows} sampled company domain(s) are unique.`,
4648
+ uniqueDomains,
4649
+ sampledRows
4650
+ ),
4651
+ northStarMetric(
4652
+ "verified_email_rows",
4653
+ "Verified email rows",
4654
+ sampledRows === 0 ? "blocked" : verifiedEmailRows === sampledRows ? "pass" : rowsWithEmail === sampledRows ? "review" : "blocked",
4655
+ sampledRows === 0 ? "No sampled rows were available for email verification review." : `${verifiedEmailRows}/${sampledRows} sampled row(s) carry verified-email evidence.`,
4656
+ verifiedEmailRows,
4657
+ sampledRows
4658
+ ),
4659
+ northStarMetric(
4660
+ "copy_completeness",
4661
+ "Copy completeness",
4662
+ sampledRows === 0 ? "blocked" : copyReadyRows === sampledRows ? "pass" : "blocked",
4663
+ sampledRows === 0 ? "No sampled rows were available for copy review." : `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.`,
4664
+ copyReadyRows,
4665
+ sampledRows
4666
+ ),
4667
+ northStarMetric(
4668
+ "copy_quality",
4669
+ "Copy QA",
4670
+ copyQaIssuesByRow.length === 0 ? "pass" : "blocked",
4671
+ copyQaIssuesByRow.length === 0 ? "No sampled copy QA blockers were detected." : `${copyQaIssuesByRow.length}/${sampledRows} sampled row(s) have copy QA issue(s): ${formatCampaignReviewIssueCounts(copyQaIssueCounts)}.`,
4672
+ copyQaIssuesByRow.length,
4673
+ 0
4674
+ ),
4675
+ northStarMetric(
4676
+ "signal_grounding",
4677
+ "Signal grounding",
4678
+ unsupportedSignalClaims > 0 ? "blocked" : signalBackedRows > 0 ? "pass" : "review",
4679
+ 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.",
4680
+ signalBackedRows,
4681
+ sampledRows
4682
+ ),
4683
+ northStarMetric(
4684
+ "approval_locks",
4685
+ "Approval locks",
4686
+ approvalLockedRows > 0 || deliveredRows === 0 ? "pass" : "review",
4687
+ 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.",
4688
+ approvalLockedRows,
4689
+ sampledRows
4690
+ ),
4691
+ northStarMetric(
4692
+ "export_send_safety",
4693
+ "Export/send safety",
4694
+ deliveredRows > 0 && status.status !== "completed" ? "blocked" : "pass",
4695
+ deliveredRows > 0 ? `${deliveredRows} row(s) are delivered; status is ${status.status}.` : "No delivered rows are reported before approval.",
4696
+ deliveredRows,
4697
+ 0
4698
+ ),
4699
+ northStarMetric(
4700
+ "holdout_attribution",
4701
+ "Holdout attribution",
4702
+ holdoutRows > 0 ? "pass" : "review",
4703
+ holdoutRows > 0 ? `${holdoutRows}/${sampledRows} sampled row(s) preserve holdout attribution.` : "No holdout attribution was visible in the sampled rows.",
4704
+ holdoutRows,
4705
+ sampledRows
4706
+ ),
4707
+ northStarMetric(
4708
+ "feedback_readiness",
4709
+ "Feedback readiness",
4710
+ feedbackReady ? "pass" : feedbackRequiredForApproval ? "blocked" : "review",
4711
+ 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.",
4712
+ feedbackReady,
4713
+ true
4714
+ ),
4715
+ northStarMetric(
4716
+ "causal_moat",
4717
+ "Causal moat",
4718
+ moat.state === "proved" ? "pass" : "review",
4719
+ moat.detail,
4720
+ moat.state,
4721
+ "proved"
4722
+ ),
4723
+ northStarMetric(
4724
+ "artifact_readback",
4725
+ "Artifact readback",
4726
+ artifactCount > 0 ? "pass" : "review",
4727
+ artifactCount > 0 ? `${artifactCount} artifact(s) are available for review.` : "No review artifact is available yet.",
4728
+ artifactCount,
4729
+ 1
4730
+ )
4731
+ ];
4732
+ const gradedMetrics = metrics.filter((metric) => metric.status !== "pending");
4733
+ const score = gradedMetrics.length === 0 ? null : Math.round(gradedMetrics.filter((metric) => metric.status === "pass").length / gradedMetrics.length * 100);
4734
+ const blockers = metrics.filter((metric) => metric.status === "blocked").map((metric) => metric.detail);
4735
+ const warnings = metrics.filter((metric) => metric.status === "review").map((metric) => metric.detail);
4736
+ const scorecardStatus = blockers.length > 0 ? "blocked" : warnings.length > 0 ? "review" : "pass";
4737
+ return {
4738
+ version: "campaign-builder-north-star.v1",
4739
+ status: scorecardStatus,
4740
+ score,
4741
+ moatState: moat.state,
4742
+ metrics,
4743
+ blockers,
4744
+ warnings,
4745
+ nextAction: scorecardStatus === "blocked" ? "Fix blocked North Star gates before approval or delivery." : terminalState.safeNextAction
4746
+ };
4747
+ }
3974
4748
  function createCampaignBuilderBuildReview(status, rows, artifacts) {
4749
+ const terminalState = status.terminalState ?? deriveCampaignBuildTerminalState({
4750
+ status: status.status,
4751
+ phase: status.currentPhase,
4752
+ phaseStatus: status.currentPhaseStatus,
4753
+ staleRunningPhase: status.staleRunningPhase === true,
4754
+ phaseAgeSeconds: status.phaseAgeSeconds ?? null,
4755
+ nextPollAfterSeconds: status.nextPollAfterSeconds ?? null,
4756
+ terminalReason: status.terminalReason ?? status.customerRowCounts?.terminalReason ?? null,
4757
+ staleReason: status.staleReason ?? null,
4758
+ nextAction: status.nextAction,
4759
+ approvalAction: status.approvalAction,
4760
+ triggerRunId: status.triggerRunId ?? null
4761
+ });
3975
4762
  const sampledRows = rows.rows.length;
3976
4763
  const availableRows = rows.count || sampledRows;
3977
4764
  const qualifiedRows = rows.rows.filter((row) => row.qualified !== false).length;
3978
4765
  const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
3979
4766
  const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
3980
4767
  const artifactCount = artifacts.length || status.artifactCount || 0;
4768
+ const northStarScorecard = createCampaignBuilderNorthStarScorecard(status, rows, artifacts, terminalState);
3981
4769
  const gates = [
3982
- {
3983
- id: "build_completed",
3984
- label: "Build completed",
3985
- passed: status.status === "completed",
3986
- detail: status.status === "completed" ? "The campaign build is completed." : `The campaign build is ${status.status}; review again after completion.`
3987
- },
3988
- {
3989
- id: "rows_available",
3990
- label: "Rows available",
3991
- passed: sampledRows > 0,
3992
- detail: sampledRows > 0 ? `${sampledRows} sampled row(s) are available for review.` : "No rows were returned for review."
3993
- },
3994
- {
3995
- id: "qualified_sample",
3996
- label: "Sample rows qualified",
3997
- passed: sampledRows > 0 && qualifiedRows === sampledRows,
3998
- detail: sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
3999
- },
4000
- {
4001
- id: "email_coverage",
4002
- label: "Email coverage",
4003
- passed: sampledRows > 0 && rowsWithEmail === sampledRows,
4004
- detail: sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
4005
- },
4006
- {
4007
- id: "artifact_available",
4008
- label: "Artifact available",
4009
- passed: artifactCount > 0,
4010
- detail: artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
4011
- },
4012
- {
4013
- id: "no_failed_rows",
4014
- label: "No failed rows",
4015
- passed: status.recordsFailed === 0,
4016
- detail: status.recordsFailed === 0 ? "The build reports zero failed rows." : `${status.recordsFailed} row(s) failed during the build.`
4017
- }
4770
+ campaignReviewGate(
4771
+ "build_reviewable",
4772
+ "Build reviewable",
4773
+ terminalState.kind === "running_healthy" ? "pending" : terminalState.kind === "running_stale" || terminalState.kind === "blocked" ? "blocked" : "pass",
4774
+ terminalState.label
4775
+ ),
4776
+ campaignReviewGate(
4777
+ "rows_available",
4778
+ "Rows available",
4779
+ sampledRows > 0 ? "pass" : terminalState.reviewable ? "blocked" : "pending",
4780
+ 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."
4781
+ ),
4782
+ campaignReviewGate(
4783
+ "qualified_sample",
4784
+ "Sample rows qualified",
4785
+ sampledRows === 0 ? terminalState.reviewable ? "blocked" : "pending" : qualifiedRows === sampledRows ? "pass" : "blocked",
4786
+ sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
4787
+ ),
4788
+ campaignReviewGate(
4789
+ "email_coverage",
4790
+ "Email coverage",
4791
+ sampledRows === 0 ? terminalState.reviewable ? "blocked" : "pending" : rowsWithEmail === sampledRows ? "pass" : "blocked",
4792
+ sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
4793
+ ),
4794
+ campaignReviewGate(
4795
+ "artifact_available",
4796
+ "Artifact available",
4797
+ artifactCount > 0 ? "pass" : terminalState.reviewable ? "review" : "pending",
4798
+ artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
4799
+ ),
4800
+ campaignReviewGate(
4801
+ "no_failed_rows",
4802
+ "No failed rows",
4803
+ status.recordsFailed === 0 ? "pass" : "blocked",
4804
+ status.recordsFailed === 0 ? "The build reports zero failed rows." : `${status.recordsFailed} row(s) failed during the build.`
4805
+ )
4018
4806
  ];
4019
- const blockedReasons = gates.filter((gate) => !gate.passed).map((gate) => gate.detail);
4807
+ const blockedReasons = gates.filter((gate) => gate.status === "blocked" || !gate.passed).map((gate) => gate.detail);
4020
4808
  const warnings = [
4021
4809
  rows.hasMore ? "This review is based on a row sample; page through remaining rows before final approval." : void 0,
4022
4810
  sampledRows > 0 && copyReadyRows < sampledRows ? `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.` : void 0,
4023
4811
  ...status.warnings
4024
4812
  ].filter((item) => Boolean(item));
4025
- const readyForDeliveryApproval = blockedReasons.length === 0;
4813
+ const readyForDeliveryApproval = terminalState.reviewable && blockedReasons.length === 0 && northStarScorecard.status !== "blocked";
4026
4814
  return {
4027
4815
  campaignBuildId: status.campaignBuildId || rows.campaignBuildId,
4028
4816
  status,
4029
4817
  rows,
4030
4818
  artifacts,
4031
4819
  gates,
4820
+ northStarScorecard,
4032
4821
  summary: {
4033
4822
  status: status.status,
4034
4823
  phase: status.currentPhase,
4824
+ terminalState: terminalState.kind,
4825
+ scorecardStatus: northStarScorecard.status,
4826
+ scorecardScore: northStarScorecard.score,
4035
4827
  sampledRows,
4036
4828
  availableRows,
4037
4829
  qualifiedRows,
@@ -4085,6 +4877,184 @@ function campaignReviewRowHasCopy(row) {
4085
4877
  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)
4086
4878
  );
4087
4879
  }
4880
+ function campaignReviewDataEmail(data) {
4881
+ return stringValue(data.email) || stringValue(data.work_email) || stringValue(data.workEmail) || stringValue(asRecord(data.contact).email);
4882
+ }
4883
+ function campaignReviewDataDomain(data) {
4884
+ const domain = stringValue(data.company_domain) || stringValue(data.companyDomain) || stringValue(asRecord(data.company).domain);
4885
+ return domain?.toLowerCase();
4886
+ }
4887
+ function campaignReviewDataHasVerifiedEmail(data) {
4888
+ const status = String(data.email_status ?? data.emailStatus ?? asRecord(data.contact).email_status ?? "").toLowerCase();
4889
+ return data.email_verified === true || data.emailVerified === true || asRecord(data.contact).email_verified === true || ["valid", "validated", "verified"].includes(status);
4890
+ }
4891
+ function campaignReviewDataCopyParts(data) {
4892
+ const copy = asRecord(data.copy);
4893
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4894
+ return [
4895
+ stringValue(data.subject),
4896
+ stringValue(data.subject_line),
4897
+ stringValue(data.opener),
4898
+ stringValue(data.body),
4899
+ stringValue(data.cta),
4900
+ stringValue(copy.subject),
4901
+ stringValue(copy.opener),
4902
+ stringValue(copy.body),
4903
+ stringValue(copy.cta),
4904
+ stringValue(rawCopy.subject),
4905
+ stringValue(rawCopy.opener),
4906
+ stringValue(rawCopy.body),
4907
+ stringValue(rawCopy.cta)
4908
+ ].filter((part) => Boolean(part));
4909
+ }
4910
+ function campaignReviewDataHasBadGreeting(data) {
4911
+ return campaignReviewDataCopyParts(data).some(
4912
+ (part) => /\bhi\s*(?:,|\{\{|\[)|\bhello\s*(?:,|\{\{|\[)/i.test(part) || /\b(first_name|firstname|name)\b/i.test(part)
4913
+ );
4914
+ }
4915
+ function campaignReviewDataHasBannedPhrase(data) {
4916
+ const text = campaignReviewDataCopyParts(data).join(" ").toLowerCase();
4917
+ return NORTH_STAR_BANNED_COPY_PHRASES.some((phrase) => text.includes(phrase));
4918
+ }
4919
+ function campaignReviewDataSubject(data) {
4920
+ const copy = asRecord(data.copy);
4921
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4922
+ return stringValue(data.subject) || stringValue(data.subject_line) || stringValue(data.subjectLine) || stringValue(copy.subject) || stringValue(rawCopy.subject);
4923
+ }
4924
+ function campaignReviewDataCta(data) {
4925
+ const copy = asRecord(data.copy);
4926
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4927
+ return stringValue(data.cta) || stringValue(copy.cta) || stringValue(rawCopy.cta);
4928
+ }
4929
+ function countCampaignReviewIssues(issueRows) {
4930
+ return issueRows.flat().reduce((acc, issue) => {
4931
+ acc[issue] = (acc[issue] || 0) + 1;
4932
+ return acc;
4933
+ }, {});
4934
+ }
4935
+ function formatCampaignReviewIssueCounts(counts) {
4936
+ const entries = Object.entries(counts);
4937
+ return entries.length > 0 ? entries.map(([issue, count]) => `${issue}=${count}`).join(", ") : "none";
4938
+ }
4939
+ function campaignReviewDataCopyQaIssues(data) {
4940
+ const issues = /* @__PURE__ */ new Set();
4941
+ const copyParts = campaignReviewDataCopyParts(data);
4942
+ if (copyParts.length === 0) return [];
4943
+ if (campaignReviewDataHasBadGreeting(data)) issues.add("bad_greeting");
4944
+ if (campaignReviewDataHasBannedPhrase(data)) issues.add("banned_or_vague_phrase");
4945
+ if (campaignReviewDataHasUnsupportedSignalClaim(data)) issues.add("unsupported_signal_claim");
4946
+ if (campaignReviewDataPersonalizationBasis(data).length === 0) issues.add("missing_personalization_basis");
4947
+ if (!campaignReviewDataCta(data)) issues.add("missing_cta");
4948
+ const subject = campaignReviewDataSubject(data);
4949
+ if (subject && subject.length > 70) issues.add("overlong_subject");
4950
+ return [...issues];
4951
+ }
4952
+ function campaignReviewDataPersonalizationBasis(data) {
4953
+ const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
4954
+ const copy = asRecord(data.copy);
4955
+ const metadata = asRecord(data.metadata);
4956
+ const signalEvidence = asRecord(data.signal_evidence ?? metadata.signal_evidence ?? metadata.signalEvidence);
4957
+ return [
4958
+ ...arrayOfStrings(data.personalization_basis) ?? [],
4959
+ ...arrayOfStrings(data.personalizationBasis) ?? [],
4960
+ ...arrayOfStrings(copy.personalization_basis) ?? [],
4961
+ ...arrayOfStrings(rawCopy.personalization_basis) ?? [],
4962
+ ...arrayOfStrings(metadata.personalization_basis) ?? [],
4963
+ ...arrayOfStrings(metadata.personalizationBasis) ?? [],
4964
+ ...arrayOfStrings(signalEvidence.personalization_basis) ?? []
4965
+ ];
4966
+ }
4967
+ function campaignReviewDataHasNoSupportedSignal(data) {
4968
+ const metadata = asRecord(data.metadata);
4969
+ const copy = asRecord(data.copy);
4970
+ const state = [
4971
+ data.copy_signal_state,
4972
+ data.signal_basis,
4973
+ data.signal_type,
4974
+ metadata.copy_signal_state,
4975
+ metadata.signal_basis,
4976
+ metadata.signal_type,
4977
+ copy.copy_signal_state
4978
+ ].map((value) => stringValue(value)?.toLowerCase()).filter(Boolean);
4979
+ return data.no_supported_signal === true || metadata.no_supported_signal === true || state.includes("no_supported_signal");
4980
+ }
4981
+ function campaignReviewDataHasSignalBasis(data) {
4982
+ if (campaignReviewDataHasNoSupportedSignal(data)) return false;
4983
+ const metadata = asRecord(data.metadata);
4984
+ const signal = asRecord(data.signal);
4985
+ const signalEvidence = asRecord(data.signal_evidence ?? metadata.signal_evidence ?? metadata.signalEvidence);
4986
+ const basis = campaignReviewDataPersonalizationBasis(data);
4987
+ return Boolean(
4988
+ 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))
4989
+ );
4990
+ }
4991
+ function campaignReviewDataHasUnsupportedSignalClaim(data) {
4992
+ const copyText = campaignReviewDataCopyParts(data).join(" ").toLowerCase();
4993
+ const claimsSignal = /\b(hiring|funding|raised|launched|expanding|appointed|announced|recent news|new role|job posting)\b/.test(copyText);
4994
+ return claimsSignal && !campaignReviewDataHasSignalBasis(data);
4995
+ }
4996
+ function campaignReviewDataHasApprovalLock(data) {
4997
+ const metadata = asRecord(data.fit_metadata ?? data.metadata);
4998
+ const approval = String(data.approval_status ?? data.delivery_approval_status ?? metadata.approval_status ?? "").toLowerCase();
4999
+ const exportStatus = String(data.export_status ?? data.exportStatus ?? "").toLowerCase();
5000
+ 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);
5001
+ }
5002
+ function campaignReviewDataHasHoldoutAttribution(data) {
5003
+ const fitMetadata = asRecord(data.fit_metadata ?? data.fitMetadata);
5004
+ const metadata = asRecord(data.metadata);
5005
+ const holdout = asRecord(
5006
+ fitMetadata.learning_holdout ?? fitMetadata.learningHoldout ?? metadata.learning_holdout ?? metadata.learningHoldout ?? data.learning_holdout ?? data.learningHoldout
5007
+ );
5008
+ const experimentKey = stringValue(holdout.experiment_key ?? holdout.experimentKey ?? data.experiment_key ?? data.experimentKey);
5009
+ const cohort = stringValue(holdout.cohort_role ?? holdout.cohortRole ?? data.cohort_role ?? data.learning_cohort ?? data.learningCohort);
5010
+ const brainApplied = holdout.brain_applied ?? holdout.brainApplied ?? data.brain_applied ?? data.signaliz_brain_applied;
5011
+ return Boolean(experimentKey && cohort && brainApplied !== void 0);
5012
+ }
5013
+ function campaignReviewStatusHasFeedbackReadiness(status) {
5014
+ const diagnostics = asRecord(status.diagnostics);
5015
+ const feedbackReadiness = asRecord(diagnostics.feedback_readiness ?? diagnostics.feedbackReadiness);
5016
+ const feedbackSubstrate = asRecord(diagnostics.feedback_substrate ?? diagnostics.feedbackSubstrate);
5017
+ const evidenceCounts = asRecord(diagnostics.evidence_counts ?? diagnostics.evidenceCounts);
5018
+ return feedbackReadiness.ready === true || feedbackReadiness.webhook_configured === true || feedbackSubstrate.stage === "ready" || Number(evidenceCounts.feedback_events ?? 0) > 0;
5019
+ }
5020
+ function campaignReviewMoatState(status, holdoutRows) {
5021
+ const diagnostics = asRecord(status.diagnostics);
5022
+ const evidenceCounts = asRecord(diagnostics.evidence_counts ?? diagnostics.evidenceCounts);
5023
+ const embeddedHoldout = asRecord(diagnostics.embedded_holdout ?? diagnostics.embeddedHoldout);
5024
+ const holdoutLift = asRecord(diagnostics.holdout_lift ?? diagnostics.holdoutLift ?? diagnostics.holdout_measurement ?? diagnostics.holdoutMeasurement);
5025
+ const controlSample = Math.max(
5026
+ Number(evidenceCounts.holdout_control_sample_size ?? 0),
5027
+ Number(evidenceCounts.embedded_holdout_control_sample ?? 0),
5028
+ Number(embeddedHoldout.control_sample_size ?? 0)
5029
+ );
5030
+ const learnedSample = Math.max(
5031
+ Number(evidenceCounts.holdout_learned_sample_size ?? 0),
5032
+ Number(evidenceCounts.embedded_holdout_learned_sample ?? 0),
5033
+ Number(embeddedHoldout.learned_sample_size ?? 0)
5034
+ );
5035
+ const feedbackEvents = Math.max(
5036
+ Number(evidenceCounts.feedback_events ?? 0),
5037
+ Number(evidenceCounts.campaign_build_feedback_events ?? 0),
5038
+ Number(evidenceCounts.delivery_holdout_sent_outcomes ?? 0)
5039
+ );
5040
+ const proved = diagnostics.closed_loop_proof_ready === true || holdoutLift.honest_measurement === true && holdoutLift.positive_lift === true;
5041
+ if (proved) {
5042
+ return {
5043
+ state: "proved",
5044
+ detail: "Real attributed feedback shows positive lift for explicit control and learned cohorts."
5045
+ };
5046
+ }
5047
+ if (controlSample > 0 && learnedSample > 0 && feedbackEvents > 0) {
5048
+ return {
5049
+ state: "moved_needs_lift_volume",
5050
+ detail: `Control and learned cohorts have attributed feedback (${controlSample} control, ${learnedSample} learned, ${feedbackEvents} feedback event(s)); keep collecting lift volume before claiming proof.`
5051
+ };
5052
+ }
5053
+ return {
5054
+ state: "scaffolding_ready",
5055
+ 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."
5056
+ };
5057
+ }
4088
5058
  function createCampaignBuilderReadinessLane(route, plan) {
4089
5059
  const builtIn = campaignBuilderBuiltInForRoute(route);
4090
5060
  const customRoute = route.provider !== "signaliz" && route.provider !== "csv" && builtIn === void 0;
@@ -4126,6 +5096,8 @@ function readinessLaneLabel(route, builtIn) {
4126
5096
  const labels = {
4127
5097
  lead_generation: "Signaliz lead generation",
4128
5098
  local_leads: "Signaliz local leads",
5099
+ company_discovery: "Signaliz company discovery",
5100
+ people_discovery: "Signaliz people discovery",
4129
5101
  email_finding: "Signaliz email finding",
4130
5102
  email_verification: "Signaliz email verification",
4131
5103
  signals: "Signaliz signals"
@@ -4210,6 +5182,35 @@ function summarizeCampaignBuilderProofDryRun(dryRunResult) {
4210
5182
  plannedTargetCount: dryRunResult.plannedTargetCount ?? dryRunResult.planned_target_count ?? null
4211
5183
  };
4212
5184
  }
5185
+ function summarizeCampaignBuilderProofLearnBack(plan) {
5186
+ if (Object.keys(plan).length === 0) return { ready: false };
5187
+ const memoryPolicy = asRecord(plan.memory_write_policy);
5188
+ const memoryItemsPolicy = asRecord(memoryPolicy.gtm_memory_items);
5189
+ const sequence = Array.isArray(plan.post_build_sequence) ? plan.post_build_sequence.map((step) => {
5190
+ const record = asRecord(step);
5191
+ return compact({
5192
+ step: record.step,
5193
+ tool: stringValue(record.tool),
5194
+ approval_boundary: stringValue(record.approval_boundary),
5195
+ reason: stringValue(record.reason)
5196
+ });
5197
+ }).filter((step) => typeof step.tool === "string") : [];
5198
+ return {
5199
+ ready: sequence.length > 0,
5200
+ source_tool: firstNonEmptyString(plan.source_tool, plan.sourceTool),
5201
+ post_build_sequence: sequence,
5202
+ memory_write_policy: {
5203
+ target_tables: Array.isArray(memoryPolicy.target_tables) ? memoryPolicy.target_tables : [],
5204
+ gtm_memory_items: {
5205
+ shareable: memoryItemsPolicy.shareable === false ? false : memoryItemsPolicy.shareable ?? null,
5206
+ allowed_redaction_states: Array.isArray(memoryItemsPolicy.allowed_redaction_states) ? memoryItemsPolicy.allowed_redaction_states : [],
5207
+ raw_private_fields_withheld: Array.isArray(memoryItemsPolicy.raw_private_fields_withheld) ? memoryItemsPolicy.raw_private_fields_withheld : []
5208
+ }
5209
+ },
5210
+ approval_policy: firstNonEmptyString(plan.approval_policy, plan.approvalPolicy),
5211
+ privacy_policy: firstNonEmptyString(plan.privacy_policy, plan.privacyPolicy)
5212
+ };
5213
+ }
4213
5214
  function firstNonEmptyString(...values) {
4214
5215
  for (const value of values) {
4215
5216
  if (typeof value === "string" && value.trim()) return value;
@@ -4692,14 +5693,87 @@ var Ops = class {
4692
5693
  const body = typeof params === "string" ? { prompt: params } : buildOpsCreateBody(params);
4693
5694
  return normalizeOpsCreateResult(withExecutionRefs(await this.call("ops_create", body)));
4694
5695
  }
5696
+ async schedule(params) {
5697
+ const createBody = typeof params === "string" ? { prompt: params, cadence: "daily" } : { ...buildOpsCreateBody(params), cadence: params.cadence ?? "daily" };
5698
+ const { activate: _activate, ...body } = createBody;
5699
+ return normalizeOpsCreateResult(withExecutionRefs(await this.call("ops_schedule", body)));
5700
+ }
5701
+ async scheduleAndWait(params) {
5702
+ const options = typeof params === "string" ? { schedule: { prompt: params, cadence: "daily" }, run: {}, wait: {} } : buildOpsScheduleAndWaitOptions(params);
5703
+ const raw = await this.call("ops_schedule_and_wait", buildOpsScheduleAndWaitBody(options));
5704
+ return normalizeOpsScheduleAndWaitCallResult(raw, options.wait, {
5705
+ approvalMessage: "ops.scheduleAndWait requires an approved schedulable Op. Retry with confirmSpend=true after reviewing approval details.",
5706
+ notCreatedMessage: "ops.scheduleAndWait requires ops_schedule_and_wait to create an op_id. Use ops.schedule for approval review flows."
5707
+ });
5708
+ }
5709
+ /** Schedule a recurring Op that delivers through a customer-owned Nango API connection. */
5710
+ async scheduleNangoAction(input) {
5711
+ return normalizeOpsCreateResult(withExecutionRefs(await this.call("ops_nango_schedule", buildOpsNangoScheduleBody(input))));
5712
+ }
5713
+ /** Schedule a recurring Nango-backed Op, run the first tick, and return result readback. */
5714
+ async scheduleNangoActionAndWait(input) {
5715
+ const options = buildOpsNangoScheduleAndWaitOptions(input);
5716
+ const raw = await this.call("ops_nango_schedule_and_wait", buildOpsNangoScheduleAndWaitBody(options));
5717
+ return normalizeOpsScheduleAndWaitCallResult(raw, options.wait, {
5718
+ approvalMessage: "ops.scheduleNangoActionAndWait requires an approved Nango-backed Op. Retry with confirmSpend=true after reviewing approval details.",
5719
+ notCreatedMessage: "ops.scheduleNangoActionAndWait requires ops_nango_schedule_and_wait to create an op_id. Use ops.scheduleNangoAction for approval review flows."
5720
+ });
5721
+ }
4695
5722
  async execute(params) {
4696
5723
  const body = typeof params === "string" ? { prompt: params } : buildOpsExecuteBody(params);
4697
5724
  return normalizeOpsExecuteResult(withExecutionRefs(await this.call("ops_execute", body)));
4698
5725
  }
5726
+ async executeAndWait(params) {
5727
+ const options = typeof params === "string" ? { execute: { prompt: params, auto_run: true }, wait: {} } : buildOpsExecuteAndWaitOptions(params);
5728
+ const execute = await this.execute(options.execute);
5729
+ if (execute.approval_required || execute.error_code === "APPROVAL_REQUIRED") {
5730
+ throw new SignalizError({
5731
+ code: "APPROVAL_REQUIRED",
5732
+ errorType: "validation",
5733
+ message: "ops.executeAndWait requires an approved executable Op. Retry with confirmSpend=true after reviewing approval details.",
5734
+ details: { execute }
5735
+ });
5736
+ }
5737
+ if (!execute.op_id) {
5738
+ throw new SignalizError({
5739
+ code: "OP_NOT_CREATED",
5740
+ errorType: "validation",
5741
+ message: "ops.executeAndWait requires ops_execute to create an op_id. Use ops.execute for dry runs or approval review flows.",
5742
+ details: { execute }
5743
+ });
5744
+ }
5745
+ const waited = await this.waitForResults({
5746
+ ...options.wait,
5747
+ op_id: execute.op_id
5748
+ });
5749
+ const runId = execute.run_id ?? execute.runId ?? waited.status.run_id ?? waited.status.runId ?? null;
5750
+ return {
5751
+ ...waited,
5752
+ execute,
5753
+ run_id: runId,
5754
+ runId,
5755
+ execution_refs: mergeExecutionRefsFrom([execute, waited])
5756
+ };
5757
+ }
4699
5758
  async run(params) {
4700
5759
  const body = typeof params === "string" ? { op_id: params } : buildOpsRunBody(params);
4701
5760
  return normalizeOpsRunResult(withExecutionRefs(await this.call("ops_run", body)));
4702
5761
  }
5762
+ async runAndWait(params) {
5763
+ const options = typeof params === "string" ? { op_id: params } : buildOpsRunAndWaitOptions(params);
5764
+ if (!options.op_id) throw new Error("ops.runAndWait requires op_id or opId");
5765
+ const run = await this.run({
5766
+ op_id: options.op_id,
5767
+ instruction: options.instruction,
5768
+ force: options.force
5769
+ });
5770
+ const waited = await this.waitForResults(options);
5771
+ return {
5772
+ ...waited,
5773
+ run,
5774
+ execution_refs: mergeExecutionRefsFrom([run, waited])
5775
+ };
5776
+ }
4703
5777
  async status(params) {
4704
5778
  const body = typeof params === "string" ? { op_id: params } : buildOpsStatusBody(params);
4705
5779
  return normalizeOpsStatusResult(withExecutionRefs(await this.call("ops_status", body)));
@@ -4708,21 +5782,188 @@ var Ops = class {
4708
5782
  const body = typeof params === "string" ? { op_id: params } : buildOpsResultsBody(params);
4709
5783
  return normalizeOpsResultsResult(withExecutionRefs(await this.call("ops_results", body)));
4710
5784
  }
4711
- async proof(params = {}) {
4712
- const data = await this.call("ops_proof", {
4713
- window_hours: params.windowHours,
4714
- output_format: "json"
4715
- });
4716
- return normalizeOpsProofResult(data);
5785
+ async waitForResults(params) {
5786
+ const options = typeof params === "string" ? { op_id: params } : buildOpsWaitForResultsOptions(params);
5787
+ if (!options.op_id) throw new Error("ops.waitForResults requires op_id or opId");
5788
+ const waitResult = await this.call("ops_wait", buildOpsWaitBody(options));
5789
+ return normalizeOpsWaitForResultsResult(waitResult, options);
4717
5790
  }
4718
- async debug(params) {
4719
- const options = typeof params === "string" ? { op_id: params } : buildOpsDebugOptions(params);
4720
- const diagnosticErrors = [];
4721
- const status = await this.status(options.op_id);
4722
- const refs = /* @__PURE__ */ new Map();
4723
- const addRefs = (items) => {
4724
- for (const ref of items ?? []) refs.set(`${ref.kind}:${ref.id}`, ref);
4725
- };
5791
+ /** Create a short-lived Nango Connect session from the Ops namespace. */
5792
+ async createNangoConnectSession(input) {
5793
+ return this.call("nango_connect_session_create", {
5794
+ provider_id: input.providerId,
5795
+ integration_id: input.integrationId,
5796
+ provider_display_name: input.providerDisplayName,
5797
+ integration_display_name: input.integrationDisplayName,
5798
+ allowed_integrations: input.allowedIntegrations,
5799
+ surface: input.surface,
5800
+ source: input.source,
5801
+ user_email: input.userEmail,
5802
+ user_display_name: input.userDisplayName
5803
+ });
5804
+ }
5805
+ /** Prepare the full Nango provider search, connect, pull/export, Ops, and route flow. */
5806
+ async prepareNangoIntegrationFlow(input = {}) {
5807
+ return this.call("nango_integration_flow_prepare", {
5808
+ query: input.query,
5809
+ search: input.search,
5810
+ provider_id: input.providerId,
5811
+ provider: input.provider,
5812
+ integration_id: input.integrationId,
5813
+ provider_config_key: input.providerConfigKey,
5814
+ workspace_connection_id: input.workspaceConnectionId,
5815
+ connection_id: input.connectionId,
5816
+ nango_connection_id: input.nangoConnectionId,
5817
+ intent: input.intent,
5818
+ action_name: input.actionName,
5819
+ tool_name: input.toolName,
5820
+ proxy_path: input.proxyPath,
5821
+ method: input.method,
5822
+ input: input.input,
5823
+ cadence: input.cadence,
5824
+ field_map: input.fieldMap,
5825
+ required_fields: input.requiredFields,
5826
+ confirm_spend: input.confirmSpend,
5827
+ write_confirmed: input.writeConfirmed,
5828
+ layer: input.layer,
5829
+ campaign_id: input.campaignId,
5830
+ limit: input.limit,
5831
+ include_provider_catalog: input.includeProviderCatalog
5832
+ });
5833
+ }
5834
+ /** List Nango-backed action tools for a workspace connection without executing them. */
5835
+ async listNangoTools(options = {}) {
5836
+ return this.call("nango_mcp_tools_list", {
5837
+ workspace_connection_id: options.workspaceConnectionId,
5838
+ connection_id: options.connectionId,
5839
+ provider_config_key: options.providerConfigKey,
5840
+ integration_id: options.integrationId,
5841
+ nango_connection_id: options.nangoConnectionId,
5842
+ format: options.format,
5843
+ include_raw: options.includeRaw
5844
+ });
5845
+ }
5846
+ /** Audit connected Nango rows, action/proxy readiness, and read/write proof gaps. */
5847
+ async proveNangoReadWrite(options = {}) {
5848
+ return this.call("nango_mcp_read_write_audit", {
5849
+ workspace_connection_id: options.workspaceConnectionId,
5850
+ connection_id: options.connectionId,
5851
+ provider_config_key: options.providerConfigKey,
5852
+ integration_id: options.integrationId,
5853
+ nango_connection_id: options.nangoConnectionId,
5854
+ limit: options.limit,
5855
+ include_raw: options.includeRaw,
5856
+ read_proxy_path: options.readProxyPath,
5857
+ write_proxy_path: options.writeProxyPath,
5858
+ method: options.method,
5859
+ execute_read_probe: options.executeReadProbe,
5860
+ execute_write_probe: options.executeWriteProbe,
5861
+ confirm: options.confirm,
5862
+ confirm_write: options.confirmWrite,
5863
+ input: options.input,
5864
+ write_input: options.writeInput
5865
+ });
5866
+ }
5867
+ /** Dry-run or execute a Nango-backed action tool through the approval-aware MCP bridge. */
5868
+ async callNangoTool(input) {
5869
+ return this.call("nango_mcp_tool_call", {
5870
+ workspace_connection_id: input.workspaceConnectionId,
5871
+ connection_id: input.connectionId,
5872
+ provider_config_key: input.providerConfigKey,
5873
+ integration_id: input.integrationId,
5874
+ nango_connection_id: input.nangoConnectionId,
5875
+ action_name: input.actionName,
5876
+ tool_name: input.toolName,
5877
+ delivery_mode: input.deliveryMode,
5878
+ proxy_path: input.proxyPath,
5879
+ nango_proxy_path: input.nangoProxyPath,
5880
+ method: input.method,
5881
+ http_method: input.httpMethod,
5882
+ proxy_headers: input.proxyHeaders,
5883
+ input: input.input,
5884
+ async: input.async,
5885
+ max_retries: input.maxRetries,
5886
+ dry_run: input.dryRun,
5887
+ confirm: input.confirm,
5888
+ confirm_write: input.confirmWrite
5889
+ });
5890
+ }
5891
+ /** Execute a Nango action and poll its async result when a status URL/action id is returned. */
5892
+ async callNangoToolAndWait(input) {
5893
+ const result = await this.call("nango_mcp_tool_call_and_wait", {
5894
+ workspace_connection_id: input.workspaceConnectionId,
5895
+ connection_id: input.connectionId,
5896
+ provider_config_key: input.providerConfigKey,
5897
+ integration_id: input.integrationId,
5898
+ nango_connection_id: input.nangoConnectionId,
5899
+ action_name: input.actionName,
5900
+ tool_name: input.toolName,
5901
+ delivery_mode: input.deliveryMode,
5902
+ proxy_path: input.proxyPath,
5903
+ nango_proxy_path: input.nangoProxyPath,
5904
+ method: input.method,
5905
+ http_method: input.httpMethod,
5906
+ proxy_headers: input.proxyHeaders,
5907
+ input: input.input,
5908
+ async: input.async ?? true,
5909
+ max_retries: input.maxRetries,
5910
+ dry_run: input.dryRun,
5911
+ confirm: input.confirm,
5912
+ confirm_write: input.confirmWrite,
5913
+ interval_ms: input.intervalMs,
5914
+ max_polls: input.maxPolls,
5915
+ timeout_ms: input.timeoutMs
5916
+ });
5917
+ const packet = asRecord2(result);
5918
+ const actionId = optionalString(packet.actionId) ?? optionalString(packet.action_id);
5919
+ const statusUrl = optionalString(packet.statusUrl) ?? optionalString(packet.status_url);
5920
+ const maxPolls = Number(packet.maxPolls ?? packet.max_polls ?? 0);
5921
+ const intervalMs = Number(packet.intervalMs ?? packet.interval_ms ?? 0);
5922
+ const timeoutMs = Number(packet.timeoutMs ?? packet.timeout_ms ?? 0);
5923
+ const timedOut = Boolean(packet.timedOut ?? packet.timed_out);
5924
+ return {
5925
+ ...packet,
5926
+ success: typeof packet.success === "boolean" ? packet.success : void 0,
5927
+ call: packet.call,
5928
+ result: packet.result,
5929
+ status: optionalString(packet.status),
5930
+ action_id: actionId,
5931
+ actionId,
5932
+ status_url: statusUrl,
5933
+ statusUrl,
5934
+ polls: Number(packet.polls ?? 0),
5935
+ max_polls: maxPolls,
5936
+ maxPolls,
5937
+ interval_ms: intervalMs,
5938
+ intervalMs,
5939
+ timeout_ms: timeoutMs,
5940
+ timeoutMs,
5941
+ timed_out: timedOut,
5942
+ timedOut
5943
+ };
5944
+ }
5945
+ /** Poll an async Nango action result by action id or status URL. */
5946
+ async getNangoActionResult(input) {
5947
+ return this.call("nango_mcp_action_result_get", {
5948
+ action_id: input.actionId,
5949
+ status_url: input.statusUrl
5950
+ });
5951
+ }
5952
+ async proof(params = {}) {
5953
+ const data = await this.call("ops_proof", {
5954
+ window_hours: params.windowHours,
5955
+ output_format: "json"
5956
+ });
5957
+ return normalizeOpsProofResult(data);
5958
+ }
5959
+ async debug(params) {
5960
+ const options = typeof params === "string" ? { op_id: params } : buildOpsDebugOptions(params);
5961
+ const diagnosticErrors = [];
5962
+ const status = await this.status(options.op_id);
5963
+ const refs = /* @__PURE__ */ new Map();
5964
+ const addRefs = (items) => {
5965
+ for (const ref of items ?? []) refs.set(`${ref.kind}:${ref.id}`, ref);
5966
+ };
4726
5967
  const recordError = (step, err) => {
4727
5968
  diagnosticErrors.push({
4728
5969
  step,
@@ -4734,51 +5975,57 @@ var Ops = class {
4734
5975
  addRefs(collectExecutionReferences(status.latest_run));
4735
5976
  }
4736
5977
  const runRefs = Array.from(refs.values()).filter((ref) => ref.kind === "trigger_run" || ref.kind === "dataplane_run" || ref.kind === "replay_run");
4737
- const runStatuses = [];
4738
- for (const ref of runRefs) {
4739
- try {
4740
- const result = await this.getTriggerRunStatus(ref.id);
4741
- const runs = "runs" in result ? result.runs : [result];
4742
- runStatuses.push(...runs);
4743
- for (const run of runs) addRefs(run.execution_refs);
4744
- } catch (err) {
4745
- recordError(`run-status:${ref.id}`, err);
4746
- }
4747
- }
5978
+ let runStatuses = [];
4748
5979
  let queue;
4749
- if (options.include_queue !== false) {
4750
- try {
4751
- queue = await this.getQueueStatus({ summary: true });
4752
- addRefs(queue.execution_refs);
4753
- } catch (err) {
4754
- recordError("queue", err);
4755
- }
4756
- }
4757
5980
  let dashboardRecent;
4758
- if (options.include_dashboard !== false) {
4759
- try {
4760
- dashboardRecent = await this.getDashboard({
4761
- section: "recent",
4762
- limit: options.dashboard_limit ?? 10,
4763
- timeRangeDays: options.time_range_days
4764
- });
4765
- } catch (err) {
4766
- recordError("dashboard:recent", err);
4767
- }
4768
- }
4769
5981
  let results;
4770
- if (options.include_results) {
4771
- try {
4772
- results = await this.results({
4773
- op_id: options.op_id,
4774
- limit: options.results_limit ?? 25,
4775
- include_failed_runs: true
4776
- });
4777
- addRefs(results.execution_refs);
4778
- } catch (err) {
4779
- recordError("results", err);
4780
- }
4781
- }
5982
+ await Promise.all([
5983
+ (async () => {
5984
+ if (runRefs.length === 0) return;
5985
+ try {
5986
+ const result = await this.getTriggerRunStatus({ run_ids: runRefs.map((ref) => ref.id) });
5987
+ const runs = "runs" in result ? result.runs : [result];
5988
+ runStatuses = runs;
5989
+ for (const run of runs) addRefs(run.execution_refs);
5990
+ } catch (err) {
5991
+ recordError(runRefs.length === 1 ? `run-status:${runRefs[0].id}` : "run-status:batch", err);
5992
+ }
5993
+ })(),
5994
+ (async () => {
5995
+ if (options.include_queue === false) return;
5996
+ try {
5997
+ queue = await this.getQueueStatus({ summary: true });
5998
+ addRefs(queue.execution_refs);
5999
+ } catch (err) {
6000
+ recordError("queue", err);
6001
+ }
6002
+ })(),
6003
+ (async () => {
6004
+ if (options.include_dashboard === false) return;
6005
+ try {
6006
+ dashboardRecent = await this.getDashboard({
6007
+ section: "recent",
6008
+ limit: options.dashboard_limit ?? 10,
6009
+ timeRangeDays: options.time_range_days
6010
+ });
6011
+ } catch (err) {
6012
+ recordError("dashboard:recent", err);
6013
+ }
6014
+ })(),
6015
+ (async () => {
6016
+ if (!options.include_results) return;
6017
+ try {
6018
+ results = await this.results({
6019
+ op_id: options.op_id,
6020
+ limit: options.results_limit ?? 25,
6021
+ include_failed_runs: true
6022
+ });
6023
+ addRefs(results.execution_refs);
6024
+ } catch (err) {
6025
+ recordError("results", err);
6026
+ }
6027
+ })()
6028
+ ]);
4782
6029
  const executionRefs = Array.from(refs.values());
4783
6030
  const replayCandidates = executionRefs.filter((ref) => ref.kind === "execution_event");
4784
6031
  const nextActions = buildDebugNextActions(status, executionRefs, diagnosticErrors);
@@ -4980,6 +6227,69 @@ function mergeExecutionRefs(result) {
4980
6227
  for (const ref of collectExecutionReferences(result)) add(ref);
4981
6228
  return Array.from(refs.values());
4982
6229
  }
6230
+ function mergeExecutionRefsFrom(results) {
6231
+ const refs = /* @__PURE__ */ new Map();
6232
+ for (const result of results) {
6233
+ if (!result) continue;
6234
+ for (const ref of mergeExecutionRefs(result)) refs.set(`${ref.kind}:${ref.id}`, ref);
6235
+ }
6236
+ return Array.from(refs.values());
6237
+ }
6238
+ function normalizeOpsScheduleAndWaitCallResult(raw, waitOptions, messages) {
6239
+ if (raw.approval_required || raw.approvalRequired || raw.error_code === "APPROVAL_REQUIRED" || raw.errorCode === "APPROVAL_REQUIRED") {
6240
+ throw new SignalizError({
6241
+ code: "APPROVAL_REQUIRED",
6242
+ errorType: "validation",
6243
+ message: messages.approvalMessage,
6244
+ details: { schedule: raw }
6245
+ });
6246
+ }
6247
+ const opId = stringValue2(raw.op_id ?? raw.opId);
6248
+ if (!opId) {
6249
+ throw new SignalizError({
6250
+ code: "OP_NOT_CREATED",
6251
+ errorType: "validation",
6252
+ message: messages.notCreatedMessage,
6253
+ details: { schedule: raw }
6254
+ });
6255
+ }
6256
+ const scheduleRaw = asRecord2(raw.schedule ?? raw.schedule_result ?? raw.scheduleResult);
6257
+ const runRaw = asRecord2(raw.run ?? raw.run_result ?? raw.runResult);
6258
+ const schedule = normalizeOpsCreateResult(withExecutionRefs(
6259
+ Object.keys(scheduleRaw).length > 0 ? scheduleRaw : {
6260
+ ...raw,
6261
+ op_id: opId,
6262
+ routine_id: raw.routine_id ?? raw.routineId ?? opId,
6263
+ status: "ready",
6264
+ phase: "scheduled",
6265
+ next_action: raw.next_action ?? raw.nextAction
6266
+ }
6267
+ ));
6268
+ const run = normalizeOpsRunResult(withExecutionRefs(
6269
+ Object.keys(runRaw).length > 0 ? runRaw : {
6270
+ success: raw.success !== false,
6271
+ op_id: opId,
6272
+ routine_id: raw.routine_id ?? raw.routineId ?? opId,
6273
+ run_id: raw.run_id ?? raw.runId ?? null,
6274
+ status: "running",
6275
+ phase: "queued",
6276
+ next_action: raw.next_action ?? raw.nextAction,
6277
+ results_count: 0,
6278
+ results_url: raw.results_url ?? raw.resultsUrl,
6279
+ delivery_status: raw.delivery_status ?? raw.deliveryStatus
6280
+ }
6281
+ ));
6282
+ const waited = normalizeOpsWaitForResultsResult(raw, { ...waitOptions, op_id: opId });
6283
+ const runId = run.run_id ?? run.runId ?? waited.run_id ?? waited.runId ?? waited.status.run_id ?? waited.status.runId ?? null;
6284
+ return {
6285
+ ...waited,
6286
+ schedule,
6287
+ run,
6288
+ run_id: runId,
6289
+ runId,
6290
+ execution_refs: mergeExecutionRefsFrom([schedule, run, waited])
6291
+ };
6292
+ }
4983
6293
  function normalizeOpsPrimitiveRetryPolicy(policy = {}) {
4984
6294
  return {
4985
6295
  ...policy,
@@ -5055,8 +6365,122 @@ function normalizeOpsPrimitive(primitive) {
5055
6365
  observability: normalizeOpsPrimitiveObservabilityPolicy(primitive.observability)
5056
6366
  };
5057
6367
  }
6368
+ function normalizeOpsExecutionToolCall(rawCall) {
6369
+ const call = asRecord2(rawCall);
6370
+ return {
6371
+ ...call,
6372
+ tool: stringValue2(call.tool),
6373
+ arguments: asRecord2(call.arguments),
6374
+ purpose: stringValue2(call.purpose)
6375
+ };
6376
+ }
6377
+ function normalizeOpsExecutionScheduleContract(rawSchedule) {
6378
+ const schedule = asRecord2(rawSchedule);
6379
+ const wakeOnEvents = Array.isArray(schedule.wake_on_events) ? schedule.wake_on_events.map(String) : Array.isArray(schedule.wakeOnEvents) ? schedule.wakeOnEvents.map(String) : [];
6380
+ return {
6381
+ ...schedule,
6382
+ create_tool: stringValue2(schedule.create_tool ?? schedule.createTool),
6383
+ createTool: stringValue2(schedule.createTool ?? schedule.create_tool),
6384
+ activate_tool: stringValue2(schedule.activate_tool ?? schedule.activateTool),
6385
+ activateTool: stringValue2(schedule.activateTool ?? schedule.activate_tool),
6386
+ cadence: stringValue2(schedule.cadence, "manual"),
6387
+ recurrence: stringValue2(schedule.recurrence),
6388
+ wake_on_events: wakeOnEvents,
6389
+ wakeOnEvents
6390
+ };
6391
+ }
6392
+ function normalizeOpsExecutionApprovalContract(rawApproval) {
6393
+ const approval = asRecord2(rawApproval);
6394
+ return {
6395
+ ...approval,
6396
+ required: Boolean(approval.required),
6397
+ tool: stringValue2(approval.tool),
6398
+ reason: stringValue2(approval.reason)
6399
+ };
6400
+ }
6401
+ function normalizeOpsExecutionMonitorContract(rawMonitor) {
6402
+ const monitor = asRecord2(rawMonitor);
6403
+ const pollAfterMs = numberValue(monitor.poll_after_ms ?? monitor.pollAfterMs);
6404
+ return {
6405
+ ...monitor,
6406
+ status_tool: stringValue2(monitor.status_tool ?? monitor.statusTool),
6407
+ statusTool: stringValue2(monitor.statusTool ?? monitor.status_tool),
6408
+ wait_tool: stringValue2(monitor.wait_tool ?? monitor.waitTool),
6409
+ waitTool: stringValue2(monitor.waitTool ?? monitor.wait_tool),
6410
+ results_tool: stringValue2(monitor.results_tool ?? monitor.resultsTool),
6411
+ resultsTool: stringValue2(monitor.resultsTool ?? monitor.results_tool),
6412
+ poll_after_ms: pollAfterMs,
6413
+ pollAfterMs
6414
+ };
6415
+ }
6416
+ function normalizeOpsExecutionResultContract(rawResult) {
6417
+ const result = asRecord2(rawResult);
6418
+ const shape = Array.isArray(result.shape) ? result.shape.map(String) : [];
6419
+ return {
6420
+ ...result,
6421
+ tool: stringValue2(result.tool),
6422
+ wait_tool: stringValue2(result.wait_tool ?? result.waitTool),
6423
+ waitTool: stringValue2(result.waitTool ?? result.wait_tool),
6424
+ shape,
6425
+ empty_result_recovery: stringValue2(result.empty_result_recovery ?? result.emptyResultRecovery),
6426
+ emptyResultRecovery: stringValue2(result.emptyResultRecovery ?? result.empty_result_recovery)
6427
+ };
6428
+ }
6429
+ function normalizeOpsExecutionNangoRouteContract(rawRoute) {
6430
+ if (!rawRoute || typeof rawRoute !== "object" || Array.isArray(rawRoute)) return void 0;
6431
+ const route = asRecord2(rawRoute);
6432
+ const requiredFields = Array.isArray(route.required_fields) ? route.required_fields.map(String) : Array.isArray(route.requiredFields) ? route.requiredFields.map(String) : [];
6433
+ return {
6434
+ ...route,
6435
+ enabled: route.enabled !== false,
6436
+ connect_tool: stringValue2(route.connect_tool ?? route.connectTool),
6437
+ connectTool: stringValue2(route.connectTool ?? route.connect_tool),
6438
+ discover_tool: stringValue2(route.discover_tool ?? route.discoverTool),
6439
+ discoverTool: stringValue2(route.discoverTool ?? route.discover_tool),
6440
+ dry_run_tool: stringValue2(route.dry_run_tool ?? route.dryRunTool),
6441
+ dryRunTool: stringValue2(route.dryRunTool ?? route.dry_run_tool),
6442
+ execute_tool: stringValue2(route.execute_tool ?? route.executeTool),
6443
+ executeTool: stringValue2(route.executeTool ?? route.execute_tool),
6444
+ execute_and_wait_tool: stringValue2(route.execute_and_wait_tool ?? route.executeAndWaitTool),
6445
+ executeAndWaitTool: stringValue2(route.executeAndWaitTool ?? route.execute_and_wait_tool),
6446
+ schedule_tool: stringValue2(route.schedule_tool ?? route.scheduleTool),
6447
+ scheduleTool: stringValue2(route.scheduleTool ?? route.schedule_tool),
6448
+ schedule_and_wait_tool: stringValue2(route.schedule_and_wait_tool ?? route.scheduleAndWaitTool),
6449
+ scheduleAndWaitTool: stringValue2(route.scheduleAndWaitTool ?? route.schedule_and_wait_tool),
6450
+ result_tool: stringValue2(route.result_tool ?? route.resultTool),
6451
+ resultTool: stringValue2(route.resultTool ?? route.result_tool),
6452
+ write_gate: stringValue2(route.write_gate ?? route.writeGate),
6453
+ writeGate: stringValue2(route.writeGate ?? route.write_gate),
6454
+ required_fields: requiredFields,
6455
+ requiredFields,
6456
+ schedule_arguments: asRecord2(route.schedule_arguments ?? route.scheduleArguments),
6457
+ scheduleArguments: asRecord2(route.scheduleArguments ?? route.schedule_arguments),
6458
+ schedule_and_wait_arguments: asRecord2(route.schedule_and_wait_arguments ?? route.scheduleAndWaitArguments),
6459
+ scheduleAndWaitArguments: asRecord2(route.scheduleAndWaitArguments ?? route.schedule_and_wait_arguments)
6460
+ };
6461
+ }
6462
+ function normalizeOpsExecutionContract(rawContract) {
6463
+ if (!rawContract || typeof rawContract !== "object" || Array.isArray(rawContract)) return void 0;
6464
+ const contract = asRecord2(rawContract);
6465
+ const nangoRoute = normalizeOpsExecutionNangoRouteContract(contract.nango_route ?? contract.nangoRoute);
6466
+ const sequence = Array.isArray(contract.sequence) ? contract.sequence.map(normalizeOpsExecutionToolCall) : [];
6467
+ return {
6468
+ ...contract,
6469
+ mode: stringValue2(contract.mode, "run_once"),
6470
+ run_now: normalizeOpsExecutionToolCall(contract.run_now ?? contract.runNow),
6471
+ runNow: normalizeOpsExecutionToolCall(contract.runNow ?? contract.run_now),
6472
+ schedule: normalizeOpsExecutionScheduleContract(contract.schedule),
6473
+ approval: normalizeOpsExecutionApprovalContract(contract.approval),
6474
+ monitor: normalizeOpsExecutionMonitorContract(contract.monitor),
6475
+ result_contract: normalizeOpsExecutionResultContract(contract.result_contract ?? contract.resultContract),
6476
+ resultContract: normalizeOpsExecutionResultContract(contract.resultContract ?? contract.result_contract),
6477
+ ...nangoRoute ? { nango_route: nangoRoute, nangoRoute } : {},
6478
+ sequence
6479
+ };
6480
+ }
5058
6481
  function normalizeOpsPlanResult(result) {
5059
6482
  const primitiveGraph = result.primitive_graph ?? result.primitiveGraph;
6483
+ const executionContract = normalizeOpsExecutionContract(result.execution_contract ?? result.executionContract);
5060
6484
  return {
5061
6485
  ...result,
5062
6486
  plan_id: result.plan_id ?? result.planId,
@@ -5080,7 +6504,8 @@ function normalizeOpsPlanResult(result) {
5080
6504
  destination_status: result.destination_status ?? result.destinationStatus,
5081
6505
  destinationStatus: result.destinationStatus ?? result.destination_status,
5082
6506
  primitive_graph: primitiveGraph?.map(normalizeOpsPrimitive),
5083
- primitiveGraph: primitiveGraph?.map(normalizeOpsPrimitive)
6507
+ primitiveGraph: primitiveGraph?.map(normalizeOpsPrimitive),
6508
+ ...executionContract ? { execution_contract: executionContract, executionContract } : {}
5084
6509
  };
5085
6510
  }
5086
6511
  function normalizeOpsCreateResult(result) {
@@ -5102,7 +6527,18 @@ function normalizeOpsCreateResult(result) {
5102
6527
  deliveryStatus: result.deliveryStatus ?? result.delivery_status,
5103
6528
  estimated_credits: result.estimated_credits ?? result.estimatedCredits,
5104
6529
  estimatedCredits: result.estimatedCredits ?? result.estimated_credits,
5105
- plan: result.plan ? normalizeOpsPlanResult(result.plan) : result.plan
6530
+ error_code: result.error_code ?? result.errorCode,
6531
+ errorCode: result.errorCode ?? result.error_code,
6532
+ approval_required: result.approval_required ?? result.approvalRequired,
6533
+ approvalRequired: result.approvalRequired ?? result.approval_required,
6534
+ retry_arguments: result.retry_arguments ?? result.retryArguments,
6535
+ retryArguments: result.retryArguments ?? result.retry_arguments,
6536
+ blockers: result.blockers,
6537
+ plan: result.plan ? normalizeOpsPlanResult(result.plan) : result.plan,
6538
+ next_step: result.next_step ?? result.nextStep,
6539
+ nextStep: result.nextStep ?? result.next_step,
6540
+ next_steps: result.next_steps ?? result.nextSteps,
6541
+ nextSteps: result.nextSteps ?? result.next_steps
5106
6542
  };
5107
6543
  }
5108
6544
  function normalizeOpsExecuteResult(result) {
@@ -5230,6 +6666,40 @@ function normalizeOpsStatusResult(result) {
5230
6666
  diagnosis: result.diagnosis ? normalizeOpsDebugDiagnosis(result.diagnosis) : void 0
5231
6667
  };
5232
6668
  }
6669
+ function normalizeOpsResultsSummary(value) {
6670
+ const summary = asRecord2(value);
6671
+ if (Object.keys(summary).length === 0) return void 0;
6672
+ const resultsTotal = summary.results_total ?? summary.resultsTotal;
6673
+ const nextCursor = summary.next_cursor ?? summary.nextCursor ?? null;
6674
+ const resultsUrl = summary.results_url ?? summary.resultsUrl;
6675
+ const nextAction = summary.next_action ?? summary.nextAction;
6676
+ const runId = summary.run_id ?? summary.runId ?? null;
6677
+ const creditsUsed = summary.credits_used ?? summary.creditsUsed;
6678
+ return {
6679
+ ...summary,
6680
+ label: optionalString(summary.label),
6681
+ status: optionalString(summary.status),
6682
+ phase: optionalString(summary.phase),
6683
+ results_count: numberValue(summary.results_count ?? summary.resultsCount),
6684
+ resultsCount: numberValue(summary.results_count ?? summary.resultsCount),
6685
+ results_total: resultsTotal === null || resultsTotal === void 0 ? resultsTotal : numberValue(resultsTotal),
6686
+ resultsTotal: resultsTotal === null || resultsTotal === void 0 ? resultsTotal : numberValue(resultsTotal),
6687
+ delivery_status: optionalString(summary.delivery_status ?? summary.deliveryStatus),
6688
+ deliveryStatus: optionalString(summary.delivery_status ?? summary.deliveryStatus),
6689
+ has_more: Boolean(summary.has_more ?? summary.hasMore),
6690
+ hasMore: Boolean(summary.has_more ?? summary.hasMore),
6691
+ next_cursor: nextCursor === null ? null : optionalString(nextCursor),
6692
+ nextCursor: nextCursor === null ? null : optionalString(nextCursor),
6693
+ results_url: optionalString(resultsUrl),
6694
+ resultsUrl: optionalString(resultsUrl),
6695
+ next_action: optionalString(nextAction),
6696
+ nextAction: optionalString(nextAction),
6697
+ run_id: runId === null ? null : optionalString(runId),
6698
+ runId: runId === null ? null : optionalString(runId),
6699
+ credits_used: creditsUsed === void 0 ? void 0 : numberValue(creditsUsed),
6700
+ creditsUsed: creditsUsed === void 0 ? void 0 : numberValue(creditsUsed)
6701
+ };
6702
+ }
5233
6703
  function normalizeOpsResultsResult(result) {
5234
6704
  const nextCursor = result.next_cursor ?? result.nextCursor ?? null;
5235
6705
  const hasMore = result.has_more ?? result.hasMore ?? false;
@@ -5254,7 +6724,104 @@ function normalizeOpsResultsResult(result) {
5254
6724
  results_url: result.results_url ?? result.resultsUrl,
5255
6725
  resultsUrl: result.resultsUrl ?? result.results_url,
5256
6726
  delivery_status: result.delivery_status ?? result.deliveryStatus,
5257
- deliveryStatus: result.deliveryStatus ?? result.delivery_status
6727
+ deliveryStatus: result.deliveryStatus ?? result.delivery_status,
6728
+ summary: normalizeOpsResultsSummary(result.summary)
6729
+ };
6730
+ }
6731
+ function normalizeOpsWaitForResultsPoll(value, fallbackPoll) {
6732
+ const raw = asRecord2(value);
6733
+ const checkedAt = stringValue2(raw.checked_at ?? raw.checkedAt);
6734
+ const resultsCount = raw.results_count ?? raw.resultsCount;
6735
+ const runId = raw.run_id ?? raw.runId ?? null;
6736
+ const nextAction = raw.next_action ?? raw.nextAction;
6737
+ return {
6738
+ ...raw,
6739
+ poll: numberValue(raw.poll) || fallbackPoll,
6740
+ checked_at: checkedAt,
6741
+ checkedAt,
6742
+ status: stringValue2(raw.status, "unknown"),
6743
+ phase: optionalString(raw.phase),
6744
+ results_count: resultsCount === void 0 ? void 0 : numberValue(resultsCount),
6745
+ resultsCount: resultsCount === void 0 ? void 0 : numberValue(resultsCount),
6746
+ run_id: runId === null ? null : optionalString(runId),
6747
+ runId: runId === null ? null : optionalString(runId),
6748
+ next_action: optionalString(nextAction),
6749
+ nextAction: optionalString(nextAction)
6750
+ };
6751
+ }
6752
+ function normalizeOpsWaitForResultsResult(result, options) {
6753
+ const opId = stringValue2(result.op_id ?? result.opId ?? options.op_id);
6754
+ const routineId = stringValue2(result.routine_id ?? result.routineId ?? opId);
6755
+ const runId = result.run_id ?? result.runId ?? null;
6756
+ const timedOut = Boolean(result.timed_out ?? result.timedOut);
6757
+ const statusRaw = asRecord2(result.status_result ?? result.statusResult);
6758
+ const resultsRaw = asRecord2(result.results_result ?? result.resultsResult);
6759
+ const historySource = Array.isArray(result.poll_history) ? result.poll_history : Array.isArray(result.history) ? result.history : [];
6760
+ const history = historySource.map((item, index) => normalizeOpsWaitForResultsPoll(item, index + 1));
6761
+ const status = normalizeOpsStatusResult(withExecutionRefs({
6762
+ success: result.success !== false,
6763
+ op_id: opId,
6764
+ routine_id: routineId,
6765
+ run_id: runId,
6766
+ status: stringValue2(result.status, "unknown"),
6767
+ phase: stringValue2(result.phase, "unknown"),
6768
+ next_action: stringValue2(result.next_action ?? result.nextAction),
6769
+ results_count: numberValue(result.results_count ?? result.resultsCount),
6770
+ results_url: optionalString(result.results_url ?? result.resultsUrl),
6771
+ delivery_status: optionalString(result.delivery_status ?? result.deliveryStatus),
6772
+ ...statusRaw
6773
+ }));
6774
+ const includeResults = options.include_results !== false && options.includeResults !== false;
6775
+ const hasResultsPacket = Object.keys(resultsRaw).length > 0 || Array.isArray(result.results);
6776
+ const results = includeResults && hasResultsPacket ? normalizeOpsResultsResult(withExecutionRefs({
6777
+ success: result.success !== false,
6778
+ op_id: opId,
6779
+ routine_id: routineId,
6780
+ run_id: runId,
6781
+ status: stringValue2(result.status, status.status),
6782
+ phase: stringValue2(result.phase, status.phase),
6783
+ results_count: numberValue(result.results_count ?? result.resultsCount),
6784
+ results_total: result.results_total ?? result.resultsTotal ?? null,
6785
+ results: Array.isArray(result.results) ? result.results : [],
6786
+ next_cursor: result.next_cursor ?? result.nextCursor ?? null,
6787
+ has_more: Boolean(result.has_more ?? result.hasMore),
6788
+ next_action: stringValue2(result.next_action ?? result.nextAction),
6789
+ results_url: optionalString(result.results_url ?? result.resultsUrl),
6790
+ delivery_status: optionalString(result.delivery_status ?? result.deliveryStatus),
6791
+ ...resultsRaw
6792
+ })) : void 0;
6793
+ const polls = numberValue(result.polls) || history.length;
6794
+ const maxPolls = result.max_polls ?? result.maxPolls;
6795
+ const intervalMs = result.interval_ms ?? result.intervalMs;
6796
+ const timeoutMs = result.timeout_ms ?? result.timeoutMs;
6797
+ const stopReason = result.stop_reason ?? result.stopReason;
6798
+ const nextAction = result.next_action ?? result.nextAction ?? results?.next_action ?? status.next_action;
6799
+ return {
6800
+ ...result,
6801
+ success: result.success !== false && !timedOut,
6802
+ op_id: opId,
6803
+ opId,
6804
+ routine_id: routineId,
6805
+ routineId,
6806
+ run_id: runId === null ? null : optionalString(runId),
6807
+ runId: runId === null ? null : optionalString(runId),
6808
+ status,
6809
+ results,
6810
+ timed_out: timedOut,
6811
+ timedOut,
6812
+ polls,
6813
+ max_polls: maxPolls === void 0 ? void 0 : numberValue(maxPolls),
6814
+ maxPolls: maxPolls === void 0 ? void 0 : numberValue(maxPolls),
6815
+ interval_ms: intervalMs === void 0 ? void 0 : numberValue(intervalMs),
6816
+ intervalMs: intervalMs === void 0 ? void 0 : numberValue(intervalMs),
6817
+ timeout_ms: timeoutMs === void 0 ? void 0 : numberValue(timeoutMs),
6818
+ timeoutMs: timeoutMs === void 0 ? void 0 : numberValue(timeoutMs),
6819
+ stop_reason: optionalString(stopReason),
6820
+ stopReason: optionalString(stopReason),
6821
+ history,
6822
+ next_action: optionalString(nextAction),
6823
+ nextAction: optionalString(nextAction),
6824
+ execution_refs: mergeExecutionRefsFrom([result, status, results, { history }])
5258
6825
  };
5259
6826
  }
5260
6827
  function normalizeOpsProofResult(data) {
@@ -5334,11 +6901,107 @@ function normalizeOpsProofResult(data) {
5334
6901
  raw: data
5335
6902
  };
5336
6903
  }
6904
+ function normalizeOpsAutopilotSchedulePacket(rawPacket) {
6905
+ const packet = asRecord2(rawPacket);
6906
+ return {
6907
+ ...packet,
6908
+ cadence: stringValue2(packet.cadence, "manual"),
6909
+ recurrence: stringValue2(packet.recurrence),
6910
+ activate_with: stringValue2(packet.activate_with ?? packet.activateWith),
6911
+ activateWith: stringValue2(packet.activate_with ?? packet.activateWith),
6912
+ run_now_with: stringValue2(packet.run_now_with ?? packet.runNowWith),
6913
+ runNowWith: stringValue2(packet.run_now_with ?? packet.runNowWith),
6914
+ monitor_with: stringValue2(packet.monitor_with ?? packet.monitorWith),
6915
+ monitorWith: stringValue2(packet.monitor_with ?? packet.monitorWith),
6916
+ retrieve_with: stringValue2(packet.retrieve_with ?? packet.retrieveWith),
6917
+ retrieveWith: stringValue2(packet.retrieve_with ?? packet.retrieveWith)
6918
+ };
6919
+ }
6920
+ function normalizeOpsAutopilotNangoRoutePacket(rawPacket) {
6921
+ if (!rawPacket || typeof rawPacket !== "object" || Array.isArray(rawPacket)) return void 0;
6922
+ const packet = asRecord2(rawPacket);
6923
+ const requiredFields = Array.isArray(packet.required_fields) ? packet.required_fields.map(String) : Array.isArray(packet.requiredFields) ? packet.requiredFields.map(String) : [];
6924
+ return {
6925
+ ...packet,
6926
+ enabled: packet.enabled !== false,
6927
+ route_label: stringValue2(packet.route_label ?? packet.routeLabel),
6928
+ routeLabel: stringValue2(packet.route_label ?? packet.routeLabel),
6929
+ connect_tool: stringValue2(packet.connect_tool ?? packet.connectTool),
6930
+ connectTool: stringValue2(packet.connect_tool ?? packet.connectTool),
6931
+ discover_tool: stringValue2(packet.discover_tool ?? packet.discoverTool),
6932
+ discoverTool: stringValue2(packet.discover_tool ?? packet.discoverTool),
6933
+ dry_run_tool: stringValue2(packet.dry_run_tool ?? packet.dryRunTool),
6934
+ dryRunTool: stringValue2(packet.dry_run_tool ?? packet.dryRunTool),
6935
+ execute_tool: stringValue2(packet.execute_tool ?? packet.executeTool),
6936
+ executeTool: stringValue2(packet.execute_tool ?? packet.executeTool),
6937
+ execute_and_wait_tool: stringValue2(packet.execute_and_wait_tool ?? packet.executeAndWaitTool),
6938
+ executeAndWaitTool: stringValue2(packet.execute_and_wait_tool ?? packet.executeAndWaitTool),
6939
+ schedule_tool: stringValue2(packet.schedule_tool ?? packet.scheduleTool),
6940
+ scheduleTool: stringValue2(packet.schedule_tool ?? packet.scheduleTool),
6941
+ schedule_and_wait_tool: stringValue2(packet.schedule_and_wait_tool ?? packet.scheduleAndWaitTool),
6942
+ scheduleAndWaitTool: stringValue2(packet.schedule_and_wait_tool ?? packet.scheduleAndWaitTool),
6943
+ result_tool: stringValue2(packet.result_tool ?? packet.resultTool),
6944
+ resultTool: stringValue2(packet.result_tool ?? packet.resultTool),
6945
+ write_gate: stringValue2(packet.write_gate ?? packet.writeGate),
6946
+ writeGate: stringValue2(packet.write_gate ?? packet.writeGate),
6947
+ required_fields: requiredFields,
6948
+ requiredFields,
6949
+ placeholders: asRecord2(packet.placeholders),
6950
+ dry_run_arguments: asRecord2(packet.dry_run_arguments ?? packet.dryRunArguments),
6951
+ dryRunArguments: asRecord2(packet.dry_run_arguments ?? packet.dryRunArguments),
6952
+ execute_arguments: asRecord2(packet.execute_arguments ?? packet.executeArguments),
6953
+ executeArguments: asRecord2(packet.execute_arguments ?? packet.executeArguments),
6954
+ execute_and_wait_arguments: asRecord2(packet.execute_and_wait_arguments ?? packet.executeAndWaitArguments),
6955
+ executeAndWaitArguments: asRecord2(packet.execute_and_wait_arguments ?? packet.executeAndWaitArguments),
6956
+ schedule_arguments: asRecord2(packet.schedule_arguments ?? packet.scheduleArguments),
6957
+ scheduleArguments: asRecord2(packet.schedule_arguments ?? packet.scheduleArguments),
6958
+ schedule_and_wait_arguments: asRecord2(packet.schedule_and_wait_arguments ?? packet.scheduleAndWaitArguments),
6959
+ scheduleAndWaitArguments: asRecord2(packet.schedule_and_wait_arguments ?? packet.scheduleAndWaitArguments),
6960
+ result_arguments: asRecord2(packet.result_arguments ?? packet.resultArguments),
6961
+ resultArguments: asRecord2(packet.result_arguments ?? packet.resultArguments)
6962
+ };
6963
+ }
6964
+ function normalizeOpsAutopilotResultPacket(rawPacket) {
6965
+ const packet = asRecord2(rawPacket);
6966
+ const resultShape = Array.isArray(packet.result_shape) ? packet.result_shape.map(String) : Array.isArray(packet.resultShape) ? packet.resultShape.map(String) : [];
6967
+ const deliveryReceipts = Array.isArray(packet.delivery_receipts) ? packet.delivery_receipts.map(String) : Array.isArray(packet.deliveryReceipts) ? packet.deliveryReceipts.map(String) : [];
6968
+ return {
6969
+ ...packet,
6970
+ retrieve_tool: stringValue2(packet.retrieve_tool ?? packet.retrieveTool),
6971
+ retrieveTool: stringValue2(packet.retrieve_tool ?? packet.retrieveTool),
6972
+ result_shape: resultShape,
6973
+ resultShape,
6974
+ delivery_receipts: deliveryReceipts,
6975
+ deliveryReceipts,
6976
+ empty_result_recovery: stringValue2(packet.empty_result_recovery ?? packet.emptyResultRecovery),
6977
+ emptyResultRecovery: stringValue2(packet.empty_result_recovery ?? packet.emptyResultRecovery)
6978
+ };
6979
+ }
6980
+ function normalizeOpsAutopilotOperatingPacket(rawPacket) {
6981
+ if (!rawPacket || typeof rawPacket !== "object" || Array.isArray(rawPacket)) return void 0;
6982
+ const packet = asRecord2(rawPacket);
6983
+ const nangoRoute = normalizeOpsAutopilotNangoRoutePacket(packet.nango_route ?? packet.nangoRoute);
6984
+ const approvalBoundaries = Array.isArray(packet.approval_boundaries) ? packet.approval_boundaries.map(String) : Array.isArray(packet.approvalBoundaries) ? packet.approvalBoundaries.map(String) : [];
6985
+ return {
6986
+ ...packet,
6987
+ north_star: stringValue2(packet.north_star ?? packet.northStar),
6988
+ northStar: stringValue2(packet.north_star ?? packet.northStar),
6989
+ schedule: normalizeOpsAutopilotSchedulePacket(packet.schedule),
6990
+ ...nangoRoute ? { nango_route: nangoRoute, nangoRoute } : {},
6991
+ result_contract: normalizeOpsAutopilotResultPacket(packet.result_contract ?? packet.resultContract),
6992
+ resultContract: normalizeOpsAutopilotResultPacket(packet.result_contract ?? packet.resultContract),
6993
+ approval_boundaries: approvalBoundaries,
6994
+ approvalBoundaries,
6995
+ saved_command_hint: stringValue2(packet.saved_command_hint ?? packet.savedCommandHint),
6996
+ savedCommandHint: stringValue2(packet.saved_command_hint ?? packet.savedCommandHint)
6997
+ };
6998
+ }
5337
6999
  function normalizeOpsAutopilotMotion(rawMotion) {
5338
7000
  const motion = asRecord2(rawMotion);
5339
7001
  const targetCount = numberValue(motion.target_count ?? motion.targetCount);
5340
7002
  const estimatedCredits = numberValue(motion.estimated_credits ?? motion.estimatedCredits);
5341
7003
  const mcpSequence = Array.isArray(motion.mcp_sequence) ? motion.mcp_sequence : Array.isArray(motion.mcpSequence) ? motion.mcpSequence : [];
7004
+ const operatingPacket = normalizeOpsAutopilotOperatingPacket(motion.operating_packet ?? motion.operatingPacket);
5342
7005
  return {
5343
7006
  ...motion,
5344
7007
  id: stringValue2(motion.id),
@@ -5361,12 +7024,16 @@ function normalizeOpsAutopilotMotion(rawMotion) {
5361
7024
  cli_command: optionalString(motion.cli_command ?? motion.cliCommand),
5362
7025
  cliCommand: optionalString(motion.cli_command ?? motion.cliCommand),
5363
7026
  mcp_sequence: mcpSequence,
5364
- mcpSequence
7027
+ mcpSequence,
7028
+ ...operatingPacket ? { operating_packet: operatingPacket, operatingPacket } : {}
5365
7029
  };
5366
7030
  }
5367
7031
  function normalizeOpsAutopilotResult(result) {
5368
7032
  const primary = normalizeOpsAutopilotMotion(result.primary_motion ?? result.primaryMotion);
5369
7033
  const motions = Array.isArray(result.motions) ? result.motions.map(normalizeOpsAutopilotMotion) : [];
7034
+ const operatingPacket = normalizeOpsAutopilotOperatingPacket(
7035
+ result.operating_packet ?? result.operatingPacket ?? primary.operating_packet ?? primary.operatingPacket
7036
+ );
5370
7037
  const queryErrors = Array.isArray(result.query_errors) ? result.query_errors.map(String) : Array.isArray(result.queryErrors) ? result.queryErrors.map(String) : [];
5371
7038
  return {
5372
7039
  ...result,
@@ -5382,6 +7049,7 @@ function normalizeOpsAutopilotResult(result) {
5382
7049
  scaleRule: optionalString(result.scale_rule ?? result.scaleRule),
5383
7050
  agent_handoff: optionalString(result.agent_handoff ?? result.agentHandoff),
5384
7051
  agentHandoff: optionalString(result.agent_handoff ?? result.agentHandoff),
7052
+ ...operatingPacket ? { operating_packet: operatingPacket, operatingPacket } : {},
5385
7053
  proof_state: optionalString(result.proof_state ?? result.proofState),
5386
7054
  proofState: optionalString(result.proof_state ?? result.proofState),
5387
7055
  proof_summary: result.proof_summary ?? result.proofSummary,
@@ -5646,11 +7314,125 @@ function normalizeOpsRoutineTickItemsResult(result) {
5646
7314
  hasMore
5647
7315
  };
5648
7316
  }
7317
+ function buildOpsNangoScheduleBody(input) {
7318
+ const {
7319
+ workspaceConnectionId,
7320
+ workspace_connection_id,
7321
+ connectionId,
7322
+ connection_id,
7323
+ providerConfigKey,
7324
+ provider_config_key,
7325
+ integrationId,
7326
+ integration_id,
7327
+ nangoConnectionId,
7328
+ nango_connection_id,
7329
+ actionName,
7330
+ action_name,
7331
+ toolName,
7332
+ tool_name,
7333
+ proxyPath,
7334
+ proxy_path,
7335
+ nangoProxyPath,
7336
+ nango_proxy_path,
7337
+ method,
7338
+ httpMethod,
7339
+ http_method,
7340
+ input: nangoInput,
7341
+ arguments: nangoArguments,
7342
+ fieldMap,
7343
+ field_map,
7344
+ requiredFields,
7345
+ required_fields,
7346
+ writeConfirmed,
7347
+ write_confirmed,
7348
+ agentWriteConfirmed,
7349
+ agent_write_confirmed,
7350
+ config,
7351
+ destinations: _destinations,
7352
+ ...schedule
7353
+ } = input;
7354
+ return {
7355
+ ...buildOpsCreateBody({
7356
+ ...schedule,
7357
+ cadence: schedule.cadence ?? "daily"
7358
+ }),
7359
+ workspace_connection_id: workspace_connection_id ?? workspaceConnectionId,
7360
+ connection_id: connection_id ?? connectionId,
7361
+ provider_config_key: provider_config_key ?? providerConfigKey,
7362
+ integration_id: integration_id ?? integrationId,
7363
+ nango_connection_id: nango_connection_id ?? nangoConnectionId,
7364
+ action_name: action_name ?? actionName,
7365
+ tool_name: tool_name ?? toolName,
7366
+ proxy_path: proxy_path ?? proxyPath ?? nango_proxy_path ?? nangoProxyPath,
7367
+ nango_proxy_path: nango_proxy_path ?? nangoProxyPath,
7368
+ method: method ?? http_method ?? httpMethod,
7369
+ input: nangoInput ?? nangoArguments,
7370
+ field_map: field_map ?? fieldMap,
7371
+ required_fields: required_fields ?? requiredFields,
7372
+ write_confirmed: write_confirmed ?? writeConfirmed,
7373
+ agent_write_confirmed: agent_write_confirmed ?? agentWriteConfirmed,
7374
+ config
7375
+ };
7376
+ }
7377
+ function buildOpsNangoScheduleAndWaitOptions(params) {
7378
+ const {
7379
+ force,
7380
+ instruction,
7381
+ nextCursor,
7382
+ includeFailedRuns,
7383
+ intervalMs,
7384
+ maxPolls,
7385
+ timeoutMs,
7386
+ includeResults,
7387
+ cursor,
7388
+ state,
7389
+ limit,
7390
+ include_failed_runs,
7391
+ interval_ms,
7392
+ max_polls,
7393
+ timeout_ms,
7394
+ include_results,
7395
+ ...schedule
7396
+ } = params;
7397
+ return {
7398
+ schedule: {
7399
+ ...schedule,
7400
+ cadence: schedule.cadence ?? "daily"
7401
+ },
7402
+ run: {
7403
+ force,
7404
+ instruction
7405
+ },
7406
+ wait: {
7407
+ cursor: cursor ?? nextCursor,
7408
+ state,
7409
+ limit,
7410
+ include_failed_runs: include_failed_runs ?? includeFailedRuns,
7411
+ interval_ms: interval_ms ?? intervalMs,
7412
+ max_polls: max_polls ?? maxPolls,
7413
+ timeout_ms: timeout_ms ?? timeoutMs,
7414
+ include_results: include_results ?? includeResults
7415
+ }
7416
+ };
7417
+ }
7418
+ function buildOpsNangoScheduleAndWaitBody(options) {
7419
+ const waitBody = buildOpsWaitBody(options.wait);
7420
+ const { op_id: _opId, ...waitWithoutOp } = waitBody;
7421
+ return {
7422
+ ...buildOpsNangoScheduleBody(options.schedule),
7423
+ ...waitWithoutOp,
7424
+ instruction: options.run.instruction,
7425
+ force: options.run.force
7426
+ };
7427
+ }
5649
7428
  function buildOpsPlanBody(params) {
5650
- const { targetCount, companyDomains, customAiPrompt, signalPrompt, outputPrompt, ...rest } = params;
7429
+ const { targetCount, wakeOnEvents, companyDomains, customAiPrompt, signalPrompt, outputPrompt, ...rest } = params;
7430
+ const destinations = Array.isArray(rest.destinations) ? rest.destinations.map(normalizeOpsDestinationRequest) : rest.destinations;
5651
7431
  return {
5652
7432
  ...rest,
7433
+ destinations,
5653
7434
  target_count: rest.target_count ?? targetCount,
7435
+ wake_on_events: rest.wake_on_events ?? wakeOnEvents,
5654
7436
  company_domains: rest.company_domains ?? companyDomains,
5655
7437
  custom_ai_prompt: rest.custom_ai_prompt ?? customAiPrompt,
5656
7438
  signal_prompt: rest.signal_prompt ?? signalPrompt,
@@ -5673,6 +7455,95 @@ function buildOpsExecuteBody(params) {
5673
7455
  output_format: rest.output_format ?? outputFormat
5674
7456
  };
5675
7457
  }
7458
+ function buildOpsExecuteAndWaitOptions(params) {
7459
+ const {
7460
+ nextCursor,
7461
+ includeFailedRuns,
7462
+ intervalMs,
7463
+ maxPolls,
7464
+ timeoutMs,
7465
+ includeResults,
7466
+ cursor,
7467
+ state,
7468
+ limit,
7469
+ include_failed_runs,
7470
+ interval_ms,
7471
+ max_polls,
7472
+ timeout_ms,
7473
+ include_results,
7474
+ ...execute
7475
+ } = params;
7476
+ return {
7477
+ execute: {
7478
+ ...execute,
7479
+ auto_run: execute.auto_run ?? execute.autoRun ?? true
7480
+ },
7481
+ wait: {
7482
+ cursor: cursor ?? nextCursor,
7483
+ state,
7484
+ limit,
7485
+ include_failed_runs: include_failed_runs ?? includeFailedRuns,
7486
+ interval_ms: interval_ms ?? intervalMs,
7487
+ max_polls: max_polls ?? maxPolls,
7488
+ timeout_ms: timeout_ms ?? timeoutMs,
7489
+ include_results: include_results ?? includeResults
7490
+ }
7491
+ };
7492
+ }
7493
+ function buildOpsScheduleAndWaitOptions(params) {
7494
+ const {
7495
+ force,
7496
+ instruction,
7497
+ nextCursor,
7498
+ includeFailedRuns,
7499
+ intervalMs,
7500
+ maxPolls,
7501
+ timeoutMs,
7502
+ includeResults,
7503
+ cursor,
7504
+ state,
7505
+ limit,
7506
+ include_failed_runs,
7507
+ interval_ms,
7508
+ max_polls,
7509
+ timeout_ms,
7510
+ include_results,
7511
+ ...schedule
7512
+ } = params;
7513
+ return {
7514
+ schedule: {
7515
+ ...schedule,
7516
+ cadence: schedule.cadence ?? "daily"
7517
+ },
7518
+ run: {
7519
+ force,
7520
+ instruction
7521
+ },
7522
+ wait: {
7523
+ cursor: cursor ?? nextCursor,
7524
+ state,
7525
+ limit,
7526
+ include_failed_runs: include_failed_runs ?? includeFailedRuns,
7527
+ interval_ms: interval_ms ?? intervalMs,
7528
+ max_polls: max_polls ?? maxPolls,
7529
+ timeout_ms: timeout_ms ?? timeoutMs,
7530
+ include_results: include_results ?? includeResults
7531
+ }
7532
+ };
7533
+ }
7534
+ function buildOpsScheduleAndWaitBody(options) {
7535
+ const waitBody = buildOpsWaitBody(options.wait);
7536
+ const { op_id: _opId, ...waitWithoutOp } = waitBody;
7537
+ return {
7538
+ ...buildOpsCreateBody({
7539
+ ...options.schedule,
7540
+ cadence: options.schedule.cadence ?? "daily"
7541
+ }),
7542
+ ...waitWithoutOp,
7543
+ instruction: options.run.instruction,
7544
+ force: options.run.force
7545
+ };
7546
+ }
5676
7547
  function buildOpsRunBody(params) {
5677
7548
  const { opId, ...rest } = params;
5678
7549
  return {
@@ -5680,6 +7551,28 @@ function buildOpsRunBody(params) {
5680
7551
  op_id: rest.op_id ?? opId
5681
7552
  };
5682
7553
  }
7554
+ function buildOpsRunAndWaitOptions(params) {
7555
+ const {
7556
+ opId,
7557
+ nextCursor,
7558
+ includeFailedRuns,
7559
+ intervalMs,
7560
+ maxPolls,
7561
+ timeoutMs,
7562
+ includeResults,
7563
+ ...rest
7564
+ } = params;
7565
+ return {
7566
+ ...rest,
7567
+ op_id: rest.op_id ?? opId,
7568
+ cursor: rest.cursor ?? nextCursor,
7569
+ include_failed_runs: rest.include_failed_runs ?? includeFailedRuns,
7570
+ interval_ms: rest.interval_ms ?? intervalMs,
7571
+ max_polls: rest.max_polls ?? maxPolls,
7572
+ timeout_ms: rest.timeout_ms ?? timeoutMs,
7573
+ include_results: rest.include_results ?? includeResults
7574
+ };
7575
+ }
5683
7576
  function buildOpsStatusBody(params) {
5684
7577
  const { opId, ...rest } = params;
5685
7578
  return {
@@ -5696,6 +7589,50 @@ function buildOpsResultsBody(params) {
5696
7589
  include_failed_runs: rest.include_failed_runs ?? includeFailedRuns
5697
7590
  };
5698
7591
  }
7592
+ function buildOpsWaitForResultsOptions(params) {
7593
+ const {
7594
+ opId,
7595
+ nextCursor,
7596
+ includeFailedRuns,
7597
+ intervalMs,
7598
+ maxPolls,
7599
+ timeoutMs,
7600
+ includeResults,
7601
+ ...rest
7602
+ } = params;
7603
+ return {
7604
+ ...rest,
7605
+ op_id: rest.op_id ?? opId,
7606
+ cursor: rest.cursor ?? nextCursor,
7607
+ include_failed_runs: rest.include_failed_runs ?? includeFailedRuns,
7608
+ interval_ms: rest.interval_ms ?? intervalMs,
7609
+ max_polls: rest.max_polls ?? maxPolls,
7610
+ timeout_ms: rest.timeout_ms ?? timeoutMs,
7611
+ include_results: rest.include_results ?? includeResults
7612
+ };
7613
+ }
7614
+ function buildOpsWaitBody(params) {
7615
+ const {
7616
+ opId,
7617
+ nextCursor,
7618
+ includeFailedRuns,
7619
+ intervalMs,
7620
+ maxPolls,
7621
+ timeoutMs,
7622
+ includeResults,
7623
+ ...rest
7624
+ } = params;
7625
+ return {
7626
+ ...rest,
7627
+ op_id: rest.op_id ?? opId,
7628
+ cursor: rest.cursor ?? nextCursor,
7629
+ include_failed_runs: rest.include_failed_runs ?? includeFailedRuns,
7630
+ interval_ms: rest.interval_ms ?? intervalMs,
7631
+ max_polls: rest.max_polls ?? maxPolls,
7632
+ timeout_ms: rest.timeout_ms ?? timeoutMs,
7633
+ include_results: rest.include_results ?? includeResults
7634
+ };
7635
+ }
5699
7636
  function buildOpsDebugOptions(params) {
5700
7637
  const {
5701
7638
  opId,
@@ -5773,6 +7710,8 @@ function normalizeOpsSinkRequest(sink) {
5773
7710
  nangoAction,
5774
7711
  proxyPath,
5775
7712
  nangoProxyPath,
7713
+ method,
7714
+ httpMethod,
5776
7715
  fieldMap,
5777
7716
  requiredFields,
5778
7717
  writeConfirmed,
@@ -5791,6 +7730,7 @@ function normalizeOpsSinkRequest(sink) {
5791
7730
  const nango_action = rest.nango_action ?? nangoAction;
5792
7731
  const proxy_path = rest.proxy_path ?? proxyPath;
5793
7732
  const nango_proxy_path = rest.nango_proxy_path ?? nangoProxyPath;
7733
+ const http_method = rest.http_method ?? httpMethod;
5794
7734
  const field_map = rest.field_map ?? fieldMap;
5795
7735
  const required_fields = rest.required_fields ?? requiredFields;
5796
7736
  const write_confirmed = rest.write_confirmed ?? writeConfirmed;
@@ -5805,6 +7745,9 @@ function normalizeOpsSinkRequest(sink) {
5805
7745
  if (nango_action !== void 0 && normalizedConfig.nango_action === void 0) normalizedConfig.nango_action = nango_action;
5806
7746
  if (proxy_path !== void 0 && normalizedConfig.proxy_path === void 0) normalizedConfig.proxy_path = proxy_path;
5807
7747
  if (nango_proxy_path !== void 0 && normalizedConfig.nango_proxy_path === void 0) normalizedConfig.nango_proxy_path = nango_proxy_path;
7748
+ if (method !== void 0 && normalizedConfig.method === void 0) normalizedConfig.method = method;
7749
+ if (http_method !== void 0 && normalizedConfig.http_method === void 0) normalizedConfig.http_method = http_method;
7750
+ if (normalizedConfig.method === void 0 && http_method !== void 0) normalizedConfig.method = http_method;
5808
7751
  if (field_map !== void 0 && normalizedConfig.field_map === void 0) normalizedConfig.field_map = field_map;
5809
7752
  if (required_fields !== void 0 && normalizedConfig.required_fields === void 0) normalizedConfig.required_fields = required_fields;
5810
7753
  if (write_confirmed !== void 0 && normalizedConfig.write_confirmed === void 0) normalizedConfig.write_confirmed = write_confirmed;
@@ -5824,6 +7767,8 @@ function normalizeOpsSinkRequest(sink) {
5824
7767
  nango_action,
5825
7768
  proxy_path,
5826
7769
  nango_proxy_path,
7770
+ method,
7771
+ http_method,
5827
7772
  field_map,
5828
7773
  required_fields,
5829
7774
  write_confirmed,
@@ -5831,6 +7776,10 @@ function normalizeOpsSinkRequest(sink) {
5831
7776
  config: normalizedConfig
5832
7777
  };
5833
7778
  }
7779
+ function normalizeOpsDestinationRequest(destination) {
7780
+ if (!destination || typeof destination !== "object" || Array.isArray(destination)) return destination;
7781
+ return normalizeOpsSinkRequest(destination);
7782
+ }
5834
7783
  function normalizeOpsSinkConfigRequest(config) {
5835
7784
  const out = { ...config ?? {} };
5836
7785
  if (out.connection_id === void 0 && out.connectionId !== void 0) out.connection_id = out.connectionId;
@@ -5844,6 +7793,8 @@ function normalizeOpsSinkConfigRequest(config) {
5844
7793
  if (out.nango_action === void 0 && out.nangoAction !== void 0) out.nango_action = out.nangoAction;
5845
7794
  if (out.proxy_path === void 0 && out.proxyPath !== void 0) out.proxy_path = out.proxyPath;
5846
7795
  if (out.nango_proxy_path === void 0 && out.nangoProxyPath !== void 0) out.nango_proxy_path = out.nangoProxyPath;
7796
+ if (out.http_method === void 0 && out.httpMethod !== void 0) out.http_method = out.httpMethod;
7797
+ if (out.method === void 0 && out.http_method !== void 0) out.method = out.http_method;
5847
7798
  if (out.field_map === void 0 && out.fieldMap !== void 0) out.field_map = out.fieldMap;
5848
7799
  if (out.required_fields === void 0 && out.requiredFields !== void 0) out.required_fields = out.requiredFields;
5849
7800
  if (out.write_confirmed === void 0 && out.writeConfirmed !== void 0) out.write_confirmed = out.writeConfirmed;
@@ -5917,7 +7868,6 @@ function toSnakeConfig(params) {
5917
7868
  return {
5918
7869
  system_prompt: params.systemPrompt || params.system_prompt || "You are a senior business research analyst. Use multi-model evidence carefully and return concise structured enrichment.",
5919
7870
  user_template: params.userTemplate || params.user_template || params.prompt,
5920
- model: params.model,
5921
7871
  temperature: params.temperature,
5922
7872
  max_concurrency: params.maxConcurrency || params.max_concurrency,
5923
7873
  max_tokens: params.maxTokens || params.max_tokens,
@@ -5941,13 +7891,10 @@ var Ai = class {
5941
7891
  /**
5942
7892
  * Run Custom AI Enrichment - Multi Model.
5943
7893
  *
5944
- * Uses OpenRouter Fusion for multi-model analysis with web search/fetch enabled
5945
- * inside Fusion, then synthesizes the requested structured output fields.
7894
+ * Uses the hosted default model with bounded synthesis, then returns the
7895
+ * requested structured output fields.
5946
7896
  */
5947
7897
  async multiModel(params) {
5948
- if (!params.model) {
5949
- throw new Error('model is required. Use an OpenRouter id such as "anthropic/claude-sonnet-4" or "google/gemini-2.5-flash".');
5950
- }
5951
7898
  if (!params.prompt && !params.userTemplate && !params.user_template) {
5952
7899
  throw new Error("prompt or userTemplate is required.");
5953
7900
  }
@@ -6164,6 +8111,10 @@ var GtmKernel = class {
6164
8111
  days: options.days,
6165
8112
  network_days: options.networkDays,
6166
8113
  min_sample_size: options.minSampleSize,
8114
+ holdout_min_sample_size: options.holdoutMinSampleSize,
8115
+ predictive_min_labeled_outcomes: options.predictiveMinLabeledOutcomes,
8116
+ predictive_min_positive_outcomes: options.predictiveMinPositiveOutcomes,
8117
+ predictive_min_memory_sample_size: options.predictiveMinMemorySampleSize,
6167
8118
  min_workspace_count: options.minWorkspaceCount,
6168
8119
  min_privacy_k: options.minPrivacyK,
6169
8120
  include_memory: options.includeMemory,
@@ -6230,9 +8181,11 @@ var GtmKernel = class {
6230
8181
  write_routine_outcomes: input.writeRoutineOutcomes
6231
8182
  });
6232
8183
  }
6233
- /** Start a background Instantly webhook-events pull and route results into the GTM feedback loop. */
8184
+ /** Start a background Instantly webhook-events or email-history pull and route results into the GTM feedback loop. */
6234
8185
  async pullInstantlyFeedback(input) {
6235
8186
  return this.callMcp("gtm_instantly_feedback_pull", {
8187
+ source: input.source,
8188
+ instantly_profile: input.instantlyProfile,
6236
8189
  campaign_id: input.campaignId,
6237
8190
  campaign_build_id: input.campaignBuildId,
6238
8191
  provider_link_id: input.providerLinkId,
@@ -6244,6 +8197,9 @@ var GtmKernel = class {
6244
8197
  to: input.to,
6245
8198
  limit: input.limit,
6246
8199
  max_pages: input.maxPages,
8200
+ email_type: input.emailType,
8201
+ mode: input.mode,
8202
+ sort_order: input.sortOrder,
6247
8203
  create_memory: input.createMemory,
6248
8204
  write_routine_outcomes: input.writeRoutineOutcomes,
6249
8205
  dry_run: input.dryRun,
@@ -6314,6 +8270,16 @@ var GtmKernel = class {
6314
8270
  min_privacy_k: input.minPrivacyK
6315
8271
  });
6316
8272
  }
8273
+ /** Queue a dry-run-first bridge from imported historical feedback to attribution-only Campaign Build surrogates. */
8274
+ async runHistoricalCampaignBuildBridge(input = {}) {
8275
+ return this.callMcp("gtm_historical_campaign_build_bridge_run", {
8276
+ dry_run: input.dryRun,
8277
+ write_approved: input.writeApproved,
8278
+ max_events: input.maxEvents,
8279
+ max_campaigns: input.maxCampaigns,
8280
+ min_feedback_events: input.minFeedbackEvents
8281
+ });
8282
+ }
6317
8283
  /** Prepare a provider-agnostic feedback webhook URL for Smartlead, HeyReach, Airbyte, or custom sender events. */
6318
8284
  async prepareFeedbackWebhook(input) {
6319
8285
  return this.callMcp("gtm_feedback_webhook_prepare", {
@@ -6373,6 +8339,7 @@ var GtmKernel = class {
6373
8339
  days: input.days,
6374
8340
  network_days: input.networkDays,
6375
8341
  min_sample_size: input.minSampleSize,
8342
+ holdout_min_sample_size: input.holdoutMinSampleSize,
6376
8343
  min_workspace_count: input.minWorkspaceCount,
6377
8344
  min_privacy_k: input.minPrivacyK,
6378
8345
  include_memory: input.includeMemory,
@@ -6393,6 +8360,7 @@ var GtmKernel = class {
6393
8360
  days: input.days,
6394
8361
  network_days: input.networkDays,
6395
8362
  min_sample_size: input.minSampleSize,
8363
+ holdout_min_sample_size: input.holdoutMinSampleSize,
6396
8364
  min_workspace_count: input.minWorkspaceCount,
6397
8365
  min_privacy_k: input.minPrivacyK,
6398
8366
  include_memory: input.includeMemory,
@@ -6517,6 +8485,29 @@ var GtmKernel = class {
6517
8485
  include_details: options.includeDetails
6518
8486
  });
6519
8487
  }
8488
+ /** Search the Agency Autopilot safe-action catalog for agency-style lead lists, Clay tables, and GTM motions. */
8489
+ async agencyAutopilotCapabilityCatalog(options = {}) {
8490
+ return this.callMcp("gtm_agency_autopilot_capability_catalog", {
8491
+ query: options.query,
8492
+ family: options.family,
8493
+ phase: options.phase,
8494
+ account_label: options.accountLabel,
8495
+ workspace_label: options.workspaceLabel,
8496
+ target_count: options.targetCount,
8497
+ limit: options.limit,
8498
+ include_agent_prompts: options.includeAgentPrompts
8499
+ });
8500
+ }
8501
+ /** Search agency operating playbooks Agency Autopilot can use for lead lists, Clay tables, GTM motions, and proof gates. */
8502
+ async agencyAutopilotPlaybookCatalog(options = {}) {
8503
+ return this.callMcp("gtm_agency_autopilot_playbook_catalog", {
8504
+ query: options.query,
8505
+ playbook_slug: options.playbookSlug,
8506
+ outcome_type: options.outcomeType,
8507
+ limit: options.limit,
8508
+ include_required_fields: options.includeRequiredFields
8509
+ });
8510
+ }
6520
8511
  /** Build the read-only Memory, Brain, risk, and provider-route runbook needed before planning a campaign. */
6521
8512
  async campaignStartContext(input = {}) {
6522
8513
  return this.callMcp("gtm_campaign_start_context", {
@@ -6875,6 +8866,35 @@ var GtmKernel = class {
6875
8866
  user_display_name: input.userDisplayName
6876
8867
  });
6877
8868
  }
8869
+ /** Prepare the full Nango provider search, connect, pull/export, Ops, and campaign-route flow. */
8870
+ async prepareNangoIntegrationFlow(input = {}) {
8871
+ return this.callMcp("nango_integration_flow_prepare", {
8872
+ query: input.query,
8873
+ search: input.search,
8874
+ provider_id: input.providerId,
8875
+ provider: input.provider,
8876
+ integration_id: input.integrationId,
8877
+ provider_config_key: input.providerConfigKey,
8878
+ workspace_connection_id: input.workspaceConnectionId,
8879
+ connection_id: input.connectionId,
8880
+ nango_connection_id: input.nangoConnectionId,
8881
+ intent: input.intent,
8882
+ action_name: input.actionName,
8883
+ tool_name: input.toolName,
8884
+ proxy_path: input.proxyPath,
8885
+ method: input.method,
8886
+ input: input.input,
8887
+ cadence: input.cadence,
8888
+ field_map: input.fieldMap,
8889
+ required_fields: input.requiredFields,
8890
+ confirm_spend: input.confirmSpend,
8891
+ write_confirmed: input.writeConfirmed,
8892
+ layer: input.layer,
8893
+ campaign_id: input.campaignId,
8894
+ limit: input.limit,
8895
+ include_provider_catalog: input.includeProviderCatalog
8896
+ });
8897
+ }
6878
8898
  /** List Nango-backed action tools for a workspace connection without executing them. */
6879
8899
  async listNangoTools(options = {}) {
6880
8900
  return this.callMcp("nango_mcp_tools_list", {
@@ -6897,6 +8917,12 @@ var GtmKernel = class {
6897
8917
  nango_connection_id: input.nangoConnectionId,
6898
8918
  action_name: input.actionName,
6899
8919
  tool_name: input.toolName,
8920
+ delivery_mode: input.deliveryMode,
8921
+ proxy_path: input.proxyPath,
8922
+ nango_proxy_path: input.nangoProxyPath,
8923
+ method: input.method,
8924
+ http_method: input.httpMethod,
8925
+ proxy_headers: input.proxyHeaders,
6900
8926
  input: input.input,
6901
8927
  async: input.async,
6902
8928
  max_retries: input.maxRetries,
@@ -7046,6 +9072,7 @@ function compact2(value) {
7046
9072
  }
7047
9073
 
7048
9074
  // src/index.ts
9075
+ var MCP_TOOL_CACHE_TTL_MS = 3e4;
7049
9076
  function inferMcpToolCategory(toolName) {
7050
9077
  if (typeof toolName !== "string" || !toolName) return void 0;
7051
9078
  if (toolName.startsWith("ops_") || toolName === "get_ops_readiness" || toolName === "get_gtm_ops_readiness" || toolName.startsWith("gtm_") || [
@@ -7177,6 +9204,21 @@ var Signaliz = class {
7177
9204
  }
7178
9205
  /** List all available MCP tools */
7179
9206
  async listTools() {
9207
+ const now = Date.now();
9208
+ if (this.toolListCache && this.toolListCache.expiresAt > now) {
9209
+ return this.toolListCache.promise;
9210
+ }
9211
+ const promise = this.fetchTools().catch((error) => {
9212
+ if (this.toolListCache?.promise === promise) this.toolListCache = void 0;
9213
+ throw error;
9214
+ });
9215
+ this.toolListCache = {
9216
+ expiresAt: now + MCP_TOOL_CACHE_TTL_MS,
9217
+ promise
9218
+ };
9219
+ return promise;
9220
+ }
9221
+ async fetchTools() {
7180
9222
  try {
7181
9223
  const manifest = await this.client.mcp("tools/call", {
7182
9224
  name: "get_tool_manifest",