@invergent/website-widget 2.7.8 → 2.7.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-MAUJDM7P.js → chunk-55XSYQGO.js} +146 -118
- package/dist/chunk-55XSYQGO.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/surogates-widget.global.js +12 -9
- package/dist/surogates-widget.global.js.map +1 -1
- package/dist/ui.cjs +54 -1
- package/dist/ui.cjs.map +1 -1
- package/dist/ui.d.cts +17 -1
- package/dist/ui.d.ts +17 -1
- package/dist/ui.js +25 -3
- package/dist/ui.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-MAUJDM7P.js.map +0 -1
package/dist/ui.d.ts
CHANGED
|
@@ -41,6 +41,12 @@ interface MountConfig extends WebsiteAgentConfig, WidgetAppearance {
|
|
|
41
41
|
inline?: boolean;
|
|
42
42
|
/** Open the panel immediately on mount (ignored when ``inline``). */
|
|
43
43
|
openByDefault?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Control-plane base URL for key-only pairing (``mountWithPairing``).
|
|
46
|
+
* Defaults to the platform pairing host; override for self-hosted /
|
|
47
|
+
* local-dev deployments.
|
|
48
|
+
*/
|
|
49
|
+
pairingUrl?: string;
|
|
44
50
|
}
|
|
45
51
|
/** Imperative handle returned by :func:`mount`. */
|
|
46
52
|
interface WidgetHandle {
|
|
@@ -62,6 +68,16 @@ interface WidgetHandle {
|
|
|
62
68
|
readonly agent: WebsiteAgent;
|
|
63
69
|
}
|
|
64
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Key-only entry point. Resolves the publishable key to its agent +
|
|
73
|
+
* ``api_web_url`` via the control-plane pairing endpoint, then mounts against
|
|
74
|
+
* that base URL — so an embed can be just ``mountWithPairing({ publishableKey })``
|
|
75
|
+
* with no hardcoded ``apiUrl``. If ``apiUrl`` is already supplied, pairing is
|
|
76
|
+
* skipped and this is equivalent to :func:`mount`.
|
|
77
|
+
*/
|
|
78
|
+
declare function mountWithPairing(config: Omit<MountConfig, 'apiUrl'> & {
|
|
79
|
+
apiUrl?: string;
|
|
80
|
+
}): Promise<WidgetHandle>;
|
|
65
81
|
declare function mount(config: MountConfig): WidgetHandle;
|
|
66
82
|
|
|
67
|
-
export { type MountConfig, type WidgetAppearance, type WidgetHandle, mount };
|
|
83
|
+
export { type MountConfig, type WidgetAppearance, type WidgetHandle, mount, mountWithPairing };
|
package/dist/ui.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WebsiteAgent, SurogatesAuthError, SurogatesRateLimitError } from './chunk-
|
|
1
|
+
import { resolvePairing, DEFAULT_PAIRING_BASE, WebsiteAgent, SurogatesAuthError, SurogatesRateLimitError } from './chunk-55XSYQGO.js';
|
|
2
2
|
import { render } from 'preact';
|
|
3
3
|
import { useState, useRef, useEffect } from 'preact/hooks';
|
|
4
4
|
import { jsx, jsxs } from 'preact/jsx-runtime';
|
|
@@ -97,6 +97,7 @@ var WIDGET_STYLES = `
|
|
|
97
97
|
border-radius: 6px;
|
|
98
98
|
}
|
|
99
99
|
.surg-close:hover { opacity: 1; background: rgba(255, 255, 255, 0.18); }
|
|
100
|
+
.surg-close svg { width: 18px; height: 18px; }
|
|
100
101
|
|
|
101
102
|
.surg-messages {
|
|
102
103
|
flex: 1;
|
|
@@ -214,6 +215,8 @@ var WIDGET_STYLES = `
|
|
|
214
215
|
padding: 0 0 8px;
|
|
215
216
|
background: #ffffff;
|
|
216
217
|
}
|
|
218
|
+
.surg-powered a { color: inherit; font-weight: 500; text-decoration: none; }
|
|
219
|
+
.surg-powered a:hover { text-decoration: underline; }
|
|
217
220
|
`;
|
|
218
221
|
|
|
219
222
|
// src/ui/markdown.ts
|
|
@@ -451,7 +454,11 @@ function Widget({ agent, config, registerOpenControl, onOpenChange }) {
|
|
|
451
454
|
),
|
|
452
455
|
/* @__PURE__ */ jsx("button", { type: "button", class: "surg-send", "aria-label": "Send", disabled: running || !input.trim(), onClick: send, children: ICON_SEND })
|
|
453
456
|
] }),
|
|
454
|
-
/* @__PURE__ */
|
|
457
|
+
/* @__PURE__ */ jsxs("div", { class: "surg-powered", children: [
|
|
458
|
+
"Powered by",
|
|
459
|
+
" ",
|
|
460
|
+
/* @__PURE__ */ jsx("a", { href: "https://surogate.ai/", target: "_blank", rel: "noopener noreferrer", children: "Surogate" })
|
|
461
|
+
] })
|
|
455
462
|
] });
|
|
456
463
|
}
|
|
457
464
|
var DEFAULT_ACCENT = "#4f46e5";
|
|
@@ -465,6 +472,21 @@ function accentForeground(hex) {
|
|
|
465
472
|
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
466
473
|
return luminance > 0.62 ? "#0f172a" : "#ffffff";
|
|
467
474
|
}
|
|
475
|
+
async function mountWithPairing(config) {
|
|
476
|
+
if (config?.apiUrl) {
|
|
477
|
+
return mount(config);
|
|
478
|
+
}
|
|
479
|
+
if (!config?.publishableKey) {
|
|
480
|
+
throw new Error(
|
|
481
|
+
'SurogatesWidget.mountWithPairing: "publishableKey" is required.'
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
const { agentId, apiWebUrl } = await resolvePairing(
|
|
485
|
+
config.pairingUrl ?? DEFAULT_PAIRING_BASE,
|
|
486
|
+
config.publishableKey
|
|
487
|
+
);
|
|
488
|
+
return mount({ ...config, apiUrl: apiWebUrl, agentId });
|
|
489
|
+
}
|
|
468
490
|
function mount(config) {
|
|
469
491
|
if (!config || !config.apiUrl || !config.publishableKey) {
|
|
470
492
|
throw new Error(
|
|
@@ -551,6 +573,6 @@ function mount(config) {
|
|
|
551
573
|
};
|
|
552
574
|
}
|
|
553
575
|
|
|
554
|
-
export { mount };
|
|
576
|
+
export { mount, mountWithPairing };
|
|
555
577
|
//# sourceMappingURL=ui.js.map
|
|
556
578
|
//# sourceMappingURL=ui.js.map
|
package/dist/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ui/styles.ts","../src/ui/markdown.ts","../src/ui/next-action.ts","../src/ui/widget.tsx","../src/ui/mount.tsx"],"names":["jsx"],"mappings":"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","file":"ui.js","sourcesContent":["/**\n * Scoped CSS for the chat widget, injected into the Shadow DOM root so\n * it can neither leak into nor be overridden by the host page. All\n * colours derive from two custom properties (``--surg-accent`` and its\n * readable foreground) set at mount time from the caller's\n * ``accentColor`` so theming is a one-line change.\n *\n * Returned as a string (rather than a constructable stylesheet) because\n * a ``<style>`` element in the shadow root is the most broadly supported\n * path across the browsers a public embed has to serve.\n */\nexport const WIDGET_STYLES = `\n:host { all: initial; }\n* { box-sizing: border-box; }\n\n.surg-root {\n --surg-radius: 16px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #0f172a;\n}\n\n.surg-launcher {\n position: fixed;\n bottom: 20px;\n width: 56px;\n height: 56px;\n border-radius: 50%;\n border: none;\n cursor: pointer;\n background: var(--surg-accent);\n color: var(--surg-accent-fg);\n box-shadow: 0 6px 24px rgba(15, 23, 42, 0.25);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n z-index: 2147483000;\n}\n.surg-launcher:hover { transform: scale(1.06); box-shadow: 0 8px 28px rgba(15, 23, 42, 0.3); }\n.surg-launcher svg { width: 26px; height: 26px; }\n.surg-pos-bottom-right { right: 20px; }\n.surg-pos-bottom-left { left: 20px; }\n\n.surg-panel {\n position: fixed;\n bottom: 88px;\n width: 380px;\n max-width: calc(100vw - 40px);\n height: 560px;\n max-height: calc(100vh - 120px);\n background: #ffffff;\n border-radius: var(--surg-radius);\n box-shadow: 0 12px 48px rgba(15, 23, 42, 0.28);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 2147483000;\n animation: surg-rise 0.18s ease;\n}\n@keyframes surg-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }\n\n.surg-inline {\n position: relative;\n bottom: auto; right: auto; left: auto;\n width: 100%;\n height: 100%;\n box-shadow: none;\n border: 1px solid #e2e8f0;\n animation: none;\n}\n\n.surg-header {\n background: var(--surg-accent);\n color: var(--surg-accent-fg);\n padding: 14px 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n}\n.surg-header-id { display: flex; align-items: center; gap: 10px; min-width: 0; }\n.surg-header-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }\n.surg-title { font-weight: 600; font-size: 15px; }\n.surg-subtitle { font-size: 11px; font-weight: 400; opacity: 0.85; }\n.surg-logo {\n width: 32px; height: 32px; border-radius: 50%; object-fit: cover;\n background: rgba(255, 255, 255, 0.2); flex-shrink: 0;\n}\n.surg-launcher-logo {\n width: 32px; height: 32px; border-radius: 50%; object-fit: cover;\n}\n.surg-close {\n background: transparent;\n border: none;\n color: inherit;\n cursor: pointer;\n opacity: 0.85;\n padding: 4px;\n display: flex;\n border-radius: 6px;\n}\n.surg-close:hover { opacity: 1; background: rgba(255, 255, 255, 0.18); }\n\n.surg-messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n background: #f8fafc;\n}\n\n.surg-bubble {\n max-width: 82%;\n padding: 9px 13px;\n border-radius: 14px;\n word-wrap: break-word;\n white-space: normal;\n}\n.surg-bubble p { margin: 0 0 8px; }\n.surg-bubble p:last-child { margin-bottom: 0; }\n.surg-bubble ul, .surg-bubble ol { margin: 4px 0; padding-left: 20px; }\n.surg-bubble code {\n background: rgba(15, 23, 42, 0.08);\n padding: 1px 5px;\n border-radius: 5px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.9em;\n}\n.surg-bubble pre {\n background: #0f172a;\n color: #e2e8f0;\n padding: 10px 12px;\n border-radius: 10px;\n overflow-x: auto;\n margin: 6px 0;\n}\n.surg-bubble pre code { background: transparent; padding: 0; color: inherit; }\n.surg-bubble a { color: var(--surg-accent); }\n\n.surg-user {\n align-self: flex-end;\n background: var(--surg-accent);\n color: var(--surg-accent-fg);\n border-bottom-right-radius: 4px;\n white-space: pre-wrap;\n}\n.surg-user a { color: var(--surg-accent-fg); text-decoration: underline; }\n.surg-assistant {\n align-self: flex-start;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-bottom-left-radius: 4px;\n}\n\n.surg-typing { display: flex; gap: 4px; padding: 4px 2px; }\n.surg-typing span {\n width: 7px; height: 7px; border-radius: 50%;\n background: #94a3b8;\n animation: surg-blink 1.2s infinite ease-in-out;\n}\n.surg-typing span:nth-child(2) { animation-delay: 0.2s; }\n.surg-typing span:nth-child(3) { animation-delay: 0.4s; }\n@keyframes surg-blink { 0%, 60%, 100% { opacity: 0.3; } 30% { opacity: 1; } }\n\n.surg-error {\n margin: 0 16px;\n padding: 8px 12px;\n background: #fef2f2;\n border: 1px solid #fecaca;\n color: #b91c1c;\n border-radius: 10px;\n font-size: 13px;\n flex-shrink: 0;\n}\n\n.surg-composer {\n display: flex;\n gap: 8px;\n padding: 12px;\n border-top: 1px solid #e2e8f0;\n background: #ffffff;\n flex-shrink: 0;\n}\n.surg-input {\n flex: 1;\n resize: none;\n border: 1px solid #cbd5e1;\n border-radius: 12px;\n padding: 9px 12px;\n font: inherit;\n color: inherit;\n max-height: 120px;\n outline: none;\n}\n.surg-input:focus { border-color: var(--surg-accent); }\n.surg-send {\n border: none;\n background: var(--surg-accent);\n color: var(--surg-accent-fg);\n border-radius: 12px;\n width: 40px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n.surg-send:disabled { opacity: 0.5; cursor: not-allowed; }\n.surg-send svg { width: 18px; height: 18px; }\n\n.surg-powered {\n text-align: center;\n font-size: 11px;\n color: #94a3b8;\n padding: 0 0 8px;\n background: #ffffff;\n}\n`;\n","/**\n * Minimal, dependency-free Markdown → HTML renderer for assistant text.\n *\n * Assistant output is *semi-trusted* (it comes from the agent, not the\n * visitor), but the widget renders into a Shadow DOM via\n * ``dangerouslySetInnerHTML``, so a careless renderer would still be an\n * XSS vector if the model ever echoed attacker-supplied HTML. The\n * contract here is therefore: **escape first, then re-introduce only the\n * small, known-safe subset of HTML the formatter produces.** No raw\n * HTML from the source string ever survives — every ``<`` in the input\n * is entity-escaped before any tag is emitted.\n *\n * Supported: fenced code blocks, inline code, bold, italic, links\n * (http/https/mailto only), unordered + ordered lists, and paragraphs\n * with line breaks. Everything else degrades to escaped plain text.\n */\n\nconst ESCAPE_MAP: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n};\n\nfunction escapeHtml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => ESCAPE_MAP[c] ?? c);\n}\n\n/** Allow only links the browser can't be tricked into executing. */\nfunction safeHref(raw: string): string | undefined {\n const url = raw.trim();\n if (/^(https?:|mailto:)/i.test(url)) return url;\n return undefined;\n}\n\n/**\n * Apply inline formatting to an *already HTML-escaped* fragment. Order\n * matters: links before emphasis so ``[a](b)`` brackets aren't eaten by\n * the italic rule, and bold (``**``) before italic (``*``).\n */\nfunction renderInline(escaped: string): string {\n let out = escaped;\n\n // Inline code first — its contents must not be touched by later rules.\n // The buffer is already escaped, so the captured group is literal text.\n out = out.replace(/`([^`]+)`/g, (_m, code: string) => `<code>${code}</code>`);\n\n // Links: [text](href). ``href`` is validated; bad schemes render as\n // the original (escaped) text without an anchor.\n out = out.replace(\n /\\[([^\\]]+)\\]\\(([^)\\s]+)\\)/g,\n (_m, text: string, href: string) => {\n const safe = safeHref(href);\n if (!safe) return `${text}`;\n return `<a href=\"${safe}\" target=\"_blank\" rel=\"noopener noreferrer\">${text}</a>`;\n },\n );\n\n out = out.replace(/\\*\\*([^*]+)\\*\\*/g, (_m, t: string) => `<strong>${t}</strong>`);\n out = out.replace(/(^|[^*])\\*([^*\\n]+)\\*/g, (_m, pre: string, t: string) => `${pre}<em>${t}</em>`);\n\n return out;\n}\n\n/**\n * Render ``src`` Markdown to a safe HTML string. Splits on fenced code\n * blocks so their bodies bypass inline formatting entirely, then groups\n * the remaining text into list and paragraph blocks.\n */\nexport function renderMarkdown(src: string): string {\n const parts = src.split(/```/);\n const html: string[] = [];\n\n parts.forEach((part, index) => {\n const isCodeFence = index % 2 === 1;\n if (isCodeFence) {\n // Drop an optional language hint on the first line.\n const body = part.replace(/^[^\\n]*\\n/, (m) => (part.includes('\\n') ? '' : m));\n html.push(`<pre><code>${escapeHtml(body.replace(/\\n$/, ''))}</code></pre>`);\n return;\n }\n\n const blocks = part.split(/\\n{2,}/);\n for (const block of blocks) {\n const lines = block.split('\\n').filter((l) => l.trim().length > 0);\n if (lines.length === 0) continue;\n\n const isUnordered = lines.every((l) => /^\\s*[-*]\\s+/.test(l));\n const isOrdered = lines.every((l) => /^\\s*\\d+\\.\\s+/.test(l));\n if (isUnordered || isOrdered) {\n const items = lines\n .map((l) => l.replace(/^\\s*(?:[-*]|\\d+\\.)\\s+/, ''))\n .map((l) => `<li>${renderInline(escapeHtml(l))}</li>`)\n .join('');\n html.push(isOrdered ? `<ol>${items}</ol>` : `<ul>${items}</ul>`);\n continue;\n }\n\n const paragraph = lines.map((l) => renderInline(escapeHtml(l))).join('<br>');\n html.push(`<p>${paragraph}</p>`);\n }\n });\n\n return html.join('');\n}\n","/**\n * Strip the per-turn ``<next_action>`` footer from assistant text.\n *\n * The harness instructs the assistant to end every final response with a\n * ``<next_action complexity=\"...\" summary=\"...\">...</next_action>`` block\n * (see ``surogates/harness/prompts/guidance/next_action.md``). It is\n * harness/UI metadata, not user-facing content — by design the *client*\n * strips it: the full chat console does so in\n * ``agent-chat-react/src/lib/next-action.ts``, and messaging channels\n * strip it server-side. This widget streams raw ``llm.delta`` frames, so\n * it must strip it too or the XML footer flashes in the bubble.\n *\n * We only need the stripping half here (the console additionally parses\n * the block into a status pill). Streaming-safe: a dangling, not-yet-\n * closed footer is hidden the moment its opener appears so partial tags\n * never flicker.\n */\n\n// Complete ``<next_action ...>...</next_action>`` blocks (+ trailing ws).\nconst NEXT_ACTION_RE = /<next_action([^>]*)>([\\s\\S]*?)<\\/next_action>\\s*/gi;\n\n// A trailing, still-streaming footer: any prefix of the literal\n// ``<next_action`` token at the start of a line through end-of-text,\n// then everything after the whole word arrives (attributes + partial\n// body, closing tag not yet seen). Anchored to start-of-line so a stray\n// ``<`` mid-sentence is never mistaken for the footer.\nconst PARTIAL_FOOTER_RE =\n /(?:^|\\n)\\s*<(?:n(?:e(?:x(?:t(?:_(?:a(?:c(?:t(?:i(?:o(?:n[\\s\\S]*)?)?)?)?)?)?)?)?)?)?)?$/i;\n\n/** Return *text* with every (complete or streaming-partial) footer removed. */\nexport function stripNextAction(text: string): string {\n if (!text) return text;\n const withoutBlocks = text.replace(NEXT_ACTION_RE, '').trimEnd();\n const match = PARTIAL_FOOTER_RE.exec(withoutBlocks);\n return match ? withoutBlocks.slice(0, match.index).trimEnd() : withoutBlocks;\n}\n","/**\n * The Preact chat UI rendered inside the widget's Shadow DOM.\n *\n * It is a thin presentation layer over the headless :class:`WebsiteAgent`:\n * all transport, auth, and streaming live in the agent; this component\n * only subscribes to the AG-UI event stream, mirrors ``agent.messages``\n * into local state, and drives ``runAgent`` from the composer.\n */\nimport { useEffect, useRef, useState } from 'preact/hooks';\n\nimport type { WebsiteAgent } from '../agent.js';\nimport { SurogatesAuthError, SurogatesRateLimitError } from '../errors.js';\nimport { renderMarkdown } from './markdown.js';\nimport { stripNextAction } from './next-action.js';\nimport type { MountConfig } from './types.js';\n\n// Shown when the operator hasn't set their own. Kept friendly and\n// provider-neutral so an unconfigured widget still looks finished.\nconst DEFAULT_SUBTITLE = 'Typically replies instantly';\nconst DEFAULT_WELCOME = 'Hi! 👋 How can I help you today?';\n\ninterface ChatMessage {\n id: string;\n role: 'user' | 'assistant';\n content: string;\n}\n\ninterface WidgetProps {\n agent: WebsiteAgent;\n config: MountConfig;\n /** Lets the imperative handle in ``mount`` drive the open/closed state. */\n registerOpenControl: (setOpen: (open: boolean) => void) => void;\n /** Reports open-state changes back so ``mount``'s ``toggle()`` stays in sync. */\n onOpenChange?: (open: boolean) => void;\n}\n\nfunction uid(): string {\n return `u-${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n}\n\n/** Project ``agent.messages`` down to the renderable user/assistant turns. */\nfunction snapshot(messages: ReadonlyArray<{ id?: string; role?: string; content?: unknown }>): ChatMessage[] {\n const out: ChatMessage[] = [];\n for (const m of messages) {\n if (m.role !== 'user' && m.role !== 'assistant') continue;\n const content = typeof m.content === 'string' ? m.content : '';\n out.push({ id: m.id ?? uid(), role: m.role, content });\n }\n return out;\n}\n\n/** Map an SDK error (thrown or via RUN_ERROR) to visitor-facing copy. */\nfunction friendlyError(err: { code?: string; message?: string } | unknown): string {\n if (err instanceof SurogatesAuthError) {\n return 'This chat isn’t accepting messages from this site yet. The site owner needs to allow this origin.';\n }\n if (err instanceof SurogatesRateLimitError) {\n return 'You’re sending messages too quickly. Please wait a moment and try again.';\n }\n if (err && typeof err === 'object') {\n const code = (err as { code?: string }).code;\n if (code === 'auth') {\n return 'This chat isn’t accepting messages from this site yet. The site owner needs to allow this origin.';\n }\n if (code === 'sse_closed') return 'Connection lost. Please send your message again.';\n const message = (err as { message?: string }).message;\n if (message && message.length < 160) return message;\n }\n return 'Something went wrong. Please try again.';\n}\n\nconst ICON_CHAT = (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\" />\n </svg>\n);\nconst ICON_CLOSE = (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" /><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n);\nconst ICON_SEND = (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\" /><polygon points=\"22 2 15 22 11 13 2 9 22 2\" />\n </svg>\n);\n\nexport function Widget({ agent, config, registerOpenControl, onOpenChange }: WidgetProps) {\n const inline = !!config.inline;\n const [open, setOpen] = useState(inline || !!config.openByDefault);\n const [messages, setMessages] = useState<ChatMessage[]>(() => snapshot(agent.messages));\n const [running, setRunning] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [input, setInput] = useState('');\n // Only the async input is state; the title is derived at render so a\n // configured title (incl. live handle.update()) always wins, falling back\n // to the bootstrapped agent name, then a generic label.\n const [agentName, setAgentName] = useState('');\n const title = config.title || agentName || 'Assistant';\n\n const listRef = useRef<HTMLDivElement>(null);\n const bootstrapStarted = useRef(false);\n\n // Expose the open setter to the mount() imperative handle.\n useEffect(() => registerOpenControl(setOpen), [registerOpenControl]);\n useEffect(() => onOpenChange?.(open), [open, onOpenChange]);\n\n // Mirror the agent's event stream into local UI state.\n useEffect(() => {\n const sub = agent.subscribe({\n onMessagesChanged: () => setMessages(snapshot(agent.messages)),\n onRunStartedEvent: () => {\n setRunning(true);\n setError(null);\n },\n onRunFinishedEvent: () => setRunning(false),\n onRunErrorEvent: ({ event }) => {\n setRunning(false);\n setError(friendlyError(event));\n },\n });\n return () => sub.unsubscribe();\n }, [agent]);\n\n // Validate config + resolve the header title the first time the panel\n // is opened. Eager bootstrap on open (not page-load) means visitors\n // who never open the chat don't create server sessions; opening is a\n // clear intent signal and surfaces misconfiguration immediately.\n useEffect(() => {\n if (!open || bootstrapStarted.current) return;\n bootstrapStarted.current = true;\n agent\n .ensureBootstrapped()\n .then((res) => {\n if (res.agentName) setAgentName(res.agentName);\n })\n .catch((err) => {\n // Allow a retry the next time the panel opens (transient network, etc.).\n bootstrapStarted.current = false;\n setError(friendlyError(err));\n });\n }, [open, agent]);\n\n // Keep the transcript pinned to the latest message.\n useEffect(() => {\n const el = listRef.current;\n if (el) el.scrollTop = el.scrollHeight;\n }, [messages, running]);\n\n const send = () => {\n const content = input.trim();\n if (!content || running) return;\n setInput('');\n agent.addMessage({ id: uid(), role: 'user', content } as never);\n setMessages(snapshot(agent.messages));\n void agent.runAgent().catch(() => {\n /* Failures surface through onRunErrorEvent; swallow the rejection. */\n });\n };\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n send();\n }\n };\n\n // An unconfigured widget always shows sensible default copy: a missing\n // subtitle/welcome falls back to the defaults. Note Studio/the embed omit\n // empty fields, so *clearing* the field in Studio is indistinguishable from\n // never setting it — both show the default. There is intentionally no\n // \"show nothing\" state today; if suppression is ever needed, add an explicit\n // sentinel rather than relying on empty string.\n const subtitle = config.subtitle ?? DEFAULT_SUBTITLE;\n const welcomeText = config.welcomeMessage ?? DEFAULT_WELCOME;\n const hasAssistant = messages.some((m) => m.role === 'assistant');\n const showWelcome = !!welcomeText && !hasAssistant;\n const showTyping = running && messages[messages.length - 1]?.role !== 'assistant';\n\n if (!inline && !open) {\n return (\n <button\n type=\"button\"\n class={`surg-launcher surg-pos-${config.position ?? 'bottom-right'}`}\n aria-label=\"Open chat\"\n onClick={() => setOpen(true)}\n >\n {config.logoUrl ? (\n <img class=\"surg-launcher-logo\" src={config.logoUrl} alt=\"\" />\n ) : (\n ICON_CHAT\n )}\n </button>\n );\n }\n\n const panelClass = inline\n ? 'surg-panel surg-inline'\n : `surg-panel surg-pos-${config.position ?? 'bottom-right'}`;\n\n return (\n <div class={panelClass} role=\"dialog\" aria-label={title}>\n <div class=\"surg-header\">\n <div class=\"surg-header-id\">\n {config.logoUrl && <img class=\"surg-logo\" src={config.logoUrl} alt=\"\" />}\n <div class=\"surg-header-text\">\n <span class=\"surg-title\">{title}</span>\n {subtitle && <span class=\"surg-subtitle\">{subtitle}</span>}\n </div>\n </div>\n {!inline && (\n <button type=\"button\" class=\"surg-close\" aria-label=\"Close chat\" onClick={() => setOpen(false)}>\n {ICON_CLOSE}\n </button>\n )}\n </div>\n\n <div class=\"surg-messages\" ref={listRef}>\n {showWelcome && (\n <div\n class=\"surg-bubble surg-assistant\"\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: renderMarkdown(welcomeText) }}\n />\n )}\n {messages.map((m) =>\n m.role === 'assistant' ? (\n <div\n key={m.id}\n class=\"surg-bubble surg-assistant\"\n dangerouslySetInnerHTML={{ __html: renderMarkdown(stripNextAction(m.content)) }}\n />\n ) : (\n <div key={m.id} class=\"surg-bubble surg-user\">\n {m.content}\n </div>\n ),\n )}\n {showTyping && (\n <div class=\"surg-bubble surg-assistant surg-typing\">\n <span /><span /><span />\n </div>\n )}\n </div>\n\n {error && <div class=\"surg-error\">{error}</div>}\n\n <div class=\"surg-composer\">\n <textarea\n class=\"surg-input\"\n rows={1}\n placeholder=\"Type a message…\"\n value={input}\n disabled={running}\n onInput={(e) => setInput((e.target as HTMLTextAreaElement).value)}\n onKeyDown={onKeyDown}\n />\n <button type=\"button\" class=\"surg-send\" aria-label=\"Send\" disabled={running || !input.trim()} onClick={send}>\n {ICON_SEND}\n </button>\n </div>\n <div class=\"surg-powered\">Powered by Surogate</div>\n </div>\n );\n}\n","/**\n * ``mount()`` — the drop-in entry point.\n *\n * A single call stands up a complete chat widget: it creates a host\n * element, isolates the UI in a Shadow DOM (so neither the host page's\n * CSS nor the widget's styles can bleed across), instantiates the\n * headless :class:`WebsiteAgent`, and renders the Preact UI into the\n * shadow root. Returns an imperative :type:`WidgetHandle` for hosts\n * that want to open/close/destroy the widget programmatically.\n *\n * This is what the ``<script>``-tag embed and the Studio live preview\n * both call. Everything cosmetic is optional; ``apiUrl`` and\n * ``publishableKey`` are the only required fields.\n */\nimport { render } from 'preact';\n\nimport { WebsiteAgent } from '../agent.js';\nimport { WIDGET_STYLES } from './styles.js';\nimport type { MountConfig, WidgetHandle } from './types.js';\nimport { Widget } from './widget.js';\n\nexport type { MountConfig, WidgetHandle, WidgetAppearance } from './types.js';\n\nconst DEFAULT_ACCENT = '#4f46e5';\n\n/**\n * Pick a readable foreground (near-black vs white) for text/icons that\n * sit on the accent colour, using an sRGB luminance approximation. A\n * pale accent gets dark text; a saturated one gets white.\n */\nfunction accentForeground(hex: string): string {\n const v = hex.replace('#', '');\n const full = v.length === 3 ? v.split('').map((c) => c + c).join('') : v;\n if (full.length !== 6 || /[^0-9a-f]/i.test(full)) return '#ffffff';\n const r = parseInt(full.slice(0, 2), 16);\n const g = parseInt(full.slice(2, 4), 16);\n const b = parseInt(full.slice(4, 6), 16);\n const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n return luminance > 0.62 ? '#0f172a' : '#ffffff';\n}\n\nexport function mount(config: MountConfig): WidgetHandle {\n if (!config || !config.apiUrl || !config.publishableKey) {\n throw new Error(\n 'SurogatesWidget.mount: \"apiUrl\" and \"publishableKey\" are required.',\n );\n }\n\n const accent = config.accentColor ?? DEFAULT_ACCENT;\n let destroyed = false;\n\n // Host element + Shadow DOM: total style isolation in both directions.\n const host = document.createElement('div');\n host.setAttribute('data-surogates-widget', '');\n const shadow = host.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = WIDGET_STYLES;\n shadow.appendChild(style);\n\n const root = document.createElement('div');\n root.className = 'surg-root';\n shadow.appendChild(root);\n\n // Accent drives CSS custom properties on the root, so it can be re-applied\n // on update() without re-rendering anything else.\n const applyAccent = (accentColor?: string) => {\n const a = accentColor ?? DEFAULT_ACCENT;\n root.style.setProperty('--surg-accent', a);\n root.style.setProperty('--surg-accent-fg', accentForeground(a));\n };\n applyAccent(accent);\n\n // Attach to the page. Drop-in embeds are often placed in <head>, where\n // document.body doesn't exist yet — defer the append until the DOM is ready.\n const attach = () => (config.target ?? document.body)?.appendChild(host);\n if (config.target || document.body) {\n attach();\n } else {\n document.addEventListener(\n \"DOMContentLoaded\",\n () => {\n if (!destroyed) attach();\n },\n { once: true },\n );\n }\n\n const agent = new WebsiteAgent(config);\n\n let setOpen: ((open: boolean) => void) | undefined;\n let isOpen = config.inline || !!config.openByDefault;\n\n // Stable callbacks so re-rendering on update() doesn't re-fire the\n // component's open-control / open-change effects.\n const registerOpenControl = (fn: (open: boolean) => void) => {\n setOpen = fn;\n };\n const onOpenChange = (o: boolean) => {\n isOpen = o;\n };\n\n // Current config is mutable so update() can re-render appearance without\n // tearing down the agent (no re-bootstrap, no lost conversation).\n let current: MountConfig = config;\n const renderWidget = () => {\n render(\n <Widget\n agent={agent}\n config={current}\n registerOpenControl={registerOpenControl}\n onOpenChange={onOpenChange}\n />,\n root,\n );\n };\n renderWidget();\n\n // Best-effort session close when the visitor navigates away.\n const endOnUnload = () => {\n void agent.end();\n };\n window.addEventListener('beforeunload', endOnUnload);\n\n return {\n agent,\n open: () => setOpen?.(true),\n close: () => setOpen?.(false),\n toggle: () => setOpen?.(!isOpen),\n update: (partial) => {\n if (destroyed) return;\n current = { ...current, ...partial };\n if ('accentColor' in partial) applyAccent(current.accentColor);\n renderWidget();\n },\n destroy: () => {\n if (destroyed) return;\n destroyed = true;\n window.removeEventListener('beforeunload', endOnUnload);\n void agent.end();\n render(null, root);\n host.remove();\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/ui/styles.ts","../src/ui/markdown.ts","../src/ui/next-action.ts","../src/ui/widget.tsx","../src/ui/mount.tsx"],"names":["jsx"],"mappings":"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","file":"ui.js","sourcesContent":["/**\n * Scoped CSS for the chat widget, injected into the Shadow DOM root so\n * it can neither leak into nor be overridden by the host page. All\n * colours derive from two custom properties (``--surg-accent`` and its\n * readable foreground) set at mount time from the caller's\n * ``accentColor`` so theming is a one-line change.\n *\n * Returned as a string (rather than a constructable stylesheet) because\n * a ``<style>`` element in the shadow root is the most broadly supported\n * path across the browsers a public embed has to serve.\n */\nexport const WIDGET_STYLES = `\n:host { all: initial; }\n* { box-sizing: border-box; }\n\n.surg-root {\n --surg-radius: 16px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #0f172a;\n}\n\n.surg-launcher {\n position: fixed;\n bottom: 20px;\n width: 56px;\n height: 56px;\n border-radius: 50%;\n border: none;\n cursor: pointer;\n background: var(--surg-accent);\n color: var(--surg-accent-fg);\n box-shadow: 0 6px 24px rgba(15, 23, 42, 0.25);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n z-index: 2147483000;\n}\n.surg-launcher:hover { transform: scale(1.06); box-shadow: 0 8px 28px rgba(15, 23, 42, 0.3); }\n.surg-launcher svg { width: 26px; height: 26px; }\n.surg-pos-bottom-right { right: 20px; }\n.surg-pos-bottom-left { left: 20px; }\n\n.surg-panel {\n position: fixed;\n bottom: 88px;\n width: 380px;\n max-width: calc(100vw - 40px);\n height: 560px;\n max-height: calc(100vh - 120px);\n background: #ffffff;\n border-radius: var(--surg-radius);\n box-shadow: 0 12px 48px rgba(15, 23, 42, 0.28);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 2147483000;\n animation: surg-rise 0.18s ease;\n}\n@keyframes surg-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }\n\n.surg-inline {\n position: relative;\n bottom: auto; right: auto; left: auto;\n width: 100%;\n height: 100%;\n box-shadow: none;\n border: 1px solid #e2e8f0;\n animation: none;\n}\n\n.surg-header {\n background: var(--surg-accent);\n color: var(--surg-accent-fg);\n padding: 14px 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n}\n.surg-header-id { display: flex; align-items: center; gap: 10px; min-width: 0; }\n.surg-header-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }\n.surg-title { font-weight: 600; font-size: 15px; }\n.surg-subtitle { font-size: 11px; font-weight: 400; opacity: 0.85; }\n.surg-logo {\n width: 32px; height: 32px; border-radius: 50%; object-fit: cover;\n background: rgba(255, 255, 255, 0.2); flex-shrink: 0;\n}\n.surg-launcher-logo {\n width: 32px; height: 32px; border-radius: 50%; object-fit: cover;\n}\n.surg-close {\n background: transparent;\n border: none;\n color: inherit;\n cursor: pointer;\n opacity: 0.85;\n padding: 4px;\n display: flex;\n border-radius: 6px;\n}\n.surg-close:hover { opacity: 1; background: rgba(255, 255, 255, 0.18); }\n.surg-close svg { width: 18px; height: 18px; }\n\n.surg-messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n background: #f8fafc;\n}\n\n.surg-bubble {\n max-width: 82%;\n padding: 9px 13px;\n border-radius: 14px;\n word-wrap: break-word;\n white-space: normal;\n}\n.surg-bubble p { margin: 0 0 8px; }\n.surg-bubble p:last-child { margin-bottom: 0; }\n.surg-bubble ul, .surg-bubble ol { margin: 4px 0; padding-left: 20px; }\n.surg-bubble code {\n background: rgba(15, 23, 42, 0.08);\n padding: 1px 5px;\n border-radius: 5px;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.9em;\n}\n.surg-bubble pre {\n background: #0f172a;\n color: #e2e8f0;\n padding: 10px 12px;\n border-radius: 10px;\n overflow-x: auto;\n margin: 6px 0;\n}\n.surg-bubble pre code { background: transparent; padding: 0; color: inherit; }\n.surg-bubble a { color: var(--surg-accent); }\n\n.surg-user {\n align-self: flex-end;\n background: var(--surg-accent);\n color: var(--surg-accent-fg);\n border-bottom-right-radius: 4px;\n white-space: pre-wrap;\n}\n.surg-user a { color: var(--surg-accent-fg); text-decoration: underline; }\n.surg-assistant {\n align-self: flex-start;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-bottom-left-radius: 4px;\n}\n\n.surg-typing { display: flex; gap: 4px; padding: 4px 2px; }\n.surg-typing span {\n width: 7px; height: 7px; border-radius: 50%;\n background: #94a3b8;\n animation: surg-blink 1.2s infinite ease-in-out;\n}\n.surg-typing span:nth-child(2) { animation-delay: 0.2s; }\n.surg-typing span:nth-child(3) { animation-delay: 0.4s; }\n@keyframes surg-blink { 0%, 60%, 100% { opacity: 0.3; } 30% { opacity: 1; } }\n\n.surg-error {\n margin: 0 16px;\n padding: 8px 12px;\n background: #fef2f2;\n border: 1px solid #fecaca;\n color: #b91c1c;\n border-radius: 10px;\n font-size: 13px;\n flex-shrink: 0;\n}\n\n.surg-composer {\n display: flex;\n gap: 8px;\n padding: 12px;\n border-top: 1px solid #e2e8f0;\n background: #ffffff;\n flex-shrink: 0;\n}\n.surg-input {\n flex: 1;\n resize: none;\n border: 1px solid #cbd5e1;\n border-radius: 12px;\n padding: 9px 12px;\n font: inherit;\n color: inherit;\n max-height: 120px;\n outline: none;\n}\n.surg-input:focus { border-color: var(--surg-accent); }\n.surg-send {\n border: none;\n background: var(--surg-accent);\n color: var(--surg-accent-fg);\n border-radius: 12px;\n width: 40px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n.surg-send:disabled { opacity: 0.5; cursor: not-allowed; }\n.surg-send svg { width: 18px; height: 18px; }\n\n.surg-powered {\n text-align: center;\n font-size: 11px;\n color: #94a3b8;\n padding: 0 0 8px;\n background: #ffffff;\n}\n.surg-powered a { color: inherit; font-weight: 500; text-decoration: none; }\n.surg-powered a:hover { text-decoration: underline; }\n`;\n","/**\n * Minimal, dependency-free Markdown → HTML renderer for assistant text.\n *\n * Assistant output is *semi-trusted* (it comes from the agent, not the\n * visitor), but the widget renders into a Shadow DOM via\n * ``dangerouslySetInnerHTML``, so a careless renderer would still be an\n * XSS vector if the model ever echoed attacker-supplied HTML. The\n * contract here is therefore: **escape first, then re-introduce only the\n * small, known-safe subset of HTML the formatter produces.** No raw\n * HTML from the source string ever survives — every ``<`` in the input\n * is entity-escaped before any tag is emitted.\n *\n * Supported: fenced code blocks, inline code, bold, italic, links\n * (http/https/mailto only), unordered + ordered lists, and paragraphs\n * with line breaks. Everything else degrades to escaped plain text.\n */\n\nconst ESCAPE_MAP: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n};\n\nfunction escapeHtml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => ESCAPE_MAP[c] ?? c);\n}\n\n/** Allow only links the browser can't be tricked into executing. */\nfunction safeHref(raw: string): string | undefined {\n const url = raw.trim();\n if (/^(https?:|mailto:)/i.test(url)) return url;\n return undefined;\n}\n\n/**\n * Apply inline formatting to an *already HTML-escaped* fragment. Order\n * matters: links before emphasis so ``[a](b)`` brackets aren't eaten by\n * the italic rule, and bold (``**``) before italic (``*``).\n */\nfunction renderInline(escaped: string): string {\n let out = escaped;\n\n // Inline code first — its contents must not be touched by later rules.\n // The buffer is already escaped, so the captured group is literal text.\n out = out.replace(/`([^`]+)`/g, (_m, code: string) => `<code>${code}</code>`);\n\n // Links: [text](href). ``href`` is validated; bad schemes render as\n // the original (escaped) text without an anchor.\n out = out.replace(\n /\\[([^\\]]+)\\]\\(([^)\\s]+)\\)/g,\n (_m, text: string, href: string) => {\n const safe = safeHref(href);\n if (!safe) return `${text}`;\n return `<a href=\"${safe}\" target=\"_blank\" rel=\"noopener noreferrer\">${text}</a>`;\n },\n );\n\n out = out.replace(/\\*\\*([^*]+)\\*\\*/g, (_m, t: string) => `<strong>${t}</strong>`);\n out = out.replace(/(^|[^*])\\*([^*\\n]+)\\*/g, (_m, pre: string, t: string) => `${pre}<em>${t}</em>`);\n\n return out;\n}\n\n/**\n * Render ``src`` Markdown to a safe HTML string. Splits on fenced code\n * blocks so their bodies bypass inline formatting entirely, then groups\n * the remaining text into list and paragraph blocks.\n */\nexport function renderMarkdown(src: string): string {\n const parts = src.split(/```/);\n const html: string[] = [];\n\n parts.forEach((part, index) => {\n const isCodeFence = index % 2 === 1;\n if (isCodeFence) {\n // Drop an optional language hint on the first line.\n const body = part.replace(/^[^\\n]*\\n/, (m) => (part.includes('\\n') ? '' : m));\n html.push(`<pre><code>${escapeHtml(body.replace(/\\n$/, ''))}</code></pre>`);\n return;\n }\n\n const blocks = part.split(/\\n{2,}/);\n for (const block of blocks) {\n const lines = block.split('\\n').filter((l) => l.trim().length > 0);\n if (lines.length === 0) continue;\n\n const isUnordered = lines.every((l) => /^\\s*[-*]\\s+/.test(l));\n const isOrdered = lines.every((l) => /^\\s*\\d+\\.\\s+/.test(l));\n if (isUnordered || isOrdered) {\n const items = lines\n .map((l) => l.replace(/^\\s*(?:[-*]|\\d+\\.)\\s+/, ''))\n .map((l) => `<li>${renderInline(escapeHtml(l))}</li>`)\n .join('');\n html.push(isOrdered ? `<ol>${items}</ol>` : `<ul>${items}</ul>`);\n continue;\n }\n\n const paragraph = lines.map((l) => renderInline(escapeHtml(l))).join('<br>');\n html.push(`<p>${paragraph}</p>`);\n }\n });\n\n return html.join('');\n}\n","/**\n * Strip the per-turn ``<next_action>`` footer from assistant text.\n *\n * The harness instructs the assistant to end every final response with a\n * ``<next_action complexity=\"...\" summary=\"...\">...</next_action>`` block\n * (see ``surogates/harness/prompts/guidance/next_action.md``). It is\n * harness/UI metadata, not user-facing content — by design the *client*\n * strips it: the full chat console does so in\n * ``agent-chat-react/src/lib/next-action.ts``, and messaging channels\n * strip it server-side. This widget streams raw ``llm.delta`` frames, so\n * it must strip it too or the XML footer flashes in the bubble.\n *\n * We only need the stripping half here (the console additionally parses\n * the block into a status pill). Streaming-safe: a dangling, not-yet-\n * closed footer is hidden the moment its opener appears so partial tags\n * never flicker.\n */\n\n// Complete ``<next_action ...>...</next_action>`` blocks (+ trailing ws).\nconst NEXT_ACTION_RE = /<next_action([^>]*)>([\\s\\S]*?)<\\/next_action>\\s*/gi;\n\n// A trailing, still-streaming footer: any prefix of the literal\n// ``<next_action`` token at the start of a line through end-of-text,\n// then everything after the whole word arrives (attributes + partial\n// body, closing tag not yet seen). Anchored to start-of-line so a stray\n// ``<`` mid-sentence is never mistaken for the footer.\nconst PARTIAL_FOOTER_RE =\n /(?:^|\\n)\\s*<(?:n(?:e(?:x(?:t(?:_(?:a(?:c(?:t(?:i(?:o(?:n[\\s\\S]*)?)?)?)?)?)?)?)?)?)?)?$/i;\n\n/** Return *text* with every (complete or streaming-partial) footer removed. */\nexport function stripNextAction(text: string): string {\n if (!text) return text;\n const withoutBlocks = text.replace(NEXT_ACTION_RE, '').trimEnd();\n const match = PARTIAL_FOOTER_RE.exec(withoutBlocks);\n return match ? withoutBlocks.slice(0, match.index).trimEnd() : withoutBlocks;\n}\n","/**\n * The Preact chat UI rendered inside the widget's Shadow DOM.\n *\n * It is a thin presentation layer over the headless :class:`WebsiteAgent`:\n * all transport, auth, and streaming live in the agent; this component\n * only subscribes to the AG-UI event stream, mirrors ``agent.messages``\n * into local state, and drives ``runAgent`` from the composer.\n */\nimport { useEffect, useRef, useState } from 'preact/hooks';\n\nimport type { WebsiteAgent } from '../agent.js';\nimport { SurogatesAuthError, SurogatesRateLimitError } from '../errors.js';\nimport { renderMarkdown } from './markdown.js';\nimport { stripNextAction } from './next-action.js';\nimport type { MountConfig } from './types.js';\n\n// Shown when the operator hasn't set their own. Kept friendly and\n// provider-neutral so an unconfigured widget still looks finished.\nconst DEFAULT_SUBTITLE = 'Typically replies instantly';\nconst DEFAULT_WELCOME = 'Hi! 👋 How can I help you today?';\n\ninterface ChatMessage {\n id: string;\n role: 'user' | 'assistant';\n content: string;\n}\n\ninterface WidgetProps {\n agent: WebsiteAgent;\n config: MountConfig;\n /** Lets the imperative handle in ``mount`` drive the open/closed state. */\n registerOpenControl: (setOpen: (open: boolean) => void) => void;\n /** Reports open-state changes back so ``mount``'s ``toggle()`` stays in sync. */\n onOpenChange?: (open: boolean) => void;\n}\n\nfunction uid(): string {\n return `u-${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n}\n\n/** Project ``agent.messages`` down to the renderable user/assistant turns. */\nfunction snapshot(messages: ReadonlyArray<{ id?: string; role?: string; content?: unknown }>): ChatMessage[] {\n const out: ChatMessage[] = [];\n for (const m of messages) {\n if (m.role !== 'user' && m.role !== 'assistant') continue;\n const content = typeof m.content === 'string' ? m.content : '';\n out.push({ id: m.id ?? uid(), role: m.role, content });\n }\n return out;\n}\n\n/** Map an SDK error (thrown or via RUN_ERROR) to visitor-facing copy. */\nfunction friendlyError(err: { code?: string; message?: string } | unknown): string {\n if (err instanceof SurogatesAuthError) {\n return 'This chat isn’t accepting messages from this site yet. The site owner needs to allow this origin.';\n }\n if (err instanceof SurogatesRateLimitError) {\n return 'You’re sending messages too quickly. Please wait a moment and try again.';\n }\n if (err && typeof err === 'object') {\n const code = (err as { code?: string }).code;\n if (code === 'auth') {\n return 'This chat isn’t accepting messages from this site yet. The site owner needs to allow this origin.';\n }\n if (code === 'sse_closed') return 'Connection lost. Please send your message again.';\n const message = (err as { message?: string }).message;\n if (message && message.length < 160) return message;\n }\n return 'Something went wrong. Please try again.';\n}\n\nconst ICON_CHAT = (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\" />\n </svg>\n);\nconst ICON_CLOSE = (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" /><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n);\nconst ICON_SEND = (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\" /><polygon points=\"22 2 15 22 11 13 2 9 22 2\" />\n </svg>\n);\n\nexport function Widget({ agent, config, registerOpenControl, onOpenChange }: WidgetProps) {\n const inline = !!config.inline;\n const [open, setOpen] = useState(inline || !!config.openByDefault);\n const [messages, setMessages] = useState<ChatMessage[]>(() => snapshot(agent.messages));\n const [running, setRunning] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [input, setInput] = useState('');\n // Only the async input is state; the title is derived at render so a\n // configured title (incl. live handle.update()) always wins, falling back\n // to the bootstrapped agent name, then a generic label.\n const [agentName, setAgentName] = useState('');\n const title = config.title || agentName || 'Assistant';\n\n const listRef = useRef<HTMLDivElement>(null);\n const bootstrapStarted = useRef(false);\n\n // Expose the open setter to the mount() imperative handle.\n useEffect(() => registerOpenControl(setOpen), [registerOpenControl]);\n useEffect(() => onOpenChange?.(open), [open, onOpenChange]);\n\n // Mirror the agent's event stream into local UI state.\n useEffect(() => {\n const sub = agent.subscribe({\n onMessagesChanged: () => setMessages(snapshot(agent.messages)),\n onRunStartedEvent: () => {\n setRunning(true);\n setError(null);\n },\n onRunFinishedEvent: () => setRunning(false),\n onRunErrorEvent: ({ event }) => {\n setRunning(false);\n setError(friendlyError(event));\n },\n });\n return () => sub.unsubscribe();\n }, [agent]);\n\n // Validate config + resolve the header title the first time the panel\n // is opened. Eager bootstrap on open (not page-load) means visitors\n // who never open the chat don't create server sessions; opening is a\n // clear intent signal and surfaces misconfiguration immediately.\n useEffect(() => {\n if (!open || bootstrapStarted.current) return;\n bootstrapStarted.current = true;\n agent\n .ensureBootstrapped()\n .then((res) => {\n if (res.agentName) setAgentName(res.agentName);\n })\n .catch((err) => {\n // Allow a retry the next time the panel opens (transient network, etc.).\n bootstrapStarted.current = false;\n setError(friendlyError(err));\n });\n }, [open, agent]);\n\n // Keep the transcript pinned to the latest message.\n useEffect(() => {\n const el = listRef.current;\n if (el) el.scrollTop = el.scrollHeight;\n }, [messages, running]);\n\n const send = () => {\n const content = input.trim();\n if (!content || running) return;\n setInput('');\n agent.addMessage({ id: uid(), role: 'user', content } as never);\n setMessages(snapshot(agent.messages));\n void agent.runAgent().catch(() => {\n /* Failures surface through onRunErrorEvent; swallow the rejection. */\n });\n };\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n send();\n }\n };\n\n // An unconfigured widget always shows sensible default copy: a missing\n // subtitle/welcome falls back to the defaults. Note Studio/the embed omit\n // empty fields, so *clearing* the field in Studio is indistinguishable from\n // never setting it — both show the default. There is intentionally no\n // \"show nothing\" state today; if suppression is ever needed, add an explicit\n // sentinel rather than relying on empty string.\n const subtitle = config.subtitle ?? DEFAULT_SUBTITLE;\n const welcomeText = config.welcomeMessage ?? DEFAULT_WELCOME;\n const hasAssistant = messages.some((m) => m.role === 'assistant');\n const showWelcome = !!welcomeText && !hasAssistant;\n const showTyping = running && messages[messages.length - 1]?.role !== 'assistant';\n\n if (!inline && !open) {\n return (\n <button\n type=\"button\"\n class={`surg-launcher surg-pos-${config.position ?? 'bottom-right'}`}\n aria-label=\"Open chat\"\n onClick={() => setOpen(true)}\n >\n {config.logoUrl ? (\n <img class=\"surg-launcher-logo\" src={config.logoUrl} alt=\"\" />\n ) : (\n ICON_CHAT\n )}\n </button>\n );\n }\n\n const panelClass = inline\n ? 'surg-panel surg-inline'\n : `surg-panel surg-pos-${config.position ?? 'bottom-right'}`;\n\n return (\n <div class={panelClass} role=\"dialog\" aria-label={title}>\n <div class=\"surg-header\">\n <div class=\"surg-header-id\">\n {config.logoUrl && <img class=\"surg-logo\" src={config.logoUrl} alt=\"\" />}\n <div class=\"surg-header-text\">\n <span class=\"surg-title\">{title}</span>\n {subtitle && <span class=\"surg-subtitle\">{subtitle}</span>}\n </div>\n </div>\n {!inline && (\n <button type=\"button\" class=\"surg-close\" aria-label=\"Close chat\" onClick={() => setOpen(false)}>\n {ICON_CLOSE}\n </button>\n )}\n </div>\n\n <div class=\"surg-messages\" ref={listRef}>\n {showWelcome && (\n <div\n class=\"surg-bubble surg-assistant\"\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: renderMarkdown(welcomeText) }}\n />\n )}\n {messages.map((m) =>\n m.role === 'assistant' ? (\n <div\n key={m.id}\n class=\"surg-bubble surg-assistant\"\n dangerouslySetInnerHTML={{ __html: renderMarkdown(stripNextAction(m.content)) }}\n />\n ) : (\n <div key={m.id} class=\"surg-bubble surg-user\">\n {m.content}\n </div>\n ),\n )}\n {showTyping && (\n <div class=\"surg-bubble surg-assistant surg-typing\">\n <span /><span /><span />\n </div>\n )}\n </div>\n\n {error && <div class=\"surg-error\">{error}</div>}\n\n <div class=\"surg-composer\">\n <textarea\n class=\"surg-input\"\n rows={1}\n placeholder=\"Type a message…\"\n value={input}\n disabled={running}\n onInput={(e) => setInput((e.target as HTMLTextAreaElement).value)}\n onKeyDown={onKeyDown}\n />\n <button type=\"button\" class=\"surg-send\" aria-label=\"Send\" disabled={running || !input.trim()} onClick={send}>\n {ICON_SEND}\n </button>\n </div>\n <div class=\"surg-powered\">\n Powered by{' '}\n <a href=\"https://surogate.ai/\" target=\"_blank\" rel=\"noopener noreferrer\">\n Surogate\n </a>\n </div>\n </div>\n );\n}\n","/**\n * ``mount()`` — the drop-in entry point.\n *\n * A single call stands up a complete chat widget: it creates a host\n * element, isolates the UI in a Shadow DOM (so neither the host page's\n * CSS nor the widget's styles can bleed across), instantiates the\n * headless :class:`WebsiteAgent`, and renders the Preact UI into the\n * shadow root. Returns an imperative :type:`WidgetHandle` for hosts\n * that want to open/close/destroy the widget programmatically.\n *\n * This is what the ``<script>``-tag embed and the Studio live preview\n * both call. Everything cosmetic is optional; ``apiUrl`` and\n * ``publishableKey`` are the only required fields.\n */\nimport { render } from 'preact';\n\nimport { WebsiteAgent } from '../agent.js';\nimport { DEFAULT_PAIRING_BASE } from '../constants.js';\nimport { resolvePairing } from '../protocol.js';\nimport { WIDGET_STYLES } from './styles.js';\nimport type { MountConfig, WidgetHandle } from './types.js';\nimport { Widget } from './widget.js';\n\nexport type { MountConfig, WidgetHandle, WidgetAppearance } from './types.js';\n\nconst DEFAULT_ACCENT = '#4f46e5';\n\n/**\n * Pick a readable foreground (near-black vs white) for text/icons that\n * sit on the accent colour, using an sRGB luminance approximation. A\n * pale accent gets dark text; a saturated one gets white.\n */\nfunction accentForeground(hex: string): string {\n const v = hex.replace('#', '');\n const full = v.length === 3 ? v.split('').map((c) => c + c).join('') : v;\n if (full.length !== 6 || /[^0-9a-f]/i.test(full)) return '#ffffff';\n const r = parseInt(full.slice(0, 2), 16);\n const g = parseInt(full.slice(2, 4), 16);\n const b = parseInt(full.slice(4, 6), 16);\n const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n return luminance > 0.62 ? '#0f172a' : '#ffffff';\n}\n\n/**\n * Key-only entry point. Resolves the publishable key to its agent +\n * ``api_web_url`` via the control-plane pairing endpoint, then mounts against\n * that base URL — so an embed can be just ``mountWithPairing({ publishableKey })``\n * with no hardcoded ``apiUrl``. If ``apiUrl`` is already supplied, pairing is\n * skipped and this is equivalent to :func:`mount`.\n */\nexport async function mountWithPairing(\n config: Omit<MountConfig, 'apiUrl'> & { apiUrl?: string },\n): Promise<WidgetHandle> {\n if (config?.apiUrl) {\n return mount(config as MountConfig);\n }\n if (!config?.publishableKey) {\n throw new Error(\n 'SurogatesWidget.mountWithPairing: \"publishableKey\" is required.',\n );\n }\n const { agentId, apiWebUrl } = await resolvePairing(\n config.pairingUrl ?? DEFAULT_PAIRING_BASE,\n config.publishableKey,\n );\n return mount({ ...config, apiUrl: apiWebUrl, agentId } as MountConfig);\n}\n\nexport function mount(config: MountConfig): WidgetHandle {\n if (!config || !config.apiUrl || !config.publishableKey) {\n throw new Error(\n 'SurogatesWidget.mount: \"apiUrl\" and \"publishableKey\" are required.',\n );\n }\n\n const accent = config.accentColor ?? DEFAULT_ACCENT;\n let destroyed = false;\n\n // Host element + Shadow DOM: total style isolation in both directions.\n const host = document.createElement('div');\n host.setAttribute('data-surogates-widget', '');\n const shadow = host.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = WIDGET_STYLES;\n shadow.appendChild(style);\n\n const root = document.createElement('div');\n root.className = 'surg-root';\n shadow.appendChild(root);\n\n // Accent drives CSS custom properties on the root, so it can be re-applied\n // on update() without re-rendering anything else.\n const applyAccent = (accentColor?: string) => {\n const a = accentColor ?? DEFAULT_ACCENT;\n root.style.setProperty('--surg-accent', a);\n root.style.setProperty('--surg-accent-fg', accentForeground(a));\n };\n applyAccent(accent);\n\n // Attach to the page. Drop-in embeds are often placed in <head>, where\n // document.body doesn't exist yet — defer the append until the DOM is ready.\n const attach = () => (config.target ?? document.body)?.appendChild(host);\n if (config.target || document.body) {\n attach();\n } else {\n document.addEventListener(\n \"DOMContentLoaded\",\n () => {\n if (!destroyed) attach();\n },\n { once: true },\n );\n }\n\n const agent = new WebsiteAgent(config);\n\n let setOpen: ((open: boolean) => void) | undefined;\n let isOpen = config.inline || !!config.openByDefault;\n\n // Stable callbacks so re-rendering on update() doesn't re-fire the\n // component's open-control / open-change effects.\n const registerOpenControl = (fn: (open: boolean) => void) => {\n setOpen = fn;\n };\n const onOpenChange = (o: boolean) => {\n isOpen = o;\n };\n\n // Current config is mutable so update() can re-render appearance without\n // tearing down the agent (no re-bootstrap, no lost conversation).\n let current: MountConfig = config;\n const renderWidget = () => {\n render(\n <Widget\n agent={agent}\n config={current}\n registerOpenControl={registerOpenControl}\n onOpenChange={onOpenChange}\n />,\n root,\n );\n };\n renderWidget();\n\n // Best-effort session close when the visitor navigates away.\n const endOnUnload = () => {\n void agent.end();\n };\n window.addEventListener('beforeunload', endOnUnload);\n\n return {\n agent,\n open: () => setOpen?.(true),\n close: () => setOpen?.(false),\n toggle: () => setOpen?.(!isOpen),\n update: (partial) => {\n if (destroyed) return;\n current = { ...current, ...partial };\n if ('accentColor' in partial) applyAccent(current.accentColor);\n renderWidget();\n },\n destroy: () => {\n if (destroyed) return;\n destroyed = true;\n window.removeEventListener('beforeunload', endOnUnload);\n void agent.end();\n render(null, root);\n host.remove();\n },\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@invergent/website-widget",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.9",
|
|
4
4
|
"description": "AG-UI-compatible TypeScript client for the Surogates public-website channel. Wraps the publishable-key bootstrap, HttpOnly cookie, CSRF double-submit, and SSE streaming behind a standard AbstractAgent so widgets built for AG-UI work out of the box.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"surogates",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/translator.ts","../src/protocol.ts","../src/agent.ts"],"names":["EventType"],"mappings":"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","file":"chunk-MAUJDM7P.js","sourcesContent":["/**\n * Wire-protocol and SDK-version constants.\n *\n * ``PROTOCOL_VERSION`` is the integer the SDK sends to the server so\n * future breaking protocol changes can be negotiated or hard-failed at\n * bootstrap instead of silently corrupting running embeds.\n *\n * ``SDK_VERSION`` travels in an ``X-Surogates-Widget-Version`` header on\n * every request so server logs can correlate a buggy client build with\n * its error surface. Kept in sync with ``package.json`` manually; the\n * build pipeline can replace it at bundle time in a later phase.\n */\n\nexport const PROTOCOL_VERSION = 1;\n\nexport const SDK_VERSION = '0.1.0';\n\n// Header names we read/write. Lifted to constants so tests, the client,\n// and the protocol layer can't drift from each other.\nexport const CSRF_HEADER = 'X-CSRF-Token';\nexport const VERSION_HEADER = 'X-Surogates-Widget-Version';\n\n// URL segments of the website-channel API, relative to ``apiUrl``.\nexport const PATH_BOOTSTRAP = '/v1/website/sessions';\nexport const PATH_MESSAGES = (sessionId: string) =>\n `/v1/website/sessions/${sessionId}/messages`;\nexport const PATH_EVENTS = (sessionId: string, after: number) =>\n `/v1/website/sessions/${sessionId}/events?after=${after}`;\nexport const PATH_END = (sessionId: string) =>\n `/v1/website/sessions/${sessionId}/end`;\n\n// Surogates native event type strings the SSE stream carries. Source\n// of truth: ``surogates/session/events.py``. Duplicated here so the\n// translator can switch on them without a network of string literals\n// scattered through the code.\nexport const SURG_EVENT = {\n USER_MESSAGE: 'user.message',\n LLM_REQUEST: 'llm.request',\n LLM_RESPONSE: 'llm.response',\n LLM_THINKING: 'llm.thinking',\n LLM_DELTA: 'llm.delta',\n TOOL_CALL: 'tool.call',\n TOOL_RESULT: 'tool.result',\n SANDBOX_PROVISION: 'sandbox.provision',\n SANDBOX_EXECUTE: 'sandbox.execute',\n SANDBOX_RESULT: 'sandbox.result',\n SANDBOX_DESTROY: 'sandbox.destroy',\n SESSION_START: 'session.start',\n SESSION_PAUSE: 'session.pause',\n SESSION_RESUME: 'session.resume',\n SESSION_COMPLETE: 'session.complete',\n SESSION_FAIL: 'session.fail',\n SESSION_DONE: 'session.done',\n CONTEXT_COMPACT: 'context.compact',\n MEMORY_UPDATE: 'memory.update',\n EXPERT_DELEGATION: 'expert.delegation',\n EXPERT_RESULT: 'expert.result',\n EXPERT_FAILURE: 'expert.failure',\n POLICY_DENIED: 'policy.denied',\n POLICY_ALLOWED: 'policy.allowed',\n HARNESS_CRASH: 'harness.crash',\n} as const;\n","/**\n * Error taxonomy for the website-widget SDK.\n *\n * Consumers should not have to parse strings to tell \"the key is wrong\"\n * apart from \"the network blinked\" -- different remediations apply.\n * Every error thrown or emitted by the SDK derives from\n * :class:`SurogatesError` so a single ``instanceof`` catch is always\n * safe; the subclasses encode the specific failure mode.\n */\n\nexport class SurogatesError extends Error {\n /** HTTP status code when the error originated from a server response. */\n readonly status?: number;\n /** Raw server detail string, if any. */\n readonly detail?: string;\n\n constructor(message: string, opts?: { status?: number; detail?: string; cause?: unknown }) {\n super(message);\n this.name = 'SurogatesError';\n if (opts?.status !== undefined) this.status = opts.status;\n if (opts?.detail !== undefined) this.detail = opts.detail;\n if (opts?.cause !== undefined) (this as { cause?: unknown }).cause = opts.cause;\n }\n}\n\n/**\n * The publishable key is invalid, expired, or the request Origin is not\n * in the agent's allow-list. Non-retryable: the embed is misconfigured\n * and no amount of backoff will help.\n */\nexport class SurogatesAuthError extends SurogatesError {\n constructor(message: string, opts?: { status?: number; detail?: string }) {\n super(message, opts);\n this.name = 'SurogatesAuthError';\n }\n}\n\n/**\n * Rate-limited (HTTP 429) or the per-session message/token cap has\n * been reached. Retryable after a backoff; the SDK exposes this as a\n * distinct type so consumers can show a \"slow down\" indicator instead\n * of a generic failure.\n */\nexport class SurogatesRateLimitError extends SurogatesError {\n /** Seconds the server asked us to wait, if it included a Retry-After header. */\n readonly retryAfter?: number;\n\n constructor(message: string, opts?: { status?: number; retryAfter?: number; detail?: string }) {\n super(message, opts);\n this.name = 'SurogatesRateLimitError';\n if (opts?.retryAfter !== undefined) this.retryAfter = opts.retryAfter;\n }\n}\n\n/**\n * The protocol itself is the problem -- the SDK received a response it\n * can't interpret, or a cookie/CSRF invariant was violated. Indicates\n * an SDK-server version mismatch or a corrupt response; raises the\n * priority of whatever error reporting channel the host app has.\n */\nexport class SurogatesProtocolError extends SurogatesError {\n constructor(message: string, opts?: { status?: number; detail?: string; cause?: unknown }) {\n super(message, opts);\n this.name = 'SurogatesProtocolError';\n }\n}\n\n/**\n * Transport-level failure: network drop, CORS preflight refusal, DNS,\n * browser offline, 5xx from an ingress/load balancer. Retryable by\n * design; the SDK does not auto-retry today -- the caller should back\n * off and re-issue the operation (typically by re-invoking\n * ``runAgent``). A future revision may add internal exponential\n * backoff; consumers should continue to surface these errors to the\n * user rather than assume they'll be swallowed.\n */\nexport class SurogatesNetworkError extends SurogatesError {\n constructor(message: string, opts?: { cause?: unknown }) {\n super(message, opts);\n this.name = 'SurogatesNetworkError';\n }\n}\n","/**\n * Pure event translation: Surogates wire events → AG-UI ``BaseEvent``\\ s.\n *\n * The SSE stream delivered by ``/v1/website/sessions/{id}/events`` carries\n * event types defined in ``surogates/session/events.py``\n * (``user.message``, ``llm.delta``, ``llm.response``, ``tool.call``, ...).\n * AG-UI consumers expect a different, standardised vocabulary\n * (``TEXT_MESSAGE_START`` / ``CONTENT`` / ``END`` triads, ``TOOL_CALL_*``,\n * ``RUN_STARTED`` / ``RUN_FINISHED``, ``STATE_SNAPSHOT``, etc.). This\n * module is the contract.\n *\n * Design notes\n * ------------\n * * **Pure and state-carried.** ``Translator`` is a class only because\n * a few decisions need a tiny bit of memory (have we emitted the\n * running ``TextMessageStart`` yet? how many tool calls are\n * outstanding? did the last ``llm.response`` signal end-of-turn?).\n * It does not touch the network or any globals; every instance is a\n * single run's translation state and discardable after\n * ``RUN_FINISHED``.\n *\n * * **Chunks over triads.** AG-UI provides\n * ``TEXT_MESSAGE_CHUNK`` / ``TOOL_CALL_CHUNK`` convenience events\n * that expand to the START / CONTENT / END triad automatically via\n * the client-side ``transformChunks`` pipeline. Surogates already\n * streams in chunks (``llm.delta``, full-payload ``tool.call``), so\n * emitting chunk events keeps the translator short and matches the\n * data we actually have.\n *\n * * **Unknown → CUSTOM.** Platform-specific events we cannot map to\n * first-class AG-UI events (memory updates, saga step transitions,\n * session resets, context compaction) are emitted as AG-UI ``CUSTOM``\n * events with ``name`` set to the Surogates event type and ``value``\n * set to the raw payload. Consumers that care can match on\n * ``name``; consumers that don't simply ignore CUSTOM.\n *\n * * **End-of-turn heuristic.** Our server does not emit a\n * ``turn.complete`` event. Per-turn completion is inferred: an\n * ``llm.response`` whose payload indicates the model is done (no\n * pending tool calls, ``finish_reason`` of ``stop`` / ``end_turn``)\n * flips ``isTurnComplete`` to ``true``. The caller emits\n * ``RUN_FINISHED`` and closes the stream. A ``session.done`` SSE\n * sentinel also completes the turn.\n */\n\nimport type { BaseEvent } from '@ag-ui/client';\nimport { EventType } from '@ag-ui/client';\n\nimport { SURG_EVENT } from './constants.js';\n\n/** Minimal shape of a single SSE frame we read from the server. */\nexport interface SurogatesFrame {\n /** Monotonic server-side event id (cursor for reconnect). */\n id: number;\n /** Surogates event type (``user.message``, ``llm.delta``, etc.). */\n type: string;\n /** Parsed JSON payload from the ``data:`` line. */\n data: unknown;\n}\n\n/** Payload keys we pull off ``llm.response`` events to detect end-of-turn. */\ninterface LlmResponsePayload {\n content?: string;\n tool_calls?: unknown[];\n finish_reason?: string;\n stop_reason?: string;\n message_id?: string;\n id?: string;\n}\n\n/** Payload keys for ``llm.delta`` events. */\ninterface LlmDeltaPayload {\n delta?: string;\n content?: string;\n message_id?: string;\n}\n\n/** Payload keys for ``tool.call`` events. */\ninterface ToolCallPayload {\n tool_call_id?: string;\n name?: string;\n arguments?: unknown;\n}\n\n/** Payload keys for ``tool.result`` events. */\ninterface ToolResultPayload {\n tool_call_id?: string;\n name?: string;\n content?: string;\n}\n\n/** Payload keys for ``llm.thinking`` events. */\ninterface LlmThinkingPayload {\n delta?: string;\n content?: string;\n message_id?: string;\n}\n\n/** Payload keys for ``policy.denied`` events. */\ninterface PolicyDeniedPayload {\n tool?: string;\n reason?: string;\n}\n\n/** Payload keys for expert delegation events. */\ninterface ExpertDelegationPayload {\n expert_name?: string;\n task?: string;\n}\n\ninterface ExpertResultPayload {\n expert_name?: string;\n result?: unknown;\n}\n\nconst END_OF_TURN_FINISH_REASONS = new Set(['stop', 'end_turn', 'length']);\n\n/**\n * Finish reasons that explicitly signal \"more work is coming\" --\n * currently just OpenAI-style ``tool_calls``. Treated as an\n * affirmative negation of end-of-turn even if the provider happens\n * to emit an empty ``tool_calls`` array alongside it (some adapters\n * do). Kept as a set so future provider quirks can be added without\n * touching the detection logic.\n */\nconst CONTINUATION_FINISH_REASONS = new Set(['tool_calls', 'function_call', 'tool_use']);\n\n/**\n * Stateful translator for one run.\n *\n * The caller is expected to build a fresh instance per ``runAgent`` turn\n * and discard it after the run finishes; every method is idempotent and\n * thread-safe only in the single-SSE-consumer sense (JavaScript events\n * are single-threaded).\n */\nexport class Translator {\n /**\n * Running message id for the current streaming assistant message.\n * Re-used across ``llm.delta`` frames so every chunk targets the\n * same AG-UI message -- that's what makes the client-side\n * ``transformChunks`` stitch them into one logical message.\n */\n private currentMessageId: string | undefined;\n\n /**\n * Running message id for the current reasoning stream, if any.\n * Kept separate from ``currentMessageId`` so reasoning deltas never\n * accidentally merge into the assistant content stream.\n */\n private currentReasoningId: string | undefined;\n\n /**\n * Tool calls that have been announced (``TOOL_CALL_CHUNK`` emitted)\n * but have not yet received a matching ``tool.result``. Non-empty\n * means the turn is mid-execution even if an ``llm.response`` looks\n * final.\n */\n private pendingToolCalls = new Set<string>();\n\n /** Set once we've seen an end-of-turn signal. */\n private _turnComplete = false;\n\n get isTurnComplete(): boolean {\n return this._turnComplete;\n }\n\n /**\n * Translate one SSE frame into zero or more AG-UI events.\n *\n * Returns an empty array when the frame carries no user-visible\n * information (internal bookkeeping events like ``harness.wake``).\n * Returns multiple events when a single Surogates event corresponds\n * to a chunk triad that the AG-UI client can't reconstruct on its\n * own -- though we try to use chunk events to keep this to one\n * output event per input in the common path.\n */\n translate(frame: SurogatesFrame): BaseEvent[] {\n switch (frame.type) {\n case SURG_EVENT.LLM_DELTA:\n return this.handleLlmDelta(frame);\n\n case SURG_EVENT.LLM_RESPONSE:\n return this.handleLlmResponse(frame);\n\n case SURG_EVENT.LLM_THINKING:\n return this.handleLlmThinking(frame);\n\n case SURG_EVENT.TOOL_CALL:\n return this.handleToolCall(frame);\n\n case SURG_EVENT.TOOL_RESULT:\n return this.handleToolResult(frame);\n\n case SURG_EVENT.POLICY_DENIED:\n return this.handlePolicyDenied(frame);\n\n case SURG_EVENT.EXPERT_DELEGATION:\n return this.handleExpertDelegation(frame);\n\n case SURG_EVENT.EXPERT_RESULT:\n return this.handleExpertResult(frame);\n\n case SURG_EVENT.EXPERT_FAILURE:\n return this.asCustom(frame);\n\n case SURG_EVENT.SESSION_FAIL:\n case SURG_EVENT.HARNESS_CRASH:\n return this.handleTerminalFailure(frame);\n\n case SURG_EVENT.SESSION_DONE:\n case SURG_EVENT.SESSION_COMPLETE:\n this._turnComplete = true;\n return [];\n\n // Frames we do not surface to AG-UI consumers: internal\n // orchestration (``user.message`` is already in the consumer's\n // own ``agent.messages``; ``llm.request``, ``session.start``,\n // ``sandbox.*``, ``harness.wake`` are implementation details).\n case SURG_EVENT.USER_MESSAGE:\n case SURG_EVENT.LLM_REQUEST:\n case SURG_EVENT.SESSION_START:\n case SURG_EVENT.SESSION_PAUSE:\n case SURG_EVENT.SESSION_RESUME:\n case SURG_EVENT.SANDBOX_PROVISION:\n case SURG_EVENT.SANDBOX_EXECUTE:\n case SURG_EVENT.SANDBOX_RESULT:\n case SURG_EVENT.SANDBOX_DESTROY:\n case SURG_EVENT.POLICY_ALLOWED:\n return [];\n\n // Everything else (``memory.update``, ``context.compact``,\n // saga events, future server additions) is forwarded as CUSTOM\n // so advanced consumers that want that visibility can still\n // subscribe -- without us pre-committing to a particular AG-UI\n // shape for a Surogates-specific event that might evolve.\n default:\n return this.asCustom(frame);\n }\n }\n\n // ------------------------------------------------------------------\n // Individual handlers\n // ------------------------------------------------------------------\n\n private handleLlmDelta(frame: SurogatesFrame): BaseEvent[] {\n const payload = (frame.data ?? {}) as LlmDeltaPayload;\n const delta = payload.delta ?? payload.content ?? '';\n if (!delta) return [];\n\n // Chunks must carry a stable messageId on the FIRST chunk so\n // transformChunks can open the TextMessageStart; later chunks can\n // reuse the same id. Surogates streams multiple assistant turns\n // in one session, so we mint a new id whenever we've closed one\n // (set back to undefined in handleLlmResponse).\n const messageId = this.currentMessageId ?? payload.message_id ?? this.mintMessageId(frame.id);\n this.currentMessageId = messageId;\n\n return [\n {\n type: EventType.TEXT_MESSAGE_CHUNK,\n messageId,\n role: 'assistant',\n delta,\n } as BaseEvent,\n ];\n }\n\n private handleLlmResponse(frame: SurogatesFrame): BaseEvent[] {\n const payload = (frame.data ?? {}) as LlmResponsePayload;\n const events: BaseEvent[] = [];\n\n // Close any open assistant message by resetting the current id.\n // transformChunks will emit TEXT_MESSAGE_END when the next chunk\n // targets a new messageId (or the stream completes). If no delta\n // was seen but the response carries content, emit a single chunk\n // so downstream consumers get something to render.\n if (!this.currentMessageId && payload.content) {\n const messageId = payload.message_id ?? payload.id ?? this.mintMessageId(frame.id);\n events.push({\n type: EventType.TEXT_MESSAGE_CHUNK,\n messageId,\n role: 'assistant',\n delta: payload.content,\n } as BaseEvent);\n }\n this.currentMessageId = undefined;\n\n // End-of-turn detection is the conjunction of three guards:\n // 1. ``finish_reason`` does NOT say \"more work is coming\". Some\n // provider adapters emit a bare ``llm.response`` with\n // ``finish_reason: 'tool_calls'`` and an empty ``tool_calls``\n // array when they stream the \"I'll use a tool now\" sentinel\n // separately from the tool definitions. Treating this as\n // end-of-turn would flip us done before the tool calls even\n // arrive. The positive check on ``CONTINUATION_FINISH_REASONS``\n // rules that out first.\n // 2. The ``tool_calls`` array is empty. If the response carries\n // any tool calls at all, more frames follow.\n // 3. No tool calls that were already announced are still pending\n // a ``tool.result``. Belt-and-suspenders: if the server sends\n // ``llm.response`` before all tool results drain, we keep the\n // stream open until they do.\n const toolCalls = Array.isArray(payload.tool_calls) ? payload.tool_calls : [];\n const finish = payload.finish_reason ?? payload.stop_reason ?? '';\n if (CONTINUATION_FINISH_REASONS.has(finish)) {\n return events;\n }\n const modelDone =\n toolCalls.length === 0 && (finish === '' || END_OF_TURN_FINISH_REASONS.has(finish));\n if (modelDone && this.pendingToolCalls.size === 0) {\n this._turnComplete = true;\n }\n\n return events;\n }\n\n private handleLlmThinking(frame: SurogatesFrame): BaseEvent[] {\n const payload = (frame.data ?? {}) as LlmThinkingPayload;\n const delta = payload.delta ?? payload.content ?? '';\n if (!delta) return [];\n\n const messageId = this.currentReasoningId ?? payload.message_id ?? this.mintMessageId(frame.id);\n const isNew = this.currentReasoningId !== messageId;\n this.currentReasoningId = messageId;\n\n const events: BaseEvent[] = [];\n if (isNew) {\n events.push({\n type: EventType.REASONING_START,\n messageId,\n } as BaseEvent);\n events.push({\n type: EventType.REASONING_MESSAGE_START,\n messageId,\n role: 'reasoning',\n } as BaseEvent);\n }\n events.push({\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId,\n delta,\n } as BaseEvent);\n return events;\n }\n\n private handleToolCall(frame: SurogatesFrame): BaseEvent[] {\n const payload = (frame.data ?? {}) as ToolCallPayload;\n const toolCallId = payload.tool_call_id ?? `tc-${frame.id}`;\n const toolCallName = payload.name ?? 'unknown_tool';\n\n // Close any running reasoning/text message first so the tool call\n // slots in cleanly between them. We don't emit explicit END\n // events because transformChunks handles that when messageId\n // switches to undefined; we just clear our running ids.\n this.currentMessageId = undefined;\n this.currentReasoningId = undefined;\n\n this.pendingToolCalls.add(toolCallId);\n\n // Surogates gives us the FULL arguments in one frame; emit as a\n // single TOOL_CALL_CHUNK which the AG-UI client expands into\n // START + ARGS + END.\n const args =\n typeof payload.arguments === 'string'\n ? payload.arguments\n : JSON.stringify(payload.arguments ?? {});\n\n return [\n {\n type: EventType.TOOL_CALL_CHUNK,\n toolCallId,\n toolCallName,\n delta: args,\n } as BaseEvent,\n ];\n }\n\n private handleToolResult(frame: SurogatesFrame): BaseEvent[] {\n const payload = (frame.data ?? {}) as ToolResultPayload;\n const toolCallId = payload.tool_call_id ?? `tc-${frame.id}`;\n const content = payload.content ?? '';\n\n this.pendingToolCalls.delete(toolCallId);\n\n // AG-UI's TOOL_CALL_RESULT needs a ``messageId`` on the tool\n // message it lands on in the assistant history; we synthesise a\n // per-call id so the AG-UI message reducer can link result to\n // call. Consumers that need stable message ids across reconnects\n // can pull them from ``frame.id``.\n return [\n {\n type: EventType.TOOL_CALL_RESULT,\n messageId: `tr-${frame.id}`,\n toolCallId,\n content,\n role: 'tool',\n } as BaseEvent,\n ];\n }\n\n private handlePolicyDenied(frame: SurogatesFrame): BaseEvent[] {\n const payload = (frame.data ?? {}) as PolicyDeniedPayload;\n // Policy denials are informational -- the LLM still continues and\n // the next ``llm.response`` will explain the refusal in natural\n // language. Surface as CUSTOM so UIs can render a \"blocked\" badge\n // without hijacking the run lifecycle.\n return [\n {\n type: EventType.CUSTOM,\n name: SURG_EVENT.POLICY_DENIED,\n value: { tool: payload.tool, reason: payload.reason },\n } as BaseEvent,\n ];\n }\n\n private handleExpertDelegation(frame: SurogatesFrame): BaseEvent[] {\n const payload = (frame.data ?? {}) as ExpertDelegationPayload;\n const name = payload.expert_name ?? 'expert';\n // Expert delegation is a natural fit for STEP_STARTED -- it's a\n // bounded sub-task the host can render as a progress row. Kept\n // paired with STEP_FINISHED in ``handleExpertResult``.\n return [\n {\n type: EventType.STEP_STARTED,\n stepName: `expert:${name}`,\n } as BaseEvent,\n ];\n }\n\n private handleExpertResult(frame: SurogatesFrame): BaseEvent[] {\n const payload = (frame.data ?? {}) as ExpertResultPayload;\n const name = payload.expert_name ?? 'expert';\n return [\n {\n type: EventType.STEP_FINISHED,\n stepName: `expert:${name}`,\n } as BaseEvent,\n ];\n }\n\n private handleTerminalFailure(frame: SurogatesFrame): BaseEvent[] {\n this._turnComplete = true;\n const payload = (frame.data ?? {}) as { error?: string; message?: string };\n const message = payload.error ?? payload.message ?? `${frame.type} fired`;\n return [\n {\n type: EventType.RUN_ERROR,\n message,\n code: frame.type,\n } as BaseEvent,\n ];\n }\n\n private asCustom(frame: SurogatesFrame): BaseEvent[] {\n return [\n {\n type: EventType.CUSTOM,\n name: frame.type,\n value: frame.data,\n } as BaseEvent,\n ];\n }\n\n /**\n * Generate a stable message id derived from the triggering frame's\n * event id. The event id is globally unique per session, so using\n * it as a suffix guarantees uniqueness without pulling in ``uuid``.\n */\n private mintMessageId(frameId: number): string {\n return `msg-${frameId}`;\n }\n}\n","/**\n * HTTP + SSE wire layer.\n *\n * Pure functions that talk to the website channel's three endpoints,\n * isolated here so :class:`WebsiteAgentClient` can stay focused on\n * state-machine orchestration. All fetches use ``credentials:\n * 'include'`` so the browser sends the HttpOnly session cookie that\n * ``bootstrap`` set -- without it the message/SSE/end routes return\n * 401 and the whole channel falls apart.\n *\n * SSE is handled by the native ``EventSource`` rather than a custom\n * fetch-based parser because the native implementation is zero bytes,\n * auto-reconnects on network drops, and respects browser-internal\n * backoff heuristics. The one thing it can't do is resume with a\n * server-side cursor, so we manage the ``?after=N`` reopen logic\n * explicitly in :class:`WebsiteAgentClient`.\n */\n\nimport {\n CSRF_HEADER,\n PATH_BOOTSTRAP,\n PATH_END,\n PATH_EVENTS,\n PATH_MESSAGES,\n VERSION_HEADER,\n SDK_VERSION,\n} from './constants.js';\nimport {\n SurogatesAuthError,\n SurogatesNetworkError,\n SurogatesProtocolError,\n SurogatesRateLimitError,\n} from './errors.js';\n\nexport interface BootstrapResult {\n sessionId: string;\n csrfToken: string;\n expiresAt: number;\n agentName: string;\n}\n\n/**\n * Append ``?agent_id=`` (or ``&agent_id=``) when an explicit id is set.\n *\n * Per-agent deployments resolve the agent from the request ``Host``\n * subdomain on every call, so the embed never needs this. Shared/\n * multi-tenant runtimes (and the Studio preview) aren't reached through\n * the per-agent host, and the server resolves the agent on *every*\n * website request (the rate limiter depends on the agent context), not\n * just bootstrap -- so the id must ride on all four endpoints.\n *\n * BRIDGE (not the long-term design): the publishable key already scopes to\n * one agent, so the deeper fix is server-side resolution of the agent from\n * the key (a key->agent registry), after which shared mode would need no\n * query param at all. Until that lands, every endpoint must thread the id.\n */\nfunction withAgentId(path: string, agentId?: string): string {\n if (!agentId) return path;\n const sep = path.includes('?') ? '&' : '?';\n return `${path}${sep}agent_id=${encodeURIComponent(agentId)}`;\n}\n\n/**\n * Shape the website channel returns from ``POST /v1/website/sessions``.\n * Mirrors the pydantic ``BootstrapResponse`` server-side; kept separate\n * from :class:`BootstrapResult` (camelCase) so future protocol drift\n * only needs to move here, not through every consumer.\n */\ninterface RawBootstrapBody {\n session_id: string;\n csrf_token: string;\n expires_at: number;\n agent_name: string;\n}\n\n/** Fetch wrapper that normalises every HTTP failure into a typed error. */\nasync function doFetch(\n url: string,\n init: RequestInit & { headers?: Record<string, string> },\n): Promise<Response> {\n let response: Response;\n try {\n response = await fetch(url, {\n ...init,\n credentials: 'include',\n headers: {\n ...init.headers,\n [VERSION_HEADER]: SDK_VERSION,\n },\n });\n } catch (cause) {\n // ``fetch`` only rejects on true network failure / CORS preflight\n // refusal. HTTP error responses resolve.\n throw new SurogatesNetworkError('Network request failed', { cause });\n }\n return response;\n}\n\n/** Pull the ``detail`` string out of the server's error body, best-effort. */\nasync function extractDetail(response: Response): Promise<string | undefined> {\n try {\n const body = (await response.json()) as { detail?: unknown };\n return typeof body.detail === 'string' ? body.detail : undefined;\n } catch {\n return undefined;\n }\n}\n\nasync function raiseForStatus(response: Response, action: string): Promise<never> {\n const detail = await extractDetail(response);\n if (response.status === 401 || response.status === 403) {\n throw new SurogatesAuthError(`${action} rejected (${response.status})`, {\n status: response.status,\n ...(detail !== undefined && { detail }),\n });\n }\n if (response.status === 429) {\n const retryAfterHeader = response.headers.get('Retry-After');\n const retryAfter = retryAfterHeader ? Number(retryAfterHeader) : undefined;\n throw new SurogatesRateLimitError(`${action} rate-limited`, {\n status: response.status,\n ...(retryAfter !== undefined && Number.isFinite(retryAfter) && { retryAfter }),\n ...(detail !== undefined && { detail }),\n });\n }\n // 5xx responses are transport-layer hiccups -- an ingress, a load\n // balancer, or a restarting worker -- not SDK/server protocol\n // drift. Surface them as :class:`SurogatesNetworkError` so\n // consumers treat them as retryable (matching the documented\n // semantics of that class) and don't escalate them as priority-1\n // diagnostics the way a genuine ``SurogatesProtocolError`` warrants.\n if (response.status >= 500) {\n throw new SurogatesNetworkError(\n `${action} failed (${response.status}${detail ? `: ${detail}` : ''})`,\n );\n }\n throw new SurogatesProtocolError(`${action} failed (${response.status})`, {\n status: response.status,\n ...(detail !== undefined && { detail }),\n });\n}\n\n/** ``POST /v1/website/sessions`` with a publishable key.\n *\n * ``agentId`` is optional. In a per-agent deployment the server resolves\n * the agent from the request ``Host`` subdomain, so the embed never needs\n * it. Shared/multi-tenant runtimes (and the Studio live preview) that\n * aren't reached through the per-agent host can pass it explicitly; it is\n * appended as the ``?agent_id=`` query param the server's resolver accepts.\n * Only the bootstrap needs it -- subsequent calls are cookie-bound.\n */\nexport async function bootstrap(\n apiUrl: string,\n publishableKey: string,\n agentId?: string,\n): Promise<BootstrapResult> {\n const response = await doFetch(apiUrl + withAgentId(PATH_BOOTSTRAP, agentId), {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${publishableKey}`,\n 'Content-Type': 'application/json',\n },\n });\n // Accept any 2xx -- the route currently declares 201, but a future\n // server change to 200 (or a proxy that strips the status to 200)\n // must not silently break the SDK. Non-2xx still funnels through\n // ``raiseForStatus`` to get the right typed error back.\n if (!response.ok) {\n await raiseForStatus(response, 'Bootstrap');\n }\n let body: RawBootstrapBody;\n try {\n body = (await response.json()) as RawBootstrapBody;\n } catch (cause) {\n throw new SurogatesProtocolError('Bootstrap returned non-JSON body', { cause });\n }\n if (!body || typeof body.session_id !== 'string' || typeof body.csrf_token !== 'string') {\n throw new SurogatesProtocolError('Bootstrap response missing required fields');\n }\n return {\n sessionId: body.session_id,\n csrfToken: body.csrf_token,\n expiresAt: Number(body.expires_at) || 0,\n agentName: body.agent_name ?? '',\n };\n}\n\n/** ``POST /v1/website/sessions/{id}/messages`` with cookie + CSRF. */\nexport async function sendMessage(\n apiUrl: string,\n sessionId: string,\n csrfToken: string,\n content: string,\n agentId?: string,\n): Promise<{ eventId: number }> {\n const response = await doFetch(apiUrl + withAgentId(PATH_MESSAGES(sessionId), agentId), {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [CSRF_HEADER]: csrfToken,\n },\n body: JSON.stringify({ content }),\n });\n // Any 2xx is a successful enqueue; the route declares 202 today\n // but we don't want to break if it's ever relaxed to 200.\n if (!response.ok) {\n await raiseForStatus(response, 'Send message');\n }\n const body = (await response.json().catch(() => ({}))) as { event_id?: number };\n return { eventId: Number(body.event_id ?? 0) };\n}\n\n/** ``POST /v1/website/sessions/{id}/end``. Best-effort; swallows errors. */\nexport async function endSession(\n apiUrl: string,\n sessionId: string,\n csrfToken: string,\n agentId?: string,\n): Promise<void> {\n try {\n await doFetch(apiUrl + withAgentId(PATH_END(sessionId), agentId), {\n method: 'POST',\n headers: { [CSRF_HEADER]: csrfToken },\n });\n } catch {\n // The local cookie/state is always cleared by the caller regardless.\n // A failed end() is not worth surfacing -- the session will idle out.\n }\n}\n\n/** Open an SSE connection at ``?after=<cursor>``. Caller owns the close. */\nexport function openEventStream(\n apiUrl: string,\n sessionId: string,\n cursor: number,\n agentId?: string,\n): EventSource {\n const url = apiUrl + withAgentId(PATH_EVENTS(sessionId, cursor), agentId);\n // ``withCredentials`` is the non-obvious flag that makes EventSource\n // send the session cookie cross-origin. Without it the server sees\n // an unauthenticated request and returns 401, and the EventSource\n // quietly goes into reconnect loops without surfacing the reason.\n return new EventSource(url, { withCredentials: true });\n}\n","/**\n * AG-UI ``AbstractAgent`` implementation for the Surogates public-website channel.\n *\n * A ``WebsiteAgent`` bridges the channel-specific HTTP protocol\n * (publishable-key bootstrap, HttpOnly cookie, CSRF double-submit,\n * ``/v1/website/sessions/{id}/events`` SSE) onto AG-UI's standard\n * ``run()`` contract so any AG-UI-aware host app -- CopilotKit,\n * custom chat UIs, LangGraph-style clients -- can talk to a Surogates\n * backend with no custom glue beyond instantiation.\n *\n * Responsibilities\n * ----------------\n * * **Lazy bootstrap.** The first ``runAgent`` call POSTs to\n * ``/v1/website/sessions`` to exchange the publishable key for a\n * session cookie + CSRF token; subsequent calls reuse them. A cookie\n * expiry (HTTP 401) triggers a transparent re-bootstrap so the host\n * app never has to manage auth state.\n *\n * * **Message → POST.** On each ``runAgent`` call the agent finds the\n * latest user message in ``input.messages`` and posts its content to\n * ``/v1/website/sessions/{id}/messages`` with the CSRF header.\n *\n * * **SSE → AG-UI.** The channel's native event stream is converted\n * to AG-UI events by :class:`Translator` frame-by-frame. The agent\n * opens a fresh EventSource per run (with ``?after=<cursor>`` to\n * skip already-processed frames) and closes it after a short grace\n * drain on turn completion so trailing server frames (late\n * ``memory.update``, ``session.done``) advance the cursor rather\n * than replaying on the next run.\n *\n * * **RUN lifecycle.** AG-UI requires every run to be bracketed by\n * ``RUN_STARTED`` and exactly one of ``RUN_FINISHED`` / ``RUN_ERROR``.\n * Surogates has no such sentinel on the wire, so the agent emits\n * them itself; a per-run ``terminated`` flag guarantees at most one\n * terminal event even under concurrent SSE ``onerror`` + translator\n * end-of-turn detection.\n */\n\nimport type {\n AgentConfig,\n BaseEvent,\n Message,\n RunAgentInput,\n} from '@ag-ui/client';\nimport { AbstractAgent, AGUIError, EventType } from '@ag-ui/client';\nimport type { Subscriber } from 'rxjs';\nimport { Observable } from 'rxjs';\n\nimport {\n type BootstrapResult,\n bootstrap,\n endSession,\n openEventStream,\n sendMessage,\n} from './protocol.js';\nimport { type SurogatesFrame, Translator } from './translator.js';\nimport { SurogatesAuthError } from './errors.js';\nimport { SURG_EVENT } from './constants.js';\n\n/**\n * Constructor config for :class:`WebsiteAgent`. Extends AG-UI's\n * base ``AgentConfig`` with the two Surogates-specific fields the\n * bootstrap needs; everything else (``agentId``, ``threadId``,\n * ``initialMessages``, ``initialState``, ``debug``) is inherited\n * unchanged so consumers reuse the AG-UI patterns they already know.\n */\nexport interface WebsiteAgentConfig extends AgentConfig {\n /** Base URL of the Surogates API, e.g. ``https://agent.acme.com``. */\n apiUrl: string;\n /** Publishable key issued by ops (``surg_wk_...``). */\n publishableKey: string;\n}\n\n/** Set of Surogates SSE event names the agent subscribes to by default. */\nconst SURG_EVENT_NAMES: readonly string[] = Object.values(SURG_EVENT);\n\n/**\n * How long to keep the EventSource open after the translator flags\n * end-of-turn, giving the server a window to deliver trailing frames\n * (late ``memory.update``, ``session.done``, etc.) that belong to the\n * closing run rather than the next one. During this window we still\n * advance ``this.cursor`` but no longer emit to the subscriber, which\n * preserves AG-UI's \"one terminal event per run\" contract while also\n * preventing replay of those frames as spurious ``CUSTOM`` events on\n * the next run.\n */\nconst DRAIN_WINDOW_MS = 250;\n\nexport class WebsiteAgent extends AbstractAgent {\n readonly apiUrl: string;\n readonly publishableKey: string;\n\n /** Cached bootstrap result. ``undefined`` until the first ``run()``. */\n private bootstrapResult: BootstrapResult | undefined;\n /** Monotonic cursor across runs. Passed to the SSE ``?after=`` param. */\n private cursor = 0;\n\n constructor(config: WebsiteAgentConfig) {\n super(config);\n if (!config.apiUrl) {\n throw new AGUIError('WebsiteAgent: \"apiUrl\" is required.');\n }\n if (!config.publishableKey) {\n throw new AGUIError('WebsiteAgent: \"publishableKey\" is required.');\n }\n // Strip a trailing slash so ``${apiUrl}/v1/...`` never double-slashes.\n this.apiUrl = config.apiUrl.replace(/\\/+$/, '');\n this.publishableKey = config.publishableKey;\n }\n\n /**\n * Ensure we have a live session cookie + CSRF token.\n *\n * Idempotent; returns the cached result when one already exists.\n * ``bootstrap()`` is the only call that requires the publishable\n * key to travel in an Authorization header, so callers that want\n * to eagerly validate their configuration (e.g. to surface a\n * setup error at widget-load time) can invoke this directly.\n */\n async ensureBootstrapped(): Promise<BootstrapResult> {\n if (this.bootstrapResult) return this.bootstrapResult;\n this.bootstrapResult = await bootstrap(\n this.apiUrl,\n this.publishableKey,\n this.agentId,\n );\n return this.bootstrapResult;\n }\n\n /**\n * Close the current session on the server side and drop local state.\n *\n * Callers should invoke this when the visitor closes the chat UI\n * so the server can mark the session complete instead of waiting\n * for the idle-reset timer. Safe to call when no session has been\n * bootstrapped yet.\n */\n async end(): Promise<void> {\n const current = this.bootstrapResult;\n if (!current) return;\n this.bootstrapResult = undefined;\n this.cursor = 0;\n await endSession(this.apiUrl, current.sessionId, current.csrfToken, this.agentId);\n }\n\n /**\n * Implementation of the AG-UI ``run()`` contract.\n *\n * Returns a cold observable: one subscription per run. AG-UI's\n * ``runAgent`` pipeline pipes this through chunk transformation,\n * verification, and subscriber notifications before updating\n * ``this.messages``.\n */\n run(input: RunAgentInput): Observable<BaseEvent> {\n return new Observable<BaseEvent>((subscriber) => {\n // Per-run mutable state owned by the factory closure. The\n // async ``driveRun`` populates ``ctx.source`` once the SSE is\n // open; the teardown at the bottom accesses whichever source\n // is current (swapped on cookie-recovery) and closes it.\n const ctx: RunContext = {\n terminated: false,\n source: undefined,\n drainTimer: undefined,\n };\n\n void this.driveRun(input, subscriber, ctx).catch((err) => {\n // Any rejection from driveRun that wasn't already turned into\n // a RUN_ERROR is a genuine bug; surface it through the RxJS\n // error channel rather than swallow.\n if (!ctx.terminated) {\n ctx.terminated = true;\n subscriber.error(err);\n }\n });\n\n return () => {\n // RxJS invokes this teardown on ``subscriber.complete()``,\n // ``subscriber.error()``, and on external ``unsubscribe()``\n // (whichever fires first). Distinguishing them matters\n // because ``finish()`` wants the EventSource to stay OPEN\n // for the drain window -- cursor advancement relies on\n // trailing frames still arriving through the listeners.\n //\n // Rule: if there's a drain timer pending, leave the source\n // alone; the timer will close it. Otherwise (error, abort)\n // close immediately. Either way, mark terminated so any\n // in-flight frame callbacks skip subscriber emission.\n ctx.terminated = true;\n if (ctx.drainTimer !== undefined) return;\n closeEventSource(ctx.source);\n ctx.source = undefined;\n };\n });\n }\n\n /**\n * Async body of the observable factory.\n *\n * Emits ``RUN_STARTED`` up front so every downstream failure is\n * well-bracketed by the AG-UI lifecycle. ``RUN_FINISHED`` is\n * emitted by the SSE listeners when the translator detects end-of-\n * turn; ``RUN_ERROR`` is emitted from the catch below when bootstrap\n * or message-send fails before the SSE has any chance to close out.\n *\n * The ``ctx.terminated`` guard is the single enforcement point for\n * AG-UI's \"exactly one terminal event\" invariant. Every emission\n * site checks it before calling ``subscriber.next`` with RUN_FINISHED\n * or RUN_ERROR, and every terminal call sets it to true first.\n */\n private async driveRun(\n input: RunAgentInput,\n subscriber: Subscriber<BaseEvent>,\n ctx: RunContext,\n ): Promise<void> {\n if (ctx.terminated) return;\n subscriber.next({\n type: EventType.RUN_STARTED,\n threadId: input.threadId,\n runId: input.runId,\n } as BaseEvent);\n\n try {\n const userText = this.extractUserText(input.messages);\n const boot = await this.ensureBootstrapped();\n\n // Open SSE BEFORE sending the message so no events race past us.\n // Browsers drop Authorization headers on EventSource, but the\n // session cookie rides along automatically (the route\n // authenticates off the cookie + the Origin re-check, not off\n // Bearer headers).\n this.replaceStreamSource(\n ctx, boot.sessionId, this.cursor, new Translator(), subscriber, input,\n );\n\n // POST the visitor's message. One retry on 401 (cookie expiry)\n // via a re-bootstrap; on retry the SSE stream is no longer tied\n // to the right session so we reopen it against the freshly\n // minted session id. A **fresh ``Translator``** is handed to\n // the replacement stream -- the old instance may have absorbed\n // previous-session trailing frames on the now-doomed SSE\n // between SSE-open and the send failing, and its internal\n // ``_turnComplete`` / ``pendingToolCalls`` state belongs to a\n // session that no longer exists. Reusing it would risk\n // flipping end-of-turn on the first frame of the new stream.\n await this.sendWithCookieRecovery(boot, userText, (fresh) => {\n this.replaceStreamSource(\n ctx, fresh.sessionId, 0, new Translator(), subscriber, input,\n );\n });\n } catch (err) {\n if (ctx.terminated) return;\n ctx.terminated = true;\n const message = err instanceof Error ? err.message : String(err);\n const code = err instanceof SurogatesAuthError ? 'auth' : 'error';\n subscriber.next({\n type: EventType.RUN_ERROR,\n message,\n code,\n } as BaseEvent);\n closeEventSource(ctx.source);\n ctx.source = undefined;\n subscriber.complete();\n }\n }\n\n /**\n * Tear down whatever EventSource is currently live on ``ctx`` and\n * open a fresh one against *sessionId* starting at *after*. Called\n * both for the initial stream and after cookie recovery. Always\n * clears the stale ``onerror`` first so the old source can't race\n * a ``RUN_ERROR`` into the subscriber after it's been replaced.\n */\n private replaceStreamSource(\n ctx: RunContext,\n sessionId: string,\n after: number,\n translator: Translator,\n subscriber: Subscriber<BaseEvent>,\n input: RunAgentInput,\n ): void {\n closeEventSource(ctx.source);\n const source = openEventStream(this.apiUrl, sessionId, after, this.agentId);\n ctx.source = source;\n this.attachStreamListeners(source, translator, subscriber, input, ctx);\n }\n\n /**\n * Try ``sendMessage``; on auth failure re-bootstrap once and retry.\n *\n * The re-bootstrap mints a new session id, so the caller passes an\n * ``onRebootstrap`` callback that reopens the SSE stream against the\n * fresh session before the retry. Visitor continuity across\n * bootstraps is a deliberate non-feature for v1; the new session\n * starts empty. Only frames emitted on the OLD session between\n * SSE-open and send-failure are lost -- and those are always\n * previous-turn events, since the current turn's ``sendMessage`` is\n * what 401-ed.\n */\n private async sendWithCookieRecovery(\n boot: BootstrapResult,\n content: string,\n onRebootstrap: (fresh: BootstrapResult) => void,\n ): Promise<void> {\n try {\n await sendMessage(this.apiUrl, boot.sessionId, boot.csrfToken, content, this.agentId);\n return;\n } catch (err) {\n if (!(err instanceof SurogatesAuthError)) throw err;\n this.bootstrapResult = undefined;\n this.cursor = 0;\n const fresh = await this.ensureBootstrapped();\n onRebootstrap(fresh);\n await sendMessage(this.apiUrl, fresh.sessionId, fresh.csrfToken, content, this.agentId);\n }\n }\n\n /**\n * Wire up the EventSource to translate and forward every frame,\n * and terminate the observable when the translator flags\n * end-of-turn or a transport failure occurs.\n *\n * After the translator flips end-of-turn we emit ``RUN_FINISHED``\n * immediately, then keep the stream open for ``DRAIN_WINDOW_MS``\n * so trailing frames (late ``memory.update``, the sentinel\n * ``session.done``) advance ``this.cursor`` rather than arriving\n * on the next run. The ``ctx.terminated`` gate stops additional\n * subscriber emissions during the drain.\n */\n private attachStreamListeners(\n source: EventSource,\n translator: Translator,\n subscriber: Subscriber<BaseEvent>,\n input: RunAgentInput,\n ctx: RunContext,\n ): void {\n const scheduleDrainClose = () => {\n // ``globalThis`` so the SDK works in both browser (returns\n // number) and Node (returns Timeout) without a type cast.\n if (ctx.drainTimer !== undefined) globalThis.clearTimeout(ctx.drainTimer);\n ctx.drainTimer = globalThis.setTimeout(() => {\n // Null the timer ref BEFORE closing the source so a\n // simultaneous ``replaceStreamSource`` (from cookie recovery)\n // doesn't see a stale timer belonging to the old stream.\n ctx.drainTimer = undefined;\n closeEventSource(source);\n }, DRAIN_WINDOW_MS);\n };\n\n const finish = () => {\n if (ctx.terminated) return;\n ctx.terminated = true;\n subscriber.next({\n type: EventType.RUN_FINISHED,\n threadId: input.threadId,\n runId: input.runId,\n } as BaseEvent);\n // Schedule the drain BEFORE ``subscriber.complete()`` so the\n // teardown invoked by RxJS sees ``ctx.drainTimer`` set and\n // leaves the source alone. Reverse the order and the teardown\n // runs with ``drainTimer === undefined``, closes the source\n // immediately, and the subsequently-scheduled timer finds\n // nothing to close -- turning the drain into a no-op and\n // breaking the cursor-advancement guarantee this whole dance\n // exists to provide.\n scheduleDrainClose();\n subscriber.complete();\n };\n\n const handleFrame = (name: string, data: string, lastEventId: string) => {\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch {\n parsed = data;\n }\n const frame: SurogatesFrame = {\n id: Number(lastEventId) || 0,\n type: name,\n data: parsed,\n };\n // Cursor advancement happens BEFORE the termination guard so\n // frames arriving during the drain window still push the\n // cursor past them. This is the whole point of the drain:\n // absorb trailing server frames so the next run's ``?after=``\n // starts beyond them.\n if (frame.id > this.cursor) this.cursor = frame.id;\n if (ctx.terminated) return;\n\n const events = translator.translate(frame);\n for (const e of events) subscriber.next(e);\n if (translator.isTurnComplete) finish();\n };\n\n // Subscribe to every Surogates event type by name. ``EventSource``\n // default ``onmessage`` only fires for frames without an ``event:``\n // line, but the server always sets one.\n for (const name of SURG_EVENT_NAMES) {\n source.addEventListener(name, (ev: MessageEvent) => {\n handleFrame(name, ev.data, ev.lastEventId);\n });\n }\n\n // Transport-level failures. The native EventSource reconnects\n // on network blips automatically (``readyState === 0``); a\n // ``readyState === 2`` is terminal. ``ctx.terminated`` stops\n // this from firing a second RUN_ERROR after finish() / the\n // driveRun catch have already terminated.\n source.onerror = () => {\n if (source.readyState !== 2 /* CLOSED */) return;\n if (ctx.terminated) return;\n ctx.terminated = true;\n subscriber.next({\n type: EventType.RUN_ERROR,\n message: 'SSE connection closed',\n code: 'sse_closed',\n } as BaseEvent);\n subscriber.complete();\n };\n }\n\n /**\n * Find the last user-authored message body to post. AG-UI supports\n * multimodal content arrays; this v1 only handles string content\n * (the website channel's ``POST /messages`` endpoint accepts plain\n * text). Multimodal support lands once the server grows a media\n * upload path.\n */\n private extractUserText(messages: Message[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = messages[i];\n if (!m || m.role !== 'user') continue;\n const content = (m as { content?: unknown }).content;\n if (typeof content === 'string' && content.trim().length > 0) {\n return content;\n }\n }\n throw new AGUIError(\n 'WebsiteAgent: no user message with string content found in input.messages.',\n );\n }\n}\n\n/** Per-run mutable state, scoped to the Observable factory closure. */\ninterface RunContext {\n terminated: boolean;\n source: EventSource | undefined;\n drainTimer: ReturnType<typeof setTimeout> | undefined;\n}\n\n/**\n * Detach the ``onerror`` listener BEFORE ``close()`` so the old\n * EventSource can't race a terminal error into the subscriber after\n * it's been swapped for a replacement (cookie recovery) or the run\n * has otherwise terminated. Safe to call on undefined.\n */\nfunction closeEventSource(source: EventSource | undefined): void {\n if (!source) return;\n try {\n source.onerror = null;\n } catch {\n /* Older EventSource implementations disallow null; ignore. */\n }\n source.close();\n}\n"]}
|