@signaliz/sdk 1.0.17 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/{chunk-ZKE4L57J.mjs → chunk-PBJFIO72.mjs} +1535 -62
- package/dist/cli.js +1820 -65
- package/dist/cli.mjs +276 -2
- package/dist/index.d.mts +621 -4
- package/dist/index.d.ts +621 -4
- package/dist/index.js +1537 -62
- package/dist/index.mjs +5 -1
- package/dist/mcp-config.js +1533 -62
- package/dist/mcp-config.mjs +1 -1
- package/package.json +2 -2
package/dist/mcp-config.js
CHANGED
|
@@ -479,6 +479,38 @@ var Signals = class {
|
|
|
479
479
|
constructor(client) {
|
|
480
480
|
this.client = client;
|
|
481
481
|
}
|
|
482
|
+
/** Turn the strongest supported company signal into three complete, evidence-backed emails. */
|
|
483
|
+
async signalToCopy(params) {
|
|
484
|
+
const data = await this.client.post("api/v1/signal-to-copy", {
|
|
485
|
+
company_domain: params.companyDomain,
|
|
486
|
+
person_name: params.personName,
|
|
487
|
+
title: params.title,
|
|
488
|
+
campaign_offer: params.campaignOffer,
|
|
489
|
+
research_prompt: params.researchPrompt
|
|
490
|
+
});
|
|
491
|
+
return {
|
|
492
|
+
success: data.success ?? true,
|
|
493
|
+
strongestSignal: data.strongest_signal ?? "",
|
|
494
|
+
whyNow: data.why_now ?? "",
|
|
495
|
+
subjectLine: data.subject_line ?? "",
|
|
496
|
+
openingLine: data.opening_line ?? "",
|
|
497
|
+
confidence: data.confidence ?? 0,
|
|
498
|
+
evidenceUrl: data.evidence_url ?? "",
|
|
499
|
+
researchPrompt: data.research_prompt,
|
|
500
|
+
variations: (data.variations ?? []).map((v) => ({
|
|
501
|
+
style: v.style,
|
|
502
|
+
subjectLine: v.subject_line ?? "",
|
|
503
|
+
openingLine: v.opening_line ?? "",
|
|
504
|
+
cta: v.cta ?? "",
|
|
505
|
+
prediction: v.prediction,
|
|
506
|
+
email: v.email
|
|
507
|
+
}))
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
/** @deprecated Use signalToCopy. Retained for compatibility. */
|
|
511
|
+
async personalize(params) {
|
|
512
|
+
return this.signalToCopy(params);
|
|
513
|
+
}
|
|
482
514
|
/** Enrich a company with actionable signals (V1) */
|
|
483
515
|
async enrich(params) {
|
|
484
516
|
const args = {
|
|
@@ -487,6 +519,9 @@ var Signals = class {
|
|
|
487
519
|
if (params.domain) args.domain = params.domain;
|
|
488
520
|
if (params.researchPrompt) args.research_prompt = params.researchPrompt;
|
|
489
521
|
if (params.signalTypes) args.signal_types = params.signalTypes;
|
|
522
|
+
if (params.targetSignalCount) args.target_signal_count = params.targetSignalCount;
|
|
523
|
+
if (params.lookbackDays) args.lookback_days = params.lookbackDays;
|
|
524
|
+
if (params.enableDeepSearch !== void 0) args.enable_deep_search = params.enableDeepSearch;
|
|
490
525
|
const data = await this.client.mcp("tools/call", {
|
|
491
526
|
name: "enrich_company_signals",
|
|
492
527
|
arguments: args
|
|
@@ -496,6 +531,16 @@ var Signals = class {
|
|
|
496
531
|
signals: (data.signals ?? []).map((s) => ({
|
|
497
532
|
title: s.title,
|
|
498
533
|
signalType: s.signal_type,
|
|
534
|
+
sourceType: s.source_type ?? s.signal_classification?.label ?? "unknown",
|
|
535
|
+
sourceProvenance: s.source_provenance ?? s.metadata?.source_provenance ?? null,
|
|
536
|
+
signalFamily: s.signal_family ?? s.signal_type ?? null,
|
|
537
|
+
signalEventType: s.signal_event_type ?? s.signal_classification?.slug ?? null,
|
|
538
|
+
signalEventLabel: s.signal_event_label ?? s.signal_classification?.label ?? null,
|
|
539
|
+
signalClassification: s.signal_classification ? {
|
|
540
|
+
label: s.signal_classification.label,
|
|
541
|
+
slug: s.signal_classification.slug,
|
|
542
|
+
rationale: s.signal_classification.rationale
|
|
543
|
+
} : null,
|
|
499
544
|
confidenceScore: s.confidence_score ?? 0,
|
|
500
545
|
detectedAt: s.detected_at ?? "",
|
|
501
546
|
url: s.url,
|
|
@@ -536,8 +581,17 @@ var Signals = class {
|
|
|
536
581
|
content: s.content ?? "",
|
|
537
582
|
date: s.date ?? null,
|
|
538
583
|
sourceUrl: s.source_url ?? null,
|
|
539
|
-
sourceType: s.source_type ?? "unknown",
|
|
540
|
-
|
|
584
|
+
sourceType: s.source_type ?? s.signal_classification?.label ?? "unknown",
|
|
585
|
+
sourceProvenance: s.source_provenance ?? s.metadata?.source_provenance ?? null,
|
|
586
|
+
signalFamily: s.signal_family ?? s.type ?? null,
|
|
587
|
+
signalEventType: s.signal_event_type ?? s.signal_classification?.slug ?? null,
|
|
588
|
+
signalEventLabel: s.signal_event_label ?? s.signal_classification?.label ?? null,
|
|
589
|
+
confidence: s.confidence ?? null,
|
|
590
|
+
signalClassification: s.signal_classification ? {
|
|
591
|
+
label: s.signal_classification.label,
|
|
592
|
+
slug: s.signal_classification.slug,
|
|
593
|
+
rationale: s.signal_classification.rationale
|
|
594
|
+
} : null
|
|
541
595
|
})),
|
|
542
596
|
signalCount: data.signal_count ?? 0,
|
|
543
597
|
intelligence: data.intelligence ? {
|
|
@@ -580,6 +634,58 @@ var Signals = class {
|
|
|
580
634
|
}
|
|
581
635
|
};
|
|
582
636
|
}
|
|
637
|
+
/** Plan or run experimental Signal Fusion with explicit spend controls. */
|
|
638
|
+
async fusion(params) {
|
|
639
|
+
const args = {};
|
|
640
|
+
if (params.companyName) args.company_name = params.companyName;
|
|
641
|
+
if (params.domain) args.domain = params.domain;
|
|
642
|
+
if (params.companyDomain) args.company_domain = params.companyDomain;
|
|
643
|
+
if (params.linkedinUrl) args.linkedin_url = params.linkedinUrl;
|
|
644
|
+
if (params.companies) args.companies = params.companies;
|
|
645
|
+
if (params.preset) args.preset = params.preset;
|
|
646
|
+
if (params.researchPrompt) args.research_prompt = params.researchPrompt;
|
|
647
|
+
if (params.signalTypes) args.signal_types = params.signalTypes;
|
|
648
|
+
if (params.lookbackDays) args.lookback_days = params.lookbackDays;
|
|
649
|
+
if (params.targetSignalCount) args.target_signal_count = params.targetSignalCount;
|
|
650
|
+
if (params.maxToolCalls) args.max_tool_calls = params.maxToolCalls;
|
|
651
|
+
if (params.maxTokens) args.max_tokens = params.maxTokens;
|
|
652
|
+
if (params.timeoutMs) args.timeout_ms = params.timeoutMs;
|
|
653
|
+
const dryRun = params.dryRun ?? params.dry_run;
|
|
654
|
+
const confirmSpend = params.confirmSpend ?? params.confirm_spend;
|
|
655
|
+
const maxCredits = params.maxCredits ?? params.max_credits;
|
|
656
|
+
const maxCostUsd = params.maxCostUsd ?? params.max_cost_usd;
|
|
657
|
+
if (dryRun !== void 0) args.dry_run = dryRun;
|
|
658
|
+
if (confirmSpend !== void 0) args.confirm_spend = confirmSpend;
|
|
659
|
+
if (maxCredits !== void 0) args.max_credits = maxCredits;
|
|
660
|
+
if (maxCostUsd !== void 0) args.max_cost_usd = maxCostUsd;
|
|
661
|
+
const data = await this.client.post("signal-fusion", args);
|
|
662
|
+
return {
|
|
663
|
+
success: data.success ?? false,
|
|
664
|
+
capability: data.capability ?? "signal_fusion",
|
|
665
|
+
displayName: data.display_name ?? "Signal Fusion",
|
|
666
|
+
company: data.company,
|
|
667
|
+
signals: (data.signals ?? []).map((s) => ({
|
|
668
|
+
id: s.id,
|
|
669
|
+
title: s.title,
|
|
670
|
+
type: s.type,
|
|
671
|
+
content: s.content ?? "",
|
|
672
|
+
date: s.date ?? null,
|
|
673
|
+
sourceUrl: s.source_url ?? null,
|
|
674
|
+
sourceType: s.source_type ?? "unknown",
|
|
675
|
+
confidence: s.confidence ?? null
|
|
676
|
+
})),
|
|
677
|
+
signalCount: data.signal_count ?? data.signals?.length ?? 0,
|
|
678
|
+
results: data.results ?? [],
|
|
679
|
+
model: data.model,
|
|
680
|
+
fusion: data.fusion,
|
|
681
|
+
costUsd: data.cost_usd ?? 0,
|
|
682
|
+
tokensUsed: data.tokens_used ?? 0,
|
|
683
|
+
costSummary: data.cost_summary,
|
|
684
|
+
creditsUsed: data.total_credits_used ?? data.credits_used ?? 0,
|
|
685
|
+
billingMetadata: data.billing_metadata,
|
|
686
|
+
raw: data
|
|
687
|
+
};
|
|
688
|
+
}
|
|
583
689
|
};
|
|
584
690
|
|
|
585
691
|
// src/resources/systems.ts
|
|
@@ -868,7 +974,11 @@ var Campaigns = class {
|
|
|
868
974
|
maxSupportedTargetCount: data.max_supported_target_count,
|
|
869
975
|
brainContext: data.brain_context ?? {},
|
|
870
976
|
learningHoldout: data.learning_holdout ?? {},
|
|
871
|
-
providerRoute: mapProviderRoute(data)
|
|
977
|
+
providerRoute: mapProviderRoute(data),
|
|
978
|
+
effectiveSourceRouting: recordOrNull(data.effective_source_routing ?? data.source_routing),
|
|
979
|
+
sourcePlan: recordOrNull(data.source_plan ?? data.plan?.source_plan),
|
|
980
|
+
sourceRoutes: recordArray(data.source_routes ?? data.source_plan?.routes ?? data.plan?.source_plan?.routes),
|
|
981
|
+
sourceShards: recordArray(data.source_shards ?? data.source_plan?.shards ?? data.plan?.source_plan?.shards)
|
|
872
982
|
};
|
|
873
983
|
}
|
|
874
984
|
async getCampaignBuildStatus(campaignBuildId) {
|
|
@@ -1014,6 +1124,7 @@ function mapCustomerRowCounts(value) {
|
|
|
1014
1124
|
acceptedRows: numberOrNull(record.accepted_rows) ?? void 0,
|
|
1015
1125
|
usableRows: numberOrNull(record.usable_rows) ?? void 0,
|
|
1016
1126
|
copiedRows: numberOrNull(record.copied_rows) ?? void 0,
|
|
1127
|
+
copySkippedRows: numberOrNull(record.copy_skipped_rows) ?? void 0,
|
|
1017
1128
|
approvalReadyRows: numberOrNull(record.approval_ready_rows) ?? void 0,
|
|
1018
1129
|
qaReadyRows: numberOrNull(record.qa_ready_rows) ?? void 0,
|
|
1019
1130
|
deliveredRows: numberOrNull(record.delivered_rows) ?? void 0,
|
|
@@ -1060,6 +1171,9 @@ function recordOrNull(value) {
|
|
|
1060
1171
|
function stringArray(value) {
|
|
1061
1172
|
return Array.isArray(value) ? value.map((item) => String(item || "").trim()).filter(Boolean) : [];
|
|
1062
1173
|
}
|
|
1174
|
+
function recordArray(value) {
|
|
1175
|
+
return Array.isArray(value) ? value.map(recordOrNull).filter((item) => Boolean(item)) : [];
|
|
1176
|
+
}
|
|
1063
1177
|
function diagnosticMessages(value) {
|
|
1064
1178
|
if (!Array.isArray(value)) return [];
|
|
1065
1179
|
return value.map((item) => {
|
|
@@ -1221,7 +1335,8 @@ function buildArgs(request) {
|
|
|
1221
1335
|
suppress_prior_campaign_ids: request.cacheReusePolicy.suppressPriorCampaignIds,
|
|
1222
1336
|
suppress_prior_list_ids: request.cacheReusePolicy.suppressPriorListIds,
|
|
1223
1337
|
uniqueness: request.cacheReusePolicy.uniqueness,
|
|
1224
|
-
require_usage_metadata: request.cacheReusePolicy.requireUsageMetadata
|
|
1338
|
+
require_usage_metadata: request.cacheReusePolicy.requireUsageMetadata,
|
|
1339
|
+
verified_cache_ttl_days: request.cacheReusePolicy.verifiedCacheTtlDays
|
|
1225
1340
|
};
|
|
1226
1341
|
}
|
|
1227
1342
|
if (request.brainPreflight) args.brain_preflight = request.brainPreflight;
|
|
@@ -1246,7 +1361,8 @@ function buildArgs(request) {
|
|
|
1246
1361
|
geographies: request.icp.geographies,
|
|
1247
1362
|
keywords: request.icp.keywords,
|
|
1248
1363
|
exclusions: request.icp.exclusions,
|
|
1249
|
-
require_verified_email: request.icp.requireVerifiedEmail
|
|
1364
|
+
require_verified_email: request.icp.requireVerifiedEmail,
|
|
1365
|
+
persona_expansion_mode: request.icp.personaExpansionMode
|
|
1250
1366
|
};
|
|
1251
1367
|
}
|
|
1252
1368
|
if (request.policy) {
|
|
@@ -1286,6 +1402,7 @@ function buildArgs(request) {
|
|
|
1286
1402
|
} : void 0,
|
|
1287
1403
|
sequence_steps: request.copy.sequenceSteps,
|
|
1288
1404
|
copy_schema: request.copy.copySchema,
|
|
1405
|
+
evidence_mode: request.copy.evidenceMode,
|
|
1289
1406
|
banned_phrases: request.copy.bannedPhrases,
|
|
1290
1407
|
approval_required: request.copy.approvalRequired,
|
|
1291
1408
|
quality_tier: request.copy.qualityTier
|
|
@@ -1342,7 +1459,8 @@ var DEFAULT_SIGNALIZ_CAMPAIGN_BUILDER_DEFAULTS = {
|
|
|
1342
1459
|
copy: {
|
|
1343
1460
|
enabled: true,
|
|
1344
1461
|
tone: "direct",
|
|
1345
|
-
maxBodyWords: 125
|
|
1462
|
+
maxBodyWords: 125,
|
|
1463
|
+
evidenceMode: "signal_led"
|
|
1346
1464
|
},
|
|
1347
1465
|
delivery: {
|
|
1348
1466
|
enabled: true,
|
|
@@ -1399,7 +1517,8 @@ var CAMPAIGN_BUILDER_OPERATING_PLAYBOOKS = [
|
|
|
1399
1517
|
"Block export when required identity, company, or email fields are missing."
|
|
1400
1518
|
],
|
|
1401
1519
|
activationBoundary: "Cache inventory is read-only. Fresh sourcing, cache writes, export, and sender loading require explicit approval.",
|
|
1402
|
-
memoryKeywords: ["cache inventory", "large list", "unique emails", "top-up sourcing", "source metadata"]
|
|
1520
|
+
memoryKeywords: ["cache inventory", "large list", "unique emails", "top-up sourcing", "source metadata"],
|
|
1521
|
+
knowledgeSources: ["data-ops/contact-data-lifecycle", "data-ops/credit-optimization-guide", "gtm/data-quality-ops", "gtm/lead-generation-strategies"]
|
|
1403
1522
|
},
|
|
1404
1523
|
{
|
|
1405
1524
|
slug: "net-new-suppressed-list",
|
|
@@ -1424,7 +1543,8 @@ var CAMPAIGN_BUILDER_OPERATING_PLAYBOOKS = [
|
|
|
1424
1543
|
"Do not describe rows as fully qualified when the approved path only proves net-new email status."
|
|
1425
1544
|
],
|
|
1426
1545
|
activationBoundary: "Suppression review must pass before export, upload, CRM sync, sender load, or sequence launch.",
|
|
1427
|
-
memoryKeywords: ["suppression", "net-new", "prior email overlap", "domain review", "provenance-rich QA"]
|
|
1546
|
+
memoryKeywords: ["suppression", "net-new", "prior email overlap", "domain review", "provenance-rich QA"],
|
|
1547
|
+
knowledgeSources: ["gtm/data-quality-ops", "data-ops/contact-data-lifecycle", "gtm/outbound-sequencing"]
|
|
1428
1548
|
},
|
|
1429
1549
|
{
|
|
1430
1550
|
slug: "proof-first-vertical-gate",
|
|
@@ -1449,7 +1569,8 @@ var CAMPAIGN_BUILDER_OPERATING_PLAYBOOKS = [
|
|
|
1449
1569
|
"Contact fit should meet the configured threshold before email finding or copy generation."
|
|
1450
1570
|
],
|
|
1451
1571
|
activationBoundary: "Proof gates are required before contact spend, copy, export, or external writes.",
|
|
1452
|
-
memoryKeywords: ["proof gate", "vertical exclusion", "company before contact", "pass review fail", "fit threshold"]
|
|
1572
|
+
memoryKeywords: ["proof gate", "vertical exclusion", "company before contact", "pass review fail", "fit threshold"],
|
|
1573
|
+
knowledgeSources: ["gtm/icp-design-framework", "gtm/strategy/scoring.yaml", "gtm/advanced/account-prioritization-frameworks", "gtm/advanced/predictive-lead-scoring", "gtm/verticals"]
|
|
1453
1574
|
},
|
|
1454
1575
|
{
|
|
1455
1576
|
slug: "signal-led-copy-approval",
|
|
@@ -1474,7 +1595,8 @@ var CAMPAIGN_BUILDER_OPERATING_PLAYBOOKS = [
|
|
|
1474
1595
|
"Destination fields must remain blocked until approval metadata is present."
|
|
1475
1596
|
],
|
|
1476
1597
|
activationBoundary: "Copy readiness is not delivery approval. Export, sync, sender load, and launch require a separate human-approved gate.",
|
|
1477
|
-
memoryKeywords: ["company signals", "evidence-fed copy", "why now", "copy blocker", "approval gate"]
|
|
1598
|
+
memoryKeywords: ["company signals", "evidence-fed copy", "why now", "copy blocker", "approval gate"],
|
|
1599
|
+
knowledgeSources: ["gtm/email-personalization", "gtm/signal-based-selling", "gtm/signal-combination-matrix", "gtm/advanced/buyer-journey-mapping"]
|
|
1478
1600
|
},
|
|
1479
1601
|
{
|
|
1480
1602
|
slug: "domain-first-recovery",
|
|
@@ -1499,7 +1621,8 @@ var CAMPAIGN_BUILDER_OPERATING_PLAYBOOKS = [
|
|
|
1499
1621
|
"Target count means final held rows, not raw sourced rows."
|
|
1500
1622
|
],
|
|
1501
1623
|
activationBoundary: "Domain recovery informs sourcing. Contact enrichment, paid qualification, and export still require approved gates.",
|
|
1502
|
-
memoryKeywords: ["domain-first", "yield modeling", "attrition", "accepted domains", "gap fill"]
|
|
1624
|
+
memoryKeywords: ["domain-first", "yield modeling", "attrition", "accepted domains", "gap fill"],
|
|
1625
|
+
knowledgeSources: ["gtm/lead-generation-strategies", "data-ops/enrichment-waterfall-strategy", "data-ops/credit-optimization-guide", "gtm/territory-account-planning"]
|
|
1503
1626
|
},
|
|
1504
1627
|
{
|
|
1505
1628
|
slug: "table-workflow-handoff",
|
|
@@ -1524,7 +1647,112 @@ var CAMPAIGN_BUILDER_OPERATING_PLAYBOOKS = [
|
|
|
1524
1647
|
"Provider route activation must dry-run before any external write."
|
|
1525
1648
|
],
|
|
1526
1649
|
activationBoundary: "MCP, webhook, managed integration, and destination routes remain dry-run until the reviewed route and campaign are approved.",
|
|
1527
|
-
memoryKeywords: ["table workflow", "request ready", "provider status", "export blocker", "BYO integration"]
|
|
1650
|
+
memoryKeywords: ["table workflow", "request ready", "provider status", "export blocker", "BYO integration"],
|
|
1651
|
+
knowledgeSources: ["gtm/multi-channel-orchestration", "data-ops/enrichment-waterfall-strategy", "recipes/clay-gtm-webhook-recipe", "systems/blueprints/multi-channel-orchestration"]
|
|
1652
|
+
},
|
|
1653
|
+
{
|
|
1654
|
+
slug: "icp-persona-segmentation",
|
|
1655
|
+
label: "ICP And Persona Segmentation",
|
|
1656
|
+
whenToUse: [
|
|
1657
|
+
"The brief has a broad market, multiple personas, or unclear buying committee.",
|
|
1658
|
+
"The campaign should rank account tiers, persona fit, vertical nuance, and exclusions before sourcing at scale."
|
|
1659
|
+
],
|
|
1660
|
+
sequence: [
|
|
1661
|
+
"Convert the brief into account tiers, persona roles, buying committee influence, and explicit exclusions.",
|
|
1662
|
+
"Choose vertical guidance only from the customer-safe GTM library and keep internal platform docs out of prompts.",
|
|
1663
|
+
"Score accounts before contacts, then score contacts against persona, seniority, function, geography, and intent fit.",
|
|
1664
|
+
"Return segment counts and review buckets before spend, copy, export, or launch."
|
|
1665
|
+
],
|
|
1666
|
+
requiredFields: {
|
|
1667
|
+
segment: ["segment_name", "tier", "persona_priority", "buying_committee_role", "fit_reason", "exclusion_reason"],
|
|
1668
|
+
scoring: ["account_fit_score", "persona_fit_score", "segment_confidence", "review_bucket"]
|
|
1669
|
+
},
|
|
1670
|
+
qualityGates: [
|
|
1671
|
+
"Do not treat adjacent personas or verticals as matches unless the brief allows them.",
|
|
1672
|
+
"Keep ICP, persona, and exclusion evidence visible in the proof packet.",
|
|
1673
|
+
"Low-confidence segments route to review before contact discovery or copy."
|
|
1674
|
+
],
|
|
1675
|
+
activationBoundary: "Segmentation is planning guidance. Paid sourcing, enrichment, export, and launch require the normal approval gates.",
|
|
1676
|
+
memoryKeywords: ["ICP design", "persona segmentation", "buying committee", "account tiers", "fit scoring", "vertical nuance"],
|
|
1677
|
+
knowledgeSources: ["gtm/icp-design-framework", "gtm/personas/sdr.yaml", "gtm/personas/ae.yaml", "gtm/personas/cro.yaml", "gtm/personas/revops.yaml", "gtm/advanced/account-prioritization-frameworks", "gtm/verticals"]
|
|
1678
|
+
},
|
|
1679
|
+
{
|
|
1680
|
+
slug: "buying-signal-prioritization",
|
|
1681
|
+
label: "Buying Signal Prioritization",
|
|
1682
|
+
whenToUse: [
|
|
1683
|
+
"The campaign depends on timing, intent, trigger events, or account prioritization.",
|
|
1684
|
+
"The operator wants why-now proof instead of generic list generation."
|
|
1685
|
+
],
|
|
1686
|
+
sequence: [
|
|
1687
|
+
"Map the campaign goal to signal families, trigger freshness, and buyer-journey stage.",
|
|
1688
|
+
"Rank signals by relevance, recency, source quality, and persona-specific actionability.",
|
|
1689
|
+
"Attach evidence URLs or evidence summaries without exposing private memory rows.",
|
|
1690
|
+
"Use signal strength to decide source order, copy angle, and review priority."
|
|
1691
|
+
],
|
|
1692
|
+
requiredFields: {
|
|
1693
|
+
signal: ["signal_type", "signal_date", "signal_source", "why_now", "signal_confidence", "buyer_stage"],
|
|
1694
|
+
prioritization: ["priority_score", "priority_reason", "next_best_action", "review_reason"]
|
|
1695
|
+
},
|
|
1696
|
+
qualityGates: [
|
|
1697
|
+
"Do not use stale, unsupported, or weak signals as personalization proof.",
|
|
1698
|
+
"Signal fit cannot override hard ICP, geography, suppression, or verified-email gates.",
|
|
1699
|
+
"Rows with uncertain why-now evidence must stay review-only."
|
|
1700
|
+
],
|
|
1701
|
+
activationBoundary: "Signal ranking can prioritize review and copy. It does not approve outreach, sender loading, exports, or sends.",
|
|
1702
|
+
memoryKeywords: ["buying signals", "intent data", "funding trigger", "new executive", "tech adoption", "buyer journey", "why now"],
|
|
1703
|
+
knowledgeSources: ["gtm/signal-based-selling", "gtm/signal-combination-matrix", "gtm/advanced/intent-data-strategies", "gtm/advanced/buyer-journey-mapping", "gtm/signals/funding-round", "gtm/signals/new-executive", "gtm/signals/tech-adoption", "recipes/funding-trigger-outreach", "recipes/new-hire-outreach"]
|
|
1704
|
+
},
|
|
1705
|
+
{
|
|
1706
|
+
slug: "multi-channel-sequence-fit",
|
|
1707
|
+
label: "Multi-Channel Sequence Fit",
|
|
1708
|
+
whenToUse: [
|
|
1709
|
+
"The campaign needs email, LinkedIn, call, webhook, CRM, or sequencer coordination.",
|
|
1710
|
+
"Copy and delivery should adapt by persona, channel, deliverability risk, and proof strength."
|
|
1711
|
+
],
|
|
1712
|
+
sequence: [
|
|
1713
|
+
"Choose channel mix from persona, deal motion, evidence strength, deliverability constraints, and connected destination readiness.",
|
|
1714
|
+
"Draft sequence logic with email, LinkedIn, call, and task handoffs only where supported by the approved destination.",
|
|
1715
|
+
"Keep copy concise, proof-backed, channel-specific, and reviewable before delivery.",
|
|
1716
|
+
"Run deliverability and destination readiness checks before sender load or launch approval."
|
|
1717
|
+
],
|
|
1718
|
+
requiredFields: {
|
|
1719
|
+
channel: ["channel", "touch_number", "persona_context", "message_angle", "channel_blocker", "approval_status"],
|
|
1720
|
+
deliverability: ["verified_email", "domain_risk", "send_window", "unsubscribe_risk", "sender_ready"]
|
|
1721
|
+
},
|
|
1722
|
+
qualityGates: [
|
|
1723
|
+
"Email copy must be row-supported and under the configured campaign tone/length limits.",
|
|
1724
|
+
"LinkedIn, phone, CRM, webhook, and sequencer actions remain destination-locked until approved.",
|
|
1725
|
+
"Deliverability risk or missing sender readiness blocks launch even when rows are qualified."
|
|
1726
|
+
],
|
|
1727
|
+
activationBoundary: "Sequence strategy is draft-only. External writes, sender loading, launch, and sending need separate explicit approval.",
|
|
1728
|
+
memoryKeywords: ["outbound sequencing", "email personalization", "LinkedIn outreach", "cold calling", "multi-channel orchestration", "deliverability"],
|
|
1729
|
+
knowledgeSources: ["gtm/outbound-sequencing", "gtm/email-personalization", "gtm/linkedin-outreach-strategies", "gtm/cold-calling-frameworks", "gtm/multi-channel-orchestration", "gtm/advanced/email-deliverability", "systems/blueprints/multi-channel-orchestration"]
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
slug: "revops-feedback-loop",
|
|
1733
|
+
label: "RevOps Feedback Loop",
|
|
1734
|
+
whenToUse: [
|
|
1735
|
+
"The campaign should improve from replies, meetings, delivery results, CRM outcomes, or operator QA.",
|
|
1736
|
+
"The operator needs post-build monitoring, governance, or continuous ops follow-up."
|
|
1737
|
+
],
|
|
1738
|
+
sequence: [
|
|
1739
|
+
"Define success metrics, guardrails, and feedback sources before launch.",
|
|
1740
|
+
"Collect aggregate outcomes, QA blockers, delivery health, and operator edits after each campaign phase.",
|
|
1741
|
+
"Separate workspace-private evidence from public-safe learnings and Brain defaults.",
|
|
1742
|
+
"Feed approved aggregate learnings back into future sourcing, qualification, copy, and suppression rules."
|
|
1743
|
+
],
|
|
1744
|
+
requiredFields: {
|
|
1745
|
+
feedback: ["source", "event_type", "metric", "sample_size", "confidence", "learning_candidate"],
|
|
1746
|
+
governance: ["approval_owner", "last_reviewed_at", "risk_flag", "next_review_action"]
|
|
1747
|
+
},
|
|
1748
|
+
qualityGates: [
|
|
1749
|
+
"Do not write memory or Brain learnings from raw replies, private labels, or provider payloads without approved redaction.",
|
|
1750
|
+
"Use holdouts or pre/post comparisons before claiming causal lift.",
|
|
1751
|
+
"Delivery, suppression, and opt-out signals must update future gates before scale-up."
|
|
1752
|
+
],
|
|
1753
|
+
activationBoundary: "Feedback review is read-only until memory writes, Brain writes, CRM updates, or campaign changes are explicitly approved.",
|
|
1754
|
+
memoryKeywords: ["RevOps", "feedback loop", "campaign learning", "reply outcomes", "sales marketing alignment", "data hygiene", "holdout"],
|
|
1755
|
+
knowledgeSources: ["gtm/revenue-operations", "gtm/advanced/sales-marketing-alignment", "gtm/advanced/predictive-lead-scoring", "gtm/data-quality-ops", "data-ops/contact-data-lifecycle", "recipes/crm-data-hygiene", "gtm/playbooks/revops-automation-playbook"]
|
|
1528
1756
|
}
|
|
1529
1757
|
];
|
|
1530
1758
|
var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
|
|
@@ -1611,7 +1839,15 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
|
|
|
1611
1839
|
agencyContext: {
|
|
1612
1840
|
partnerEcosystem: ["signaliz", "instantly", "nango"]
|
|
1613
1841
|
},
|
|
1614
|
-
operatingPlaybookSlugs: [
|
|
1842
|
+
operatingPlaybookSlugs: [
|
|
1843
|
+
"net-new-suppressed-list",
|
|
1844
|
+
"proof-first-vertical-gate",
|
|
1845
|
+
"signal-led-copy-approval",
|
|
1846
|
+
"icp-persona-segmentation",
|
|
1847
|
+
"buying-signal-prioritization",
|
|
1848
|
+
"multi-channel-sequence-fit",
|
|
1849
|
+
"revops-feedback-loop"
|
|
1850
|
+
]
|
|
1615
1851
|
},
|
|
1616
1852
|
{
|
|
1617
1853
|
slug: "non-medical-home-care",
|
|
@@ -1684,7 +1920,15 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
|
|
|
1684
1920
|
agencyContext: {
|
|
1685
1921
|
partnerEcosystem: ["clay", "octave", "signaliz", "instantly", "nango"]
|
|
1686
1922
|
},
|
|
1687
|
-
operatingPlaybookSlugs: [
|
|
1923
|
+
operatingPlaybookSlugs: [
|
|
1924
|
+
"proof-first-vertical-gate",
|
|
1925
|
+
"domain-first-recovery",
|
|
1926
|
+
"table-workflow-handoff",
|
|
1927
|
+
"icp-persona-segmentation",
|
|
1928
|
+
"buying-signal-prioritization",
|
|
1929
|
+
"multi-channel-sequence-fit",
|
|
1930
|
+
"revops-feedback-loop"
|
|
1931
|
+
]
|
|
1688
1932
|
},
|
|
1689
1933
|
{
|
|
1690
1934
|
slug: "agency-founder-led",
|
|
@@ -1745,7 +1989,15 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
|
|
|
1745
1989
|
agencyContext: {
|
|
1746
1990
|
partnerEcosystem: ["signaliz", "octave", "instantly", "nango"]
|
|
1747
1991
|
},
|
|
1748
|
-
operatingPlaybookSlugs: [
|
|
1992
|
+
operatingPlaybookSlugs: [
|
|
1993
|
+
"net-new-suppressed-list",
|
|
1994
|
+
"signal-led-copy-approval",
|
|
1995
|
+
"table-workflow-handoff",
|
|
1996
|
+
"icp-persona-segmentation",
|
|
1997
|
+
"buying-signal-prioritization",
|
|
1998
|
+
"multi-channel-sequence-fit",
|
|
1999
|
+
"revops-feedback-loop"
|
|
2000
|
+
]
|
|
1749
2001
|
},
|
|
1750
2002
|
{
|
|
1751
2003
|
slug: "cloud-infrastructure-displacement",
|
|
@@ -1832,7 +2084,15 @@ var CAMPAIGN_BUILDER_STRATEGY_TEMPLATES = [
|
|
|
1832
2084
|
agencyContext: {
|
|
1833
2085
|
partnerEcosystem: ["signaliz", "octave", "instantly", "nango"]
|
|
1834
2086
|
},
|
|
1835
|
-
operatingPlaybookSlugs: [
|
|
2087
|
+
operatingPlaybookSlugs: [
|
|
2088
|
+
"proof-first-vertical-gate",
|
|
2089
|
+
"domain-first-recovery",
|
|
2090
|
+
"signal-led-copy-approval",
|
|
2091
|
+
"icp-persona-segmentation",
|
|
2092
|
+
"buying-signal-prioritization",
|
|
2093
|
+
"multi-channel-sequence-fit",
|
|
2094
|
+
"revops-feedback-loop"
|
|
2095
|
+
]
|
|
1836
2096
|
}
|
|
1837
2097
|
];
|
|
1838
2098
|
var CampaignBuilderAgent = class {
|
|
@@ -1863,6 +2123,9 @@ var CampaignBuilderAgent = class {
|
|
|
1863
2123
|
const plan = await this.createPlan(request, options);
|
|
1864
2124
|
return createCampaignBuilderReadiness(plan);
|
|
1865
2125
|
}
|
|
2126
|
+
activeWorkContext(input) {
|
|
2127
|
+
return createCampaignBuilderActiveWorkContext(input);
|
|
2128
|
+
}
|
|
1866
2129
|
async proof(request, options = {}) {
|
|
1867
2130
|
const plan = await this.createPlan(request, options);
|
|
1868
2131
|
const result = await this.dryRunPlan(plan, {
|
|
@@ -1904,6 +2167,7 @@ var CampaignBuilderAgent = class {
|
|
|
1904
2167
|
},
|
|
1905
2168
|
brain_defaults: plan.buildRequest.brainDefaults,
|
|
1906
2169
|
brain_preflight: plan.buildRequest.brainPreflight,
|
|
2170
|
+
learning_holdout: plan.buildRequest.learningHoldout,
|
|
1907
2171
|
campaign_strategy_context: plan.buildRequest.campaignStrategyContext,
|
|
1908
2172
|
delivery_risk_preflight: plan.buildRequest.deliveryRisk,
|
|
1909
2173
|
delivery_risk: plan.buildRequest.deliveryRisk
|
|
@@ -1917,7 +2181,8 @@ var CampaignBuilderAgent = class {
|
|
|
1917
2181
|
estimated_credits: plan.estimatedCredits
|
|
1918
2182
|
},
|
|
1919
2183
|
brain_delivery_risk_preflight: plan.buildRequest.deliveryRisk,
|
|
1920
|
-
delivery_risk: plan.buildRequest.deliveryRisk
|
|
2184
|
+
delivery_risk: plan.buildRequest.deliveryRisk,
|
|
2185
|
+
learning_holdout: plan.buildRequest.learningHoldout
|
|
1921
2186
|
},
|
|
1922
2187
|
approval_required: plan.approvals.some((approval) => approval.blocking),
|
|
1923
2188
|
actor_type: "agent",
|
|
@@ -2245,9 +2510,10 @@ function createCampaignBuilderAgentPlan(request, context = {}) {
|
|
|
2245
2510
|
const estimatedCredits = estimateCredits(resolvedRequest, defaults, targetCount);
|
|
2246
2511
|
const buildRequest = createBuildRequest(resolvedRequest, defaults, targetCount, campaignName, context.scopeBuildArgs, routes, memory, context.scopeBrainPreflight);
|
|
2247
2512
|
const brainPreflight = asRecord(buildRequest.brainPreflight);
|
|
2513
|
+
const evidence = buildRequest.evidence ?? null;
|
|
2248
2514
|
const approvals = deriveApprovals(resolvedRequest, routes, memory, estimatedCredits, defaults.delivery?.approvalRequired !== false);
|
|
2249
2515
|
return {
|
|
2250
|
-
planId: `cbp_${hashString(JSON.stringify({ goal: resolvedRequest.goal, campaignName, targetCount, routes }))}`,
|
|
2516
|
+
planId: `cbp_${hashString(JSON.stringify({ goal: resolvedRequest.goal, campaignName, targetCount, routes, evidence }))}`,
|
|
2251
2517
|
campaignName,
|
|
2252
2518
|
goal: resolvedRequest.goal,
|
|
2253
2519
|
targetCount,
|
|
@@ -2262,6 +2528,7 @@ function createCampaignBuilderAgentPlan(request, context = {}) {
|
|
|
2262
2528
|
approvals,
|
|
2263
2529
|
buildRequest,
|
|
2264
2530
|
brainPreflight,
|
|
2531
|
+
evidence,
|
|
2265
2532
|
operatingPlaybooks,
|
|
2266
2533
|
mcpFlow: createMcpFlow(resolvedRequest, buildRequest, routes, memory, approvals),
|
|
2267
2534
|
estimatedCredits,
|
|
@@ -2353,6 +2620,90 @@ function createCampaignBuilderReadiness(plan) {
|
|
|
2353
2620
|
nextActions: campaignReadinessNextActions(plan, ready, customRoutes.length)
|
|
2354
2621
|
};
|
|
2355
2622
|
}
|
|
2623
|
+
function createCampaignBuilderActiveWorkContext(input) {
|
|
2624
|
+
const plan = input.readiness?.plan ?? input.plan;
|
|
2625
|
+
const remembered = input.remembered ?? {};
|
|
2626
|
+
const lastResult = asRecord(input.lastResult);
|
|
2627
|
+
const readinessBlockers = input.readiness?.summary.blockers ?? [];
|
|
2628
|
+
const explicitBlockers = input.blockers ?? [];
|
|
2629
|
+
const blockers = uniqueStrings([...explicitBlockers, ...readinessBlockers]);
|
|
2630
|
+
const warnings = uniqueStrings([
|
|
2631
|
+
...input.warnings ?? [],
|
|
2632
|
+
...input.readiness?.summary.warnings ?? [],
|
|
2633
|
+
...plan?.warnings ?? []
|
|
2634
|
+
]);
|
|
2635
|
+
const missingApprovals = plan ? (input.approval ? getMissingCampaignBuilderApprovals(plan, input.approval) : plan.approvals.filter((approval) => approval.blocking)).map((approval) => approval.type) : [];
|
|
2636
|
+
const refs = normalizeCampaignBuilderActiveWorkRefs({
|
|
2637
|
+
plan,
|
|
2638
|
+
remembered,
|
|
2639
|
+
refs: input.refs,
|
|
2640
|
+
lastResult
|
|
2641
|
+
});
|
|
2642
|
+
const state = inferCampaignBuilderActiveWorkState({
|
|
2643
|
+
plan,
|
|
2644
|
+
readiness: input.readiness,
|
|
2645
|
+
remembered,
|
|
2646
|
+
lastResult,
|
|
2647
|
+
blockers,
|
|
2648
|
+
missingApprovals
|
|
2649
|
+
});
|
|
2650
|
+
const nextSafeAction = campaignBuilderActiveWorkNextAction(state, blockers, missingApprovals, refs);
|
|
2651
|
+
const approvalPacket = createCampaignBuilderApprovalPacket(plan, input.approval, missingApprovals);
|
|
2652
|
+
const nextSafeMcpAction = campaignBuilderActiveWorkNextMcpAction({
|
|
2653
|
+
state,
|
|
2654
|
+
plan,
|
|
2655
|
+
refs,
|
|
2656
|
+
approvalPacket
|
|
2657
|
+
});
|
|
2658
|
+
const summary = {
|
|
2659
|
+
plan_id: plan?.planId ?? remembered.planId ?? null,
|
|
2660
|
+
campaign_name: plan?.campaignName ?? null,
|
|
2661
|
+
target_count: plan?.targetCount ?? null,
|
|
2662
|
+
gtm_campaign_id: plan?.buildRequest.gtmCampaignId ?? remembered.gtmCampaignId ?? null,
|
|
2663
|
+
campaign_build_id: stringValue(lastResult.campaign_build_id ?? lastResult.campaignBuildId) ?? remembered.campaignBuildId ?? null,
|
|
2664
|
+
provider_campaign_id: remembered.providerCampaignId ?? null
|
|
2665
|
+
};
|
|
2666
|
+
const approvalBoundary = campaignBuilderActiveWorkApprovalBoundary(missingApprovals, state);
|
|
2667
|
+
const conversationStarters = campaignBuilderActiveWorkConversationStarters({
|
|
2668
|
+
state,
|
|
2669
|
+
blockers,
|
|
2670
|
+
missingApprovals,
|
|
2671
|
+
refs,
|
|
2672
|
+
nextSafeAction
|
|
2673
|
+
});
|
|
2674
|
+
return {
|
|
2675
|
+
packet_version: "campaign-builder-active-work-context.v1",
|
|
2676
|
+
read_only: true,
|
|
2677
|
+
no_spend: true,
|
|
2678
|
+
no_provider_writes: true,
|
|
2679
|
+
private_safe: true,
|
|
2680
|
+
state,
|
|
2681
|
+
summary,
|
|
2682
|
+
refs,
|
|
2683
|
+
blockers,
|
|
2684
|
+
warnings,
|
|
2685
|
+
missing_approval_types: missingApprovals,
|
|
2686
|
+
approval_packet: approvalPacket,
|
|
2687
|
+
approval_boundary: approvalBoundary,
|
|
2688
|
+
next_safe_action: nextSafeAction,
|
|
2689
|
+
next_safe_mcp_action: nextSafeMcpAction,
|
|
2690
|
+
conversation_starters: conversationStarters,
|
|
2691
|
+
openai_agent_handoff: createCampaignBuilderOpenAiAgentHandoff({
|
|
2692
|
+
plan,
|
|
2693
|
+
state,
|
|
2694
|
+
summary,
|
|
2695
|
+
refs,
|
|
2696
|
+
blockers,
|
|
2697
|
+
warnings,
|
|
2698
|
+
missingApprovals,
|
|
2699
|
+
approvalPacket,
|
|
2700
|
+
approvalBoundary,
|
|
2701
|
+
nextSafeAction,
|
|
2702
|
+
nextSafeMcpAction,
|
|
2703
|
+
conversationStarters
|
|
2704
|
+
})
|
|
2705
|
+
};
|
|
2706
|
+
}
|
|
2356
2707
|
function createCampaignBuilderProofReceipt(plan, result) {
|
|
2357
2708
|
const strategyMemory = asRecord(plan.strategyMemoryStatus);
|
|
2358
2709
|
const dryRunResult = asRecord(result);
|
|
@@ -2411,6 +2762,7 @@ function createCampaignBuilderProofReceipt(plan, result) {
|
|
|
2411
2762
|
estimated_credits: plan.estimatedCredits,
|
|
2412
2763
|
operating_playbooks: plan.operatingPlaybooks.map((playbook) => playbook.slug)
|
|
2413
2764
|
},
|
|
2765
|
+
evidence: plan.evidence,
|
|
2414
2766
|
gates,
|
|
2415
2767
|
strategy_memory_status: summarizeCampaignBuilderProofStrategyMemory(strategyMemory),
|
|
2416
2768
|
dry_run_result: summarizeCampaignBuilderProofDryRun(dryRunResult),
|
|
@@ -2459,6 +2811,56 @@ function getCampaignBuilderOperatingPlaybooksForRequest(request) {
|
|
|
2459
2811
|
]);
|
|
2460
2812
|
return slugs.map(getCampaignBuilderOperatingPlaybook).filter((playbook) => Boolean(playbook));
|
|
2461
2813
|
}
|
|
2814
|
+
function normalizeCampaignBuilderEvidence(evidence) {
|
|
2815
|
+
if (!evidence) return null;
|
|
2816
|
+
const attachmentSummary = (evidence.attachments ?? []).filter((attachment) => stringValue(attachment.name)).slice(0, 5).map((attachment) => ({
|
|
2817
|
+
name: stringValue(attachment.name) ?? "attachment",
|
|
2818
|
+
kind: stringValue(attachment.kind) ?? null,
|
|
2819
|
+
media_type: stringValue(attachment.mediaType) ?? null,
|
|
2820
|
+
redacted: attachment.redacted !== false,
|
|
2821
|
+
columns: uniqueStrings(attachment.columns ?? []).slice(0, 25),
|
|
2822
|
+
row_count: numberOrUndefined2(attachment.rowCount) ?? null,
|
|
2823
|
+
content_summary: stringValue(attachment.contentSummary)?.slice(0, 500) ?? null,
|
|
2824
|
+
evidence_fields: uniqueStrings(attachment.evidenceFields ?? []).slice(0, 25)
|
|
2825
|
+
}));
|
|
2826
|
+
const urlSummary = (evidence.urls ?? []).filter((url) => stringValue(url.url)).slice(0, 8).map((url) => {
|
|
2827
|
+
const rawUrl = stringValue(url.url) ?? "";
|
|
2828
|
+
return {
|
|
2829
|
+
url: rawUrl,
|
|
2830
|
+
host: stringValue(url.host) ?? hostFromUrl(rawUrl) ?? null,
|
|
2831
|
+
type: stringValue(url.type) ?? null,
|
|
2832
|
+
label: stringValue(url.label) ?? null,
|
|
2833
|
+
summary: stringValue(url.summary)?.slice(0, 500) ?? null,
|
|
2834
|
+
redacted: url.redacted === true
|
|
2835
|
+
};
|
|
2836
|
+
});
|
|
2837
|
+
const operatorNotes = uniqueStrings(evidence.notes ?? []).map((note) => note.slice(0, 240)).slice(0, 5);
|
|
2838
|
+
if (!attachmentSummary.length && !urlSummary.length && !operatorNotes.length) return null;
|
|
2839
|
+
return {
|
|
2840
|
+
packet_version: "campaign-builder-evidence.v1",
|
|
2841
|
+
private_safe: true,
|
|
2842
|
+
attachment_summary: attachmentSummary,
|
|
2843
|
+
url_summary: urlSummary,
|
|
2844
|
+
operator_notes: operatorNotes,
|
|
2845
|
+
privacy_boundary: "Use these attachment and URL summaries as planning evidence only. Do not expose raw private attachment text, raw rows, leads, emails, provider payloads, copy bodies, or secrets.",
|
|
2846
|
+
raw_private_fields_withheld: [
|
|
2847
|
+
"raw_private_attachment_text",
|
|
2848
|
+
"raw_attachment_rows",
|
|
2849
|
+
"raw_leads",
|
|
2850
|
+
"email_addresses",
|
|
2851
|
+
"provider_auth_payloads",
|
|
2852
|
+
"copy_bodies",
|
|
2853
|
+
"secrets"
|
|
2854
|
+
]
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2857
|
+
function hostFromUrl(value) {
|
|
2858
|
+
try {
|
|
2859
|
+
return new URL(value).hostname || void 0;
|
|
2860
|
+
} catch {
|
|
2861
|
+
return void 0;
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2462
2864
|
function applyCampaignBuilderStrategyTemplate(request) {
|
|
2463
2865
|
const template = getCampaignBuilderStrategyTemplate(request.strategyTemplate);
|
|
2464
2866
|
if (!template) return request;
|
|
@@ -3150,7 +3552,7 @@ function normalizeMemory(request) {
|
|
|
3150
3552
|
};
|
|
3151
3553
|
}
|
|
3152
3554
|
function defaultAgencyMemoryQueries(request, configured) {
|
|
3153
|
-
const operatingPlaybookTerms = getCampaignBuilderOperatingPlaybooksForRequest(request).flatMap((playbook) => [playbook.label, ...playbook.memoryKeywords]).join(" ");
|
|
3555
|
+
const operatingPlaybookTerms = getCampaignBuilderOperatingPlaybooksForRequest(request).flatMap((playbook) => [playbook.label, ...playbook.memoryKeywords, ...playbook.knowledgeSources]).join(" ");
|
|
3154
3556
|
const queries = [{
|
|
3155
3557
|
query: request.goal,
|
|
3156
3558
|
scopes: ["workspace", configured.useNetworkPatterns ? "network_patterns" : "operator_notes"],
|
|
@@ -3316,6 +3718,21 @@ function routesFromCustomerTools(tools) {
|
|
|
3316
3718
|
}))
|
|
3317
3719
|
);
|
|
3318
3720
|
}
|
|
3721
|
+
function normalizeCampaignBuilderLearningHoldout(input, gtmCampaignId, campaignName) {
|
|
3722
|
+
const source = asRecord(input);
|
|
3723
|
+
if (source.enabled === false) return { enabled: false };
|
|
3724
|
+
const controlPercentage = numberOrUndefined2(source.control_percentage ?? source.controlPercentage);
|
|
3725
|
+
const minControlSize = numberOrUndefined2(source.min_control_size ?? source.minControlSize);
|
|
3726
|
+
const experimentKey = stringValue(source.experiment_key ?? source.experimentKey) ?? (gtmCampaignId ? `gtm_campaign:${gtmCampaignId}:learning_holdout` : `campaign_builder_agent:${hashString(campaignName)}:learning_holdout`);
|
|
3727
|
+
return {
|
|
3728
|
+
enabled: true,
|
|
3729
|
+
controlPercentage: controlPercentage ?? 0.2,
|
|
3730
|
+
minControlSize: minControlSize ?? 50,
|
|
3731
|
+
experimentKey,
|
|
3732
|
+
sourceCampaignId: stringValue(source.source_campaign_id ?? source.sourceCampaignId) ?? gtmCampaignId,
|
|
3733
|
+
primaryMetric: stringValue(source.primary_metric ?? source.primaryMetric) ?? "positive_reply_rate"
|
|
3734
|
+
};
|
|
3735
|
+
}
|
|
3319
3736
|
function createBuildRequest(request, defaults, targetCount, campaignName, scopeBuildArgs, routes = [], memory, scopeBrainPreflight) {
|
|
3320
3737
|
const buildRequest = {
|
|
3321
3738
|
name: campaignName,
|
|
@@ -3346,8 +3763,15 @@ function createBuildRequest(request, defaults, targetCount, campaignName, scopeB
|
|
|
3346
3763
|
fillPolicy: "aggressive"
|
|
3347
3764
|
}
|
|
3348
3765
|
};
|
|
3766
|
+
const evidence = normalizeCampaignBuilderEvidence(request.evidence);
|
|
3767
|
+
if (evidence) buildRequest.evidence = evidence;
|
|
3349
3768
|
const scoped = asRecord(scopeBuildArgs);
|
|
3350
3769
|
buildRequest.gtmCampaignId = request.gtmCampaignId ?? stringValue(scoped.gtm_campaign_id ?? scoped.gtmCampaignId);
|
|
3770
|
+
buildRequest.learningHoldout = normalizeCampaignBuilderLearningHoldout(
|
|
3771
|
+
request.learningHoldout ?? scoped.learning_holdout ?? scoped.learningHoldout,
|
|
3772
|
+
buildRequest.gtmCampaignId,
|
|
3773
|
+
campaignName
|
|
3774
|
+
);
|
|
3351
3775
|
if (typeof scoped.name === "string") buildRequest.name = scoped.name;
|
|
3352
3776
|
if (typeof scoped.prompt === "string") buildRequest.prompt = scoped.prompt;
|
|
3353
3777
|
if (typeof scoped.target_count === "number") buildRequest.targetCount = scoped.target_count;
|
|
@@ -3372,6 +3796,7 @@ function createBrainPreflight(request, buildRequest, routes, memory) {
|
|
|
3372
3796
|
memory.enabled ? "feedback" : void 0
|
|
3373
3797
|
]);
|
|
3374
3798
|
const providerChain = uniqueStrings(["signaliz", ...request.preferredProviders ?? [], ...routes.map((route) => route.provider)]);
|
|
3799
|
+
const evidence = buildRequest.evidence ?? normalizeCampaignBuilderEvidence(request.evidence);
|
|
3375
3800
|
const targetIcp = compact({
|
|
3376
3801
|
personas: buildRequest.icp?.personas,
|
|
3377
3802
|
industries: buildRequest.icp?.industries,
|
|
@@ -3384,6 +3809,7 @@ function createBrainPreflight(request, buildRequest, routes, memory) {
|
|
|
3384
3809
|
const learningArgs = compact({
|
|
3385
3810
|
campaign_brief: request.goal,
|
|
3386
3811
|
target_icp: targetIcp,
|
|
3812
|
+
evidence_context: evidence,
|
|
3387
3813
|
layers,
|
|
3388
3814
|
provider_chain: providerChain,
|
|
3389
3815
|
lead_source: providerChain[0] ?? "signaliz",
|
|
@@ -3395,6 +3821,7 @@ function createBrainPreflight(request, buildRequest, routes, memory) {
|
|
|
3395
3821
|
const defaultsArgs = compact({
|
|
3396
3822
|
campaign_brief: request.goal,
|
|
3397
3823
|
target_icp: targetIcp,
|
|
3824
|
+
evidence_context: evidence,
|
|
3398
3825
|
layers,
|
|
3399
3826
|
include_global: memory.useNetworkPatterns,
|
|
3400
3827
|
include_memory: memory.enabled,
|
|
@@ -3405,6 +3832,7 @@ function createBrainPreflight(request, buildRequest, routes, memory) {
|
|
|
3405
3832
|
provider_chain: providerChain,
|
|
3406
3833
|
lead_source: providerChain[0] ?? "signaliz",
|
|
3407
3834
|
target_icp: targetIcp,
|
|
3835
|
+
evidence_context: evidence,
|
|
3408
3836
|
include_global: memory.useNetworkPatterns,
|
|
3409
3837
|
limit: 25
|
|
3410
3838
|
});
|
|
@@ -3417,11 +3845,14 @@ function createBrainPreflight(request, buildRequest, routes, memory) {
|
|
|
3417
3845
|
label: playbook.label,
|
|
3418
3846
|
quality_gates: playbook.qualityGates,
|
|
3419
3847
|
required_fields: playbook.requiredFields,
|
|
3420
|
-
activation_boundary: playbook.activationBoundary
|
|
3848
|
+
activation_boundary: playbook.activationBoundary,
|
|
3849
|
+
knowledge_sources: playbook.knowledgeSources
|
|
3421
3850
|
})),
|
|
3422
3851
|
target_icp: targetIcp,
|
|
3852
|
+
evidence_context: evidence,
|
|
3423
3853
|
provider_chain: providerChain,
|
|
3424
3854
|
lead_source: providerChain[0] ?? "signaliz",
|
|
3855
|
+
learning_holdout: buildRequest.learningHoldout,
|
|
3425
3856
|
tool_sequence: [
|
|
3426
3857
|
{ tool: "gtm_brain_learning_cycle_plan", purpose: "Plan the Brain learning loop for this campaign.", arguments: learningArgs },
|
|
3427
3858
|
{ tool: "gtm_brain_seed_defaults", purpose: "Seed campaign defaults from workspace memory and safe aggregate patterns.", arguments: defaultsArgs },
|
|
@@ -3691,7 +4122,7 @@ function createMcpFlow(request, buildRequest, routes, memory, approvals) {
|
|
|
3691
4122
|
phase: "plan",
|
|
3692
4123
|
tool: "nango_mcp_tools_list",
|
|
3693
4124
|
arguments: {
|
|
3694
|
-
format: "
|
|
4125
|
+
format: "openai"
|
|
3695
4126
|
},
|
|
3696
4127
|
readOnly: true,
|
|
3697
4128
|
approvalRequired: false
|
|
@@ -4000,6 +4431,7 @@ function buildGtmCampaignBuildPlanArgs(request, buildRequest, routes, memory) {
|
|
|
4000
4431
|
layers: layers.length > 0 ? layers : void 0,
|
|
4001
4432
|
preferred_providers: preferredProviders.length > 0 ? preferredProviders : void 0,
|
|
4002
4433
|
strategy_model: strategyModel,
|
|
4434
|
+
evidence_context: buildRequest.evidence ?? normalizeCampaignBuilderEvidence(request.evidence),
|
|
4003
4435
|
include_strategy_patterns: campaignBuilderStrategyPatternsEnabled(agencyContext),
|
|
4004
4436
|
include_workflow_patterns: campaignBuilderWorkflowPatternsEnabled(agencyContext),
|
|
4005
4437
|
include_nango_catalog: agencyContext.includeNangoCatalog !== false,
|
|
@@ -4008,7 +4440,8 @@ function buildGtmCampaignBuildPlanArgs(request, buildRequest, routes, memory) {
|
|
|
4008
4440
|
slug: playbook.slug,
|
|
4009
4441
|
label: playbook.label,
|
|
4010
4442
|
quality_gates: playbook.qualityGates,
|
|
4011
|
-
activation_boundary: playbook.activationBoundary
|
|
4443
|
+
activation_boundary: playbook.activationBoundary,
|
|
4444
|
+
knowledge_sources: playbook.knowledgeSources
|
|
4012
4445
|
})),
|
|
4013
4446
|
include_memory: memory.enabled,
|
|
4014
4447
|
include_brain: true,
|
|
@@ -4089,8 +4522,19 @@ function buildCampaignArgs(request) {
|
|
|
4089
4522
|
if (request.dedupKeys) args.dedup_keys = request.dedupKeys;
|
|
4090
4523
|
if (request.brainPreflight) args.brain_preflight = request.brainPreflight;
|
|
4091
4524
|
if (request.brainDefaults) args.brain_defaults = request.brainDefaults;
|
|
4525
|
+
if (request.learningHoldout) {
|
|
4526
|
+
args.learning_holdout = compact({
|
|
4527
|
+
enabled: request.learningHoldout.enabled,
|
|
4528
|
+
control_percentage: request.learningHoldout.controlPercentage,
|
|
4529
|
+
min_control_size: request.learningHoldout.minControlSize,
|
|
4530
|
+
experiment_key: request.learningHoldout.experimentKey,
|
|
4531
|
+
source_campaign_id: request.learningHoldout.sourceCampaignId,
|
|
4532
|
+
primary_metric: request.learningHoldout.primaryMetric
|
|
4533
|
+
});
|
|
4534
|
+
}
|
|
4092
4535
|
if (request.campaignStrategyContext) args.campaign_strategy_context = request.campaignStrategyContext;
|
|
4093
4536
|
if (request.deliveryRisk) args.delivery_risk = request.deliveryRisk;
|
|
4537
|
+
if (request.evidence) args.evidence_context = request.evidence;
|
|
4094
4538
|
if (request.icp) {
|
|
4095
4539
|
args.icp = compact({
|
|
4096
4540
|
personas: request.icp.personas,
|
|
@@ -4140,7 +4584,8 @@ function buildCampaignArgs(request) {
|
|
|
4140
4584
|
max_body_words: request.copy.maxBodyWords,
|
|
4141
4585
|
sender_context: request.copy.senderContext,
|
|
4142
4586
|
offer_context: request.copy.offerContext,
|
|
4143
|
-
model: request.copy.model
|
|
4587
|
+
model: request.copy.model,
|
|
4588
|
+
evidence_mode: request.copy.evidenceMode
|
|
4144
4589
|
});
|
|
4145
4590
|
}
|
|
4146
4591
|
if (request.delivery) {
|
|
@@ -4185,6 +4630,7 @@ function buildExecutionPrepareArgs(request, dryRun, confirmSpend) {
|
|
|
4185
4630
|
delivery_risk: buildArgs2.delivery_risk,
|
|
4186
4631
|
dedup_keys: buildArgs2.dedup_keys,
|
|
4187
4632
|
policy: buildArgs2.policy,
|
|
4633
|
+
learning_holdout: buildArgs2.learning_holdout,
|
|
4188
4634
|
signals: buildArgs2.signals,
|
|
4189
4635
|
qualification: buildArgs2.qualification,
|
|
4190
4636
|
copy: buildArgs2.copy,
|
|
@@ -4219,10 +4665,145 @@ function mapBuildResult(data, dryRun) {
|
|
|
4219
4665
|
targetLimitReason: data.target_limit_reason,
|
|
4220
4666
|
maxSupportedTargetCount: data.max_supported_target_count,
|
|
4221
4667
|
brainContext: data.brain_context ?? {},
|
|
4668
|
+
learningHoldout: data.learning_holdout ?? {},
|
|
4222
4669
|
providerRoute: mapProviderRoute2(data)
|
|
4223
4670
|
};
|
|
4224
4671
|
}
|
|
4672
|
+
var CAMPAIGN_BUILDER_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "canceled", "cancelled"]);
|
|
4673
|
+
var CAMPAIGN_BUILDER_BLOCKED_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["failed", "canceled", "cancelled"]);
|
|
4674
|
+
function mapAgentCampaignPhaseHealth(value) {
|
|
4675
|
+
if (!Array.isArray(value)) return [];
|
|
4676
|
+
return value.map((phase) => {
|
|
4677
|
+
const record = asRecord(phase);
|
|
4678
|
+
return {
|
|
4679
|
+
phase: stringValue(record.phase) ?? "unknown",
|
|
4680
|
+
status: stringValue(record.status) ?? "unknown",
|
|
4681
|
+
recordsProcessed: numberOrNull2(record.records_processed ?? record.recordsProcessed),
|
|
4682
|
+
recordsSucceeded: numberOrNull2(record.records_succeeded ?? record.recordsSucceeded),
|
|
4683
|
+
recordsFailed: numberOrNull2(record.records_failed ?? record.recordsFailed),
|
|
4684
|
+
heartbeatAgeSeconds: numberOrNull2(record.heartbeat_age_seconds ?? record.heartbeatAgeSeconds ?? record.phase_age_seconds)
|
|
4685
|
+
};
|
|
4686
|
+
});
|
|
4687
|
+
}
|
|
4688
|
+
function deriveCampaignBuildTerminalState(input) {
|
|
4689
|
+
const status = input.status.toLowerCase();
|
|
4690
|
+
const heartbeatAgeSeconds = input.phaseAgeSeconds;
|
|
4691
|
+
const stale = input.staleRunningPhase === true;
|
|
4692
|
+
const safeWatchAction = input.triggerRunId ? `signaliz ops run-status ${input.triggerRunId} --watch` : "signaliz campaign-agent status <campaign_build_id>";
|
|
4693
|
+
if (CAMPAIGN_BUILDER_BLOCKED_TERMINAL_STATUSES.has(status)) {
|
|
4694
|
+
return {
|
|
4695
|
+
kind: "blocked",
|
|
4696
|
+
label: "Blocked terminal state",
|
|
4697
|
+
reviewable: true,
|
|
4698
|
+
terminal: true,
|
|
4699
|
+
stale: false,
|
|
4700
|
+
phase: input.phase,
|
|
4701
|
+
phaseStatus: input.phaseStatus,
|
|
4702
|
+
heartbeatAgeSeconds,
|
|
4703
|
+
nextPollAfterSeconds: input.nextPollAfterSeconds,
|
|
4704
|
+
terminalReason: input.terminalReason,
|
|
4705
|
+
staleReason: null,
|
|
4706
|
+
safeNextAction: input.nextAction || "Review errors and rerun only after the blocker is resolved."
|
|
4707
|
+
};
|
|
4708
|
+
}
|
|
4709
|
+
if (status === "pending_approval") {
|
|
4710
|
+
return {
|
|
4711
|
+
kind: "pending_approval",
|
|
4712
|
+
label: "Pending delivery approval",
|
|
4713
|
+
reviewable: true,
|
|
4714
|
+
terminal: false,
|
|
4715
|
+
stale: false,
|
|
4716
|
+
phase: input.phase,
|
|
4717
|
+
phaseStatus: input.phaseStatus,
|
|
4718
|
+
heartbeatAgeSeconds,
|
|
4719
|
+
nextPollAfterSeconds: input.nextPollAfterSeconds,
|
|
4720
|
+
terminalReason: input.terminalReason,
|
|
4721
|
+
staleReason: null,
|
|
4722
|
+
safeNextAction: input.approvalAction || input.nextAction || "Review rows and approve delivery only after the approval packet passes."
|
|
4723
|
+
};
|
|
4724
|
+
}
|
|
4725
|
+
if (CAMPAIGN_BUILDER_TERMINAL_STATUSES.has(status)) {
|
|
4726
|
+
return {
|
|
4727
|
+
kind: "terminal",
|
|
4728
|
+
label: "Terminal and reviewable",
|
|
4729
|
+
reviewable: true,
|
|
4730
|
+
terminal: true,
|
|
4731
|
+
stale: false,
|
|
4732
|
+
phase: input.phase,
|
|
4733
|
+
phaseStatus: input.phaseStatus,
|
|
4734
|
+
heartbeatAgeSeconds,
|
|
4735
|
+
nextPollAfterSeconds: input.nextPollAfterSeconds,
|
|
4736
|
+
terminalReason: input.terminalReason,
|
|
4737
|
+
staleReason: null,
|
|
4738
|
+
safeNextAction: input.nextAction || "Review rows, artifacts, scorecard, and approval locks before delivery."
|
|
4739
|
+
};
|
|
4740
|
+
}
|
|
4741
|
+
if (status === "running" || status === "queued" || status === "processing") {
|
|
4742
|
+
if (stale) {
|
|
4743
|
+
return {
|
|
4744
|
+
kind: "running_stale",
|
|
4745
|
+
label: "Running but stale",
|
|
4746
|
+
reviewable: false,
|
|
4747
|
+
terminal: false,
|
|
4748
|
+
stale: true,
|
|
4749
|
+
phase: input.phase,
|
|
4750
|
+
phaseStatus: input.phaseStatus,
|
|
4751
|
+
heartbeatAgeSeconds,
|
|
4752
|
+
nextPollAfterSeconds: input.nextPollAfterSeconds,
|
|
4753
|
+
terminalReason: null,
|
|
4754
|
+
staleReason: input.staleReason || "The active phase heartbeat is stale.",
|
|
4755
|
+
safeNextAction: input.nextAction || safeWatchAction
|
|
4756
|
+
};
|
|
4757
|
+
}
|
|
4758
|
+
return {
|
|
4759
|
+
kind: "running_healthy",
|
|
4760
|
+
label: "Running and healthy",
|
|
4761
|
+
reviewable: false,
|
|
4762
|
+
terminal: false,
|
|
4763
|
+
stale: false,
|
|
4764
|
+
phase: input.phase,
|
|
4765
|
+
phaseStatus: input.phaseStatus,
|
|
4766
|
+
heartbeatAgeSeconds,
|
|
4767
|
+
nextPollAfterSeconds: input.nextPollAfterSeconds,
|
|
4768
|
+
terminalReason: null,
|
|
4769
|
+
staleReason: null,
|
|
4770
|
+
safeNextAction: input.nextAction || safeWatchAction
|
|
4771
|
+
};
|
|
4772
|
+
}
|
|
4773
|
+
return {
|
|
4774
|
+
kind: "unknown",
|
|
4775
|
+
label: "Unknown build state",
|
|
4776
|
+
reviewable: false,
|
|
4777
|
+
terminal: false,
|
|
4778
|
+
stale,
|
|
4779
|
+
phase: input.phase,
|
|
4780
|
+
phaseStatus: input.phaseStatus,
|
|
4781
|
+
heartbeatAgeSeconds,
|
|
4782
|
+
nextPollAfterSeconds: input.nextPollAfterSeconds,
|
|
4783
|
+
terminalReason: input.terminalReason,
|
|
4784
|
+
staleReason: input.staleReason,
|
|
4785
|
+
safeNextAction: input.nextAction || "Read campaign build status again before making a launch or delivery decision."
|
|
4786
|
+
};
|
|
4787
|
+
}
|
|
4225
4788
|
function mapAgentCampaignBuildStatus(data) {
|
|
4789
|
+
const customerRowCounts = mapAgentCustomerRowCounts(data.customer_row_counts);
|
|
4790
|
+
const phaseHealth = mapAgentCampaignPhaseHealth(data.phases);
|
|
4791
|
+
const terminalReason = stringValue(data.terminal_reason) ?? customerRowCounts?.terminalReason ?? null;
|
|
4792
|
+
const staleReason = stringValue(data.stale_reason) ?? stringValue(asRecord(data.diagnostics).stale_reason) ?? null;
|
|
4793
|
+
const nextPollAfterSeconds = numberOrNull2(data.next_poll_after_seconds ?? data.nextPollAfterSeconds);
|
|
4794
|
+
const terminalState = deriveCampaignBuildTerminalState({
|
|
4795
|
+
status: stringValue(data.status) ?? "unknown",
|
|
4796
|
+
phase: stringValue(data.current_phase) ?? null,
|
|
4797
|
+
phaseStatus: stringValue(data.current_phase_status) ?? null,
|
|
4798
|
+
staleRunningPhase: data.stale_running_phase === true,
|
|
4799
|
+
phaseAgeSeconds: numberOrNull2(data.phase_age_seconds),
|
|
4800
|
+
nextPollAfterSeconds,
|
|
4801
|
+
terminalReason,
|
|
4802
|
+
staleReason,
|
|
4803
|
+
nextAction: formatAgentNextAction(data.next_action),
|
|
4804
|
+
approvalAction: formatAgentNextAction(data.approval_action),
|
|
4805
|
+
triggerRunId: stringValue(data.trigger_run_id) ?? null
|
|
4806
|
+
});
|
|
4226
4807
|
return {
|
|
4227
4808
|
campaignBuildId: stringValue(data.campaign_build_id) ?? "",
|
|
4228
4809
|
campaignId: stringValue(data.campaign_id) ?? null,
|
|
@@ -4240,12 +4821,18 @@ function mapAgentCampaignBuildStatus(data) {
|
|
|
4240
4821
|
errors: diagnosticMessages2(data.errors),
|
|
4241
4822
|
artifactCount: numberOrUndefined2(data.artifact_count) ?? 0,
|
|
4242
4823
|
artifactDownloads: Array.isArray(data.artifact_downloads) ? data.artifact_downloads.map(mapAgentCampaignArtifactDownload) : [],
|
|
4243
|
-
customerRowCounts
|
|
4824
|
+
customerRowCounts,
|
|
4244
4825
|
phaseAttemptTotals: nullableRecord(data.phase_attempt_totals) ?? void 0,
|
|
4245
4826
|
providerRoute: mapProviderRoute2(data),
|
|
4246
4827
|
triggerRunId: stringValue(data.trigger_run_id) ?? null,
|
|
4247
4828
|
staleRunningPhase: data.stale_running_phase === true,
|
|
4248
4829
|
phaseAgeSeconds: numberOrNull2(data.phase_age_seconds),
|
|
4830
|
+
nextPollAfterSeconds,
|
|
4831
|
+
terminalReason,
|
|
4832
|
+
staleReason,
|
|
4833
|
+
safeNextAction: terminalState.safeNextAction,
|
|
4834
|
+
terminalState,
|
|
4835
|
+
phaseHealth,
|
|
4249
4836
|
diagnostics: nullableRecord(data.diagnostics) ?? {},
|
|
4250
4837
|
nextAction: formatAgentNextAction(data.next_action),
|
|
4251
4838
|
approvalAction: formatAgentNextAction(data.approval_action),
|
|
@@ -4275,6 +4862,7 @@ function mapAgentCustomerRowCounts(value) {
|
|
|
4275
4862
|
acceptedRows: numberOrUndefined2(record.accepted_rows),
|
|
4276
4863
|
usableRows: numberOrUndefined2(record.usable_rows),
|
|
4277
4864
|
copiedRows: numberOrUndefined2(record.copied_rows),
|
|
4865
|
+
copySkippedRows: numberOrUndefined2(record.copy_skipped_rows),
|
|
4278
4866
|
approvalReadyRows: numberOrUndefined2(record.approval_ready_rows),
|
|
4279
4867
|
qaReadyRows: numberOrUndefined2(record.qa_ready_rows),
|
|
4280
4868
|
deliveredRows: numberOrUndefined2(record.delivered_rows),
|
|
@@ -4333,67 +4921,285 @@ function mapAgentCampaignArtifact(data) {
|
|
|
4333
4921
|
createdAt: stringValue(data.created_at) ?? ""
|
|
4334
4922
|
};
|
|
4335
4923
|
}
|
|
4924
|
+
var NORTH_STAR_BANNED_COPY_PHRASES = [
|
|
4925
|
+
"i have been following",
|
|
4926
|
+
"i saw that",
|
|
4927
|
+
"would you be opposed",
|
|
4928
|
+
"unlock new opportunities",
|
|
4929
|
+
"significant success",
|
|
4930
|
+
"we understand",
|
|
4931
|
+
"hope this finds you well"
|
|
4932
|
+
];
|
|
4933
|
+
function campaignReviewGate(id, label, status, detail) {
|
|
4934
|
+
return {
|
|
4935
|
+
id,
|
|
4936
|
+
label,
|
|
4937
|
+
status,
|
|
4938
|
+
passed: status !== "blocked",
|
|
4939
|
+
detail
|
|
4940
|
+
};
|
|
4941
|
+
}
|
|
4942
|
+
function northStarMetric(id, label, status, detail, value, target) {
|
|
4943
|
+
return {
|
|
4944
|
+
id,
|
|
4945
|
+
label,
|
|
4946
|
+
status,
|
|
4947
|
+
detail,
|
|
4948
|
+
...value !== void 0 ? { value } : {},
|
|
4949
|
+
...target !== void 0 ? { target } : {}
|
|
4950
|
+
};
|
|
4951
|
+
}
|
|
4952
|
+
function createCampaignBuilderNorthStarScorecard(status, rows, artifacts, terminalState) {
|
|
4953
|
+
if (!terminalState.reviewable) {
|
|
4954
|
+
const pendingMetric = northStarMetric(
|
|
4955
|
+
"terminal_state",
|
|
4956
|
+
"Terminal-state contract",
|
|
4957
|
+
terminalState.kind === "running_stale" ? "blocked" : "pending",
|
|
4958
|
+
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.`,
|
|
4959
|
+
terminalState.kind,
|
|
4960
|
+
"terminal or pending_approval"
|
|
4961
|
+
);
|
|
4962
|
+
return {
|
|
4963
|
+
version: "campaign-builder-north-star.v1",
|
|
4964
|
+
status: pendingMetric.status,
|
|
4965
|
+
score: null,
|
|
4966
|
+
moatState: "scaffolding_ready",
|
|
4967
|
+
metrics: [pendingMetric],
|
|
4968
|
+
blockers: pendingMetric.status === "blocked" ? [pendingMetric.detail] : [],
|
|
4969
|
+
warnings: pendingMetric.status === "pending" ? [pendingMetric.detail] : [],
|
|
4970
|
+
nextAction: terminalState.safeNextAction
|
|
4971
|
+
};
|
|
4972
|
+
}
|
|
4973
|
+
const sampledRows = rows.rows.length;
|
|
4974
|
+
const rowData = rows.rows.map((row) => asRecord(row.data));
|
|
4975
|
+
const requestedTarget = status.customerRowCounts?.requestedTarget ?? status.recordsTotal ?? rows.count;
|
|
4976
|
+
const finalRows = status.customerRowCounts?.deliveredRows ?? status.customerRowCounts?.qaReadyRows ?? status.customerRowCounts?.copySkippedRows ?? status.customerRowCounts?.copiedRows ?? status.customerRowCounts?.acceptedRows ?? rows.count;
|
|
4977
|
+
const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
|
|
4978
|
+
const verifiedEmailRows = rowData.filter(campaignReviewDataHasVerifiedEmail).length;
|
|
4979
|
+
const uniqueEmails = new Set(rowData.map(campaignReviewDataEmail).filter(Boolean)).size;
|
|
4980
|
+
const uniqueDomains = new Set(rowData.map(campaignReviewDataDomain).filter(Boolean)).size;
|
|
4981
|
+
const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
|
|
4982
|
+
const copyQaIssuesByRow = rowData.map(campaignReviewDataCopyQaIssues).filter((issues) => issues.length > 0);
|
|
4983
|
+
const copyQaIssueCounts = countCampaignReviewIssues(copyQaIssuesByRow);
|
|
4984
|
+
const signalBackedRows = rowData.filter(campaignReviewDataHasSignalBasis).length;
|
|
4985
|
+
const noSupportedSignalRows = rowData.filter(campaignReviewDataHasNoSupportedSignal).length;
|
|
4986
|
+
const unsupportedSignalClaims = rowData.filter(campaignReviewDataHasUnsupportedSignalClaim).length;
|
|
4987
|
+
const approvalLockedRows = rowData.filter(campaignReviewDataHasApprovalLock).length;
|
|
4988
|
+
const holdoutRows = rowData.filter(campaignReviewDataHasHoldoutAttribution).length;
|
|
4989
|
+
const artifactCount = artifacts.length || status.artifactCount || 0;
|
|
4990
|
+
const deliveredRows = status.customerRowCounts?.deliveredRows ?? 0;
|
|
4991
|
+
const feedbackReady = campaignReviewStatusHasFeedbackReadiness(status);
|
|
4992
|
+
const feedbackRequiredForApproval = terminalState.kind === "pending_approval";
|
|
4993
|
+
const moat = campaignReviewMoatState(status, holdoutRows);
|
|
4994
|
+
const metrics = [
|
|
4995
|
+
northStarMetric(
|
|
4996
|
+
"terminal_state",
|
|
4997
|
+
"Terminal-state contract",
|
|
4998
|
+
terminalState.kind === "blocked" ? "blocked" : "pass",
|
|
4999
|
+
terminalState.label,
|
|
5000
|
+
terminalState.kind,
|
|
5001
|
+
"terminal or pending_approval"
|
|
5002
|
+
),
|
|
5003
|
+
northStarMetric(
|
|
5004
|
+
"target_fill",
|
|
5005
|
+
"Target fill",
|
|
5006
|
+
requestedTarget && finalRows < requestedTarget ? "review" : "pass",
|
|
5007
|
+
requestedTarget ? `${finalRows || 0}/${requestedTarget} final or reviewable row(s) are accounted for.` : "No requested target was reported; using sampled rows for review.",
|
|
5008
|
+
finalRows || sampledRows,
|
|
5009
|
+
requestedTarget || sampledRows
|
|
5010
|
+
),
|
|
5011
|
+
northStarMetric(
|
|
5012
|
+
"unique_emails",
|
|
5013
|
+
"Unique emails",
|
|
5014
|
+
sampledRows === 0 ? "blocked" : uniqueEmails === rowsWithEmail ? "pass" : "blocked",
|
|
5015
|
+
sampledRows === 0 ? "No sampled rows were available for email uniqueness review." : `${uniqueEmails}/${rowsWithEmail} sampled email(s) are unique.`,
|
|
5016
|
+
uniqueEmails,
|
|
5017
|
+
rowsWithEmail
|
|
5018
|
+
),
|
|
5019
|
+
northStarMetric(
|
|
5020
|
+
"unique_domains",
|
|
5021
|
+
"Unique domains",
|
|
5022
|
+
sampledRows === 0 ? "blocked" : uniqueDomains === sampledRows ? "pass" : "review",
|
|
5023
|
+
sampledRows === 0 ? "No sampled rows were available for domain uniqueness review." : `${uniqueDomains}/${sampledRows} sampled company domain(s) are unique.`,
|
|
5024
|
+
uniqueDomains,
|
|
5025
|
+
sampledRows
|
|
5026
|
+
),
|
|
5027
|
+
northStarMetric(
|
|
5028
|
+
"verified_email_rows",
|
|
5029
|
+
"Verified email rows",
|
|
5030
|
+
sampledRows === 0 ? "blocked" : verifiedEmailRows === sampledRows ? "pass" : rowsWithEmail === sampledRows ? "review" : "blocked",
|
|
5031
|
+
sampledRows === 0 ? "No sampled rows were available for email verification review." : `${verifiedEmailRows}/${sampledRows} sampled row(s) carry verified-email evidence.`,
|
|
5032
|
+
verifiedEmailRows,
|
|
5033
|
+
sampledRows
|
|
5034
|
+
),
|
|
5035
|
+
northStarMetric(
|
|
5036
|
+
"copy_completeness",
|
|
5037
|
+
"Copy completeness",
|
|
5038
|
+
sampledRows === 0 ? "blocked" : copyReadyRows === sampledRows ? "pass" : "blocked",
|
|
5039
|
+
sampledRows === 0 ? "No sampled rows were available for copy review." : `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.`,
|
|
5040
|
+
copyReadyRows,
|
|
5041
|
+
sampledRows
|
|
5042
|
+
),
|
|
5043
|
+
northStarMetric(
|
|
5044
|
+
"copy_quality",
|
|
5045
|
+
"Copy QA",
|
|
5046
|
+
copyQaIssuesByRow.length === 0 ? "pass" : "blocked",
|
|
5047
|
+
copyQaIssuesByRow.length === 0 ? "No sampled copy QA blockers were detected." : `${copyQaIssuesByRow.length}/${sampledRows} sampled row(s) have copy QA issue(s): ${formatCampaignReviewIssueCounts(copyQaIssueCounts)}.`,
|
|
5048
|
+
copyQaIssuesByRow.length,
|
|
5049
|
+
0
|
|
5050
|
+
),
|
|
5051
|
+
northStarMetric(
|
|
5052
|
+
"signal_grounding",
|
|
5053
|
+
"Signal grounding",
|
|
5054
|
+
unsupportedSignalClaims > 0 ? "blocked" : signalBackedRows > 0 ? "pass" : "review",
|
|
5055
|
+
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.",
|
|
5056
|
+
signalBackedRows,
|
|
5057
|
+
sampledRows
|
|
5058
|
+
),
|
|
5059
|
+
northStarMetric(
|
|
5060
|
+
"approval_locks",
|
|
5061
|
+
"Approval locks",
|
|
5062
|
+
approvalLockedRows > 0 || deliveredRows === 0 ? "pass" : "review",
|
|
5063
|
+
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.",
|
|
5064
|
+
approvalLockedRows,
|
|
5065
|
+
sampledRows
|
|
5066
|
+
),
|
|
5067
|
+
northStarMetric(
|
|
5068
|
+
"export_send_safety",
|
|
5069
|
+
"Export/send safety",
|
|
5070
|
+
deliveredRows > 0 && status.status !== "completed" ? "blocked" : "pass",
|
|
5071
|
+
deliveredRows > 0 ? `${deliveredRows} row(s) are delivered; status is ${status.status}.` : "No delivered rows are reported before approval.",
|
|
5072
|
+
deliveredRows,
|
|
5073
|
+
0
|
|
5074
|
+
),
|
|
5075
|
+
northStarMetric(
|
|
5076
|
+
"holdout_attribution",
|
|
5077
|
+
"Holdout attribution",
|
|
5078
|
+
holdoutRows > 0 ? "pass" : "review",
|
|
5079
|
+
holdoutRows > 0 ? `${holdoutRows}/${sampledRows} sampled row(s) preserve holdout attribution.` : "No holdout attribution was visible in the sampled rows.",
|
|
5080
|
+
holdoutRows,
|
|
5081
|
+
sampledRows
|
|
5082
|
+
),
|
|
5083
|
+
northStarMetric(
|
|
5084
|
+
"feedback_readiness",
|
|
5085
|
+
"Feedback readiness",
|
|
5086
|
+
feedbackReady ? "pass" : feedbackRequiredForApproval ? "blocked" : "review",
|
|
5087
|
+
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.",
|
|
5088
|
+
feedbackReady,
|
|
5089
|
+
true
|
|
5090
|
+
),
|
|
5091
|
+
northStarMetric(
|
|
5092
|
+
"causal_moat",
|
|
5093
|
+
"Causal moat",
|
|
5094
|
+
moat.state === "proved" ? "pass" : "review",
|
|
5095
|
+
moat.detail,
|
|
5096
|
+
moat.state,
|
|
5097
|
+
"proved"
|
|
5098
|
+
),
|
|
5099
|
+
northStarMetric(
|
|
5100
|
+
"artifact_readback",
|
|
5101
|
+
"Artifact readback",
|
|
5102
|
+
artifactCount > 0 ? "pass" : "review",
|
|
5103
|
+
artifactCount > 0 ? `${artifactCount} artifact(s) are available for review.` : "No review artifact is available yet.",
|
|
5104
|
+
artifactCount,
|
|
5105
|
+
1
|
|
5106
|
+
)
|
|
5107
|
+
];
|
|
5108
|
+
const gradedMetrics = metrics.filter((metric) => metric.status !== "pending");
|
|
5109
|
+
const score = gradedMetrics.length === 0 ? null : Math.round(gradedMetrics.filter((metric) => metric.status === "pass").length / gradedMetrics.length * 100);
|
|
5110
|
+
const blockers = metrics.filter((metric) => metric.status === "blocked").map((metric) => metric.detail);
|
|
5111
|
+
const warnings = metrics.filter((metric) => metric.status === "review").map((metric) => metric.detail);
|
|
5112
|
+
const scorecardStatus = blockers.length > 0 ? "blocked" : warnings.length > 0 ? "review" : "pass";
|
|
5113
|
+
return {
|
|
5114
|
+
version: "campaign-builder-north-star.v1",
|
|
5115
|
+
status: scorecardStatus,
|
|
5116
|
+
score,
|
|
5117
|
+
moatState: moat.state,
|
|
5118
|
+
metrics,
|
|
5119
|
+
blockers,
|
|
5120
|
+
warnings,
|
|
5121
|
+
nextAction: scorecardStatus === "blocked" ? "Fix blocked North Star gates before approval or delivery." : terminalState.safeNextAction
|
|
5122
|
+
};
|
|
5123
|
+
}
|
|
4336
5124
|
function createCampaignBuilderBuildReview(status, rows, artifacts) {
|
|
5125
|
+
const terminalState = status.terminalState ?? deriveCampaignBuildTerminalState({
|
|
5126
|
+
status: status.status,
|
|
5127
|
+
phase: status.currentPhase,
|
|
5128
|
+
phaseStatus: status.currentPhaseStatus,
|
|
5129
|
+
staleRunningPhase: status.staleRunningPhase === true,
|
|
5130
|
+
phaseAgeSeconds: status.phaseAgeSeconds ?? null,
|
|
5131
|
+
nextPollAfterSeconds: status.nextPollAfterSeconds ?? null,
|
|
5132
|
+
terminalReason: status.terminalReason ?? status.customerRowCounts?.terminalReason ?? null,
|
|
5133
|
+
staleReason: status.staleReason ?? null,
|
|
5134
|
+
nextAction: status.nextAction,
|
|
5135
|
+
approvalAction: status.approvalAction,
|
|
5136
|
+
triggerRunId: status.triggerRunId ?? null
|
|
5137
|
+
});
|
|
4337
5138
|
const sampledRows = rows.rows.length;
|
|
4338
5139
|
const availableRows = rows.count || sampledRows;
|
|
4339
5140
|
const qualifiedRows = rows.rows.filter((row) => row.qualified !== false).length;
|
|
4340
5141
|
const rowsWithEmail = rows.rows.filter(campaignReviewRowHasEmail).length;
|
|
4341
5142
|
const copyReadyRows = rows.rows.filter(campaignReviewRowHasCopy).length;
|
|
4342
5143
|
const artifactCount = artifacts.length || status.artifactCount || 0;
|
|
5144
|
+
const northStarScorecard = createCampaignBuilderNorthStarScorecard(status, rows, artifacts, terminalState);
|
|
4343
5145
|
const gates = [
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
5146
|
+
campaignReviewGate(
|
|
5147
|
+
"build_reviewable",
|
|
5148
|
+
"Build reviewable",
|
|
5149
|
+
terminalState.kind === "running_healthy" ? "pending" : terminalState.kind === "running_stale" || terminalState.kind === "blocked" ? "blocked" : "pass",
|
|
5150
|
+
terminalState.label
|
|
5151
|
+
),
|
|
5152
|
+
campaignReviewGate(
|
|
5153
|
+
"rows_available",
|
|
5154
|
+
"Rows available",
|
|
5155
|
+
sampledRows > 0 ? "pass" : terminalState.reviewable ? "blocked" : "pending",
|
|
5156
|
+
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."
|
|
5157
|
+
),
|
|
5158
|
+
campaignReviewGate(
|
|
5159
|
+
"qualified_sample",
|
|
5160
|
+
"Sample rows qualified",
|
|
5161
|
+
sampledRows === 0 ? terminalState.reviewable ? "blocked" : "pending" : qualifiedRows === sampledRows ? "pass" : "blocked",
|
|
5162
|
+
sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
|
|
5163
|
+
),
|
|
5164
|
+
campaignReviewGate(
|
|
5165
|
+
"email_coverage",
|
|
5166
|
+
"Email coverage",
|
|
5167
|
+
sampledRows === 0 ? terminalState.reviewable ? "blocked" : "pending" : rowsWithEmail === sampledRows ? "pass" : "blocked",
|
|
5168
|
+
sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
|
|
5169
|
+
),
|
|
5170
|
+
campaignReviewGate(
|
|
5171
|
+
"artifact_available",
|
|
5172
|
+
"Artifact available",
|
|
5173
|
+
artifactCount > 0 ? "pass" : terminalState.reviewable ? "review" : "pending",
|
|
5174
|
+
artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
|
|
5175
|
+
),
|
|
5176
|
+
campaignReviewGate(
|
|
5177
|
+
"no_failed_rows",
|
|
5178
|
+
"No failed rows",
|
|
5179
|
+
status.recordsFailed === 0 ? "pass" : "blocked",
|
|
5180
|
+
status.recordsFailed === 0 ? "The build reports zero failed rows." : `${status.recordsFailed} row(s) failed during the build.`
|
|
5181
|
+
)
|
|
4380
5182
|
];
|
|
4381
|
-
const blockedReasons = gates.filter((gate) => !gate.passed).map((gate) => gate.detail);
|
|
5183
|
+
const blockedReasons = gates.filter((gate) => gate.status === "blocked" || !gate.passed).map((gate) => gate.detail);
|
|
4382
5184
|
const warnings = [
|
|
4383
5185
|
rows.hasMore ? "This review is based on a row sample; page through remaining rows before final approval." : void 0,
|
|
4384
5186
|
sampledRows > 0 && copyReadyRows < sampledRows ? `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.` : void 0,
|
|
4385
5187
|
...status.warnings
|
|
4386
5188
|
].filter((item) => Boolean(item));
|
|
4387
|
-
const readyForDeliveryApproval = blockedReasons.length === 0;
|
|
5189
|
+
const readyForDeliveryApproval = terminalState.reviewable && blockedReasons.length === 0 && northStarScorecard.status !== "blocked";
|
|
4388
5190
|
return {
|
|
4389
5191
|
campaignBuildId: status.campaignBuildId || rows.campaignBuildId,
|
|
4390
5192
|
status,
|
|
4391
5193
|
rows,
|
|
4392
5194
|
artifacts,
|
|
4393
5195
|
gates,
|
|
5196
|
+
northStarScorecard,
|
|
4394
5197
|
summary: {
|
|
4395
5198
|
status: status.status,
|
|
4396
5199
|
phase: status.currentPhase,
|
|
5200
|
+
terminalState: terminalState.kind,
|
|
5201
|
+
scorecardStatus: northStarScorecard.status,
|
|
5202
|
+
scorecardScore: northStarScorecard.score,
|
|
4397
5203
|
sampledRows,
|
|
4398
5204
|
availableRows,
|
|
4399
5205
|
qualifiedRows,
|
|
@@ -4447,6 +5253,184 @@ function campaignReviewRowHasCopy(row) {
|
|
|
4447
5253
|
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)
|
|
4448
5254
|
);
|
|
4449
5255
|
}
|
|
5256
|
+
function campaignReviewDataEmail(data) {
|
|
5257
|
+
return stringValue(data.email) || stringValue(data.work_email) || stringValue(data.workEmail) || stringValue(asRecord(data.contact).email);
|
|
5258
|
+
}
|
|
5259
|
+
function campaignReviewDataDomain(data) {
|
|
5260
|
+
const domain = stringValue(data.company_domain) || stringValue(data.companyDomain) || stringValue(asRecord(data.company).domain);
|
|
5261
|
+
return domain?.toLowerCase();
|
|
5262
|
+
}
|
|
5263
|
+
function campaignReviewDataHasVerifiedEmail(data) {
|
|
5264
|
+
const status = String(data.email_status ?? data.emailStatus ?? asRecord(data.contact).email_status ?? "").toLowerCase();
|
|
5265
|
+
return data.email_verified === true || data.emailVerified === true || asRecord(data.contact).email_verified === true || ["valid", "validated", "verified"].includes(status);
|
|
5266
|
+
}
|
|
5267
|
+
function campaignReviewDataCopyParts(data) {
|
|
5268
|
+
const copy = asRecord(data.copy);
|
|
5269
|
+
const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
|
|
5270
|
+
return [
|
|
5271
|
+
stringValue(data.subject),
|
|
5272
|
+
stringValue(data.subject_line),
|
|
5273
|
+
stringValue(data.opener),
|
|
5274
|
+
stringValue(data.body),
|
|
5275
|
+
stringValue(data.cta),
|
|
5276
|
+
stringValue(copy.subject),
|
|
5277
|
+
stringValue(copy.opener),
|
|
5278
|
+
stringValue(copy.body),
|
|
5279
|
+
stringValue(copy.cta),
|
|
5280
|
+
stringValue(rawCopy.subject),
|
|
5281
|
+
stringValue(rawCopy.opener),
|
|
5282
|
+
stringValue(rawCopy.body),
|
|
5283
|
+
stringValue(rawCopy.cta)
|
|
5284
|
+
].filter((part) => Boolean(part));
|
|
5285
|
+
}
|
|
5286
|
+
function campaignReviewDataHasBadGreeting(data) {
|
|
5287
|
+
return campaignReviewDataCopyParts(data).some(
|
|
5288
|
+
(part) => /\bhi\s*(?:,|\{\{|\[)|\bhello\s*(?:,|\{\{|\[)/i.test(part) || /\b(first_name|firstname|name)\b/i.test(part)
|
|
5289
|
+
);
|
|
5290
|
+
}
|
|
5291
|
+
function campaignReviewDataHasBannedPhrase(data) {
|
|
5292
|
+
const text = campaignReviewDataCopyParts(data).join(" ").toLowerCase();
|
|
5293
|
+
return NORTH_STAR_BANNED_COPY_PHRASES.some((phrase) => text.includes(phrase));
|
|
5294
|
+
}
|
|
5295
|
+
function campaignReviewDataSubject(data) {
|
|
5296
|
+
const copy = asRecord(data.copy);
|
|
5297
|
+
const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
|
|
5298
|
+
return stringValue(data.subject) || stringValue(data.subject_line) || stringValue(data.subjectLine) || stringValue(copy.subject) || stringValue(rawCopy.subject);
|
|
5299
|
+
}
|
|
5300
|
+
function campaignReviewDataCta(data) {
|
|
5301
|
+
const copy = asRecord(data.copy);
|
|
5302
|
+
const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
|
|
5303
|
+
return stringValue(data.cta) || stringValue(copy.cta) || stringValue(rawCopy.cta);
|
|
5304
|
+
}
|
|
5305
|
+
function countCampaignReviewIssues(issueRows) {
|
|
5306
|
+
return issueRows.flat().reduce((acc, issue) => {
|
|
5307
|
+
acc[issue] = (acc[issue] || 0) + 1;
|
|
5308
|
+
return acc;
|
|
5309
|
+
}, {});
|
|
5310
|
+
}
|
|
5311
|
+
function formatCampaignReviewIssueCounts(counts) {
|
|
5312
|
+
const entries = Object.entries(counts);
|
|
5313
|
+
return entries.length > 0 ? entries.map(([issue, count]) => `${issue}=${count}`).join(", ") : "none";
|
|
5314
|
+
}
|
|
5315
|
+
function campaignReviewDataCopyQaIssues(data) {
|
|
5316
|
+
const issues = /* @__PURE__ */ new Set();
|
|
5317
|
+
const copyParts = campaignReviewDataCopyParts(data);
|
|
5318
|
+
if (copyParts.length === 0) return [];
|
|
5319
|
+
if (campaignReviewDataHasBadGreeting(data)) issues.add("bad_greeting");
|
|
5320
|
+
if (campaignReviewDataHasBannedPhrase(data)) issues.add("banned_or_vague_phrase");
|
|
5321
|
+
if (campaignReviewDataHasUnsupportedSignalClaim(data)) issues.add("unsupported_signal_claim");
|
|
5322
|
+
if (campaignReviewDataPersonalizationBasis(data).length === 0) issues.add("missing_personalization_basis");
|
|
5323
|
+
if (!campaignReviewDataCta(data)) issues.add("missing_cta");
|
|
5324
|
+
const subject = campaignReviewDataSubject(data);
|
|
5325
|
+
if (subject && subject.length > 70) issues.add("overlong_subject");
|
|
5326
|
+
return [...issues];
|
|
5327
|
+
}
|
|
5328
|
+
function campaignReviewDataPersonalizationBasis(data) {
|
|
5329
|
+
const rawCopy = asRecord(data.raw_copy ?? data.rawCopy);
|
|
5330
|
+
const copy = asRecord(data.copy);
|
|
5331
|
+
const metadata = asRecord(data.metadata);
|
|
5332
|
+
const signalEvidence = asRecord(data.signal_evidence ?? metadata.signal_evidence ?? metadata.signalEvidence);
|
|
5333
|
+
return [
|
|
5334
|
+
...arrayOfStrings(data.personalization_basis) ?? [],
|
|
5335
|
+
...arrayOfStrings(data.personalizationBasis) ?? [],
|
|
5336
|
+
...arrayOfStrings(copy.personalization_basis) ?? [],
|
|
5337
|
+
...arrayOfStrings(rawCopy.personalization_basis) ?? [],
|
|
5338
|
+
...arrayOfStrings(metadata.personalization_basis) ?? [],
|
|
5339
|
+
...arrayOfStrings(metadata.personalizationBasis) ?? [],
|
|
5340
|
+
...arrayOfStrings(signalEvidence.personalization_basis) ?? []
|
|
5341
|
+
];
|
|
5342
|
+
}
|
|
5343
|
+
function campaignReviewDataHasNoSupportedSignal(data) {
|
|
5344
|
+
const metadata = asRecord(data.metadata);
|
|
5345
|
+
const copy = asRecord(data.copy);
|
|
5346
|
+
const state = [
|
|
5347
|
+
data.copy_signal_state,
|
|
5348
|
+
data.signal_basis,
|
|
5349
|
+
data.signal_type,
|
|
5350
|
+
metadata.copy_signal_state,
|
|
5351
|
+
metadata.signal_basis,
|
|
5352
|
+
metadata.signal_type,
|
|
5353
|
+
copy.copy_signal_state
|
|
5354
|
+
].map((value) => stringValue(value)?.toLowerCase()).filter(Boolean);
|
|
5355
|
+
return data.no_supported_signal === true || metadata.no_supported_signal === true || state.includes("no_supported_signal");
|
|
5356
|
+
}
|
|
5357
|
+
function campaignReviewDataHasSignalBasis(data) {
|
|
5358
|
+
if (campaignReviewDataHasNoSupportedSignal(data)) return false;
|
|
5359
|
+
const metadata = asRecord(data.metadata);
|
|
5360
|
+
const signal = asRecord(data.signal);
|
|
5361
|
+
const signalEvidence = asRecord(data.signal_evidence ?? metadata.signal_evidence ?? metadata.signalEvidence);
|
|
5362
|
+
const basis = campaignReviewDataPersonalizationBasis(data);
|
|
5363
|
+
return Boolean(
|
|
5364
|
+
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))
|
|
5365
|
+
);
|
|
5366
|
+
}
|
|
5367
|
+
function campaignReviewDataHasUnsupportedSignalClaim(data) {
|
|
5368
|
+
const copyText = campaignReviewDataCopyParts(data).join(" ").toLowerCase();
|
|
5369
|
+
const claimsSignal = /\b(hiring|funding|raised|launched|expanding|appointed|announced|recent news|new role|job posting)\b/.test(copyText);
|
|
5370
|
+
return claimsSignal && !campaignReviewDataHasSignalBasis(data);
|
|
5371
|
+
}
|
|
5372
|
+
function campaignReviewDataHasApprovalLock(data) {
|
|
5373
|
+
const metadata = asRecord(data.fit_metadata ?? data.metadata);
|
|
5374
|
+
const approval = String(data.approval_status ?? data.delivery_approval_status ?? metadata.approval_status ?? "").toLowerCase();
|
|
5375
|
+
const exportStatus = String(data.export_status ?? data.exportStatus ?? "").toLowerCase();
|
|
5376
|
+
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);
|
|
5377
|
+
}
|
|
5378
|
+
function campaignReviewDataHasHoldoutAttribution(data) {
|
|
5379
|
+
const fitMetadata = asRecord(data.fit_metadata ?? data.fitMetadata);
|
|
5380
|
+
const metadata = asRecord(data.metadata);
|
|
5381
|
+
const holdout = asRecord(
|
|
5382
|
+
fitMetadata.learning_holdout ?? fitMetadata.learningHoldout ?? metadata.learning_holdout ?? metadata.learningHoldout ?? data.learning_holdout ?? data.learningHoldout
|
|
5383
|
+
);
|
|
5384
|
+
const experimentKey = stringValue(holdout.experiment_key ?? holdout.experimentKey ?? data.experiment_key ?? data.experimentKey);
|
|
5385
|
+
const cohort = stringValue(holdout.cohort_role ?? holdout.cohortRole ?? data.cohort_role ?? data.learning_cohort ?? data.learningCohort);
|
|
5386
|
+
const brainApplied = holdout.brain_applied ?? holdout.brainApplied ?? data.brain_applied ?? data.signaliz_brain_applied;
|
|
5387
|
+
return Boolean(experimentKey && cohort && brainApplied !== void 0);
|
|
5388
|
+
}
|
|
5389
|
+
function campaignReviewStatusHasFeedbackReadiness(status) {
|
|
5390
|
+
const diagnostics = asRecord(status.diagnostics);
|
|
5391
|
+
const feedbackReadiness = asRecord(diagnostics.feedback_readiness ?? diagnostics.feedbackReadiness);
|
|
5392
|
+
const feedbackSubstrate = asRecord(diagnostics.feedback_substrate ?? diagnostics.feedbackSubstrate);
|
|
5393
|
+
const evidenceCounts = asRecord(diagnostics.evidence_counts ?? diagnostics.evidenceCounts);
|
|
5394
|
+
return feedbackReadiness.ready === true || feedbackReadiness.webhook_configured === true || feedbackSubstrate.stage === "ready" || Number(evidenceCounts.feedback_events ?? 0) > 0;
|
|
5395
|
+
}
|
|
5396
|
+
function campaignReviewMoatState(status, holdoutRows) {
|
|
5397
|
+
const diagnostics = asRecord(status.diagnostics);
|
|
5398
|
+
const evidenceCounts = asRecord(diagnostics.evidence_counts ?? diagnostics.evidenceCounts);
|
|
5399
|
+
const embeddedHoldout = asRecord(diagnostics.embedded_holdout ?? diagnostics.embeddedHoldout);
|
|
5400
|
+
const holdoutLift = asRecord(diagnostics.holdout_lift ?? diagnostics.holdoutLift ?? diagnostics.holdout_measurement ?? diagnostics.holdoutMeasurement);
|
|
5401
|
+
const controlSample = Math.max(
|
|
5402
|
+
Number(evidenceCounts.holdout_control_sample_size ?? 0),
|
|
5403
|
+
Number(evidenceCounts.embedded_holdout_control_sample ?? 0),
|
|
5404
|
+
Number(embeddedHoldout.control_sample_size ?? 0)
|
|
5405
|
+
);
|
|
5406
|
+
const learnedSample = Math.max(
|
|
5407
|
+
Number(evidenceCounts.holdout_learned_sample_size ?? 0),
|
|
5408
|
+
Number(evidenceCounts.embedded_holdout_learned_sample ?? 0),
|
|
5409
|
+
Number(embeddedHoldout.learned_sample_size ?? 0)
|
|
5410
|
+
);
|
|
5411
|
+
const feedbackEvents = Math.max(
|
|
5412
|
+
Number(evidenceCounts.feedback_events ?? 0),
|
|
5413
|
+
Number(evidenceCounts.campaign_build_feedback_events ?? 0),
|
|
5414
|
+
Number(evidenceCounts.delivery_holdout_sent_outcomes ?? 0)
|
|
5415
|
+
);
|
|
5416
|
+
const proved = diagnostics.closed_loop_proof_ready === true || holdoutLift.honest_measurement === true && holdoutLift.positive_lift === true;
|
|
5417
|
+
if (proved) {
|
|
5418
|
+
return {
|
|
5419
|
+
state: "proved",
|
|
5420
|
+
detail: "Real attributed feedback shows positive lift for explicit control and learned cohorts."
|
|
5421
|
+
};
|
|
5422
|
+
}
|
|
5423
|
+
if (controlSample > 0 && learnedSample > 0 && feedbackEvents > 0) {
|
|
5424
|
+
return {
|
|
5425
|
+
state: "moved_needs_lift_volume",
|
|
5426
|
+
detail: `Control and learned cohorts have attributed feedback (${controlSample} control, ${learnedSample} learned, ${feedbackEvents} feedback event(s)); keep collecting lift volume before claiming proof.`
|
|
5427
|
+
};
|
|
5428
|
+
}
|
|
5429
|
+
return {
|
|
5430
|
+
state: "scaffolding_ready",
|
|
5431
|
+
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."
|
|
5432
|
+
};
|
|
5433
|
+
}
|
|
4450
5434
|
function createCampaignBuilderReadinessLane(route, plan) {
|
|
4451
5435
|
const builtIn = campaignBuilderBuiltInForRoute(route);
|
|
4452
5436
|
const customRoute = route.provider !== "signaliz" && route.provider !== "csv" && builtIn === void 0;
|
|
@@ -4480,6 +5464,342 @@ function createCampaignBuilderReadinessLane(route, plan) {
|
|
|
4480
5464
|
nextAction: customRoute ? `Review ${route.provider} route setup before launch approval.` : void 0
|
|
4481
5465
|
};
|
|
4482
5466
|
}
|
|
5467
|
+
function createCampaignBuilderApprovalPacket(plan, approval, missingTypes) {
|
|
5468
|
+
const requestedTypes = plan ? uniqueStrings(plan.approvals.filter((item) => item.blocking).map((item) => item.type)) : [];
|
|
5469
|
+
const approvalMatchesPlan = Boolean(plan && approval?.planId === plan.planId);
|
|
5470
|
+
const approvedTypes = approvalMatchesPlan ? approval?.approvedTypes ?? [] : [];
|
|
5471
|
+
const approvedRoutes = approvalMatchesPlan ? approval?.approvedRouteIds ?? [] : [];
|
|
5472
|
+
const missingIds = new Set(
|
|
5473
|
+
plan && approvalMatchesPlan && approval ? getMissingCampaignBuilderApprovals(plan, approval).map((item) => item.id) : plan?.approvals.filter((item) => item.blocking).map((item) => item.id) ?? []
|
|
5474
|
+
);
|
|
5475
|
+
const routeIdsRequired = plan ? uniqueStrings(plan.approvals.map((item) => item.routeId).filter((id) => Boolean(id))) : [];
|
|
5476
|
+
const spendLimitRequired = plan ? Math.max(0, ...plan.approvals.filter((item) => item.type === "spend" && item.blocking).map((item) => item.estimatedCredits ?? plan.estimatedCredits)) : 0;
|
|
5477
|
+
const normalizedSpendLimit = spendLimitRequired > 0 ? spendLimitRequired : null;
|
|
5478
|
+
const approvalState = !plan ? "unscoped" : missingTypes.length > 0 ? "requires_approval" : "approved";
|
|
5479
|
+
const approvalInput = {
|
|
5480
|
+
approved_by: approval?.approvedBy ?? null,
|
|
5481
|
+
approved_at: approval?.approvedAt ?? null,
|
|
5482
|
+
approved_types: approvedTypes,
|
|
5483
|
+
approved_route_ids: approvedRoutes,
|
|
5484
|
+
spend_limit_credits: approval?.spendLimitCredits ?? null,
|
|
5485
|
+
notes: approval?.notes
|
|
5486
|
+
};
|
|
5487
|
+
return {
|
|
5488
|
+
packet_version: "campaign-builder-approval-packet.v1",
|
|
5489
|
+
read_only: true,
|
|
5490
|
+
no_spend: true,
|
|
5491
|
+
no_provider_writes: true,
|
|
5492
|
+
private_safe: true,
|
|
5493
|
+
approval_state: approvalState,
|
|
5494
|
+
plan_id: plan?.planId ?? null,
|
|
5495
|
+
campaign_name: plan?.campaignName ?? null,
|
|
5496
|
+
requested_approval_types: requestedTypes,
|
|
5497
|
+
missing_approval_types: uniqueStrings(missingTypes),
|
|
5498
|
+
spend_limit_credits_required: normalizedSpendLimit,
|
|
5499
|
+
route_ids_required: routeIdsRequired,
|
|
5500
|
+
required_approvals: (plan?.approvals ?? []).map((item) => compact({
|
|
5501
|
+
id: item.id,
|
|
5502
|
+
type: item.type,
|
|
5503
|
+
label: item.label,
|
|
5504
|
+
reason: item.reason,
|
|
5505
|
+
blocking: item.blocking,
|
|
5506
|
+
route_id: item.routeId,
|
|
5507
|
+
provider: item.provider,
|
|
5508
|
+
estimated_credits: item.estimatedCredits,
|
|
5509
|
+
status: missingIds.has(item.id) ? "missing" : "approved"
|
|
5510
|
+
})),
|
|
5511
|
+
approval_input: approvalInput,
|
|
5512
|
+
cli_handoff: plan ? {
|
|
5513
|
+
command: campaignBuilderApprovalCliHandoff(requestedTypes, routeIdsRequired, normalizedSpendLimit),
|
|
5514
|
+
safety: "Template only: run after explicit human approval identity, approved types, route scope, and spend limit are filled in."
|
|
5515
|
+
} : null,
|
|
5516
|
+
sdk_handoff: plan ? {
|
|
5517
|
+
method: "campaignBuilderAgent.buildApprovedPlan",
|
|
5518
|
+
approval: compact({
|
|
5519
|
+
approvedBy: approval?.approvedBy ?? "<operator-email>",
|
|
5520
|
+
approvedTypes: requestedTypes,
|
|
5521
|
+
approvedRouteIds: routeIdsRequired,
|
|
5522
|
+
spendLimitCredits: normalizedSpendLimit ?? void 0,
|
|
5523
|
+
notes: approval?.notes
|
|
5524
|
+
}),
|
|
5525
|
+
options: {
|
|
5526
|
+
idempotencyKey: "<set-before-execution>"
|
|
5527
|
+
},
|
|
5528
|
+
safety: "Call only after the approval packet is accepted; this helper does not execute writes or spend."
|
|
5529
|
+
} : null,
|
|
5530
|
+
mcp_handoffs: plan ? campaignBuilderApprovalMcpHandoffs(plan, requestedTypes, routeIdsRequired, normalizedSpendLimit) : [],
|
|
5531
|
+
safety_boundary: "This approval packet is a reusable request for human approval; it is not an approval grant and it does not authorize spend, provider writes, sender loading, delivery, or launch by itself."
|
|
5532
|
+
};
|
|
5533
|
+
}
|
|
5534
|
+
function campaignBuilderApprovalCliHandoff(requestedTypes, routeIds, spendLimit) {
|
|
5535
|
+
return [
|
|
5536
|
+
"signaliz campaign-agent build-approved --request-file campaign-request.json --confirm-launch",
|
|
5537
|
+
requestedTypes.length > 0 ? `--approved-types ${shellArg(requestedTypes.join(","))}` : "--approve-all",
|
|
5538
|
+
"--approved-by <operator-email>",
|
|
5539
|
+
spendLimit !== null ? `--spend-limit ${spendLimit}` : void 0,
|
|
5540
|
+
routeIds.length > 0 ? `--approved-route-ids ${shellArg(routeIds.join(","))}` : void 0
|
|
5541
|
+
].filter(Boolean).join(" ");
|
|
5542
|
+
}
|
|
5543
|
+
function campaignBuilderApprovalMcpHandoffs(plan, requestedTypes, routeIds, spendLimit) {
|
|
5544
|
+
const approvalContext = compact({
|
|
5545
|
+
plan_id: plan.planId,
|
|
5546
|
+
approval_types: requestedTypes,
|
|
5547
|
+
approved_route_ids: routeIds,
|
|
5548
|
+
spend_limit_credits: spendLimit,
|
|
5549
|
+
approved_by: "<operator-email>"
|
|
5550
|
+
});
|
|
5551
|
+
return plan.mcpFlow.filter((step) => ["execution-prepare", "approved-build", "delivery-approval"].includes(step.id)).map((step) => ({
|
|
5552
|
+
id: step.id,
|
|
5553
|
+
tool: step.tool,
|
|
5554
|
+
arguments: step.id === "approved-build" ? compact({ ...step.arguments, approval_context: approvalContext }) : step.arguments,
|
|
5555
|
+
read_only: step.readOnly,
|
|
5556
|
+
approval_required: step.approvalRequired,
|
|
5557
|
+
safety: step.readOnly ? "Read-only readiness handoff; safe to inspect before approval." : "Write-capable handoff; run only after the approval packet is accepted."
|
|
5558
|
+
}));
|
|
5559
|
+
}
|
|
5560
|
+
function campaignBuilderActiveWorkNextMcpAction(input) {
|
|
5561
|
+
const campaignBuildRef = input.refs.find((ref) => ref.kind === "campaign_build" && ref.id);
|
|
5562
|
+
if (campaignBuildRef) {
|
|
5563
|
+
return {
|
|
5564
|
+
id: "check-campaign-build-status",
|
|
5565
|
+
tool: "get_campaign_build_status",
|
|
5566
|
+
arguments: { campaign_build_id: campaignBuildRef.id },
|
|
5567
|
+
read_only: true,
|
|
5568
|
+
approval_required: false,
|
|
5569
|
+
safety: "Read-only status check for the remembered campaign build; does not spend, write, export, send, or approve anything."
|
|
5570
|
+
};
|
|
5571
|
+
}
|
|
5572
|
+
const campaignRef = input.refs.find((ref) => ref.kind === "gtm_campaign" && ref.id);
|
|
5573
|
+
if (campaignRef) {
|
|
5574
|
+
return {
|
|
5575
|
+
id: "check-gtm-campaign-status",
|
|
5576
|
+
tool: "gtm_campaign_execution_status",
|
|
5577
|
+
arguments: { campaign_id: campaignRef.id },
|
|
5578
|
+
read_only: true,
|
|
5579
|
+
approval_required: false,
|
|
5580
|
+
safety: "Read-only campaign execution status check; does not start or mutate the campaign."
|
|
5581
|
+
};
|
|
5582
|
+
}
|
|
5583
|
+
const preferredStepId = input.state === "ready_for_dry_run" ? "dry-run-build" : input.state === "planning" || input.state === "blocked" ? "agency-campaign-build-plan" : null;
|
|
5584
|
+
const preferredStep = preferredStepId ? input.plan?.mcpFlow.find((step) => step.id === preferredStepId && step.readOnly) : void 0;
|
|
5585
|
+
if (preferredStep) return campaignBuilderMcpStepToActiveWorkAction(preferredStep, "Read-only continuation from the saved campaign-agent plan.");
|
|
5586
|
+
const readOnlyApprovalHandoff = input.approvalPacket.mcp_handoffs.find((handoff) => handoff.read_only && !handoff.approval_required);
|
|
5587
|
+
if (readOnlyApprovalHandoff) return readOnlyApprovalHandoff;
|
|
5588
|
+
const firstReadOnlyPlanStep = input.plan?.mcpFlow.find((step) => step.readOnly && !step.approvalRequired);
|
|
5589
|
+
return firstReadOnlyPlanStep ? campaignBuilderMcpStepToActiveWorkAction(firstReadOnlyPlanStep, "Read-only saved plan check; safe before approvals or writes.") : null;
|
|
5590
|
+
}
|
|
5591
|
+
function campaignBuilderMcpStepToActiveWorkAction(step, safety) {
|
|
5592
|
+
return {
|
|
5593
|
+
id: step.id,
|
|
5594
|
+
tool: step.tool,
|
|
5595
|
+
arguments: step.arguments,
|
|
5596
|
+
read_only: step.readOnly,
|
|
5597
|
+
approval_required: step.approvalRequired,
|
|
5598
|
+
safety
|
|
5599
|
+
};
|
|
5600
|
+
}
|
|
5601
|
+
function normalizeCampaignBuilderActiveWorkRefs(input) {
|
|
5602
|
+
const refs = [];
|
|
5603
|
+
const pushRef = (ref) => {
|
|
5604
|
+
if (!ref?.id) return;
|
|
5605
|
+
refs.push(ref);
|
|
5606
|
+
};
|
|
5607
|
+
pushRef(input.plan?.planId ? { kind: "plan", id: input.plan.planId, label: input.plan.campaignName, source: "plan" } : void 0);
|
|
5608
|
+
pushRef(input.remembered.planId ? { kind: "plan", id: input.remembered.planId, source: "remembered" } : void 0);
|
|
5609
|
+
pushRef(
|
|
5610
|
+
input.plan?.buildRequest.gtmCampaignId ?? input.remembered.gtmCampaignId ? {
|
|
5611
|
+
kind: "gtm_campaign",
|
|
5612
|
+
id: input.plan?.buildRequest.gtmCampaignId ?? input.remembered.gtmCampaignId,
|
|
5613
|
+
label: input.plan?.campaignName,
|
|
5614
|
+
source: input.plan?.buildRequest.gtmCampaignId ? "plan" : "remembered"
|
|
5615
|
+
} : void 0
|
|
5616
|
+
);
|
|
5617
|
+
const campaignBuildId = stringValue(input.lastResult.campaign_build_id ?? input.lastResult.campaignBuildId) ?? input.remembered.campaignBuildId;
|
|
5618
|
+
pushRef(campaignBuildId ? { kind: "campaign_build", id: campaignBuildId, status: activeWorkStatus(input.lastResult) ?? input.remembered.status, source: "result" } : void 0);
|
|
5619
|
+
pushRef(
|
|
5620
|
+
input.remembered.providerCampaignId ? {
|
|
5621
|
+
kind: "provider_campaign",
|
|
5622
|
+
id: input.remembered.providerCampaignId,
|
|
5623
|
+
label: input.remembered.provider,
|
|
5624
|
+
status: input.remembered.status,
|
|
5625
|
+
source: "remembered"
|
|
5626
|
+
} : void 0
|
|
5627
|
+
);
|
|
5628
|
+
for (const ref of input.remembered.refs ?? []) pushRef(ref);
|
|
5629
|
+
for (const ref of input.refs ?? []) pushRef(ref);
|
|
5630
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5631
|
+
return refs.filter((ref) => {
|
|
5632
|
+
const key = `${ref.kind}:${ref.id}`;
|
|
5633
|
+
if (seen.has(key)) return false;
|
|
5634
|
+
seen.add(key);
|
|
5635
|
+
return true;
|
|
5636
|
+
});
|
|
5637
|
+
}
|
|
5638
|
+
function inferCampaignBuilderActiveWorkState(input) {
|
|
5639
|
+
if (input.blockers.length > 0) return "blocked";
|
|
5640
|
+
const status = activeWorkStatus(input.lastResult) ?? input.remembered.status;
|
|
5641
|
+
const normalized = status?.toLowerCase();
|
|
5642
|
+
if (normalized && ["completed", "complete", "succeeded", "success", "done"].includes(normalized)) return "completed";
|
|
5643
|
+
if (normalized && ["queued", "running", "in_progress", "processing", "pending"].includes(normalized)) return "queued_or_running";
|
|
5644
|
+
const dryRunComplete = input.lastResult.dry_run === true || input.lastResult.dryRun === true || normalized === "dry_run";
|
|
5645
|
+
if (dryRunComplete) return input.missingApprovals.length > 0 ? "needs_approval" : "dry_run_complete";
|
|
5646
|
+
if (input.missingApprovals.length > 0) return "needs_approval";
|
|
5647
|
+
if (input.readiness?.summary.ready === true) return "ready_for_dry_run";
|
|
5648
|
+
if (input.plan) return "planning";
|
|
5649
|
+
return "unknown";
|
|
5650
|
+
}
|
|
5651
|
+
function campaignBuilderActiveWorkApprovalBoundary(missingApprovals, state) {
|
|
5652
|
+
if (missingApprovals.length > 0) {
|
|
5653
|
+
return `Blocked before spend, provider writes, delivery, or launch until approval covers: ${uniqueStrings(missingApprovals).join(", ")}.`;
|
|
5654
|
+
}
|
|
5655
|
+
if (state === "dry_run_complete" || state === "ready_for_dry_run") {
|
|
5656
|
+
return "Read-only planning and dry-runs are allowed; spend, provider writes, sender loading, delivery, and launch still require explicit approval.";
|
|
5657
|
+
}
|
|
5658
|
+
return "This packet is read-only and does not authorize spend, provider writes, sender loading, delivery, or launch.";
|
|
5659
|
+
}
|
|
5660
|
+
function campaignBuilderActiveWorkNextAction(state, blockers, missingApprovals, refs) {
|
|
5661
|
+
if (blockers.length > 0) return `Resolve blocker: ${blockers[0]}`;
|
|
5662
|
+
if (missingApprovals.length > 0) return `Ask the operator to approve ${uniqueStrings(missingApprovals).join(", ")} before any write or spend action.`;
|
|
5663
|
+
if (state === "ready_for_dry_run") return "Run or inspect the read-only campaign dry-run, then return the receipt for approval review.";
|
|
5664
|
+
if (state === "dry_run_complete") return "Review the dry-run receipt and collect explicit launch or delivery approval before any write action.";
|
|
5665
|
+
if (state === "queued_or_running") return "Poll the remembered campaign build or provider job status; do not start a duplicate job.";
|
|
5666
|
+
if (state === "completed") return "Review final artifacts and delivery gates before export, sync, sender load, or launch.";
|
|
5667
|
+
if (refs.length > 0) return "Use the remembered refs to fetch current read-only status before asking the operator for IDs.";
|
|
5668
|
+
return "Create or refresh a campaign-builder plan before taking any spendful or write action.";
|
|
5669
|
+
}
|
|
5670
|
+
function campaignBuilderActiveWorkConversationStarters(input) {
|
|
5671
|
+
const starters = [];
|
|
5672
|
+
if (input.blockers.length > 0) {
|
|
5673
|
+
starters.push(
|
|
5674
|
+
`I found a blocker: ${input.blockers[0]}. Want me to stay in read-only diagnostics and prepare the fix path?`
|
|
5675
|
+
);
|
|
5676
|
+
}
|
|
5677
|
+
if (input.missingApprovals.length > 0) {
|
|
5678
|
+
starters.push(
|
|
5679
|
+
`You still owe approval for ${uniqueStrings(input.missingApprovals).join(", ")}. Want the exact approval packet before anything writes or spends?`
|
|
5680
|
+
);
|
|
5681
|
+
}
|
|
5682
|
+
if (input.state === "queued_or_running") {
|
|
5683
|
+
const buildRef = input.refs.find((ref) => ref.kind === "campaign_build");
|
|
5684
|
+
starters.push(buildRef ? `I can check campaign build ${buildRef.id} now and avoid starting a duplicate job.` : "I can check the running job status now and avoid starting duplicate work.");
|
|
5685
|
+
}
|
|
5686
|
+
if (input.state === "dry_run_complete") {
|
|
5687
|
+
starters.push("The dry run is complete. Want me to summarize blockers, artifacts, and the approval handoff?");
|
|
5688
|
+
}
|
|
5689
|
+
if (input.state === "ready_for_dry_run") {
|
|
5690
|
+
starters.push("Everything is ready for a safe dry run. Want me to run the no-spend preview first?");
|
|
5691
|
+
}
|
|
5692
|
+
if (input.state === "completed") {
|
|
5693
|
+
starters.push("The build looks complete. Want me to review rows, artifacts, and delivery gates before export or send approval?");
|
|
5694
|
+
}
|
|
5695
|
+
starters.push(`Next safe action: ${input.nextSafeAction}`);
|
|
5696
|
+
return uniqueStrings(starters).slice(0, 4);
|
|
5697
|
+
}
|
|
5698
|
+
function createCampaignBuilderOpenAiAgentHandoff(input) {
|
|
5699
|
+
const readOnlyPlanActions = input.plan?.mcpFlow.filter((step) => step.readOnly && !step.approvalRequired).map((step) => campaignBuilderMcpStepToActiveWorkAction(step, "Read-only Signaliz MCP continuation for the saved campaign-agent plan.")) ?? [];
|
|
5700
|
+
const routeSetupActions = readOnlyPlanActions.filter(
|
|
5701
|
+
(action) => action.tool === "gtm_provider_recipe_prepare" || action.tool === "gtm_provider_route_activate"
|
|
5702
|
+
);
|
|
5703
|
+
const safeReadActions = uniqueCampaignBuilderActiveWorkActions([
|
|
5704
|
+
input.nextSafeMcpAction,
|
|
5705
|
+
...routeSetupActions,
|
|
5706
|
+
...readOnlyPlanActions
|
|
5707
|
+
]);
|
|
5708
|
+
const approvalGatedActions = uniqueCampaignBuilderActiveWorkActions([
|
|
5709
|
+
...input.approvalPacket.mcp_handoffs.filter((action) => action.approval_required || !action.read_only),
|
|
5710
|
+
...input.plan?.mcpFlow.filter((step) => step.approvalRequired || !step.readOnly).map((step) => campaignBuilderMcpStepToActiveWorkAction(step, "Approval-gated Signaliz MCP continuation for the saved campaign-agent plan.")) ?? []
|
|
5711
|
+
]);
|
|
5712
|
+
const approvalTypes = uniqueStrings([
|
|
5713
|
+
...input.missingApprovals,
|
|
5714
|
+
...input.approvalPacket.requested_approval_types
|
|
5715
|
+
]);
|
|
5716
|
+
return {
|
|
5717
|
+
packet_version: "campaign-builder-openai-agent-handoff.v1",
|
|
5718
|
+
surface: "openai_agents_sdk",
|
|
5719
|
+
agent: {
|
|
5720
|
+
name: "Signaliz GTM Campaign Agent",
|
|
5721
|
+
handoff_description: "Owns safe, conversational GTM campaign planning and continuation across Signaliz MCP, SDK, attachments, URLs, customer tools, approvals, and sender/export gates.",
|
|
5722
|
+
instructions: [
|
|
5723
|
+
"Be warm, direct, and expert-level GTM. Translate the current state into plain operator language before proposing work.",
|
|
5724
|
+
"Start from the packet state, refs, evidence_context, approval_boundary, and next_safe_action. Do not ask for IDs already present in refs or summary.",
|
|
5725
|
+
"Use attachment_summary, url_summary, and operator_notes as first-class planning evidence, while honoring the evidence privacy_boundary and withheld raw fields.",
|
|
5726
|
+
"Prefer Signaliz-native, cached, remembered, and read-only MCP actions before provider calls. Never start duplicate jobs when a campaign_build or provider job is queued or running.",
|
|
5727
|
+
"Call only tools listed in safe_read_tools without approval. Treat approval_gated_tools as human-review surfaces, not permission to spend, write, export, load senders, deliver, or launch.",
|
|
5728
|
+
"When approval is missing, show the approval_packet and the exact missing approval scope before any side-effecting step."
|
|
5729
|
+
],
|
|
5730
|
+
output_contract: [
|
|
5731
|
+
"current_state: one concise sentence describing where the campaign work stands.",
|
|
5732
|
+
"evidence_used: attachment, URL, memory, route, or integration evidence used without exposing private raw rows or secrets.",
|
|
5733
|
+
"next_safe_mcp_action: the read-only Signaliz MCP action to run next, or null when none is safe.",
|
|
5734
|
+
"approval_boundary: the human approval scope required before spend, provider writes, delivery, export, sender load, or launch.",
|
|
5735
|
+
"operator_reply: conversational GTM guidance with blockers, risks, and the safest next move."
|
|
5736
|
+
]
|
|
5737
|
+
},
|
|
5738
|
+
context: {
|
|
5739
|
+
state: input.state,
|
|
5740
|
+
summary: input.summary,
|
|
5741
|
+
refs: input.refs,
|
|
5742
|
+
blockers: input.blockers,
|
|
5743
|
+
warnings: input.warnings,
|
|
5744
|
+
evidence_context: input.plan?.evidence ?? null,
|
|
5745
|
+
approval_packet: input.approvalPacket,
|
|
5746
|
+
approval_boundary: input.approvalBoundary,
|
|
5747
|
+
next_safe_action: input.nextSafeAction,
|
|
5748
|
+
conversation_starters: input.conversationStarters
|
|
5749
|
+
},
|
|
5750
|
+
tools: {
|
|
5751
|
+
mcp_server_label: "signaliz",
|
|
5752
|
+
remote_mcp_compatible: true,
|
|
5753
|
+
next_safe_mcp_action: input.nextSafeMcpAction,
|
|
5754
|
+
safe_read_tools: safeReadActions.map(
|
|
5755
|
+
(action) => campaignBuilderOpenAiAgentTool(action, "Safe read-only Signaliz MCP action available before approval.")
|
|
5756
|
+
),
|
|
5757
|
+
approval_gated_tools: approvalGatedActions.map(
|
|
5758
|
+
(action) => campaignBuilderOpenAiAgentTool(action, "Requires explicit human review before execution.")
|
|
5759
|
+
)
|
|
5760
|
+
},
|
|
5761
|
+
guardrails: {
|
|
5762
|
+
input: [
|
|
5763
|
+
"Reject or pause requests to expose raw private attachment text, raw rows, leads, emails, provider payloads, copy bodies, API keys, or secrets.",
|
|
5764
|
+
"Pause when the user asks to spend credits, write to a provider, export, load a sender, deliver, or launch without explicit approval scope."
|
|
5765
|
+
],
|
|
5766
|
+
tool: [
|
|
5767
|
+
"Read-only tools may run when they match safe_read_tools and their arguments match this packet.",
|
|
5768
|
+
"Any tool with approval_required=true or read_only=false requires human review and the approval_packet scope first."
|
|
5769
|
+
],
|
|
5770
|
+
human_review_required_for: approvalTypes,
|
|
5771
|
+
privacy: input.plan?.evidence?.privacy_boundary ?? "Use summarized context only. Do not expose raw private attachment text, raw rows, leads, emails, provider payloads, copy bodies, secrets, or credentials."
|
|
5772
|
+
}
|
|
5773
|
+
};
|
|
5774
|
+
}
|
|
5775
|
+
function campaignBuilderOpenAiAgentTool(action, purpose) {
|
|
5776
|
+
return {
|
|
5777
|
+
id: action.id,
|
|
5778
|
+
type: "mcp",
|
|
5779
|
+
server_label: "signaliz",
|
|
5780
|
+
name: action.tool,
|
|
5781
|
+
arguments: action.arguments,
|
|
5782
|
+
read_only: action.read_only,
|
|
5783
|
+
approval_required: action.approval_required,
|
|
5784
|
+
purpose,
|
|
5785
|
+
safety: action.safety
|
|
5786
|
+
};
|
|
5787
|
+
}
|
|
5788
|
+
function uniqueCampaignBuilderActiveWorkActions(actions) {
|
|
5789
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5790
|
+
const unique = [];
|
|
5791
|
+
for (const action of actions) {
|
|
5792
|
+
if (!action) continue;
|
|
5793
|
+
const key = `${action.id}:${action.tool}`;
|
|
5794
|
+
if (seen.has(key)) continue;
|
|
5795
|
+
seen.add(key);
|
|
5796
|
+
unique.push(action);
|
|
5797
|
+
}
|
|
5798
|
+
return unique;
|
|
5799
|
+
}
|
|
5800
|
+
function activeWorkStatus(record) {
|
|
5801
|
+
return stringValue(record.status ?? record.state ?? record.phase);
|
|
5802
|
+
}
|
|
4483
5803
|
function campaignBuilderBuiltInForRoute(route) {
|
|
4484
5804
|
return Object.entries(CAMPAIGN_BUILDER_BUILT_IN_ROUTE_IDS).find(([, routeId]) => route.id === routeId)?.[0];
|
|
4485
5805
|
}
|
|
@@ -7276,6 +8596,36 @@ function recordsFromParams(params) {
|
|
|
7276
8596
|
if (params.input) return { input: params.input };
|
|
7277
8597
|
return { input: {} };
|
|
7278
8598
|
}
|
|
8599
|
+
function toFusionConfig(params) {
|
|
8600
|
+
return {
|
|
8601
|
+
system_prompt: params.systemPrompt || params.system_prompt,
|
|
8602
|
+
user_template: params.userTemplate || params.user_template || params.prompt,
|
|
8603
|
+
preset: params.preset,
|
|
8604
|
+
temperature: params.temperature,
|
|
8605
|
+
max_tool_calls: params.maxToolCalls || params.max_tool_calls,
|
|
8606
|
+
max_tokens: params.maxTokens || params.max_tokens,
|
|
8607
|
+
timeout_ms: params.timeoutMs || params.timeout_ms,
|
|
8608
|
+
output_fields: params.outputFields || params.output_fields || [{ name: "response", type: "text", description: "Structured Fusion enrichment result" }]
|
|
8609
|
+
};
|
|
8610
|
+
}
|
|
8611
|
+
function toFusionSpendControls(params) {
|
|
8612
|
+
const controls = {};
|
|
8613
|
+
const dryRun = params.dryRun ?? params.dry_run;
|
|
8614
|
+
const confirmSpend = params.confirmSpend ?? params.confirm_spend;
|
|
8615
|
+
const maxCredits = params.maxCredits ?? params.max_credits;
|
|
8616
|
+
const maxCostUsd = params.maxCostUsd ?? params.max_cost_usd;
|
|
8617
|
+
if (dryRun !== void 0) controls.dry_run = dryRun;
|
|
8618
|
+
if (confirmSpend !== void 0) controls.confirm_spend = confirmSpend;
|
|
8619
|
+
if (maxCredits !== void 0) controls.max_credits = maxCredits;
|
|
8620
|
+
if (maxCostUsd !== void 0) controls.max_cost_usd = maxCostUsd;
|
|
8621
|
+
return controls;
|
|
8622
|
+
}
|
|
8623
|
+
function fusionRecordsFromParams(params) {
|
|
8624
|
+
const records = params.records || params.inputs;
|
|
8625
|
+
if (records?.length) return { records };
|
|
8626
|
+
if (params.input) return { input: params.input };
|
|
8627
|
+
return { input: {} };
|
|
8628
|
+
}
|
|
7279
8629
|
var Ai = class {
|
|
7280
8630
|
constructor(client) {
|
|
7281
8631
|
this.client = client;
|
|
@@ -7309,6 +8659,33 @@ var Ai = class {
|
|
|
7309
8659
|
raw: data
|
|
7310
8660
|
};
|
|
7311
8661
|
}
|
|
8662
|
+
/**
|
|
8663
|
+
* Plan or run experimental AI Enrichment Fusion with explicit spend controls.
|
|
8664
|
+
*/
|
|
8665
|
+
async fusion(params) {
|
|
8666
|
+
if (!params.prompt && !params.userTemplate && !params.user_template) {
|
|
8667
|
+
throw new Error("prompt or userTemplate is required.");
|
|
8668
|
+
}
|
|
8669
|
+
const data = await this.client.post("ai-enrichment-fusion", {
|
|
8670
|
+
...fusionRecordsFromParams(params),
|
|
8671
|
+
config: toFusionConfig(params),
|
|
8672
|
+
...toFusionSpendControls(params)
|
|
8673
|
+
});
|
|
8674
|
+
return {
|
|
8675
|
+
success: data.success ?? false,
|
|
8676
|
+
capability: data.capability ?? "ai_enrichment_fusion",
|
|
8677
|
+
displayName: data.display_name ?? "AI Enrichment Fusion",
|
|
8678
|
+
results: data.results ?? [],
|
|
8679
|
+
creditsUsed: data.total_credits_used ?? data.credits_used ?? 0,
|
|
8680
|
+
model: data.model,
|
|
8681
|
+
fusion: data.fusion,
|
|
8682
|
+
tokensUsed: data.tokens_used ?? 0,
|
|
8683
|
+
costUsd: data.cost_usd ?? 0,
|
|
8684
|
+
costSummary: data.cost_summary,
|
|
8685
|
+
billingMetadata: data.billing_metadata,
|
|
8686
|
+
raw: data
|
|
8687
|
+
};
|
|
8688
|
+
}
|
|
7312
8689
|
};
|
|
7313
8690
|
|
|
7314
8691
|
// src/resources/gtm-kernel.ts
|
|
@@ -7662,6 +9039,16 @@ var GtmKernel = class {
|
|
|
7662
9039
|
min_privacy_k: input.minPrivacyK
|
|
7663
9040
|
});
|
|
7664
9041
|
}
|
|
9042
|
+
/** Queue a dry-run-first bridge from imported historical feedback to attribution-only Campaign Build surrogates. */
|
|
9043
|
+
async runHistoricalCampaignBuildBridge(input = {}) {
|
|
9044
|
+
return this.callMcp("gtm_historical_campaign_build_bridge_run", {
|
|
9045
|
+
dry_run: input.dryRun,
|
|
9046
|
+
write_approved: input.writeApproved,
|
|
9047
|
+
max_events: input.maxEvents,
|
|
9048
|
+
max_campaigns: input.maxCampaigns,
|
|
9049
|
+
min_feedback_events: input.minFeedbackEvents
|
|
9050
|
+
});
|
|
9051
|
+
}
|
|
7665
9052
|
/** Prepare a provider-agnostic feedback webhook URL for Smartlead, HeyReach, Airbyte, or custom sender events. */
|
|
7666
9053
|
async prepareFeedbackWebhook(input) {
|
|
7667
9054
|
return this.callMcp("gtm_feedback_webhook_prepare", {
|
|
@@ -7838,6 +9225,24 @@ var GtmKernel = class {
|
|
|
7838
9225
|
limit: input.limit
|
|
7839
9226
|
});
|
|
7840
9227
|
}
|
|
9228
|
+
/** Predict booked-meeting likelihood for uploaded, inline, or MCP-sourced lead rows without persisting raw rows. */
|
|
9229
|
+
async predictMeetingLikelihood(input) {
|
|
9230
|
+
return this.callMcp("gtm_predict_meeting_likelihood", {
|
|
9231
|
+
lead_rows: input.leadRows,
|
|
9232
|
+
input_source: input.inputSource,
|
|
9233
|
+
campaign_id: input.campaignId,
|
|
9234
|
+
campaign_build_id: input.campaignBuildId,
|
|
9235
|
+
days: input.days,
|
|
9236
|
+
feedback_limit: input.feedbackLimit,
|
|
9237
|
+
include_features: input.includeFeatures,
|
|
9238
|
+
include_global: input.includeGlobal,
|
|
9239
|
+
min_feedback_events: input.minFeedbackEvents,
|
|
9240
|
+
min_historical_feature_sample_size: input.minHistoricalFeatureSampleSize,
|
|
9241
|
+
min_workspace_count: input.minWorkspaceCount,
|
|
9242
|
+
min_privacy_k: input.minPrivacyK,
|
|
9243
|
+
limit: input.limit
|
|
9244
|
+
});
|
|
9245
|
+
}
|
|
7841
9246
|
/** Rank provider-chain, ICP, source, copy, and sequence failure patterns without writing raw lead data. */
|
|
7842
9247
|
async failurePatterns(options = {}) {
|
|
7843
9248
|
return this.callMcp("gtm_brain_failure_patterns", {
|
|
@@ -8453,6 +9858,70 @@ function compact2(value) {
|
|
|
8453
9858
|
return out;
|
|
8454
9859
|
}
|
|
8455
9860
|
|
|
9861
|
+
// src/resources/public-ground-truth.ts
|
|
9862
|
+
var PublicGroundTruth = class {
|
|
9863
|
+
constructor(client) {
|
|
9864
|
+
this.client = client;
|
|
9865
|
+
}
|
|
9866
|
+
/**
|
|
9867
|
+
* Search the public-ground-truth cache by company/name/domain/native ID,
|
|
9868
|
+
* or by plain-language industry + location.
|
|
9869
|
+
* Results use the same snake_case wire shape returned by the hosted MCP API.
|
|
9870
|
+
*/
|
|
9871
|
+
async search(params) {
|
|
9872
|
+
return this.client.mcp("tools/call", {
|
|
9873
|
+
name: "search_public_ground_truth",
|
|
9874
|
+
arguments: {
|
|
9875
|
+
query: params.query ?? params.search,
|
|
9876
|
+
industry: params.industry,
|
|
9877
|
+
location: params.location,
|
|
9878
|
+
source_id: params.sourceId ?? params.source_id,
|
|
9879
|
+
entity_type: params.entityType ?? params.entity_type,
|
|
9880
|
+
native_id: params.nativeId ?? params.native_id,
|
|
9881
|
+
entity_name: params.entityName ?? params.entity_name,
|
|
9882
|
+
domain: params.domain,
|
|
9883
|
+
website_url: params.websiteUrl ?? params.website_url,
|
|
9884
|
+
phone: params.phone,
|
|
9885
|
+
email: params.email,
|
|
9886
|
+
city: params.city,
|
|
9887
|
+
state: params.state,
|
|
9888
|
+
postal_code: params.postalCode ?? params.postal_code,
|
|
9889
|
+
country: params.country,
|
|
9890
|
+
source_url: params.sourceUrl ?? params.source_url,
|
|
9891
|
+
join_keys: params.joinKeys ?? params.join_keys,
|
|
9892
|
+
row_data: params.rowData ?? params.row_data,
|
|
9893
|
+
naics: params.naics,
|
|
9894
|
+
industry_code: params.industryCode ?? params.industry_code,
|
|
9895
|
+
industry_type: params.industryType ?? params.industry_type,
|
|
9896
|
+
year: params.year,
|
|
9897
|
+
quarter: params.quarter,
|
|
9898
|
+
area_fips: params.areaFips ?? params.area_fips,
|
|
9899
|
+
geo_id: params.geoId ?? params.geo_id,
|
|
9900
|
+
own_code: params.ownCode ?? params.own_code,
|
|
9901
|
+
source_updated_after: params.sourceUpdatedAfter ?? params.source_updated_after,
|
|
9902
|
+
source_updated_before: params.sourceUpdatedBefore ?? params.source_updated_before,
|
|
9903
|
+
observed_after: params.observedAfter ?? params.observed_after,
|
|
9904
|
+
observed_before: params.observedBefore ?? params.observed_before,
|
|
9905
|
+
limit: params.limit,
|
|
9906
|
+
offset: params.offset
|
|
9907
|
+
}
|
|
9908
|
+
});
|
|
9909
|
+
}
|
|
9910
|
+
/**
|
|
9911
|
+
* List available public-ground-truth cache sources and their join-key metadata.
|
|
9912
|
+
*/
|
|
9913
|
+
async listSources(params = {}) {
|
|
9914
|
+
return this.client.mcp("tools/call", {
|
|
9915
|
+
name: "list_public_ground_truth_sources",
|
|
9916
|
+
arguments: {
|
|
9917
|
+
query: params.query ?? params.search,
|
|
9918
|
+
status: params.status,
|
|
9919
|
+
limit: params.limit
|
|
9920
|
+
}
|
|
9921
|
+
});
|
|
9922
|
+
}
|
|
9923
|
+
};
|
|
9924
|
+
|
|
8456
9925
|
// src/index.ts
|
|
8457
9926
|
var MCP_TOOL_CACHE_TTL_MS = 3e4;
|
|
8458
9927
|
function inferMcpToolCategory(toolName) {
|
|
@@ -8495,6 +9964,7 @@ function inferMcpToolCategory(toolName) {
|
|
|
8495
9964
|
}
|
|
8496
9965
|
if (toolName.includes("icp")) return "icp";
|
|
8497
9966
|
if (toolName.startsWith("ai_clean_")) return "data_cleaning";
|
|
9967
|
+
if (toolName.includes("public_ground_truth")) return "public_data";
|
|
8498
9968
|
if (toolName.includes("system") || toolName.includes("workflow")) return "automation";
|
|
8499
9969
|
if (toolName.includes("agent") || toolName.includes("platform_health") || toolName === "discover_capabilities") return "observability";
|
|
8500
9970
|
return void 0;
|
|
@@ -8546,6 +10016,7 @@ var Signaliz = class {
|
|
|
8546
10016
|
this.ops = new Ops(this.client);
|
|
8547
10017
|
this.ai = new Ai(this.client);
|
|
8548
10018
|
this.gtm = new GtmKernel(this.client);
|
|
10019
|
+
this.publicGroundTruth = new PublicGroundTruth(this.client);
|
|
8549
10020
|
}
|
|
8550
10021
|
/** Get current workspace info including credits */
|
|
8551
10022
|
async getWorkspace() {
|