@adzenai/ai 1.2.1 → 1.2.2

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 CHANGED
@@ -107,6 +107,8 @@ Anchors are snapped forward to the next paragraph break, so segments are always
107
107
 
108
108
  At most one ad is placed per paragraph break. Ads that would land on an already-used break move to the next free one (the end of the message counts as a slot), and any that still have nowhere to go are held back until the message grows, rather than stacking in one spot.
109
109
 
110
+ Inline ads are deduped by placement instance — the `(ad_id, paragraph_index)` pair — not by `ad_id` alone, so a creative served more than once in a response (spaced repeats) renders each showing rather than just the first. `paragraph_index` is the identity key while `content_offset` stays the position anchor; servers that omit `paragraph_index` fall back to `0` and keep the previous single-render behavior.
111
+
110
112
  ## Configuration
111
113
 
112
114
  ```typescript
@@ -134,6 +136,22 @@ SDK-managed headers (auth, content-type, correlation IDs) always take precedence
134
136
 
135
137
  The middleware never makes the assistant message wait on the ad pipeline: the prefetch is dispatched without being awaited, and content events are forwarded as they arrive whether or not an ad has been matched yet.
136
138
 
139
+ ## Publish to NPM
140
+
141
+ The following commands needs to run both in /ai and /core package folders.
142
+
143
+ ```bash
144
+ pnpm build
145
+ ```
146
+
147
+ ```bash
148
+ npm version 1.2.2 --no-git-tag-version
149
+ ```
150
+
151
+ ```bash
152
+ npm publish --access public
153
+ ```
154
+
137
155
  ## License
138
156
 
139
157
  MIT
@@ -323,6 +323,15 @@ var import_react4 = require("react");
323
323
  function readOffset(payload) {
324
324
  return typeof payload.content_offset === "number" ? payload.content_offset : Number.MAX_SAFE_INTEGER;
325
325
  }
326
+ function sameInstance(a, b) {
327
+ return a.ad_id === b.ad_id && a.paragraph_index === b.paragraph_index;
328
+ }
329
+ function addInlineAd(existing, anchored) {
330
+ if (existing.some((a) => sameInstance(a, anchored))) return existing;
331
+ return [...existing, anchored].sort(
332
+ (a, b) => a.content_offset - b.content_offset
333
+ );
334
+ }
326
335
  var PARAGRAPH_BREAK = /\n[ \t]*\n/;
327
336
  var ENDS_ON_PARAGRAPH_BREAK = /\n[ \t]*\n$/;
328
337
  function snapToParagraphEnd(content, offset) {
@@ -371,19 +380,19 @@ function useAdzenInlineAds() {
371
380
  if (!messageId) return;
372
381
  const anchored = {
373
382
  ...payload,
383
+ // Back-compat: older servers omit paragraph_index, so treat it as 0.
384
+ // With those servers each ad_id appears once, so (ad_id, 0) degrades to
385
+ // the previous ad_id-only dedup behavior.
386
+ paragraph_index: payload.paragraph_index ?? 0,
374
387
  message_id: messageId,
375
388
  content_offset: readOffset(payload)
376
389
  };
377
390
  setAdsMap((prev) => {
378
391
  const existing = prev.get(messageId) ?? [];
379
- if (existing.some((a) => a.ad_id === anchored.ad_id)) return prev;
392
+ const merged = addInlineAd(existing, anchored);
393
+ if (merged === existing) return prev;
380
394
  const next = new Map(prev);
381
- next.set(
382
- messageId,
383
- [...existing, anchored].sort(
384
- (a, b) => a.content_offset - b.content_offset
385
- )
386
- );
395
+ next.set(messageId, merged);
387
396
  return next;
388
397
  });
389
398
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/copilotkit/react/index.ts","../../../src/copilotkit/react/AdzenCard.tsx","../../../src/copilotkit/react/useAdzenPlacement.ts","../../../src/copilotkit/react/InlineAd.tsx","../../../src/copilotkit/react/useAdzenInlineAds.ts","../../../src/copilotkit/react/bridge.ts"],"sourcesContent":["export { AdzenCard } from \"./AdzenCard.js\";\nexport type { AdzenCardProps } from \"./AdzenCard.js\";\nexport { InlineAd } from \"./InlineAd.js\";\nexport type { InlineAdProps } from \"./InlineAd.js\";\nexport { useAdzenPlacement } from \"./useAdzenPlacement.js\";\nexport type { UseAdzenPlacementReturn } from \"./useAdzenPlacement.js\";\nexport {\n useAdzenInlineAds,\n buildInlineAdSegments,\n} from \"./useAdzenInlineAds.js\";\nexport type {\n UseAdzenInlineAdsReturn,\n InlineAdSegment,\n} from \"./useAdzenInlineAds.js\";\nexport {\n dispatchPlacementEvent,\n dispatchInlineAdEvent,\n dispatchPlacementEvents,\n} from \"./bridge.js\";\n","import { useRef, useEffect, useState } from \"react\";\nimport { observeViewability } from \"@adzenai/core\";\nimport { useAdzenPlacement } from \"./useAdzenPlacement.js\";\n\nexport interface AdzenCardProps {\n messageId: string;\n adUnitPosition?: string;\n viewabilityThresholdMs?: number;\n className?: string;\n}\n\nfunction fireBeacon(url: string): void {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {\n setTimeout(() => {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {});\n }, 1000);\n });\n}\n\n/**\n * Renders a sponsored ad card for a given assistant message. Returns `null`\n * (no DOM output) when there is no ad for the message.\n */\nexport function AdzenCard({\n messageId,\n adUnitPosition = \"chin\",\n viewabilityThresholdMs = 1000,\n className,\n}: AdzenCardProps) {\n const { getAdForMessage } = useAdzenPlacement();\n const ad = getAdForMessage(messageId);\n const cardRef = useRef<HTMLDivElement>(null);\n const [renderFired, setRenderFired] = useState(false);\n const [viewed, setViewed] = useState(false);\n\n useEffect(() => {\n if (!ad || renderFired || !ad.render_impression_url) return;\n setRenderFired(true);\n\n const url = new URL(ad.render_impression_url);\n url.searchParams.set(\"ad_unit_position\", adUnitPosition);\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n }, [ad, renderFired, adUnitPosition]);\n\n useEffect(() => {\n if (!ad || !cardRef.current || viewed || !ad.view_impression_url) return;\n\n return observeViewability({\n element: cardRef.current,\n thresholdMs: viewabilityThresholdMs,\n onViewed: () => {\n setViewed(true);\n if (!ad.view_impression_url) return;\n const url = new URL(ad.view_impression_url);\n url.searchParams.set(\"ad_unit_position\", adUnitPosition);\n url.searchParams.set(\"viewability_ms\", String(viewabilityThresholdMs));\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n },\n });\n }, [ad, viewed, adUnitPosition, viewabilityThresholdMs]);\n\n if (!ad) return null;\n\n const initial = ad.advertiser_name?.charAt(0).toUpperCase() ?? \"?\";\n\n return (\n <div\n ref={cardRef}\n className={className}\n data-adzen-ad-unit-position={adUnitPosition}\n style={cardStyles.wrapper}\n >\n <div style={cardStyles.avatar}>\n {ad.advertiser_image_url ? (\n <img\n src={ad.advertiser_image_url}\n alt={ad.advertiser_name}\n style={cardStyles.avatarImg}\n />\n ) : (\n <span style={cardStyles.avatarLetter}>{initial}</span>\n )}\n </div>\n\n <div style={cardStyles.content}>\n <div style={cardStyles.topRow}>\n <span style={cardStyles.advertiserName}>{ad.advertiser_name}</span>\n <span style={cardStyles.relevantAd}>Relevant Ad</span>\n </div>\n <div style={cardStyles.bottomRow}>\n <span style={cardStyles.headline}>{ad.headline}</span>\n <a\n href={ad.destination_url}\n target=\"_blank\"\n rel=\"noopener sponsored\"\n style={cardStyles.cta}\n >\n {ad.cta_text}\n </a>\n </div>\n </div>\n </div>\n );\n}\n\nconst cardStyles: Record<string, React.CSSProperties> = {\n wrapper: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.625rem\",\n fontSize: \"0.85rem\",\n padding: \"0.625rem 0.75rem\",\n },\n avatar: {\n width: 32,\n height: 32,\n borderRadius: \"50%\",\n background: \"#d1d5db\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n overflow: \"hidden\",\n },\n avatarImg: {\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\" as const,\n borderRadius: \"50%\",\n },\n avatarLetter: {\n fontSize: \"0.8rem\",\n fontWeight: 700,\n color: \"#374151\",\n lineHeight: 1,\n },\n content: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"0.125rem\",\n minWidth: 0,\n },\n topRow: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n },\n advertiserName: {\n fontWeight: 700,\n color: \"inherit\",\n },\n relevantAd: {\n color: \"#8b949e\",\n fontSize: \"0.8rem\",\n },\n bottomRow: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n },\n headline: {\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n },\n cta: {\n color: \"#58a6ff\",\n textDecoration: \"none\",\n fontWeight: 500,\n flexShrink: 0,\n },\n};\n","import { useState, useEffect, useCallback } from \"react\";\nimport type { AdzenPlacement } from \"@adzenai/core\";\n\nexport interface UseAdzenPlacementReturn {\n getAdForMessage(messageId: string): AdzenPlacement | null;\n placements: Map<string, AdzenPlacement>;\n}\n\n/**\n * React hook that subscribes to `adzen_placement` custom events and\n * maintains a map of messageId -> AdzenPlacement.\n *\n * Works anywhere in the React tree below `<CopilotKit>` — no wrapper\n * provider required.\n */\nexport function useAdzenPlacement(): UseAdzenPlacementReturn {\n const [placements, setPlacements] = useState<Map<string, AdzenPlacement>>(\n () => new Map(),\n );\n\n useEffect(() => {\n function handler(e: Event) {\n const detail = (e as CustomEvent).detail;\n if (!detail || typeof detail !== \"object\") return;\n\n const placement = detail as AdzenPlacement & { message_id?: string };\n const messageId = placement.message_id;\n if (!messageId) return;\n\n setPlacements((prev) => {\n const existing = prev.get(messageId);\n if (existing && existing.ad_id === placement.ad_id) return prev;\n const next = new Map(prev);\n next.set(messageId, placement);\n return next;\n });\n }\n\n window.addEventListener(\"adzen_placement\", handler);\n return () => window.removeEventListener(\"adzen_placement\", handler);\n }, []);\n\n const getAdForMessage = useCallback(\n (messageId: string): AdzenPlacement | null => {\n return placements.get(messageId) ?? null;\n },\n [placements],\n );\n\n return { getAdForMessage, placements };\n}\n","import { useRef, useEffect, useState } from \"react\";\nimport { observeViewability } from \"@adzenai/core\";\nimport type { InlineAdEvent } from \"@adzenai/core\";\n\nexport interface InlineAdProps {\n ad: InlineAdEvent;\n viewabilityThresholdMs?: number;\n className?: string;\n}\n\nfunction fireBeacon(url: string): void {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {\n setTimeout(() => {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {});\n }, 1000);\n });\n}\n\n/**\n * Renders an inline ad as a single line of text — an \"Ad\" pill, the advertiser\n * name, and the CTA as the only link — rather than a card, so it sits in the\n * message body without competing with the assistant's own content.\n *\n * Tracks render/view impressions via the same beacon pattern as AdzenCard.\n */\nexport function InlineAd({\n ad,\n viewabilityThresholdMs = 1000,\n className,\n}: InlineAdProps) {\n const ref = useRef<HTMLDivElement>(null);\n const [renderFired, setRenderFired] = useState(false);\n const [viewed, setViewed] = useState(false);\n\n useEffect(() => {\n if (renderFired || !ad.tracking.impression_url) return;\n setRenderFired(true);\n\n const url = new URL(ad.tracking.impression_url);\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n }, [ad, renderFired]);\n\n useEffect(() => {\n if (!ref.current || viewed || !ad.tracking.impression_url) return;\n\n return observeViewability({\n element: ref.current,\n thresholdMs: viewabilityThresholdMs,\n onViewed: () => {\n setViewed(true);\n if (!ad.tracking.impression_url) return;\n const url = new URL(ad.tracking.impression_url);\n url.searchParams.set(\"type\", \"view\");\n url.searchParams.set(\"viewability_ms\", String(viewabilityThresholdMs));\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n },\n });\n }, [ad, viewed, viewabilityThresholdMs]);\n\n const advertiser = ad.creative.advertiser_name ?? ad.creative.headline;\n\n return (\n <div ref={ref} className={className} style={inlineStyles.wrapper}>\n <span style={inlineStyles.badge} aria-label=\"Advertisement\">\n Ad\n </span>\n {advertiser ? (\n <span style={inlineStyles.advertiser}>{advertiser} </span>\n ) : null}\n <a\n href={ad.creative.destination_url}\n target=\"_blank\"\n rel=\"noopener sponsored\"\n style={inlineStyles.cta}\n >\n {ad.creative.cta_text}\n </a>\n </div>\n );\n}\n\nconst inlineStyles: Record<string, React.CSSProperties> = {\n // Block-level so the snippet starts on its own line below the text it\n // follows, but otherwise plain: no border, background, or padding.\n wrapper: {\n display: \"block\",\n margin: \"0.5rem 0\",\n fontSize: \"0.9em\",\n lineHeight: 1.5,\n },\n badge: {\n display: \"inline-block\",\n marginRight: \"0.4em\",\n padding: \"0.1em 0.4em\",\n borderRadius: 4,\n background: \"#30363d\",\n color: \"#8b949e\",\n fontSize: \"0.75em\",\n fontWeight: 700,\n letterSpacing: \"0.05em\",\n textTransform: \"uppercase\" as const,\n verticalAlign: \"middle\",\n },\n advertiser: {\n color: \"#8b949e\",\n },\n cta: {\n color: \"#58a6ff\",\n textDecoration: \"none\",\n fontWeight: 500,\n },\n};\n","import { useState, useEffect, useCallback } from \"react\";\nimport type { InlineAdEvent, InlineAdEventPayload } from \"@adzenai/core\";\n\n/** A run of message text, or an ad anchored to the end of the run before it. */\nexport type InlineAdSegment =\n | { kind: \"text\"; text: string }\n | { kind: \"ad\"; ad: InlineAdEventPayload };\n\nexport interface UseAdzenInlineAdsReturn {\n getInlineAdsForMessage(messageId: string): InlineAdEventPayload[];\n getInlineAdSegments(messageId: string, content: string): InlineAdSegment[];\n}\n\n/**\n * Ads without an anchor sort last so they render at the end of the message,\n * which is the best guess for a payload that predates `content_offset`.\n */\nfunction readOffset(payload: InlineAdEvent & { content_offset?: unknown }): number {\n return typeof payload.content_offset === \"number\"\n ? payload.content_offset\n : Number.MAX_SAFE_INTEGER;\n}\n\n/** A blank line, which ends a paragraph in both plain text and Markdown. */\nconst PARAGRAPH_BREAK = /\\n[ \\t]*\\n/;\nconst ENDS_ON_PARAGRAPH_BREAK = /\\n[ \\t]*\\n$/;\n\n/**\n * Moves an anchor forward to the end of the paragraph it falls in.\n *\n * Deltas are token-sized, so a raw offset routinely lands mid-word or inside\n * a Markdown construct (`**bold te|xt**`). Splitting there would break both\n * the sentence and the Markdown, so the ad is deferred to the next paragraph\n * break — the `paragraph_break` position the API asks for. While the paragraph\n * is still streaming there is no break yet, so the ad sits at the end of the\n * content and settles into place once the paragraph closes.\n */\nfunction snapToParagraphEnd(content: string, offset: number): number {\n if (offset >= content.length) return content.length;\n\n // The preceding frame already ended on a blank line, so this is the start of\n // a fresh paragraph and the ad belongs here.\n if (ENDS_ON_PARAGRAPH_BREAK.test(content.slice(0, offset))) return offset;\n\n // Search from one character back so an offset landing inside the blank line\n // itself still resolves to that break rather than skipping to the next one.\n return nextParagraphEnd(content, Math.max(0, offset - 1));\n}\n\n/**\n * Position just past the first paragraph break at or after `from`, or the end\n * of the content when no break follows.\n */\nfunction nextParagraphEnd(content: string, from: number): number {\n if (from >= content.length) return content.length;\n const match = PARAGRAPH_BREAK.exec(content.slice(from));\n return match ? from + match.index + match[0].length : content.length;\n}\n\n/**\n * Splits `content` into text and ad segments, placing each ad after the text\n * that had streamed when it arrived.\n *\n * At most one ad is placed per paragraph break. Several ads routinely resolve\n * to the same break — the API can emit a burst of them, and every ad anchored\n * inside one paragraph snaps to that paragraph's end — so each ad after the\n * first is moved to the next free break, with the end of the content usable as\n * a final slot. An ad with no free slot is left out and appears once the\n * message has grown enough to hold it, which keeps ads from piling up in one\n * spot while a paragraph is still streaming.\n *\n * @param ads Must be sorted by `content_offset` ascending.\n */\nexport function buildInlineAdSegments(\n content: string,\n ads: InlineAdEventPayload[],\n): InlineAdSegment[] {\n if (ads.length === 0) {\n return content ? [{ kind: \"text\", text: content }] : [];\n }\n\n const segments: InlineAdSegment[] = [];\n let cursor = 0;\n let lastAnchor = -1;\n\n for (const ad of ads) {\n let anchor = snapToParagraphEnd(content, Math.max(ad.content_offset, 0));\n if (anchor <= lastAnchor) anchor = nextParagraphEnd(content, lastAnchor);\n if (anchor <= lastAnchor) continue;\n\n if (anchor > cursor) {\n segments.push({ kind: \"text\", text: content.slice(cursor, anchor) });\n }\n segments.push({ kind: \"ad\", ad });\n cursor = anchor;\n lastAnchor = anchor;\n }\n\n if (cursor < content.length) {\n segments.push({ kind: \"text\", text: content.slice(cursor) });\n }\n\n return segments;\n}\n\n/**\n * React hook that subscribes to `adzen_inline_ad` custom events on\n * `window` and maintains a map of messageId -> InlineAdEventPayload[].\n *\n * Supports multiple inline ads per message per the cardinality requirements.\n * Ads are kept sorted by `content_offset` so they can be interleaved with the\n * message text at the point in the stream where they arrived.\n */\nexport function useAdzenInlineAds(): UseAdzenInlineAdsReturn {\n const [adsMap, setAdsMap] = useState<Map<string, InlineAdEventPayload[]>>(\n () => new Map(),\n );\n\n useEffect(() => {\n function handler(e: Event) {\n const detail = (e as CustomEvent).detail;\n if (!detail || typeof detail !== \"object\") return;\n\n const payload = detail as InlineAdEvent & { message_id?: string };\n const messageId = payload.message_id;\n if (!messageId) return;\n\n const anchored: InlineAdEventPayload = {\n ...payload,\n message_id: messageId,\n content_offset: readOffset(payload),\n };\n\n setAdsMap((prev) => {\n const existing = prev.get(messageId) ?? [];\n if (existing.some((a) => a.ad_id === anchored.ad_id)) return prev;\n const next = new Map(prev);\n next.set(\n messageId,\n [...existing, anchored].sort(\n (a, b) => a.content_offset - b.content_offset,\n ),\n );\n return next;\n });\n }\n\n window.addEventListener(\"adzen_inline_ad\", handler);\n return () => window.removeEventListener(\"adzen_inline_ad\", handler);\n }, []);\n\n const getInlineAdsForMessage = useCallback(\n (messageId: string): InlineAdEventPayload[] => {\n return adsMap.get(messageId) ?? [];\n },\n [adsMap],\n );\n\n const getInlineAdSegments = useCallback(\n (messageId: string, content: string): InlineAdSegment[] =>\n buildInlineAdSegments(content, adsMap.get(messageId) ?? []),\n [adsMap],\n );\n\n return { getInlineAdsForMessage, getInlineAdSegments };\n}\n","import type { AdzenPlacement, InlineAdEventPayload } from \"@adzenai/core\";\n\nfunction isCustomEvent(event: Record<string, unknown>): boolean {\n const type = String(event.type ?? \"\");\n return type === \"CUSTOM_EVENT\" || type === \"CUSTOM\" || type === \"CustomEvent\";\n}\n\n/**\n * Checks whether an AG-UI event is an `adzen_placement` custom event\n * and, if so, dispatches it as a browser `CustomEvent` on `window`\n * so that `useAdzenPlacement` can pick it up.\n *\n * Call this for every event returned by middleware `processEvent()` or\n * `processStream()`. Non-placement events are silently ignored.\n */\nexport function dispatchPlacementEvent(\n event: Record<string, unknown>,\n): boolean {\n if (typeof window === \"undefined\") return false;\n if (event.name !== \"adzen_placement\") return false;\n if (!isCustomEvent(event)) return false;\n\n const value = event.value as (AdzenPlacement & { message_id?: string }) | undefined;\n if (!value) return false;\n\n window.dispatchEvent(\n new CustomEvent(\"adzen_placement\", { detail: value }),\n );\n return true;\n}\n\n/**\n * Checks whether an AG-UI event is an `adzen_inline_ad` custom event\n * and, if so, dispatches it as a browser `CustomEvent` on `window`\n * so that `useAdzenInlineAds` can pick it up.\n */\nexport function dispatchInlineAdEvent(\n event: Record<string, unknown>,\n): boolean {\n if (typeof window === \"undefined\") return false;\n if (event.name !== \"adzen_inline_ad\") return false;\n if (!isCustomEvent(event)) return false;\n\n const value = event.value as Partial<InlineAdEventPayload> | undefined;\n if (!value) return false;\n\n window.dispatchEvent(\n new CustomEvent(\"adzen_inline_ad\", { detail: value }),\n );\n return true;\n}\n\n/**\n * Convenience wrapper: iterates an array of AG-UI events and dispatches\n * any placement or inline ad events to the browser.\n */\nexport function dispatchPlacementEvents(\n events: Record<string, unknown>[],\n): void {\n for (const event of events) {\n dispatchPlacementEvent(event);\n dispatchInlineAdEvent(event);\n }\n}\n"],"mappings":"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","names":["import_react","import_react","import_core","import_jsx_runtime","fireBeacon","import_react"]}
1
+ {"version":3,"sources":["../../../src/copilotkit/react/index.ts","../../../src/copilotkit/react/AdzenCard.tsx","../../../src/copilotkit/react/useAdzenPlacement.ts","../../../src/copilotkit/react/InlineAd.tsx","../../../src/copilotkit/react/useAdzenInlineAds.ts","../../../src/copilotkit/react/bridge.ts"],"sourcesContent":["export { AdzenCard } from \"./AdzenCard.js\";\nexport type { AdzenCardProps } from \"./AdzenCard.js\";\nexport { InlineAd } from \"./InlineAd.js\";\nexport type { InlineAdProps } from \"./InlineAd.js\";\nexport { useAdzenPlacement } from \"./useAdzenPlacement.js\";\nexport type { UseAdzenPlacementReturn } from \"./useAdzenPlacement.js\";\nexport {\n useAdzenInlineAds,\n buildInlineAdSegments,\n} from \"./useAdzenInlineAds.js\";\nexport type {\n UseAdzenInlineAdsReturn,\n InlineAdSegment,\n} from \"./useAdzenInlineAds.js\";\nexport {\n dispatchPlacementEvent,\n dispatchInlineAdEvent,\n dispatchPlacementEvents,\n} from \"./bridge.js\";\n","import { useRef, useEffect, useState } from \"react\";\nimport { observeViewability } from \"@adzenai/core\";\nimport { useAdzenPlacement } from \"./useAdzenPlacement.js\";\n\nexport interface AdzenCardProps {\n messageId: string;\n adUnitPosition?: string;\n viewabilityThresholdMs?: number;\n className?: string;\n}\n\nfunction fireBeacon(url: string): void {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {\n setTimeout(() => {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {});\n }, 1000);\n });\n}\n\n/**\n * Renders a sponsored ad card for a given assistant message. Returns `null`\n * (no DOM output) when there is no ad for the message.\n */\nexport function AdzenCard({\n messageId,\n adUnitPosition = \"chin\",\n viewabilityThresholdMs = 1000,\n className,\n}: AdzenCardProps) {\n const { getAdForMessage } = useAdzenPlacement();\n const ad = getAdForMessage(messageId);\n const cardRef = useRef<HTMLDivElement>(null);\n const [renderFired, setRenderFired] = useState(false);\n const [viewed, setViewed] = useState(false);\n\n useEffect(() => {\n if (!ad || renderFired || !ad.render_impression_url) return;\n setRenderFired(true);\n\n const url = new URL(ad.render_impression_url);\n url.searchParams.set(\"ad_unit_position\", adUnitPosition);\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n }, [ad, renderFired, adUnitPosition]);\n\n useEffect(() => {\n if (!ad || !cardRef.current || viewed || !ad.view_impression_url) return;\n\n return observeViewability({\n element: cardRef.current,\n thresholdMs: viewabilityThresholdMs,\n onViewed: () => {\n setViewed(true);\n if (!ad.view_impression_url) return;\n const url = new URL(ad.view_impression_url);\n url.searchParams.set(\"ad_unit_position\", adUnitPosition);\n url.searchParams.set(\"viewability_ms\", String(viewabilityThresholdMs));\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n },\n });\n }, [ad, viewed, adUnitPosition, viewabilityThresholdMs]);\n\n if (!ad) return null;\n\n const initial = ad.advertiser_name?.charAt(0).toUpperCase() ?? \"?\";\n\n return (\n <div\n ref={cardRef}\n className={className}\n data-adzen-ad-unit-position={adUnitPosition}\n style={cardStyles.wrapper}\n >\n <div style={cardStyles.avatar}>\n {ad.advertiser_image_url ? (\n <img\n src={ad.advertiser_image_url}\n alt={ad.advertiser_name}\n style={cardStyles.avatarImg}\n />\n ) : (\n <span style={cardStyles.avatarLetter}>{initial}</span>\n )}\n </div>\n\n <div style={cardStyles.content}>\n <div style={cardStyles.topRow}>\n <span style={cardStyles.advertiserName}>{ad.advertiser_name}</span>\n <span style={cardStyles.relevantAd}>Relevant Ad</span>\n </div>\n <div style={cardStyles.bottomRow}>\n <span style={cardStyles.headline}>{ad.headline}</span>\n <a\n href={ad.destination_url}\n target=\"_blank\"\n rel=\"noopener sponsored\"\n style={cardStyles.cta}\n >\n {ad.cta_text}\n </a>\n </div>\n </div>\n </div>\n );\n}\n\nconst cardStyles: Record<string, React.CSSProperties> = {\n wrapper: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.625rem\",\n fontSize: \"0.85rem\",\n padding: \"0.625rem 0.75rem\",\n },\n avatar: {\n width: 32,\n height: 32,\n borderRadius: \"50%\",\n background: \"#d1d5db\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n overflow: \"hidden\",\n },\n avatarImg: {\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\" as const,\n borderRadius: \"50%\",\n },\n avatarLetter: {\n fontSize: \"0.8rem\",\n fontWeight: 700,\n color: \"#374151\",\n lineHeight: 1,\n },\n content: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"0.125rem\",\n minWidth: 0,\n },\n topRow: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n },\n advertiserName: {\n fontWeight: 700,\n color: \"inherit\",\n },\n relevantAd: {\n color: \"#8b949e\",\n fontSize: \"0.8rem\",\n },\n bottomRow: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n },\n headline: {\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n },\n cta: {\n color: \"#58a6ff\",\n textDecoration: \"none\",\n fontWeight: 500,\n flexShrink: 0,\n },\n};\n","import { useState, useEffect, useCallback } from \"react\";\nimport type { AdzenPlacement } from \"@adzenai/core\";\n\nexport interface UseAdzenPlacementReturn {\n getAdForMessage(messageId: string): AdzenPlacement | null;\n placements: Map<string, AdzenPlacement>;\n}\n\n/**\n * React hook that subscribes to `adzen_placement` custom events and\n * maintains a map of messageId -> AdzenPlacement.\n *\n * Works anywhere in the React tree below `<CopilotKit>` — no wrapper\n * provider required.\n */\nexport function useAdzenPlacement(): UseAdzenPlacementReturn {\n const [placements, setPlacements] = useState<Map<string, AdzenPlacement>>(\n () => new Map(),\n );\n\n useEffect(() => {\n function handler(e: Event) {\n const detail = (e as CustomEvent).detail;\n if (!detail || typeof detail !== \"object\") return;\n\n const placement = detail as AdzenPlacement & { message_id?: string };\n const messageId = placement.message_id;\n if (!messageId) return;\n\n setPlacements((prev) => {\n const existing = prev.get(messageId);\n if (existing && existing.ad_id === placement.ad_id) return prev;\n const next = new Map(prev);\n next.set(messageId, placement);\n return next;\n });\n }\n\n window.addEventListener(\"adzen_placement\", handler);\n return () => window.removeEventListener(\"adzen_placement\", handler);\n }, []);\n\n const getAdForMessage = useCallback(\n (messageId: string): AdzenPlacement | null => {\n return placements.get(messageId) ?? null;\n },\n [placements],\n );\n\n return { getAdForMessage, placements };\n}\n","import { useRef, useEffect, useState } from \"react\";\nimport { observeViewability } from \"@adzenai/core\";\nimport type { InlineAdEvent } from \"@adzenai/core\";\n\nexport interface InlineAdProps {\n ad: InlineAdEvent;\n viewabilityThresholdMs?: number;\n className?: string;\n}\n\nfunction fireBeacon(url: string): void {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {\n setTimeout(() => {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {});\n }, 1000);\n });\n}\n\n/**\n * Renders an inline ad as a single line of text — an \"Ad\" pill, the advertiser\n * name, and the CTA as the only link — rather than a card, so it sits in the\n * message body without competing with the assistant's own content.\n *\n * Tracks render/view impressions via the same beacon pattern as AdzenCard.\n */\nexport function InlineAd({\n ad,\n viewabilityThresholdMs = 1000,\n className,\n}: InlineAdProps) {\n const ref = useRef<HTMLDivElement>(null);\n const [renderFired, setRenderFired] = useState(false);\n const [viewed, setViewed] = useState(false);\n\n useEffect(() => {\n if (renderFired || !ad.tracking.impression_url) return;\n setRenderFired(true);\n\n const url = new URL(ad.tracking.impression_url);\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n }, [ad, renderFired]);\n\n useEffect(() => {\n if (!ref.current || viewed || !ad.tracking.impression_url) return;\n\n return observeViewability({\n element: ref.current,\n thresholdMs: viewabilityThresholdMs,\n onViewed: () => {\n setViewed(true);\n if (!ad.tracking.impression_url) return;\n const url = new URL(ad.tracking.impression_url);\n url.searchParams.set(\"type\", \"view\");\n url.searchParams.set(\"viewability_ms\", String(viewabilityThresholdMs));\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n },\n });\n }, [ad, viewed, viewabilityThresholdMs]);\n\n const advertiser = ad.creative.advertiser_name ?? ad.creative.headline;\n\n return (\n <div ref={ref} className={className} style={inlineStyles.wrapper}>\n <span style={inlineStyles.badge} aria-label=\"Advertisement\">\n Ad\n </span>\n {advertiser ? (\n <span style={inlineStyles.advertiser}>{advertiser} </span>\n ) : null}\n <a\n href={ad.creative.destination_url}\n target=\"_blank\"\n rel=\"noopener sponsored\"\n style={inlineStyles.cta}\n >\n {ad.creative.cta_text}\n </a>\n </div>\n );\n}\n\nconst inlineStyles: Record<string, React.CSSProperties> = {\n // Block-level so the snippet starts on its own line below the text it\n // follows, but otherwise plain: no border, background, or padding.\n wrapper: {\n display: \"block\",\n margin: \"0.5rem 0\",\n fontSize: \"0.9em\",\n lineHeight: 1.5,\n },\n badge: {\n display: \"inline-block\",\n marginRight: \"0.4em\",\n padding: \"0.1em 0.4em\",\n borderRadius: 4,\n background: \"#30363d\",\n color: \"#8b949e\",\n fontSize: \"0.75em\",\n fontWeight: 700,\n letterSpacing: \"0.05em\",\n textTransform: \"uppercase\" as const,\n verticalAlign: \"middle\",\n },\n advertiser: {\n color: \"#8b949e\",\n },\n cta: {\n color: \"#58a6ff\",\n textDecoration: \"none\",\n fontWeight: 500,\n },\n};\n","import { useState, useEffect, useCallback } from \"react\";\nimport type { InlineAdEvent, InlineAdEventPayload } from \"@adzenai/core\";\n\n/** A run of message text, or an ad anchored to the end of the run before it. */\nexport type InlineAdSegment =\n | { kind: \"text\"; text: string }\n | { kind: \"ad\"; ad: InlineAdEventPayload };\n\nexport interface UseAdzenInlineAdsReturn {\n getInlineAdsForMessage(messageId: string): InlineAdEventPayload[];\n getInlineAdSegments(messageId: string, content: string): InlineAdSegment[];\n}\n\n/**\n * Ads without an anchor sort last so they render at the end of the message,\n * which is the best guess for a payload that predates `content_offset`.\n */\nfunction readOffset(payload: InlineAdEvent & { content_offset?: unknown }): number {\n return typeof payload.content_offset === \"number\"\n ? payload.content_offset\n : Number.MAX_SAFE_INTEGER;\n}\n\n/**\n * Placement instance key: an inline ad is identified by the\n * `(ad_id, paragraph_index)` pair, not `ad_id` alone. The streaming backend\n * can serve the same creative more than once in a response (spaced repeats),\n * each anchored to a different paragraph, so keying by `ad_id` alone would drop\n * every showing after the first.\n */\nfunction sameInstance(a: InlineAdEventPayload, b: InlineAdEventPayload): boolean {\n return a.ad_id === b.ad_id && a.paragraph_index === b.paragraph_index;\n}\n\n/**\n * Merges `anchored` into `existing`, deduping by placement instance and keeping\n * the list sorted by `content_offset` for render order. Returns the same array\n * reference when the ad is a duplicate so callers can bail out of a state\n * update. `paragraph_index` is the identity key; `content_offset` is the\n * position anchor — do not conflate them.\n */\nexport function addInlineAd(\n existing: InlineAdEventPayload[],\n anchored: InlineAdEventPayload,\n): InlineAdEventPayload[] {\n if (existing.some((a) => sameInstance(a, anchored))) return existing;\n return [...existing, anchored].sort(\n (a, b) => a.content_offset - b.content_offset,\n );\n}\n\n/** A blank line, which ends a paragraph in both plain text and Markdown. */\nconst PARAGRAPH_BREAK = /\\n[ \\t]*\\n/;\nconst ENDS_ON_PARAGRAPH_BREAK = /\\n[ \\t]*\\n$/;\n\n/**\n * Moves an anchor forward to the end of the paragraph it falls in.\n *\n * Deltas are token-sized, so a raw offset routinely lands mid-word or inside\n * a Markdown construct (`**bold te|xt**`). Splitting there would break both\n * the sentence and the Markdown, so the ad is deferred to the next paragraph\n * break — the `paragraph_break` position the API asks for. While the paragraph\n * is still streaming there is no break yet, so the ad sits at the end of the\n * content and settles into place once the paragraph closes.\n */\nfunction snapToParagraphEnd(content: string, offset: number): number {\n if (offset >= content.length) return content.length;\n\n // The preceding frame already ended on a blank line, so this is the start of\n // a fresh paragraph and the ad belongs here.\n if (ENDS_ON_PARAGRAPH_BREAK.test(content.slice(0, offset))) return offset;\n\n // Search from one character back so an offset landing inside the blank line\n // itself still resolves to that break rather than skipping to the next one.\n return nextParagraphEnd(content, Math.max(0, offset - 1));\n}\n\n/**\n * Position just past the first paragraph break at or after `from`, or the end\n * of the content when no break follows.\n */\nfunction nextParagraphEnd(content: string, from: number): number {\n if (from >= content.length) return content.length;\n const match = PARAGRAPH_BREAK.exec(content.slice(from));\n return match ? from + match.index + match[0].length : content.length;\n}\n\n/**\n * Splits `content` into text and ad segments, placing each ad after the text\n * that had streamed when it arrived.\n *\n * At most one ad is placed per paragraph break. Several ads routinely resolve\n * to the same break — the API can emit a burst of them, and every ad anchored\n * inside one paragraph snaps to that paragraph's end — so each ad after the\n * first is moved to the next free break, with the end of the content usable as\n * a final slot. An ad with no free slot is left out and appears once the\n * message has grown enough to hold it, which keeps ads from piling up in one\n * spot while a paragraph is still streaming.\n *\n * @param ads Must be sorted by `content_offset` ascending.\n */\nexport function buildInlineAdSegments(\n content: string,\n ads: InlineAdEventPayload[],\n): InlineAdSegment[] {\n if (ads.length === 0) {\n return content ? [{ kind: \"text\", text: content }] : [];\n }\n\n const segments: InlineAdSegment[] = [];\n let cursor = 0;\n let lastAnchor = -1;\n\n for (const ad of ads) {\n let anchor = snapToParagraphEnd(content, Math.max(ad.content_offset, 0));\n if (anchor <= lastAnchor) anchor = nextParagraphEnd(content, lastAnchor);\n if (anchor <= lastAnchor) continue;\n\n if (anchor > cursor) {\n segments.push({ kind: \"text\", text: content.slice(cursor, anchor) });\n }\n segments.push({ kind: \"ad\", ad });\n cursor = anchor;\n lastAnchor = anchor;\n }\n\n if (cursor < content.length) {\n segments.push({ kind: \"text\", text: content.slice(cursor) });\n }\n\n return segments;\n}\n\n/**\n * React hook that subscribes to `adzen_inline_ad` custom events on\n * `window` and maintains a map of messageId -> InlineAdEventPayload[].\n *\n * Supports multiple inline ads per message per the cardinality requirements.\n * Ads are kept sorted by `content_offset` so they can be interleaved with the\n * message text at the point in the stream where they arrived.\n */\nexport function useAdzenInlineAds(): UseAdzenInlineAdsReturn {\n const [adsMap, setAdsMap] = useState<Map<string, InlineAdEventPayload[]>>(\n () => new Map(),\n );\n\n useEffect(() => {\n function handler(e: Event) {\n const detail = (e as CustomEvent).detail;\n if (!detail || typeof detail !== \"object\") return;\n\n const payload = detail as InlineAdEvent & { message_id?: string };\n const messageId = payload.message_id;\n if (!messageId) return;\n\n const anchored: InlineAdEventPayload = {\n ...payload,\n // Back-compat: older servers omit paragraph_index, so treat it as 0.\n // With those servers each ad_id appears once, so (ad_id, 0) degrades to\n // the previous ad_id-only dedup behavior.\n paragraph_index: payload.paragraph_index ?? 0,\n message_id: messageId,\n content_offset: readOffset(payload),\n };\n\n setAdsMap((prev) => {\n const existing = prev.get(messageId) ?? [];\n const merged = addInlineAd(existing, anchored);\n if (merged === existing) return prev;\n const next = new Map(prev);\n next.set(messageId, merged);\n return next;\n });\n }\n\n window.addEventListener(\"adzen_inline_ad\", handler);\n return () => window.removeEventListener(\"adzen_inline_ad\", handler);\n }, []);\n\n const getInlineAdsForMessage = useCallback(\n (messageId: string): InlineAdEventPayload[] => {\n return adsMap.get(messageId) ?? [];\n },\n [adsMap],\n );\n\n const getInlineAdSegments = useCallback(\n (messageId: string, content: string): InlineAdSegment[] =>\n buildInlineAdSegments(content, adsMap.get(messageId) ?? []),\n [adsMap],\n );\n\n return { getInlineAdsForMessage, getInlineAdSegments };\n}\n","import type { AdzenPlacement, InlineAdEventPayload } from \"@adzenai/core\";\n\nfunction isCustomEvent(event: Record<string, unknown>): boolean {\n const type = String(event.type ?? \"\");\n return type === \"CUSTOM_EVENT\" || type === \"CUSTOM\" || type === \"CustomEvent\";\n}\n\n/**\n * Checks whether an AG-UI event is an `adzen_placement` custom event\n * and, if so, dispatches it as a browser `CustomEvent` on `window`\n * so that `useAdzenPlacement` can pick it up.\n *\n * Call this for every event returned by middleware `processEvent()` or\n * `processStream()`. Non-placement events are silently ignored.\n */\nexport function dispatchPlacementEvent(\n event: Record<string, unknown>,\n): boolean {\n if (typeof window === \"undefined\") return false;\n if (event.name !== \"adzen_placement\") return false;\n if (!isCustomEvent(event)) return false;\n\n const value = event.value as (AdzenPlacement & { message_id?: string }) | undefined;\n if (!value) return false;\n\n window.dispatchEvent(\n new CustomEvent(\"adzen_placement\", { detail: value }),\n );\n return true;\n}\n\n/**\n * Checks whether an AG-UI event is an `adzen_inline_ad` custom event\n * and, if so, dispatches it as a browser `CustomEvent` on `window`\n * so that `useAdzenInlineAds` can pick it up.\n */\nexport function dispatchInlineAdEvent(\n event: Record<string, unknown>,\n): boolean {\n if (typeof window === \"undefined\") return false;\n if (event.name !== \"adzen_inline_ad\") return false;\n if (!isCustomEvent(event)) return false;\n\n const value = event.value as Partial<InlineAdEventPayload> | undefined;\n if (!value) return false;\n\n window.dispatchEvent(\n new CustomEvent(\"adzen_inline_ad\", { detail: value }),\n );\n return true;\n}\n\n/**\n * Convenience wrapper: iterates an array of AG-UI events and dispatches\n * any placement or inline ad events to the browser.\n */\nexport function dispatchPlacementEvents(\n events: Record<string, unknown>[],\n): void {\n for (const event of events) {\n dispatchPlacementEvent(event);\n dispatchInlineAdEvent(event);\n }\n}\n"],"mappings":"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","names":["import_react","import_react","import_core","import_jsx_runtime","fireBeacon","import_react"]}
@@ -290,6 +290,15 @@ import { useState as useState4, useEffect as useEffect4, useCallback as useCallb
290
290
  function readOffset(payload) {
291
291
  return typeof payload.content_offset === "number" ? payload.content_offset : Number.MAX_SAFE_INTEGER;
292
292
  }
293
+ function sameInstance(a, b) {
294
+ return a.ad_id === b.ad_id && a.paragraph_index === b.paragraph_index;
295
+ }
296
+ function addInlineAd(existing, anchored) {
297
+ if (existing.some((a) => sameInstance(a, anchored))) return existing;
298
+ return [...existing, anchored].sort(
299
+ (a, b) => a.content_offset - b.content_offset
300
+ );
301
+ }
293
302
  var PARAGRAPH_BREAK = /\n[ \t]*\n/;
294
303
  var ENDS_ON_PARAGRAPH_BREAK = /\n[ \t]*\n$/;
295
304
  function snapToParagraphEnd(content, offset) {
@@ -338,19 +347,19 @@ function useAdzenInlineAds() {
338
347
  if (!messageId) return;
339
348
  const anchored = {
340
349
  ...payload,
350
+ // Back-compat: older servers omit paragraph_index, so treat it as 0.
351
+ // With those servers each ad_id appears once, so (ad_id, 0) degrades to
352
+ // the previous ad_id-only dedup behavior.
353
+ paragraph_index: payload.paragraph_index ?? 0,
341
354
  message_id: messageId,
342
355
  content_offset: readOffset(payload)
343
356
  };
344
357
  setAdsMap((prev) => {
345
358
  const existing = prev.get(messageId) ?? [];
346
- if (existing.some((a) => a.ad_id === anchored.ad_id)) return prev;
359
+ const merged = addInlineAd(existing, anchored);
360
+ if (merged === existing) return prev;
347
361
  const next = new Map(prev);
348
- next.set(
349
- messageId,
350
- [...existing, anchored].sort(
351
- (a, b) => a.content_offset - b.content_offset
352
- )
353
- );
362
+ next.set(messageId, merged);
354
363
  return next;
355
364
  });
356
365
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/copilotkit/react/AdzenCard.tsx","../../../src/copilotkit/react/useAdzenPlacement.ts","../../../src/copilotkit/react/InlineAd.tsx","../../../src/copilotkit/react/useAdzenInlineAds.ts","../../../src/copilotkit/react/bridge.ts"],"sourcesContent":["import { useRef, useEffect, useState } from \"react\";\nimport { observeViewability } from \"@adzenai/core\";\nimport { useAdzenPlacement } from \"./useAdzenPlacement.js\";\n\nexport interface AdzenCardProps {\n messageId: string;\n adUnitPosition?: string;\n viewabilityThresholdMs?: number;\n className?: string;\n}\n\nfunction fireBeacon(url: string): void {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {\n setTimeout(() => {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {});\n }, 1000);\n });\n}\n\n/**\n * Renders a sponsored ad card for a given assistant message. Returns `null`\n * (no DOM output) when there is no ad for the message.\n */\nexport function AdzenCard({\n messageId,\n adUnitPosition = \"chin\",\n viewabilityThresholdMs = 1000,\n className,\n}: AdzenCardProps) {\n const { getAdForMessage } = useAdzenPlacement();\n const ad = getAdForMessage(messageId);\n const cardRef = useRef<HTMLDivElement>(null);\n const [renderFired, setRenderFired] = useState(false);\n const [viewed, setViewed] = useState(false);\n\n useEffect(() => {\n if (!ad || renderFired || !ad.render_impression_url) return;\n setRenderFired(true);\n\n const url = new URL(ad.render_impression_url);\n url.searchParams.set(\"ad_unit_position\", adUnitPosition);\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n }, [ad, renderFired, adUnitPosition]);\n\n useEffect(() => {\n if (!ad || !cardRef.current || viewed || !ad.view_impression_url) return;\n\n return observeViewability({\n element: cardRef.current,\n thresholdMs: viewabilityThresholdMs,\n onViewed: () => {\n setViewed(true);\n if (!ad.view_impression_url) return;\n const url = new URL(ad.view_impression_url);\n url.searchParams.set(\"ad_unit_position\", adUnitPosition);\n url.searchParams.set(\"viewability_ms\", String(viewabilityThresholdMs));\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n },\n });\n }, [ad, viewed, adUnitPosition, viewabilityThresholdMs]);\n\n if (!ad) return null;\n\n const initial = ad.advertiser_name?.charAt(0).toUpperCase() ?? \"?\";\n\n return (\n <div\n ref={cardRef}\n className={className}\n data-adzen-ad-unit-position={adUnitPosition}\n style={cardStyles.wrapper}\n >\n <div style={cardStyles.avatar}>\n {ad.advertiser_image_url ? (\n <img\n src={ad.advertiser_image_url}\n alt={ad.advertiser_name}\n style={cardStyles.avatarImg}\n />\n ) : (\n <span style={cardStyles.avatarLetter}>{initial}</span>\n )}\n </div>\n\n <div style={cardStyles.content}>\n <div style={cardStyles.topRow}>\n <span style={cardStyles.advertiserName}>{ad.advertiser_name}</span>\n <span style={cardStyles.relevantAd}>Relevant Ad</span>\n </div>\n <div style={cardStyles.bottomRow}>\n <span style={cardStyles.headline}>{ad.headline}</span>\n <a\n href={ad.destination_url}\n target=\"_blank\"\n rel=\"noopener sponsored\"\n style={cardStyles.cta}\n >\n {ad.cta_text}\n </a>\n </div>\n </div>\n </div>\n );\n}\n\nconst cardStyles: Record<string, React.CSSProperties> = {\n wrapper: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.625rem\",\n fontSize: \"0.85rem\",\n padding: \"0.625rem 0.75rem\",\n },\n avatar: {\n width: 32,\n height: 32,\n borderRadius: \"50%\",\n background: \"#d1d5db\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n overflow: \"hidden\",\n },\n avatarImg: {\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\" as const,\n borderRadius: \"50%\",\n },\n avatarLetter: {\n fontSize: \"0.8rem\",\n fontWeight: 700,\n color: \"#374151\",\n lineHeight: 1,\n },\n content: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"0.125rem\",\n minWidth: 0,\n },\n topRow: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n },\n advertiserName: {\n fontWeight: 700,\n color: \"inherit\",\n },\n relevantAd: {\n color: \"#8b949e\",\n fontSize: \"0.8rem\",\n },\n bottomRow: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n },\n headline: {\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n },\n cta: {\n color: \"#58a6ff\",\n textDecoration: \"none\",\n fontWeight: 500,\n flexShrink: 0,\n },\n};\n","import { useState, useEffect, useCallback } from \"react\";\nimport type { AdzenPlacement } from \"@adzenai/core\";\n\nexport interface UseAdzenPlacementReturn {\n getAdForMessage(messageId: string): AdzenPlacement | null;\n placements: Map<string, AdzenPlacement>;\n}\n\n/**\n * React hook that subscribes to `adzen_placement` custom events and\n * maintains a map of messageId -> AdzenPlacement.\n *\n * Works anywhere in the React tree below `<CopilotKit>` — no wrapper\n * provider required.\n */\nexport function useAdzenPlacement(): UseAdzenPlacementReturn {\n const [placements, setPlacements] = useState<Map<string, AdzenPlacement>>(\n () => new Map(),\n );\n\n useEffect(() => {\n function handler(e: Event) {\n const detail = (e as CustomEvent).detail;\n if (!detail || typeof detail !== \"object\") return;\n\n const placement = detail as AdzenPlacement & { message_id?: string };\n const messageId = placement.message_id;\n if (!messageId) return;\n\n setPlacements((prev) => {\n const existing = prev.get(messageId);\n if (existing && existing.ad_id === placement.ad_id) return prev;\n const next = new Map(prev);\n next.set(messageId, placement);\n return next;\n });\n }\n\n window.addEventListener(\"adzen_placement\", handler);\n return () => window.removeEventListener(\"adzen_placement\", handler);\n }, []);\n\n const getAdForMessage = useCallback(\n (messageId: string): AdzenPlacement | null => {\n return placements.get(messageId) ?? null;\n },\n [placements],\n );\n\n return { getAdForMessage, placements };\n}\n","import { useRef, useEffect, useState } from \"react\";\nimport { observeViewability } from \"@adzenai/core\";\nimport type { InlineAdEvent } from \"@adzenai/core\";\n\nexport interface InlineAdProps {\n ad: InlineAdEvent;\n viewabilityThresholdMs?: number;\n className?: string;\n}\n\nfunction fireBeacon(url: string): void {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {\n setTimeout(() => {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {});\n }, 1000);\n });\n}\n\n/**\n * Renders an inline ad as a single line of text — an \"Ad\" pill, the advertiser\n * name, and the CTA as the only link — rather than a card, so it sits in the\n * message body without competing with the assistant's own content.\n *\n * Tracks render/view impressions via the same beacon pattern as AdzenCard.\n */\nexport function InlineAd({\n ad,\n viewabilityThresholdMs = 1000,\n className,\n}: InlineAdProps) {\n const ref = useRef<HTMLDivElement>(null);\n const [renderFired, setRenderFired] = useState(false);\n const [viewed, setViewed] = useState(false);\n\n useEffect(() => {\n if (renderFired || !ad.tracking.impression_url) return;\n setRenderFired(true);\n\n const url = new URL(ad.tracking.impression_url);\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n }, [ad, renderFired]);\n\n useEffect(() => {\n if (!ref.current || viewed || !ad.tracking.impression_url) return;\n\n return observeViewability({\n element: ref.current,\n thresholdMs: viewabilityThresholdMs,\n onViewed: () => {\n setViewed(true);\n if (!ad.tracking.impression_url) return;\n const url = new URL(ad.tracking.impression_url);\n url.searchParams.set(\"type\", \"view\");\n url.searchParams.set(\"viewability_ms\", String(viewabilityThresholdMs));\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n },\n });\n }, [ad, viewed, viewabilityThresholdMs]);\n\n const advertiser = ad.creative.advertiser_name ?? ad.creative.headline;\n\n return (\n <div ref={ref} className={className} style={inlineStyles.wrapper}>\n <span style={inlineStyles.badge} aria-label=\"Advertisement\">\n Ad\n </span>\n {advertiser ? (\n <span style={inlineStyles.advertiser}>{advertiser} </span>\n ) : null}\n <a\n href={ad.creative.destination_url}\n target=\"_blank\"\n rel=\"noopener sponsored\"\n style={inlineStyles.cta}\n >\n {ad.creative.cta_text}\n </a>\n </div>\n );\n}\n\nconst inlineStyles: Record<string, React.CSSProperties> = {\n // Block-level so the snippet starts on its own line below the text it\n // follows, but otherwise plain: no border, background, or padding.\n wrapper: {\n display: \"block\",\n margin: \"0.5rem 0\",\n fontSize: \"0.9em\",\n lineHeight: 1.5,\n },\n badge: {\n display: \"inline-block\",\n marginRight: \"0.4em\",\n padding: \"0.1em 0.4em\",\n borderRadius: 4,\n background: \"#30363d\",\n color: \"#8b949e\",\n fontSize: \"0.75em\",\n fontWeight: 700,\n letterSpacing: \"0.05em\",\n textTransform: \"uppercase\" as const,\n verticalAlign: \"middle\",\n },\n advertiser: {\n color: \"#8b949e\",\n },\n cta: {\n color: \"#58a6ff\",\n textDecoration: \"none\",\n fontWeight: 500,\n },\n};\n","import { useState, useEffect, useCallback } from \"react\";\nimport type { InlineAdEvent, InlineAdEventPayload } from \"@adzenai/core\";\n\n/** A run of message text, or an ad anchored to the end of the run before it. */\nexport type InlineAdSegment =\n | { kind: \"text\"; text: string }\n | { kind: \"ad\"; ad: InlineAdEventPayload };\n\nexport interface UseAdzenInlineAdsReturn {\n getInlineAdsForMessage(messageId: string): InlineAdEventPayload[];\n getInlineAdSegments(messageId: string, content: string): InlineAdSegment[];\n}\n\n/**\n * Ads without an anchor sort last so they render at the end of the message,\n * which is the best guess for a payload that predates `content_offset`.\n */\nfunction readOffset(payload: InlineAdEvent & { content_offset?: unknown }): number {\n return typeof payload.content_offset === \"number\"\n ? payload.content_offset\n : Number.MAX_SAFE_INTEGER;\n}\n\n/** A blank line, which ends a paragraph in both plain text and Markdown. */\nconst PARAGRAPH_BREAK = /\\n[ \\t]*\\n/;\nconst ENDS_ON_PARAGRAPH_BREAK = /\\n[ \\t]*\\n$/;\n\n/**\n * Moves an anchor forward to the end of the paragraph it falls in.\n *\n * Deltas are token-sized, so a raw offset routinely lands mid-word or inside\n * a Markdown construct (`**bold te|xt**`). Splitting there would break both\n * the sentence and the Markdown, so the ad is deferred to the next paragraph\n * break — the `paragraph_break` position the API asks for. While the paragraph\n * is still streaming there is no break yet, so the ad sits at the end of the\n * content and settles into place once the paragraph closes.\n */\nfunction snapToParagraphEnd(content: string, offset: number): number {\n if (offset >= content.length) return content.length;\n\n // The preceding frame already ended on a blank line, so this is the start of\n // a fresh paragraph and the ad belongs here.\n if (ENDS_ON_PARAGRAPH_BREAK.test(content.slice(0, offset))) return offset;\n\n // Search from one character back so an offset landing inside the blank line\n // itself still resolves to that break rather than skipping to the next one.\n return nextParagraphEnd(content, Math.max(0, offset - 1));\n}\n\n/**\n * Position just past the first paragraph break at or after `from`, or the end\n * of the content when no break follows.\n */\nfunction nextParagraphEnd(content: string, from: number): number {\n if (from >= content.length) return content.length;\n const match = PARAGRAPH_BREAK.exec(content.slice(from));\n return match ? from + match.index + match[0].length : content.length;\n}\n\n/**\n * Splits `content` into text and ad segments, placing each ad after the text\n * that had streamed when it arrived.\n *\n * At most one ad is placed per paragraph break. Several ads routinely resolve\n * to the same break — the API can emit a burst of them, and every ad anchored\n * inside one paragraph snaps to that paragraph's end — so each ad after the\n * first is moved to the next free break, with the end of the content usable as\n * a final slot. An ad with no free slot is left out and appears once the\n * message has grown enough to hold it, which keeps ads from piling up in one\n * spot while a paragraph is still streaming.\n *\n * @param ads Must be sorted by `content_offset` ascending.\n */\nexport function buildInlineAdSegments(\n content: string,\n ads: InlineAdEventPayload[],\n): InlineAdSegment[] {\n if (ads.length === 0) {\n return content ? [{ kind: \"text\", text: content }] : [];\n }\n\n const segments: InlineAdSegment[] = [];\n let cursor = 0;\n let lastAnchor = -1;\n\n for (const ad of ads) {\n let anchor = snapToParagraphEnd(content, Math.max(ad.content_offset, 0));\n if (anchor <= lastAnchor) anchor = nextParagraphEnd(content, lastAnchor);\n if (anchor <= lastAnchor) continue;\n\n if (anchor > cursor) {\n segments.push({ kind: \"text\", text: content.slice(cursor, anchor) });\n }\n segments.push({ kind: \"ad\", ad });\n cursor = anchor;\n lastAnchor = anchor;\n }\n\n if (cursor < content.length) {\n segments.push({ kind: \"text\", text: content.slice(cursor) });\n }\n\n return segments;\n}\n\n/**\n * React hook that subscribes to `adzen_inline_ad` custom events on\n * `window` and maintains a map of messageId -> InlineAdEventPayload[].\n *\n * Supports multiple inline ads per message per the cardinality requirements.\n * Ads are kept sorted by `content_offset` so they can be interleaved with the\n * message text at the point in the stream where they arrived.\n */\nexport function useAdzenInlineAds(): UseAdzenInlineAdsReturn {\n const [adsMap, setAdsMap] = useState<Map<string, InlineAdEventPayload[]>>(\n () => new Map(),\n );\n\n useEffect(() => {\n function handler(e: Event) {\n const detail = (e as CustomEvent).detail;\n if (!detail || typeof detail !== \"object\") return;\n\n const payload = detail as InlineAdEvent & { message_id?: string };\n const messageId = payload.message_id;\n if (!messageId) return;\n\n const anchored: InlineAdEventPayload = {\n ...payload,\n message_id: messageId,\n content_offset: readOffset(payload),\n };\n\n setAdsMap((prev) => {\n const existing = prev.get(messageId) ?? [];\n if (existing.some((a) => a.ad_id === anchored.ad_id)) return prev;\n const next = new Map(prev);\n next.set(\n messageId,\n [...existing, anchored].sort(\n (a, b) => a.content_offset - b.content_offset,\n ),\n );\n return next;\n });\n }\n\n window.addEventListener(\"adzen_inline_ad\", handler);\n return () => window.removeEventListener(\"adzen_inline_ad\", handler);\n }, []);\n\n const getInlineAdsForMessage = useCallback(\n (messageId: string): InlineAdEventPayload[] => {\n return adsMap.get(messageId) ?? [];\n },\n [adsMap],\n );\n\n const getInlineAdSegments = useCallback(\n (messageId: string, content: string): InlineAdSegment[] =>\n buildInlineAdSegments(content, adsMap.get(messageId) ?? []),\n [adsMap],\n );\n\n return { getInlineAdsForMessage, getInlineAdSegments };\n}\n","import type { AdzenPlacement, InlineAdEventPayload } from \"@adzenai/core\";\n\nfunction isCustomEvent(event: Record<string, unknown>): boolean {\n const type = String(event.type ?? \"\");\n return type === \"CUSTOM_EVENT\" || type === \"CUSTOM\" || type === \"CustomEvent\";\n}\n\n/**\n * Checks whether an AG-UI event is an `adzen_placement` custom event\n * and, if so, dispatches it as a browser `CustomEvent` on `window`\n * so that `useAdzenPlacement` can pick it up.\n *\n * Call this for every event returned by middleware `processEvent()` or\n * `processStream()`. Non-placement events are silently ignored.\n */\nexport function dispatchPlacementEvent(\n event: Record<string, unknown>,\n): boolean {\n if (typeof window === \"undefined\") return false;\n if (event.name !== \"adzen_placement\") return false;\n if (!isCustomEvent(event)) return false;\n\n const value = event.value as (AdzenPlacement & { message_id?: string }) | undefined;\n if (!value) return false;\n\n window.dispatchEvent(\n new CustomEvent(\"adzen_placement\", { detail: value }),\n );\n return true;\n}\n\n/**\n * Checks whether an AG-UI event is an `adzen_inline_ad` custom event\n * and, if so, dispatches it as a browser `CustomEvent` on `window`\n * so that `useAdzenInlineAds` can pick it up.\n */\nexport function dispatchInlineAdEvent(\n event: Record<string, unknown>,\n): boolean {\n if (typeof window === \"undefined\") return false;\n if (event.name !== \"adzen_inline_ad\") return false;\n if (!isCustomEvent(event)) return false;\n\n const value = event.value as Partial<InlineAdEventPayload> | undefined;\n if (!value) return false;\n\n window.dispatchEvent(\n new CustomEvent(\"adzen_inline_ad\", { detail: value }),\n );\n return true;\n}\n\n/**\n * Convenience wrapper: iterates an array of AG-UI events and dispatches\n * any placement or inline ad events to the browser.\n */\nexport function dispatchPlacementEvents(\n events: Record<string, unknown>[],\n): void {\n for (const event of events) {\n dispatchPlacementEvent(event);\n dispatchInlineAdEvent(event);\n }\n}\n"],"mappings":"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","names":["useEffect","useState","useState","useEffect","useRef","useEffect","useState","observeViewability","jsx","jsxs","fireBeacon","useState","useEffect","useCallback"]}
1
+ {"version":3,"sources":["../../../src/copilotkit/react/AdzenCard.tsx","../../../src/copilotkit/react/useAdzenPlacement.ts","../../../src/copilotkit/react/InlineAd.tsx","../../../src/copilotkit/react/useAdzenInlineAds.ts","../../../src/copilotkit/react/bridge.ts"],"sourcesContent":["import { useRef, useEffect, useState } from \"react\";\nimport { observeViewability } from \"@adzenai/core\";\nimport { useAdzenPlacement } from \"./useAdzenPlacement.js\";\n\nexport interface AdzenCardProps {\n messageId: string;\n adUnitPosition?: string;\n viewabilityThresholdMs?: number;\n className?: string;\n}\n\nfunction fireBeacon(url: string): void {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {\n setTimeout(() => {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {});\n }, 1000);\n });\n}\n\n/**\n * Renders a sponsored ad card for a given assistant message. Returns `null`\n * (no DOM output) when there is no ad for the message.\n */\nexport function AdzenCard({\n messageId,\n adUnitPosition = \"chin\",\n viewabilityThresholdMs = 1000,\n className,\n}: AdzenCardProps) {\n const { getAdForMessage } = useAdzenPlacement();\n const ad = getAdForMessage(messageId);\n const cardRef = useRef<HTMLDivElement>(null);\n const [renderFired, setRenderFired] = useState(false);\n const [viewed, setViewed] = useState(false);\n\n useEffect(() => {\n if (!ad || renderFired || !ad.render_impression_url) return;\n setRenderFired(true);\n\n const url = new URL(ad.render_impression_url);\n url.searchParams.set(\"ad_unit_position\", adUnitPosition);\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n }, [ad, renderFired, adUnitPosition]);\n\n useEffect(() => {\n if (!ad || !cardRef.current || viewed || !ad.view_impression_url) return;\n\n return observeViewability({\n element: cardRef.current,\n thresholdMs: viewabilityThresholdMs,\n onViewed: () => {\n setViewed(true);\n if (!ad.view_impression_url) return;\n const url = new URL(ad.view_impression_url);\n url.searchParams.set(\"ad_unit_position\", adUnitPosition);\n url.searchParams.set(\"viewability_ms\", String(viewabilityThresholdMs));\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n },\n });\n }, [ad, viewed, adUnitPosition, viewabilityThresholdMs]);\n\n if (!ad) return null;\n\n const initial = ad.advertiser_name?.charAt(0).toUpperCase() ?? \"?\";\n\n return (\n <div\n ref={cardRef}\n className={className}\n data-adzen-ad-unit-position={adUnitPosition}\n style={cardStyles.wrapper}\n >\n <div style={cardStyles.avatar}>\n {ad.advertiser_image_url ? (\n <img\n src={ad.advertiser_image_url}\n alt={ad.advertiser_name}\n style={cardStyles.avatarImg}\n />\n ) : (\n <span style={cardStyles.avatarLetter}>{initial}</span>\n )}\n </div>\n\n <div style={cardStyles.content}>\n <div style={cardStyles.topRow}>\n <span style={cardStyles.advertiserName}>{ad.advertiser_name}</span>\n <span style={cardStyles.relevantAd}>Relevant Ad</span>\n </div>\n <div style={cardStyles.bottomRow}>\n <span style={cardStyles.headline}>{ad.headline}</span>\n <a\n href={ad.destination_url}\n target=\"_blank\"\n rel=\"noopener sponsored\"\n style={cardStyles.cta}\n >\n {ad.cta_text}\n </a>\n </div>\n </div>\n </div>\n );\n}\n\nconst cardStyles: Record<string, React.CSSProperties> = {\n wrapper: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.625rem\",\n fontSize: \"0.85rem\",\n padding: \"0.625rem 0.75rem\",\n },\n avatar: {\n width: 32,\n height: 32,\n borderRadius: \"50%\",\n background: \"#d1d5db\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n overflow: \"hidden\",\n },\n avatarImg: {\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\" as const,\n borderRadius: \"50%\",\n },\n avatarLetter: {\n fontSize: \"0.8rem\",\n fontWeight: 700,\n color: \"#374151\",\n lineHeight: 1,\n },\n content: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"0.125rem\",\n minWidth: 0,\n },\n topRow: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n },\n advertiserName: {\n fontWeight: 700,\n color: \"inherit\",\n },\n relevantAd: {\n color: \"#8b949e\",\n fontSize: \"0.8rem\",\n },\n bottomRow: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n },\n headline: {\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n },\n cta: {\n color: \"#58a6ff\",\n textDecoration: \"none\",\n fontWeight: 500,\n flexShrink: 0,\n },\n};\n","import { useState, useEffect, useCallback } from \"react\";\nimport type { AdzenPlacement } from \"@adzenai/core\";\n\nexport interface UseAdzenPlacementReturn {\n getAdForMessage(messageId: string): AdzenPlacement | null;\n placements: Map<string, AdzenPlacement>;\n}\n\n/**\n * React hook that subscribes to `adzen_placement` custom events and\n * maintains a map of messageId -> AdzenPlacement.\n *\n * Works anywhere in the React tree below `<CopilotKit>` — no wrapper\n * provider required.\n */\nexport function useAdzenPlacement(): UseAdzenPlacementReturn {\n const [placements, setPlacements] = useState<Map<string, AdzenPlacement>>(\n () => new Map(),\n );\n\n useEffect(() => {\n function handler(e: Event) {\n const detail = (e as CustomEvent).detail;\n if (!detail || typeof detail !== \"object\") return;\n\n const placement = detail as AdzenPlacement & { message_id?: string };\n const messageId = placement.message_id;\n if (!messageId) return;\n\n setPlacements((prev) => {\n const existing = prev.get(messageId);\n if (existing && existing.ad_id === placement.ad_id) return prev;\n const next = new Map(prev);\n next.set(messageId, placement);\n return next;\n });\n }\n\n window.addEventListener(\"adzen_placement\", handler);\n return () => window.removeEventListener(\"adzen_placement\", handler);\n }, []);\n\n const getAdForMessage = useCallback(\n (messageId: string): AdzenPlacement | null => {\n return placements.get(messageId) ?? null;\n },\n [placements],\n );\n\n return { getAdForMessage, placements };\n}\n","import { useRef, useEffect, useState } from \"react\";\nimport { observeViewability } from \"@adzenai/core\";\nimport type { InlineAdEvent } from \"@adzenai/core\";\n\nexport interface InlineAdProps {\n ad: InlineAdEvent;\n viewabilityThresholdMs?: number;\n className?: string;\n}\n\nfunction fireBeacon(url: string): void {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {\n setTimeout(() => {\n fetch(url, { method: \"GET\", keepalive: true }).catch(() => {});\n }, 1000);\n });\n}\n\n/**\n * Renders an inline ad as a single line of text — an \"Ad\" pill, the advertiser\n * name, and the CTA as the only link — rather than a card, so it sits in the\n * message body without competing with the assistant's own content.\n *\n * Tracks render/view impressions via the same beacon pattern as AdzenCard.\n */\nexport function InlineAd({\n ad,\n viewabilityThresholdMs = 1000,\n className,\n}: InlineAdProps) {\n const ref = useRef<HTMLDivElement>(null);\n const [renderFired, setRenderFired] = useState(false);\n const [viewed, setViewed] = useState(false);\n\n useEffect(() => {\n if (renderFired || !ad.tracking.impression_url) return;\n setRenderFired(true);\n\n const url = new URL(ad.tracking.impression_url);\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n }, [ad, renderFired]);\n\n useEffect(() => {\n if (!ref.current || viewed || !ad.tracking.impression_url) return;\n\n return observeViewability({\n element: ref.current,\n thresholdMs: viewabilityThresholdMs,\n onViewed: () => {\n setViewed(true);\n if (!ad.tracking.impression_url) return;\n const url = new URL(ad.tracking.impression_url);\n url.searchParams.set(\"type\", \"view\");\n url.searchParams.set(\"viewability_ms\", String(viewabilityThresholdMs));\n url.searchParams.set(\"timestamp\", new Date().toISOString());\n fireBeacon(url.toString());\n },\n });\n }, [ad, viewed, viewabilityThresholdMs]);\n\n const advertiser = ad.creative.advertiser_name ?? ad.creative.headline;\n\n return (\n <div ref={ref} className={className} style={inlineStyles.wrapper}>\n <span style={inlineStyles.badge} aria-label=\"Advertisement\">\n Ad\n </span>\n {advertiser ? (\n <span style={inlineStyles.advertiser}>{advertiser} </span>\n ) : null}\n <a\n href={ad.creative.destination_url}\n target=\"_blank\"\n rel=\"noopener sponsored\"\n style={inlineStyles.cta}\n >\n {ad.creative.cta_text}\n </a>\n </div>\n );\n}\n\nconst inlineStyles: Record<string, React.CSSProperties> = {\n // Block-level so the snippet starts on its own line below the text it\n // follows, but otherwise plain: no border, background, or padding.\n wrapper: {\n display: \"block\",\n margin: \"0.5rem 0\",\n fontSize: \"0.9em\",\n lineHeight: 1.5,\n },\n badge: {\n display: \"inline-block\",\n marginRight: \"0.4em\",\n padding: \"0.1em 0.4em\",\n borderRadius: 4,\n background: \"#30363d\",\n color: \"#8b949e\",\n fontSize: \"0.75em\",\n fontWeight: 700,\n letterSpacing: \"0.05em\",\n textTransform: \"uppercase\" as const,\n verticalAlign: \"middle\",\n },\n advertiser: {\n color: \"#8b949e\",\n },\n cta: {\n color: \"#58a6ff\",\n textDecoration: \"none\",\n fontWeight: 500,\n },\n};\n","import { useState, useEffect, useCallback } from \"react\";\nimport type { InlineAdEvent, InlineAdEventPayload } from \"@adzenai/core\";\n\n/** A run of message text, or an ad anchored to the end of the run before it. */\nexport type InlineAdSegment =\n | { kind: \"text\"; text: string }\n | { kind: \"ad\"; ad: InlineAdEventPayload };\n\nexport interface UseAdzenInlineAdsReturn {\n getInlineAdsForMessage(messageId: string): InlineAdEventPayload[];\n getInlineAdSegments(messageId: string, content: string): InlineAdSegment[];\n}\n\n/**\n * Ads without an anchor sort last so they render at the end of the message,\n * which is the best guess for a payload that predates `content_offset`.\n */\nfunction readOffset(payload: InlineAdEvent & { content_offset?: unknown }): number {\n return typeof payload.content_offset === \"number\"\n ? payload.content_offset\n : Number.MAX_SAFE_INTEGER;\n}\n\n/**\n * Placement instance key: an inline ad is identified by the\n * `(ad_id, paragraph_index)` pair, not `ad_id` alone. The streaming backend\n * can serve the same creative more than once in a response (spaced repeats),\n * each anchored to a different paragraph, so keying by `ad_id` alone would drop\n * every showing after the first.\n */\nfunction sameInstance(a: InlineAdEventPayload, b: InlineAdEventPayload): boolean {\n return a.ad_id === b.ad_id && a.paragraph_index === b.paragraph_index;\n}\n\n/**\n * Merges `anchored` into `existing`, deduping by placement instance and keeping\n * the list sorted by `content_offset` for render order. Returns the same array\n * reference when the ad is a duplicate so callers can bail out of a state\n * update. `paragraph_index` is the identity key; `content_offset` is the\n * position anchor — do not conflate them.\n */\nexport function addInlineAd(\n existing: InlineAdEventPayload[],\n anchored: InlineAdEventPayload,\n): InlineAdEventPayload[] {\n if (existing.some((a) => sameInstance(a, anchored))) return existing;\n return [...existing, anchored].sort(\n (a, b) => a.content_offset - b.content_offset,\n );\n}\n\n/** A blank line, which ends a paragraph in both plain text and Markdown. */\nconst PARAGRAPH_BREAK = /\\n[ \\t]*\\n/;\nconst ENDS_ON_PARAGRAPH_BREAK = /\\n[ \\t]*\\n$/;\n\n/**\n * Moves an anchor forward to the end of the paragraph it falls in.\n *\n * Deltas are token-sized, so a raw offset routinely lands mid-word or inside\n * a Markdown construct (`**bold te|xt**`). Splitting there would break both\n * the sentence and the Markdown, so the ad is deferred to the next paragraph\n * break — the `paragraph_break` position the API asks for. While the paragraph\n * is still streaming there is no break yet, so the ad sits at the end of the\n * content and settles into place once the paragraph closes.\n */\nfunction snapToParagraphEnd(content: string, offset: number): number {\n if (offset >= content.length) return content.length;\n\n // The preceding frame already ended on a blank line, so this is the start of\n // a fresh paragraph and the ad belongs here.\n if (ENDS_ON_PARAGRAPH_BREAK.test(content.slice(0, offset))) return offset;\n\n // Search from one character back so an offset landing inside the blank line\n // itself still resolves to that break rather than skipping to the next one.\n return nextParagraphEnd(content, Math.max(0, offset - 1));\n}\n\n/**\n * Position just past the first paragraph break at or after `from`, or the end\n * of the content when no break follows.\n */\nfunction nextParagraphEnd(content: string, from: number): number {\n if (from >= content.length) return content.length;\n const match = PARAGRAPH_BREAK.exec(content.slice(from));\n return match ? from + match.index + match[0].length : content.length;\n}\n\n/**\n * Splits `content` into text and ad segments, placing each ad after the text\n * that had streamed when it arrived.\n *\n * At most one ad is placed per paragraph break. Several ads routinely resolve\n * to the same break — the API can emit a burst of them, and every ad anchored\n * inside one paragraph snaps to that paragraph's end — so each ad after the\n * first is moved to the next free break, with the end of the content usable as\n * a final slot. An ad with no free slot is left out and appears once the\n * message has grown enough to hold it, which keeps ads from piling up in one\n * spot while a paragraph is still streaming.\n *\n * @param ads Must be sorted by `content_offset` ascending.\n */\nexport function buildInlineAdSegments(\n content: string,\n ads: InlineAdEventPayload[],\n): InlineAdSegment[] {\n if (ads.length === 0) {\n return content ? [{ kind: \"text\", text: content }] : [];\n }\n\n const segments: InlineAdSegment[] = [];\n let cursor = 0;\n let lastAnchor = -1;\n\n for (const ad of ads) {\n let anchor = snapToParagraphEnd(content, Math.max(ad.content_offset, 0));\n if (anchor <= lastAnchor) anchor = nextParagraphEnd(content, lastAnchor);\n if (anchor <= lastAnchor) continue;\n\n if (anchor > cursor) {\n segments.push({ kind: \"text\", text: content.slice(cursor, anchor) });\n }\n segments.push({ kind: \"ad\", ad });\n cursor = anchor;\n lastAnchor = anchor;\n }\n\n if (cursor < content.length) {\n segments.push({ kind: \"text\", text: content.slice(cursor) });\n }\n\n return segments;\n}\n\n/**\n * React hook that subscribes to `adzen_inline_ad` custom events on\n * `window` and maintains a map of messageId -> InlineAdEventPayload[].\n *\n * Supports multiple inline ads per message per the cardinality requirements.\n * Ads are kept sorted by `content_offset` so they can be interleaved with the\n * message text at the point in the stream where they arrived.\n */\nexport function useAdzenInlineAds(): UseAdzenInlineAdsReturn {\n const [adsMap, setAdsMap] = useState<Map<string, InlineAdEventPayload[]>>(\n () => new Map(),\n );\n\n useEffect(() => {\n function handler(e: Event) {\n const detail = (e as CustomEvent).detail;\n if (!detail || typeof detail !== \"object\") return;\n\n const payload = detail as InlineAdEvent & { message_id?: string };\n const messageId = payload.message_id;\n if (!messageId) return;\n\n const anchored: InlineAdEventPayload = {\n ...payload,\n // Back-compat: older servers omit paragraph_index, so treat it as 0.\n // With those servers each ad_id appears once, so (ad_id, 0) degrades to\n // the previous ad_id-only dedup behavior.\n paragraph_index: payload.paragraph_index ?? 0,\n message_id: messageId,\n content_offset: readOffset(payload),\n };\n\n setAdsMap((prev) => {\n const existing = prev.get(messageId) ?? [];\n const merged = addInlineAd(existing, anchored);\n if (merged === existing) return prev;\n const next = new Map(prev);\n next.set(messageId, merged);\n return next;\n });\n }\n\n window.addEventListener(\"adzen_inline_ad\", handler);\n return () => window.removeEventListener(\"adzen_inline_ad\", handler);\n }, []);\n\n const getInlineAdsForMessage = useCallback(\n (messageId: string): InlineAdEventPayload[] => {\n return adsMap.get(messageId) ?? [];\n },\n [adsMap],\n );\n\n const getInlineAdSegments = useCallback(\n (messageId: string, content: string): InlineAdSegment[] =>\n buildInlineAdSegments(content, adsMap.get(messageId) ?? []),\n [adsMap],\n );\n\n return { getInlineAdsForMessage, getInlineAdSegments };\n}\n","import type { AdzenPlacement, InlineAdEventPayload } from \"@adzenai/core\";\n\nfunction isCustomEvent(event: Record<string, unknown>): boolean {\n const type = String(event.type ?? \"\");\n return type === \"CUSTOM_EVENT\" || type === \"CUSTOM\" || type === \"CustomEvent\";\n}\n\n/**\n * Checks whether an AG-UI event is an `adzen_placement` custom event\n * and, if so, dispatches it as a browser `CustomEvent` on `window`\n * so that `useAdzenPlacement` can pick it up.\n *\n * Call this for every event returned by middleware `processEvent()` or\n * `processStream()`. Non-placement events are silently ignored.\n */\nexport function dispatchPlacementEvent(\n event: Record<string, unknown>,\n): boolean {\n if (typeof window === \"undefined\") return false;\n if (event.name !== \"adzen_placement\") return false;\n if (!isCustomEvent(event)) return false;\n\n const value = event.value as (AdzenPlacement & { message_id?: string }) | undefined;\n if (!value) return false;\n\n window.dispatchEvent(\n new CustomEvent(\"adzen_placement\", { detail: value }),\n );\n return true;\n}\n\n/**\n * Checks whether an AG-UI event is an `adzen_inline_ad` custom event\n * and, if so, dispatches it as a browser `CustomEvent` on `window`\n * so that `useAdzenInlineAds` can pick it up.\n */\nexport function dispatchInlineAdEvent(\n event: Record<string, unknown>,\n): boolean {\n if (typeof window === \"undefined\") return false;\n if (event.name !== \"adzen_inline_ad\") return false;\n if (!isCustomEvent(event)) return false;\n\n const value = event.value as Partial<InlineAdEventPayload> | undefined;\n if (!value) return false;\n\n window.dispatchEvent(\n new CustomEvent(\"adzen_inline_ad\", { detail: value }),\n );\n return true;\n}\n\n/**\n * Convenience wrapper: iterates an array of AG-UI events and dispatches\n * any placement or inline ad events to the browser.\n */\nexport function dispatchPlacementEvents(\n events: Record<string, unknown>[],\n): void {\n for (const event of events) {\n dispatchPlacementEvent(event);\n dispatchInlineAdEvent(event);\n }\n}\n"],"mappings":"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","names":["useEffect","useState","useState","useEffect","useRef","useEffect","useState","observeViewability","jsx","jsxs","fireBeacon","useState","useEffect","useCallback"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adzenai/ai",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "AI-powered ad integration for CopilotKit and AG-UI agents",
5
5
  "license": "MIT",
6
6
  "type": "module",