@syntrologie/adapt-chatbot 2.8.0-canary.273 → 2.8.0-canary.274

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  AdaptiveChatBar
3
3
  } from "./chunk-QELVKBQV.js";
4
- import "./chunk-236EKEHS.js";
4
+ import "./chunk-LX2VCWH5.js";
5
5
  import "./chunk-BDFKAVVA.js";
6
6
  import "./chunk-UVKRO5ER.js";
7
7
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"AdaptiveChatTrail.d.ts","sourceRoot":"","sources":["../src/AdaptiveChatTrail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAQ,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAIhD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;CACrE;AAED,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,6GAA6G;IAC7G,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5C,+EAA+E;IAC/E,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;CAC7B;AAqBD,qBAAa,iBAAkB,SAAQ,UAAU;IAC/C,OAAgB,UAAU;;;;;;;;;;;;;MAKxB;IAEF,QAAQ,EAAE,YAAY,EAAE,CAAM;IAC9B,YAAY,SAAmB;IAC/B,QAAQ,UAAS;IACjB;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAa;IAEhC,gBAAgB,IAAI,WAAW;IAK/B,iBAAiB,IAAI,IAAI;IAKlC,OAAO,CAAC,SAAS,CAMf;IAEF;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB,CAStB;IAEF,OAAO,CAAC,WAAW,CAMjB;IAEO,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAyB5C,MAAM;CAoMhB;AA6QD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,qBAAqB,EAAE,iBAAiB,CAAC;KAC1C;CACF"}
1
+ {"version":3,"file":"AdaptiveChatTrail.d.ts","sourceRoot":"","sources":["../src/AdaptiveChatTrail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAQ,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAIhD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;CACrE;AAED,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,6GAA6G;IAC7G,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5C,+EAA+E;IAC/E,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;CAC7B;AAqBD,qBAAa,iBAAkB,SAAQ,UAAU;IAC/C,OAAgB,UAAU;;;;;;;;;;;;;MAKxB;IAEF,QAAQ,EAAE,YAAY,EAAE,CAAM;IAC9B,YAAY,SAAmB;IAC/B,QAAQ,UAAS;IACjB;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAa;IAEhC,gBAAgB,IAAI,WAAW;IAK/B,iBAAiB,IAAI,IAAI;IAKlC,OAAO,CAAC,SAAS,CAMf;IAEF;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB,CAStB;IAEF,OAAO,CAAC,WAAW,CAMjB;IAEO,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAyB5C,MAAM;CA6LhB;AAqQD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,qBAAqB,EAAE,iBAAiB,CAAC;KAC1C;CACF"}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AdaptiveChatTrail
3
- } from "./chunk-236EKEHS.js";
3
+ } from "./chunk-LX2VCWH5.js";
4
4
  import "./chunk-BDFKAVVA.js";
5
5
  import "./chunk-UVKRO5ER.js";
6
6
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"ChatTransport.d.ts","sourceRoot":"","sources":["../src/ChatTransport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ChatTransport.d.ts","sourceRoot":"","sources":["../src/ChatTransport.ts"],"names":[],"mappings":"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"}
@@ -132,20 +132,27 @@ var AdaptiveChatTrail = class extends LitElement {
132
132
  width: "100%"
133
133
  };
134
134
  return html`
135
- <div data-syntro-chat-trail-frame style=${styleMap(frameStyles)}>
136
- ${// Collapse-mode "expand" affordance lives ABOVE the scroll
137
- // container now. Previously it was rendered as the first child
138
- // INSIDE the scroll container but with `overflow: hidden` on
139
- // collapsed mode, the `:first-child { margin-top: auto }` rule
140
- // pushed it ABOVE the scroll viewport's top edge, where the
141
- // overflow clip hid it. Moving it out of the scroll container
142
- // makes it always visible at the top of the trail frame.
143
- this.expanded ? nothing : hidden > 0 ? html`<button
135
+ ${// Single toggle button above the frame. Same DOM node morphs:
136
+ // collapsed "↑ N more · expand" / "↑ expand" (calls _onExpand)
137
+ // expanded → "⌄ minimize" (calls _onCollapse)
138
+ // Lives OUTSIDE the frame so the absolute blur veil (top:0,
139
+ // z-index:2 inside the frame) doesn't cover it. Shown whenever
140
+ // there's anything in the trail the user expects this affordance
141
+ // available regardless of how many chips are on screen.
142
+ this.messages.length === 0 ? nothing : this.expanded ? html`<button
143
+ type="button"
144
+ data-trail-toggle
145
+ data-trail-collapse
146
+ @click=${this._onCollapse}
147
+ style=${styleMap(moreStyles())}
148
+ >⌄ minimize</button>` : html`<button
144
149
  type="button"
150
+ data-trail-toggle
145
151
  data-trail-more
146
152
  @click=${this._onExpand}
147
153
  style=${styleMap(moreStyles())}
148
- >↑ ${hidden} more · expand</button>` : nothing}
154
+ >${hidden > 0 ? html`↑ ${hidden} more · expand` : html`↑ expand`}</button>`}
155
+ <div data-syntro-chat-trail-frame style=${styleMap(frameStyles)}>
149
156
  <div data-syntro-chat-trail style=${styleMap(containerStyles)}>
150
157
  ${visible.map((m, i) => {
151
158
  const pos = this.expanded ? 0 : visible.length - 1 - i;
@@ -189,16 +196,6 @@ var AdaptiveChatTrail = class extends LitElement {
189
196
  style=${styleMap(blurVeilStyles())}
190
197
  ></div>`}
191
198
  </div>
192
- ${// Always-available "minimize" affordance: sits just above the
193
- // chat-row (chat-bar arranges trail then row in a flex column),
194
- // so the user can collapse the trail no matter how far down
195
- // they've scrolled inside the expanded view.
196
- this.expanded ? html`<button
197
- type="button"
198
- data-trail-collapse
199
- @click=${this._onCollapse}
200
- style=${styleMap(stickyMinimizeStyles())}
201
- >⌄ minimize</button>` : nothing}
202
199
  `;
203
200
  }
204
201
  };
@@ -311,10 +308,18 @@ function caretStyles() {
311
308
  animation: "syntro-trail-caret 1s steps(2, end) infinite"
312
309
  };
313
310
  }
314
- function stickyMinimizeStyles() {
311
+ function moreStyles() {
315
312
  return {
316
- alignSelf: "center",
317
- marginTop: "4px",
313
+ // Center horizontally regardless of the parent's display model.
314
+ // ``alignSelf: center`` only works when the parent is a flex/grid
315
+ // container with cross-axis alignment; the chat-bar's flex column
316
+ // stretches us full-width instead. ``display: block`` + auto inline
317
+ // margins centers the auto-sized button without needing the parent
318
+ // to opt in.
319
+ display: "block",
320
+ marginLeft: "auto",
321
+ marginRight: "auto",
322
+ marginBottom: "6px",
318
323
  fontSize: "10px",
319
324
  fontWeight: "500",
320
325
  letterSpacing: "0.06em",
@@ -330,21 +335,6 @@ function stickyMinimizeStyles() {
330
335
  transition: "opacity 150ms ease, background 150ms ease"
331
336
  };
332
337
  }
333
- function moreStyles() {
334
- return {
335
- alignSelf: "center",
336
- fontSize: "10px",
337
- fontWeight: "500",
338
- letterSpacing: "0.06em",
339
- padding: "2px 8px",
340
- border: "none",
341
- background: "transparent",
342
- color: "var(--sc-tile-text-color, currentColor)",
343
- cursor: "pointer",
344
- opacity: "0.6",
345
- transition: "opacity 150ms ease"
346
- };
347
- }
348
338
  function ensureStreamingKeyframes() {
349
339
  if (typeof document === "undefined") return;
350
340
  if (document.head.querySelector("style[data-syntro-trail-caret-keyframes]")) return;
@@ -389,4 +379,4 @@ if (!customElements.get("adaptive-chat-trail")) {
389
379
  export {
390
380
  AdaptiveChatTrail
391
381
  };
392
- //# sourceMappingURL=chunk-236EKEHS.js.map
382
+ //# sourceMappingURL=chunk-LX2VCWH5.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/AdaptiveChatTrail.ts"],
4
+ "sourcesContent": ["/**\n * AdaptiveChatTrail \u2014 the \"bubble-up\" message column that sits above the\n * chat bar in the canvas lid.\n *\n * Messages appear immediately above the chat bar and drift upward as\n * newer ones arrive. Each stack position carries an opacity + blur\n * falloff so older messages read as fading from the page. Beyond a\n * `visibleCount` cap (default 3) the trail collapses into an \"N more \u00B7\n * expand\" affordance \u2014 clicking emits `trail-expand`.\n *\n * Light DOM (no shadow root) so the host page's CSS variables and the\n * surrounding canvas tokens flow through without a nested shadow\n * boundary.\n *\n * See PRD \u00A74.3 (chat trail) for the canonical motion + falloff spec.\n */\n\nimport { renderMarkdown } from '@syntrologie/chat';\nimport { html, LitElement, nothing } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\n\nexport interface TrailToolCall {\n id: string;\n name: string;\n status: 'args-streaming' | 'pending' | 'running' | 'done' | 'error';\n}\n\nexport interface TrailMessage {\n /** Stable identity for keyed rendering (must be unique within the trail). */\n id: number | string;\n role: 'user' | 'assistant' | 'system';\n text: string;\n /** Streaming \u2192 assistant text still arriving from backend. Complete \u2192 final. Error \u2192 fatal during stream. */\n status?: 'streaming' | 'complete' | 'error';\n /** Tool calls attached to an assistant message (rendered as compact chips). */\n toolCalls?: TrailToolCall[];\n}\n\n/** PRD \u00A74.3 constants. */\nconst DEFAULT_VISIBLE = 3;\nconst OPACITY_STEP = 0.22;\nconst OPACITY_FLOOR = 0.18;\nconst Y_DRIFT_PX = -2;\n/**\n * Height of the absolute blur veil at the top of the trail's scroll\n * area. Pixel-based, NOT message-count-based: with the old per-chip\n * `filter: blur(pos * step)` model, a single long markdown response\n * at pos=1 would have its entire body uniformly blurred \u2014 including\n * the chunk visually adjacent to the crisp newest message \u2014 because\n * blur is a property of the chip element, not of pixels. Switching\n * to a fixed-height backdrop-blur veil keeps the bottom of the\n * visible area (newest content) crisp regardless of message length.\n */\nconst TRAIL_BLUR_VEIL_PX = 72;\n/** Max blur applied at the very top of the veil; tapers to 0 at the bottom edge. */\nconst TRAIL_BLUR_MAX_PX = 5;\n\nexport class AdaptiveChatTrail extends LitElement {\n static override properties = {\n messages: { attribute: false },\n visibleCount: { type: Number },\n expanded: { type: Boolean },\n greeting: { type: String },\n };\n\n messages: TrailMessage[] = [];\n visibleCount = DEFAULT_VISIBLE;\n expanded = false;\n /**\n * Pre-conversation phantom assistant message. Rendered only when\n * `messages` is empty. NOT injected into chatSession state \u2014 the\n * greeting is configuration, not conversation, so it disappears\n * automatically when the first real message arrives.\n */\n greeting: string | undefined = undefined;\n\n override createRenderRoot(): HTMLElement {\n // Light DOM \u2014 canvas-level CSS vars reach the chips directly.\n return this;\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n ensureStreamingKeyframes();\n }\n\n private _onExpand = (): void => {\n // Self-manage: clicking the inline link expands the trail in place.\n // We still dispatch the event so any parent that wants to react\n // (telemetry, mirror state to a side panel, etc.) can listen.\n this.expanded = true;\n this.dispatchEvent(new CustomEvent('trail-expand', { bubbles: true, composed: true }));\n };\n\n /**\n * Approve a pending client tool call. The trail emits a generic\n * event so the host can decide whether to forward to chatSession\n * (the common path) or override. Keeps the trail itself free of\n * direct chatSession coupling \u2014 it's a pure view component.\n */\n private _onToolCallClick = (tc: TrailToolCall): void => {\n if (tc.status !== 'pending') return;\n this.dispatchEvent(\n new CustomEvent<{ toolCallId: string; approved: boolean }>('trail-toolcall-approved', {\n detail: { toolCallId: tc.id, approved: true },\n bubbles: true,\n composed: true,\n })\n );\n };\n\n private _onCollapse = (): void => {\n // Mirror of _onExpand for the minimize affordance shown while\n // expanded. Self-manages + dispatches `trail-collapse` for parent\n // observers (telemetry, side-panel mirrors).\n this.expanded = false;\n this.dispatchEvent(new CustomEvent('trail-collapse', { bubbles: true, composed: true }));\n };\n\n override updated(changed: Map<string, unknown>): void {\n // Anchor the scroll to the bottom whenever:\n // - the trail flips into expanded mode (UX continuity), OR\n // - new messages or text deltas arrive (so a long streaming chip\n // stays pinned to the latest line, not the start of the chip).\n // Skip when the user has manually scrolled UP into history \u2014 we'd\n // rather lose pinning than yank them out of what they're reading.\n if (changed.has('expanded') || changed.has('messages')) {\n requestAnimationFrame(() => {\n const container = this.querySelector<HTMLElement>('[data-syntro-chat-trail]');\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n // 200px tolerance \u2014 streaming deltas can append a paragraph in\n // one update, easily 80-150px. A tighter window would strand\n // the viewer at the top of the chip mid-stream because the\n // first delta blew past the threshold. User-driven scroll-up\n // of >200px disables auto-pin until they scroll back down.\n if (distanceFromBottom < 200 || changed.has('expanded')) {\n container.scrollTop = container.scrollHeight;\n }\n });\n }\n }\n\n override render() {\n if (this.messages.length === 0 && !this.greeting) return nothing;\n if (this.messages.length === 0 && this.greeting) {\n // Render the greeting as a pure-display assistant chip. No\n // tool-calls, no streaming caret, single chip at pos=0.\n return html`<div data-syntro-chat-trail style=${styleMap({\n display: 'flex',\n flexDirection: 'column',\n gap: '6px',\n justifyContent: 'flex-end',\n width: '100%',\n padding: '0',\n pointerEvents: 'auto',\n })}>\n <div\n data-trail-chip\n data-role=\"assistant\"\n data-status=\"greeting\"\n style=${styleMap(chipStyles('assistant', 0, { isStreaming: false, isError: false }))}\n >${unsafeHTML(renderMarkdown(stripTrailingWhitespace(this.greeting)))}</div>\n </div>`;\n }\n\n // Choose the slice we actually paint. Newest are at the END of the\n // array (closest to the chat bar). When not expanded, take the last\n // `visibleCount`; when expanded, take all.\n const visible = this.expanded ? this.messages : this.messages.slice(-this.visibleCount);\n const hidden = this.messages.length - visible.length;\n\n const baseStyles: Record<string, string> = {\n display: 'flex',\n flexDirection: 'column',\n gap: '6px',\n // NO `justifyContent: flex-end` here \u2014 combined with `overflow: auto`\n // it's a known cross-browser bug: oversized flex children get\n // pushed ABOVE the container without contributing to scrollHeight,\n // so the scrollbar never appears. We rely on `:first-child {\n // margin-top: auto }` (injected as global CSS) to push short\n // content to the bottom of the box while leaving overflow handling\n // to the native scroll container.\n width: '100%',\n // The trail itself has no chrome \u2014 chips sit on the host page bg.\n padding: '0',\n pointerEvents: 'auto',\n };\n\n // Expanded mode: cap height, scroll, and add a visible top border so\n // the panel reads as a bounded region. NO background tint or shadow\n // \u2014 the ambient (collapsed) treatment is fine and we don't want to\n // suddenly introduce panel chrome that wasn't there before. The\n // border alone marks the panel's top edge.\n // Falloff is turned off per-chip below \u2014 readability wins once the\n // user has explicitly asked for history. See PRD \u00A74.3.\n // Collapsed mode also gets a (looser) cap + scroll so a single long\n // markdown chip doesn't blow the chat-bar off-screen with no\n // scroll affordance. Tighter than expanded so the bubble-up trail\n // doesn't dominate the surface visually.\n const modeStyles: Record<string, string> = this.expanded\n ? {\n maxHeight: 'min(320px, 40vh)',\n overflowY: 'auto',\n paddingTop: '8px',\n borderTop: 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))',\n scrollBehavior: 'smooth',\n scrollbarWidth: 'thin',\n }\n : {\n // Collapsed mode = ambient peek surface. Never a scrollbar:\n // long messages slide up behind the blur veil instead of\n // exposing a scrollbar. The \"\u2191 N more \u00B7 expand\" affordance\n // is the only entry into history; clicking it switches to\n // expanded mode where scroll IS allowed.\n // Height kept short so the trail reads as a peek strip above\n // the chat bar, not a panel \u2014 long single replies fade into\n // the blur veil at the top, user expands to read the rest.\n maxHeight: 'min(140px, 22vh)',\n overflow: 'hidden',\n };\n\n const containerStyles = { ...baseStyles, ...modeStyles };\n\n // Wrap the scroll container in a positioning frame so the absolute\n // blur veil (rendered at the top) anchors to the scroll viewport's\n // top edge \u2014 NOT to a chip that scrolls under it. Pixel-anchored\n // blur means: the top TRAIL_BLUR_VEIL_PX of the visible area is\n // veiled regardless of how many messages occupy those pixels.\n const frameStyles: Record<string, string> = {\n position: 'relative',\n width: '100%',\n };\n\n return html`\n ${\n // Single toggle button above the frame. Same DOM node morphs:\n // collapsed \u2192 \"\u2191 N more \u00B7 expand\" / \"\u2191 expand\" (calls _onExpand)\n // expanded \u2192 \"\u2304 minimize\" (calls _onCollapse)\n // Lives OUTSIDE the frame so the absolute blur veil (top:0,\n // z-index:2 inside the frame) doesn't cover it. Shown whenever\n // there's anything in the trail \u2014 the user expects this affordance\n // available regardless of how many chips are on screen.\n this.messages.length === 0\n ? nothing\n : this.expanded\n ? html`<button\n type=\"button\"\n data-trail-toggle\n data-trail-collapse\n @click=${this._onCollapse}\n style=${styleMap(moreStyles())}\n >\u2304 minimize</button>`\n : html`<button\n type=\"button\"\n data-trail-toggle\n data-trail-more\n @click=${this._onExpand}\n style=${styleMap(moreStyles())}\n >${hidden > 0 ? html`\u2191 ${hidden} more \u00B7 expand` : html`\u2191 expand`}</button>`\n }\n <div data-syntro-chat-trail-frame style=${styleMap(frameStyles)}>\n <div data-syntro-chat-trail style=${styleMap(containerStyles)}>\n ${visible.map((m, i) => {\n // Stack position from the bar: 0 = closest, N-1 = oldest.\n // visible[last] is closest, so reverse the index from the\n // tail of the visible slice. When expanded, the ambient\n // falloff is suppressed (pos = 0) so every chip reads at\n // full opacity \u2014 explicit history view, not ambient trail.\n const pos = this.expanded ? 0 : visible.length - 1 - i;\n const isStreaming = m.status === 'streaming';\n const isError = m.status === 'error' || m.role === 'system';\n const toolCalls = m.toolCalls ?? [];\n // Assistant text is markdown-formatted by the agent (lists,\n // code, bold, links). renderMarkdown sanitizes via DOMPurify,\n // then unsafeHTML injects the safe HTML. User + system chips\n // stay plain text \u2014 no markdown risk + no expansion attack\n // surface on user-typed input.\n const renderedText =\n m.role === 'assistant'\n ? html`${unsafeHTML(renderMarkdown(stripTrailingWhitespace(m.text)))}`\n : html`${m.text}`;\n return html`<div\n data-trail-chip\n data-role=${m.role}\n data-status=${m.status ?? 'complete'}\n style=${styleMap(chipStyles(m.role, pos, { isStreaming, isError }))}\n >${renderedText}${\n isStreaming\n ? html`<span\n data-trail-caret\n aria-hidden=\"true\"\n style=${styleMap(caretStyles())}\n ></span>`\n : nothing\n }${\n toolCalls.length > 0\n ? html`<div data-trail-toolcalls style=${styleMap(toolCallStripStyles())}>\n ${toolCalls.map(\n (tc) => html`<button\n type=\"button\"\n data-trail-toolcall\n data-tool-id=${tc.id}\n data-tool-status=${tc.status}\n @click=${() => this._onToolCallClick(tc)}\n ?disabled=${tc.status !== 'pending'}\n style=${styleMap(toolCallChipStyles(tc.status))}\n title=\"${tc.name} \u00B7 ${tc.status}\"\n >${toolCallIcon(tc.status)} ${tc.name}</button>`\n )}\n </div>`\n : nothing\n }</div>`;\n })}\n </div>\n ${\n // The blur veil only fires in collapsed (ambient) mode.\n // Expanded = the user explicitly asked for history; blurring\n // the top of the panel would hide content (including the\n // minimize button just above the trail's top edge) the user\n // came here to see.\n this.expanded\n ? nothing\n : html`<div\n data-trail-blur-veil\n aria-hidden=\"true\"\n style=${styleMap(blurVeilStyles())}\n ></div>`\n }\n </div>\n `;\n }\n}\n\n/**\n * Absolute blur veil at the top of the scroll viewport. Uses\n * `backdrop-filter: blur(...)` so whatever pixels sit beneath the veil\n * (top of the scroll content) become blurred, regardless of which chip\n * those pixels belong to. A linear mask fades the blur effect from full\n * at the top to zero at the bottom of the veil \u2014 newest content stays\n * crisp because it lives below the veil's height.\n *\n * Note: `mask-image` controls where the BACKDROP-FILTER applies (via\n * masking the veil element), so the bottom edge of the veil naturally\n * dissolves into the unblurred scroll area below.\n */\nfunction blurVeilStyles(): Record<string, string> {\n const mask = 'linear-gradient(to bottom, black 0%, black 40%, transparent 100%)';\n return {\n position: 'absolute',\n top: '0',\n left: '0',\n right: '0',\n height: `${TRAIL_BLUR_VEIL_PX}px`,\n pointerEvents: 'none',\n backdropFilter: `blur(${TRAIL_BLUR_MAX_PX}px)`,\n WebkitBackdropFilter: `blur(${TRAIL_BLUR_MAX_PX}px)`,\n maskImage: mask,\n WebkitMaskImage: mask,\n zIndex: '2',\n };\n}\n\nfunction chipStyles(\n role: TrailMessage['role'],\n pos: number,\n state: { isStreaming: boolean; isError: boolean } = { isStreaming: false, isError: false }\n): Record<string, string> {\n const opacity = Math.max(OPACITY_FLOOR, 1 - pos * OPACITY_STEP);\n const yPx = pos * Y_DRIFT_PX;\n\n // Error chips override role-specific styling \u2014 they shouldn't read\n // as \"an assistant reply.\" Streaming chips get a subtle accent ring\n // so the in-progress state is visually distinct from a settled reply.\n // All four states resolve to design-system tokens via the theme; the\n // fallbacks here are conservative neutrals so the widget still renders\n // sensibly on a host page that hasn't set tokens yet.\n const border = state.isError\n ? 'var(--sc-content-bubble-border-error, 1px solid rgba(220, 80, 80, 0.55))'\n : state.isStreaming\n ? 'var(--sc-content-bubble-border-user, 1px solid rgba(255, 255, 255, 0.28))'\n : role === 'user'\n ? 'var(--sc-content-bubble-border-user, 1px solid rgba(255, 255, 255, 0.28))'\n : 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))';\n const background = state.isError\n ? 'var(--sc-content-bubble-background-error, rgba(140, 40, 40, 0.42))'\n : role === 'user'\n ? 'var(--sc-content-bubble-background-user, rgba(255, 255, 255, 0.10))'\n : 'var(--sc-content-bubble-background, rgba(20, 22, 24, 0.35))';\n\n const base: Record<string, string> = {\n alignSelf: role === 'user' ? 'flex-end' : 'flex-start',\n maxWidth: '85%',\n fontSize: '11px',\n lineHeight: '1.45',\n padding: '4px 10px',\n borderRadius: '10px',\n border,\n background,\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n color: state.isError\n ? 'var(--sc-content-bubble-text-error, rgba(255, 220, 220, 0.95))'\n : 'var(--sc-tile-title-color, inherit)',\n transition:\n 'opacity 240ms cubic-bezier(0.22, 1, 0.36, 1), transform 240ms cubic-bezier(0.22, 1, 0.36, 1)',\n // Per-position opacity + y-drift falloff is still per-chip \u2014 those\n // operate naturally per element. Blur is NOT per-chip anymore: it's\n // applied via an absolute-positioned veil at the top of the scroll\n // viewport (TRAIL_BLUR_VEIL_PX above), so a single long message\n // doesn't get uniformly blurred across its full body.\n opacity: String(toFixedTrim(opacity)),\n transform: `translateY(${yPx}px)`,\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n };\n return base;\n}\n\nfunction toolCallStripStyles(): Record<string, string> {\n return {\n display: 'flex',\n flexWrap: 'wrap',\n gap: '4px',\n marginTop: '4px',\n paddingTop: '4px',\n borderTop: '1px dashed var(--sc-content-divider-color, rgba(255, 255, 255, 0.12))',\n };\n}\n\nfunction toolCallChipStyles(status: TrailToolCall['status']): Record<string, string> {\n const isPending = status === 'pending';\n const isError = status === 'error';\n const isDone = status === 'done';\n return {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '4px',\n padding: '2px 7px',\n border: isError\n ? 'var(--sc-content-bubble-border-error, 1px solid rgba(220, 80, 80, 0.45))'\n : isPending\n ? 'var(--sc-content-bubble-border-user, 1px solid rgba(255, 255, 255, 0.28))'\n : 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))',\n background: isError\n ? 'var(--sc-content-bubble-background-error, rgba(140, 40, 40, 0.35))'\n : isPending\n ? 'var(--sc-content-bubble-background-user, rgba(255, 255, 255, 0.10))'\n : isDone\n ? 'var(--sc-content-bubble-background-idle, rgba(40, 44, 50, 0.4))'\n : 'var(--sc-content-bubble-background, rgba(20, 22, 24, 0.35))',\n color: 'var(--sc-tile-text-color, inherit)',\n borderRadius: '999px',\n fontSize: '10px',\n fontWeight: '500',\n fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',\n cursor: isPending ? 'pointer' : 'default',\n opacity: isDone ? '0.7' : '1',\n };\n}\n\nfunction toolCallIcon(status: TrailToolCall['status']): string {\n switch (status) {\n case 'args-streaming':\n case 'running':\n return '\u22EF';\n case 'pending':\n return '?';\n case 'done':\n return '\u2713';\n case 'error':\n return '\u2717';\n default:\n return '\u00B7';\n }\n}\n\nfunction caretStyles(): Record<string, string> {\n return {\n display: 'inline-block',\n width: '6px',\n height: '12px',\n marginLeft: '3px',\n verticalAlign: '-1px',\n background: 'hsl(var(--sc-accent-color) / 0.85)',\n borderRadius: '1px',\n animation: 'syntro-trail-caret 1s steps(2, end) infinite',\n };\n}\n\nfunction moreStyles(): Record<string, string> {\n return {\n // Center horizontally regardless of the parent's display model.\n // ``alignSelf: center`` only works when the parent is a flex/grid\n // container with cross-axis alignment; the chat-bar's flex column\n // stretches us full-width instead. ``display: block`` + auto inline\n // margins centers the auto-sized button without needing the parent\n // to opt in.\n display: 'block',\n marginLeft: 'auto',\n marginRight: 'auto',\n marginBottom: '6px',\n fontSize: '10px',\n fontWeight: '500',\n letterSpacing: '0.06em',\n padding: '4px 10px',\n border: '1px solid hsl(var(--sc-accent-color) / 0.32)',\n borderRadius: '999px',\n background: 'hsl(var(--sc-accent-color) / 0.10)',\n color: 'var(--sc-tile-text-color, currentColor)',\n cursor: 'pointer',\n opacity: '0.85',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n transition: 'opacity 150ms ease, background 150ms ease',\n };\n}\n\n/**\n * Inject the streaming-caret keyframes once per document. Light-DOM\n * components can't ship CSS via Lit's `styles` static \u2014 the rules\n * need to be present in the document's stylesheet. Idempotent via\n * a data attribute on the injected style tag.\n */\nfunction ensureStreamingKeyframes(): void {\n if (typeof document === 'undefined') return;\n if (document.head.querySelector('style[data-syntro-trail-caret-keyframes]')) return;\n const style = document.createElement('style');\n style.setAttribute('data-syntro-trail-caret-keyframes', 'true');\n // Caret animation + markdown reset for assistant chips. The trail\n // chips are tiny (11px line-height); default <p>/<ul>/<pre> margins\n // would push the chip vertically and break the falloff layout.\n style.textContent = [\n '@keyframes syntro-trail-caret { 0%{opacity:1} 50%{opacity:0} 100%{opacity:1} }',\n // Anchor short content to the bottom of the scrollable trail \u2014\n // see baseStyles comment for why we can't use justify-content.\n '[data-syntro-chat-trail] > :first-child { margin-top: auto }',\n // Tight spacing \u2014 trail chips are an 11px-line-height pill. Default\n // browser <p>/<ul> margins (1em \u2248 11px each side) would dominate the\n // chip. Keep paragraph + list separators minimal (4px) so multi-\n // paragraph replies read as one continuous flow, not a vertical\n // stack of fragments.\n '[data-trail-chip] > p:first-child { margin-top: 0 }',\n '[data-trail-chip] > p:last-child { margin-bottom: 0 }',\n '[data-trail-chip] p { margin: 4px 0 }',\n '[data-trail-chip] br + br { display: none }',\n '[data-trail-chip] ul, [data-trail-chip] ol { margin: 4px 0; padding-left: 1.1em }',\n '[data-trail-chip] li { margin: 0 }',\n '[data-trail-chip] li + li { margin-top: 1px }',\n '[data-trail-chip] pre { margin: 6px 0; padding: 6px 8px; background: var(--sc-content-code-background-block, rgba(0, 0, 0, 0.35)); border-radius: 6px; overflow-x: auto; font-size: 10px }',\n '[data-trail-chip] code { font-family: ui-monospace, SF Mono, Menlo, monospace; font-size: 10px; background: var(--sc-content-code-background, rgba(0, 0, 0, 0.25)); padding: 1px 4px; border-radius: 3px }',\n '[data-trail-chip] pre code { background: transparent; padding: 0 }',\n '[data-trail-chip] a { color: var(--sc-content-link-color, var(--sc-color-primary, #b72e2a)); text-decoration: underline }',\n '[data-trail-chip] strong { font-weight: 600 }',\n ].join(' ');\n document.head.appendChild(style);\n}\n\n/**\n * Pre-process LLM markdown before rendering. Three common artifacts:\n *\n * 1. Trailing \" \" (two spaces = markdown hard-break) sprinkled at\n * end-of-line, even inside list items where it just leaks\n * invisible whitespace into the DOM.\n * 2. Runs of 3+ blank lines used as visual separators. Markdown\n * treats any number of blank lines as a single paragraph break,\n * so the extras add nothing semantically \u2014 but combined with\n * paragraph margins they create huge gaps in the tiny chip layout.\n * 3. Leading / trailing newlines on the whole message. Trailing\n * `\\n\\n` is the common one \u2014 marked turns it into a final empty\n * <p> that still occupies a full line-height of vertical space,\n * leaving a visible gap between the last text and the chat bar.\n *\n * Collapsing all three keeps the rendered HTML tight without changing\n * the agent's semantic intent.\n */\nfunction stripTrailingWhitespace(text: string): string {\n return text\n .replace(/[ \\t]+$/gm, '') // trailing whitespace per line\n .replace(/\\n{3,}/g, '\\n\\n') // collapse runs of blank lines\n .trim(); // drop leading/trailing whitespace+newlines on the whole message\n}\n\n/** Two-decimal trim that avoids trailing zeros (\".50\" \u2192 \".5\"). */\nfunction toFixedTrim(n: number): string {\n const s = n.toFixed(2);\n return s.replace(/\\.?0+$/, '') || '0';\n}\n\nif (!customElements.get('adaptive-chat-trail')) {\n customElements.define('adaptive-chat-trail', AdaptiveChatTrail);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'adaptive-chat-trail': AdaptiveChatTrail;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/dist/runtime.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  renderFallbackHtml
12
12
  } from "./chunk-VVJNW5ZS.js";
13
13
  import "./chunk-QELVKBQV.js";
14
- import "./chunk-236EKEHS.js";
14
+ import "./chunk-LX2VCWH5.js";
15
15
  import "./chunk-BDFKAVVA.js";
16
16
  import "./chunk-AUER7ZCK.js";
17
17
  import "./chunk-435KJD27.js";
@@ -554,6 +554,16 @@ var ChatTransport = class {
554
554
  const h = {};
555
555
  if (token) h.Authorization = `Bearer ${token}`;
556
556
  if (cft) h["CF-Turnstile-Token"] = cft;
557
+ try {
558
+ const rt = this._config?.runtime;
559
+ const fromRuntime = rt?.telemetry?.getDistinctId?.();
560
+ const fromWindow = window.posthog?.get_distinct_id?.();
561
+ const did = fromRuntime || fromWindow;
562
+ if (typeof did === "string" && did.length > 0) {
563
+ h["X-Distinct-Id"] = did;
564
+ }
565
+ } catch {
566
+ }
557
567
  return h;
558
568
  };
559
569
  const runtime2 = this._config.runtime;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/ChatSession.ts", "../src/ChatTransport.ts", "../src/AdaptiveChatBarMountable.ts", "../src/AdaptiveChipsStripMountable.ts", "../src/NavLinkMountable.ts", "../src/TextAnswerMountable.ts", "../src/runtime.ts"],
4
- "sourcesContent": ["/**\n * ChatSession \u2014 singleton holder of the chat conversation state.\n *\n * The chat is conceptually one thing. Whether it's rendered in the\n * mini-canvas's lid, the drawer's lid, or as a tile inside any slot,\n * they're all VIEWS of the same conversation. This module owns that\n * shared state.\n *\n * Why a module-level singleton (not on `runtime`):\n * - Per-page scope is the right granularity for a chat (one user,\n * one conversation, however many canvas instances on the page).\n * - Avoids a new SmartCanvasRuntime API surface for now. Easy to\n * promote to `runtime.chat` later without breaking the widget API.\n * - Module identity is stable per page; multiple imports return the\n * same instance.\n *\n * Separation of concerns:\n * - `ChatSession` owns state: messages, inFlight.\n * - Views (AdaptiveChatBar via AdaptiveChatBarMountable) subscribe\n * to state changes and dispatch user actions back via send() /\n * interrupt().\n * - Transports (the LLM call, SSE pipeline, stub timers) listen\n * for \"send\" / \"interrupt\" events and eventually call receive()\n * with the assistant's reply. Transports are NOT owned by this\n * module \u2014 adapters live separately.\n *\n * No persistence in this slice. Future: snapshot to runtime.state\n * for cross-session continuity.\n */\n\nimport type { TrailMessage, TrailToolCall } from './AdaptiveChatTrail';\n\nexport interface ChatSessionState {\n readonly messages: readonly TrailMessage[];\n readonly inFlight: boolean;\n}\n\nexport interface ToolResultEvent {\n toolCallId: string;\n result: unknown;\n approved: boolean;\n}\n\nexport type ChatSessionSubscriber = (state: ChatSessionState) => void;\nexport type SendListener = (event: { text: string }) => void;\nexport type InterruptListener = () => void;\nexport type ToolResultListener = (event: ToolResultEvent) => void;\nexport type Unsubscribe = () => void;\n\nexport const CHAT_SESSION_STORAGE_KEY = 'syntro:chat:v1';\n\ninterface PersistedShape {\n messages: TrailMessage[];\n nextId: number;\n}\n\nfunction isValidMessage(value: unknown): value is TrailMessage {\n if (typeof value !== 'object' || value === null) return false;\n const m = value as Record<string, unknown>;\n return (\n (typeof m.id === 'number' || typeof m.id === 'string') &&\n (m.role === 'user' || m.role === 'assistant' || m.role === 'system') &&\n typeof m.text === 'string'\n );\n}\n\nfunction loadFromStorage(): PersistedShape | null {\n try {\n const raw = globalThis.localStorage?.getItem(CHAT_SESSION_STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n if (!parsed || !Array.isArray(parsed.messages)) return null;\n const messages = parsed.messages.filter(isValidMessage);\n const nextId = typeof parsed.nextId === 'number' ? parsed.nextId : messages.length + 1;\n return { messages, nextId };\n } catch {\n return null;\n }\n}\n\nexport class ChatSession {\n private _messages: TrailMessage[] = [];\n private _inFlight = false;\n private _nextId = 1;\n\n private subscribers = new Set<ChatSessionSubscriber>();\n private sendListeners = new Set<SendListener>();\n private interruptListeners = new Set<InterruptListener>();\n private toolResultListeners = new Set<ToolResultListener>();\n\n constructor() {\n const restored = loadFromStorage();\n if (restored) {\n this._messages = restored.messages;\n this._nextId = restored.nextId;\n }\n }\n\n /** Snapshot the current state. Always returns a fresh immutable view. */\n getState(): ChatSessionState {\n return { messages: [...this._messages], inFlight: this._inFlight };\n }\n\n /**\n * Subscribe to state changes. Called immediately with the current\n * state, then again on every change. Returns an unsubscribe function.\n */\n subscribe(cb: ChatSessionSubscriber): Unsubscribe {\n this.subscribers.add(cb);\n cb(this.getState());\n return () => {\n this.subscribers.delete(cb);\n };\n }\n\n /**\n * User submitted a message. Appends a user-role message, sets\n * inFlight=true, notifies state subscribers, and fires a \"send\"\n * event so transports can pick it up. Empty/whitespace text is a\n * no-op (matches the chat bar's local guard).\n */\n send(text: string): void {\n const trimmed = text.trim();\n if (!trimmed) return;\n this._messages.push({ id: this._nextId++, role: 'user', text: trimmed });\n this._inFlight = true;\n this.notify();\n for (const listener of this.sendListeners) listener({ text: trimmed });\n }\n\n /**\n * Single-shot assistant reply (no streaming). Equivalent to\n * receiveStart + receiveDelta + receiveEnd in one call. Useful for\n * stub transports and tests that don't model streaming.\n */\n receive(text: string): void {\n const id = `m-${this._nextId++}`;\n this._messages.push({ id, role: 'assistant', text, status: 'complete' });\n this._inFlight = false;\n this.notify();\n }\n\n /**\n * Begin a streaming assistant message. Appends an empty assistant\n * message with status='streaming'. Caller (transport adapter) feeds\n * deltas via receiveDelta(id, text) and signals completion via\n * receiveEnd(id). `inFlight` stays true through the stream.\n */\n receiveStart(id: string): void {\n this._messages.push({ id, role: 'assistant', text: '', status: 'streaming' });\n this._inFlight = true;\n this.notify();\n }\n\n /**\n * Append a delta to a streaming message. No-op when the id is\n * unknown (race between transport events and reset, etc.) \u2014 never\n * throws so transports can fire-and-forget.\n */\n receiveDelta(id: string, delta: string): void {\n const msg = this._messages.find((m) => m.id === id);\n if (!msg || msg.status !== 'streaming') return;\n msg.text += delta;\n this.notify();\n }\n\n /**\n * Mark a streaming message complete and clear inFlight. No-op when\n * the id is unknown (defensive against transport double-fires).\n */\n receiveEnd(id: string): void {\n const msg = this._messages.find((m) => m.id === id);\n if (msg && msg.status === 'streaming') {\n msg.status = 'complete';\n }\n this._inFlight = false;\n this.notify();\n }\n\n /**\n * Transport reports a fatal error. Marks any in-flight streaming\n * message as 'error' (so the trail can render a styled error chip\n * instead of pretending the partial text was a complete answer),\n * appends a system-role message with the error text for visibility,\n * and clears inFlight.\n */\n error(message: string): void {\n for (const m of this._messages) {\n if (m.status === 'streaming') m.status = 'error';\n }\n this._messages.push({\n id: `err-${this._nextId++}`,\n role: 'system',\n text: message,\n status: 'error',\n });\n this._inFlight = false;\n this.notify();\n }\n\n /**\n * User clicked the in-flight stop button. Clears inFlight and\n * fires an \"interrupt\" event so transports can cancel their\n * in-flight request. No-op if not in-flight (idempotent).\n */\n interrupt(): void {\n if (!this._inFlight) return;\n this._inFlight = false;\n this.notify();\n for (const listener of this.interruptListeners) listener();\n }\n\n /**\n * Wipe state. Used by the canvas-close path (start a fresh\n * conversation next time) and by tests.\n */\n reset(): void {\n this._messages = [];\n this._inFlight = false;\n this._nextId = 1;\n this.notify();\n }\n\n /** Register a transport's send listener. Returns unsubscribe. */\n onSend(listener: SendListener): Unsubscribe {\n this.sendListeners.add(listener);\n return () => {\n this.sendListeners.delete(listener);\n };\n }\n\n /**\n * True when at least one transport has wired itself to the session's\n * send pipeline. Views can use this to surface \"no chat backend\n * connected\" affordances instead of hanging in `inFlight` after a\n * send fires into the void.\n */\n hasTransport(): boolean {\n return this.sendListeners.size > 0;\n }\n\n /** Register a transport's interrupt listener. Returns unsubscribe. */\n onInterrupt(listener: InterruptListener): Unsubscribe {\n this.interruptListeners.add(listener);\n return () => {\n this.interruptListeners.delete(listener);\n };\n }\n\n /**\n * Register a transport's tool-result listener. The transport\n * forwards `tool-result` actions back to the agent after the user\n * approves or rejects a client-tool call. Returns unsubscribe.\n */\n onToolResult(listener: ToolResultListener): Unsubscribe {\n this.toolResultListeners.add(listener);\n return () => {\n this.toolResultListeners.delete(listener);\n };\n }\n\n // -------------------------------------------------------------------------\n // Tool calls\n // -------------------------------------------------------------------------\n\n /**\n * Attach a tool call to a streaming assistant message. No-op when\n * the message id is unknown (race between transport events and\n * reset / late mount).\n */\n addToolCall(messageId: string | number, toolCall: TrailToolCall): void {\n const msg = this._messages.find((m) => m.id === messageId);\n if (!msg) return;\n msg.toolCalls = [...(msg.toolCalls ?? []), { ...toolCall }];\n this.notify();\n }\n\n /**\n * Partially update a tool call by id. Used by the transport to\n * advance status (args-streaming \u2192 running \u2192 done) as AG-UI events\n * arrive. No-op when the id is unknown.\n */\n updateToolCall(toolCallId: string, patch: Partial<TrailToolCall>): void {\n for (const msg of this._messages) {\n const tcs = msg.toolCalls;\n if (!tcs) continue;\n const idx = tcs.findIndex((tc) => tc.id === toolCallId);\n if (idx === -1) continue;\n const next = [...tcs];\n next[idx] = { ...next[idx], ...patch } as TrailToolCall;\n msg.toolCalls = next;\n this.notify();\n return;\n }\n }\n\n /**\n * Resolve a (client-) tool call. Marks the call done, persists, and\n * fires onToolResult so the transport can forward the result + the\n * user's approve/reject decision back to the agent. No-op when the\n * id is unknown.\n */\n resolveToolCall(toolCallId: string, result: unknown, approved: boolean): void {\n let found = false;\n for (const msg of this._messages) {\n const tcs = msg.toolCalls;\n if (!tcs) continue;\n const idx = tcs.findIndex((tc) => tc.id === toolCallId);\n if (idx === -1) continue;\n const next = [...tcs];\n next[idx] = { ...next[idx], status: 'done' } as TrailToolCall;\n msg.toolCalls = next;\n found = true;\n break;\n }\n if (!found) return;\n this.notify();\n for (const listener of this.toolResultListeners) {\n listener({ toolCallId, result, approved });\n }\n }\n\n private notify(): void {\n const state = this.getState();\n this.persist();\n for (const sub of this.subscribers) sub(state);\n }\n\n private persist(): void {\n try {\n if (this._messages.length === 0) {\n globalThis.localStorage?.removeItem(CHAT_SESSION_STORAGE_KEY);\n return;\n }\n const payload: PersistedShape = { messages: this._messages, nextId: this._nextId };\n globalThis.localStorage?.setItem(CHAT_SESSION_STORAGE_KEY, JSON.stringify(payload));\n } catch {\n // private mode / quota / SSR \u2014 swallow; in-memory state is still valid\n }\n }\n}\n\n/**\n * The module-level singleton. Every `<adaptive-chat-bar>` (regardless\n * of which slot it lives in) reads from and writes to this instance.\n * Multiple imports of this module return the same object.\n *\n * Scope: per-page. Correct for the canonical case (one user, one\n * conversation, however many canvas views surface it). Multiple\n * `<smart-canvas>` instances on the same page will share state \u2014\n * tracked in project_future_work.md for per-runtime scoping.\n */\nexport const chatSession = new ChatSession();\n", "/**\n * ChatTransport \u2014 singleton transport adapter that bridges chatSession\n * (the view-side state holder) to the real backend chat pipeline\n * (AG-UI SSE at `/api/adaptive/stream`).\n *\n * This is the unification of two formerly separate paths:\n * - `ChatAssistantLit` (old) owns the transport plumbing \u2014\n * AgUiTransport, Cloudflare Turnstile, headers, A2UI passthrough,\n * telemetry, fallback card.\n * - `AdaptiveChatBar` + `chatSession` (new) owns the canvas-lid UX \u2014\n * glassmorphism bar, bubble-up trail, per-page persistence.\n *\n * This module ports every transport-side concern from ChatAssistantLit\n * into a session-shaped adapter so the new bar gets full backend\n * parity. The bar stays a pure view; the transport stays a pure pipe.\n *\n * Lifecycle:\n * - `configure({ backendUrl, runtime, ... })` \u2014 called once by the\n * SDK runtime at bootstrap from chat config. Idempotent; second\n * call with the same backendUrl is a no-op.\n * - Connection is LAZY. The transport doesn't acquire Turnstile or\n * open the SSE until the first chatSession.send() \u2014 saves cost\n * on pages where the user never opens chat.\n * - On chatSession.send \u2192 transport ensures connected (acquires\n * Turnstile token, builds AgUiTransport) then forwards\n * `{type:'user-message', text}` to the agent.\n * - On chatSession.onInterrupt \u2192 transport sends stop-generation.\n * - AgUi events stream into chatSession via receiveStart / Delta /\n * End / error.\n * - A2UI custom events forward to runtime.actions.applyBatch so the\n * agent can drive canvas updates.\n *\n * Telemetry parity with ChatAssistantLit:\n * - chatbot.transport_error (every error, with status/body/name)\n * - chatbot.a2ui_applied\n * - chatbot.fallback_rendered \u2014 emitted when the transport gives up\n * (Turnstile failed AND backend rejected) so existing PostHog\n * queries keep working.\n *\n * Threading model: single-flight per page. Multiple concurrent sends\n * are queued by AgUiTransport itself. Multiple bar mounts share this\n * one transport via chatSession.\n */\n\nimport type { ServerEvent } from '@syntrologie/chat';\n// AgUiTransport / Turnstile helpers live in the existing packages \u2014\n// we reuse them instead of re-implementing.\nimport { AgUiTransport } from '@syntrologie/chat/transport/agui';\n\nimport type { TrailToolCall } from './AdaptiveChatTrail';\nimport { chatSession } from './ChatSession';\nimport type { ElementMutation } from './elements';\nimport { decodeMutationEnvelope } from './elements';\nimport { acquireTokenWithChallenge } from './Turnstile';\nimport type { ChatbotWidgetRuntime } from './types';\n\n/**\n * Per-customer \"chat unavailable\" card config. Surfaced via onFallback\n * when the transport gives up after repeated failures. Mirrors\n * ChatbotFallback in types.ts so consumers can pass the same config.\n */\nexport interface ChatbotFallbackConfig {\n title?: string;\n message?: string;\n ctaLabel?: string;\n ctaHref?: string;\n}\n\nexport interface ChatTransportConfig {\n /** Backend host, e.g. \"https://demo-api.syntrologie.com\" or \"\" for same-origin. */\n backendUrl: string;\n /** Runtime hooks for A2UI apply + telemetry. */\n runtime: ChatbotWidgetRuntime;\n /** Optional thread id for conversation continuity across reloads. */\n threadId?: string;\n /**\n * Optional set of client-tool names the transport should treat as\n * \"needs user approval\" (surfaced via chatSession.toolCalls instead\n * of auto-executed server-side). Empty by default \u2014 all tool calls\n * are server-driven and the transport just emits the \"tool call\n * happened\" event.\n */\n clientTools?: Set<string>;\n /**\n * Per-customer \"chat unavailable\" card. Rendered by the host\n * (typically AdaptiveChatBarMountable) after repeated connect\n * failures wipe the chat surface \u2014 exactly the same UX\n * ChatAssistantLit's fallback path provides.\n */\n fallback?: ChatbotFallbackConfig;\n /**\n * Arbitrary JSON object attached to every AG-UI request as\n * `forwardedProps`. Used by the adaptive chat surface to ship the\n * LLM-authored UI element config (`uiTemplates`, `elementsEnabled`)\n * per request \u2014 the backend reads these to enable the\n * `mount_element` / `patch_element` / `unmount_element` tools.\n * Callable form is re-evaluated on every turn so a config edit can\n * land mid-conversation.\n */\n forwardedProps?: Record<string, unknown> | (() => Record<string, unknown> | undefined);\n /**\n * Optional callback for `syntro.element.mutation` AG-UI custom events.\n * The transport decodes the envelope on receipt; if it matches the\n * element-mutation schema, mutations are routed here instead of the\n * generic `runtime.actions.applyBatch` legacy path. Consumers are\n * expected to forward to `ElementInstanceStore.apply(mutations)` \u2014\n * we don't take a store reference directly so the transport stays\n * agnostic of which surface owns the store (the chat-bar widget\n * may own one; ChatAssistantLit owns its own; both share this\n * singleton transport).\n *\n * When unset, element-mutation envelopes fall back to applyBatch,\n * matching legacy behaviour (which would no-op since applyBatch\n * doesn't know about MutationEnvelope).\n */\n onElementMutation?: (mutations: ElementMutation[]) => void;\n}\n\n/**\n * Debounce window for connect errors. Single transient errors\n * (cold-start 502, brief CORS preflight failure) should NOT swap the\n * chat to the fallback card; only sustained failure should. Matches\n * ChatAssistantLit's ERROR_DEBOUNCE_MS.\n */\nexport const FALLBACK_DEBOUNCE_MS = 1_500;\n\nexport interface FallbackPayload {\n reason: 'connect_failed' | 'connect_timeout';\n fallback: ChatbotFallbackConfig;\n transportId: string;\n transportAgeMs: number;\n messagesSucceeded: number;\n hadTurnstileToken: boolean | null;\n errorStatus: number | null;\n errorBody: string | null;\n errorMessage: string | null;\n errorName: string | null;\n}\n\nexport type FallbackListener = (payload: FallbackPayload) => void;\n\ntype ChatTransportStatus =\n | 'idle' // configure() not called yet\n | 'configured' // ready but not connected\n | 'acquiring' // Turnstile token acquisition in flight\n | 'connected' // AgUiTransport up and ready\n | 'error'; // last attempt failed; will retry on next send\n\ninterface SyntroGlobalConfig {\n token?: string;\n}\n\n/**\n * Read the workspace's syn_* token from runtime-config.js. The SDK\n * sets this global at bootstrap; for non-CDN test environments the\n * test page sets it directly. Returns undefined when the global is\n * missing \u2014 the connect will then fall through to the unauthenticated\n * path and the backend will 401 (caught by error handling below).\n */\nfunction readSyntroToken(): string | undefined {\n if (typeof window === 'undefined') return undefined;\n const cfg = (window as unknown as { __SYNTRO_CONFIG__?: SyntroGlobalConfig }).__SYNTRO_CONFIG__;\n const token = cfg?.token;\n return typeof token === 'string' && token.length > 0 ? token : undefined;\n}\n\n/**\n * Debug logger gated on `window.__SYNTRO_CHAT_DEBUG__`. Mirrors\n * ChatAssistantLit so flipping that flag in the customer's DevTools\n * enables verbose tracing across the whole chat pipeline (the bar\n * AND the legacy assistant) without rebuilding the SDK.\n */\nfunction debug(...args: unknown[]): void {\n if (typeof window === 'undefined') return;\n if ((window as unknown as { __SYNTRO_CHAT_DEBUG__?: boolean }).__SYNTRO_CHAT_DEBUG__) {\n console.debug('[chat-transport]', ...args);\n }\n}\n\nexport class ChatTransport {\n private _config: ChatTransportConfig | null = null;\n private _status: ChatTransportStatus = 'idle';\n private _agui: AgUiTransport | null = null;\n private _transportUnsub: (() => void) | null = null;\n private _sessionUnsubSend: (() => void) | null = null;\n private _sessionUnsubInterrupt: (() => void) | null = null;\n private _sessionUnsubToolResult: (() => void) | null = null;\n private _connectInFlight: Promise<boolean> | null = null;\n /** Bounded streaming-message id for the currently-being-typed assistant turn. */\n private _currentAssistantMessageId: string | null = null;\n /** Outcome bookkeeping for telemetry. */\n private _hadTurnstileToken: boolean | null = null;\n /** Count of successful round-trips for telemetry payloads. */\n private _messagesSucceeded = 0;\n /** True once a successful assistant message has landed \u2014 gates fallback. */\n private _hasSucceeded = false;\n /** True once fallback has fired (one-shot). */\n private _fallbackRendered = false;\n /** Active debounce timer; null when no debounce pending. */\n private _errorDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n /** Most recent error payload, captured so debounced fallback can attach it. */\n private _lastErrorPayload: {\n message?: string;\n status?: number | null;\n body?: string | null;\n errorName?: string | null;\n } | null = null;\n private _fallbackListeners = new Set<FallbackListener>();\n /**\n * Per-configure-cycle id for correlating telemetry events from a\n * single transport lifetime. Mirrors ChatAssistantLit's mountId\n * but scoped to configure cycles since the transport is a\n * singleton across mounts.\n */\n private _transportId = `tx_${Math.random().toString(36).slice(2, 8)}`;\n private _configuredAt = 0;\n\n /**\n * Configure the transport. Idempotent \u2014 calling again with the same\n * backendUrl is a no-op; calling with a different backendUrl tears\n * the connection down and re-arms.\n */\n configure(config: ChatTransportConfig): void {\n const same =\n this._config &&\n this._config.backendUrl === config.backendUrl &&\n this._config.threadId === config.threadId;\n if (same) {\n // Allow runtime + clientTools to be replaced without reconnect \u2014\n // the bar may remount with a fresh runtime closure.\n this._config = { ...this._config, ...config };\n return;\n }\n this._disconnect();\n this._config = config;\n this._status = 'configured';\n this._transportId = `tx_${Math.random().toString(36).slice(2, 8)}`;\n this._configuredAt = Date.now();\n this._wireSession();\n debug('configured', {\n transportId: this._transportId,\n backendUrl: config.backendUrl,\n threadId: config.threadId,\n });\n }\n\n /** ms since the most recent configure() call. 0 before any configure. */\n private _ageMs(): number {\n return this._configuredAt === 0 ? 0 : Date.now() - this._configuredAt;\n }\n\n /** True when configure() has been called and we're ready to lazy-connect on send. */\n get isConfigured(): boolean {\n return this._status !== 'idle';\n }\n\n /** True when AgUiTransport is up. */\n get isConnected(): boolean {\n return this._status === 'connected';\n }\n\n /**\n * Subscribe to fallback events \u2014 fires once per configure cycle\n * when the transport gives up after sustained failure. Hosts\n * (typically AdaptiveChatBarMountable) use this to swap the chat\n * bar for a static \"contact support\" card.\n */\n onFallback(listener: FallbackListener): () => void {\n this._fallbackListeners.add(listener);\n return () => {\n this._fallbackListeners.delete(listener);\n };\n }\n\n /**\n * Test seam \u2014 drive a synthetic error event through the transport's\n * error handling without standing up a real AgUi transport. Production\n * code path uses _onTransportEvent. Exported as a public method to\n * keep the test isolation simple; not part of the documented API.\n */\n simulateError(payload: {\n message?: string;\n status?: number | null;\n body?: string | null;\n errorName?: string | null;\n }): void {\n this._handleErrorEvent(payload);\n }\n\n /**\n * Test seam \u2014 register a synthetic successful message-complete so\n * the hasSucceeded gate flips without a real AgUi round-trip.\n */\n simulateSuccessfulMessage(): void {\n this._hasSucceeded = true;\n this._messagesSucceeded += 1;\n this._clearDebounceTimer();\n }\n\n /**\n * Tear connection-level state down. Used internally by configure()\n * to swap backends; preserves host-registered fallback listeners\n * because the host UI handler (e.g. AdaptiveChatBarMountable's\n * \"swap to fallback card\" callback) is configuration-independent.\n */\n private _disconnect(): void {\n if (this._transportUnsub) {\n this._transportUnsub();\n this._transportUnsub = null;\n }\n if (this._sessionUnsubSend) {\n this._sessionUnsubSend();\n this._sessionUnsubSend = null;\n }\n if (this._sessionUnsubInterrupt) {\n this._sessionUnsubInterrupt();\n this._sessionUnsubInterrupt = null;\n }\n if (this._sessionUnsubToolResult) {\n this._sessionUnsubToolResult();\n this._sessionUnsubToolResult = null;\n }\n if (this._agui) {\n this._agui.disconnect();\n this._agui = null;\n }\n this._clearDebounceTimer();\n this._config = null;\n this._status = 'idle';\n this._connectInFlight = null;\n this._currentAssistantMessageId = null;\n this._hadTurnstileToken = null;\n this._messagesSucceeded = 0;\n this._hasSucceeded = false;\n this._fallbackRendered = false;\n this._lastErrorPayload = null;\n }\n\n /**\n * Tear everything down \u2014 connection state PLUS host listeners.\n * Use this in test teardown or when fully shutting the transport\n * (page unload, integration test reset). The mountable calls\n * _disconnect indirectly via reconfigure.\n */\n reset(): void {\n this._disconnect();\n this._fallbackListeners.clear();\n }\n\n private _clearDebounceTimer(): void {\n if (this._errorDebounceTimer) {\n clearTimeout(this._errorDebounceTimer);\n this._errorDebounceTimer = null;\n }\n }\n\n /**\n * Shared error-handling kernel \u2014 called by the AG-UI subscriber and\n * by the simulateError test seam. Publishes transport_error\n * telemetry, captures lastErrorPayload, and starts the debounce\n * timer that will fire fallback if no successful message arrives\n * before it expires. Gated by hasSucceeded (post-success errors\n * never fallback) and _fallbackRendered (one-shot).\n */\n private _handleErrorEvent(payload: {\n message?: string;\n status?: number | null;\n body?: string | null;\n errorName?: string | null;\n }): void {\n const status = payload.status ?? null;\n const body = payload.body ? String(payload.body).slice(0, 200) : null;\n this._lastErrorPayload = {\n message: payload.message,\n status,\n body,\n errorName: payload.errorName ?? null,\n };\n\n this._config?.runtime.events.publish('chatbot.transport_error', {\n source: 'chat-transport',\n transportId: this._transportId,\n transportAgeMs: this._ageMs(),\n messagesSucceeded: this._messagesSucceeded,\n hadTurnstileToken: this._hadTurnstileToken,\n hasSucceeded: this._hasSucceeded,\n errorMessage: payload.message ?? null,\n errorStatus: status,\n errorBody: body,\n errorName: payload.errorName ?? null,\n });\n\n if (this._hasSucceeded || this._fallbackRendered || this._errorDebounceTimer) return;\n this._errorDebounceTimer = setTimeout(() => {\n this._errorDebounceTimer = null;\n if (this._hasSucceeded || this._fallbackRendered) return;\n this._renderFallback('connect_failed');\n }, FALLBACK_DEBOUNCE_MS);\n }\n\n /**\n * Fire the fallback. One-shot per configure cycle. Notifies all\n * fallback listeners with the per-customer card config and the\n * diagnostic snapshot for telemetry/debug.\n */\n private _renderFallback(reason: FallbackPayload['reason']): void {\n if (this._fallbackRendered) return;\n this._fallbackRendered = true;\n debug('fallback', { reason, transportId: this._transportId, ageMs: this._ageMs() });\n const fallback = this._config?.fallback ?? {};\n const payload: FallbackPayload = {\n reason,\n fallback,\n transportId: this._transportId,\n transportAgeMs: this._ageMs(),\n messagesSucceeded: this._messagesSucceeded,\n hadTurnstileToken: this._hadTurnstileToken,\n errorStatus: this._lastErrorPayload?.status ?? null,\n errorBody: this._lastErrorPayload?.body ?? null,\n errorMessage: this._lastErrorPayload?.message ?? null,\n errorName: this._lastErrorPayload?.errorName ?? null,\n };\n this._config?.runtime.events.publish(\n 'chatbot.fallback_rendered',\n payload as unknown as Record<string, unknown>\n );\n for (const listener of this._fallbackListeners) listener(payload);\n }\n\n // -------------------------------------------------------------------------\n // Internal: chatSession wiring\n // -------------------------------------------------------------------------\n\n private _wireSession(): void {\n this._sessionUnsubSend = chatSession.onSend(({ text }) => {\n void this._forwardUserMessage(text);\n });\n this._sessionUnsubInterrupt = chatSession.onInterrupt(() => {\n this._agui?.send({ type: 'stop-generation' });\n });\n this._sessionUnsubToolResult = chatSession.onToolResult(({ toolCallId, result, approved }) => {\n this._agui?.send({ type: 'tool-result', toolCallId, result, approved });\n });\n }\n\n private async _forwardUserMessage(text: string): Promise<void> {\n const ok = await this._ensureConnected();\n if (!ok || !this._agui) {\n // Connect failed; surface to the session so the bar shows an error\n // chip and clears inFlight (otherwise the user is stuck on the\n // \u23F9 stop button forever).\n chatSession.error(\"Couldn't connect to chat. Please try again.\");\n return;\n }\n this._agui.send({ type: 'user-message', text });\n }\n\n // -------------------------------------------------------------------------\n // Internal: connect (lazy, Turnstile-gated)\n // -------------------------------------------------------------------------\n\n /**\n * Ensure AgUiTransport is up. Idempotent \u2014 multiple concurrent calls\n * dedupe to a single Turnstile acquisition + transport setup.\n * Returns true on success, false on terminal failure.\n */\n private async _ensureConnected(): Promise<boolean> {\n if (this._status === 'connected' && this._agui) return true;\n if (!this._config) return false;\n if (this._connectInFlight) return this._connectInFlight;\n\n this._connectInFlight = (async () => {\n this._status = 'acquiring';\n const cft = await this._acquireTurnstileWithChallenge();\n this._hadTurnstileToken = cft !== null;\n\n if (this._config === null) return false; // raced with reset()\n\n const baseUrl = this._config.backendUrl.replace(/\\/$/, '');\n const streamUrl = `${baseUrl}/api/adaptive/stream`;\n const token = readSyntroToken();\n const buildHeaders = (): Record<string, string> => {\n const h: Record<string, string> = {};\n if (token) h.Authorization = `Bearer ${token}`;\n if (cft) h['CF-Turnstile-Token'] = cft;\n return h;\n };\n\n const runtime = this._config.runtime;\n // Resolve forwardedProps at run-time (each AG-UI request), not at\n // transport construction. The config can be updated via\n // `configure()` with the same backendUrl (no reconnect), so the\n // late-bound closure keeps the transport in sync with the most\n // recent uiTemplates / elementsEnabled declarations.\n const resolveForwardedProps = (): Record<string, unknown> | undefined => {\n const raw = this._config?.forwardedProps;\n return typeof raw === 'function' ? raw() : raw;\n };\n this._agui = new AgUiTransport({\n url: streamUrl,\n headers: buildHeaders,\n threadId: this._config.threadId,\n clientTools: this._config.clientTools,\n forwardedProps: resolveForwardedProps,\n onA2UIEvent: (payload) => {\n // Discriminate `syntro.element.mutation` envelopes (LLM-\n // authored UI element mounts/patches/unmounts) from legacy\n // A2UI / raw ActionStep payloads. Mirrors ChatAssistantLit's\n // routing \u2014 the chat-bar lid surface needs the same wiring so\n // mount_element / patch_element / unmount_element results\n // actually land on the host page.\n const mutations = decodeMutationEnvelope(payload);\n if (mutations !== null) {\n const handler = this._config?.onElementMutation;\n if (handler) {\n try {\n handler(mutations);\n runtime.events.publish('chatbot.element_mutation_applied', {\n source: 'chat-transport',\n count: mutations.length,\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[chat-transport] element mutation apply failed:', msg);\n }\n return;\n }\n // No handler registered \u2014 the surface using this transport\n // didn't opt into element mutations. Drop silently rather\n // than feeding a MutationEnvelope into applyBatch (which\n // would no-op or warn).\n return;\n }\n runtime.actions\n .applyBatch([payload as unknown as Record<string, unknown>])\n .then(() => {\n runtime.events.publish('chatbot.a2ui_applied', { source: 'chat-transport' });\n })\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[chat-transport] A2UI apply failed:', msg);\n });\n },\n });\n this._agui.connect();\n this._transportUnsub = this._agui.subscribe((event) => this._onTransportEvent(event));\n this._status = 'connected';\n return true;\n })();\n\n try {\n return await this._connectInFlight;\n } finally {\n this._connectInFlight = null;\n }\n }\n\n /**\n * Acquire a Turnstile token via the managed-challenge flow.\n * Delegates to the shared Turnstile helper that owns the verify\n * panel lifecycle. Returns null when Turnstile is disabled at\n * build time or acquisition fails.\n */\n private async _acquireTurnstileWithChallenge(): Promise<string | null> {\n const { token } = await acquireTokenWithChallenge();\n return token;\n }\n\n // -------------------------------------------------------------------------\n // Internal: AgUi event \u2192 chatSession\n // -------------------------------------------------------------------------\n\n /**\n * Look up a tool call's current chatSession-side status by id.\n * Used to decide between addToolCall (first sighting) and\n * updateToolCall (subsequent updates) when AG-UI re-emits\n * `tool-call` events for client tools transitioning to 'pending'.\n */\n private _findToolCallStatus(toolCallId: string): TrailToolCall['status'] | null {\n const state = chatSession.getState();\n for (const msg of state.messages) {\n const tc = msg.toolCalls?.find((t) => t.id === toolCallId);\n if (tc) return tc.status;\n }\n return null;\n }\n\n /** Map AG-UI ToolCallStatus to TrailToolCall status. */\n private _mapToolCallStatus(status: string): TrailToolCall['status'] {\n switch (status) {\n case 'args-streaming':\n return 'args-streaming';\n case 'pending':\n return 'pending';\n case 'running':\n return 'running';\n case 'done':\n return 'done';\n case 'error':\n return 'error';\n default:\n return 'running';\n }\n }\n\n private _onTransportEvent(event: ServerEvent): void {\n const runtime = this._config?.runtime;\n\n switch (event.type) {\n case 'session-ready':\n case 'messages-snapshot':\n case 'typing':\n // Local-only events from AgUiTransport.connect() \u2014 no UI impact.\n return;\n\n case 'message-append': {\n // Start of a new streaming assistant message.\n this._currentAssistantMessageId = event.message.id;\n chatSession.receiveStart(event.message.id);\n if (event.message.content && event.message.content.length > 0) {\n chatSession.receiveDelta(event.message.id, event.message.content);\n }\n return;\n }\n\n case 'message-delta': {\n const id = event.messageId ?? this._currentAssistantMessageId;\n if (!id || !event.delta) return;\n chatSession.receiveDelta(id, event.delta);\n return;\n }\n\n case 'message-complete': {\n const id = event.messageId ?? this._currentAssistantMessageId;\n if (!id) return;\n chatSession.receiveEnd(id);\n this._currentAssistantMessageId = null;\n this._messagesSucceeded += 1;\n // Flip the hasSucceeded gate: errors after this point will no\n // longer trigger a fallback swap (matches ChatAssistantLit).\n this._hasSucceeded = true;\n this._clearDebounceTimer();\n return;\n }\n\n case 'tool-call': {\n // tool-call arrives twice in the AG-UI flow: once at start\n // (status='args-streaming') and once at end for client tools\n // with status='pending' awaiting user approval. Add on the\n // first sighting, update on the second.\n const targetMessageId = event.messageId ?? this._currentAssistantMessageId;\n if (!targetMessageId) return;\n const existing = this._findToolCallStatus(event.toolCall.id);\n if (!existing) {\n chatSession.addToolCall(targetMessageId, {\n id: event.toolCall.id,\n name: event.toolCall.name,\n status: this._mapToolCallStatus(event.toolCall.status),\n });\n } else {\n chatSession.updateToolCall(event.toolCall.id, {\n name: event.toolCall.name,\n status: this._mapToolCallStatus(event.toolCall.status),\n });\n }\n return;\n }\n\n case 'tool-call-args-delta':\n // We don't currently surface streaming args in the trail chip\n // (too noisy for the compact UI), but a future expanded view\n // could subscribe and render them. Logged under debug.\n debug('tool-call-args-delta', event);\n return;\n\n case 'tool-call-done':\n // Server-side tool finished. Mark complete in the trail so the\n // chip transitions from \"running\" to \"done\". Client-tool\n // resolution goes through chatSession.resolveToolCall instead.\n chatSession.updateToolCall(event.toolCallId, { status: 'done' });\n return;\n\n case 'a2ui':\n // A2UI events also flow via onA2UIEvent in the transport\n // constructor \u2014 that path is what applies the batch. This\n // duplicate event is the public mirror; ignore here.\n return;\n\n case 'error': {\n const status = event.status ?? null;\n const body = event.body ? String(event.body).slice(0, 200) : null;\n console.warn(\n `[chat-transport] error status=${status ?? 'no-status'} succeeded=${this._messagesSucceeded}`,\n event\n );\n // Route through the shared error-handling kernel so the\n // debounce / hasSucceeded gate / fallback path apply uniformly\n // regardless of whether the error came from AG-UI or a test seam.\n this._handleErrorEvent({\n message: event.message,\n status,\n body,\n errorName: event.errorName ?? null,\n });\n // The chip-level error chip (shown immediately, no debounce)\n // gives the user feedback even when we're still waiting to see\n // if the failure is sustained enough to swap to the fallback.\n chatSession.error(event.message ?? 'Chat connection failed');\n this._currentAssistantMessageId = null;\n // Tear AgUiTransport down so the next send re-acquires Turnstile\n // and re-opens. Avoids reusing a broken HttpAgent under us.\n if (this._agui) {\n this._agui.disconnect();\n this._agui = null;\n }\n if (this._transportUnsub) {\n this._transportUnsub();\n this._transportUnsub = null;\n }\n this._status = 'error';\n return;\n }\n }\n }\n}\n\n/**\n * The module-level singleton. Imported by both the SDK runtime\n * bootstrap (to call configure) and any code that needs to read\n * transport state. Mirrors the chatSession singleton shape.\n */\nexport const chatTransport = new ChatTransport();\n", "/**\n * AdaptiveChatBarMountable \u2014 `MountableWidget` adapter that turns the\n * standalone `<adaptive-chat-bar>` Lit element into something the\n * runtime widget registry can mount.\n *\n * Registered as widget id `adaptive-chatbot:chat-bar`. Customers\n * reference it from canvas config \u2014 either as a slot's `lid` widget\n * (replaces the launcher when referenced from `slots.drawer.lid`) or\n * as a regular tile widget.\n *\n * The bar is a VIEW. State lives in the singleton `chatSession` \u2014\n * whether the bar mounts in the mini-canvas lid, the drawer lid, or\n * both, they're all views of the same conversation. The mountable:\n * - subscribes to chatSession.subscribe() and pushes state into\n * the bar's `messages` + `inFlight` props on every change\n * - routes the bar's `chat-message-sent` event \u2192 chatSession.send()\n * - routes the bar's `chat-interrupt` event \u2192 chatSession.interrupt()\n * - bubbles the bar's `canvas-close` event \u2192 optional onClose\n * callback (canvas-level concern, not session-level)\n *\n * Props (passed via mountConfig):\n * - `placeholder?: string` \u2014 input placeholder copy\n * - `onClose?: () => void` \u2014 fires on \u2715 click (canvas-level)\n *\n * Note: messages and inFlight are NOT props. They flow from chatSession.\n * A canvas-config author who wants to pre-seed messages should do so\n * via the session directly (e.g. via a startup action) \u2014 but typical\n * use is to let the conversation start empty.\n */\n\nimport './AdaptiveChatBar';\nimport type { AdaptiveChatBar } from './AdaptiveChatBar';\nimport { renderFallbackHtml } from './ChatAssistantLit';\nimport type { Unsubscribe } from './ChatSession';\nimport { chatSession } from './ChatSession';\nimport type { ChatTransportConfig } from './ChatTransport';\nimport { chatTransport } from './ChatTransport';\nimport {\n ActionHandler,\n ElementInstanceStore,\n type ElementMutation,\n fetchMountedElements,\n ItemHandler,\n TileHandler,\n} from './elements';\nimport type { ChatbotFallback, ChatbotWidgetRuntime } from './types.js';\n\ninterface ChatBarMountConfig {\n placeholder?: string;\n /** Initial assistant greeting shown before any real conversation. */\n greeting?: string;\n onClose?: () => void;\n /**\n * Backend host for the live chat transport. Same shape as\n * ChatbotConfig.backendUrl. When provided alongside `runtime`, the\n * bar configures the singleton ChatTransport on first mount so\n * sends hit the real `/api/adaptive/stream` SSE pipeline. Omitting\n * either disables the transport \u2014 the bar stays a pure view and\n * nothing replies. Useful for storybook / config-preview surfaces.\n */\n backendUrl?: string;\n /** Runtime injected by WidgetRegistry \u2014 required for backendUrl wiring. */\n runtime?: ChatbotWidgetRuntime;\n /** Optional thread id for conversation continuity across reloads. */\n threadId?: string;\n /**\n * Per-customer \"chat unavailable\" card. Rendered in place of the\n * chat bar when the transport gives up after sustained failure.\n * Mirrors ChatAssistantLit's fallback behaviour.\n */\n fallback?: ChatbotFallback;\n /**\n * Declarations for the LLM-authored UI element pipeline (Phase 5+).\n * When `elementsEnabled === true` AND `uiTemplates` is non-null, the\n * backend exposes `mount_element` / `patch_element` / `unmount_element`\n * tools to the chat agent, scoped to the templates declared here.\n *\n * Shape mirrors `UiTemplatesBlock` from `syntrologie_common.sdk.templates`\n * \u2014 typed loosely on the SDK side because the SDK never introspects\n * templates (it only receives mount/patch/unmount mutations); the\n * backend Pydantic models are the validation gate.\n *\n * Passed verbatim as `forwardedProps.uiTemplates` to the AG-UI\n * transport so the backend's `/api/adaptive/stream` reads them off\n * the inbound request body.\n */\n uiTemplates?: Record<string, unknown>;\n elementsEnabled?: boolean;\n}\n\ninterface MountState {\n bar: AdaptiveChatBar;\n cleanup: () => void;\n cfg: ChatBarMountConfig;\n}\n\nconst STATE_KEY = '__syntroChatBarMount';\n\nfunction getState(container: HTMLElement): MountState | null {\n return (container as unknown as Record<string, MountState | undefined>)[STATE_KEY] ?? null;\n}\nfunction setState(container: HTMLElement, state: MountState | null): void {\n (container as unknown as Record<string, MountState | null>)[STATE_KEY] = state;\n}\n\nfunction applyPlaceholder(bar: AdaptiveChatBar, cfg: ChatBarMountConfig): void {\n if (cfg.placeholder !== undefined) bar.placeholder = cfg.placeholder;\n if (cfg.greeting !== undefined) bar.greeting = cfg.greeting;\n}\n\n/**\n * Configure the singleton transport if the caller supplied a backend\n * URL + runtime. Idempotent \u2014 second mount with same args is a no-op.\n * Missing backendUrl OR runtime leaves the transport untouched (the\n * bar is then a pure local view; useful for previews / storybook).\n *\n * Spread (not cherry-pick) so the bar-only fields (placeholder,\n * onClose) flow through silently; ChatTransport ignores them. Cast\n * narrows after the guard.\n */\n/**\n * Lazily-constructed ElementInstanceStore singleton for the chat-bar\n * surface. Mounted on first configure with `elementsEnabled` so the\n * `runtime` closure is captured; subsequent reconfigures with the same\n * runtime reuse it. ChatAssistantLit owns its own store separately \u2014\n * each surface keeps independent state to avoid cross-surface\n * double-mounts on the same instance_id.\n */\nlet _elementStore: ElementInstanceStore | null = null;\n\nfunction getOrCreateElementStore(runtime: ChatbotWidgetRuntime): ElementInstanceStore {\n if (_elementStore) return _elementStore;\n _elementStore = new ElementInstanceStore({\n actions: runtime.actions,\n // Pass the runtime's event bus so ItemHandler can broadcast\n // `element.compositional_append` / `_patch` / `_remove` events\n // to container widgets (chips strip, FAQ accordion, nav tips).\n events: {\n publish: runtime.events.publish.bind(runtime.events),\n subscribe: runtime.events.subscribe?.bind(runtime.events),\n },\n handlers: [new TileHandler(), new ActionHandler(), new ItemHandler()],\n });\n return _elementStore;\n}\n\n/**\n * Track whether we've already hydrated this store's snapshot. The store\n * itself is a singleton across mounts in the same page session, so we\n * want to hydrate exactly ONCE \u2014 repeated mounts (e.g. after an SPA\n * navigation that unmounts + remounts the chat-bar) reuse the warm\n * store without re-firing handler.mount() for every persisted item.\n *\n * The store's own version-dedup also prevents double-application, but\n * we still don't want to issue redundant HTTP requests on every mount.\n */\nlet _hydrationStarted = false;\n\nfunction hydrateOnce(runtime: ChatbotWidgetRuntime, backendUrl: string): void {\n if (_hydrationStarted) return;\n _hydrationStarted = true;\n const store = getOrCreateElementStore(runtime);\n // The fetcher reads `window.__SYNTRO_CONFIG__.token` by default; the\n // boot path already populated that. Empty/missing backendUrl falls\n // back to a same-origin relative endpoint so dev-server proxies\n // (Vite, Webpack) and host pages serving the SDK from their own\n // domain work without extra config. Same-origin is also the only\n // path that lets the `syntro_chat_session` cookie ride along \u2014 Lax\n // cookies don't follow cross-origin fetches.\n const trimmed = backendUrl.replace(/\\/$/, '');\n const endpoint = trimmed\n ? `${trimmed}/api/adaptive/mounted_elements`\n : '/api/adaptive/mounted_elements';\n fetchMountedElements({ endpoint }).then((response) => {\n if (!response) return; // 404 / network error / feature off \u2014 silent no-op\n // Hydrate replays each instance through its handler.mount(); for\n // ItemHandler that publishes `element.compositional_append` events,\n // which the chips strip subscribes to and re-inserts. Effects are\n // NOT replayed \u2014 they were never persisted in mounted_elements\n // (`_emit_effect_envelope` in the agent skips persistence).\n void store.hydrate(response.mounted_elements);\n });\n}\n\nfunction configureTransportIfPossible(cfg: ChatBarMountConfig): void {\n // backendUrl === \"\" is a valid configuration \u2014 the chat-bar makes\n // relative-URL fetches and the host page's dev-server proxy (or\n // production reverse proxy) forwards `/api/...` to the backend.\n // This keeps the chat on the page's own origin, which lets cookies\n // ride along even when the backend container is on a different host.\n if (cfg.backendUrl === undefined || !cfg.runtime) return;\n // Translate the element-instantiation declarations into the opaque\n // `forwardedProps` bag the transport ships on every AG-UI request.\n // Only attach when the feature is on AND templates are declared \u2014\n // otherwise the backend stays in its safe-default (feature_disabled)\n // path and the tools are never exposed.\n const elementsActive = cfg.elementsEnabled === true && cfg.uiTemplates != null;\n const forwardedProps: Record<string, unknown> | undefined = elementsActive\n ? { elementsEnabled: true, uiTemplates: cfg.uiTemplates }\n : undefined;\n\n // Wire the `syntro.element.mutation` envelope route to a per-surface\n // ElementInstanceStore. The store applies mounts/patches/unmounts to\n // the host page via the runtime's ActionEngine. ChatAssistantLit has\n // its own equivalent wiring (it instantiates AgUiTransport directly,\n // not via this singleton); the lid surface needs the same routing\n // here so mount_element results actually land.\n const runtime = cfg.runtime;\n const onElementMutation = elementsActive\n ? (mutations: ElementMutation[]) => {\n void getOrCreateElementStore(runtime).apply(mutations);\n }\n : undefined;\n\n // Hydrate the element store from the backend session on first chat-bar\n // mount. Async \u2014 by the time the response arrives, container widgets\n // (chips strip, FAQ accordion) have already subscribed to the event\n // bus, so the replay events land in their subscribers. Items mounted\n // by the LLM in previous turns survive SPA navigation + page reload\n // for the lifetime of the AdaptiveSession (~24h default).\n if (elementsActive) {\n hydrateOnce(runtime, cfg.backendUrl);\n }\n\n chatTransport.configure({\n ...cfg,\n forwardedProps,\n onElementMutation,\n } as ChatTransportConfig);\n}\n\nfunction wireListeners(bar: AdaptiveChatBar, state: MountState): Unsubscribe {\n const onMessageSent = (e: Event) => {\n const text = (e as CustomEvent<{ text: string }>).detail.text;\n chatSession.send(text);\n // If no transport is listening for sends (no backendUrl in\n // mountConfig, the runtime injection didn't happen, or a test\n // didn't wire one up) the session would sit in `inFlight: true`\n // forever. Surface a clear, actionable error so the user sees\n // *something* rather than an indefinitely-spinning stop button.\n if (!chatSession.hasTransport()) {\n chatSession.error('Chat backend not configured \u2014 set backendUrl in the canvas config.');\n }\n };\n const onInterrupt = () => {\n chatSession.interrupt();\n };\n const onToolCallApproved = (e: Event) => {\n const detail = (e as CustomEvent<{ toolCallId: string; approved: boolean }>).detail;\n // Default to an empty-object result \u2014 the agent re-runs with the\n // approval signal. Hosts that want richer results (e.g. a form\n // result payload) can listen for `trail-toolcall-approved` higher\n // up and call chatSession.resolveToolCall themselves.\n chatSession.resolveToolCall(detail.toolCallId, {}, detail.approved);\n };\n // onClose reads state.cfg fresh on each invocation so update() can\n // swap the callback without re-wiring listeners.\n const onClose = () => {\n state.cfg.onClose?.();\n };\n bar.addEventListener('chat-message-sent', onMessageSent);\n bar.addEventListener('chat-interrupt', onInterrupt);\n bar.addEventListener('canvas-close', onClose);\n bar.addEventListener('trail-toolcall-approved', onToolCallApproved);\n return () => {\n bar.removeEventListener('chat-message-sent', onMessageSent);\n bar.removeEventListener('chat-interrupt', onInterrupt);\n bar.removeEventListener('canvas-close', onClose);\n bar.removeEventListener('trail-toolcall-approved', onToolCallApproved);\n };\n}\n\nexport const AdaptiveChatBarMountable = {\n mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void {\n const cfg = (mountConfig ?? {}) as ChatBarMountConfig;\n configureTransportIfPossible(cfg);\n const bar = document.createElement('adaptive-chat-bar') as AdaptiveChatBar;\n applyPlaceholder(bar, cfg);\n\n // Subscribe to the shared session. Fires immediately with the\n // current state so the bar reflects any pre-existing conversation.\n const unsubSession = chatSession.subscribe((s) => {\n bar.messages = [...s.messages];\n bar.inFlight = s.inFlight;\n });\n\n // state holds the mutable cfg so update() can swap callbacks\n // without forcing a listener re-wire.\n const state = { bar, cleanup: () => {}, cfg };\n const unwireListeners = wireListeners(bar, state);\n\n container.appendChild(bar);\n\n // Subscribe to the transport's fallback signal. When the transport\n // gives up after sustained failure, replace the bar with the\n // per-customer \"chat unavailable\" card. One-shot \u2014 fallback fires\n // once per configure cycle, so we don't need to track re-mounts.\n const unsubFallback = chatTransport.onFallback(() => {\n bar.remove();\n container.innerHTML = renderFallbackHtml(state.cfg.fallback);\n });\n\n state.cleanup = () => {\n unsubSession();\n unsubFallback();\n unwireListeners();\n bar.remove();\n setState(container, null);\n };\n\n setState(container, state);\n return state.cleanup;\n },\n\n update(container: HTMLElement, mountConfig?: Record<string, unknown>): void {\n const state = getState(container);\n if (!state) return;\n const cfg = (mountConfig ?? {}) as ChatBarMountConfig;\n configureTransportIfPossible(cfg);\n applyPlaceholder(state.bar, cfg);\n state.cfg = cfg;\n },\n};\n", "/**\n * AdaptiveChipsStripMountable \u2014 `MountableWidget` adapter for\n * `<adaptive-chips-strip>`. Registered as widget id\n * `adaptive-chatbot:chips-strip`.\n *\n * Mirrors `AdaptiveChatBarMountable`: creates the Lit element, forwards\n * props, bridges DOM events \u2192 prop callbacks, cleans up on unmount.\n *\n * Props (passed via mountConfig):\n * - `chips: SuggestionChip[]` \u2014 the strip's contents (required)\n * - `onChipRevealed?: ({id, payload}) => void` \u2014 fires when a chip\n * is opened\n * - `onChipDismissed?: ({id}) => void` \u2014 fires when \u00D7 is clicked\n *\n * The runtime registry injects `runtime` into mountConfig (see\n * `WidgetRegistry.mount`). We forward it as `runtimeRef` on the strip\n * so it can mount chip payload widgets through the same registry.\n */\n\nimport './AdaptiveChipsStrip';\nimport type { AdaptiveChipsStrip, SuggestionChip } from './AdaptiveChipsStrip';\n\ninterface ChipsStripMountConfig {\n chips?: SuggestionChip[];\n onChipRevealed?: (detail: { id: string; payload: SuggestionChip['config']['payload'] }) => void;\n onChipDismissed?: (detail: { id: string }) => void;\n // Injected by WidgetRegistry \u2014 see runtime-sdk/src/widgets/WidgetRegistry.ts.\n runtime?: unknown;\n // Injected by SyntroTileCard from the tile's resolved chromeless\n // state (which itself comes from `slot.theme.chromeless` with\n // `tile.chromeless` as the per-tile override).\n chromeless?: boolean;\n}\n\ninterface MountState {\n strip: AdaptiveChipsStrip;\n listeners: {\n revealed: (e: Event) => void;\n dismissed: (e: Event) => void;\n };\n}\n\nconst STATE_KEY = '__syntroChipsStripMount';\n\nfunction getState(container: HTMLElement): MountState | null {\n return (container as unknown as Record<string, MountState | undefined>)[STATE_KEY] ?? null;\n}\nfunction setState(container: HTMLElement, state: MountState | null): void {\n (container as unknown as Record<string, MountState | null>)[STATE_KEY] = state;\n}\n\nfunction applyProps(strip: AdaptiveChipsStrip, cfg: ChipsStripMountConfig): void {\n if (cfg.chips !== undefined) strip.chips = cfg.chips;\n // `runtime` arrives in every mountConfig from the WidgetRegistry \u2014\n // forward it so the strip can resolve + mount payload widgets.\n if (cfg.runtime !== undefined) {\n strip.runtimeRef = cfg.runtime as AdaptiveChipsStrip['runtimeRef'];\n }\n if (cfg.chromeless !== undefined) strip.chromeless = cfg.chromeless;\n}\n\nexport const AdaptiveChipsStripMountable = {\n mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void {\n const cfg = (mountConfig ?? {}) as ChipsStripMountConfig;\n const strip = document.createElement('adaptive-chips-strip') as AdaptiveChipsStrip;\n applyProps(strip, cfg);\n\n const state: MountState = {\n strip,\n listeners: {\n revealed: (e) => {\n const cb = cfg.onChipRevealed;\n if (cb) {\n cb(\n (e as CustomEvent<{ id: string; payload: SuggestionChip['config']['payload'] }>)\n .detail\n );\n }\n },\n dismissed: (e) => {\n const cb = cfg.onChipDismissed;\n if (cb) cb((e as CustomEvent<{ id: string }>).detail);\n },\n },\n };\n strip.addEventListener('chip-revealed', state.listeners.revealed);\n strip.addEventListener('chip-dismissed', state.listeners.dismissed);\n\n container.appendChild(strip);\n setState(container, state);\n\n return () => {\n strip.removeEventListener('chip-revealed', state.listeners.revealed);\n strip.removeEventListener('chip-dismissed', state.listeners.dismissed);\n strip.remove();\n setState(container, null);\n };\n },\n\n update(container: HTMLElement, mountConfig?: Record<string, unknown>): void {\n const state = getState(container);\n if (!state) return;\n const cfg = (mountConfig ?? {}) as ChipsStripMountConfig;\n applyProps(state.strip, cfg);\n // Re-wire callback listeners so the new cfg's handlers fire.\n state.strip.removeEventListener('chip-revealed', state.listeners.revealed);\n state.strip.removeEventListener('chip-dismissed', state.listeners.dismissed);\n state.listeners.revealed = (e) => {\n const cb = cfg.onChipRevealed;\n if (cb) {\n cb((e as CustomEvent<{ id: string; payload: SuggestionChip['config']['payload'] }>).detail);\n }\n };\n state.listeners.dismissed = (e) => {\n const cb = cfg.onChipDismissed;\n if (cb) cb((e as CustomEvent<{ id: string }>).detail);\n };\n state.strip.addEventListener('chip-revealed', state.listeners.revealed);\n state.strip.addEventListener('chip-dismissed', state.listeners.dismissed);\n },\n};\n", "/**\n * NavLinkMountable \u2014 `MountableWidget` used as the payload of an\n * LLM-authored navigation chip.\n *\n * Flow:\n * LLM mounts `suggest-navigation` ItemTemplate with `{title, url}`\n * \u2192 backend builds chip with payload `{widget: 'adaptive-chatbot:nav-link', props: {url}}`\n * \u2192 user clicks the chip \u2192 chips-strip mounts THIS widget into the\n * chip's drawer\n * \u2192 user clicks the \"Go to <url>\" button this widget renders\n * \u2192 same-origin SPA navigation via pushState + popstate (matches\n * `adaptive-nav`'s `executeNavigate` path for SPA-router compatibility)\n *\n * Registered as widget id `adaptive-chatbot:nav-link`.\n *\n * Security note: only http(s), same-origin destinations are honored.\n * `javascript:`, `data:`, and cross-origin URLs are silently dropped at\n * URL construction. The ItemTemplate's EnumField allowlist is the\n * primary constraint; this is defense-in-depth.\n */\n\ninterface NavLinkProps {\n url?: string;\n /** Optional override for the button label. Defaults to \"Go to <url>\".\n * The chip's title already advertises the destination; this is just\n * the call-to-action verb. */\n label?: string;\n}\n\ninterface MountState {\n button: HTMLButtonElement;\n cfg: NavLinkProps;\n onClick: (e: Event) => void;\n}\n\nconst STATE_KEY = '__syntroNavLinkMount';\n\nfunction getState(container: HTMLElement): MountState | null {\n return (container as unknown as Record<string, MountState | undefined>)[STATE_KEY] ?? null;\n}\nfunction setState(container: HTMLElement, state: MountState | null): void {\n (container as unknown as Record<string, MountState | null>)[STATE_KEY] = state;\n}\n\nfunction navigateTo(rawUrl: string): void {\n if (!rawUrl) return;\n let resolved: URL;\n try {\n resolved = new URL(rawUrl, window.location.origin);\n } catch {\n return;\n }\n if (resolved.origin !== window.location.origin) return;\n if (resolved.protocol !== 'http:' && resolved.protocol !== 'https:') return;\n window.history.pushState(null, '', resolved.toString());\n window.dispatchEvent(new PopStateEvent('popstate'));\n}\n\nfunction buildButton(cfg: NavLinkProps): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.dataset.syntroNavLink = '';\n button.style.display = 'inline-flex';\n button.style.alignItems = 'center';\n button.style.gap = '6px';\n button.style.padding = '6px 12px';\n button.style.borderRadius = '8px';\n button.style.fontFamily = \"var(--sc-font-family, 'system-ui')\";\n button.style.fontSize = '12px';\n button.style.fontWeight = '600';\n button.style.cursor = 'pointer';\n button.style.color = 'var(--sc-accent-foreground, currentColor)';\n button.style.background = 'hsl(var(--sc-accent-color) / 0.85)';\n button.style.border = '1px solid hsl(var(--sc-accent-color) / 0.30)';\n applyLabel(button, cfg);\n return button;\n}\n\nfunction applyLabel(button: HTMLButtonElement, cfg: NavLinkProps): void {\n const url = String(cfg.url ?? '');\n const label = cfg.label ?? (url ? `Go to ${url}` : 'Go');\n // textContent (not innerHTML) \u2014 LLM-authored URLs are an enum on the\n // backend so injection isn't realistic, but defense-in-depth is cheap.\n button.textContent = label;\n}\n\nexport const NavLinkMountable = {\n mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void {\n const cfg = (mountConfig ?? {}) as NavLinkProps;\n const button = buildButton(cfg);\n const onClick = (_e: Event): void => {\n const current = getState(container);\n const url = current?.cfg.url ?? cfg.url;\n navigateTo(String(url ?? ''));\n };\n button.addEventListener('click', onClick);\n container.appendChild(button);\n setState(container, { button, cfg, onClick });\n return () => {\n button.removeEventListener('click', onClick);\n button.remove();\n setState(container, null);\n };\n },\n\n update(container: HTMLElement, mountConfig?: Record<string, unknown>): void {\n const state = getState(container);\n if (!state) return;\n const cfg = (mountConfig ?? {}) as NavLinkProps;\n applyLabel(state.button, cfg);\n state.cfg = cfg;\n },\n};\n", "/**\n * TextAnswerMountable \u2014 `MountableWidget` for the simplest chip payload\n * kind: a single paragraph of body text.\n *\n * Registered as widget id `adaptive-chatbot:text-answer`. Used by\n * `AdaptiveChipsStrip` when a chip's payload references it.\n *\n * Replaces the old hand-rolled resolver path. Customers wanting richer\n * payloads (markdown, mini-product cards, embedded charts, etc.) ship\n * their own mountable through the same registry \u2014 no special\n * resolver-map seam.\n */\n\ninterface TextAnswerProps {\n text?: string;\n}\n\ninterface MountState {\n paragraph: HTMLParagraphElement;\n cfg: TextAnswerProps;\n}\n\nconst STATE_KEY = '__syntroTextAnswerMount';\n\nfunction getState(container: HTMLElement): MountState | null {\n return (container as unknown as Record<string, MountState | undefined>)[STATE_KEY] ?? null;\n}\nfunction setState(container: HTMLElement, state: MountState | null): void {\n (container as unknown as Record<string, MountState | null>)[STATE_KEY] = state;\n}\n\nfunction applyText(p: HTMLParagraphElement, text: string): void {\n p.textContent = text;\n}\n\nexport const TextAnswerMountable = {\n mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void {\n const cfg = (mountConfig ?? {}) as TextAnswerProps;\n const paragraph = document.createElement('p');\n paragraph.dataset.syntroTextAnswer = '';\n // Keep the styling minimal \u2014 paragraph inherits the slot's\n // `--sc-tile-text-color` (set by the chips strip's drawer chrome)\n // so the text reads consistently against the host's surface.\n paragraph.style.margin = '0';\n paragraph.style.fontSize = '12px';\n paragraph.style.lineHeight = '1.55';\n paragraph.style.color = 'var(--sc-tile-text-color, currentColor)';\n applyText(paragraph, String(cfg.text ?? ''));\n container.appendChild(paragraph);\n\n setState(container, { paragraph, cfg });\n return () => {\n paragraph.remove();\n setState(container, null);\n };\n },\n\n update(container: HTMLElement, mountConfig?: Record<string, unknown>): void {\n const state = getState(container);\n if (!state) return;\n const cfg = (mountConfig ?? {}) as TextAnswerProps;\n applyText(state.paragraph, String(cfg.text ?? ''));\n state.cfg = cfg;\n },\n};\n", "/**\n * Adaptive Chatbot - Runtime Module\n *\n * Runtime manifest for the AI chat assistant adaptive.\n * Uses @syntrologie/chat for the UI \u2014 no React dependency.\n *\n * Chat surfaces are config-driven: customers reference the chat-bar\n * widget id (`adaptive-chatbot:chat-bar`) from `slots.drawer.lid` (to\n * replace the launcher FAB) or any other slot's `lid` / tile slot. The\n * adaptive no longer auto-registers a canvas lid renderer.\n */\n\n// Side-effect imports so the custom elements register with the global\n// registry as soon as the adaptive is loaded.\nimport './AdaptiveChatBar';\nimport './AdaptiveChatTrail';\nimport { AdaptiveChatBarMountable } from './AdaptiveChatBarMountable';\nimport { AdaptiveChipsStripMountable } from './AdaptiveChipsStripMountable';\nimport { ChatAssistantLitMountable } from './ChatAssistantLit';\nimport { NavLinkMountable } from './NavLinkMountable';\nimport { TextAnswerMountable } from './TextAnswerMountable';\n\nexport const runtime = {\n id: 'adaptive-chatbot',\n version: '2.0.0',\n name: 'Chat Assistant',\n description: 'AI chat assistant powered by @syntrologie/chat with SSE transport',\n\n executors: [],\n\n widgets: [\n {\n id: 'adaptive-chatbot:assistant',\n component: ChatAssistantLitMountable,\n metadata: {\n name: 'Chat Assistant',\n description: 'AI-powered chat assistant with SSE streaming and A2UI support',\n icon: '\uD83D\uDCAC',\n },\n },\n // PRD \u00A73 chat lid: the standalone chat bar UI shell. Used as a slot\n // `lid` widget (where it replaces the launcher) or as a regular\n // tile widget (renders inside the drawer). Headless of any chat\n // transport \u2014 emits chat-message-sent / chat-interrupt / canvas-close\n // for the parent to wire to whatever pipeline it owns.\n {\n id: 'adaptive-chatbot:chat-bar',\n component: AdaptiveChatBarMountable,\n metadata: {\n name: 'Chat Bar',\n description:\n 'Always-visible chat input row with bubble-up trail. Headless \u2014 wire to your own transport.',\n icon: '\u2726',\n },\n },\n // PRD \u00A74.5 suggested-chips tile: horizontally-wrapping chip strip\n // with click-to-reveal payload drawer. Each chip is a\n // `suggestions:chip` action.\n {\n id: 'adaptive-chatbot:chips-strip',\n component: AdaptiveChipsStripMountable,\n metadata: {\n name: 'Suggested Chips',\n description: 'Wrapping chip strip with click-to-reveal payloads.',\n icon: '\u2728',\n },\n },\n // Built-in chip payload \u2014 a single paragraph of body text. Chip\n // payloads are widget references (same {widget,props} shape as\n // tile/lid configs); this is the simplest one shipped out of the\n // box so the chip pattern works without requiring a downstream\n // adaptive for a \"just answer with text\" case.\n {\n id: 'adaptive-chatbot:text-answer',\n component: TextAnswerMountable,\n metadata: {\n name: 'Text Answer',\n description: 'Single paragraph payload \u2014 the default chip-drawer content.',\n icon: '\u00B6',\n },\n },\n // Chip payload for LLM-authored navigation suggestions. Backed by\n // the `suggest-navigation` ItemTemplate: the backend wraps a curated\n // URL into props.url, the chip's payload-drawer mounts this widget,\n // user clicks \"Go to <url>\" \u2192 same-origin SPA navigation. Kept here\n // (not in adaptive-nav) so chip payloads ship with the chatbot\n // bundle that already provides chips-strip + text-answer.\n {\n id: 'adaptive-chatbot:nav-link',\n component: NavLinkMountable,\n metadata: {\n name: 'Nav Link',\n description:\n 'Same-origin navigation button \u2014 used as a chip payload for LLM-authored nav suggestions.',\n icon: '\u2192',\n },\n },\n ],\n};\n\nexport default runtime;\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * ChatSession \u2014 singleton holder of the chat conversation state.\n *\n * The chat is conceptually one thing. Whether it's rendered in the\n * mini-canvas's lid, the drawer's lid, or as a tile inside any slot,\n * they're all VIEWS of the same conversation. This module owns that\n * shared state.\n *\n * Why a module-level singleton (not on `runtime`):\n * - Per-page scope is the right granularity for a chat (one user,\n * one conversation, however many canvas instances on the page).\n * - Avoids a new SmartCanvasRuntime API surface for now. Easy to\n * promote to `runtime.chat` later without breaking the widget API.\n * - Module identity is stable per page; multiple imports return the\n * same instance.\n *\n * Separation of concerns:\n * - `ChatSession` owns state: messages, inFlight.\n * - Views (AdaptiveChatBar via AdaptiveChatBarMountable) subscribe\n * to state changes and dispatch user actions back via send() /\n * interrupt().\n * - Transports (the LLM call, SSE pipeline, stub timers) listen\n * for \"send\" / \"interrupt\" events and eventually call receive()\n * with the assistant's reply. Transports are NOT owned by this\n * module \u2014 adapters live separately.\n *\n * No persistence in this slice. Future: snapshot to runtime.state\n * for cross-session continuity.\n */\n\nimport type { TrailMessage, TrailToolCall } from './AdaptiveChatTrail';\n\nexport interface ChatSessionState {\n readonly messages: readonly TrailMessage[];\n readonly inFlight: boolean;\n}\n\nexport interface ToolResultEvent {\n toolCallId: string;\n result: unknown;\n approved: boolean;\n}\n\nexport type ChatSessionSubscriber = (state: ChatSessionState) => void;\nexport type SendListener = (event: { text: string }) => void;\nexport type InterruptListener = () => void;\nexport type ToolResultListener = (event: ToolResultEvent) => void;\nexport type Unsubscribe = () => void;\n\nexport const CHAT_SESSION_STORAGE_KEY = 'syntro:chat:v1';\n\ninterface PersistedShape {\n messages: TrailMessage[];\n nextId: number;\n}\n\nfunction isValidMessage(value: unknown): value is TrailMessage {\n if (typeof value !== 'object' || value === null) return false;\n const m = value as Record<string, unknown>;\n return (\n (typeof m.id === 'number' || typeof m.id === 'string') &&\n (m.role === 'user' || m.role === 'assistant' || m.role === 'system') &&\n typeof m.text === 'string'\n );\n}\n\nfunction loadFromStorage(): PersistedShape | null {\n try {\n const raw = globalThis.localStorage?.getItem(CHAT_SESSION_STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n if (!parsed || !Array.isArray(parsed.messages)) return null;\n const messages = parsed.messages.filter(isValidMessage);\n const nextId = typeof parsed.nextId === 'number' ? parsed.nextId : messages.length + 1;\n return { messages, nextId };\n } catch {\n return null;\n }\n}\n\nexport class ChatSession {\n private _messages: TrailMessage[] = [];\n private _inFlight = false;\n private _nextId = 1;\n\n private subscribers = new Set<ChatSessionSubscriber>();\n private sendListeners = new Set<SendListener>();\n private interruptListeners = new Set<InterruptListener>();\n private toolResultListeners = new Set<ToolResultListener>();\n\n constructor() {\n const restored = loadFromStorage();\n if (restored) {\n this._messages = restored.messages;\n this._nextId = restored.nextId;\n }\n }\n\n /** Snapshot the current state. Always returns a fresh immutable view. */\n getState(): ChatSessionState {\n return { messages: [...this._messages], inFlight: this._inFlight };\n }\n\n /**\n * Subscribe to state changes. Called immediately with the current\n * state, then again on every change. Returns an unsubscribe function.\n */\n subscribe(cb: ChatSessionSubscriber): Unsubscribe {\n this.subscribers.add(cb);\n cb(this.getState());\n return () => {\n this.subscribers.delete(cb);\n };\n }\n\n /**\n * User submitted a message. Appends a user-role message, sets\n * inFlight=true, notifies state subscribers, and fires a \"send\"\n * event so transports can pick it up. Empty/whitespace text is a\n * no-op (matches the chat bar's local guard).\n */\n send(text: string): void {\n const trimmed = text.trim();\n if (!trimmed) return;\n this._messages.push({ id: this._nextId++, role: 'user', text: trimmed });\n this._inFlight = true;\n this.notify();\n for (const listener of this.sendListeners) listener({ text: trimmed });\n }\n\n /**\n * Single-shot assistant reply (no streaming). Equivalent to\n * receiveStart + receiveDelta + receiveEnd in one call. Useful for\n * stub transports and tests that don't model streaming.\n */\n receive(text: string): void {\n const id = `m-${this._nextId++}`;\n this._messages.push({ id, role: 'assistant', text, status: 'complete' });\n this._inFlight = false;\n this.notify();\n }\n\n /**\n * Begin a streaming assistant message. Appends an empty assistant\n * message with status='streaming'. Caller (transport adapter) feeds\n * deltas via receiveDelta(id, text) and signals completion via\n * receiveEnd(id). `inFlight` stays true through the stream.\n */\n receiveStart(id: string): void {\n this._messages.push({ id, role: 'assistant', text: '', status: 'streaming' });\n this._inFlight = true;\n this.notify();\n }\n\n /**\n * Append a delta to a streaming message. No-op when the id is\n * unknown (race between transport events and reset, etc.) \u2014 never\n * throws so transports can fire-and-forget.\n */\n receiveDelta(id: string, delta: string): void {\n const msg = this._messages.find((m) => m.id === id);\n if (!msg || msg.status !== 'streaming') return;\n msg.text += delta;\n this.notify();\n }\n\n /**\n * Mark a streaming message complete and clear inFlight. No-op when\n * the id is unknown (defensive against transport double-fires).\n */\n receiveEnd(id: string): void {\n const msg = this._messages.find((m) => m.id === id);\n if (msg && msg.status === 'streaming') {\n msg.status = 'complete';\n }\n this._inFlight = false;\n this.notify();\n }\n\n /**\n * Transport reports a fatal error. Marks any in-flight streaming\n * message as 'error' (so the trail can render a styled error chip\n * instead of pretending the partial text was a complete answer),\n * appends a system-role message with the error text for visibility,\n * and clears inFlight.\n */\n error(message: string): void {\n for (const m of this._messages) {\n if (m.status === 'streaming') m.status = 'error';\n }\n this._messages.push({\n id: `err-${this._nextId++}`,\n role: 'system',\n text: message,\n status: 'error',\n });\n this._inFlight = false;\n this.notify();\n }\n\n /**\n * User clicked the in-flight stop button. Clears inFlight and\n * fires an \"interrupt\" event so transports can cancel their\n * in-flight request. No-op if not in-flight (idempotent).\n */\n interrupt(): void {\n if (!this._inFlight) return;\n this._inFlight = false;\n this.notify();\n for (const listener of this.interruptListeners) listener();\n }\n\n /**\n * Wipe state. Used by the canvas-close path (start a fresh\n * conversation next time) and by tests.\n */\n reset(): void {\n this._messages = [];\n this._inFlight = false;\n this._nextId = 1;\n this.notify();\n }\n\n /** Register a transport's send listener. Returns unsubscribe. */\n onSend(listener: SendListener): Unsubscribe {\n this.sendListeners.add(listener);\n return () => {\n this.sendListeners.delete(listener);\n };\n }\n\n /**\n * True when at least one transport has wired itself to the session's\n * send pipeline. Views can use this to surface \"no chat backend\n * connected\" affordances instead of hanging in `inFlight` after a\n * send fires into the void.\n */\n hasTransport(): boolean {\n return this.sendListeners.size > 0;\n }\n\n /** Register a transport's interrupt listener. Returns unsubscribe. */\n onInterrupt(listener: InterruptListener): Unsubscribe {\n this.interruptListeners.add(listener);\n return () => {\n this.interruptListeners.delete(listener);\n };\n }\n\n /**\n * Register a transport's tool-result listener. The transport\n * forwards `tool-result` actions back to the agent after the user\n * approves or rejects a client-tool call. Returns unsubscribe.\n */\n onToolResult(listener: ToolResultListener): Unsubscribe {\n this.toolResultListeners.add(listener);\n return () => {\n this.toolResultListeners.delete(listener);\n };\n }\n\n // -------------------------------------------------------------------------\n // Tool calls\n // -------------------------------------------------------------------------\n\n /**\n * Attach a tool call to a streaming assistant message. No-op when\n * the message id is unknown (race between transport events and\n * reset / late mount).\n */\n addToolCall(messageId: string | number, toolCall: TrailToolCall): void {\n const msg = this._messages.find((m) => m.id === messageId);\n if (!msg) return;\n msg.toolCalls = [...(msg.toolCalls ?? []), { ...toolCall }];\n this.notify();\n }\n\n /**\n * Partially update a tool call by id. Used by the transport to\n * advance status (args-streaming \u2192 running \u2192 done) as AG-UI events\n * arrive. No-op when the id is unknown.\n */\n updateToolCall(toolCallId: string, patch: Partial<TrailToolCall>): void {\n for (const msg of this._messages) {\n const tcs = msg.toolCalls;\n if (!tcs) continue;\n const idx = tcs.findIndex((tc) => tc.id === toolCallId);\n if (idx === -1) continue;\n const next = [...tcs];\n next[idx] = { ...next[idx], ...patch } as TrailToolCall;\n msg.toolCalls = next;\n this.notify();\n return;\n }\n }\n\n /**\n * Resolve a (client-) tool call. Marks the call done, persists, and\n * fires onToolResult so the transport can forward the result + the\n * user's approve/reject decision back to the agent. No-op when the\n * id is unknown.\n */\n resolveToolCall(toolCallId: string, result: unknown, approved: boolean): void {\n let found = false;\n for (const msg of this._messages) {\n const tcs = msg.toolCalls;\n if (!tcs) continue;\n const idx = tcs.findIndex((tc) => tc.id === toolCallId);\n if (idx === -1) continue;\n const next = [...tcs];\n next[idx] = { ...next[idx], status: 'done' } as TrailToolCall;\n msg.toolCalls = next;\n found = true;\n break;\n }\n if (!found) return;\n this.notify();\n for (const listener of this.toolResultListeners) {\n listener({ toolCallId, result, approved });\n }\n }\n\n private notify(): void {\n const state = this.getState();\n this.persist();\n for (const sub of this.subscribers) sub(state);\n }\n\n private persist(): void {\n try {\n if (this._messages.length === 0) {\n globalThis.localStorage?.removeItem(CHAT_SESSION_STORAGE_KEY);\n return;\n }\n const payload: PersistedShape = { messages: this._messages, nextId: this._nextId };\n globalThis.localStorage?.setItem(CHAT_SESSION_STORAGE_KEY, JSON.stringify(payload));\n } catch {\n // private mode / quota / SSR \u2014 swallow; in-memory state is still valid\n }\n }\n}\n\n/**\n * The module-level singleton. Every `<adaptive-chat-bar>` (regardless\n * of which slot it lives in) reads from and writes to this instance.\n * Multiple imports of this module return the same object.\n *\n * Scope: per-page. Correct for the canonical case (one user, one\n * conversation, however many canvas views surface it). Multiple\n * `<smart-canvas>` instances on the same page will share state \u2014\n * tracked in project_future_work.md for per-runtime scoping.\n */\nexport const chatSession = new ChatSession();\n", "/**\n * ChatTransport \u2014 singleton transport adapter that bridges chatSession\n * (the view-side state holder) to the real backend chat pipeline\n * (AG-UI SSE at `/api/adaptive/stream`).\n *\n * This is the unification of two formerly separate paths:\n * - `ChatAssistantLit` (old) owns the transport plumbing \u2014\n * AgUiTransport, Cloudflare Turnstile, headers, A2UI passthrough,\n * telemetry, fallback card.\n * - `AdaptiveChatBar` + `chatSession` (new) owns the canvas-lid UX \u2014\n * glassmorphism bar, bubble-up trail, per-page persistence.\n *\n * This module ports every transport-side concern from ChatAssistantLit\n * into a session-shaped adapter so the new bar gets full backend\n * parity. The bar stays a pure view; the transport stays a pure pipe.\n *\n * Lifecycle:\n * - `configure({ backendUrl, runtime, ... })` \u2014 called once by the\n * SDK runtime at bootstrap from chat config. Idempotent; second\n * call with the same backendUrl is a no-op.\n * - Connection is LAZY. The transport doesn't acquire Turnstile or\n * open the SSE until the first chatSession.send() \u2014 saves cost\n * on pages where the user never opens chat.\n * - On chatSession.send \u2192 transport ensures connected (acquires\n * Turnstile token, builds AgUiTransport) then forwards\n * `{type:'user-message', text}` to the agent.\n * - On chatSession.onInterrupt \u2192 transport sends stop-generation.\n * - AgUi events stream into chatSession via receiveStart / Delta /\n * End / error.\n * - A2UI custom events forward to runtime.actions.applyBatch so the\n * agent can drive canvas updates.\n *\n * Telemetry parity with ChatAssistantLit:\n * - chatbot.transport_error (every error, with status/body/name)\n * - chatbot.a2ui_applied\n * - chatbot.fallback_rendered \u2014 emitted when the transport gives up\n * (Turnstile failed AND backend rejected) so existing PostHog\n * queries keep working.\n *\n * Threading model: single-flight per page. Multiple concurrent sends\n * are queued by AgUiTransport itself. Multiple bar mounts share this\n * one transport via chatSession.\n */\n\nimport type { ServerEvent } from '@syntrologie/chat';\n// AgUiTransport / Turnstile helpers live in the existing packages \u2014\n// we reuse them instead of re-implementing.\nimport { AgUiTransport } from '@syntrologie/chat/transport/agui';\n\nimport type { TrailToolCall } from './AdaptiveChatTrail';\nimport { chatSession } from './ChatSession';\nimport type { ElementMutation } from './elements';\nimport { decodeMutationEnvelope } from './elements';\nimport { acquireTokenWithChallenge } from './Turnstile';\nimport type { ChatbotWidgetRuntime } from './types';\n\n/**\n * Per-customer \"chat unavailable\" card config. Surfaced via onFallback\n * when the transport gives up after repeated failures. Mirrors\n * ChatbotFallback in types.ts so consumers can pass the same config.\n */\nexport interface ChatbotFallbackConfig {\n title?: string;\n message?: string;\n ctaLabel?: string;\n ctaHref?: string;\n}\n\nexport interface ChatTransportConfig {\n /** Backend host, e.g. \"https://demo-api.syntrologie.com\" or \"\" for same-origin. */\n backendUrl: string;\n /** Runtime hooks for A2UI apply + telemetry. */\n runtime: ChatbotWidgetRuntime;\n /** Optional thread id for conversation continuity across reloads. */\n threadId?: string;\n /**\n * Optional set of client-tool names the transport should treat as\n * \"needs user approval\" (surfaced via chatSession.toolCalls instead\n * of auto-executed server-side). Empty by default \u2014 all tool calls\n * are server-driven and the transport just emits the \"tool call\n * happened\" event.\n */\n clientTools?: Set<string>;\n /**\n * Per-customer \"chat unavailable\" card. Rendered by the host\n * (typically AdaptiveChatBarMountable) after repeated connect\n * failures wipe the chat surface \u2014 exactly the same UX\n * ChatAssistantLit's fallback path provides.\n */\n fallback?: ChatbotFallbackConfig;\n /**\n * Arbitrary JSON object attached to every AG-UI request as\n * `forwardedProps`. Used by the adaptive chat surface to ship the\n * LLM-authored UI element config (`uiTemplates`, `elementsEnabled`)\n * per request \u2014 the backend reads these to enable the\n * `mount_element` / `patch_element` / `unmount_element` tools.\n * Callable form is re-evaluated on every turn so a config edit can\n * land mid-conversation.\n */\n forwardedProps?: Record<string, unknown> | (() => Record<string, unknown> | undefined);\n /**\n * Optional callback for `syntro.element.mutation` AG-UI custom events.\n * The transport decodes the envelope on receipt; if it matches the\n * element-mutation schema, mutations are routed here instead of the\n * generic `runtime.actions.applyBatch` legacy path. Consumers are\n * expected to forward to `ElementInstanceStore.apply(mutations)` \u2014\n * we don't take a store reference directly so the transport stays\n * agnostic of which surface owns the store (the chat-bar widget\n * may own one; ChatAssistantLit owns its own; both share this\n * singleton transport).\n *\n * When unset, element-mutation envelopes fall back to applyBatch,\n * matching legacy behaviour (which would no-op since applyBatch\n * doesn't know about MutationEnvelope).\n */\n onElementMutation?: (mutations: ElementMutation[]) => void;\n}\n\n/**\n * Debounce window for connect errors. Single transient errors\n * (cold-start 502, brief CORS preflight failure) should NOT swap the\n * chat to the fallback card; only sustained failure should. Matches\n * ChatAssistantLit's ERROR_DEBOUNCE_MS.\n */\nexport const FALLBACK_DEBOUNCE_MS = 1_500;\n\nexport interface FallbackPayload {\n reason: 'connect_failed' | 'connect_timeout';\n fallback: ChatbotFallbackConfig;\n transportId: string;\n transportAgeMs: number;\n messagesSucceeded: number;\n hadTurnstileToken: boolean | null;\n errorStatus: number | null;\n errorBody: string | null;\n errorMessage: string | null;\n errorName: string | null;\n}\n\nexport type FallbackListener = (payload: FallbackPayload) => void;\n\ntype ChatTransportStatus =\n | 'idle' // configure() not called yet\n | 'configured' // ready but not connected\n | 'acquiring' // Turnstile token acquisition in flight\n | 'connected' // AgUiTransport up and ready\n | 'error'; // last attempt failed; will retry on next send\n\ninterface SyntroGlobalConfig {\n token?: string;\n}\n\n/**\n * Read the workspace's syn_* token from runtime-config.js. The SDK\n * sets this global at bootstrap; for non-CDN test environments the\n * test page sets it directly. Returns undefined when the global is\n * missing \u2014 the connect will then fall through to the unauthenticated\n * path and the backend will 401 (caught by error handling below).\n */\nfunction readSyntroToken(): string | undefined {\n if (typeof window === 'undefined') return undefined;\n const cfg = (window as unknown as { __SYNTRO_CONFIG__?: SyntroGlobalConfig }).__SYNTRO_CONFIG__;\n const token = cfg?.token;\n return typeof token === 'string' && token.length > 0 ? token : undefined;\n}\n\n/**\n * Debug logger gated on `window.__SYNTRO_CHAT_DEBUG__`. Mirrors\n * ChatAssistantLit so flipping that flag in the customer's DevTools\n * enables verbose tracing across the whole chat pipeline (the bar\n * AND the legacy assistant) without rebuilding the SDK.\n */\nfunction debug(...args: unknown[]): void {\n if (typeof window === 'undefined') return;\n if ((window as unknown as { __SYNTRO_CHAT_DEBUG__?: boolean }).__SYNTRO_CHAT_DEBUG__) {\n console.debug('[chat-transport]', ...args);\n }\n}\n\nexport class ChatTransport {\n private _config: ChatTransportConfig | null = null;\n private _status: ChatTransportStatus = 'idle';\n private _agui: AgUiTransport | null = null;\n private _transportUnsub: (() => void) | null = null;\n private _sessionUnsubSend: (() => void) | null = null;\n private _sessionUnsubInterrupt: (() => void) | null = null;\n private _sessionUnsubToolResult: (() => void) | null = null;\n private _connectInFlight: Promise<boolean> | null = null;\n /** Bounded streaming-message id for the currently-being-typed assistant turn. */\n private _currentAssistantMessageId: string | null = null;\n /** Outcome bookkeeping for telemetry. */\n private _hadTurnstileToken: boolean | null = null;\n /** Count of successful round-trips for telemetry payloads. */\n private _messagesSucceeded = 0;\n /** True once a successful assistant message has landed \u2014 gates fallback. */\n private _hasSucceeded = false;\n /** True once fallback has fired (one-shot). */\n private _fallbackRendered = false;\n /** Active debounce timer; null when no debounce pending. */\n private _errorDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n /** Most recent error payload, captured so debounced fallback can attach it. */\n private _lastErrorPayload: {\n message?: string;\n status?: number | null;\n body?: string | null;\n errorName?: string | null;\n } | null = null;\n private _fallbackListeners = new Set<FallbackListener>();\n /**\n * Per-configure-cycle id for correlating telemetry events from a\n * single transport lifetime. Mirrors ChatAssistantLit's mountId\n * but scoped to configure cycles since the transport is a\n * singleton across mounts.\n */\n private _transportId = `tx_${Math.random().toString(36).slice(2, 8)}`;\n private _configuredAt = 0;\n\n /**\n * Configure the transport. Idempotent \u2014 calling again with the same\n * backendUrl is a no-op; calling with a different backendUrl tears\n * the connection down and re-arms.\n */\n configure(config: ChatTransportConfig): void {\n const same =\n this._config &&\n this._config.backendUrl === config.backendUrl &&\n this._config.threadId === config.threadId;\n if (same) {\n // Allow runtime + clientTools to be replaced without reconnect \u2014\n // the bar may remount with a fresh runtime closure.\n this._config = { ...this._config, ...config };\n return;\n }\n this._disconnect();\n this._config = config;\n this._status = 'configured';\n this._transportId = `tx_${Math.random().toString(36).slice(2, 8)}`;\n this._configuredAt = Date.now();\n this._wireSession();\n debug('configured', {\n transportId: this._transportId,\n backendUrl: config.backendUrl,\n threadId: config.threadId,\n });\n }\n\n /** ms since the most recent configure() call. 0 before any configure. */\n private _ageMs(): number {\n return this._configuredAt === 0 ? 0 : Date.now() - this._configuredAt;\n }\n\n /** True when configure() has been called and we're ready to lazy-connect on send. */\n get isConfigured(): boolean {\n return this._status !== 'idle';\n }\n\n /** True when AgUiTransport is up. */\n get isConnected(): boolean {\n return this._status === 'connected';\n }\n\n /**\n * Subscribe to fallback events \u2014 fires once per configure cycle\n * when the transport gives up after sustained failure. Hosts\n * (typically AdaptiveChatBarMountable) use this to swap the chat\n * bar for a static \"contact support\" card.\n */\n onFallback(listener: FallbackListener): () => void {\n this._fallbackListeners.add(listener);\n return () => {\n this._fallbackListeners.delete(listener);\n };\n }\n\n /**\n * Test seam \u2014 drive a synthetic error event through the transport's\n * error handling without standing up a real AgUi transport. Production\n * code path uses _onTransportEvent. Exported as a public method to\n * keep the test isolation simple; not part of the documented API.\n */\n simulateError(payload: {\n message?: string;\n status?: number | null;\n body?: string | null;\n errorName?: string | null;\n }): void {\n this._handleErrorEvent(payload);\n }\n\n /**\n * Test seam \u2014 register a synthetic successful message-complete so\n * the hasSucceeded gate flips without a real AgUi round-trip.\n */\n simulateSuccessfulMessage(): void {\n this._hasSucceeded = true;\n this._messagesSucceeded += 1;\n this._clearDebounceTimer();\n }\n\n /**\n * Tear connection-level state down. Used internally by configure()\n * to swap backends; preserves host-registered fallback listeners\n * because the host UI handler (e.g. AdaptiveChatBarMountable's\n * \"swap to fallback card\" callback) is configuration-independent.\n */\n private _disconnect(): void {\n if (this._transportUnsub) {\n this._transportUnsub();\n this._transportUnsub = null;\n }\n if (this._sessionUnsubSend) {\n this._sessionUnsubSend();\n this._sessionUnsubSend = null;\n }\n if (this._sessionUnsubInterrupt) {\n this._sessionUnsubInterrupt();\n this._sessionUnsubInterrupt = null;\n }\n if (this._sessionUnsubToolResult) {\n this._sessionUnsubToolResult();\n this._sessionUnsubToolResult = null;\n }\n if (this._agui) {\n this._agui.disconnect();\n this._agui = null;\n }\n this._clearDebounceTimer();\n this._config = null;\n this._status = 'idle';\n this._connectInFlight = null;\n this._currentAssistantMessageId = null;\n this._hadTurnstileToken = null;\n this._messagesSucceeded = 0;\n this._hasSucceeded = false;\n this._fallbackRendered = false;\n this._lastErrorPayload = null;\n }\n\n /**\n * Tear everything down \u2014 connection state PLUS host listeners.\n * Use this in test teardown or when fully shutting the transport\n * (page unload, integration test reset). The mountable calls\n * _disconnect indirectly via reconfigure.\n */\n reset(): void {\n this._disconnect();\n this._fallbackListeners.clear();\n }\n\n private _clearDebounceTimer(): void {\n if (this._errorDebounceTimer) {\n clearTimeout(this._errorDebounceTimer);\n this._errorDebounceTimer = null;\n }\n }\n\n /**\n * Shared error-handling kernel \u2014 called by the AG-UI subscriber and\n * by the simulateError test seam. Publishes transport_error\n * telemetry, captures lastErrorPayload, and starts the debounce\n * timer that will fire fallback if no successful message arrives\n * before it expires. Gated by hasSucceeded (post-success errors\n * never fallback) and _fallbackRendered (one-shot).\n */\n private _handleErrorEvent(payload: {\n message?: string;\n status?: number | null;\n body?: string | null;\n errorName?: string | null;\n }): void {\n const status = payload.status ?? null;\n const body = payload.body ? String(payload.body).slice(0, 200) : null;\n this._lastErrorPayload = {\n message: payload.message,\n status,\n body,\n errorName: payload.errorName ?? null,\n };\n\n this._config?.runtime.events.publish('chatbot.transport_error', {\n source: 'chat-transport',\n transportId: this._transportId,\n transportAgeMs: this._ageMs(),\n messagesSucceeded: this._messagesSucceeded,\n hadTurnstileToken: this._hadTurnstileToken,\n hasSucceeded: this._hasSucceeded,\n errorMessage: payload.message ?? null,\n errorStatus: status,\n errorBody: body,\n errorName: payload.errorName ?? null,\n });\n\n if (this._hasSucceeded || this._fallbackRendered || this._errorDebounceTimer) return;\n this._errorDebounceTimer = setTimeout(() => {\n this._errorDebounceTimer = null;\n if (this._hasSucceeded || this._fallbackRendered) return;\n this._renderFallback('connect_failed');\n }, FALLBACK_DEBOUNCE_MS);\n }\n\n /**\n * Fire the fallback. One-shot per configure cycle. Notifies all\n * fallback listeners with the per-customer card config and the\n * diagnostic snapshot for telemetry/debug.\n */\n private _renderFallback(reason: FallbackPayload['reason']): void {\n if (this._fallbackRendered) return;\n this._fallbackRendered = true;\n debug('fallback', { reason, transportId: this._transportId, ageMs: this._ageMs() });\n const fallback = this._config?.fallback ?? {};\n const payload: FallbackPayload = {\n reason,\n fallback,\n transportId: this._transportId,\n transportAgeMs: this._ageMs(),\n messagesSucceeded: this._messagesSucceeded,\n hadTurnstileToken: this._hadTurnstileToken,\n errorStatus: this._lastErrorPayload?.status ?? null,\n errorBody: this._lastErrorPayload?.body ?? null,\n errorMessage: this._lastErrorPayload?.message ?? null,\n errorName: this._lastErrorPayload?.errorName ?? null,\n };\n this._config?.runtime.events.publish(\n 'chatbot.fallback_rendered',\n payload as unknown as Record<string, unknown>\n );\n for (const listener of this._fallbackListeners) listener(payload);\n }\n\n // -------------------------------------------------------------------------\n // Internal: chatSession wiring\n // -------------------------------------------------------------------------\n\n private _wireSession(): void {\n this._sessionUnsubSend = chatSession.onSend(({ text }) => {\n void this._forwardUserMessage(text);\n });\n this._sessionUnsubInterrupt = chatSession.onInterrupt(() => {\n this._agui?.send({ type: 'stop-generation' });\n });\n this._sessionUnsubToolResult = chatSession.onToolResult(({ toolCallId, result, approved }) => {\n this._agui?.send({ type: 'tool-result', toolCallId, result, approved });\n });\n }\n\n private async _forwardUserMessage(text: string): Promise<void> {\n const ok = await this._ensureConnected();\n if (!ok || !this._agui) {\n // Connect failed; surface to the session so the bar shows an error\n // chip and clears inFlight (otherwise the user is stuck on the\n // \u23F9 stop button forever).\n chatSession.error(\"Couldn't connect to chat. Please try again.\");\n return;\n }\n this._agui.send({ type: 'user-message', text });\n }\n\n // -------------------------------------------------------------------------\n // Internal: connect (lazy, Turnstile-gated)\n // -------------------------------------------------------------------------\n\n /**\n * Ensure AgUiTransport is up. Idempotent \u2014 multiple concurrent calls\n * dedupe to a single Turnstile acquisition + transport setup.\n * Returns true on success, false on terminal failure.\n */\n private async _ensureConnected(): Promise<boolean> {\n if (this._status === 'connected' && this._agui) return true;\n if (!this._config) return false;\n if (this._connectInFlight) return this._connectInFlight;\n\n this._connectInFlight = (async () => {\n this._status = 'acquiring';\n const cft = await this._acquireTurnstileWithChallenge();\n this._hadTurnstileToken = cft !== null;\n\n if (this._config === null) return false; // raced with reset()\n\n const baseUrl = this._config.backendUrl.replace(/\\/$/, '');\n const streamUrl = `${baseUrl}/api/adaptive/stream`;\n const token = readSyntroToken();\n const buildHeaders = (): Record<string, string> => {\n const h: Record<string, string> = {};\n if (token) h.Authorization = `Bearer ${token}`;\n if (cft) h['CF-Turnstile-Token'] = cft;\n // Forward PostHog distinct_id so the backend can resolve historical\n // context (KNOWN ABOUT VISITOR block, persona summaries). Two\n // sources, in order:\n // 1. runtime.telemetry.getDistinctId() \u2014 preferred, mirrors what\n // the rest of the SDK uses for capture identity.\n // 2. window.posthog.get_distinct_id() \u2014 fallback for embeds where\n // the host page initialises PostHog directly.\n // Defensive: missing/blocked PostHog \u2192 header omitted \u2192 backend\n // falls back to cold-start.\n try {\n const rt = this._config?.runtime as\n | { telemetry?: { getDistinctId?: () => string | null | undefined } }\n | undefined;\n const fromRuntime = rt?.telemetry?.getDistinctId?.();\n const fromWindow = (\n window as unknown as { posthog?: { get_distinct_id?: () => string } }\n ).posthog?.get_distinct_id?.();\n const did = fromRuntime || fromWindow;\n if (typeof did === 'string' && did.length > 0) {\n h['X-Distinct-Id'] = did;\n }\n } catch {\n // PostHog throw on get_distinct_id is extremely rare but cheap to guard.\n }\n return h;\n };\n\n const runtime = this._config.runtime;\n // Resolve forwardedProps at run-time (each AG-UI request), not at\n // transport construction. The config can be updated via\n // `configure()` with the same backendUrl (no reconnect), so the\n // late-bound closure keeps the transport in sync with the most\n // recent uiTemplates / elementsEnabled declarations.\n const resolveForwardedProps = (): Record<string, unknown> | undefined => {\n const raw = this._config?.forwardedProps;\n return typeof raw === 'function' ? raw() : raw;\n };\n this._agui = new AgUiTransport({\n url: streamUrl,\n headers: buildHeaders,\n threadId: this._config.threadId,\n clientTools: this._config.clientTools,\n forwardedProps: resolveForwardedProps,\n onA2UIEvent: (payload) => {\n // Discriminate `syntro.element.mutation` envelopes (LLM-\n // authored UI element mounts/patches/unmounts) from legacy\n // A2UI / raw ActionStep payloads. Mirrors ChatAssistantLit's\n // routing \u2014 the chat-bar lid surface needs the same wiring so\n // mount_element / patch_element / unmount_element results\n // actually land on the host page.\n const mutations = decodeMutationEnvelope(payload);\n if (mutations !== null) {\n const handler = this._config?.onElementMutation;\n if (handler) {\n try {\n handler(mutations);\n runtime.events.publish('chatbot.element_mutation_applied', {\n source: 'chat-transport',\n count: mutations.length,\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[chat-transport] element mutation apply failed:', msg);\n }\n return;\n }\n // No handler registered \u2014 the surface using this transport\n // didn't opt into element mutations. Drop silently rather\n // than feeding a MutationEnvelope into applyBatch (which\n // would no-op or warn).\n return;\n }\n runtime.actions\n .applyBatch([payload as unknown as Record<string, unknown>])\n .then(() => {\n runtime.events.publish('chatbot.a2ui_applied', { source: 'chat-transport' });\n })\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[chat-transport] A2UI apply failed:', msg);\n });\n },\n });\n this._agui.connect();\n this._transportUnsub = this._agui.subscribe((event) => this._onTransportEvent(event));\n this._status = 'connected';\n return true;\n })();\n\n try {\n return await this._connectInFlight;\n } finally {\n this._connectInFlight = null;\n }\n }\n\n /**\n * Acquire a Turnstile token via the managed-challenge flow.\n * Delegates to the shared Turnstile helper that owns the verify\n * panel lifecycle. Returns null when Turnstile is disabled at\n * build time or acquisition fails.\n */\n private async _acquireTurnstileWithChallenge(): Promise<string | null> {\n const { token } = await acquireTokenWithChallenge();\n return token;\n }\n\n // -------------------------------------------------------------------------\n // Internal: AgUi event \u2192 chatSession\n // -------------------------------------------------------------------------\n\n /**\n * Look up a tool call's current chatSession-side status by id.\n * Used to decide between addToolCall (first sighting) and\n * updateToolCall (subsequent updates) when AG-UI re-emits\n * `tool-call` events for client tools transitioning to 'pending'.\n */\n private _findToolCallStatus(toolCallId: string): TrailToolCall['status'] | null {\n const state = chatSession.getState();\n for (const msg of state.messages) {\n const tc = msg.toolCalls?.find((t) => t.id === toolCallId);\n if (tc) return tc.status;\n }\n return null;\n }\n\n /** Map AG-UI ToolCallStatus to TrailToolCall status. */\n private _mapToolCallStatus(status: string): TrailToolCall['status'] {\n switch (status) {\n case 'args-streaming':\n return 'args-streaming';\n case 'pending':\n return 'pending';\n case 'running':\n return 'running';\n case 'done':\n return 'done';\n case 'error':\n return 'error';\n default:\n return 'running';\n }\n }\n\n private _onTransportEvent(event: ServerEvent): void {\n const runtime = this._config?.runtime;\n\n switch (event.type) {\n case 'session-ready':\n case 'messages-snapshot':\n case 'typing':\n // Local-only events from AgUiTransport.connect() \u2014 no UI impact.\n return;\n\n case 'message-append': {\n // Start of a new streaming assistant message.\n this._currentAssistantMessageId = event.message.id;\n chatSession.receiveStart(event.message.id);\n if (event.message.content && event.message.content.length > 0) {\n chatSession.receiveDelta(event.message.id, event.message.content);\n }\n return;\n }\n\n case 'message-delta': {\n const id = event.messageId ?? this._currentAssistantMessageId;\n if (!id || !event.delta) return;\n chatSession.receiveDelta(id, event.delta);\n return;\n }\n\n case 'message-complete': {\n const id = event.messageId ?? this._currentAssistantMessageId;\n if (!id) return;\n chatSession.receiveEnd(id);\n this._currentAssistantMessageId = null;\n this._messagesSucceeded += 1;\n // Flip the hasSucceeded gate: errors after this point will no\n // longer trigger a fallback swap (matches ChatAssistantLit).\n this._hasSucceeded = true;\n this._clearDebounceTimer();\n return;\n }\n\n case 'tool-call': {\n // tool-call arrives twice in the AG-UI flow: once at start\n // (status='args-streaming') and once at end for client tools\n // with status='pending' awaiting user approval. Add on the\n // first sighting, update on the second.\n const targetMessageId = event.messageId ?? this._currentAssistantMessageId;\n if (!targetMessageId) return;\n const existing = this._findToolCallStatus(event.toolCall.id);\n if (!existing) {\n chatSession.addToolCall(targetMessageId, {\n id: event.toolCall.id,\n name: event.toolCall.name,\n status: this._mapToolCallStatus(event.toolCall.status),\n });\n } else {\n chatSession.updateToolCall(event.toolCall.id, {\n name: event.toolCall.name,\n status: this._mapToolCallStatus(event.toolCall.status),\n });\n }\n return;\n }\n\n case 'tool-call-args-delta':\n // We don't currently surface streaming args in the trail chip\n // (too noisy for the compact UI), but a future expanded view\n // could subscribe and render them. Logged under debug.\n debug('tool-call-args-delta', event);\n return;\n\n case 'tool-call-done':\n // Server-side tool finished. Mark complete in the trail so the\n // chip transitions from \"running\" to \"done\". Client-tool\n // resolution goes through chatSession.resolveToolCall instead.\n chatSession.updateToolCall(event.toolCallId, { status: 'done' });\n return;\n\n case 'a2ui':\n // A2UI events also flow via onA2UIEvent in the transport\n // constructor \u2014 that path is what applies the batch. This\n // duplicate event is the public mirror; ignore here.\n return;\n\n case 'error': {\n const status = event.status ?? null;\n const body = event.body ? String(event.body).slice(0, 200) : null;\n console.warn(\n `[chat-transport] error status=${status ?? 'no-status'} succeeded=${this._messagesSucceeded}`,\n event\n );\n // Route through the shared error-handling kernel so the\n // debounce / hasSucceeded gate / fallback path apply uniformly\n // regardless of whether the error came from AG-UI or a test seam.\n this._handleErrorEvent({\n message: event.message,\n status,\n body,\n errorName: event.errorName ?? null,\n });\n // The chip-level error chip (shown immediately, no debounce)\n // gives the user feedback even when we're still waiting to see\n // if the failure is sustained enough to swap to the fallback.\n chatSession.error(event.message ?? 'Chat connection failed');\n this._currentAssistantMessageId = null;\n // Tear AgUiTransport down so the next send re-acquires Turnstile\n // and re-opens. Avoids reusing a broken HttpAgent under us.\n if (this._agui) {\n this._agui.disconnect();\n this._agui = null;\n }\n if (this._transportUnsub) {\n this._transportUnsub();\n this._transportUnsub = null;\n }\n this._status = 'error';\n return;\n }\n }\n }\n}\n\n/**\n * The module-level singleton. Imported by both the SDK runtime\n * bootstrap (to call configure) and any code that needs to read\n * transport state. Mirrors the chatSession singleton shape.\n */\nexport const chatTransport = new ChatTransport();\n", "/**\n * AdaptiveChatBarMountable \u2014 `MountableWidget` adapter that turns the\n * standalone `<adaptive-chat-bar>` Lit element into something the\n * runtime widget registry can mount.\n *\n * Registered as widget id `adaptive-chatbot:chat-bar`. Customers\n * reference it from canvas config \u2014 either as a slot's `lid` widget\n * (replaces the launcher when referenced from `slots.drawer.lid`) or\n * as a regular tile widget.\n *\n * The bar is a VIEW. State lives in the singleton `chatSession` \u2014\n * whether the bar mounts in the mini-canvas lid, the drawer lid, or\n * both, they're all views of the same conversation. The mountable:\n * - subscribes to chatSession.subscribe() and pushes state into\n * the bar's `messages` + `inFlight` props on every change\n * - routes the bar's `chat-message-sent` event \u2192 chatSession.send()\n * - routes the bar's `chat-interrupt` event \u2192 chatSession.interrupt()\n * - bubbles the bar's `canvas-close` event \u2192 optional onClose\n * callback (canvas-level concern, not session-level)\n *\n * Props (passed via mountConfig):\n * - `placeholder?: string` \u2014 input placeholder copy\n * - `onClose?: () => void` \u2014 fires on \u2715 click (canvas-level)\n *\n * Note: messages and inFlight are NOT props. They flow from chatSession.\n * A canvas-config author who wants to pre-seed messages should do so\n * via the session directly (e.g. via a startup action) \u2014 but typical\n * use is to let the conversation start empty.\n */\n\nimport './AdaptiveChatBar';\nimport type { AdaptiveChatBar } from './AdaptiveChatBar';\nimport { renderFallbackHtml } from './ChatAssistantLit';\nimport type { Unsubscribe } from './ChatSession';\nimport { chatSession } from './ChatSession';\nimport type { ChatTransportConfig } from './ChatTransport';\nimport { chatTransport } from './ChatTransport';\nimport {\n ActionHandler,\n ElementInstanceStore,\n type ElementMutation,\n fetchMountedElements,\n ItemHandler,\n TileHandler,\n} from './elements';\nimport type { ChatbotFallback, ChatbotWidgetRuntime } from './types.js';\n\ninterface ChatBarMountConfig {\n placeholder?: string;\n /** Initial assistant greeting shown before any real conversation. */\n greeting?: string;\n onClose?: () => void;\n /**\n * Backend host for the live chat transport. Same shape as\n * ChatbotConfig.backendUrl. When provided alongside `runtime`, the\n * bar configures the singleton ChatTransport on first mount so\n * sends hit the real `/api/adaptive/stream` SSE pipeline. Omitting\n * either disables the transport \u2014 the bar stays a pure view and\n * nothing replies. Useful for storybook / config-preview surfaces.\n */\n backendUrl?: string;\n /** Runtime injected by WidgetRegistry \u2014 required for backendUrl wiring. */\n runtime?: ChatbotWidgetRuntime;\n /** Optional thread id for conversation continuity across reloads. */\n threadId?: string;\n /**\n * Per-customer \"chat unavailable\" card. Rendered in place of the\n * chat bar when the transport gives up after sustained failure.\n * Mirrors ChatAssistantLit's fallback behaviour.\n */\n fallback?: ChatbotFallback;\n /**\n * Declarations for the LLM-authored UI element pipeline (Phase 5+).\n * When `elementsEnabled === true` AND `uiTemplates` is non-null, the\n * backend exposes `mount_element` / `patch_element` / `unmount_element`\n * tools to the chat agent, scoped to the templates declared here.\n *\n * Shape mirrors `UiTemplatesBlock` from `syntrologie_common.sdk.templates`\n * \u2014 typed loosely on the SDK side because the SDK never introspects\n * templates (it only receives mount/patch/unmount mutations); the\n * backend Pydantic models are the validation gate.\n *\n * Passed verbatim as `forwardedProps.uiTemplates` to the AG-UI\n * transport so the backend's `/api/adaptive/stream` reads them off\n * the inbound request body.\n */\n uiTemplates?: Record<string, unknown>;\n elementsEnabled?: boolean;\n}\n\ninterface MountState {\n bar: AdaptiveChatBar;\n cleanup: () => void;\n cfg: ChatBarMountConfig;\n}\n\nconst STATE_KEY = '__syntroChatBarMount';\n\nfunction getState(container: HTMLElement): MountState | null {\n return (container as unknown as Record<string, MountState | undefined>)[STATE_KEY] ?? null;\n}\nfunction setState(container: HTMLElement, state: MountState | null): void {\n (container as unknown as Record<string, MountState | null>)[STATE_KEY] = state;\n}\n\nfunction applyPlaceholder(bar: AdaptiveChatBar, cfg: ChatBarMountConfig): void {\n if (cfg.placeholder !== undefined) bar.placeholder = cfg.placeholder;\n if (cfg.greeting !== undefined) bar.greeting = cfg.greeting;\n}\n\n/**\n * Configure the singleton transport if the caller supplied a backend\n * URL + runtime. Idempotent \u2014 second mount with same args is a no-op.\n * Missing backendUrl OR runtime leaves the transport untouched (the\n * bar is then a pure local view; useful for previews / storybook).\n *\n * Spread (not cherry-pick) so the bar-only fields (placeholder,\n * onClose) flow through silently; ChatTransport ignores them. Cast\n * narrows after the guard.\n */\n/**\n * Lazily-constructed ElementInstanceStore singleton for the chat-bar\n * surface. Mounted on first configure with `elementsEnabled` so the\n * `runtime` closure is captured; subsequent reconfigures with the same\n * runtime reuse it. ChatAssistantLit owns its own store separately \u2014\n * each surface keeps independent state to avoid cross-surface\n * double-mounts on the same instance_id.\n */\nlet _elementStore: ElementInstanceStore | null = null;\n\nfunction getOrCreateElementStore(runtime: ChatbotWidgetRuntime): ElementInstanceStore {\n if (_elementStore) return _elementStore;\n _elementStore = new ElementInstanceStore({\n actions: runtime.actions,\n // Pass the runtime's event bus so ItemHandler can broadcast\n // `element.compositional_append` / `_patch` / `_remove` events\n // to container widgets (chips strip, FAQ accordion, nav tips).\n events: {\n publish: runtime.events.publish.bind(runtime.events),\n subscribe: runtime.events.subscribe?.bind(runtime.events),\n },\n handlers: [new TileHandler(), new ActionHandler(), new ItemHandler()],\n });\n return _elementStore;\n}\n\n/**\n * Track whether we've already hydrated this store's snapshot. The store\n * itself is a singleton across mounts in the same page session, so we\n * want to hydrate exactly ONCE \u2014 repeated mounts (e.g. after an SPA\n * navigation that unmounts + remounts the chat-bar) reuse the warm\n * store without re-firing handler.mount() for every persisted item.\n *\n * The store's own version-dedup also prevents double-application, but\n * we still don't want to issue redundant HTTP requests on every mount.\n */\nlet _hydrationStarted = false;\n\nfunction hydrateOnce(runtime: ChatbotWidgetRuntime, backendUrl: string): void {\n if (_hydrationStarted) return;\n _hydrationStarted = true;\n const store = getOrCreateElementStore(runtime);\n // The fetcher reads `window.__SYNTRO_CONFIG__.token` by default; the\n // boot path already populated that. Empty/missing backendUrl falls\n // back to a same-origin relative endpoint so dev-server proxies\n // (Vite, Webpack) and host pages serving the SDK from their own\n // domain work without extra config. Same-origin is also the only\n // path that lets the `syntro_chat_session` cookie ride along \u2014 Lax\n // cookies don't follow cross-origin fetches.\n const trimmed = backendUrl.replace(/\\/$/, '');\n const endpoint = trimmed\n ? `${trimmed}/api/adaptive/mounted_elements`\n : '/api/adaptive/mounted_elements';\n fetchMountedElements({ endpoint }).then((response) => {\n if (!response) return; // 404 / network error / feature off \u2014 silent no-op\n // Hydrate replays each instance through its handler.mount(); for\n // ItemHandler that publishes `element.compositional_append` events,\n // which the chips strip subscribes to and re-inserts. Effects are\n // NOT replayed \u2014 they were never persisted in mounted_elements\n // (`_emit_effect_envelope` in the agent skips persistence).\n void store.hydrate(response.mounted_elements);\n });\n}\n\nfunction configureTransportIfPossible(cfg: ChatBarMountConfig): void {\n // backendUrl === \"\" is a valid configuration \u2014 the chat-bar makes\n // relative-URL fetches and the host page's dev-server proxy (or\n // production reverse proxy) forwards `/api/...` to the backend.\n // This keeps the chat on the page's own origin, which lets cookies\n // ride along even when the backend container is on a different host.\n if (cfg.backendUrl === undefined || !cfg.runtime) return;\n // Translate the element-instantiation declarations into the opaque\n // `forwardedProps` bag the transport ships on every AG-UI request.\n // Only attach when the feature is on AND templates are declared \u2014\n // otherwise the backend stays in its safe-default (feature_disabled)\n // path and the tools are never exposed.\n const elementsActive = cfg.elementsEnabled === true && cfg.uiTemplates != null;\n const forwardedProps: Record<string, unknown> | undefined = elementsActive\n ? { elementsEnabled: true, uiTemplates: cfg.uiTemplates }\n : undefined;\n\n // Wire the `syntro.element.mutation` envelope route to a per-surface\n // ElementInstanceStore. The store applies mounts/patches/unmounts to\n // the host page via the runtime's ActionEngine. ChatAssistantLit has\n // its own equivalent wiring (it instantiates AgUiTransport directly,\n // not via this singleton); the lid surface needs the same routing\n // here so mount_element results actually land.\n const runtime = cfg.runtime;\n const onElementMutation = elementsActive\n ? (mutations: ElementMutation[]) => {\n void getOrCreateElementStore(runtime).apply(mutations);\n }\n : undefined;\n\n // Hydrate the element store from the backend session on first chat-bar\n // mount. Async \u2014 by the time the response arrives, container widgets\n // (chips strip, FAQ accordion) have already subscribed to the event\n // bus, so the replay events land in their subscribers. Items mounted\n // by the LLM in previous turns survive SPA navigation + page reload\n // for the lifetime of the AdaptiveSession (~24h default).\n if (elementsActive) {\n hydrateOnce(runtime, cfg.backendUrl);\n }\n\n chatTransport.configure({\n ...cfg,\n forwardedProps,\n onElementMutation,\n } as ChatTransportConfig);\n}\n\nfunction wireListeners(bar: AdaptiveChatBar, state: MountState): Unsubscribe {\n const onMessageSent = (e: Event) => {\n const text = (e as CustomEvent<{ text: string }>).detail.text;\n chatSession.send(text);\n // If no transport is listening for sends (no backendUrl in\n // mountConfig, the runtime injection didn't happen, or a test\n // didn't wire one up) the session would sit in `inFlight: true`\n // forever. Surface a clear, actionable error so the user sees\n // *something* rather than an indefinitely-spinning stop button.\n if (!chatSession.hasTransport()) {\n chatSession.error('Chat backend not configured \u2014 set backendUrl in the canvas config.');\n }\n };\n const onInterrupt = () => {\n chatSession.interrupt();\n };\n const onToolCallApproved = (e: Event) => {\n const detail = (e as CustomEvent<{ toolCallId: string; approved: boolean }>).detail;\n // Default to an empty-object result \u2014 the agent re-runs with the\n // approval signal. Hosts that want richer results (e.g. a form\n // result payload) can listen for `trail-toolcall-approved` higher\n // up and call chatSession.resolveToolCall themselves.\n chatSession.resolveToolCall(detail.toolCallId, {}, detail.approved);\n };\n // onClose reads state.cfg fresh on each invocation so update() can\n // swap the callback without re-wiring listeners.\n const onClose = () => {\n state.cfg.onClose?.();\n };\n bar.addEventListener('chat-message-sent', onMessageSent);\n bar.addEventListener('chat-interrupt', onInterrupt);\n bar.addEventListener('canvas-close', onClose);\n bar.addEventListener('trail-toolcall-approved', onToolCallApproved);\n return () => {\n bar.removeEventListener('chat-message-sent', onMessageSent);\n bar.removeEventListener('chat-interrupt', onInterrupt);\n bar.removeEventListener('canvas-close', onClose);\n bar.removeEventListener('trail-toolcall-approved', onToolCallApproved);\n };\n}\n\nexport const AdaptiveChatBarMountable = {\n mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void {\n const cfg = (mountConfig ?? {}) as ChatBarMountConfig;\n configureTransportIfPossible(cfg);\n const bar = document.createElement('adaptive-chat-bar') as AdaptiveChatBar;\n applyPlaceholder(bar, cfg);\n\n // Subscribe to the shared session. Fires immediately with the\n // current state so the bar reflects any pre-existing conversation.\n const unsubSession = chatSession.subscribe((s) => {\n bar.messages = [...s.messages];\n bar.inFlight = s.inFlight;\n });\n\n // state holds the mutable cfg so update() can swap callbacks\n // without forcing a listener re-wire.\n const state = { bar, cleanup: () => {}, cfg };\n const unwireListeners = wireListeners(bar, state);\n\n container.appendChild(bar);\n\n // Subscribe to the transport's fallback signal. When the transport\n // gives up after sustained failure, replace the bar with the\n // per-customer \"chat unavailable\" card. One-shot \u2014 fallback fires\n // once per configure cycle, so we don't need to track re-mounts.\n const unsubFallback = chatTransport.onFallback(() => {\n bar.remove();\n container.innerHTML = renderFallbackHtml(state.cfg.fallback);\n });\n\n state.cleanup = () => {\n unsubSession();\n unsubFallback();\n unwireListeners();\n bar.remove();\n setState(container, null);\n };\n\n setState(container, state);\n return state.cleanup;\n },\n\n update(container: HTMLElement, mountConfig?: Record<string, unknown>): void {\n const state = getState(container);\n if (!state) return;\n const cfg = (mountConfig ?? {}) as ChatBarMountConfig;\n configureTransportIfPossible(cfg);\n applyPlaceholder(state.bar, cfg);\n state.cfg = cfg;\n },\n};\n", "/**\n * AdaptiveChipsStripMountable \u2014 `MountableWidget` adapter for\n * `<adaptive-chips-strip>`. Registered as widget id\n * `adaptive-chatbot:chips-strip`.\n *\n * Mirrors `AdaptiveChatBarMountable`: creates the Lit element, forwards\n * props, bridges DOM events \u2192 prop callbacks, cleans up on unmount.\n *\n * Props (passed via mountConfig):\n * - `chips: SuggestionChip[]` \u2014 the strip's contents (required)\n * - `onChipRevealed?: ({id, payload}) => void` \u2014 fires when a chip\n * is opened\n * - `onChipDismissed?: ({id}) => void` \u2014 fires when \u00D7 is clicked\n *\n * The runtime registry injects `runtime` into mountConfig (see\n * `WidgetRegistry.mount`). We forward it as `runtimeRef` on the strip\n * so it can mount chip payload widgets through the same registry.\n */\n\nimport './AdaptiveChipsStrip';\nimport type { AdaptiveChipsStrip, SuggestionChip } from './AdaptiveChipsStrip';\n\ninterface ChipsStripMountConfig {\n chips?: SuggestionChip[];\n onChipRevealed?: (detail: { id: string; payload: SuggestionChip['config']['payload'] }) => void;\n onChipDismissed?: (detail: { id: string }) => void;\n // Injected by WidgetRegistry \u2014 see runtime-sdk/src/widgets/WidgetRegistry.ts.\n runtime?: unknown;\n // Injected by SyntroTileCard from the tile's resolved chromeless\n // state (which itself comes from `slot.theme.chromeless` with\n // `tile.chromeless` as the per-tile override).\n chromeless?: boolean;\n}\n\ninterface MountState {\n strip: AdaptiveChipsStrip;\n listeners: {\n revealed: (e: Event) => void;\n dismissed: (e: Event) => void;\n };\n}\n\nconst STATE_KEY = '__syntroChipsStripMount';\n\nfunction getState(container: HTMLElement): MountState | null {\n return (container as unknown as Record<string, MountState | undefined>)[STATE_KEY] ?? null;\n}\nfunction setState(container: HTMLElement, state: MountState | null): void {\n (container as unknown as Record<string, MountState | null>)[STATE_KEY] = state;\n}\n\nfunction applyProps(strip: AdaptiveChipsStrip, cfg: ChipsStripMountConfig): void {\n if (cfg.chips !== undefined) strip.chips = cfg.chips;\n // `runtime` arrives in every mountConfig from the WidgetRegistry \u2014\n // forward it so the strip can resolve + mount payload widgets.\n if (cfg.runtime !== undefined) {\n strip.runtimeRef = cfg.runtime as AdaptiveChipsStrip['runtimeRef'];\n }\n if (cfg.chromeless !== undefined) strip.chromeless = cfg.chromeless;\n}\n\nexport const AdaptiveChipsStripMountable = {\n mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void {\n const cfg = (mountConfig ?? {}) as ChipsStripMountConfig;\n const strip = document.createElement('adaptive-chips-strip') as AdaptiveChipsStrip;\n applyProps(strip, cfg);\n\n const state: MountState = {\n strip,\n listeners: {\n revealed: (e) => {\n const cb = cfg.onChipRevealed;\n if (cb) {\n cb(\n (e as CustomEvent<{ id: string; payload: SuggestionChip['config']['payload'] }>)\n .detail\n );\n }\n },\n dismissed: (e) => {\n const cb = cfg.onChipDismissed;\n if (cb) cb((e as CustomEvent<{ id: string }>).detail);\n },\n },\n };\n strip.addEventListener('chip-revealed', state.listeners.revealed);\n strip.addEventListener('chip-dismissed', state.listeners.dismissed);\n\n container.appendChild(strip);\n setState(container, state);\n\n return () => {\n strip.removeEventListener('chip-revealed', state.listeners.revealed);\n strip.removeEventListener('chip-dismissed', state.listeners.dismissed);\n strip.remove();\n setState(container, null);\n };\n },\n\n update(container: HTMLElement, mountConfig?: Record<string, unknown>): void {\n const state = getState(container);\n if (!state) return;\n const cfg = (mountConfig ?? {}) as ChipsStripMountConfig;\n applyProps(state.strip, cfg);\n // Re-wire callback listeners so the new cfg's handlers fire.\n state.strip.removeEventListener('chip-revealed', state.listeners.revealed);\n state.strip.removeEventListener('chip-dismissed', state.listeners.dismissed);\n state.listeners.revealed = (e) => {\n const cb = cfg.onChipRevealed;\n if (cb) {\n cb((e as CustomEvent<{ id: string; payload: SuggestionChip['config']['payload'] }>).detail);\n }\n };\n state.listeners.dismissed = (e) => {\n const cb = cfg.onChipDismissed;\n if (cb) cb((e as CustomEvent<{ id: string }>).detail);\n };\n state.strip.addEventListener('chip-revealed', state.listeners.revealed);\n state.strip.addEventListener('chip-dismissed', state.listeners.dismissed);\n },\n};\n", "/**\n * NavLinkMountable \u2014 `MountableWidget` used as the payload of an\n * LLM-authored navigation chip.\n *\n * Flow:\n * LLM mounts `suggest-navigation` ItemTemplate with `{title, url}`\n * \u2192 backend builds chip with payload `{widget: 'adaptive-chatbot:nav-link', props: {url}}`\n * \u2192 user clicks the chip \u2192 chips-strip mounts THIS widget into the\n * chip's drawer\n * \u2192 user clicks the \"Go to <url>\" button this widget renders\n * \u2192 same-origin SPA navigation via pushState + popstate (matches\n * `adaptive-nav`'s `executeNavigate` path for SPA-router compatibility)\n *\n * Registered as widget id `adaptive-chatbot:nav-link`.\n *\n * Security note: only http(s), same-origin destinations are honored.\n * `javascript:`, `data:`, and cross-origin URLs are silently dropped at\n * URL construction. The ItemTemplate's EnumField allowlist is the\n * primary constraint; this is defense-in-depth.\n */\n\ninterface NavLinkProps {\n url?: string;\n /** Optional override for the button label. Defaults to \"Go to <url>\".\n * The chip's title already advertises the destination; this is just\n * the call-to-action verb. */\n label?: string;\n}\n\ninterface MountState {\n button: HTMLButtonElement;\n cfg: NavLinkProps;\n onClick: (e: Event) => void;\n}\n\nconst STATE_KEY = '__syntroNavLinkMount';\n\nfunction getState(container: HTMLElement): MountState | null {\n return (container as unknown as Record<string, MountState | undefined>)[STATE_KEY] ?? null;\n}\nfunction setState(container: HTMLElement, state: MountState | null): void {\n (container as unknown as Record<string, MountState | null>)[STATE_KEY] = state;\n}\n\nfunction navigateTo(rawUrl: string): void {\n if (!rawUrl) return;\n let resolved: URL;\n try {\n resolved = new URL(rawUrl, window.location.origin);\n } catch {\n return;\n }\n if (resolved.origin !== window.location.origin) return;\n if (resolved.protocol !== 'http:' && resolved.protocol !== 'https:') return;\n window.history.pushState(null, '', resolved.toString());\n window.dispatchEvent(new PopStateEvent('popstate'));\n}\n\nfunction buildButton(cfg: NavLinkProps): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.dataset.syntroNavLink = '';\n button.style.display = 'inline-flex';\n button.style.alignItems = 'center';\n button.style.gap = '6px';\n button.style.padding = '6px 12px';\n button.style.borderRadius = '8px';\n button.style.fontFamily = \"var(--sc-font-family, 'system-ui')\";\n button.style.fontSize = '12px';\n button.style.fontWeight = '600';\n button.style.cursor = 'pointer';\n button.style.color = 'var(--sc-accent-foreground, currentColor)';\n button.style.background = 'hsl(var(--sc-accent-color) / 0.85)';\n button.style.border = '1px solid hsl(var(--sc-accent-color) / 0.30)';\n applyLabel(button, cfg);\n return button;\n}\n\nfunction applyLabel(button: HTMLButtonElement, cfg: NavLinkProps): void {\n const url = String(cfg.url ?? '');\n const label = cfg.label ?? (url ? `Go to ${url}` : 'Go');\n // textContent (not innerHTML) \u2014 LLM-authored URLs are an enum on the\n // backend so injection isn't realistic, but defense-in-depth is cheap.\n button.textContent = label;\n}\n\nexport const NavLinkMountable = {\n mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void {\n const cfg = (mountConfig ?? {}) as NavLinkProps;\n const button = buildButton(cfg);\n const onClick = (_e: Event): void => {\n const current = getState(container);\n const url = current?.cfg.url ?? cfg.url;\n navigateTo(String(url ?? ''));\n };\n button.addEventListener('click', onClick);\n container.appendChild(button);\n setState(container, { button, cfg, onClick });\n return () => {\n button.removeEventListener('click', onClick);\n button.remove();\n setState(container, null);\n };\n },\n\n update(container: HTMLElement, mountConfig?: Record<string, unknown>): void {\n const state = getState(container);\n if (!state) return;\n const cfg = (mountConfig ?? {}) as NavLinkProps;\n applyLabel(state.button, cfg);\n state.cfg = cfg;\n },\n};\n", "/**\n * TextAnswerMountable \u2014 `MountableWidget` for the simplest chip payload\n * kind: a single paragraph of body text.\n *\n * Registered as widget id `adaptive-chatbot:text-answer`. Used by\n * `AdaptiveChipsStrip` when a chip's payload references it.\n *\n * Replaces the old hand-rolled resolver path. Customers wanting richer\n * payloads (markdown, mini-product cards, embedded charts, etc.) ship\n * their own mountable through the same registry \u2014 no special\n * resolver-map seam.\n */\n\ninterface TextAnswerProps {\n text?: string;\n}\n\ninterface MountState {\n paragraph: HTMLParagraphElement;\n cfg: TextAnswerProps;\n}\n\nconst STATE_KEY = '__syntroTextAnswerMount';\n\nfunction getState(container: HTMLElement): MountState | null {\n return (container as unknown as Record<string, MountState | undefined>)[STATE_KEY] ?? null;\n}\nfunction setState(container: HTMLElement, state: MountState | null): void {\n (container as unknown as Record<string, MountState | null>)[STATE_KEY] = state;\n}\n\nfunction applyText(p: HTMLParagraphElement, text: string): void {\n p.textContent = text;\n}\n\nexport const TextAnswerMountable = {\n mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void {\n const cfg = (mountConfig ?? {}) as TextAnswerProps;\n const paragraph = document.createElement('p');\n paragraph.dataset.syntroTextAnswer = '';\n // Keep the styling minimal \u2014 paragraph inherits the slot's\n // `--sc-tile-text-color` (set by the chips strip's drawer chrome)\n // so the text reads consistently against the host's surface.\n paragraph.style.margin = '0';\n paragraph.style.fontSize = '12px';\n paragraph.style.lineHeight = '1.55';\n paragraph.style.color = 'var(--sc-tile-text-color, currentColor)';\n applyText(paragraph, String(cfg.text ?? ''));\n container.appendChild(paragraph);\n\n setState(container, { paragraph, cfg });\n return () => {\n paragraph.remove();\n setState(container, null);\n };\n },\n\n update(container: HTMLElement, mountConfig?: Record<string, unknown>): void {\n const state = getState(container);\n if (!state) return;\n const cfg = (mountConfig ?? {}) as TextAnswerProps;\n applyText(state.paragraph, String(cfg.text ?? ''));\n state.cfg = cfg;\n },\n};\n", "/**\n * Adaptive Chatbot - Runtime Module\n *\n * Runtime manifest for the AI chat assistant adaptive.\n * Uses @syntrologie/chat for the UI \u2014 no React dependency.\n *\n * Chat surfaces are config-driven: customers reference the chat-bar\n * widget id (`adaptive-chatbot:chat-bar`) from `slots.drawer.lid` (to\n * replace the launcher FAB) or any other slot's `lid` / tile slot. The\n * adaptive no longer auto-registers a canvas lid renderer.\n */\n\n// Side-effect imports so the custom elements register with the global\n// registry as soon as the adaptive is loaded.\nimport './AdaptiveChatBar';\nimport './AdaptiveChatTrail';\nimport { AdaptiveChatBarMountable } from './AdaptiveChatBarMountable';\nimport { AdaptiveChipsStripMountable } from './AdaptiveChipsStripMountable';\nimport { ChatAssistantLitMountable } from './ChatAssistantLit';\nimport { NavLinkMountable } from './NavLinkMountable';\nimport { TextAnswerMountable } from './TextAnswerMountable';\n\nexport const runtime = {\n id: 'adaptive-chatbot',\n version: '2.0.0',\n name: 'Chat Assistant',\n description: 'AI chat assistant powered by @syntrologie/chat with SSE transport',\n\n executors: [],\n\n widgets: [\n {\n id: 'adaptive-chatbot:assistant',\n component: ChatAssistantLitMountable,\n metadata: {\n name: 'Chat Assistant',\n description: 'AI-powered chat assistant with SSE streaming and A2UI support',\n icon: '\uD83D\uDCAC',\n },\n },\n // PRD \u00A73 chat lid: the standalone chat bar UI shell. Used as a slot\n // `lid` widget (where it replaces the launcher) or as a regular\n // tile widget (renders inside the drawer). Headless of any chat\n // transport \u2014 emits chat-message-sent / chat-interrupt / canvas-close\n // for the parent to wire to whatever pipeline it owns.\n {\n id: 'adaptive-chatbot:chat-bar',\n component: AdaptiveChatBarMountable,\n metadata: {\n name: 'Chat Bar',\n description:\n 'Always-visible chat input row with bubble-up trail. Headless \u2014 wire to your own transport.',\n icon: '\u2726',\n },\n },\n // PRD \u00A74.5 suggested-chips tile: horizontally-wrapping chip strip\n // with click-to-reveal payload drawer. Each chip is a\n // `suggestions:chip` action.\n {\n id: 'adaptive-chatbot:chips-strip',\n component: AdaptiveChipsStripMountable,\n metadata: {\n name: 'Suggested Chips',\n description: 'Wrapping chip strip with click-to-reveal payloads.',\n icon: '\u2728',\n },\n },\n // Built-in chip payload \u2014 a single paragraph of body text. Chip\n // payloads are widget references (same {widget,props} shape as\n // tile/lid configs); this is the simplest one shipped out of the\n // box so the chip pattern works without requiring a downstream\n // adaptive for a \"just answer with text\" case.\n {\n id: 'adaptive-chatbot:text-answer',\n component: TextAnswerMountable,\n metadata: {\n name: 'Text Answer',\n description: 'Single paragraph payload \u2014 the default chip-drawer content.',\n icon: '\u00B6',\n },\n },\n // Chip payload for LLM-authored navigation suggestions. Backed by\n // the `suggest-navigation` ItemTemplate: the backend wraps a curated\n // URL into props.url, the chip's payload-drawer mounts this widget,\n // user clicks \"Go to <url>\" \u2192 same-origin SPA navigation. Kept here\n // (not in adaptive-nav) so chip payloads ship with the chatbot\n // bundle that already provides chips-strip + text-answer.\n {\n id: 'adaptive-chatbot:nav-link',\n component: NavLinkMountable,\n metadata: {\n name: 'Nav Link',\n description:\n 'Same-origin navigation button \u2014 used as a chip payload for LLM-authored nav suggestions.',\n icon: '\u2192',\n },\n },\n ],\n};\n\nexport default runtime;\n"],
5
+ "mappings": "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",
6
6
  "names": ["runtime", "runtime", "STATE_KEY", "getState", "setState", "STATE_KEY", "getState", "setState", "STATE_KEY", "getState", "setState"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syntrologie/adapt-chatbot",
3
- "version": "2.8.0-canary.273",
3
+ "version": "2.8.0-canary.274",
4
4
  "description": "Adaptive Chatbot - AI chat assistant widget with action execution",
5
5
  "license": "Proprietary",
6
6
  "private": false,
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/AdaptiveChatTrail.ts"],
4
- "sourcesContent": ["/**\n * AdaptiveChatTrail \u2014 the \"bubble-up\" message column that sits above the\n * chat bar in the canvas lid.\n *\n * Messages appear immediately above the chat bar and drift upward as\n * newer ones arrive. Each stack position carries an opacity + blur\n * falloff so older messages read as fading from the page. Beyond a\n * `visibleCount` cap (default 3) the trail collapses into an \"N more \u00B7\n * expand\" affordance \u2014 clicking emits `trail-expand`.\n *\n * Light DOM (no shadow root) so the host page's CSS variables and the\n * surrounding canvas tokens flow through without a nested shadow\n * boundary.\n *\n * See PRD \u00A74.3 (chat trail) for the canonical motion + falloff spec.\n */\n\nimport { renderMarkdown } from '@syntrologie/chat';\nimport { html, LitElement, nothing } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\n\nexport interface TrailToolCall {\n id: string;\n name: string;\n status: 'args-streaming' | 'pending' | 'running' | 'done' | 'error';\n}\n\nexport interface TrailMessage {\n /** Stable identity for keyed rendering (must be unique within the trail). */\n id: number | string;\n role: 'user' | 'assistant' | 'system';\n text: string;\n /** Streaming \u2192 assistant text still arriving from backend. Complete \u2192 final. Error \u2192 fatal during stream. */\n status?: 'streaming' | 'complete' | 'error';\n /** Tool calls attached to an assistant message (rendered as compact chips). */\n toolCalls?: TrailToolCall[];\n}\n\n/** PRD \u00A74.3 constants. */\nconst DEFAULT_VISIBLE = 3;\nconst OPACITY_STEP = 0.22;\nconst OPACITY_FLOOR = 0.18;\nconst Y_DRIFT_PX = -2;\n/**\n * Height of the absolute blur veil at the top of the trail's scroll\n * area. Pixel-based, NOT message-count-based: with the old per-chip\n * `filter: blur(pos * step)` model, a single long markdown response\n * at pos=1 would have its entire body uniformly blurred \u2014 including\n * the chunk visually adjacent to the crisp newest message \u2014 because\n * blur is a property of the chip element, not of pixels. Switching\n * to a fixed-height backdrop-blur veil keeps the bottom of the\n * visible area (newest content) crisp regardless of message length.\n */\nconst TRAIL_BLUR_VEIL_PX = 72;\n/** Max blur applied at the very top of the veil; tapers to 0 at the bottom edge. */\nconst TRAIL_BLUR_MAX_PX = 5;\n\nexport class AdaptiveChatTrail extends LitElement {\n static override properties = {\n messages: { attribute: false },\n visibleCount: { type: Number },\n expanded: { type: Boolean },\n greeting: { type: String },\n };\n\n messages: TrailMessage[] = [];\n visibleCount = DEFAULT_VISIBLE;\n expanded = false;\n /**\n * Pre-conversation phantom assistant message. Rendered only when\n * `messages` is empty. NOT injected into chatSession state \u2014 the\n * greeting is configuration, not conversation, so it disappears\n * automatically when the first real message arrives.\n */\n greeting: string | undefined = undefined;\n\n override createRenderRoot(): HTMLElement {\n // Light DOM \u2014 canvas-level CSS vars reach the chips directly.\n return this;\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n ensureStreamingKeyframes();\n }\n\n private _onExpand = (): void => {\n // Self-manage: clicking the inline link expands the trail in place.\n // We still dispatch the event so any parent that wants to react\n // (telemetry, mirror state to a side panel, etc.) can listen.\n this.expanded = true;\n this.dispatchEvent(new CustomEvent('trail-expand', { bubbles: true, composed: true }));\n };\n\n /**\n * Approve a pending client tool call. The trail emits a generic\n * event so the host can decide whether to forward to chatSession\n * (the common path) or override. Keeps the trail itself free of\n * direct chatSession coupling \u2014 it's a pure view component.\n */\n private _onToolCallClick = (tc: TrailToolCall): void => {\n if (tc.status !== 'pending') return;\n this.dispatchEvent(\n new CustomEvent<{ toolCallId: string; approved: boolean }>('trail-toolcall-approved', {\n detail: { toolCallId: tc.id, approved: true },\n bubbles: true,\n composed: true,\n })\n );\n };\n\n private _onCollapse = (): void => {\n // Mirror of _onExpand for the minimize affordance shown while\n // expanded. Self-manages + dispatches `trail-collapse` for parent\n // observers (telemetry, side-panel mirrors).\n this.expanded = false;\n this.dispatchEvent(new CustomEvent('trail-collapse', { bubbles: true, composed: true }));\n };\n\n override updated(changed: Map<string, unknown>): void {\n // Anchor the scroll to the bottom whenever:\n // - the trail flips into expanded mode (UX continuity), OR\n // - new messages or text deltas arrive (so a long streaming chip\n // stays pinned to the latest line, not the start of the chip).\n // Skip when the user has manually scrolled UP into history \u2014 we'd\n // rather lose pinning than yank them out of what they're reading.\n if (changed.has('expanded') || changed.has('messages')) {\n requestAnimationFrame(() => {\n const container = this.querySelector<HTMLElement>('[data-syntro-chat-trail]');\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n // 200px tolerance \u2014 streaming deltas can append a paragraph in\n // one update, easily 80-150px. A tighter window would strand\n // the viewer at the top of the chip mid-stream because the\n // first delta blew past the threshold. User-driven scroll-up\n // of >200px disables auto-pin until they scroll back down.\n if (distanceFromBottom < 200 || changed.has('expanded')) {\n container.scrollTop = container.scrollHeight;\n }\n });\n }\n }\n\n override render() {\n if (this.messages.length === 0 && !this.greeting) return nothing;\n if (this.messages.length === 0 && this.greeting) {\n // Render the greeting as a pure-display assistant chip. No\n // tool-calls, no streaming caret, single chip at pos=0.\n return html`<div data-syntro-chat-trail style=${styleMap({\n display: 'flex',\n flexDirection: 'column',\n gap: '6px',\n justifyContent: 'flex-end',\n width: '100%',\n padding: '0',\n pointerEvents: 'auto',\n })}>\n <div\n data-trail-chip\n data-role=\"assistant\"\n data-status=\"greeting\"\n style=${styleMap(chipStyles('assistant', 0, { isStreaming: false, isError: false }))}\n >${unsafeHTML(renderMarkdown(stripTrailingWhitespace(this.greeting)))}</div>\n </div>`;\n }\n\n // Choose the slice we actually paint. Newest are at the END of the\n // array (closest to the chat bar). When not expanded, take the last\n // `visibleCount`; when expanded, take all.\n const visible = this.expanded ? this.messages : this.messages.slice(-this.visibleCount);\n const hidden = this.messages.length - visible.length;\n\n const baseStyles: Record<string, string> = {\n display: 'flex',\n flexDirection: 'column',\n gap: '6px',\n // NO `justifyContent: flex-end` here \u2014 combined with `overflow: auto`\n // it's a known cross-browser bug: oversized flex children get\n // pushed ABOVE the container without contributing to scrollHeight,\n // so the scrollbar never appears. We rely on `:first-child {\n // margin-top: auto }` (injected as global CSS) to push short\n // content to the bottom of the box while leaving overflow handling\n // to the native scroll container.\n width: '100%',\n // The trail itself has no chrome \u2014 chips sit on the host page bg.\n padding: '0',\n pointerEvents: 'auto',\n };\n\n // Expanded mode: cap height, scroll, and add a visible top border so\n // the panel reads as a bounded region. NO background tint or shadow\n // \u2014 the ambient (collapsed) treatment is fine and we don't want to\n // suddenly introduce panel chrome that wasn't there before. The\n // border alone marks the panel's top edge.\n // Falloff is turned off per-chip below \u2014 readability wins once the\n // user has explicitly asked for history. See PRD \u00A74.3.\n // Collapsed mode also gets a (looser) cap + scroll so a single long\n // markdown chip doesn't blow the chat-bar off-screen with no\n // scroll affordance. Tighter than expanded so the bubble-up trail\n // doesn't dominate the surface visually.\n const modeStyles: Record<string, string> = this.expanded\n ? {\n maxHeight: 'min(320px, 40vh)',\n overflowY: 'auto',\n paddingTop: '8px',\n borderTop: 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))',\n scrollBehavior: 'smooth',\n scrollbarWidth: 'thin',\n }\n : {\n // Collapsed mode = ambient peek surface. Never a scrollbar:\n // long messages slide up behind the blur veil instead of\n // exposing a scrollbar. The \"\u2191 N more \u00B7 expand\" affordance\n // is the only entry into history; clicking it switches to\n // expanded mode where scroll IS allowed.\n // Height kept short so the trail reads as a peek strip above\n // the chat bar, not a panel \u2014 long single replies fade into\n // the blur veil at the top, user expands to read the rest.\n maxHeight: 'min(140px, 22vh)',\n overflow: 'hidden',\n };\n\n const containerStyles = { ...baseStyles, ...modeStyles };\n\n // Wrap the scroll container in a positioning frame so the absolute\n // blur veil (rendered at the top) anchors to the scroll viewport's\n // top edge \u2014 NOT to a chip that scrolls under it. Pixel-anchored\n // blur means: the top TRAIL_BLUR_VEIL_PX of the visible area is\n // veiled regardless of how many messages occupy those pixels.\n const frameStyles: Record<string, string> = {\n position: 'relative',\n width: '100%',\n };\n\n return html`\n <div data-syntro-chat-trail-frame style=${styleMap(frameStyles)}>\n ${\n // Collapse-mode \"expand\" affordance lives ABOVE the scroll\n // container now. Previously it was rendered as the first child\n // INSIDE the scroll container \u2014 but with `overflow: hidden` on\n // collapsed mode, the `:first-child { margin-top: auto }` rule\n // pushed it ABOVE the scroll viewport's top edge, where the\n // overflow clip hid it. Moving it out of the scroll container\n // makes it always visible at the top of the trail frame.\n this.expanded\n ? nothing\n : hidden > 0\n ? html`<button\n type=\"button\"\n data-trail-more\n @click=${this._onExpand}\n style=${styleMap(moreStyles())}\n >\u2191 ${hidden} more \u00B7 expand</button>`\n : nothing\n }\n <div data-syntro-chat-trail style=${styleMap(containerStyles)}>\n ${visible.map((m, i) => {\n // Stack position from the bar: 0 = closest, N-1 = oldest.\n // visible[last] is closest, so reverse the index from the\n // tail of the visible slice. When expanded, the ambient\n // falloff is suppressed (pos = 0) so every chip reads at\n // full opacity \u2014 explicit history view, not ambient trail.\n const pos = this.expanded ? 0 : visible.length - 1 - i;\n const isStreaming = m.status === 'streaming';\n const isError = m.status === 'error' || m.role === 'system';\n const toolCalls = m.toolCalls ?? [];\n // Assistant text is markdown-formatted by the agent (lists,\n // code, bold, links). renderMarkdown sanitizes via DOMPurify,\n // then unsafeHTML injects the safe HTML. User + system chips\n // stay plain text \u2014 no markdown risk + no expansion attack\n // surface on user-typed input.\n const renderedText =\n m.role === 'assistant'\n ? html`${unsafeHTML(renderMarkdown(stripTrailingWhitespace(m.text)))}`\n : html`${m.text}`;\n return html`<div\n data-trail-chip\n data-role=${m.role}\n data-status=${m.status ?? 'complete'}\n style=${styleMap(chipStyles(m.role, pos, { isStreaming, isError }))}\n >${renderedText}${\n isStreaming\n ? html`<span\n data-trail-caret\n aria-hidden=\"true\"\n style=${styleMap(caretStyles())}\n ></span>`\n : nothing\n }${\n toolCalls.length > 0\n ? html`<div data-trail-toolcalls style=${styleMap(toolCallStripStyles())}>\n ${toolCalls.map(\n (tc) => html`<button\n type=\"button\"\n data-trail-toolcall\n data-tool-id=${tc.id}\n data-tool-status=${tc.status}\n @click=${() => this._onToolCallClick(tc)}\n ?disabled=${tc.status !== 'pending'}\n style=${styleMap(toolCallChipStyles(tc.status))}\n title=\"${tc.name} \u00B7 ${tc.status}\"\n >${toolCallIcon(tc.status)} ${tc.name}</button>`\n )}\n </div>`\n : nothing\n }</div>`;\n })}\n </div>\n ${\n // The blur veil only fires in collapsed (ambient) mode.\n // Expanded = the user explicitly asked for history; blurring\n // the top of the panel would hide content (including the\n // minimize button just above the trail's top edge) the user\n // came here to see.\n this.expanded\n ? nothing\n : html`<div\n data-trail-blur-veil\n aria-hidden=\"true\"\n style=${styleMap(blurVeilStyles())}\n ></div>`\n }\n </div>\n ${\n // Always-available \"minimize\" affordance: sits just above the\n // chat-row (chat-bar arranges trail then row in a flex column),\n // so the user can collapse the trail no matter how far down\n // they've scrolled inside the expanded view.\n this.expanded\n ? html`<button\n type=\"button\"\n data-trail-collapse\n @click=${this._onCollapse}\n style=${styleMap(stickyMinimizeStyles())}\n >\u2304 minimize</button>`\n : nothing\n }\n `;\n }\n}\n\n/**\n * Absolute blur veil at the top of the scroll viewport. Uses\n * `backdrop-filter: blur(...)` so whatever pixels sit beneath the veil\n * (top of the scroll content) become blurred, regardless of which chip\n * those pixels belong to. A linear mask fades the blur effect from full\n * at the top to zero at the bottom of the veil \u2014 newest content stays\n * crisp because it lives below the veil's height.\n *\n * Note: `mask-image` controls where the BACKDROP-FILTER applies (via\n * masking the veil element), so the bottom edge of the veil naturally\n * dissolves into the unblurred scroll area below.\n */\nfunction blurVeilStyles(): Record<string, string> {\n const mask = 'linear-gradient(to bottom, black 0%, black 40%, transparent 100%)';\n return {\n position: 'absolute',\n top: '0',\n left: '0',\n right: '0',\n height: `${TRAIL_BLUR_VEIL_PX}px`,\n pointerEvents: 'none',\n backdropFilter: `blur(${TRAIL_BLUR_MAX_PX}px)`,\n WebkitBackdropFilter: `blur(${TRAIL_BLUR_MAX_PX}px)`,\n maskImage: mask,\n WebkitMaskImage: mask,\n zIndex: '2',\n };\n}\n\nfunction chipStyles(\n role: TrailMessage['role'],\n pos: number,\n state: { isStreaming: boolean; isError: boolean } = { isStreaming: false, isError: false }\n): Record<string, string> {\n const opacity = Math.max(OPACITY_FLOOR, 1 - pos * OPACITY_STEP);\n const yPx = pos * Y_DRIFT_PX;\n\n // Error chips override role-specific styling \u2014 they shouldn't read\n // as \"an assistant reply.\" Streaming chips get a subtle accent ring\n // so the in-progress state is visually distinct from a settled reply.\n // All four states resolve to design-system tokens via the theme; the\n // fallbacks here are conservative neutrals so the widget still renders\n // sensibly on a host page that hasn't set tokens yet.\n const border = state.isError\n ? 'var(--sc-content-bubble-border-error, 1px solid rgba(220, 80, 80, 0.55))'\n : state.isStreaming\n ? 'var(--sc-content-bubble-border-user, 1px solid rgba(255, 255, 255, 0.28))'\n : role === 'user'\n ? 'var(--sc-content-bubble-border-user, 1px solid rgba(255, 255, 255, 0.28))'\n : 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))';\n const background = state.isError\n ? 'var(--sc-content-bubble-background-error, rgba(140, 40, 40, 0.42))'\n : role === 'user'\n ? 'var(--sc-content-bubble-background-user, rgba(255, 255, 255, 0.10))'\n : 'var(--sc-content-bubble-background, rgba(20, 22, 24, 0.35))';\n\n const base: Record<string, string> = {\n alignSelf: role === 'user' ? 'flex-end' : 'flex-start',\n maxWidth: '85%',\n fontSize: '11px',\n lineHeight: '1.45',\n padding: '4px 10px',\n borderRadius: '10px',\n border,\n background,\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n color: state.isError\n ? 'var(--sc-content-bubble-text-error, rgba(255, 220, 220, 0.95))'\n : 'var(--sc-tile-title-color, inherit)',\n transition:\n 'opacity 240ms cubic-bezier(0.22, 1, 0.36, 1), transform 240ms cubic-bezier(0.22, 1, 0.36, 1)',\n // Per-position opacity + y-drift falloff is still per-chip \u2014 those\n // operate naturally per element. Blur is NOT per-chip anymore: it's\n // applied via an absolute-positioned veil at the top of the scroll\n // viewport (TRAIL_BLUR_VEIL_PX above), so a single long message\n // doesn't get uniformly blurred across its full body.\n opacity: String(toFixedTrim(opacity)),\n transform: `translateY(${yPx}px)`,\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n };\n return base;\n}\n\nfunction toolCallStripStyles(): Record<string, string> {\n return {\n display: 'flex',\n flexWrap: 'wrap',\n gap: '4px',\n marginTop: '4px',\n paddingTop: '4px',\n borderTop: '1px dashed var(--sc-content-divider-color, rgba(255, 255, 255, 0.12))',\n };\n}\n\nfunction toolCallChipStyles(status: TrailToolCall['status']): Record<string, string> {\n const isPending = status === 'pending';\n const isError = status === 'error';\n const isDone = status === 'done';\n return {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '4px',\n padding: '2px 7px',\n border: isError\n ? 'var(--sc-content-bubble-border-error, 1px solid rgba(220, 80, 80, 0.45))'\n : isPending\n ? 'var(--sc-content-bubble-border-user, 1px solid rgba(255, 255, 255, 0.28))'\n : 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))',\n background: isError\n ? 'var(--sc-content-bubble-background-error, rgba(140, 40, 40, 0.35))'\n : isPending\n ? 'var(--sc-content-bubble-background-user, rgba(255, 255, 255, 0.10))'\n : isDone\n ? 'var(--sc-content-bubble-background-idle, rgba(40, 44, 50, 0.4))'\n : 'var(--sc-content-bubble-background, rgba(20, 22, 24, 0.35))',\n color: 'var(--sc-tile-text-color, inherit)',\n borderRadius: '999px',\n fontSize: '10px',\n fontWeight: '500',\n fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',\n cursor: isPending ? 'pointer' : 'default',\n opacity: isDone ? '0.7' : '1',\n };\n}\n\nfunction toolCallIcon(status: TrailToolCall['status']): string {\n switch (status) {\n case 'args-streaming':\n case 'running':\n return '\u22EF';\n case 'pending':\n return '?';\n case 'done':\n return '\u2713';\n case 'error':\n return '\u2717';\n default:\n return '\u00B7';\n }\n}\n\nfunction caretStyles(): Record<string, string> {\n return {\n display: 'inline-block',\n width: '6px',\n height: '12px',\n marginLeft: '3px',\n verticalAlign: '-1px',\n background: 'hsl(var(--sc-accent-color) / 0.85)',\n borderRadius: '1px',\n animation: 'syntro-trail-caret 1s steps(2, end) infinite',\n };\n}\n\nfunction stickyMinimizeStyles(): Record<string, string> {\n return {\n alignSelf: 'center',\n marginTop: '4px',\n fontSize: '10px',\n fontWeight: '500',\n letterSpacing: '0.06em',\n padding: '4px 10px',\n border: '1px solid hsl(var(--sc-accent-color) / 0.32)',\n borderRadius: '999px',\n background: 'hsl(var(--sc-accent-color) / 0.10)',\n color: 'var(--sc-tile-text-color, currentColor)',\n cursor: 'pointer',\n opacity: '0.85',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n transition: 'opacity 150ms ease, background 150ms ease',\n };\n}\n\nfunction moreStyles(): Record<string, string> {\n return {\n alignSelf: 'center',\n fontSize: '10px',\n fontWeight: '500',\n letterSpacing: '0.06em',\n padding: '2px 8px',\n border: 'none',\n background: 'transparent',\n color: 'var(--sc-tile-text-color, currentColor)',\n cursor: 'pointer',\n opacity: '0.6',\n transition: 'opacity 150ms ease',\n };\n}\n\n/**\n * Inject the streaming-caret keyframes once per document. Light-DOM\n * components can't ship CSS via Lit's `styles` static \u2014 the rules\n * need to be present in the document's stylesheet. Idempotent via\n * a data attribute on the injected style tag.\n */\nfunction ensureStreamingKeyframes(): void {\n if (typeof document === 'undefined') return;\n if (document.head.querySelector('style[data-syntro-trail-caret-keyframes]')) return;\n const style = document.createElement('style');\n style.setAttribute('data-syntro-trail-caret-keyframes', 'true');\n // Caret animation + markdown reset for assistant chips. The trail\n // chips are tiny (11px line-height); default <p>/<ul>/<pre> margins\n // would push the chip vertically and break the falloff layout.\n style.textContent = [\n '@keyframes syntro-trail-caret { 0%{opacity:1} 50%{opacity:0} 100%{opacity:1} }',\n // Anchor short content to the bottom of the scrollable trail \u2014\n // see baseStyles comment for why we can't use justify-content.\n '[data-syntro-chat-trail] > :first-child { margin-top: auto }',\n // Tight spacing \u2014 trail chips are an 11px-line-height pill. Default\n // browser <p>/<ul> margins (1em \u2248 11px each side) would dominate the\n // chip. Keep paragraph + list separators minimal (4px) so multi-\n // paragraph replies read as one continuous flow, not a vertical\n // stack of fragments.\n '[data-trail-chip] > p:first-child { margin-top: 0 }',\n '[data-trail-chip] > p:last-child { margin-bottom: 0 }',\n '[data-trail-chip] p { margin: 4px 0 }',\n '[data-trail-chip] br + br { display: none }',\n '[data-trail-chip] ul, [data-trail-chip] ol { margin: 4px 0; padding-left: 1.1em }',\n '[data-trail-chip] li { margin: 0 }',\n '[data-trail-chip] li + li { margin-top: 1px }',\n '[data-trail-chip] pre { margin: 6px 0; padding: 6px 8px; background: var(--sc-content-code-background-block, rgba(0, 0, 0, 0.35)); border-radius: 6px; overflow-x: auto; font-size: 10px }',\n '[data-trail-chip] code { font-family: ui-monospace, SF Mono, Menlo, monospace; font-size: 10px; background: var(--sc-content-code-background, rgba(0, 0, 0, 0.25)); padding: 1px 4px; border-radius: 3px }',\n '[data-trail-chip] pre code { background: transparent; padding: 0 }',\n '[data-trail-chip] a { color: var(--sc-content-link-color, var(--sc-color-primary, #b72e2a)); text-decoration: underline }',\n '[data-trail-chip] strong { font-weight: 600 }',\n ].join(' ');\n document.head.appendChild(style);\n}\n\n/**\n * Pre-process LLM markdown before rendering. Three common artifacts:\n *\n * 1. Trailing \" \" (two spaces = markdown hard-break) sprinkled at\n * end-of-line, even inside list items where it just leaks\n * invisible whitespace into the DOM.\n * 2. Runs of 3+ blank lines used as visual separators. Markdown\n * treats any number of blank lines as a single paragraph break,\n * so the extras add nothing semantically \u2014 but combined with\n * paragraph margins they create huge gaps in the tiny chip layout.\n * 3. Leading / trailing newlines on the whole message. Trailing\n * `\\n\\n` is the common one \u2014 marked turns it into a final empty\n * <p> that still occupies a full line-height of vertical space,\n * leaving a visible gap between the last text and the chat bar.\n *\n * Collapsing all three keeps the rendered HTML tight without changing\n * the agent's semantic intent.\n */\nfunction stripTrailingWhitespace(text: string): string {\n return text\n .replace(/[ \\t]+$/gm, '') // trailing whitespace per line\n .replace(/\\n{3,}/g, '\\n\\n') // collapse runs of blank lines\n .trim(); // drop leading/trailing whitespace+newlines on the whole message\n}\n\n/** Two-decimal trim that avoids trailing zeros (\".50\" \u2192 \".5\"). */\nfunction toFixedTrim(n: number): string {\n const s = n.toFixed(2);\n return s.replace(/\\.?0+$/, '') || '0';\n}\n\nif (!customElements.get('adaptive-chat-trail')) {\n customElements.define('adaptive-chat-trail', AdaptiveChatTrail);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'adaptive-chat-trail': AdaptiveChatTrail;\n }\n}\n"],
5
- "mappings": ";;;;;AAkBA,SAAS,MAAM,YAAY,eAAe;AAC1C,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAoB3B,IAAM,kBAAkB;AACxB,IAAM,eAAe;AACrB,IAAM,gBAAgB;AACtB,IAAM,aAAa;AAWnB,IAAM,qBAAqB;AAE3B,IAAM,oBAAoB;AAEnB,IAAM,oBAAN,cAAgC,WAAW;AAAA,EAA3C;AAAA;AAQL,oBAA2B,CAAC;AAC5B,wBAAe;AACf,oBAAW;AAOX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA+B;AAY/B,SAAQ,YAAY,MAAY;AAI9B,WAAK,WAAW;AAChB,WAAK,cAAc,IAAI,YAAY,gBAAgB,EAAE,SAAS,MAAM,UAAU,KAAK,CAAC,CAAC;AAAA,IACvF;AAQA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAQ,mBAAmB,CAAC,OAA4B;AACtD,UAAI,GAAG,WAAW,UAAW;AAC7B,WAAK;AAAA,QACH,IAAI,YAAuD,2BAA2B;AAAA,UACpF,QAAQ,EAAE,YAAY,GAAG,IAAI,UAAU,KAAK;AAAA,UAC5C,SAAS;AAAA,UACT,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,IACF;AAEA,SAAQ,cAAc,MAAY;AAIhC,WAAK,WAAW;AAChB,WAAK,cAAc,IAAI,YAAY,kBAAkB,EAAE,SAAS,MAAM,UAAU,KAAK,CAAC,CAAC;AAAA,IACzF;AAAA;AAAA,EAzCS,mBAAgC;AAEvC,WAAO;AAAA,EACT;AAAA,EAES,oBAA0B;AACjC,UAAM,kBAAkB;AACxB,6BAAyB;AAAA,EAC3B;AAAA,EAmCS,QAAQ,SAAqC;AAOpD,QAAI,QAAQ,IAAI,UAAU,KAAK,QAAQ,IAAI,UAAU,GAAG;AACtD,4BAAsB,MAAM;AAC1B,cAAM,YAAY,KAAK,cAA2B,0BAA0B;AAC5E,YAAI,CAAC,UAAW;AAChB,cAAM,qBACJ,UAAU,eAAe,UAAU,YAAY,UAAU;AAM3D,YAAI,qBAAqB,OAAO,QAAQ,IAAI,UAAU,GAAG;AACvD,oBAAU,YAAY,UAAU;AAAA,QAClC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAES,SAAS;AAChB,QAAI,KAAK,SAAS,WAAW,KAAK,CAAC,KAAK,SAAU,QAAO;AACzD,QAAI,KAAK,SAAS,WAAW,KAAK,KAAK,UAAU;AAG/C,aAAO,yCAAyC,SAAS;AAAA,QACvD,SAAS;AAAA,QACT,eAAe;AAAA,QACf,KAAK;AAAA,QACL,gBAAgB;AAAA,QAChB,OAAO;AAAA,QACP,SAAS;AAAA,QACT,eAAe;AAAA,MACjB,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKU,SAAS,WAAW,aAAa,GAAG,EAAE,aAAa,OAAO,SAAS,MAAM,CAAC,CAAC,CAAC;AAAA,WACnF,WAAW,eAAe,wBAAwB,KAAK,QAAQ,CAAC,CAAC,CAAC;AAAA;AAAA,IAEzE;AAKA,UAAM,UAAU,KAAK,WAAW,KAAK,WAAW,KAAK,SAAS,MAAM,CAAC,KAAK,YAAY;AACtF,UAAM,SAAS,KAAK,SAAS,SAAS,QAAQ;AAE9C,UAAM,aAAqC;AAAA,MACzC,SAAS;AAAA,MACT,eAAe;AAAA,MACf,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQL,OAAO;AAAA;AAAA,MAEP,SAAS;AAAA,MACT,eAAe;AAAA,IACjB;AAaA,UAAM,aAAqC,KAAK,WAC5C;AAAA,MACE,WAAW;AAAA,MACX,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,IAClB,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASE,WAAW;AAAA,MACX,UAAU;AAAA,IACZ;AAEJ,UAAM,kBAAkB,EAAE,GAAG,YAAY,GAAG,WAAW;AAOvD,UAAM,cAAsC;AAAA,MAC1C,UAAU;AAAA,MACV,OAAO;AAAA,IACT;AAEA,WAAO;AAAA,gDACqC,SAAS,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAS7D,KAAK,WACD,UACA,SAAS,IACP;AAAA;AAAA;AAAA,yBAGW,KAAK,SAAS;AAAA,wBACf,SAAS,WAAW,CAAC,CAAC;AAAA,mBAC3B,MAAM,4BACX,OACR;AAAA,0CACoC,SAAS,eAAe,CAAC;AAAA,UACzD,QAAQ,IAAI,CAAC,GAAG,MAAM;AAMtB,YAAM,MAAM,KAAK,WAAW,IAAI,QAAQ,SAAS,IAAI;AACrD,YAAM,cAAc,EAAE,WAAW;AACjC,YAAM,UAAU,EAAE,WAAW,WAAW,EAAE,SAAS;AACnD,YAAM,YAAY,EAAE,aAAa,CAAC;AAMlC,YAAM,eACJ,EAAE,SAAS,cACP,OAAO,WAAW,eAAe,wBAAwB,EAAE,IAAI,CAAC,CAAC,CAAC,KAClE,OAAO,EAAE,IAAI;AACnB,aAAO;AAAA;AAAA,0BAES,EAAE,IAAI;AAAA,4BACJ,EAAE,UAAU,UAAU;AAAA,sBAC5B,SAAS,WAAW,EAAE,MAAM,KAAK,EAAE,aAAa,QAAQ,CAAC,CAAC,CAAC;AAAA,eAClE,YAAY,GACb,cACI;AAAA;AAAA;AAAA,4BAGU,SAAS,YAAY,CAAC,CAAC;AAAA,8BAEjC,OACN,GACE,UAAU,SAAS,IACf,uCAAuC,SAAS,oBAAoB,CAAC,CAAC;AAAA,sBAClE,UAAU;AAAA,QACV,CAAC,OAAO;AAAA;AAAA;AAAA,uCAGS,GAAG,EAAE;AAAA,2CACD,GAAG,MAAM;AAAA,iCACnB,MAAM,KAAK,iBAAiB,EAAE,CAAC;AAAA,oCAC5B,GAAG,WAAW,SAAS;AAAA,gCAC3B,SAAS,mBAAmB,GAAG,MAAM,CAAC,CAAC;AAAA,iCACtC,GAAG,IAAI,MAAM,GAAG,MAAM;AAAA,yBAC9B,aAAa,GAAG,MAAM,CAAC,IAAI,GAAG,IAAI;AAAA,MACvC,CAAC;AAAA,4BAEH,OACN;AAAA,IACJ,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQF,KAAK,WACD,UACA;AAAA;AAAA;AAAA,sBAGU,SAAS,eAAe,CAAC,CAAC;AAAA,oBAE1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOE,KAAK,WACD;AAAA;AAAA;AAAA,uBAGW,KAAK,WAAW;AAAA,sBACjB,SAAS,qBAAqB,CAAC,CAAC;AAAA,oCAE1C,OACN;AAAA;AAAA,EAEJ;AACF;AA3Ra,kBACK,aAAa;AAAA,EAC3B,UAAU,EAAE,WAAW,MAAM;AAAA,EAC7B,cAAc,EAAE,MAAM,OAAO;AAAA,EAC7B,UAAU,EAAE,MAAM,QAAQ;AAAA,EAC1B,UAAU,EAAE,MAAM,OAAO;AAC3B;AAmSF,SAAS,iBAAyC;AAChD,QAAM,OAAO;AACb,SAAO;AAAA,IACL,UAAU;AAAA,IACV,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ,GAAG,kBAAkB;AAAA,IAC7B,eAAe;AAAA,IACf,gBAAgB,QAAQ,iBAAiB;AAAA,IACzC,sBAAsB,QAAQ,iBAAiB;AAAA,IAC/C,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,QAAQ;AAAA,EACV;AACF;AAEA,SAAS,WACP,MACA,KACA,QAAoD,EAAE,aAAa,OAAO,SAAS,MAAM,GACjE;AACxB,QAAM,UAAU,KAAK,IAAI,eAAe,IAAI,MAAM,YAAY;AAC9D,QAAM,MAAM,MAAM;AAQlB,QAAM,SAAS,MAAM,UACjB,6EACA,MAAM,cACJ,8EACA,SAAS,SACP,8EACA;AACR,QAAM,aAAa,MAAM,UACrB,uEACA,SAAS,SACP,wEACA;AAEN,QAAM,OAA+B;AAAA,IACnC,WAAW,SAAS,SAAS,aAAa;AAAA,IAC1C,UAAU;AAAA,IACV,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,sBAAsB;AAAA,IACtB,OAAO,MAAM,UACT,mEACA;AAAA,IACJ,YACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMF,SAAS,OAAO,YAAY,OAAO,CAAC;AAAA,IACpC,WAAW,cAAc,GAAG;AAAA,IAC5B,YAAY;AAAA,IACZ,WAAW;AAAA,EACb;AACA,SAAO;AACT;AAEA,SAAS,sBAA8C;AACrD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,UAAU;AAAA,IACV,KAAK;AAAA,IACL,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,WAAW;AAAA,EACb;AACF;AAEA,SAAS,mBAAmB,QAAyD;AACnF,QAAM,YAAY,WAAW;AAC7B,QAAM,UAAU,WAAW;AAC3B,QAAM,SAAS,WAAW;AAC1B,SAAO;AAAA,IACL,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,SAAS;AAAA,IACT,QAAQ,UACJ,6EACA,YACE,8EACA;AAAA,IACN,YAAY,UACR,uEACA,YACE,wEACA,SACE,oEACA;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ,YAAY,YAAY;AAAA,IAChC,SAAS,SAAS,QAAQ;AAAA,EAC5B;AACF;AAEA,SAAS,aAAa,QAAyC;AAC7D,UAAQ,QAAQ;AAAA,IACd,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,cAAsC;AAC7C,SAAO;AAAA,IACL,SAAS;AAAA,IACT,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,WAAW;AAAA,EACb;AACF;AAEA,SAAS,uBAA+C;AACtD,SAAO;AAAA,IACL,WAAW;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,sBAAsB;AAAA,IACtB,YAAY;AAAA,EACd;AACF;AAEA,SAAS,aAAqC;AAC5C,SAAO;AAAA,IACL,WAAW;AAAA,IACX,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AACF;AAQA,SAAS,2BAAiC;AACxC,MAAI,OAAO,aAAa,YAAa;AACrC,MAAI,SAAS,KAAK,cAAc,0CAA0C,EAAG;AAC7E,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,aAAa,qCAAqC,MAAM;AAI9D,QAAM,cAAc;AAAA,IAClB;AAAA;AAAA;AAAA,IAGA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,GAAG;AACV,WAAS,KAAK,YAAY,KAAK;AACjC;AAoBA,SAAS,wBAAwB,MAAsB;AACrD,SAAO,KACJ,QAAQ,aAAa,EAAE,EACvB,QAAQ,WAAW,MAAM,EACzB,KAAK;AACV;AAGA,SAAS,YAAY,GAAmB;AACtC,QAAM,IAAI,EAAE,QAAQ,CAAC;AACrB,SAAO,EAAE,QAAQ,UAAU,EAAE,KAAK;AACpC;AAEA,IAAI,CAAC,eAAe,IAAI,qBAAqB,GAAG;AAC9C,iBAAe,OAAO,uBAAuB,iBAAiB;AAChE;",
6
- "names": []
7
- }