@syntrologie/adapt-chatbot 2.56.0 → 2.57.1

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
  {
2
2
  "version": 3,
3
- "sources": ["../src/generated/widget-props-field-index.json", "../src/schema.ts"],
4
- "sourcesContent": ["{\n \"$comment\": \"DO NOT EDIT BY HAND \u2014 generated from packages/runtime-sdk/schema/canvas-config.schema.json and platform/common/syntrologie_common/sdk/widget-props-always-allowed-fields.json. Re-run: node packages/runtime-sdk/scripts/generate-widget-props-field-index.mjs. Consumed by packages/adaptives/adaptive-chatbot/src/schema.ts (uiTemplates dangling-field-reference rule). Mirrors platform/common/syntrologie_common/sdk/schema.py:widget_schema_field_exists \u2014 same resolution semantics, same widget allowlist (_DANGLING_CHECK_WIDGETS in adaptive_tools_gen.py) \u2014 keep both in sync. The $alwaysAllowedFields key is the merged tileChromeFields/handleField/handlesField from the always-allowed-fields JSON above \u2014 fields that are NEVER real widget-props properties for ANY widget.\",\n \"$alwaysAllowedFields\": [\n \"icon\",\n \"product_handle\",\n \"product_handles\",\n \"subtitle\",\n \"title\"\n ],\n \"adaptive-product:card\": [\n \"density\",\n \"product\",\n \"product.attributes\",\n \"product.availability\",\n \"product.badge\",\n \"product.badge.label\",\n \"product.badge.tone\",\n \"product.brand\",\n \"product.ctas\",\n \"product.framing\",\n \"product.id\",\n \"product.identifiers\",\n \"product.identifiers.gtin\",\n \"product.identifiers.mpn\",\n \"product.identifiers.shopifyHandle\",\n \"product.identifiers.sku\",\n \"product.identifiers.wooSlug\",\n \"product.image\",\n \"product.image.alt\",\n \"product.image.bind\",\n \"product.image.src\",\n \"product.labels\",\n \"product.name\",\n \"product.price\",\n \"product.price.amount\",\n \"product.price.bind\",\n \"product.price.cadence\",\n \"product.rating\",\n \"product.rating.count\",\n \"product.rating.max\",\n \"product.rating.value\",\n \"product.salePrice\",\n \"product.salePrice.amount\",\n \"product.salePrice.bind\",\n \"product.salePrice.cadence\",\n \"product.salePrice.validUntil\",\n \"product.specs\",\n \"product.tagline\",\n \"product.variants\",\n \"product.variants.attributes\",\n \"product.variants.variants\",\n \"visibleFacts\"\n ],\n \"adaptive-product:comparison\": [\n \"heading\",\n \"products\",\n \"rows\"\n ]\n}\n", "/**\n * Adaptive Chatbot - Config Schema\n *\n * Zod schema for validating chatbot widget configuration.\n */\n\nimport { z } from 'zod';\n\nimport { SuggestionChipSchema } from './AdaptiveChipsStrip';\nimport widgetPropsFieldIndex from './generated/widget-props-field-index.json';\n\n// Key `generate-widget-props-field-index.mjs` writes the merged tile-chrome /\n// hydration-handle exemption list under \u2014 see the `uiTemplates` superRefine\n// below (dangling widget-props field references) for the read side.\nconst ALWAYS_ALLOWED_FIELDS_KEY = '$alwaysAllowedFields';\n\n// The widget composes `${backendUrl}/api/adaptive/stream` (AG-UI chat turns)\n// and `${backendUrl}/api/adaptive/session` (server-push SSE: nudges, typing,\n// snapshots). backendUrl must be either:\n// - \"\" (empty string) \u2014 same-origin opt-in; resolves to /api/adaptive/*\n// - an absolute http:// or https:// URL \u2014 the chat backend's origin\n// A relative path like \"/api/chat\" produces \"/api/chat/api/adaptive/session\"\n// which 404s on the host SPA and surfaces as a generic SSE error.\n//\n// Split into a `.url()` branch + literal \"\" branch so zod-to-json-schema\n// produces an `anyOf` that AJV (used by `make sdk-validate`) enforces.\n// Refinements alone do not survive the JSON-schema translation.\nconst backendUrlSchema = z.union(\n [\n z\n .string()\n .url('backendUrl must be an absolute URL')\n .regex(/^https?:\\/\\//, 'backendUrl must use http or https'),\n z.literal(''),\n ],\n {\n errorMap: () => ({\n message:\n 'backendUrl must be either \"\" (same-origin) or an absolute http(s):// URL \u2014 relative paths are not allowed',\n }),\n }\n);\n\n// Per-customer fallback card. Rendered in place of the chat input when the\n// chat backend is unreachable for any reason (most commonly: Cloudflare\n// Turnstile bot-check failure for an end-user on a hardened browser / locked\n// network). The customer fills these in; we never show Cloudflare branding\n// to the end-user.\n//\n// `ctaHref` follows the same absolute-URL guard as `backendUrl` so a typo\n// like \"/support\" can't 404 on the host SPA \u2014 must be empty (same-origin)\n// or an absolute http(s):// URL pointing to the customer's existing support\n// path.\nconst fallbackHrefSchema = z.union(\n [\n z\n .string()\n .url('fallback.ctaHref must be an absolute URL')\n .regex(/^https?:\\/\\//, 'fallback.ctaHref must use http or https'),\n z.literal(''),\n ],\n {\n errorMap: () => ({\n message: 'fallback.ctaHref must be either \"\" (same-origin) or an absolute http(s):// URL',\n }),\n }\n);\n\n// `.strict()` so unknown keys are rejected at validation time (matches the\n// emitted JSON Schema's `additionalProperties: false`). Future fields land\n// here intentionally rather than silently slipping through and behaving\n// like feature stubs.\nconst fallbackSchema = z\n .object({\n /** Header shown on the fallback card. Defaults to a generic \"unavailable\" copy. */\n title: z.string().optional(),\n /** Body copy explaining the situation in the customer's voice. */\n message: z.string().optional(),\n /** Label for the call-to-action link. If `ctaHref` is set this should be too. */\n ctaLabel: z.string().optional(),\n /** Target of the CTA link \u2014 typically the customer's existing support path. */\n ctaHref: fallbackHrefSchema.optional(),\n })\n .strict();\n\nexport const configSchema = z.object({\n /** Backend URL for the chat API endpoint. \"\" for same-origin, or an absolute http(s):// URL. */\n backendUrl: backendUrlSchema,\n /** MLflow run ID for experiment tracking */\n mlflowRunId: z.string().optional(),\n /** Greeting message shown when the chat opens */\n greeting: z.string().default('Hi! How can I help?'),\n /** Maximum number of history messages sent to the backend */\n maxHistory: z.number().int().min(1).max(100).default(20),\n /** Quick-reply suggestions shown in the empty state */\n suggestions: z.array(z.string()).optional(),\n voiceInput: z\n .boolean()\n .optional()\n .describe(\n 'Show a mic button that dictates into the input via the browser Web Speech API where supported (vendor-cloud recognition; degrades to hidden when unsupported). Absent/false = no button.'\n ),\n /**\n * Per-customer fallback card rendered when chat can't open (Turnstile\n * failure, network error, etc.). The end-user is redirected to the\n * customer's normal support path \u2014 no Cloudflare-branded challenge is\n * ever shown.\n */\n fallback: fallbackSchema.optional(),\n});\n\nexport type ChatbotConfigSchema = z.infer<typeof configSchema>;\n\nexport function validateChatbotConfig(data: unknown) {\n return configSchema.safeParse(data);\n}\n\n// ============================================================================\n// Chat Bar Props (adaptive-chatbot:chat-bar widget)\n// ============================================================================\n\n/**\n * Props schema for the `adaptive-chatbot:chat-bar` widget. This is the\n * headless chat-input shell \u2014 no transport, no message persistence.\n * Customers wire `chat-message-sent` / `chat-interrupt` / `canvas-close`\n * to their own pipeline.\n *\n * All fields are optional: the bar renders an empty trail + send-only\n * placeholder if nothing is passed. Authors who want a stateful chat\n * use `adaptive-chatbot:assistant` instead.\n */\nexport const chatBarPropsSchema = z\n .object({\n placeholder: z\n .string()\n .optional()\n .describe('Input placeholder copy. Defaults to \"Ask, find, or navigate\u2026\".'),\n assistantName: z\n .string()\n .optional()\n .describe(\n 'Name shown in the (forceExpanded) chat header. Defaults to \"Assistant\". Per-workspace override, e.g. \"Vela Concierge\".'\n ),\n personas: z\n .array(\n z.object({\n id: z.string().min(1),\n name: z.string().min(1).max(24),\n role_title: z.string().max(48).optional(),\n pronouns: z.string().max(16).optional(),\n avatar: z\n .object({\n sm: z.string().url().optional(),\n md: z.string().url().optional(),\n lg: z.string().url().optional(),\n })\n .optional(),\n intro_line: z.string().max(160).optional(),\n })\n )\n .optional()\n .describe(\n 'Persona roster (FEAT-1784917430): human-presenting concierge identities. One is 50:50 sticky-assigned per visitor; its name/avatar render in the header, trail, and typing line, and the name enters the LLM voice. The header always carries an \"AI concierge\" disclosure.'\n ),\n // `messages` and `inFlight` are runtime state \u2014 included here so\n // authors who pre-seed a conversation (e.g. a demo or guided\n // walkthrough) can declare it. Most production deployments will\n // leave these blank and update them via the mountable's API.\n messages: z\n .array(\n z.object({\n id: z.union([z.number(), z.string()]),\n role: z.enum(['user', 'assistant']),\n text: z.string(),\n })\n )\n .optional(),\n inFlight: z.boolean().optional(),\n forceExpanded: z\n .boolean()\n .optional()\n .describe(\n 'Render the trail + input expanded immediately on mount instead of waiting for the first message. Used on canvases where the chat IS the surface (velvet, dedicated drawer).'\n ),\n backendUrl: z\n .string()\n .optional()\n .describe(\n 'Backend chat endpoint. Empty string + non-undefined enables relative-URL fetches (the host page proxies /api/* to the backend).'\n ),\n greeting: z.string().optional().describe('Greeting line shown above the input on first mount.'),\n introSuggestion: z\n .object({\n label: z.string().min(1),\n prompt: z.string().min(1),\n })\n .optional()\n .describe(\n 'Pre-conversation tap-target shown alongside the greeting when no messages exist. `label` is the button copy the visitor sees; `prompt` is the text submitted on tap, exactly as if they had typed it.'\n ),\n fallback: z\n .object({\n title: z.string().optional(),\n message: z.string().optional(),\n ctaLabel: z.string().optional(),\n })\n .passthrough()\n .optional()\n .describe('Per-customer \"chat unavailable\" card shown when the transport gives up.'),\n elementsEnabled: z\n .boolean()\n .optional()\n .describe(\n 'Opt the chat into the LLM-authored UI element pipeline. When true AND uiTemplates is set, the backend exposes mount_element / patch_element / unmount_element tools scoped to the declared templates.'\n ),\n uiTemplates: z\n .record(z.unknown())\n .superRefine((block, ctx) => {\n // ---------------------------------------------------------------------------\n // Type-homogeneity gate \u2014 mirrors Python UiTemplatesBlock._types_are_homogeneous\n //\n // All templates of the same type must share an authorable field shape.\n // Per-type mount tools assume that every template of a given widget/kind\n // exposes the same set of field names, the same required flags, the same\n // admin_pinned_fields, and the same EnumField value sets. A divergent\n // template would produce a value that is schema-valid but server-rejected.\n //\n // Groups: tiles by `widget`; items+actions by `kind`.\n // 4-dimension signature per group member:\n // 1. frozenset of field keys\n // 2. frozenset of field keys where required === true\n // 3. frozenset of admin_pinned_fields\n // 4. sorted enum values per enum field (order-independent comparison)\n //\n // The data is untyped (z.unknown()), so we read defensively: treat missing\n // arrays as [], missing fields as {}.\n // ---------------------------------------------------------------------------\n\n type TemplateRaw = {\n id?: unknown;\n widget?: unknown;\n kind?: unknown;\n fields?: unknown;\n admin_pinned_fields?: unknown;\n default_slot?: unknown;\n default_starred?: unknown;\n allowed_slots?: unknown;\n default_parent?: unknown;\n allowed_parents?: unknown;\n placement_type?: unknown;\n allowed_targets?: unknown;\n };\n\n function asStringArray(v: unknown): string[] {\n if (!Array.isArray(v)) return [];\n return v.filter((x): x is string => typeof x === 'string');\n }\n\n function asFieldsRecord(v: unknown): Record<string, unknown> {\n if (v !== null && typeof v === 'object' && !Array.isArray(v)) {\n return v as Record<string, unknown>;\n }\n return {};\n }\n\n function templateSignature(\n t: TemplateRaw\n ): readonly [Set<string>, Set<string>, Set<string>, Array<[string, string[]]>] {\n const fields = asFieldsRecord(t.fields);\n const fieldKeys = new Set(Object.keys(fields));\n\n const requiredKeys = new Set<string>();\n for (const [k, spec] of Object.entries(fields)) {\n const s = spec as Record<string, unknown> | null | undefined;\n if (s && typeof s === 'object' && s.required === true) {\n requiredKeys.add(k);\n }\n }\n\n const pinned = new Set(asStringArray(t.admin_pinned_fields));\n\n const enumValues: Array<[string, string[]]> = [];\n for (const [k, spec] of Object.entries(fields)) {\n const s = spec as Record<string, unknown> | null | undefined;\n if (s && typeof s === 'object' && s.type === 'enum') {\n const vals = asStringArray(s.values).slice().sort();\n enumValues.push([k, vals]);\n }\n }\n enumValues.sort(([a], [b]) => a.localeCompare(b));\n\n return [fieldKeys, requiredKeys, pinned, enumValues] as const;\n }\n\n function signaturesEqual(\n a: ReturnType<typeof templateSignature>,\n b: ReturnType<typeof templateSignature>\n ): boolean {\n // 1. field keys\n if (a[0].size !== b[0].size) return false;\n for (const k of a[0]) if (!b[0].has(k)) return false;\n\n // 2. required keys\n if (a[1].size !== b[1].size) return false;\n for (const k of a[1]) if (!b[1].has(k)) return false;\n\n // 3. pinned keys\n if (a[2].size !== b[2].size) return false;\n for (const k of a[2]) if (!b[2].has(k)) return false;\n\n // 4. enum values (sorted arrays of [field, sorted-values] pairs)\n if (a[3].length !== b[3].length) return false;\n for (let i = 0; i < a[3].length; i++) {\n const [ak, av] = a[3][i];\n const [bk, bv] = b[3][i];\n if (ak !== bk) return false;\n if (av.length !== bv.length) return false;\n for (let j = 0; j < av.length; j++) {\n if (av[j] !== bv[j]) return false;\n }\n }\n\n return true;\n }\n\n const groups = new Map<string, TemplateRaw[]>();\n\n const tiles = Array.isArray(block.tiles) ? (block.tiles as TemplateRaw[]) : [];\n for (const t of tiles) {\n if (typeof t.widget === 'string') {\n const key = `widget:${t.widget}`;\n const group = groups.get(key) ?? [];\n group.push(t);\n groups.set(key, group);\n }\n }\n\n const items = Array.isArray(block.items) ? (block.items as TemplateRaw[]) : [];\n const actions = Array.isArray(block.actions) ? (block.actions as TemplateRaw[]) : [];\n for (const t of [...items, ...actions]) {\n if (typeof t.kind === 'string') {\n const key = `kind:${t.kind}`;\n const group = groups.get(key) ?? [];\n group.push(t);\n groups.set(key, group);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Per-template cross-field gates \u2014 mirror ALL five Python model validators\n // (TileTemplate/ItemTemplate/ActionTemplate in\n // syntrologie_common/sdk/templates.py). Each spans TWO fields at once, so\n // JSON Schema cannot express it and zod refinements do not survive\n // zod-to-json-schema \u2014 this refinement is the authoring-time home.\n //\n // The runtime rejects a bad block ALL-OR-NOTHING and only logs it, so every\n // mount_* tool silently vanishes for the turn (BUG-1786640934: a contact-card\n // pinned `heading`/`channels`, neither a declared field, and the whole\n // workspace lost its elements capability).\n // ---------------------------------------------------------------------------\n\n // ---------------------------------------------------------------------------\n // 5. Dangling widget-props field references.\n //\n // `widgetPropsFieldIndex` is generated (never hand-typed) by\n // packages/runtime-sdk/scripts/generate-widget-props-field-index.mjs\n // from the SAME canvas-config.schema.json that Python's\n // `syntrologie_common.sdk.schema.widget_schema_field_exists` reads \u2014\n // one data source, two per-language walkers (see that script's\n // module docstring for why the walking code itself can't be\n // shared across a browser-bundled TS file and a server-side Python\n // module). Scoped to the SAME small widget allowlist as the\n // runtime's own `_DANGLING_CHECK_WIDGETS`\n // (adaptive_tools_gen.py) \u2014 a widget not in the generated index is\n // silently exempt, matching the runtime's own scoping (several\n // widgets don't map fields 1:1 onto their props root; see that\n // script's docstring).\n //\n // This is the authoring-time half of the SAME condition the\n // runtime already detects and fail-open-drops (adaptive_tools_gen.\n // dangling_field_dropped) \u2014 that log-only drop stays, unchanged,\n // for configs published before this rule existed; this rule stops\n // a NEW dangling reference from being authored in the first place.\n // The generated file also carries a `$comment` header string and an\n // `$alwaysAllowedFields` string array (see below) alongside the\n // per-widget path arrays \u2014 cast through `unknown` since `$comment`\n // intentionally breaks a direct `Record<string, string[]>` shape;\n // `checkDanglingFieldReferences` only ever reads a widget key that\n // came from `t.widget` (a real widget const), so `$comment` is\n // never looked up as a widget.\n const widgetFieldIndex = widgetPropsFieldIndex as unknown as Record<string, string[]>;\n\n // Fields that are structurally part of a TileTemplate's authoring\n // surface but are NEVER real widget-props properties, so this check\n // must not flag them as dangling: tile chrome (title/icon/subtitle,\n // rendered by the tile shell) and search-hydration handles\n // (product_handle/product_handles, popped by the mount executor\n // BEFORE the widget ever sees its props). Read from the SAME\n // generated `widgetPropsFieldIndex` above \u2014 never hand-retyped here\n // \u2014 so this list can only ever match what `syntrologie_common.sdk.\n // schema.TILE_CHROME_FIELDS`/`HANDLE_FIELD`/`HANDLES_FIELD` load\n // from `platform/common/syntrologie_common/sdk/widget-props-always-\n // allowed-fields.json`, the single source both languages derive\n // from (see `generate-widget-props-field-index.mjs`'s module\n // docstring). A prior version of this rule hand-retyped this exact\n // list and silently drifted from Python (BUG-1786833318) \u2014 flagging\n // `title`/`icon`/`product_handle`(s) as dangling on every real\n // production config (main-peptides-demo-publish.json,\n // iglowmedspa-demo-publish.json, nei-native-demo-publish.json all\n // author them on adaptive-product:card/:comparison templates).\n // BUG-1786834379 moved the list into the generated artifact so\n // that class of drift is structurally impossible: the freshness\n // check (`make app-check-widget-props-field-index-fresh`) fails if\n // this array is ever stale relative to its JSON source.\n const alwaysAllowedRaw = widgetFieldIndex[ALWAYS_ALLOWED_FIELDS_KEY];\n if (!Array.isArray(alwaysAllowedRaw)) {\n throw new Error(\n `widget-props-field-index.json is missing \"${ALWAYS_ALLOWED_FIELDS_KEY}\" \u2014 regenerate via ` +\n 'node packages/runtime-sdk/scripts/generate-widget-props-field-index.mjs'\n );\n }\n const NEVER_DANGLING_FIELDS = new Set(alwaysAllowedRaw);\n\n function checkDanglingFieldReferences(t: TemplateRaw, where: string): void {\n if (typeof t.widget !== 'string') return; // items/actions carry `kind`, not `widget` \u2014 not in scope\n const validPaths = widgetFieldIndex[t.widget];\n if (!validPaths) return; // widget outside the checked allowlist \u2014 genuinely unchecked, not a false negative\n const validSet = new Set(validPaths);\n const fields = asFieldsRecord(t.fields);\n for (const key of Object.keys(fields)) {\n if (validSet.has(key) || NEVER_DANGLING_FIELDS.has(key)) continue;\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: field ${JSON.stringify(key)} does not exist in the ${JSON.stringify(t.widget)} ` +\n `widget's props schema \u2014 it was removed, renamed, or never existed. Remove it from ` +\n `\\`fields\\`, or point it at the widget's current path.`,\n });\n }\n }\n\n /** Dunder / prototype-pollution names \u2014 mirrors RESERVED_FIELD_SEGMENTS. */\n const RESERVED_SEGMENTS = new Set([\n '__class__',\n '__init__',\n '__proto__',\n '__dict__',\n '__getattribute__',\n '__setattr__',\n 'constructor',\n 'prototype',\n ]);\n\n function checkTemplate(t: TemplateRaw, where: string): void {\n const fields = asFieldsRecord(t.fields);\n const fieldKeys = Object.keys(fields);\n\n // 1. admin_pinned_fields must be a subset of the declared fields.\n // Props the LLM cannot author are already un-authorable by NOT being\n // fields; pinning a non-field is a no-op the runtime treats as fatal.\n const unknownPins = asStringArray(t.admin_pinned_fields).filter(\n (name) => !(name in fields)\n );\n if (unknownPins.length > 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: admin_pinned_fields references unknown fields: ` +\n `${JSON.stringify(unknownPins)} \u2014 every pinned name must be a key of ` +\n `\\`fields\\` (declared: ${JSON.stringify(fieldKeys)})`,\n });\n }\n\n // 2. default_slot must be one of allowed_slots (tiles only).\n if (typeof t.default_slot === 'string') {\n const slots = Array.isArray(t.allowed_slots)\n ? (t.allowed_slots as Array<Record<string, unknown>>)\n .map((s) => (s && typeof s === 'object' ? s.slot : undefined))\n .filter((s): s is string => typeof s === 'string')\n : [];\n if (!slots.includes(t.default_slot)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: default_slot ${JSON.stringify(t.default_slot)} is not in ` +\n `allowed_slots ${JSON.stringify(slots)}`,\n });\n }\n }\n\n // 2b. ItemTemplate: default_parent must be one of allowed_parents[].tile_id.\n // Same failure class as default_slot, for items (adversarial-review find:\n // the first cut mirrored only the tile rule and left this one unchecked).\n if (typeof t.default_parent === 'string') {\n const parents = Array.isArray(t.allowed_parents)\n ? (t.allowed_parents as Array<Record<string, unknown>>)\n .map((p) => (p && typeof p === 'object' ? p.tile_id : undefined))\n .filter((p): p is string => typeof p === 'string')\n : [];\n if (!parents.includes(t.default_parent)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: default_parent ${JSON.stringify(t.default_parent)} is not in ` +\n `allowed_parents ${JSON.stringify(parents)}`,\n });\n }\n }\n\n // 2c. ActionTemplate: placement_type and allowed_targets must agree \u2014\n // 'selector' needs targets to place against, 'none' must carry none.\n if (typeof t.placement_type === 'string') {\n const targets = Array.isArray(t.allowed_targets) ? t.allowed_targets : [];\n if (t.placement_type === 'selector' && targets.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${where}: placement_type 'selector' requires a non-empty allowed_targets list`,\n });\n }\n if (t.placement_type === 'none' && targets.length > 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${where}: placement_type 'none' must have empty allowed_targets`,\n });\n }\n }\n\n // 3. Field-key paths: no empty segments, no reserved segments.\n for (const key of fieldKeys) {\n const segments = key.split('.');\n if (segments.some((seg) => seg.length === 0)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: field key ${JSON.stringify(key)} contains empty segment(s) ` +\n `(leading dot, trailing dot, or '..' are not allowed)`,\n });\n continue;\n }\n for (const seg of segments) {\n if (RESERVED_SEGMENTS.has(seg)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: field key ${JSON.stringify(key)} uses reserved segment ` +\n `${JSON.stringify(seg)} \u2014 dunder / prototype-pollution names are blocked`,\n });\n }\n }\n }\n\n // 4. Dot-prefix overlap: declaring both `product` and `product.tagline` is\n // ambiguous \u2014 a patch on the parent would obliterate the child.\n const sortedKeys = [...fieldKeys].sort();\n for (let i = 0; i < sortedKeys.length - 1; i++) {\n const a = sortedKeys[i];\n const b = sortedKeys[i + 1];\n if (b.startsWith(`${a}.`)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: field keys ${JSON.stringify(a)} and ${JSON.stringify(b)} claim the ` +\n `same prefix namespace \u2014 declaring both is ambiguous (a patch on the parent ` +\n `would overwrite the child)`,\n });\n }\n }\n }\n\n for (const t of tiles) {\n checkTemplate(t, `tile ${JSON.stringify(String(t.id))}`);\n checkDanglingFieldReferences(t, `tile ${JSON.stringify(String(t.id))}`);\n }\n for (const t of items) checkTemplate(t, `item ${JSON.stringify(String(t.id))}`);\n for (const t of actions) checkTemplate(t, `action ${JSON.stringify(String(t.id))}`);\n\n for (const [typeKey, members] of groups) {\n if (members.length < 2) continue;\n\n const headSig = templateSignature(members[0]);\n for (let i = 1; i < members.length; i++) {\n const other = members[i];\n if (!signaturesEqual(headSig, templateSignature(other))) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `templates of type ${JSON.stringify(typeKey)} must share an authorable field ` +\n `shape, but ${JSON.stringify(String(members[0].id))} and ` +\n `${JSON.stringify(String(other.id))} differ ` +\n `(field names / required / admin_pinned / enum values)`,\n });\n }\n }\n }\n })\n .optional()\n .describe(\n 'Template declarations the LLM can mount via the elements pipeline. Shape mirrors syntrologie_common.sdk.templates.UiTemplatesBlock. Passed verbatim as forwardedProps.uiTemplates to the AG-UI transport.'\n ),\n voiceInput: z\n .boolean()\n .optional()\n .describe(\n 'Show a mic button that dictates into the input via the browser Web Speech API where supported (vendor-cloud recognition; degrades to hidden when unsupported). Absent/false = no button.'\n ),\n supportChat: z\n .object({\n vendor: z.string().min(1),\n suppressNative: z.boolean().optional(),\n widgetId: z\n .string()\n .min(1)\n .optional()\n .describe(\n \"LeadConnector / GoHighLevel only: the chat widget id from the site's embed script (`data-widget-id`). The bridge fetches the location id from the vendor's public widget config \u2014 never hand-type it.\"\n ),\n inbox: z\n .object({\n title: z\n .string()\n .min(1)\n .describe(\n 'Heading of the thread card the SDK opens when the visitor taps the announcement of a team message that arrived with no handoff card on the page.'\n ),\n toastTitle: z\n .string()\n .min(1)\n .describe(\n 'Title of that announcement. Its body is the message itself. Name the team in the workspace voice.'\n ),\n })\n .strict()\n .optional()\n .describe(\n \"Where a team message lands when NO adaptive-chatbot:leave-a-message card is on the page \u2014 the vendor's own proactive triggers, or a reply after navigation tore the card down. The SDK writes it into the same thread the card hydrates from, announces it through the canvas, and on tap mounts the workspace's leave-a-message template opened on the thread. Required whenever suppressNative is true: with the vendor's UI hidden, an un-announced message is a message the visitor never sees.\"\n ),\n })\n .strict()\n .superRefine((supportChat, ctx) => {\n if (supportChat.suppressNative && !supportChat.inbox) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['inbox'],\n message:\n 'supportChat.inbox is required when suppressNative is true: the vendor UI is hidden, so a team message that arrives with no handoff card mounted has nowhere to show without it',\n });\n }\n // Cross-field requirement JSON Schema cannot express on this object:\n // 'leadconnector' is the one vendor whose bridge needs per-site\n // config (every other vendor's SDK self-identifies on the page).\n // Without this, a config naming that vendor and omitting widgetId is\n // schema-VALID and fails only at runtime \u2014 bootSupportChat warns and\n // disables handoff, so the customer discovers it when a visitor\n // tries to reach a human. Caught here at authoring time instead:\n // `make sdk-validate` runs this via validate-config.mjs's per-tile\n // props pass (see runtime-sdk/scripts/lib/zod-authoring-checks.mjs).\n if (supportChat.vendor !== 'leadconnector') return;\n if (supportChat.widgetId) return;\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['widgetId'],\n message:\n 'supportChat.widgetId is required when vendor is \"leadconnector\" (the chat widget id from the site\\'s embed, data-widget-id)',\n });\n })\n .optional()\n .describe(\n 'Names the site\\'s own support-chat product (\"crisp\" or \"leadconnector\") for async human handoff via the adaptive-chatbot:leave-a-message widget. Resolved at chat-bar boot regardless of whether that widget ever mounts, so suppressNative (hide the vendor\\'s native launcher bubble) always applies. Absent vendor SDK fails soft \u2014 a console warning, never a thrown error.'\n ),\n })\n .strict();\n\nexport type ChatBarProps = z.infer<typeof chatBarPropsSchema>;\n\n// ============================================================================\n// Chips Strip Props (adaptive-chatbot:chips-strip widget)\n// ============================================================================\n\n/**\n * Props schema for the `adaptive-chatbot:chips-strip` widget. Chips\n * follow the compositional-action shape (see SuggestionChipSchema in\n * AdaptiveChipsStrip.ts) \u2014 same pattern as faq:question.\n */\nexport const chipsStripPropsSchema = z\n .object({\n chips: z\n .array(SuggestionChipSchema)\n .default([])\n .describe(\n 'Suggestion chips authored as suggestions:chip compositional actions. Each chip declares an id, a title, and a payload that mounts in the drawer when clicked. Conditional visibility via per-chip triggerWhen.'\n ),\n })\n .strict();\n\nexport type ChipsStripProps = z.infer<typeof chipsStripPropsSchema>;\n\n// ============================================================================\n// Tile Widget Props Export\n// ============================================================================\n\n/**\n * Tile widget definitions for unified JSON Schema generation.\n * Maps widget IDs to their props validation schema so the build script\n * can inject if/then constraints on tile.props.\n */\nexport const tileWidgets = [\n {\n widget: 'adaptive-chatbot:assistant',\n defName: 'chatbotAssistantProps',\n propsSchema: configSchema,\n },\n {\n widget: 'adaptive-chatbot:chat-bar',\n defName: 'chatBarProps',\n propsSchema: chatBarPropsSchema,\n },\n {\n widget: 'adaptive-chatbot:chips-strip',\n defName: 'chipsStripProps',\n propsSchema: chipsStripPropsSchema,\n },\n];\n\n// ============================================================================\n// Capabilities Documentation (injected into JSON Schema for LLM prompts)\n// ============================================================================\n\nexport const CAPABILITIES_DOCUMENTATION = {\n packageId: 'adaptive-chatbot',\n plannerSummary:\n 'Conversational assistant, always-visible chat input, and suggestion-chip surfaces. Reach for this when a strategy needs dialog, chat-triggered UI, or prompt chips that reveal richer payloads.',\n description:\n 'Chat and conversational UI adaptive. Provides a full assistant, a headless chat bar, suggestion chips, and chip payload widgets for text answers and navigation links.',\n whenToUse: [\n {\n goal: 'Mount a full AI assistant with backend streaming',\n action:\n 'Add a tile with widget: \"adaptive-chatbot:assistant\" and configure backendUrl, greeting, maxHistory, suggestions, and fallback props.',\n },\n {\n goal: 'Replace a launcher or slot lid with a chat input row',\n action:\n 'Use widget: \"adaptive-chatbot:chat-bar\" from a tile, slots.drawer.lid, or another slot lid when the host owns the transport.',\n },\n {\n goal: 'Offer suggested prompts or choices that reveal payload content',\n action:\n 'Add widget: \"adaptive-chatbot:chips-strip\" with suggestions:chip entries; each chip payload should reference a supported widget such as adaptive-chatbot:text-answer, adaptive-chatbot:nav-link, or another tile widget.',\n },\n {\n goal: 'Escalate to a human when the assistant cannot help',\n action:\n 'Mount widget: \"adaptive-chatbot:leave-a-message\" (with the chat-bar\\'s supportChat.vendor already configured) when the visitor explicitly asks for a human, when you cannot resolve after a genuine attempt, or on clear frustration \u2014 never as a first response.',\n },\n ],\n conventions: [\n {\n name: 'backendUrl is explicit',\n description:\n 'assistant backendUrl must be either \"\" for same-origin /api/adaptive/* routing or an absolute http(s) URL. Relative paths are rejected because they compose to invalid /api/adaptive/session URLs.',\n },\n {\n name: 'Chat bar is headless',\n description:\n 'adaptive-chatbot:chat-bar renders the input and trail but does not own a transport by default. Hosts should listen for chat-message-sent, chat-interrupt, and canvas-close or provide backendUrl explicitly.',\n },\n {\n name: 'Suggestion chips are compositional',\n description:\n 'suggestions:chip entries are config data rendered by adaptive-chatbot:chips-strip. Use triggerWhen for conditional visibility and use payload.widget for the content mounted after a click.',\n },\n {\n name: 'LLM-authored UI is opt-in',\n description:\n 'Set elementsEnabled and uiTemplates only when the backend is allowed to mount, patch, and unmount declared UI templates during chat turns.',\n },\n {\n name: 'Reply-toast copy is opt-in workspace configuration',\n description:\n \"adaptive-chatbot:leave-a-message announces an operator's reply with a toast ONLY when the workspace authors the words in default_widget_props.replyToast ({title, body}). No authored copy means no toast \u2014 absence is the opt-out, and the tile's own mail face remains the announcement. The bundle never supplies stand-in copy: it would be in the wrong voice and, on a non-English storefront, the wrong language.\",\n },\n {\n name: 'Leave-a-message copy is 100% workspace-authored',\n description:\n 'adaptive-chatbot:leave-a-message ships NO hardcoded visitor-facing strings across its five states (compose \u2192 email \u2192 sent \u2192 mail \u2192 flipped). Every label, framing line, prompt, and button comes from default_widget_props.copy ({framing, messageLabel, sendLabel, emailPrompt, emailLabel, emailPlaceholder, emailSubmitLabel, emailSkipLabel, sentBody, mailLabel, replyLabel, replyPlaceholder, replySendLabel}). The compose/email/reply steps use icon-only send controls whose aria-label IS the authored copy \u2014 an icon button with no accessible name is a real defect, so an unauthored sendLabel/emailSubmitLabel/replySendLabel removes the control entirely rather than shipping it mute. Every other field follows the same absence-is-the-opt-out rule as replyToast.',\n },\n {\n name: 'Human handoff is escalation-only',\n description:\n \"adaptive-chatbot:leave-a-message delivers through the site's own support-chat vendor named in the chat-bar's supportChat.vendor prop \u2014 never mount it as a first response; reach for it only after a genuine resolution attempt, an explicit request for a person, or clear frustration.\",\n },\n ],\n // Structured {name, when, props} entries \u2014 the CAPABILITIES_DOCUMENTATION\n // contract every consumer renders (`_render_capabilities_markdown` in\n // tech-core, `format_capabilities_for_prompt` in syntrologie_common).\n // Plain strings here crashed both renderers (BUG-1784941121).\n events: [\n {\n name: 'chat-message-sent',\n when: 'Visitor sends a chat message (typed, or via a suggested-reply chip)',\n props: '{ text }',\n },\n {\n name: 'chat-interrupt',\n when: 'Visitor interrupts the streaming assistant reply',\n props: '{}',\n },\n {\n name: 'canvas-close',\n when: 'Chat surface requests the host canvas to close',\n props: '{}',\n },\n {\n name: 'trail-suggested-reply',\n when: 'Visitor taps a suggest_replies chip in the chat trail',\n props: '{ text }',\n },\n {\n name: 'supportchat:reply_received',\n when: \"A human operator replies through the site's support-chat vendor while the adaptive-chatbot:leave-a-message tile is un-flipped (not yet read) \u2014 published on the runtime EventBus, not as a DOM CustomEvent, so tile notifications rules can badge it\",\n props: '{ text }',\n },\n ],\n};\n\n/**\n * Action step schemas \u2014 registered into the unified canvas-config\n * JSON Schema's action-step union so authors (and the tactician LLM)\n * can emit `suggestions:chip` items inside a chips-strip tile's\n * `props.chips` array with full validation.\n *\n * `.innerType()` strips the `.refine()` wrapper (cross-field\n * refinements don't translate to JSON Schema); runtime Zod still\n * enforces them.\n */\nexport const actionStepSchemas = [\n { defName: 'suggestionChip', schema: SuggestionChipSchema.innerType() },\n];\n"],
5
- "mappings": "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",
3
+ "sources": ["../src/generated/mount-tool-description.json", "../src/generated/widget-props-field-index.json", "../src/schema.ts"],
4
+ "sourcesContent": ["{\n \"maxLength\": 1024,\n \"base\": \"Mount a {type_key} element. Pick template_id; author the fields.\",\n \"templatesPrefix\": \" Templates: \",\n \"templateItem\": \"'{id}' \u2014 {description}\",\n \"templateSeparator\": \"; \",\n \"notePrefix\": \" \",\n \"notes\": {\n \"syntro-product-reco-carousel\": \"The store's recommendation engine picks the products in this carousel; heading and focus steer it, they do not choose which products appear.\",\n \"adaptive-chatbot:leave-a-message\": \"Mounting shows the visitor a draft card; nothing is sent until the visitor taps Send in it, so never say it was sent or is on its way: ask them to review it and tap Send.\"\n }\n}\n", "{\n \"$comment\": \"DO NOT EDIT BY HAND \u2014 generated from packages/runtime-sdk/schema/canvas-config.schema.json and platform/common/syntrologie_common/sdk/widget-props-always-allowed-fields.json. Re-run: node packages/runtime-sdk/scripts/generate-widget-props-field-index.mjs. Consumed by packages/adaptives/adaptive-chatbot/src/schema.ts (uiTemplates dangling-field-reference rule). Mirrors platform/common/syntrologie_common/sdk/schema.py:widget_schema_field_exists \u2014 same resolution semantics, same widget allowlist (_DANGLING_CHECK_WIDGETS in adaptive_tools_gen.py) \u2014 keep both in sync. The $alwaysAllowedFields key is the merged tileChromeFields/handleField/handlesField from the always-allowed-fields JSON above \u2014 fields that are NEVER real widget-props properties for ANY widget.\",\n \"$alwaysAllowedFields\": [\n \"icon\",\n \"product_handle\",\n \"product_handles\",\n \"subtitle\",\n \"title\"\n ],\n \"adaptive-product:card\": [\n \"density\",\n \"product\",\n \"product.attributes\",\n \"product.availability\",\n \"product.badge\",\n \"product.badge.label\",\n \"product.badge.tone\",\n \"product.brand\",\n \"product.ctas\",\n \"product.framing\",\n \"product.id\",\n \"product.identifiers\",\n \"product.identifiers.gtin\",\n \"product.identifiers.mpn\",\n \"product.identifiers.shopifyHandle\",\n \"product.identifiers.sku\",\n \"product.identifiers.wooSlug\",\n \"product.image\",\n \"product.image.alt\",\n \"product.image.bind\",\n \"product.image.src\",\n \"product.labels\",\n \"product.name\",\n \"product.price\",\n \"product.price.amount\",\n \"product.price.bind\",\n \"product.price.cadence\",\n \"product.rating\",\n \"product.rating.count\",\n \"product.rating.max\",\n \"product.rating.value\",\n \"product.salePrice\",\n \"product.salePrice.amount\",\n \"product.salePrice.bind\",\n \"product.salePrice.cadence\",\n \"product.salePrice.validUntil\",\n \"product.specs\",\n \"product.tagline\",\n \"product.variants\",\n \"product.variants.attributes\",\n \"product.variants.variants\",\n \"visibleFacts\"\n ],\n \"adaptive-product:comparison\": [\n \"heading\",\n \"products\",\n \"rows\"\n ]\n}\n", "/**\n * Adaptive Chatbot - Config Schema\n *\n * Zod schema for validating chatbot widget configuration.\n */\n\nimport { z } from 'zod';\n\nimport { SuggestionChipSchema } from './AdaptiveChipsStrip';\nimport mountToolDescriptionSpec from './generated/mount-tool-description.json';\nimport widgetPropsFieldIndex from './generated/widget-props-field-index.json';\n\n// Key `generate-widget-props-field-index.mjs` writes the merged tile-chrome /\n// hydration-handle exemption list under \u2014 see the `uiTemplates` superRefine\n// below (dangling widget-props field references) for the read side.\nconst ALWAYS_ALLOWED_FIELDS_KEY = '$alwaysAllowedFields';\n\n// The widget composes `${backendUrl}/api/adaptive/stream` (AG-UI chat turns)\n// and `${backendUrl}/api/adaptive/session` (server-push SSE: nudges, typing,\n// snapshots). backendUrl must be either:\n// - \"\" (empty string) \u2014 same-origin opt-in; resolves to /api/adaptive/*\n// - an absolute http:// or https:// URL \u2014 the chat backend's origin\n// A relative path like \"/api/chat\" produces \"/api/chat/api/adaptive/session\"\n// which 404s on the host SPA and surfaces as a generic SSE error.\n//\n// Split into a `.url()` branch + literal \"\" branch so zod-to-json-schema\n// produces an `anyOf` that AJV (used by `make sdk-validate`) enforces.\n// Refinements alone do not survive the JSON-schema translation.\nconst backendUrlSchema = z.union(\n [\n z\n .string()\n .url('backendUrl must be an absolute URL')\n .regex(/^https?:\\/\\//, 'backendUrl must use http or https'),\n z.literal(''),\n ],\n {\n errorMap: () => ({\n message:\n 'backendUrl must be either \"\" (same-origin) or an absolute http(s):// URL \u2014 relative paths are not allowed',\n }),\n }\n);\n\n// Per-customer fallback card. Rendered in place of the chat input when the\n// chat backend is unreachable for any reason (most commonly: Cloudflare\n// Turnstile bot-check failure for an end-user on a hardened browser / locked\n// network). The customer fills these in; we never show Cloudflare branding\n// to the end-user.\n//\n// `ctaHref` follows the same absolute-URL guard as `backendUrl` so a typo\n// like \"/support\" can't 404 on the host SPA \u2014 must be empty (same-origin)\n// or an absolute http(s):// URL pointing to the customer's existing support\n// path.\nconst fallbackHrefSchema = z.union(\n [\n z\n .string()\n .url('fallback.ctaHref must be an absolute URL')\n .regex(/^https?:\\/\\//, 'fallback.ctaHref must use http or https'),\n z.literal(''),\n ],\n {\n errorMap: () => ({\n message: 'fallback.ctaHref must be either \"\" (same-origin) or an absolute http(s):// URL',\n }),\n }\n);\n\n// `.strict()` so unknown keys are rejected at validation time (matches the\n// emitted JSON Schema's `additionalProperties: false`). Future fields land\n// here intentionally rather than silently slipping through and behaving\n// like feature stubs.\nconst fallbackSchema = z\n .object({\n /** Header shown on the fallback card. Defaults to a generic \"unavailable\" copy. */\n title: z.string().optional(),\n /** Body copy explaining the situation in the customer's voice. */\n message: z.string().optional(),\n /** Label for the call-to-action link. If `ctaHref` is set this should be too. */\n ctaLabel: z.string().optional(),\n /** Target of the CTA link \u2014 typically the customer's existing support path. */\n ctaHref: fallbackHrefSchema.optional(),\n })\n .strict();\n\nexport const configSchema = z.object({\n /** Backend URL for the chat API endpoint. \"\" for same-origin, or an absolute http(s):// URL. */\n backendUrl: backendUrlSchema,\n /** MLflow run ID for experiment tracking */\n mlflowRunId: z.string().optional(),\n /** Greeting message shown when the chat opens */\n greeting: z.string().default('Hi! How can I help?'),\n /** Maximum number of history messages sent to the backend */\n maxHistory: z.number().int().min(1).max(100).default(20),\n /** Quick-reply suggestions shown in the empty state */\n suggestions: z.array(z.string()).optional(),\n voiceInput: z\n .boolean()\n .optional()\n .describe(\n 'Show a mic button that dictates into the input via the browser Web Speech API where supported (vendor-cloud recognition; degrades to hidden when unsupported). Absent/false = no button.'\n ),\n /**\n * Per-customer fallback card rendered when chat can't open (Turnstile\n * failure, network error, etc.). The end-user is redirected to the\n * customer's normal support path \u2014 no Cloudflare-branded challenge is\n * ever shown.\n */\n fallback: fallbackSchema.optional(),\n});\n\nexport type ChatbotConfigSchema = z.infer<typeof configSchema>;\n\nexport function validateChatbotConfig(data: unknown) {\n return configSchema.safeParse(data);\n}\n\n// ============================================================================\n// Chat Bar Props (adaptive-chatbot:chat-bar widget)\n// ============================================================================\n\n/**\n * Props schema for the `adaptive-chatbot:chat-bar` widget. This is the\n * headless chat-input shell \u2014 no transport, no message persistence.\n * Customers wire `chat-message-sent` / `chat-interrupt` / `canvas-close`\n * to their own pipeline.\n *\n * All fields are optional: the bar renders an empty trail + send-only\n * placeholder if nothing is passed. Authors who want a stateful chat\n * use `adaptive-chatbot:assistant` instead.\n */\nexport const chatBarPropsSchema = z\n .object({\n placeholder: z\n .string()\n .optional()\n .describe('Input placeholder copy. Defaults to \"Ask, find, or navigate\u2026\".'),\n assistantName: z\n .string()\n .optional()\n .describe(\n 'Name shown in the (forceExpanded) chat header. Defaults to \"Assistant\". Per-workspace override, e.g. \"Vela Concierge\".'\n ),\n personas: z\n .array(\n z.object({\n id: z.string().min(1),\n name: z.string().min(1).max(24),\n role_title: z.string().max(48).optional(),\n pronouns: z.string().max(16).optional(),\n avatar: z\n .object({\n sm: z.string().url().optional(),\n md: z.string().url().optional(),\n lg: z.string().url().optional(),\n })\n .optional(),\n intro_line: z.string().max(160).optional(),\n })\n )\n .optional()\n .describe(\n 'Persona roster (FEAT-1784917430): human-presenting concierge identities. One is 50:50 sticky-assigned per visitor; its name/avatar render in the header, trail, and typing line, and the name enters the LLM voice. The header always carries an \"AI concierge\" disclosure.'\n ),\n // `messages` and `inFlight` are runtime state \u2014 included here so\n // authors who pre-seed a conversation (e.g. a demo or guided\n // walkthrough) can declare it. Most production deployments will\n // leave these blank and update them via the mountable's API.\n messages: z\n .array(\n z.object({\n id: z.union([z.number(), z.string()]),\n role: z.enum(['user', 'assistant']),\n text: z.string(),\n })\n )\n .optional(),\n inFlight: z.boolean().optional(),\n forceExpanded: z\n .boolean()\n .optional()\n .describe(\n 'Render the trail + input expanded immediately on mount instead of waiting for the first message. Used on canvases where the chat IS the surface (velvet, dedicated drawer).'\n ),\n backendUrl: z\n .string()\n .optional()\n .describe(\n 'Backend chat endpoint. Empty string + non-undefined enables relative-URL fetches (the host page proxies /api/* to the backend).'\n ),\n greeting: z.string().optional().describe('Greeting line shown above the input on first mount.'),\n introSuggestion: z\n .object({\n label: z.string().min(1),\n prompt: z.string().min(1),\n })\n .optional()\n .describe(\n 'Pre-conversation tap-target shown alongside the greeting when no messages exist. `label` is the button copy the visitor sees; `prompt` is the text submitted on tap, exactly as if they had typed it.'\n ),\n fallback: z\n .object({\n title: z.string().optional(),\n message: z.string().optional(),\n ctaLabel: z.string().optional(),\n })\n .passthrough()\n .optional()\n .describe('Per-customer \"chat unavailable\" card shown when the transport gives up.'),\n elementsEnabled: z\n .boolean()\n .optional()\n .describe(\n 'Opt the chat into the LLM-authored UI element pipeline. When true AND uiTemplates is set, the backend exposes mount_element / patch_element / unmount_element tools scoped to the declared templates.'\n ),\n uiTemplates: z\n .record(z.unknown())\n .superRefine((block, ctx) => {\n // ---------------------------------------------------------------------------\n // Type-homogeneity gate \u2014 mirrors Python UiTemplatesBlock._types_are_homogeneous\n //\n // All templates of the same type must share an authorable field shape.\n // Per-type mount tools assume that every template of a given widget/kind\n // exposes the same set of field names, the same required flags, the same\n // admin_pinned_fields, and the same EnumField value sets. A divergent\n // template would produce a value that is schema-valid but server-rejected.\n //\n // Groups: tiles by `widget`; items+actions by `kind`.\n // 4-dimension signature per group member:\n // 1. frozenset of field keys\n // 2. frozenset of field keys where required === true\n // 3. frozenset of admin_pinned_fields\n // 4. sorted enum values per enum field (order-independent comparison)\n //\n // The data is untyped (z.unknown()), so we read defensively: treat missing\n // arrays as [], missing fields as {}.\n // ---------------------------------------------------------------------------\n\n type TemplateRaw = {\n id?: unknown;\n description?: unknown;\n widget?: unknown;\n kind?: unknown;\n fields?: unknown;\n admin_pinned_fields?: unknown;\n default_slot?: unknown;\n default_starred?: unknown;\n allowed_slots?: unknown;\n default_parent?: unknown;\n allowed_parents?: unknown;\n placement_type?: unknown;\n allowed_targets?: unknown;\n };\n\n function asStringArray(v: unknown): string[] {\n if (!Array.isArray(v)) return [];\n return v.filter((x): x is string => typeof x === 'string');\n }\n\n function asFieldsRecord(v: unknown): Record<string, unknown> {\n if (v !== null && typeof v === 'object' && !Array.isArray(v)) {\n return v as Record<string, unknown>;\n }\n return {};\n }\n\n function templateSignature(\n t: TemplateRaw\n ): readonly [Set<string>, Set<string>, Set<string>, Array<[string, string[]]>] {\n const fields = asFieldsRecord(t.fields);\n const fieldKeys = new Set(Object.keys(fields));\n\n const requiredKeys = new Set<string>();\n for (const [k, spec] of Object.entries(fields)) {\n const s = spec as Record<string, unknown> | null | undefined;\n if (s && typeof s === 'object' && s.required === true) {\n requiredKeys.add(k);\n }\n }\n\n const pinned = new Set(asStringArray(t.admin_pinned_fields));\n\n const enumValues: Array<[string, string[]]> = [];\n for (const [k, spec] of Object.entries(fields)) {\n const s = spec as Record<string, unknown> | null | undefined;\n if (s && typeof s === 'object' && s.type === 'enum') {\n const vals = asStringArray(s.values).slice().sort();\n enumValues.push([k, vals]);\n }\n }\n enumValues.sort(([a], [b]) => a.localeCompare(b));\n\n return [fieldKeys, requiredKeys, pinned, enumValues] as const;\n }\n\n function signaturesEqual(\n a: ReturnType<typeof templateSignature>,\n b: ReturnType<typeof templateSignature>\n ): boolean {\n // 1. field keys\n if (a[0].size !== b[0].size) return false;\n for (const k of a[0]) if (!b[0].has(k)) return false;\n\n // 2. required keys\n if (a[1].size !== b[1].size) return false;\n for (const k of a[1]) if (!b[1].has(k)) return false;\n\n // 3. pinned keys\n if (a[2].size !== b[2].size) return false;\n for (const k of a[2]) if (!b[2].has(k)) return false;\n\n // 4. enum values (sorted arrays of [field, sorted-values] pairs)\n if (a[3].length !== b[3].length) return false;\n for (let i = 0; i < a[3].length; i++) {\n const [ak, av] = a[3][i];\n const [bk, bv] = b[3][i];\n if (ak !== bk) return false;\n if (av.length !== bv.length) return false;\n for (let j = 0; j < av.length; j++) {\n if (av[j] !== bv[j]) return false;\n }\n }\n\n return true;\n }\n\n const groups = new Map<string, TemplateRaw[]>();\n\n const tiles = Array.isArray(block.tiles) ? (block.tiles as TemplateRaw[]) : [];\n for (const t of tiles) {\n if (typeof t.widget === 'string') {\n const key = `widget:${t.widget}`;\n const group = groups.get(key) ?? [];\n group.push(t);\n groups.set(key, group);\n }\n }\n\n const items = Array.isArray(block.items) ? (block.items as TemplateRaw[]) : [];\n const actions = Array.isArray(block.actions) ? (block.actions as TemplateRaw[]) : [];\n for (const t of [...items, ...actions]) {\n if (typeof t.kind === 'string') {\n const key = `kind:${t.kind}`;\n const group = groups.get(key) ?? [];\n group.push(t);\n groups.set(key, group);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Per-template cross-field gates \u2014 mirror ALL five Python model validators\n // (TileTemplate/ItemTemplate/ActionTemplate in\n // syntrologie_common/sdk/templates.py). Each spans TWO fields at once, so\n // JSON Schema cannot express it and zod refinements do not survive\n // zod-to-json-schema \u2014 this refinement is the authoring-time home.\n //\n // The runtime rejects a bad block ALL-OR-NOTHING and only logs it, so every\n // mount_* tool silently vanishes for the turn (BUG-1786640934: a contact-card\n // pinned `heading`/`channels`, neither a declared field, and the whole\n // workspace lost its elements capability).\n // ---------------------------------------------------------------------------\n\n // ---------------------------------------------------------------------------\n // 5. Dangling widget-props field references.\n //\n // `widgetPropsFieldIndex` is generated (never hand-typed) by\n // packages/runtime-sdk/scripts/generate-widget-props-field-index.mjs\n // from the SAME canvas-config.schema.json that Python's\n // `syntrologie_common.sdk.schema.widget_schema_field_exists` reads \u2014\n // one data source, two per-language walkers (see that script's\n // module docstring for why the walking code itself can't be\n // shared across a browser-bundled TS file and a server-side Python\n // module). Scoped to the SAME small widget allowlist as the\n // runtime's own `_DANGLING_CHECK_WIDGETS`\n // (adaptive_tools_gen.py) \u2014 a widget not in the generated index is\n // silently exempt, matching the runtime's own scoping (several\n // widgets don't map fields 1:1 onto their props root; see that\n // script's docstring).\n //\n // This is the authoring-time half of the SAME condition the\n // runtime already detects and fail-open-drops (adaptive_tools_gen.\n // dangling_field_dropped) \u2014 that log-only drop stays, unchanged,\n // for configs published before this rule existed; this rule stops\n // a NEW dangling reference from being authored in the first place.\n // The generated file also carries a `$comment` header string and an\n // `$alwaysAllowedFields` string array (see below) alongside the\n // per-widget path arrays \u2014 cast through `unknown` since `$comment`\n // intentionally breaks a direct `Record<string, string[]>` shape;\n // `checkDanglingFieldReferences` only ever reads a widget key that\n // came from `t.widget` (a real widget const), so `$comment` is\n // never looked up as a widget.\n const widgetFieldIndex = widgetPropsFieldIndex as unknown as Record<string, string[]>;\n\n // Fields that are structurally part of a TileTemplate's authoring\n // surface but are NEVER real widget-props properties, so this check\n // must not flag them as dangling: tile chrome (title/icon/subtitle,\n // rendered by the tile shell) and search-hydration handles\n // (product_handle/product_handles, popped by the mount executor\n // BEFORE the widget ever sees its props). Read from the SAME\n // generated `widgetPropsFieldIndex` above \u2014 never hand-retyped here\n // \u2014 so this list can only ever match what `syntrologie_common.sdk.\n // schema.TILE_CHROME_FIELDS`/`HANDLE_FIELD`/`HANDLES_FIELD` load\n // from `platform/common/syntrologie_common/sdk/widget-props-always-\n // allowed-fields.json`, the single source both languages derive\n // from (see `generate-widget-props-field-index.mjs`'s module\n // docstring). A prior version of this rule hand-retyped this exact\n // list and silently drifted from Python (BUG-1786833318) \u2014 flagging\n // `title`/`icon`/`product_handle`(s) as dangling on every real\n // production config (main-peptides-demo-publish.json,\n // iglowmedspa-demo-publish.json, nei-native-demo-publish.json all\n // author them on adaptive-product:card/:comparison templates).\n // BUG-1786834379 moved the list into the generated artifact so\n // that class of drift is structurally impossible: the freshness\n // check (`make app-check-widget-props-field-index-fresh`) fails if\n // this array is ever stale relative to its JSON source.\n const alwaysAllowedRaw = widgetFieldIndex[ALWAYS_ALLOWED_FIELDS_KEY];\n if (!Array.isArray(alwaysAllowedRaw)) {\n throw new Error(\n `widget-props-field-index.json is missing \"${ALWAYS_ALLOWED_FIELDS_KEY}\" \u2014 regenerate via ` +\n 'node packages/runtime-sdk/scripts/generate-widget-props-field-index.mjs'\n );\n }\n const NEVER_DANGLING_FIELDS = new Set(alwaysAllowedRaw);\n\n function checkDanglingFieldReferences(t: TemplateRaw, where: string): void {\n if (typeof t.widget !== 'string') return; // items/actions carry `kind`, not `widget` \u2014 not in scope\n const validPaths = widgetFieldIndex[t.widget];\n if (!validPaths) return; // widget outside the checked allowlist \u2014 genuinely unchecked, not a false negative\n const validSet = new Set(validPaths);\n const fields = asFieldsRecord(t.fields);\n for (const key of Object.keys(fields)) {\n if (validSet.has(key) || NEVER_DANGLING_FIELDS.has(key)) continue;\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: field ${JSON.stringify(key)} does not exist in the ${JSON.stringify(t.widget)} ` +\n `widget's props schema \u2014 it was removed, renamed, or never existed. Remove it from ` +\n `\\`fields\\`, or point it at the widget's current path.`,\n });\n }\n }\n\n /** Dunder / prototype-pollution names \u2014 mirrors RESERVED_FIELD_SEGMENTS. */\n const RESERVED_SEGMENTS = new Set([\n '__class__',\n '__init__',\n '__proto__',\n '__dict__',\n '__getattribute__',\n '__setattr__',\n 'constructor',\n 'prototype',\n ]);\n\n function checkTemplate(t: TemplateRaw, where: string): void {\n const fields = asFieldsRecord(t.fields);\n const fieldKeys = Object.keys(fields);\n\n // 1. admin_pinned_fields must be a subset of the declared fields.\n // Props the LLM cannot author are already un-authorable by NOT being\n // fields; pinning a non-field is a no-op the runtime treats as fatal.\n const unknownPins = asStringArray(t.admin_pinned_fields).filter(\n (name) => !(name in fields)\n );\n if (unknownPins.length > 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: admin_pinned_fields references unknown fields: ` +\n `${JSON.stringify(unknownPins)} \u2014 every pinned name must be a key of ` +\n `\\`fields\\` (declared: ${JSON.stringify(fieldKeys)})`,\n });\n }\n\n // 2. default_slot must be one of allowed_slots (tiles only).\n if (typeof t.default_slot === 'string') {\n const slots = Array.isArray(t.allowed_slots)\n ? (t.allowed_slots as Array<Record<string, unknown>>)\n .map((s) => (s && typeof s === 'object' ? s.slot : undefined))\n .filter((s): s is string => typeof s === 'string')\n : [];\n if (!slots.includes(t.default_slot)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: default_slot ${JSON.stringify(t.default_slot)} is not in ` +\n `allowed_slots ${JSON.stringify(slots)}`,\n });\n }\n }\n\n // 2b. ItemTemplate: default_parent must be one of allowed_parents[].tile_id.\n // Same failure class as default_slot, for items (adversarial-review find:\n // the first cut mirrored only the tile rule and left this one unchecked).\n if (typeof t.default_parent === 'string') {\n const parents = Array.isArray(t.allowed_parents)\n ? (t.allowed_parents as Array<Record<string, unknown>>)\n .map((p) => (p && typeof p === 'object' ? p.tile_id : undefined))\n .filter((p): p is string => typeof p === 'string')\n : [];\n if (!parents.includes(t.default_parent)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: default_parent ${JSON.stringify(t.default_parent)} is not in ` +\n `allowed_parents ${JSON.stringify(parents)}`,\n });\n }\n }\n\n // 2c. ActionTemplate: placement_type and allowed_targets must agree \u2014\n // 'selector' needs targets to place against, 'none' must carry none.\n if (typeof t.placement_type === 'string') {\n const targets = Array.isArray(t.allowed_targets) ? t.allowed_targets : [];\n if (t.placement_type === 'selector' && targets.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${where}: placement_type 'selector' requires a non-empty allowed_targets list`,\n });\n }\n if (t.placement_type === 'none' && targets.length > 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${where}: placement_type 'none' must have empty allowed_targets`,\n });\n }\n }\n\n // 3. Field-key paths: no empty segments, no reserved segments.\n for (const key of fieldKeys) {\n const segments = key.split('.');\n if (segments.some((seg) => seg.length === 0)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: field key ${JSON.stringify(key)} contains empty segment(s) ` +\n `(leading dot, trailing dot, or '..' are not allowed)`,\n });\n continue;\n }\n for (const seg of segments) {\n if (RESERVED_SEGMENTS.has(seg)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: field key ${JSON.stringify(key)} uses reserved segment ` +\n `${JSON.stringify(seg)} \u2014 dunder / prototype-pollution names are blocked`,\n });\n }\n }\n }\n\n // 4. Dot-prefix overlap: declaring both `product` and `product.tagline` is\n // ambiguous \u2014 a patch on the parent would obliterate the child.\n const sortedKeys = [...fieldKeys].sort();\n for (let i = 0; i < sortedKeys.length - 1; i++) {\n const a = sortedKeys[i];\n const b = sortedKeys[i + 1];\n if (b.startsWith(`${a}.`)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `${where}: field keys ${JSON.stringify(a)} and ${JSON.stringify(b)} claim the ` +\n `same prefix namespace \u2014 declaring both is ambiguous (a patch on the parent ` +\n `would overwrite the child)`,\n });\n }\n }\n }\n\n for (const t of tiles) {\n checkTemplate(t, `tile ${JSON.stringify(String(t.id))}`);\n checkDanglingFieldReferences(t, `tile ${JSON.stringify(String(t.id))}`);\n }\n for (const t of items) checkTemplate(t, `item ${JSON.stringify(String(t.id))}`);\n for (const t of actions) checkTemplate(t, `action ${JSON.stringify(String(t.id))}`);\n\n for (const [typeKey, members] of groups) {\n if (members.length < 2) continue;\n\n const headSig = templateSignature(members[0]);\n for (let i = 1; i < members.length; i++) {\n const other = members[i];\n if (!signaturesEqual(headSig, templateSignature(other))) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `templates of type ${JSON.stringify(typeKey)} must share an authorable field ` +\n `shape, but ${JSON.stringify(String(members[0].id))} and ` +\n `${JSON.stringify(String(other.id))} differ ` +\n `(field names / required / admin_pinned / enum values)`,\n });\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Assembled mount-tool description cap (authoring time, PR #3873).\n //\n // The runtime builds ONE tool per widget/kind whose description is every\n // template's description plus a note the server owns. A model provider\n // silently drops a tool past the cap, so an over-long set is refused\n // here. The cap, the string pieces and the notes are generated from\n // platform/common's mount_tool_description.py (never hand-typed), so\n // this builds the exact string the runtime sends. Grouping mirrors the\n // runtime: raw widget for tiles, raw kind for items and actions.\n // ---------------------------------------------------------------------------\n const toolSpec = mountToolDescriptionSpec as {\n maxLength: number;\n base: string;\n templatesPrefix: string;\n templateItem: string;\n templateSeparator: string;\n notePrefix: string;\n notes: Record<string, string>;\n };\n const toolGroups = new Map<string, TemplateRaw[]>();\n for (const t of tiles) {\n if (typeof t.widget === 'string')\n toolGroups.set(t.widget, [...(toolGroups.get(t.widget) ?? []), t]);\n }\n for (const t of [...items, ...actions]) {\n if (typeof t.kind === 'string')\n toolGroups.set(t.kind, [...(toolGroups.get(t.kind) ?? []), t]);\n }\n for (const [typeKey, members] of toolGroups) {\n const listed = members\n .filter((t) => typeof t.description === 'string' && t.description.length > 0)\n // split/join, not .replace: same every-occurrence semantics as\n // Python's str.replace, and no `$&`-style replacement patterns.\n .map((t) =>\n toolSpec.templateItem\n .split('{id}')\n .join(String(t.id))\n .split('{description}')\n .join(t.description as string)\n )\n .join(toolSpec.templateSeparator);\n let text = toolSpec.base.split('{type_key}').join(typeKey);\n if (listed) text += toolSpec.templatesPrefix + listed;\n const note = toolSpec.notes[typeKey];\n if (note) text += toolSpec.notePrefix + note;\n if (text.length > toolSpec.maxLength) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `the mount tool for ${JSON.stringify(typeKey)} would have a ${text.length} chars ` +\n `description (templates ${members.map((t) => JSON.stringify(String(t.id))).join(', ')} ` +\n `plus the server's note), over the ${toolSpec.maxLength}-char cap. The model ` +\n `provider silently drops an over-long tool, so these widgets could never mount: ` +\n `shorten those templates' descriptions or ids.`,\n });\n }\n }\n })\n .optional()\n .describe(\n 'Template declarations the LLM can mount via the elements pipeline. Shape mirrors syntrologie_common.sdk.templates.UiTemplatesBlock. Passed verbatim as forwardedProps.uiTemplates to the AG-UI transport.'\n ),\n voiceInput: z\n .boolean()\n .optional()\n .describe(\n 'Show a mic button that dictates into the input via the browser Web Speech API where supported (vendor-cloud recognition; degrades to hidden when unsupported). Absent/false = no button.'\n ),\n supportChat: z\n .object({\n vendor: z.string().min(1),\n suppressNative: z.boolean().optional(),\n widgetId: z\n .string()\n .min(1)\n .optional()\n .describe(\n \"LeadConnector / GoHighLevel only: the chat widget id from the site's embed script (`data-widget-id`). The bridge fetches the location id from the vendor's public widget config \u2014 never hand-type it.\"\n ),\n inbox: z\n .object({\n title: z\n .string()\n .min(1)\n .describe(\n 'Heading of the thread card the SDK opens when the visitor taps the announcement of a team message that arrived with no handoff card on the page.'\n ),\n toastTitle: z\n .string()\n .min(1)\n .describe(\n 'Title of that announcement. Its body is the message itself. Name the team in the workspace voice.'\n ),\n })\n .strict()\n .optional()\n .describe(\n \"Where a team message lands when NO adaptive-chatbot:leave-a-message card is on the page \u2014 the vendor's own proactive triggers, or a reply after navigation tore the card down. The SDK writes it into the same thread the card hydrates from, announces it through the canvas, and on tap mounts the workspace's leave-a-message template opened on the thread. Required whenever suppressNative is true: with the vendor's UI hidden, an un-announced message is a message the visitor never sees.\"\n ),\n })\n .strict()\n .superRefine((supportChat, ctx) => {\n if (supportChat.suppressNative && !supportChat.inbox) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['inbox'],\n message:\n 'supportChat.inbox is required when suppressNative is true: the vendor UI is hidden, so a team message that arrives with no handoff card mounted has nowhere to show without it',\n });\n }\n // Cross-field requirement JSON Schema cannot express on this object:\n // 'leadconnector' is the one vendor whose bridge needs per-site\n // config (every other vendor's SDK self-identifies on the page).\n // Without this, a config naming that vendor and omitting widgetId is\n // schema-VALID and fails only at runtime \u2014 bootSupportChat warns and\n // disables handoff, so the customer discovers it when a visitor\n // tries to reach a human. Caught here at authoring time instead:\n // `make sdk-validate` runs this via validate-config.mjs's per-tile\n // props pass (see runtime-sdk/scripts/lib/zod-authoring-checks.mjs).\n if (supportChat.vendor !== 'leadconnector') return;\n if (supportChat.widgetId) return;\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['widgetId'],\n message:\n 'supportChat.widgetId is required when vendor is \"leadconnector\" (the chat widget id from the site\\'s embed, data-widget-id)',\n });\n })\n .optional()\n .describe(\n 'Names the site\\'s own support-chat product (\"crisp\" or \"leadconnector\") for async human handoff via the adaptive-chatbot:leave-a-message widget. Resolved at chat-bar boot regardless of whether that widget ever mounts, so suppressNative (hide the vendor\\'s native launcher bubble) always applies. Absent vendor SDK fails soft \u2014 a console warning, never a thrown error.'\n ),\n })\n .strict();\n\nexport type ChatBarProps = z.infer<typeof chatBarPropsSchema>;\n\n// ============================================================================\n// Chips Strip Props (adaptive-chatbot:chips-strip widget)\n// ============================================================================\n\n/**\n * Props schema for the `adaptive-chatbot:chips-strip` widget. Chips\n * follow the compositional-action shape (see SuggestionChipSchema in\n * AdaptiveChipsStrip.ts) \u2014 same pattern as faq:question.\n */\nexport const chipsStripPropsSchema = z\n .object({\n chips: z\n .array(SuggestionChipSchema)\n .default([])\n .describe(\n 'Suggestion chips authored as suggestions:chip compositional actions. Each chip declares an id, a title, and a payload that mounts in the drawer when clicked. Conditional visibility via per-chip triggerWhen.'\n ),\n })\n .strict();\n\nexport type ChipsStripProps = z.infer<typeof chipsStripPropsSchema>;\n\n// ============================================================================\n// Tile Widget Props Export\n// ============================================================================\n\n/**\n * Tile widget definitions for unified JSON Schema generation.\n * Maps widget IDs to their props validation schema so the build script\n * can inject if/then constraints on tile.props.\n */\nexport const tileWidgets = [\n {\n widget: 'adaptive-chatbot:assistant',\n defName: 'chatbotAssistantProps',\n propsSchema: configSchema,\n },\n {\n widget: 'adaptive-chatbot:chat-bar',\n defName: 'chatBarProps',\n propsSchema: chatBarPropsSchema,\n },\n {\n widget: 'adaptive-chatbot:chips-strip',\n defName: 'chipsStripProps',\n propsSchema: chipsStripPropsSchema,\n },\n];\n\n// ============================================================================\n// Capabilities Documentation (injected into JSON Schema for LLM prompts)\n// ============================================================================\n\nexport const CAPABILITIES_DOCUMENTATION = {\n packageId: 'adaptive-chatbot',\n plannerSummary:\n 'Conversational assistant, always-visible chat input, and suggestion-chip surfaces. Reach for this when a strategy needs dialog, chat-triggered UI, or prompt chips that reveal richer payloads.',\n description:\n 'Chat and conversational UI adaptive. Provides a full assistant, a headless chat bar, suggestion chips, and chip payload widgets for text answers and navigation links.',\n whenToUse: [\n {\n goal: 'Mount a full AI assistant with backend streaming',\n action:\n 'Add a tile with widget: \"adaptive-chatbot:assistant\" and configure backendUrl, greeting, maxHistory, suggestions, and fallback props.',\n },\n {\n goal: 'Replace a launcher or slot lid with a chat input row',\n action:\n 'Use widget: \"adaptive-chatbot:chat-bar\" from a tile, slots.drawer.lid, or another slot lid when the host owns the transport.',\n },\n {\n goal: 'Offer suggested prompts or choices that reveal payload content',\n action:\n 'Add widget: \"adaptive-chatbot:chips-strip\" with suggestions:chip entries; each chip payload should reference a supported widget such as adaptive-chatbot:text-answer, adaptive-chatbot:nav-link, or another tile widget.',\n },\n {\n goal: 'Escalate to a human when the assistant cannot help',\n action:\n 'Mount widget: \"adaptive-chatbot:leave-a-message\" (with the chat-bar\\'s supportChat.vendor already configured) when the visitor explicitly asks for a human, when you cannot resolve after a genuine attempt, or on clear frustration \u2014 never as a first response.',\n },\n ],\n conventions: [\n {\n name: 'backendUrl is explicit',\n description:\n 'assistant backendUrl must be either \"\" for same-origin /api/adaptive/* routing or an absolute http(s) URL. Relative paths are rejected because they compose to invalid /api/adaptive/session URLs.',\n },\n {\n name: 'Chat bar is headless',\n description:\n 'adaptive-chatbot:chat-bar renders the input and trail but does not own a transport by default. Hosts should listen for chat-message-sent, chat-interrupt, and canvas-close or provide backendUrl explicitly.',\n },\n {\n name: 'Suggestion chips are compositional',\n description:\n 'suggestions:chip entries are config data rendered by adaptive-chatbot:chips-strip. Use triggerWhen for conditional visibility and use payload.widget for the content mounted after a click.',\n },\n {\n name: 'LLM-authored UI is opt-in',\n description:\n 'Set elementsEnabled and uiTemplates only when the backend is allowed to mount, patch, and unmount declared UI templates during chat turns.',\n },\n {\n name: 'Reply-toast copy is opt-in workspace configuration',\n description:\n \"adaptive-chatbot:leave-a-message announces an operator's reply with a toast ONLY when the workspace authors the words in default_widget_props.replyToast ({title, body}). No authored copy means no toast \u2014 absence is the opt-out, and the tile's own mail face remains the announcement. The bundle never supplies stand-in copy: it would be in the wrong voice and, on a non-English storefront, the wrong language.\",\n },\n {\n name: 'Leave-a-message copy is 100% workspace-authored',\n description:\n 'adaptive-chatbot:leave-a-message ships NO hardcoded visitor-facing strings across its five states (compose \u2192 email \u2192 sent \u2192 mail \u2192 flipped). Every label, framing line, prompt, and button comes from default_widget_props.copy ({framing, messageLabel, sendLabel, emailPrompt, emailLabel, emailPlaceholder, emailSubmitLabel, emailSkipLabel, sentBody, mailLabel, replyLabel, replyPlaceholder, replySendLabel}). The compose/email/reply steps use icon-only send controls whose aria-label IS the authored copy \u2014 an icon button with no accessible name is a real defect, so an unauthored sendLabel/emailSubmitLabel/replySendLabel removes the control entirely rather than shipping it mute. Every other field follows the same absence-is-the-opt-out rule as replyToast.',\n },\n {\n name: 'Human handoff is escalation-only',\n description:\n \"adaptive-chatbot:leave-a-message delivers through the site's own support-chat vendor named in the chat-bar's supportChat.vendor prop \u2014 never mount it as a first response; reach for it only after a genuine resolution attempt, an explicit request for a person, or clear frustration.\",\n },\n ],\n // Structured {name, when, props} entries \u2014 the CAPABILITIES_DOCUMENTATION\n // contract every consumer renders (`_render_capabilities_markdown` in\n // tech-core, `format_capabilities_for_prompt` in syntrologie_common).\n // Plain strings here crashed both renderers (BUG-1784941121).\n events: [\n {\n name: 'chat-message-sent',\n when: 'Visitor sends a chat message (typed, or via a suggested-reply chip)',\n props: '{ text }',\n },\n {\n name: 'chat-interrupt',\n when: 'Visitor interrupts the streaming assistant reply',\n props: '{}',\n },\n {\n name: 'canvas-close',\n when: 'Chat surface requests the host canvas to close',\n props: '{}',\n },\n {\n name: 'trail-suggested-reply',\n when: 'Visitor taps a suggest_replies chip in the chat trail',\n props: '{ text }',\n },\n {\n name: 'supportchat:reply_received',\n when: \"A human operator replies through the site's support-chat vendor while the adaptive-chatbot:leave-a-message tile is un-flipped (not yet read) \u2014 published on the runtime EventBus, not as a DOM CustomEvent, so tile notifications rules can badge it\",\n props: '{ text }',\n },\n ],\n};\n\n/**\n * Action step schemas \u2014 registered into the unified canvas-config\n * JSON Schema's action-step union so authors (and the tactician LLM)\n * can emit `suggestions:chip` items inside a chips-strip tile's\n * `props.chips` array with full validation.\n *\n * `.innerType()` strips the `.refine()` wrapper (cross-field\n * refinements don't translate to JSON Schema); runtime Zod still\n * enforces them.\n */\nexport const actionStepSchemas = [\n { defName: 'suggestionChip', schema: SuggestionChipSchema.innerType() },\n];\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syntrologie/adapt-chatbot",
3
- "version": "2.56.0",
3
+ "version": "2.57.1",
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/elements/tileLifecycleProps.ts", "../src/elements/ElementInstanceStore.ts", "../src/elements/TileHandler.ts", "../src/elements/ActionHandler.ts", "../src/elements/ItemHandler.ts", "../src/elements/createElementStore.ts", "../src/elements/ElementSessionController.ts"],
4
- "sourcesContent": ["import { parseTileLayout, type TileLifecycleMountProps } from '@syntrologie/sdk-contracts';\nimport type { MountedElement, SlotPlacement } from './types';\n\n/**\n * The one builder for an `element.tile_mounted` / `element.tile_patched`\n * payload. The runtime rebuilds a tile from the LATEST event for its id, so\n * every site that publishes one (the handler's mount and patch, the store's\n * replays) must send the same fields: a replay built by hand dropped the\n * agent's placement plan and ungrouped the tiles it had just placed.\n */\nexport function tileMountProps(\n instance: MountedElement & { placement: SlotPlacement },\n widget: string\n): TileLifecycleMountProps {\n const layout = parseTileLayout(instance.layout);\n return {\n slot: instance.placement.slot,\n instance_id: instance.instance_id,\n widget,\n props: (instance.content ?? {}) as Record<string, unknown>,\n ...(layout ? { layout } : {}),\n };\n}\n", "/**\n * Type-neutral store for LLM-authored UI element instances \u2014 Phase 6.\n *\n * On SDK boot:\n * 1. Caller fetches `/api/adaptive/mounted_elements` (see `fetcher.ts`).\n * 2. Calls `store.hydrate(response.mounted_elements)`.\n * 3. The store iterates each instance, looks up its handler, and\n * either mounts (stateful) or skips (imperative already-applied).\n *\n * During chat turns:\n * - AGUI `syntro.element.mutation` envelopes arrive via\n * `AgUiTransport.onA2UIEvent`. The transport caller decodes the\n * envelope and invokes `store.apply(envelope.mutations)`.\n *\n * Concurrency / dedup:\n * - Per-instance `version` is the dedup key. A mutation whose version\n * is `<= currentVersion` is treated as already-applied and silently\n * dropped \u2014 handles the SSE-during-boot-fetch race per the design\n * doc (the live mutation might arrive before the boot snapshot does,\n * and the snapshot is then a no-op).\n * - The store is single-threaded by Node/browser convention; no\n * cross-tab sync in v1.\n *\n * The store delegates all DOM work to per-type handlers\n * (`ElementTypeHandler`). It owns the registry, the in-memory state,\n * and the dispatch logic.\n */\n\nimport {\n TILE_MOUNTED_EVENT,\n TILE_REPLAY_REQUEST_EVENT,\n TILE_UNMOUNTED_EVENT,\n} from '@syntrologie/sdk-contracts';\n\nimport type { ActionEngine } from '../types';\nimport type { ElementHandlerContext, ElementTypeHandler } from './ElementTypeHandler';\nimport { tileMountProps } from './tileLifecycleProps';\nimport type { ElementMutation, MountedElement } from './types';\n\nconst COMPOSITIONAL_REPLAY_REQUEST_EVENT = 'element.compositional_replay_request';\nconst COMPOSITIONAL_APPEND_EVENT = 'element.compositional_append';\n\n/**\n * Slot name reserved for tiles the backend wants rendered INLINE IN THE\n * CHAT TRAIL (in the conversation flow, like a rich message) rather than\n * in the canvas deck. The reco carousel uses this: the deck tile mounts\n * BEHIND the open chat panel, so the visitor never sees it. The backend\n * owns the placement decision \u2014 `adaptive_agent.py` emits\n * `SlotPlacement(slot=\"chat-inline\")` for the reco mount. On the chat\n * side these tiles:\n * - surface via {@link ElementInstanceStore.listInlineWidgets} so the\n * trail can mount the widget element inline; and\n * - are SUPPRESSED from {@link ElementInstanceStore.listTileReceipts}\n * (a receipt chip would be a redundant, weaker affordance for a\n * widget already visible in the trail).\n * Every other slot keeps its existing deck + receipt-chip behavior.\n */\nexport const CHAT_INLINE_SLOT = 'chat-inline';\n\n/** One inline-rendered widget for the chat trail \u2014 a `chat-inline` tile. */\nexport interface InlineWidget {\n instanceId: string;\n /** Custom-element tag to mount (e.g. `syntro-product-reco-carousel`). */\n tag: string;\n /** Payload assigned to the mounted element as ONE `props` object property\n * (`el.props = props` \u2014 the standard tile-widget mount contract; never\n * spread as individual per-key properties). */\n props: Record<string, unknown>;\n /** `mounted_by.turn_id` \u2014 lets the chat trail restore the widget to its\n * original transcript position after a reload. */\n turnId?: string;\n /** `tool_call_id` of the mount call \u2014 pins the restored widget to the exact\n * transcript message hosting that call; `turnId` is the coarser fallback. */\n toolCallId?: string;\n}\n\nexport interface ElementInstanceStoreOptions {\n actions: ActionEngine;\n /** Runtime's event bus. ItemHandler publishes\n * `element.compositional_append` events here so container widgets\n * (chips strip, FAQ accordion, nav tips) can subscribe and inject\n * LLM-authored items into their compositional-actions lists.\n *\n * When `subscribe` is also provided, the store listens for\n * `element.compositional_replay_request` events. A container widget\n * publishes this event with its `tile_id` whenever it subscribes\n * (e.g. on first mount AND on SPA-navigation remount); the store\n * responds by re-publishing `element.compositional_append` for every\n * matching item still in its in-memory map. This is what makes\n * LLM-authored items persist across navigations within the page\n * session \u2014 the store survives chat-bar unmount/remount, container\n * widgets re-pull on each fresh mount.\n */\n events?: {\n publish: (name: string, props?: Record<string, unknown>) => void;\n /**\n * Overloaded to match the runtime SDK's `EventBus.subscribe`:\n * pass `{names:[...]}` to filter, or a callback to receive every\n * event. The store uses the filtered form so it doesn't fan\n * unrelated traffic (PostHog autocapture, every action lifecycle\n * event) into a string-compare on each delivery.\n */\n subscribe?: {\n (\n filter: { names?: string[] },\n handler: (event: { name: string; props?: Record<string, unknown> }) => void\n ): () => void;\n (handler: (event: { name: string; props?: Record<string, unknown> }) => void): () => void;\n };\n };\n handlers?: ElementTypeHandler[];\n /** Override the logger \u2014 used by tests to capture log output. */\n logger?: Pick<Console, 'info' | 'warn' | 'error'>;\n /**\n * Optional template\u2192widget resolver used by the rehydrate replay.\n * Without this, replayed tile mounts emit ``widget: template_id``,\n * which only works when the admin's template ids match the widget\n * registry (e.g. ``adaptive-faq:accordion``). When an admin uses a\n * friendly id like ``product-card`` mapping to widget\n * ``adaptive-product:card``, the rehydrate path needs the resolver\n * to match the live mount path.\n *\n * Same shape as ``TileHandler``'s resolver \u2014 pass the same function\n * to both so live mounts and replays agree on the widget id.\n */\n resolveTileWidget?: (templateId: string) => string | undefined;\n}\n\nexport interface ApplyReport {\n ok: boolean;\n rejectedInstanceIds: string[];\n}\n\nexport class ElementInstanceStore {\n private readonly _actions: ActionEngine;\n private readonly _events: ElementInstanceStoreOptions['events'];\n private readonly _handlers: Map<string, ElementTypeHandler>;\n private readonly _instances: Map<string, MountedElement> = new Map();\n private readonly _logger: Pick<Console, 'info' | 'warn' | 'error'>;\n private readonly _resolveTileWidget?: (templateId: string) => string | undefined;\n /** Active TTL timeout \u2014 replaced on each call to `scheduleClientTtl`. */\n private _ttlTimer: ReturnType<typeof setTimeout> | null = null;\n private _unsubscribeReplay?: () => void;\n private _disposed = false;\n private _disposing = false;\n /** Instance IDs the client has dismissed. Persists across canvas remounts\n * so a dismissed tile never silently reappears on replay/broadcast. */\n private readonly _dismissedIds: Set<string> = new Set();\n\n constructor(options: ElementInstanceStoreOptions) {\n this._actions = options.actions;\n this._events = options.events;\n this._resolveTileWidget = options.resolveTileWidget;\n this._handlers = new Map();\n for (const handler of options.handlers ?? []) {\n this._handlers.set(handler.typeName, handler);\n }\n this._logger = options.logger ?? console;\n this._subscribeReplayRequests();\n }\n\n /**\n * Listen for `element.compositional_replay_request` from container\n * widgets and re-publish `element.compositional_append` for every\n * matching item still tracked in `_instances`. This keeps LLM-authored\n * items visible across SPA navigations: the store is a module-level\n * singleton that survives chat-bar unmount/remount, so when a fresh\n * chips-strip mounts and subscribes, asking for a replay re-populates\n * its chip array.\n */\n private _subscribeReplayRequests(): void {\n const subscribe = this._events?.subscribe;\n const publish = this._events?.publish;\n if (!subscribe || !publish) return;\n this._unsubscribeReplay = subscribe(\n {\n names: [\n COMPOSITIONAL_REPLAY_REQUEST_EVENT,\n TILE_REPLAY_REQUEST_EVENT,\n TILE_UNMOUNTED_EVENT,\n ],\n },\n (event) => {\n if (this._disposed) return;\n if (event.name === TILE_UNMOUNTED_EVENT) {\n // Client dismiss: keep the instance + content, mark it dismissed so\n // replay/broadcast skip it (fixes the zombie-tile reappearance bug).\n const id = String(event.props?.instance_id ?? '');\n if (id) this._dismissedIds.add(id);\n return;\n }\n if (event.name === COMPOSITIONAL_REPLAY_REQUEST_EVENT) {\n const tileId = String(event.props?.tile_id ?? '');\n if (!tileId) return;\n this._replayItems(tileId, publish);\n return;\n }\n if (event.name === TILE_REPLAY_REQUEST_EVENT) {\n // SmartCanvasElementLit asks for every active LLM-authored tile\n // so it can merge them into its `_tiles` array on (re)mount.\n this._replayTiles(publish);\n return;\n }\n }\n );\n }\n\n private _replayItems(\n tileId: string,\n publish: (name: string, props?: Record<string, unknown>) => void\n ): void {\n for (const instance of this._instances.values()) {\n if (this._disposed) return;\n if (instance.element_type !== 'item') continue;\n if (instance.placement.type !== 'parent_tile') continue;\n if (instance.placement.tile_id !== tileId) continue;\n if (instance.state !== 'active') continue;\n publish(COMPOSITIONAL_APPEND_EVENT, {\n tile_id: tileId,\n instance_id: instance.instance_id,\n item: instance.content,\n position: instance.placement.position,\n });\n }\n }\n\n private _replayTiles(publish: (name: string, props?: Record<string, unknown>) => void): void {\n for (const instance of this._instances.values()) {\n if (this._disposed) return;\n if (instance.element_type !== 'tile') continue;\n if (instance.placement.type !== 'slot') continue;\n if (instance.state !== 'active') continue;\n if (this._dismissedIds.has(instance.instance_id)) continue;\n const props = tileMountProps(\n { ...instance, placement: instance.placement },\n this._resolveTileWidget?.(instance.template_id) ?? instance.template_id\n );\n publish(TILE_MOUNTED_EVENT, props as unknown as Record<string, unknown>);\n }\n }\n\n registerHandler(handler: ElementTypeHandler): void {\n this._handlers.set(handler.typeName, handler);\n }\n\n isDismissed(instanceId: string): boolean {\n return this._dismissedIds.has(instanceId);\n }\n\n getDismissedIds(): ReadonlySet<string> {\n return this._dismissedIds;\n }\n\n /** Store-driven receipt source for the chat trail (one per active slot-tile). */\n listTileReceipts(): Array<\n | {\n kind: 'tile';\n instanceId: string;\n title: string;\n dismissed: boolean;\n turnId?: string;\n toolCallId?: string;\n imageUrl?: string;\n }\n | { kind: 'alert'; instanceId: string; title: string; turnId?: string; toolCallId?: string }\n > {\n const out: Array<\n | {\n kind: 'tile';\n instanceId: string;\n title: string;\n dismissed: boolean;\n turnId?: string;\n toolCallId?: string;\n imageUrl?: string;\n }\n | { kind: 'alert'; instanceId: string; title: string; turnId?: string; toolCallId?: string }\n > = [];\n for (const inst of this._instances.values()) {\n if (inst.element_type !== 'tile' || inst.placement.type !== 'slot') continue;\n if (inst.state !== 'active') continue; // reverted/expired \u2192 no chip\n // chat-inline tiles are rendered inline in the trail (see\n // listInlineWidgets) \u2014 a receipt chip would be a redundant, weaker\n // affordance for something already visible in the conversation.\n if (inst.placement.slot === CHAT_INLINE_SLOT) continue;\n const content = inst.content as\n | { product?: { name?: unknown }; title?: unknown; heading?: unknown }\n | undefined;\n const productName = content?.product?.name;\n const contentTitle = content?.title;\n const contentHeading = content?.heading; // comparisons/reco carry `heading`, not `title`\n const title =\n typeof productName === 'string' && productName\n ? productName\n : typeof contentTitle === 'string' && contentTitle\n ? contentTitle\n : typeof contentHeading === 'string' && contentHeading\n ? contentHeading\n : // Never show a raw template id (kebab-case machine name) in the\n // chat \u2014 prettify the last-resort fallback into Title Case.\n inst.template_id.replace(/[-_]+/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n // COI-037: the chip's leading visual is a tiny product thumbnail when\n // the mount carries one \u2014 the decorative stack glyph read as a BROKEN\n // image next to a product name (2026-07-24 tester screenshots). One\n // extraction per mount shape: product card \u2192 product.image.src; reco\n // carousel \u2192 first item's image_url; comparison \u2192 first product's image.\n const imgContent = inst.content as {\n product?: { image?: { src?: unknown } };\n products?: Array<{ image?: { src?: unknown } }>;\n items?: Array<{ image_url?: unknown }>;\n };\n const imageCandidate =\n imgContent?.product?.image?.src ??\n imgContent?.items?.[0]?.image_url ??\n imgContent?.products?.[0]?.image?.src;\n const imageUrl =\n typeof imageCandidate === 'string' && imageCandidate.trim() ? imageCandidate : undefined;\n // Alert-slot tiles are nudges \u2192 a distinct, navigating chat chip (kind:'alert').\n // Everything else is a recallable deck tile (kind:'tile') with dismiss state.\n if (inst.placement.slot === 'alert') {\n out.push({\n kind: 'alert',\n instanceId: inst.instance_id,\n title,\n turnId: inst.mounted_by?.turn_id,\n toolCallId: inst.tool_call_id ?? undefined,\n });\n } else {\n out.push({\n kind: 'tile',\n instanceId: inst.instance_id,\n title,\n dismissed: this._dismissedIds.has(inst.instance_id),\n turnId: inst.mounted_by?.turn_id,\n toolCallId: inst.tool_call_id ?? undefined,\n ...(imageUrl ? { imageUrl } : {}),\n });\n }\n }\n return out;\n }\n\n /**\n * Store-driven inline-widget source for the chat trail \u2014 one entry per\n * active `chat-inline` slot-tile. The trail mounts each entry's widget\n * element (by tag name, via `document.createElement`) inline in the\n * conversation flow. Distinct from {@link listTileReceipts}: those are\n * chips that recall a deck tile; these ARE the rendered widget.\n */\n listInlineWidgets(): InlineWidget[] {\n const out: InlineWidget[] = [];\n for (const inst of this._instances.values()) {\n if (inst.element_type !== 'tile' || inst.placement.type !== 'slot') continue;\n if (inst.placement.slot !== CHAT_INLINE_SLOT) continue;\n if (inst.state !== 'active') continue; // reverted/expired \u2192 not rendered\n if (this._dismissedIds.has(inst.instance_id)) continue;\n out.push({\n instanceId: inst.instance_id,\n tag: this._resolveTileWidget?.(inst.template_id) ?? inst.template_id,\n props: (inst.content ?? {}) as Record<string, unknown>,\n turnId: inst.mounted_by?.turn_id,\n toolCallId: inst.tool_call_id ?? undefined,\n });\n }\n return out;\n }\n\n /** Clear a client dismissal and re-publish the tile's mount from retained content. */\n recreate(instanceId: string): void {\n if (this._disposed) return;\n const instance = this._instances.get(instanceId);\n const publish = this._events?.publish;\n if (\n !instance ||\n instance.element_type !== 'tile' ||\n instance.placement.type !== 'slot' ||\n !publish\n ) {\n return;\n }\n this._dismissedIds.delete(instanceId);\n const props = tileMountProps(\n { ...instance, placement: instance.placement },\n this._resolveTileWidget?.(instance.template_id) ?? instance.template_id\n );\n publish(TILE_MOUNTED_EVENT, props as unknown as Record<string, unknown>);\n }\n\n /**\n * Apply the server-provided boot snapshot. For each instance:\n * - stateful: validated + mounted via the type handler;\n * - imperative + already-applied: recorded only (no DOM action);\n * - any: tracked in the in-memory map for future patches.\n *\n * Safe to call once on SDK init. Calling again would re-mount\n * stateful instances; callers should not.\n */\n async hydrate(snapshot: MountedElement[]): Promise<void> {\n await this._hydrateWithReport(snapshot);\n }\n\n private async _hydrateWithReport(\n snapshot: MountedElement[],\n reconcile = false\n ): Promise<ApplyReport> {\n const rejectedInstanceIds: string[] = [];\n for (const instance of snapshot) {\n if (this._disposed) return { ok: false, rejectedInstanceIds };\n // Version-dedup against newer live state. Mirrors _applyOne's\n // guard so the apply-before-hydrate race (live mutation lands\n // first, slow boot snapshot resolves after) is genuinely a\n // no-op as the class header promises \u2014 without this, a stale\n // snapshot would clobber the in-memory version and re-render\n // older content. See test\n // \"hydrate honors version dedup when newer state already applied\".\n const existing = this._instances.get(instance.instance_id);\n if (\n existing &&\n (existing.version > instance.version ||\n (existing.version === instance.version &&\n (!reconcile || existing.state === instance.state)))\n ) {\n continue;\n }\n if (reconcile && existing?.state !== 'active')\n this._dismissedIds.delete(instance.instance_id);\n const handler = this._handlers.get(instance.element_type);\n if (!handler) {\n rejectedInstanceIds.push(instance.instance_id);\n this._logger.warn('elements: hydrate skipped \u2014 unknown element_type', {\n element_type: instance.element_type,\n instance_id: instance.instance_id,\n });\n continue;\n }\n this._instances.set(instance.instance_id, instance);\n if (handler.lifecycle === 'imperative') {\n // Don't re-fire scroll/navigate on page reload.\n continue;\n }\n if (instance.state !== 'active') {\n // Reverted / expired / applied stateful \u2014 record only.\n continue;\n }\n if (!(await this._mount(handler, instance))) {\n rejectedInstanceIds.push(instance.instance_id);\n if (!this._disposed) {\n if (existing) this._instances.set(instance.instance_id, existing);\n else this._instances.delete(instance.instance_id);\n }\n }\n }\n // Belt-and-suspenders: after every per-instance handler.mount() has\n // run, publish one more pass for each active LLM-authored tile/item\n // so any subscriber that was wired between the per-handler publishes\n // (or any handler that skipped publish for any reason) still ends\n // up with the right state. Receivers dedup by `instance_id` so the\n // double-emit is idempotent.\n //\n // Items also rely on a per-tile_id replay request (the container\n // widget publishes the request on its own mount), so the explicit\n // replay sweep is the boot-time backstop and the request-driven\n // replay is the SPA-nav backstop.\n this._broadcastActive();\n return { ok: !this._disposed && rejectedInstanceIds.length === 0, rejectedInstanceIds };\n }\n\n /**\n * Re-publish `element.tile_mounted` / `element.compositional_append`\n * for every active LLM-authored instance in the store. Idempotent \u2014\n * receivers key by `instance_id` (tiles) or by widget identity\n * (items via tile_id filter).\n */\n private _broadcastActive(): void {\n if (this._disposed) return;\n const publish = this._events?.publish;\n if (!publish) return;\n this._replayTiles(publish);\n // Items: group by tile_id so each receiver's filter still matches.\n const itemTileIds = new Set<string>();\n for (const instance of this._instances.values()) {\n if (instance.element_type !== 'item') continue;\n if (instance.placement.type !== 'parent_tile') continue;\n if (instance.state !== 'active') continue;\n itemTileIds.add(instance.placement.tile_id);\n }\n for (const tileId of itemTileIds) {\n this._replayItems(tileId, publish);\n }\n }\n\n /**\n * Apply a batch of mutations, atomically per-batch. Each mutation is\n * version-checked (mount: must not conflict with existing instance;\n * patch: resulting_version must be > current; unmount: idempotent).\n */\n async apply(mutations: ElementMutation[]): Promise<void> {\n await this.applyWithReport(mutations);\n }\n\n async applyWithReport(mutations: ElementMutation[]): Promise<ApplyReport> {\n return this._applyWithReport(mutations, false);\n }\n\n private async _applyWithReport(\n mutations: ElementMutation[],\n authoritative: boolean\n ): Promise<ApplyReport> {\n const rejectedInstanceIds: string[] = [];\n for (const mutation of mutations) {\n if (this._disposed) return { ok: false, rejectedInstanceIds };\n const id =\n (mutation?.op === 'mount' ? mutation.instance?.instance_id : mutation?.instance_id) ??\n '<invalid>';\n try {\n if (!(await this._applyOne(mutation, authoritative))) rejectedInstanceIds.push(id);\n } catch {\n rejectedInstanceIds.push(id);\n // Mutations are independent \u2014 one failing should not abort the\n // rest. Log loudly so the failure surfaces.\n this._logger.error('elements: mutation failed', { instance_id: id });\n }\n }\n return { ok: !this._disposed && rejectedInstanceIds.length === 0, rejectedInstanceIds };\n }\n\n /** Read-only snapshot of currently-known instances. */\n getInstances(): MountedElement[] {\n return Array.from(this._instances.values());\n }\n\n getInstance(instance_id: string): MountedElement | undefined {\n return this._instances.get(instance_id);\n }\n\n /** Replace a host-provided complete UI snapshot; never replay imperative actions. */\n async reconcileSnapshot(snapshot: MountedElement[]): Promise<ApplyReport> {\n const rejected = new Set<string>();\n if (this._disposed) return { ok: false, rejectedInstanceIds: [] };\n const active = snapshot.filter(\n (instance) => instance.state === 'active' && instance.element_type !== 'action'\n );\n const ids = new Set(active.map((instance) => instance.instance_id));\n for (const instance of this._instances.values()) {\n if (this._disposed) return { ok: false, rejectedInstanceIds: [...rejected] };\n if (ids.has(instance.instance_id)) continue;\n if (instance.state === 'active') {\n const report = await this.applyWithReport([\n { op: 'unmount', instance_id: instance.instance_id },\n ]);\n if (!report.ok) {\n rejected.add(instance.instance_id);\n continue;\n }\n }\n if (this._disposed) return { ok: false, rejectedInstanceIds: [...rejected] };\n this._instances.delete(instance.instance_id);\n this._dismissedIds.delete(instance.instance_id);\n }\n // Existing containers deduplicate append events. New content must travel\n // through patch so an already-visible FAQ/chip receives its new version.\n for (const instance of active) {\n if (this._disposed) return { ok: false, rejectedInstanceIds: [...rejected] };\n const existing = this._instances.get(instance.instance_id);\n if (!existing || existing.state !== 'active' || existing.version === instance.version)\n continue;\n // A failed delta may have advanced individual instances without advancing\n // the view revision. Only authoritative reconciliation may roll those back.\n const report = await this._applyWithReport(\n [\n {\n op: 'patch',\n instance_id: instance.instance_id,\n patches: [],\n expected_version: existing.version,\n resulting_version: instance.version,\n resulting_content: instance.content,\n },\n ],\n true\n );\n for (const id of report.rejectedInstanceIds) rejected.add(id);\n }\n const report = await this._hydrateWithReport(\n active.filter((instance) => !rejected.has(instance.instance_id)),\n true\n );\n for (const id of report.rejectedInstanceIds) rejected.add(id);\n return { ok: report.ok && rejected.size === 0, rejectedInstanceIds: [...rejected] };\n }\n\n /** Release page-lifetime listeners and timers; queued work becomes inert. */\n dispose(unmount = false): void {\n if (this._disposed || this._disposing) return;\n this._disposing = true;\n if (unmount) {\n for (const instance of this._instances.values()) {\n const handler = this._handlers.get(instance.element_type);\n if (instance.state === 'active' && handler?.lifecycle !== 'imperative') {\n void handler?.unmount(instance, this._ctx()).catch(() => {});\n }\n }\n }\n this._disposed = true;\n this._disposing = false;\n this._unsubscribeReplay?.();\n this._unsubscribeReplay = undefined;\n if (this._ttlTimer !== null) clearTimeout(this._ttlTimer);\n this._ttlTimer = null;\n this._instances.clear();\n this._dismissedIds.clear();\n }\n\n /**\n * Schedule a client-side TTL that unmounts every active stateful\n * instance when the server-side session expires. Replaces any\n * previously-scheduled TTL.\n *\n * Avoids the orphan-window where session-expired tiles linger on the\n * page until manual refresh \u2014 the server forgets at the 24h mark and\n * the SDK tears down concurrently. Re-schedule on each boot fetch /\n * mutation event that carries a refreshed `session_expires_at`.\n *\n * Pass `null` to clear the existing timer without scheduling a new one.\n * Pass a past timestamp to trigger cleanup immediately.\n */\n scheduleClientTtl(session_expires_at: string | null): void {\n if (this._disposed) return;\n if (this._ttlTimer !== null) {\n clearTimeout(this._ttlTimer);\n this._ttlTimer = null;\n }\n if (session_expires_at === null) return;\n\n const expiryMs = Date.parse(session_expires_at);\n if (Number.isNaN(expiryMs)) {\n this._logger.warn('elements: invalid session_expires_at', { session_expires_at });\n return;\n }\n const delay = Math.max(0, expiryMs - Date.now());\n this._ttlTimer = setTimeout(() => {\n this._ttlTimer = null;\n void this._handleSessionExpiry();\n }, delay);\n }\n\n // -------------------------------------------------------------------\n // Internals\n // -------------------------------------------------------------------\n\n private async _handleSessionExpiry(): Promise<void> {\n // Tear down every active stateful instance \u2014 server has already\n // forgotten this session so no further mutations can arrive.\n const unmounts: ElementMutation[] = [];\n for (const instance of this._instances.values()) {\n if (instance.state !== 'active') continue;\n const handler = this._handlers.get(instance.element_type);\n if (!handler || handler.lifecycle === 'imperative') continue;\n unmounts.push({ op: 'unmount', instance_id: instance.instance_id });\n }\n if (unmounts.length > 0) {\n this._logger.info('elements: client TTL fired \u2014 unmounting active instances', {\n count: unmounts.length,\n });\n await this.apply(unmounts);\n }\n }\n\n private async _applyOne(mutation: ElementMutation, authoritative = false): Promise<boolean> {\n if (mutation.op === 'mount') {\n const existing = this._instances.get(mutation.instance.instance_id);\n if (existing && existing.version >= mutation.instance.version) {\n // Already applied (likely the boot snapshot beat the SSE event).\n return true;\n }\n const handler = this._handlers.get(mutation.instance.element_type);\n if (!handler) {\n this._logger.warn('elements: mount dropped \u2014 unknown element_type', {\n element_type: mutation.instance.element_type,\n });\n return false;\n }\n // Active stateful mounts and applied imperative mounts both fire\n // the handler; everything else is a record-only entry (matches\n // hydrate()'s rules).\n const isActiveStateful =\n mutation.instance.state === 'active' && handler.lifecycle !== 'imperative';\n const isAppliedImperative =\n mutation.instance.state === 'applied' && handler.lifecycle === 'imperative';\n if (isActiveStateful || isAppliedImperative) {\n // Commit to the map BEFORE firing the handler \u2014 mirrors hydrate()'s\n // set-before-mount ordering. handler.mount() publishes TILE_MOUNTED\n // SYNCHRONOUSLY, and a subscriber invoked from inside that publish\n // (the chat-bar's inline-widget refresh reads listInlineWidgets())\n // must see this instance. With set-AFTER-mount, that read returned\n // empty and no later event re-fired, so a live chat-inline carousel\n // never reached the trail. If the mount is rejected or throws, roll\n // the map back to its prior entry so a rejected/throwing mount does\n // not leave the map advanced \u2014 the version-dedup guard above would\n // otherwise drop the corrective re-send at the same version.\n this._instances.set(mutation.instance.instance_id, mutation.instance);\n const applied = await this._mount(handler, mutation.instance);\n if (this._disposed) return false;\n if (!applied) {\n if (existing) {\n this._instances.set(mutation.instance.instance_id, existing);\n } else {\n this._instances.delete(mutation.instance.instance_id);\n }\n }\n return applied;\n }\n this._instances.set(mutation.instance.instance_id, mutation.instance);\n return true;\n }\n\n if (mutation.op === 'patch') {\n const existing = this._instances.get(mutation.instance_id);\n if (!existing) {\n this._logger.warn('elements: patch dropped \u2014 unknown instance', {\n instance_id: mutation.instance_id,\n });\n return false;\n }\n // BUG-1783567300: a malformed envelope (backend wire drift \u2014 the\n // deployed backend spent months sending patches WITHOUT\n // resulting_content/resulting_version) must be dropped LOUDLY, never\n // committed. Committing `content: undefined` re-rendered the patched\n // tile as a blank white card; `undefined <= version` is false, so the\n // stale guard alone can't catch it.\n if (\n typeof mutation.resulting_version !== 'number' ||\n mutation.resulting_content == null ||\n typeof mutation.resulting_content !== 'object'\n ) {\n this._logger.warn('elements: patch dropped \u2014 malformed envelope (missing resulting_*)', {\n instance_id: mutation.instance_id,\n });\n return false;\n }\n if (!authoritative && mutation.resulting_version <= existing.version) {\n // Stale patch \u2014 already applied or older than current.\n return true;\n }\n const patched: MountedElement = {\n ...existing,\n content: mutation.resulting_content,\n version: mutation.resulting_version,\n };\n const handler = this._handlers.get(patched.element_type);\n if (handler) {\n const validation = handler.validate(patched, this._ctx());\n if (!validation.accepted) {\n // Critical: do NOT commit to _instances here. The version-dedup\n // guard above would otherwise drop a corrective re-send at the\n // same version, leaving the map permanently divergent from the\n // DOM.\n this._logger.warn('elements: patch validation failed', {\n instance_id: mutation.instance_id,\n reason: validation.reason,\n });\n return false;\n }\n try {\n await handler.patch(patched, this._ctx());\n } catch (err) {\n // Same reasoning as validation failure \u2014 a throwing handler\n // must not leave _instances advanced ahead of the DOM.\n this._logger.warn('elements: handler.patch threw', {\n instance_id: mutation.instance_id,\n err,\n });\n return false;\n }\n }\n if (this._disposed) return false;\n this._instances.set(mutation.instance_id, patched);\n return true;\n }\n\n if (mutation.op === 'unmount') {\n const existing = this._instances.get(mutation.instance_id);\n if (!existing) {\n // Idempotent \u2014 never-knew-it or already unmounted is fine.\n return true;\n }\n const reverted: MountedElement = { ...existing, state: 'reverted' };\n this._instances.set(mutation.instance_id, reverted);\n const handler = this._handlers.get(existing.element_type);\n if (handler) {\n try {\n await handler.unmount(reverted, this._ctx());\n } catch (err) {\n // Keep the pre-handler commit for synchronous event subscribers, but\n // restore the prior state if cleanup fails so projections remain\n // aligned with the still-mounted DOM and a retry remains meaningful.\n if (!this._disposed) this._instances.set(mutation.instance_id, existing);\n this._logger.warn('elements: handler.unmount threw', {\n instance_id: mutation.instance_id,\n err,\n });\n return false;\n }\n }\n return !this._disposed;\n }\n return false;\n }\n\n private async _mount(handler: ElementTypeHandler, instance: MountedElement): Promise<boolean> {\n try {\n const validation = handler.validate(instance, this._ctx());\n if (!validation.accepted) {\n this._logger.warn('elements: mount validation failed', {\n instance_id: instance.instance_id,\n reason: validation.reason,\n detail: validation.detail,\n });\n return false;\n }\n await handler.mount(instance, this._ctx());\n } catch (err) {\n // Mirror the patch branch \u2014 a throwing handler must not leave the\n // map advanced ahead of the DOM, otherwise the dedup guard drops\n // corrective re-sends.\n this._logger.warn('elements: handler.mount threw', {\n instance_id: instance.instance_id,\n err,\n });\n return false;\n }\n return !this._disposed;\n }\n\n private _ctx(): ElementHandlerContext {\n return {\n actions: this._actions,\n events: this._events && {\n publish: (name, props) => {\n if (!this._disposed) this._events?.publish(name, props);\n },\n },\n };\n }\n}\n", "/**\n * Tile element handler \u2014 translates `slot`-placed mount/patch/unmount\n * mutations into pub/sub events that the runtime SDK subscribes to.\n *\n * The runtime SDK's `SmartCanvasElementLit` already routes config-driven\n * tiles to inline slots via `tile.slot === slotName`. LLM-authored tiles\n * use the same path: we publish a TileConfig-shaped envelope and the\n * runtime merges it into its tile array, then existing routing renders\n * it inside the slot's `<syntro-tile-stack>` alongside any config tiles.\n *\n * Why pub/sub instead of `core:mountWidget` via ActionEngine?\n *\n * - `Surfaces.mount` is exclusive per slot \u2014 a second LLM tile to the\n * same slot would displace the first.\n * - `Surfaces` only knows built-in slot names; inline slots resolve\n * anchors via SyntroInlineSlot, not via Surfaces.\n * - Tiles need the SyntroTileCard chrome (chromeless handling, theme\n * overrides, dismiss control) that `core:mountWidget` skips.\n *\n * Container widgets stay dumb: the runtime owns one tile array per slot\n * and renders it the same way regardless of provenance. Subscribers\n * filter on `slot === this.name`.\n *\n * Event names + payload types live in `@syntrologie/sdk-contracts` so\n * both packages share the contract. Renaming a field there is a TS\n * error here and in the runtime subscriber \u2014 no silent drift.\n */\n\nimport {\n TILE_MOUNTED_EVENT,\n TILE_PATCHED_EVENT,\n TILE_UNMOUNTED_EVENT,\n type TileLifecycleUnmountProps,\n} from '@syntrologie/sdk-contracts';\n\nimport type {\n ElementHandlerContext,\n ElementTypeHandler,\n ValidateResult,\n} from './ElementTypeHandler';\nimport { tileMountProps } from './tileLifecycleProps';\nimport type { MountedElement, SlotPlacement } from './types';\n\nexport {\n TILE_MOUNTED_EVENT,\n TILE_PATCHED_EVENT,\n TILE_UNMOUNTED_EVENT,\n} from '@syntrologie/sdk-contracts';\n\n/**\n * Optional template\u2192widget resolver. ``uiTemplates`` declares both\n * the LLM-facing ``id`` (e.g. ``\"product-card\"``) and the runtime\n * widget the chrome should render (e.g. ``\"adaptive-product:card\"``).\n * Without this resolver, the handler falls back to ``template_id`` as\n * the widget id \u2014 which only works when the admin chose ids matching\n * the widget registry, and silently mounts to \"Widget not available\"\n * when they don't.\n */\nexport type TileTemplateWidgetResolver = (templateId: string) => string | undefined;\n\nexport class TileHandler implements ElementTypeHandler {\n readonly typeName = 'tile' as const;\n readonly lifecycle = 'stateful' as const;\n\n constructor(private readonly resolveWidget?: TileTemplateWidgetResolver) {}\n\n private _widgetFor(instance: MountedElement): string {\n return this.resolveWidget?.(instance.template_id) ?? instance.template_id;\n }\n\n validate(instance: MountedElement, _ctx: ElementHandlerContext): ValidateResult {\n if (instance.element_type !== 'tile') {\n return { accepted: false, reason: 'wrong_element_type', detail: instance.element_type };\n }\n if (instance.placement.type !== 'slot') {\n return {\n accepted: false,\n reason: 'tile_requires_slot_placement',\n detail: `got placement.type=${instance.placement.type}`,\n };\n }\n if (typeof instance.template_id !== 'string' || instance.template_id.length === 0) {\n return { accepted: false, reason: 'missing_template_id' };\n }\n // When a resolver is configured, the template_id MUST resolve to a known\n // widget. Falling back to the raw template_id as a widget id (see\n // `_widgetFor`) mounts to a widget the registry will never have, which\n // renders as a permanently blank card in the canvas deck (the sc-mount\n // body stays empty). Reject loudly instead so the mount never enters the\n // tile pipeline \u2014 matching the repo's \"fail loudly, no silent defaults\"\n // rule \u2014 which surfaces the unknown template_id back to the caller.\n if (this.resolveWidget && this.resolveWidget(instance.template_id) === undefined) {\n return { accepted: false, reason: 'unknown_template_id', detail: instance.template_id };\n }\n return { accepted: true };\n }\n\n async mount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void> {\n if (!ctx.events) return;\n const placement = instance.placement as SlotPlacement;\n const props = tileMountProps({ ...instance, placement }, this._widgetFor(instance));\n ctx.events.publish(TILE_MOUNTED_EVENT, props as unknown as Record<string, unknown>);\n }\n\n async patch(instance: MountedElement, ctx: ElementHandlerContext): Promise<void> {\n if (!ctx.events) return;\n const placement = instance.placement as SlotPlacement;\n const props = tileMountProps({ ...instance, placement }, this._widgetFor(instance));\n ctx.events.publish(TILE_PATCHED_EVENT, props as unknown as Record<string, unknown>);\n }\n\n async unmount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void> {\n if (!ctx.events) return;\n const placement = instance.placement as SlotPlacement;\n const props: TileLifecycleUnmountProps = {\n slot: placement.slot,\n instance_id: instance.instance_id,\n };\n ctx.events.publish(TILE_UNMOUNTED_EVENT, props as unknown as Record<string, unknown>);\n }\n}\n", "/**\n * Action element handler \u2014 dispatches an ActionStep through\n * `ActionEngine.applyBatch`.\n *\n * The `content` field of an action `MountedElement` carries the action's\n * params; the executor `kind` comes from the template (which the SDK\n * doesn't have locally yet \u2014 for v1 the template's `kind` is encoded\n * into `content.__kind`, set by the backend tool). When proper\n * template loading lands on the SDK side, this handler resolves the\n * kind from the loaded template instead.\n *\n * **Imperative actions never re-fire on boot.** The `lifecycle` field\n * here is `imperative` \u2014 `ElementInstanceStore` skips boot rehydrate\n * for imperative instances with `state === 'applied'`. The backend\n * record exists for audit; replaying a scroll/navigate on every page\n * load would be visibly wrong.\n *\n * Stateful actions (e.g. `content:insertHtml`, `content:setText`,\n * `content:addClass` once Phase 7's renderer lands) ARE re-applied on\n * boot. v1 supports the imperative shape; stateful action handling\n * waits on the Portable Text renderer.\n */\n\nimport type { ActionStep } from '../types';\nimport type {\n ElementHandlerContext,\n ElementTypeHandler,\n ValidateResult,\n} from './ElementTypeHandler';\nimport type { MountedElement } from './types';\n\nexport class ActionHandler implements ElementTypeHandler {\n readonly typeName = 'action' as const;\n // v1: only imperative actions are wired. Stateful content-mutation\n // actions arrive with Phase 7 (Portable Text renderer).\n readonly lifecycle = 'imperative' as const;\n\n validate(instance: MountedElement, _ctx: ElementHandlerContext): ValidateResult {\n if (instance.element_type !== 'action') {\n return { accepted: false, reason: 'wrong_element_type' };\n }\n const kind = (instance.content as { __kind?: unknown }).__kind;\n if (typeof kind !== 'string' || kind.length === 0) {\n return { accepted: false, reason: 'missing_action_kind' };\n }\n return { accepted: true };\n }\n\n async mount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void> {\n const action = this._actionFromInstance(instance);\n await ctx.actions.applyBatch([action]);\n }\n\n async patch(_instance: MountedElement, _ctx: ElementHandlerContext): Promise<void> {\n // Stateful actions re-apply on patch. Imperative actions don't\n // support patch (the backend rejects), but if one slips through\n // we treat it as a no-op rather than crash.\n }\n\n async unmount(_instance: MountedElement, _ctx: ElementHandlerContext): Promise<void> {\n // Imperative actions can't be unmounted \u2014 the scroll/navigate\n // already happened. The store calls this for completeness; no-op is\n // correct. Stateful actions (Phase 7) will need real reverter logic.\n }\n\n private _actionFromInstance(instance: MountedElement): ActionStep {\n const { __kind, ...params } = instance.content as { __kind: string } & Record<string, unknown>;\n // Selector-targeted templates (e.g. `content:setText`) declare their\n // anchor via the template's `allowed_targets`, which lands on the\n // mounted instance as `placement: {type: \"selector\", selector, position}`.\n // The runtime executors expect a full `AnchorId` object on the\n // action params, so translate placement \u2192 anchorId here. Imperative\n // navigation actions use `placement: {type: \"none\"}` and skip this.\n const placement = instance.placement;\n const anchorOverride: Partial<Record<string, unknown>> =\n placement.type === 'selector'\n ? { anchorId: { selector: placement.selector, route: '**' } }\n : {};\n // Imperative templates (e.g. `navigation:scrollTo`) declare the\n // anchor as a string `EnumField` named `anchorId` in the template\n // fields \u2014 that's the only field shape the agent can author, since\n // EnumField is string-only. The runtime executor still wants an\n // `AnchorId` object (`{selector, route}`), so wrap any string we\n // receive on `anchorId` here. Without this wrap the executor's\n // `resolveAnchor` returns null on a non-object and silently\n // refuses to scroll. `route: '**'` matches every page \u2014 the\n // agent's enum already enumerates the legal selectors, so the\n // route check would be redundant.\n if (typeof params.anchorId === 'string') {\n params.anchorId = { selector: params.anchorId, route: '**' };\n }\n return {\n kind: __kind,\n ...params,\n ...anchorOverride,\n label: `action:${instance.template_id}:${instance.instance_id}`,\n } as unknown as ActionStep;\n }\n}\n", "/**\n * Item element handler \u2014 translates `parent_tile`-placed mount/patch/\n * unmount mutations into pub/sub events that container widgets subscribe\n * to.\n *\n * Container widgets (chips strip, FAQ accordion, nav tips) declare\n * themselves participants by implementing the `CompositionalContainer`\n * contract and subscribing to `runtime.events` with\n * `element.compositional_append` / `element.compositional_patch` /\n * `element.compositional_remove`. Each container filters by\n * `props.tile_id === this.tileId`.\n *\n * Why pub/sub rather than DOM lookup? The runtime's event bus is\n * domain-agnostic and already exists. Container widgets that aren't\n * mounted yet (slow inline-slot wrapper hydration) miss the event,\n * but the next subscribe-then-replay pass at container mount time picks\n * up the durable session state from the LLM's `mounted_elements`. No\n * DOM crawling, no MutationObserver wiring, no ordering assumption.\n *\n * The handler is dumb: it forwards the wire `content` as the `item`\n * payload. The backend wrote that content as a fully-formed\n * compositional-item envelope (`{kind, config: {id, title, payload}}`)\n * so the receiving container can append it verbatim.\n */\n\nimport type {\n ElementHandlerContext,\n ElementTypeHandler,\n ValidateResult,\n} from './ElementTypeHandler';\nimport type { MountedElement, ParentTilePlacement } from './types';\n\n/** Event names. Kept as module constants so subscribers can import them\n * instead of memorizing strings. */\nexport const COMPOSITIONAL_APPEND_EVENT = 'element.compositional_append';\nexport const COMPOSITIONAL_PATCH_EVENT = 'element.compositional_patch';\nexport const COMPOSITIONAL_REMOVE_EVENT = 'element.compositional_remove';\n\nexport interface CompositionalAppendEvent {\n /** Parent tile id from the mount's placement. Receivers filter on this. */\n tile_id: string;\n /** Unique id of the mounted item \u2014 the receiving container should also\n * set this on the item's DOM as `data-syntro-element-id`. */\n instance_id: string;\n /** Wire shape constructed by the backend \u2014 `{kind, config: {\u2026}}`. */\n item: Record<string, unknown>;\n /** Insertion position \u2014 receivers honor this when appending. */\n position: 'append' | 'prepend';\n}\n\nexport interface CompositionalPatchEvent {\n tile_id: string;\n instance_id: string;\n item: Record<string, unknown>;\n}\n\nexport interface CompositionalRemoveEvent {\n tile_id: string;\n instance_id: string;\n}\n\nexport class ItemHandler implements ElementTypeHandler {\n readonly typeName = 'item' as const;\n readonly lifecycle = 'stateful' as const;\n\n validate(instance: MountedElement, _ctx: ElementHandlerContext): ValidateResult {\n if (instance.element_type !== 'item') {\n return {\n accepted: false,\n reason: 'wrong_element_type',\n detail: instance.element_type,\n };\n }\n if (instance.placement.type !== 'parent_tile') {\n return {\n accepted: false,\n reason: 'item_requires_parent_tile_placement',\n detail: `got placement.type=${instance.placement.type}`,\n };\n }\n if (typeof instance.template_id !== 'string' || instance.template_id.length === 0) {\n return { accepted: false, reason: 'missing_template_id' };\n }\n return { accepted: true };\n }\n\n async mount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void> {\n if (!ctx.events) return; // no bus \u2192 no-op, validate already passed\n const placement = instance.placement as ParentTilePlacement;\n ctx.events.publish(COMPOSITIONAL_APPEND_EVENT, {\n tile_id: placement.tile_id,\n instance_id: instance.instance_id,\n item: instance.content,\n position: placement.position,\n } satisfies CompositionalAppendEvent as unknown as Record<string, unknown>);\n }\n\n async patch(instance: MountedElement, ctx: ElementHandlerContext): Promise<void> {\n if (!ctx.events) return;\n const placement = instance.placement as ParentTilePlacement;\n ctx.events.publish(COMPOSITIONAL_PATCH_EVENT, {\n tile_id: placement.tile_id,\n instance_id: instance.instance_id,\n item: instance.content,\n } satisfies CompositionalPatchEvent as unknown as Record<string, unknown>);\n }\n\n async unmount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void> {\n if (!ctx.events) return;\n const placement = instance.placement as ParentTilePlacement;\n ctx.events.publish(COMPOSITIONAL_REMOVE_EVENT, {\n tile_id: placement.tile_id,\n instance_id: instance.instance_id,\n } satisfies CompositionalRemoveEvent as unknown as Record<string, unknown>);\n }\n}\n", "import type { ChatbotWidgetRuntime } from '../types';\nimport { ActionHandler } from './ActionHandler';\nimport { ElementInstanceStore } from './ElementInstanceStore';\nimport { ItemHandler } from './ItemHandler';\nimport { TileHandler, type TileTemplateWidgetResolver } from './TileHandler';\n\n/** Independent template state shared by live mounts and replay in one surface. */\nexport function createTemplateWidgetResolver() {\n const widgets = new Map<string, string>();\n return {\n resolve: (templateId: string): string | undefined => widgets.get(templateId),\n refresh(uiTemplates: Record<string, unknown> | undefined): void {\n widgets.clear();\n // Backend-originated recommendation tiles are valid without an admin alias.\n widgets.set('syntro-product-reco-carousel', 'syntro-product-reco-carousel');\n const tiles = uiTemplates?.tiles;\n if (!Array.isArray(tiles)) return;\n for (const template of tiles) {\n if (\n typeof template?.id === 'string' &&\n template.id &&\n typeof template?.widget === 'string' &&\n template.widget\n ) {\n widgets.set(template.id, template.widget);\n }\n }\n },\n };\n}\n\n/** UI-free store construction; the owning surface controls its lifetime. */\nexport function createElementStore(\n runtime: Pick<ChatbotWidgetRuntime, 'actions' | 'events'>,\n resolveTileWidget: TileTemplateWidgetResolver\n): ElementInstanceStore {\n return new ElementInstanceStore({\n actions: runtime.actions,\n events: {\n publish: runtime.events.publish.bind(runtime.events),\n subscribe: runtime.events.subscribe?.bind(runtime.events),\n },\n handlers: [new TileHandler(resolveTileWidget), new ActionHandler(), new ItemHandler()],\n resolveTileWidget,\n });\n}\n", "import type { SnapshotFrame } from '@syntrologie/runtime-contracts';\nimport type { ChatbotWidgetRuntime } from '../types';\nimport { createElementStore, createTemplateWidgetResolver } from './createElementStore';\nimport type { ElementInstanceStore } from './ElementInstanceStore';\nimport type { MountedElement, MountedElementsResponse, MutationEnvelope } from './types';\n\nexport type ApplyOutcome =\n | { status: 'applied' | 'duplicate'; resultId: string }\n | { status: 'rejected'; reason: 'invalid' | 'wrong_scope' | 'disposed' | 'projection_failed' };\n\nexport interface ElementSessionController {\n configureTemplates(templates: Record<string, unknown> | undefined): void;\n acceptHostFrame(payload: unknown): Promise<ApplyOutcome>;\n applyLegacyEnvelope(envelope: MutationEnvelope): Promise<void>;\n hydrateLegacy(instances: MountedElement[], expiresAt: string | null): Promise<void>;\n dispose(): Promise<void>;\n}\n\nexport interface ElementSessionOptions extends Pick<ChatbotWidgetRuntime, 'actions' | 'events'> {\n templates?: Record<string, unknown>;\n /** The older standalone assistant accepted template IDs as widget IDs. */\n legacyTemplateFallback?: boolean;\n /** The one conversation this owner renders. A frame for any other is refused. */\n hostScope?: { sessionRef: string };\n}\n\n/** Compatibility accessors belong to this owner, never a module-wide store. */\nexport interface OwnedElementSession extends ElementSessionController {\n readonly store: ElementInstanceStore;\n resolveTileWidget(templateId: string): string | undefined;\n whenIdle(): Promise<void>;\n hydrateOnce(load: () => Promise<MountedElementsResponse | null>): void;\n listen(target: EventTarget, name: string, listener: EventListener): () => void;\n}\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\nfunction text(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\nfunction integer(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;\n}\nfunction expiry(value: unknown): value is string | null {\n return value === null || (typeof value === 'string' && Number.isFinite(Date.parse(value)));\n}\n\n/** Validate boundary inputs against generated contracts before any action runs. */\nfunction instance(value: unknown): value is MountedElement {\n if (\n !record(value) ||\n !text(value.instance_id) ||\n !text(value.template_id) ||\n !record(value.content) ||\n !record(value.placement) ||\n !record(value.mounted_by) ||\n !text(value.mounted_by.agent_id) ||\n !text(value.mounted_by.turn_id) ||\n typeof value.mounted_at !== 'string' ||\n !Number.isFinite(Date.parse(value.mounted_at)) ||\n !integer(value.version) ||\n value.version < 1 ||\n typeof value.state !== 'string' ||\n !['active', 'applied', 'reverted', 'expired'].includes(value.state)\n )\n return false;\n if (value.element_type === 'tile')\n return value.placement.type === 'slot' && text(value.placement.slot);\n if (value.element_type === 'item')\n return (\n value.placement.type === 'parent_tile' &&\n text(value.placement.tile_id) &&\n typeof value.placement.position === 'string' &&\n ['append', 'prepend'].includes(value.placement.position)\n );\n if (value.element_type !== 'action') return false;\n return (\n value.placement.type === 'none' ||\n (value.placement.type === 'selector' &&\n text(value.placement.selector) &&\n typeof value.placement.position === 'string' &&\n ['beforebegin', 'afterbegin', 'beforeend', 'afterend', 'replace', 'modify'].includes(\n value.placement.position\n ))\n );\n}\n\nfunction frame(value: unknown): value is SnapshotFrame & { mounted_elements: MountedElement[] } {\n if (\n !record(value) ||\n value.schema_version !== 'v1' ||\n value.kind !== 'snapshot' ||\n !text(value.session_ref) ||\n !text(value.result_id) ||\n !expiry(value.session_expires_at) ||\n !Array.isArray(value.mounted_elements) ||\n !value.mounted_elements.every(instance)\n )\n return false;\n return (\n new Set(value.mounted_elements.map((item) => item.instance_id)).size ===\n value.mounted_elements.length\n );\n}\n\n/**\n * How many applied turn ids this owner remembers.\n *\n * A host replays the tool results it still holds whenever the card remounts,\n * and it does not promise an order, so an id has to stay refused after newer\n * ones have landed. 32 covers any replay window a host actually has; past\n * that the oldest is forgotten, and a turn that far back is one no host is\n * still holding.\n */\nconst APPLIED_HISTORY = 32;\n\n/** The UI-independent owner for ordered projection, replay and teardown. */\nexport function createElementSessionController(\n options: ElementSessionOptions\n): OwnedElementSession {\n const templates = createTemplateWidgetResolver();\n templates.refresh(options.templates);\n const resolveTileWidget = (id: string): string | undefined =>\n templates.resolve(id) ??\n (options.legacyTemplateFallback && !options.hostScope ? id : undefined);\n const store = createElementStore(options, resolveTileWidget);\n const cleanups = new Set<() => void>();\n const scope = options.hostScope;\n let generation = 0;\n let disposed = false;\n let hydrated = false;\n let pending: Promise<void> = Promise.resolve();\n let lastAppliedResultId: string | undefined;\n /** Every result id this owner has applied, newest last, capped. */\n const appliedResultIds = new Set<string>();\n\n function enqueue<T>(operation: () => Promise<T>): Promise<T> {\n const next = pending.then(operation);\n pending = next.then(\n () => {},\n () => {}\n );\n return next;\n }\n const rejected = (\n reason: Extract<ApplyOutcome, { status: 'rejected' }>['reason']\n ): ApplyOutcome => ({ status: 'rejected', reason });\n\n return {\n store,\n resolveTileWidget,\n whenIdle: () => pending,\n configureTemplates(next) {\n if (!disposed) templates.refresh(next);\n },\n acceptHostFrame(payload) {\n return enqueue(async () => {\n if (disposed) return rejected('disposed');\n if (!frame(payload)) return rejected('invalid');\n if (!scope || payload.session_ref !== scope.sessionRef) return rejected('wrong_scope');\n // The whole of the dedup. A frame is a complete snapshot, so a turn\n // this owner has already applied has nothing left to say: not the one\n // on screen arriving a second time down the other delivery channel,\n // and not an older one the host re-pushes after a newer turn landed,\n // which would otherwise roll the card back or empty it.\n if (payload.result_id === lastAppliedResultId || appliedResultIds.has(payload.result_id))\n return { status: 'duplicate', resultId: payload.result_id };\n const report = await store.reconcileSnapshot(payload.mounted_elements);\n if (disposed) return rejected('disposed');\n // Nothing to recover from and nothing to ask for: the next snapshot\n // the host delivers is already the whole truth.\n if (!report.ok) return rejected('projection_failed');\n lastAppliedResultId = payload.result_id;\n appliedResultIds.add(payload.result_id);\n if (appliedResultIds.size > APPLIED_HISTORY) {\n appliedResultIds.delete(appliedResultIds.values().next().value as string);\n }\n store.scheduleClientTtl(payload.session_expires_at);\n return { status: 'applied', resultId: payload.result_id };\n });\n },\n applyLegacyEnvelope(envelope) {\n return enqueue(async () => {\n if (!disposed) await store.apply(envelope.mutations);\n });\n },\n hydrateLegacy(instances, expiresAt) {\n return enqueue(async () => {\n if (disposed) return;\n await store.hydrate(instances);\n if (!disposed) store.scheduleClientTtl(expiresAt);\n });\n },\n hydrateOnce(load) {\n if (hydrated || disposed) return;\n hydrated = true;\n const started = generation;\n void load()\n .then((response) => {\n if (disposed || started !== generation || !response) return;\n return enqueue(async () => {\n if (disposed || started !== generation) return;\n await store.hydrate(response.mounted_elements);\n if (!disposed) store.scheduleClientTtl(response.session_expires_at);\n });\n })\n .catch(() => {});\n },\n listen(target, name, listener) {\n if (disposed) return () => {};\n target.addEventListener(name, listener);\n const remove = () => {\n target.removeEventListener(name, listener);\n cleanups.delete(remove);\n };\n cleanups.add(remove);\n return remove;\n },\n dispose() {\n if (!disposed) {\n disposed = true;\n generation++;\n for (const cleanup of cleanups) cleanup();\n store.dispose(true);\n templates.refresh(undefined);\n }\n return pending;\n },\n };\n}\n"],
5
- "mappings": "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",
6
- "names": ["instance", "instance", "report", "instance", "instance", "COMPOSITIONAL_APPEND_EVENT", "instance"]
7
- }