@the_ro_show/agent-ads-sdk 0.4.3 → 0.6.0
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 +381 -244
- package/dist/index.d.mts +266 -1
- package/dist/index.d.ts +266 -1
- package/dist/index.js +332 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +332 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -261,11 +261,13 @@ function createClickEvent(params) {
|
|
|
261
261
|
if (params.metadata !== void 0) {
|
|
262
262
|
event.metadata = {
|
|
263
263
|
...params.metadata,
|
|
264
|
-
href: params.href
|
|
264
|
+
href: params.href,
|
|
265
|
+
click_context: params.click_context
|
|
265
266
|
};
|
|
266
267
|
} else {
|
|
267
268
|
event.metadata = {
|
|
268
|
-
href: params.href
|
|
269
|
+
href: params.href,
|
|
270
|
+
click_context: params.click_context
|
|
269
271
|
};
|
|
270
272
|
}
|
|
271
273
|
return event;
|
|
@@ -358,8 +360,10 @@ var DEFAULT_MAX_RETRIES = 2;
|
|
|
358
360
|
var AttentionMarketClient = class {
|
|
359
361
|
http;
|
|
360
362
|
agentId;
|
|
363
|
+
appId;
|
|
361
364
|
constructor(config) {
|
|
362
365
|
this.agentId = config.agentId;
|
|
366
|
+
this.appId = config.appId;
|
|
363
367
|
this.validateConfig(config);
|
|
364
368
|
const httpConfig = {
|
|
365
369
|
apiKey: config.apiKey,
|
|
@@ -517,6 +521,332 @@ var AttentionMarketClient = class {
|
|
|
517
521
|
body: request
|
|
518
522
|
});
|
|
519
523
|
}
|
|
524
|
+
// ============================================================================
|
|
525
|
+
// Intenture Network APIs (Intent-Key Based Matching)
|
|
526
|
+
// ============================================================================
|
|
527
|
+
/**
|
|
528
|
+
* Validate intent-key format.
|
|
529
|
+
* Format: vertical.category[.subcategory][.intent]
|
|
530
|
+
* Examples: "coffee", "coffee.purchase", "legal.estate_planning.wills"
|
|
531
|
+
*/
|
|
532
|
+
validateIntentKey(intentKey) {
|
|
533
|
+
if (!intentKey || intentKey.trim().length === 0) {
|
|
534
|
+
throw new Error("intentKey cannot be empty");
|
|
535
|
+
}
|
|
536
|
+
const validPattern = /^[a-z0-9_]+(\.[a-z0-9_]+)*$/;
|
|
537
|
+
if (!validPattern.test(intentKey)) {
|
|
538
|
+
throw new Error(
|
|
539
|
+
`Invalid intentKey format: "${intentKey}". Use lowercase letters, numbers, underscores, and dots only. Example: "coffee.purchase.delivery"`
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
const segments = intentKey.split(".");
|
|
543
|
+
if (segments.length === 0) {
|
|
544
|
+
throw new Error("intentKey must have at least one segment");
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Validate placement ID is not empty
|
|
549
|
+
*/
|
|
550
|
+
validatePlacementId(placementId) {
|
|
551
|
+
if (!placementId || placementId.trim().length === 0) {
|
|
552
|
+
throw new Error("placementId cannot be empty");
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Validate revenue share percentage (0-50)
|
|
557
|
+
*/
|
|
558
|
+
validateRevenueShare(pct) {
|
|
559
|
+
if (pct !== void 0) {
|
|
560
|
+
if (typeof pct !== "number" || isNaN(pct)) {
|
|
561
|
+
throw new Error("revenueSharePct must be a number");
|
|
562
|
+
}
|
|
563
|
+
if (pct < 0 || pct > 50) {
|
|
564
|
+
throw new Error(
|
|
565
|
+
`revenueSharePct must be between 0 and 50, got ${pct}. Revenue share above 50% is not supported.`
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Normalize and validate locale string
|
|
572
|
+
*/
|
|
573
|
+
normalizeLocale(locale) {
|
|
574
|
+
if (!locale || locale.trim().length === 0) {
|
|
575
|
+
return "en";
|
|
576
|
+
}
|
|
577
|
+
const normalized = locale.trim();
|
|
578
|
+
const languageCode = normalized.split("-")[0]?.toLowerCase();
|
|
579
|
+
if (!languageCode || languageCode.length < 2) {
|
|
580
|
+
return "en";
|
|
581
|
+
}
|
|
582
|
+
return languageCode;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Request an offer using explicit intent-key matching.
|
|
586
|
+
*
|
|
587
|
+
* Use this API when you have HIGH CONFIDENCE about user intent.
|
|
588
|
+
* Intent-keys enable deterministic matching and agent-to-agent coordination.
|
|
589
|
+
*
|
|
590
|
+
* **Current Limitations:**
|
|
591
|
+
* - Backend uses semantic matching (intentKey mapped to taxonomy field)
|
|
592
|
+
* - `campaign_id` not yet available from backend (returns `unit_id` as placeholder)
|
|
593
|
+
* - `click_url` equals `direct_url` (click redirector not implemented)
|
|
594
|
+
* - Revenue share tracked but payouts not active (preview mode)
|
|
595
|
+
*
|
|
596
|
+
* @example
|
|
597
|
+
* ```typescript
|
|
598
|
+
* // User explicitly said "order coffee for delivery"
|
|
599
|
+
* const offer = await client.requestOffer({
|
|
600
|
+
* placementId: 'order_card',
|
|
601
|
+
* intentKey: 'coffee.purchase.delivery',
|
|
602
|
+
* context: { geo: { city: 'SF', country: 'US' } }
|
|
603
|
+
* });
|
|
604
|
+
*
|
|
605
|
+
* if (offer) {
|
|
606
|
+
* // Use tracked click URL for attribution
|
|
607
|
+
* window.open(offer.click_url);
|
|
608
|
+
* }
|
|
609
|
+
* ```
|
|
610
|
+
*
|
|
611
|
+
* @throws {Error} If agentId or appId not provided in SDKConfig
|
|
612
|
+
* @throws {Error} If intentKey format is invalid
|
|
613
|
+
* @throws {Error} If revenueSharePct out of range (0-50)
|
|
614
|
+
*/
|
|
615
|
+
async requestOffer(params, options) {
|
|
616
|
+
if (!this.agentId) {
|
|
617
|
+
throw new Error(
|
|
618
|
+
'requestOffer() requires agentId in SDKConfig. Provide agentId in constructor: new AttentionMarketClient({ agentId: "agt_123", ... })'
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
if (!this.appId) {
|
|
622
|
+
throw new Error(
|
|
623
|
+
'requestOffer() requires appId in SDKConfig. Provide appId in constructor: new AttentionMarketClient({ appId: "app_456", ... })'
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
this.validateIntentKey(params.intentKey);
|
|
627
|
+
this.validatePlacementId(params.placementId);
|
|
628
|
+
this.validateRevenueShare(params.revenueSharePct);
|
|
629
|
+
const semanticContext = params.context?.semanticContext;
|
|
630
|
+
const country = params.context?.geo?.country || "US";
|
|
631
|
+
const language = this.normalizeLocale(params.context?.locale);
|
|
632
|
+
if (params.sourceAgentId && console.warn) {
|
|
633
|
+
console.warn(
|
|
634
|
+
"Revenue share is in PREVIEW mode. sourceAgentId and revenueSharePct are logged for analytics but payouts are not active yet. Expected: Q2 2026"
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
const idempotencyKey = options?.idempotencyKey || generateUUID();
|
|
638
|
+
const request = {
|
|
639
|
+
request_id: idempotencyKey,
|
|
640
|
+
agent_id: this.agentId,
|
|
641
|
+
placement: {
|
|
642
|
+
type: "sponsored_suggestion",
|
|
643
|
+
// Default placement type
|
|
644
|
+
surface: params.placementId
|
|
645
|
+
},
|
|
646
|
+
opportunity: {
|
|
647
|
+
intent: {
|
|
648
|
+
taxonomy: params.intentKey,
|
|
649
|
+
// TEMPORARY: Maps to taxonomy until intent_key field added
|
|
650
|
+
query: semanticContext || params.intentKey
|
|
651
|
+
},
|
|
652
|
+
context: {
|
|
653
|
+
country,
|
|
654
|
+
language,
|
|
655
|
+
platform: "web",
|
|
656
|
+
...params.context?.geo?.region ? { region: params.context.geo.region } : {},
|
|
657
|
+
...params.context?.geo?.city ? { city: params.context.geo.city } : {}
|
|
658
|
+
},
|
|
659
|
+
constraints: {
|
|
660
|
+
max_units: 1,
|
|
661
|
+
allowed_unit_types: ["sponsored_suggestion"]
|
|
662
|
+
},
|
|
663
|
+
privacy: {
|
|
664
|
+
data_policy: "coarse_only"
|
|
665
|
+
}
|
|
666
|
+
},
|
|
667
|
+
// Add semantic context if provided (for fallback matching)
|
|
668
|
+
...semanticContext ? { context: semanticContext } : {},
|
|
669
|
+
user_intent: params.intentKey
|
|
670
|
+
};
|
|
671
|
+
const response = await this.decideRaw(request, { idempotencyKey });
|
|
672
|
+
if (response.status === "no_fill" || response.units.length === 0) {
|
|
673
|
+
return null;
|
|
674
|
+
}
|
|
675
|
+
const adUnit = response.units[0];
|
|
676
|
+
if (!adUnit || adUnit.unit_type !== "sponsored_suggestion") {
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
const impressionId = generateUUID();
|
|
680
|
+
const matchMethod = semanticContext ? "hybrid" : "semantic";
|
|
681
|
+
return {
|
|
682
|
+
offer_id: adUnit.unit_id,
|
|
683
|
+
request_id: response.request_id,
|
|
684
|
+
impression_id: impressionId,
|
|
685
|
+
// LIMITATION: Backend doesn't return campaign_id yet - use unit_id as placeholder
|
|
686
|
+
campaign_id: adUnit.unit_id,
|
|
687
|
+
creative: {
|
|
688
|
+
title: adUnit.suggestion.title,
|
|
689
|
+
body: adUnit.suggestion.body,
|
|
690
|
+
cta: adUnit.suggestion.cta
|
|
691
|
+
},
|
|
692
|
+
// LIMITATION: Click redirector not implemented yet
|
|
693
|
+
// Both URLs are identical until /c/{token} endpoint is deployed
|
|
694
|
+
click_url: adUnit.suggestion.action_url,
|
|
695
|
+
direct_url: adUnit.suggestion.action_url,
|
|
696
|
+
disclosure: {
|
|
697
|
+
label: adUnit.disclosure.label,
|
|
698
|
+
sponsor_name: adUnit.disclosure.sponsor_name
|
|
699
|
+
},
|
|
700
|
+
tracking_token: adUnit.tracking.token,
|
|
701
|
+
match_info: {
|
|
702
|
+
match_method: matchMethod,
|
|
703
|
+
// Accurately reports semantic or hybrid
|
|
704
|
+
matched_intent_key: params.intentKey
|
|
705
|
+
},
|
|
706
|
+
// Revenue share: Logged for analytics but payouts not active
|
|
707
|
+
...params.sourceAgentId ? {
|
|
708
|
+
revenue_share: {
|
|
709
|
+
status: "preview",
|
|
710
|
+
source_agent_id: params.sourceAgentId,
|
|
711
|
+
...params.revenueSharePct !== void 0 ? { source_agent_pct: params.revenueSharePct } : {}
|
|
712
|
+
}
|
|
713
|
+
} : {},
|
|
714
|
+
ttl_ms: response.ttl_ms
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Request an offer using semantic context matching.
|
|
719
|
+
*
|
|
720
|
+
* This is the fuzzy, discovery API for when you're NOT certain what
|
|
721
|
+
* the user wants. Pass conversation context and let semantic search
|
|
722
|
+
* figure out the best match.
|
|
723
|
+
*
|
|
724
|
+
* **Current Limitations:**
|
|
725
|
+
* - `campaign_id` not yet available from backend (returns `unit_id` as placeholder)
|
|
726
|
+
* - `click_url` equals `direct_url` (click redirector not implemented)
|
|
727
|
+
* - Revenue share tracked but payouts not active (preview mode)
|
|
728
|
+
* - Conversation history auto-limited to last 5 messages
|
|
729
|
+
*
|
|
730
|
+
* @example
|
|
731
|
+
* ```typescript
|
|
732
|
+
* const offer = await client.requestOfferFromContext({
|
|
733
|
+
* placementId: 'chat_suggestion',
|
|
734
|
+
* userMessage: "I'm so tired, long day at work...",
|
|
735
|
+
* conversationHistory: ["How was your day?", "Exhausting"],
|
|
736
|
+
* context: { geo: { city: 'NYC' } }
|
|
737
|
+
* });
|
|
738
|
+
*
|
|
739
|
+
* if (offer) {
|
|
740
|
+
* console.log(`Maybe you'd like: ${offer.creative.title}`);
|
|
741
|
+
* }
|
|
742
|
+
* ```
|
|
743
|
+
*
|
|
744
|
+
* @throws {Error} If agentId was not provided in SDKConfig
|
|
745
|
+
* @throws {Error} If revenueSharePct out of range (0-50)
|
|
746
|
+
*/
|
|
747
|
+
async requestOfferFromContext(params, options) {
|
|
748
|
+
if (!this.agentId) {
|
|
749
|
+
throw new Error(
|
|
750
|
+
'requestOfferFromContext() requires agentId in SDKConfig. Provide agentId in constructor: new AttentionMarketClient({ agentId: "agt_123", ... })'
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
if (!this.appId) {
|
|
754
|
+
throw new Error(
|
|
755
|
+
'requestOfferFromContext() requires appId in SDKConfig. Provide appId in constructor: new AttentionMarketClient({ appId: "app_456", ... })'
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
this.validatePlacementId(params.placementId);
|
|
759
|
+
this.validateRevenueShare(params.revenueSharePct);
|
|
760
|
+
if (params.sourceAgentId && console.warn) {
|
|
761
|
+
console.warn(
|
|
762
|
+
"Revenue share is in PREVIEW mode. sourceAgentId and revenueSharePct are logged for analytics but payouts are not active yet. Expected: Q2 2026"
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
const historyLimit = 5;
|
|
766
|
+
const history = params.conversationHistory || [];
|
|
767
|
+
const limitedHistory = history.slice(-historyLimit);
|
|
768
|
+
const contextParts = [...limitedHistory, params.userMessage];
|
|
769
|
+
const context = contextParts.join("\n");
|
|
770
|
+
const country = params.context?.geo?.country || "US";
|
|
771
|
+
const language = this.normalizeLocale(params.context?.locale);
|
|
772
|
+
const taxonomy = params.suggestedCategory || "unknown";
|
|
773
|
+
const idempotencyKey = options?.idempotencyKey || generateUUID();
|
|
774
|
+
const request = {
|
|
775
|
+
request_id: idempotencyKey,
|
|
776
|
+
agent_id: this.agentId,
|
|
777
|
+
placement: {
|
|
778
|
+
type: "sponsored_suggestion",
|
|
779
|
+
surface: params.placementId
|
|
780
|
+
},
|
|
781
|
+
opportunity: {
|
|
782
|
+
intent: {
|
|
783
|
+
taxonomy,
|
|
784
|
+
query: params.userMessage
|
|
785
|
+
},
|
|
786
|
+
context: {
|
|
787
|
+
country,
|
|
788
|
+
language,
|
|
789
|
+
platform: "web",
|
|
790
|
+
...params.context?.geo?.region ? { region: params.context.geo.region } : {},
|
|
791
|
+
...params.context?.geo?.city ? { city: params.context.geo.city } : {}
|
|
792
|
+
},
|
|
793
|
+
constraints: {
|
|
794
|
+
max_units: 1,
|
|
795
|
+
allowed_unit_types: ["sponsored_suggestion"]
|
|
796
|
+
},
|
|
797
|
+
privacy: {
|
|
798
|
+
data_policy: "coarse_only"
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
...context ? { context } : {},
|
|
802
|
+
user_intent: params.userMessage
|
|
803
|
+
};
|
|
804
|
+
const response = await this.decideRaw(request, { idempotencyKey });
|
|
805
|
+
if (response.status === "no_fill" || response.units.length === 0) {
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
const adUnit = response.units[0];
|
|
809
|
+
if (!adUnit || adUnit.unit_type !== "sponsored_suggestion") {
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
const impressionId = generateUUID();
|
|
813
|
+
const similarity = adUnit._score?.relevance;
|
|
814
|
+
return {
|
|
815
|
+
offer_id: adUnit.unit_id,
|
|
816
|
+
request_id: response.request_id,
|
|
817
|
+
impression_id: impressionId,
|
|
818
|
+
// LIMITATION: Backend doesn't return campaign_id yet - use unit_id as placeholder
|
|
819
|
+
campaign_id: adUnit.unit_id,
|
|
820
|
+
creative: {
|
|
821
|
+
title: adUnit.suggestion.title,
|
|
822
|
+
body: adUnit.suggestion.body,
|
|
823
|
+
cta: adUnit.suggestion.cta
|
|
824
|
+
},
|
|
825
|
+
// LIMITATION: Click redirector not implemented yet
|
|
826
|
+
// Both URLs are identical until /c/{token} endpoint is deployed
|
|
827
|
+
click_url: adUnit.suggestion.action_url,
|
|
828
|
+
direct_url: adUnit.suggestion.action_url,
|
|
829
|
+
disclosure: {
|
|
830
|
+
label: adUnit.disclosure.label,
|
|
831
|
+
sponsor_name: adUnit.disclosure.sponsor_name
|
|
832
|
+
},
|
|
833
|
+
tracking_token: adUnit.tracking.token,
|
|
834
|
+
match_info: {
|
|
835
|
+
match_method: "semantic",
|
|
836
|
+
// Always semantic for this API
|
|
837
|
+
...similarity !== void 0 ? { similarity } : {}
|
|
838
|
+
},
|
|
839
|
+
// Revenue share: Logged for analytics but payouts not active
|
|
840
|
+
...params.sourceAgentId ? {
|
|
841
|
+
revenue_share: {
|
|
842
|
+
status: "preview",
|
|
843
|
+
source_agent_id: params.sourceAgentId,
|
|
844
|
+
...params.revenueSharePct !== void 0 ? { source_agent_pct: params.revenueSharePct } : {}
|
|
845
|
+
}
|
|
846
|
+
} : {},
|
|
847
|
+
ttl_ms: response.ttl_ms
|
|
848
|
+
};
|
|
849
|
+
}
|
|
520
850
|
};
|
|
521
851
|
|
|
522
852
|
// src/mock-client.ts
|