@aparte/core 0.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/dist/client/aparte-client.d.ts +326 -0
  4. package/dist/client/aparte-client.d.ts.map +1 -0
  5. package/dist/client/stream-adapter.d.ts +197 -0
  6. package/dist/client/stream-adapter.d.ts.map +1 -0
  7. package/dist/components/bubble/aparte-chat-bubble.d.ts +153 -0
  8. package/dist/components/bubble/aparte-chat-bubble.d.ts.map +1 -0
  9. package/dist/components/bubble/bubble-sync.d.ts +44 -0
  10. package/dist/components/bubble/bubble-sync.d.ts.map +1 -0
  11. package/dist/components/bubble/index.d.ts +4 -0
  12. package/dist/components/bubble/index.d.ts.map +1 -0
  13. package/dist/components/chat/aparte-chat.d.ts +49 -0
  14. package/dist/components/chat/aparte-chat.d.ts.map +1 -0
  15. package/dist/components/chat/index.d.ts +2 -0
  16. package/dist/components/chat/index.d.ts.map +1 -0
  17. package/dist/components/composer/aparte-composer-action.d.ts +37 -0
  18. package/dist/components/composer/aparte-composer-action.d.ts.map +1 -0
  19. package/dist/components/composer/aparte-composer-add-attachment.d.ts +27 -0
  20. package/dist/components/composer/aparte-composer-add-attachment.d.ts.map +1 -0
  21. package/dist/components/composer/aparte-composer-attachments.d.ts +26 -0
  22. package/dist/components/composer/aparte-composer-attachments.d.ts.map +1 -0
  23. package/dist/components/composer/aparte-composer-cancel.d.ts +21 -0
  24. package/dist/components/composer/aparte-composer-cancel.d.ts.map +1 -0
  25. package/dist/components/composer/aparte-composer-input.d.ts +63 -0
  26. package/dist/components/composer/aparte-composer-input.d.ts.map +1 -0
  27. package/dist/components/composer/aparte-composer-send.d.ts +25 -0
  28. package/dist/components/composer/aparte-composer-send.d.ts.map +1 -0
  29. package/dist/components/composer/aparte-composer.d.ts +150 -0
  30. package/dist/components/composer/aparte-composer.d.ts.map +1 -0
  31. package/dist/components/composer/index.d.ts +9 -0
  32. package/dist/components/composer/index.d.ts.map +1 -0
  33. package/dist/components/conversation-list/aparte-conversation-list.d.ts +50 -0
  34. package/dist/components/conversation-list/aparte-conversation-list.d.ts.map +1 -0
  35. package/dist/components/conversation-list/index.d.ts +2 -0
  36. package/dist/components/conversation-list/index.d.ts.map +1 -0
  37. package/dist/components/elicitation/aparte-elicitation.d.ts +29 -0
  38. package/dist/components/elicitation/aparte-elicitation.d.ts.map +1 -0
  39. package/dist/components/index.d.ts +10 -0
  40. package/dist/components/index.d.ts.map +1 -0
  41. package/dist/components/status/aparte-chat-status.d.ts +34 -0
  42. package/dist/components/status/aparte-chat-status.d.ts.map +1 -0
  43. package/dist/components/status/index.d.ts +2 -0
  44. package/dist/components/status/index.d.ts.map +1 -0
  45. package/dist/components/viewport/aparte-chat-viewport.d.ts +358 -0
  46. package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -0
  47. package/dist/components/viewport/index.d.ts +2 -0
  48. package/dist/components/viewport/index.d.ts.map +1 -0
  49. package/dist/config/action-provider.d.ts +59 -0
  50. package/dist/config/action-provider.d.ts.map +1 -0
  51. package/dist/config/aparte-config.d.ts +490 -0
  52. package/dist/config/aparte-config.d.ts.map +1 -0
  53. package/dist/config/attachment-renderer.d.ts +28 -0
  54. package/dist/config/attachment-renderer.d.ts.map +1 -0
  55. package/dist/config/avatar-provider.d.ts +35 -0
  56. package/dist/config/avatar-provider.d.ts.map +1 -0
  57. package/dist/config/bubble-shell-renderer.d.ts +41 -0
  58. package/dist/config/bubble-shell-renderer.d.ts.map +1 -0
  59. package/dist/config/config-context.d.ts +47 -0
  60. package/dist/config/config-context.d.ts.map +1 -0
  61. package/dist/config/error-renderer.d.ts +26 -0
  62. package/dist/config/error-renderer.d.ts.map +1 -0
  63. package/dist/config/icon-provider.d.ts +63 -0
  64. package/dist/config/icon-provider.d.ts.map +1 -0
  65. package/dist/config/index.d.ts +19 -0
  66. package/dist/config/index.d.ts.map +1 -0
  67. package/dist/config/locale.d.ts +60 -0
  68. package/dist/config/locale.d.ts.map +1 -0
  69. package/dist/config/sanitize.d.ts +31 -0
  70. package/dist/config/sanitize.d.ts.map +1 -0
  71. package/dist/config/sibling-nav-renderer.d.ts +27 -0
  72. package/dist/config/sibling-nav-renderer.d.ts.map +1 -0
  73. package/dist/config/skeleton-provider.d.ts +26 -0
  74. package/dist/config/skeleton-provider.d.ts.map +1 -0
  75. package/dist/config/status-renderer.d.ts +23 -0
  76. package/dist/config/status-renderer.d.ts.map +1 -0
  77. package/dist/conversations/conversation-controller.d.ts +126 -0
  78. package/dist/conversations/conversation-controller.d.ts.map +1 -0
  79. package/dist/conversations/conversation-manager.d.ts +98 -0
  80. package/dist/conversations/conversation-manager.d.ts.map +1 -0
  81. package/dist/conversations/index.d.ts +5 -0
  82. package/dist/conversations/index.d.ts.map +1 -0
  83. package/dist/conversations/types.d.ts +172 -0
  84. package/dist/conversations/types.d.ts.map +1 -0
  85. package/dist/custom-elements.json +15833 -0
  86. package/dist/elicitation/index.d.ts +20 -0
  87. package/dist/elicitation/index.d.ts.map +1 -0
  88. package/dist/elicitation/panel.d.ts +18 -0
  89. package/dist/elicitation/panel.d.ts.map +1 -0
  90. package/dist/elicitation/types.d.ts +98 -0
  91. package/dist/elicitation/types.d.ts.map +1 -0
  92. package/dist/host/aparte-chat-host.d.ts +176 -0
  93. package/dist/host/aparte-chat-host.d.ts.map +1 -0
  94. package/dist/host/index.d.ts +2 -0
  95. package/dist/host/index.d.ts.map +1 -0
  96. package/dist/index-D89k-n2Q.js +6294 -0
  97. package/dist/index-D89k-n2Q.js.map +1 -0
  98. package/dist/index.css +2554 -0
  99. package/dist/index.d.ts +62 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +3763 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/index.node.d.ts +53 -0
  104. package/dist/index.node.d.ts.map +1 -0
  105. package/dist/index.node.js +49 -0
  106. package/dist/index.node.js.map +1 -0
  107. package/dist/interop/element-props.d.ts +23 -0
  108. package/dist/interop/element-props.d.ts.map +1 -0
  109. package/dist/parsers/aparte-event-stream.d.ts +13 -0
  110. package/dist/parsers/aparte-event-stream.d.ts.map +1 -0
  111. package/dist/parsers/aparte-stream-parser.d.ts +137 -0
  112. package/dist/parsers/aparte-stream-parser.d.ts.map +1 -0
  113. package/dist/parsers/index.d.ts +4 -0
  114. package/dist/parsers/index.d.ts.map +1 -0
  115. package/dist/primitives/index.d.ts +8 -0
  116. package/dist/primitives/index.d.ts.map +1 -0
  117. package/dist/primitives/progress-spinner/aparte-progress-spinner.d.ts +25 -0
  118. package/dist/primitives/progress-spinner/aparte-progress-spinner.d.ts.map +1 -0
  119. package/dist/primitives/progress-spinner/index.d.ts +2 -0
  120. package/dist/primitives/progress-spinner/index.d.ts.map +1 -0
  121. package/dist/primitives/select/aparte-optgroup.d.ts +32 -0
  122. package/dist/primitives/select/aparte-optgroup.d.ts.map +1 -0
  123. package/dist/primitives/select/aparte-option.d.ts +30 -0
  124. package/dist/primitives/select/aparte-option.d.ts.map +1 -0
  125. package/dist/primitives/select/aparte-select.d.ts +74 -0
  126. package/dist/primitives/select/aparte-select.d.ts.map +1 -0
  127. package/dist/primitives/select/index.d.ts +9 -0
  128. package/dist/primitives/select/index.d.ts.map +1 -0
  129. package/dist/renderers/index.d.ts +5 -0
  130. package/dist/renderers/index.d.ts.map +1 -0
  131. package/dist/renderers/segment-renderers.d.ts +31 -0
  132. package/dist/renderers/segment-renderers.d.ts.map +1 -0
  133. package/dist/runtime/message-repository.d.ts +111 -0
  134. package/dist/runtime/message-repository.d.ts.map +1 -0
  135. package/dist/transport/backend-handler.d.ts +50 -0
  136. package/dist/transport/backend-handler.d.ts.map +1 -0
  137. package/dist/transport/backend-transport.d.ts +39 -0
  138. package/dist/transport/backend-transport.d.ts.map +1 -0
  139. package/dist/transport/direct-transport.d.ts +36 -0
  140. package/dist/transport/direct-transport.d.ts.map +1 -0
  141. package/dist/transport/index.d.ts +6 -0
  142. package/dist/transport/index.d.ts.map +1 -0
  143. package/dist/transport/types.d.ts +73 -0
  144. package/dist/transport/types.d.ts.map +1 -0
  145. package/dist/types/chat.d.ts +223 -0
  146. package/dist/types/chat.d.ts.map +1 -0
  147. package/dist/types/errors.d.ts +25 -0
  148. package/dist/types/errors.d.ts.map +1 -0
  149. package/dist/types/event-map.d.ts +41 -0
  150. package/dist/types/event-map.d.ts.map +1 -0
  151. package/dist/types/events.d.ts +290 -0
  152. package/dist/types/events.d.ts.map +1 -0
  153. package/dist/types/imperative-api.d.ts +38 -0
  154. package/dist/types/imperative-api.d.ts.map +1 -0
  155. package/dist/types/index.d.ts +22 -0
  156. package/dist/types/index.d.ts.map +1 -0
  157. package/dist/types/model-provider.d.ts +229 -0
  158. package/dist/types/model-provider.d.ts.map +1 -0
  159. package/dist/types/models.d.ts +173 -0
  160. package/dist/types/models.d.ts.map +1 -0
  161. package/dist/types/parsers.d.ts +42 -0
  162. package/dist/types/parsers.d.ts.map +1 -0
  163. package/dist/types/providers.d.ts +93 -0
  164. package/dist/types/providers.d.ts.map +1 -0
  165. package/dist/types/segments.d.ts +221 -0
  166. package/dist/types/segments.d.ts.map +1 -0
  167. package/dist/types/theming.d.ts +44 -0
  168. package/dist/types/theming.d.ts.map +1 -0
  169. package/dist/types/tools.d.ts +98 -0
  170. package/dist/types/tools.d.ts.map +1 -0
  171. package/dist/utils/assert-never.d.ts +9 -0
  172. package/dist/utils/assert-never.d.ts.map +1 -0
  173. package/dist/utils/css-escape.d.ts +15 -0
  174. package/dist/utils/css-escape.d.ts.map +1 -0
  175. package/package.json +68 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-D89k-n2Q.js","sources":["../src/types/chat.ts","../src/types/errors.ts","../src/config/icon-provider.ts","../src/config/locale.ts","../src/transport/types.ts","../src/transport/direct-transport.ts","../src/parsers/aparte-stream-parser.ts","../src/parsers/aparte-event-stream.ts","../src/transport/backend-transport.ts","../src/transport/backend-handler.ts","../src/config/sanitize.ts","../src/config/aparte-config.ts","../src/config/config-context.ts","../src/config/skeleton-provider.ts","../src/renderers/segment-renderers.ts","../src/components/bubble/bubble-sync.ts","../src/conversations/types.ts","../src/conversations/conversation-manager.ts","../src/conversations/conversation-controller.ts","../src/host/aparte-chat-host.ts","../src/utils/assert-never.ts","../src/client/stream-adapter.ts","../src/client/aparte-client.ts","../src/runtime/message-repository.ts","../src/interop/element-props.ts","../src/elicitation/panel.ts","../src/elicitation/index.ts"],"sourcesContent":["/**\n * Aparte Chat Request & Stream Types\n */\n\nimport type { AparteTool, AparteToolCall } from './tools.js';\nimport type { AparteSegment } from './segments.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Content Parts — multimodal message content (Vercel AI SDK compatible)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Plain text content part */\nexport interface AparteTextPart {\n type: 'text';\n text: string;\n}\n\n/**\n * Image content part.\n * `image` must be a base64 data URL: `data:image/png;base64,...`\n * The data URL encodes the MIME type, making it consumable by all providers.\n */\nexport interface AparteImagePart {\n type: 'image';\n image: string;\n mimeType?: string;\n}\n\n/**\n * File content part — reserved for future PDF/audio support.\n * `data` must be a base64 data URL.\n */\nexport interface AparteFilePart {\n type: 'file';\n data: string;\n mimeType: string;\n name?: string;\n}\n\n/** Discriminated union of all content part types */\nexport type AparteContentPart = AparteTextPart | AparteImagePart | AparteFilePart;\n\n/**\n * Extract plain text from a `string | AparteContentPart[]` content value.\n * Use this in providers and app-layer code whenever you need the text-only representation.\n */\nexport function contentToText(content: string | AparteContentPart[]): string {\n if (typeof content === 'string') return content;\n return content\n .filter((p): p is AparteTextPart => p.type === 'text')\n .map(p => p.text)\n .join('');\n}\n\nexport interface AparteChatMessage {\n role: 'user' | 'assistant' | 'system' | 'tool_call' | 'tool_result';\n /**\n * Message content — either a plain string (backward compatible) or an array\n * of typed content parts for multimodal messages (text + images + files).\n *\n * Use `contentToText(content)` to extract the text-only representation.\n */\n content: string | AparteContentPart[];\n /** For role='tool_call': tool calls made by the assistant in this turn */\n toolCalls?: AparteToolCall[];\n /** For role='tool_result': id of the tool call this responds to */\n toolCallId?: string;\n /** For role='tool_call': text streamed before the tool call in the same turn */\n precedingText?: string;\n}\n\nexport interface AparteChatRequest {\n messages: AparteChatMessage[];\n modelId: string;\n temperature?: number;\n maxTokens?: number;\n stream?: boolean;\n /** Tools the AI is allowed to call */\n tools?: AparteTool[];\n /**\n * Random seed for reproducibility / diversity.\n * Automatically set to a random integer on every `aparte-retry` so the\n * provider generates a different response even with the same history.\n * Supported by OpenAI, OpenRouter, LM Studio, Mistral, Ollama, and Gemini.\n * Anthropic does not expose a seed parameter — ignored silently there.\n */\n seed?: number;\n\n /**\n * Controls which tool the model must call — mirrors OpenAI tool_choice / Anthropic tool_choice.\n * - 'auto' : model decides (default when tools are present)\n * - 'none' : no tools injected this turn — model answers directly\n * - { name } : model MUST call this tool (provider injects a strong directive)\n * - { name, input }: synthetic call — the agent loop (inline `_streamLoop`\n * or the injected `runStreamAgent`) bypasses the LLM\n * entirely and runs the handler directly with the provided\n * input, then re-calls the LLM with the tool_result in history.\n */\n toolChoice?: 'auto' | 'none' | { name: string; input?: Record<string, unknown> };\n\n /**\n * Optional prefill string applied after the chat template's generation\n * prompt — the model continues from the end of this string. Provider- and\n * model-specific; core prescribes no syntax (the consuming app/orchestrator\n * decides what control tokens, if any, to inject). Providers that support a\n * \"continue final message\" mode apply it; others MAY ignore this field.\n */\n prefill?: string;\n\n /**\n * Verbatim system message to use INSTEAD of the provider building its own\n * (e.g. the transformers provider's tool-system-message). Generic transport\n * field : when set, the provider uses this string as the system message\n * as-is. `tools` may still be passed (for the tool-call parser/dispatch)\n * but is NOT re-rendered into the system prompt. Used to feed a fine-tuned\n * model its EXACT training system prompt (anti-OOD). Providers that build no\n * system message MAY ignore it.\n */\n systemOverride?: string;\n\n /**\n * Hint : stream tokens AS THEY ARRIVE (bypass the provider's flush-throttle).\n * Default throttling coalesces UI paints to protect WebGPU decode speed ;\n * a short codegen turn that drives a live preview (e.g. xlsx_ops ops) opts\n * in so the consumer can render progressively. Providers MAY ignore it.\n */\n fastStream?: boolean;\n\n /**\n * Opaque metadata bag threaded through the request pipeline (e.g. from a\n * requestInterceptor to the `_streamLoop` post-processor). Never sent to the\n * AI provider — stripped before the network call. The well-known keys are\n * typed; see {@link AparteRequestMeta}.\n */\n _meta?: AparteRequestMeta;\n}\n\n/** One phase of a multi-turn `_meta.pipeline` run: each phase is a single LLM\n * turn whose reply becomes context for the next. */\nexport type ApartePipelinePhase =\n | { mode: 'text'; system: string }\n | { mode: 'thinking'; system: string; label?: string }\n | { mode: 'artifact'; system: string; mimeType: string; kind: string };\n\n/** A `{ mimeType, kind }` artifact hint for the `_meta` artifact modes. */\nexport interface AparteArtifactHint {\n mimeType: string;\n kind: string;\n}\n\n/**\n * Well-known keys of {@link AparteChatRequest._meta}, typed for discoverability.\n * The index signature keeps it an open channel for consumer-specific context.\n * None of these reach the provider — they're stripped before the network call.\n */\nexport interface AparteRequestMeta {\n /** Multi-phase run — each phase is one LLM turn; reply N is context for N+1. */\n pipeline?: ApartePipelinePhase[];\n /** Segments injected into the bubble before streaming (e.g. a plan thinking block). */\n prefixSegments?: AparteSegment[];\n /** Promote the first code fence in the reply to an artifact (for small models that ignore `<artifact>` XML). */\n artifactHint?: AparteArtifactHint;\n /** Treat the WHOLE reply as a raw artifact of this kind. */\n artifactRaw?: AparteArtifactHint;\n /** Parse an `<artifact>` XML block of this kind out of the stream. */\n artifactXml?: AparteArtifactHint;\n /** Consumer-specific context (open channel). */\n [key: string]: unknown;\n}\n\n/**\n * Token usage reported by the AI provider after a completed response.\n */\nexport interface AparteUsage {\n /** Tokens in the prompt / conversation history sent to the model */\n inputTokens: number;\n /** Tokens generated by the model */\n outputTokens: number;\n /** Total tokens (inputTokens + outputTokens). Populated when the provider reports it. */\n totalTokens?: number;\n /** Tokens read from the provider's prompt cache (Anthropic, OpenAI). */\n cacheReadTokens?: number;\n /** Wall-clock generation time in milliseconds (provider-measured). Used to compute tokens/sec. */\n durationMs?: number;\n\n // ── Extended timing (offline transformers provider) ──────────────────\n /** Time to first token in ms — the prefill / TTFT phase. */\n ttftMs?: number;\n /** Decode-phase wall time in ms (≈ durationMs − ttftMs). */\n decodeMs?: number;\n /** Tokens emitted during the decode phase (outputTokens minus the first). */\n decodeTokens?: number;\n /** Total turn wall-clock time in ms, measured client-side across all phases. */\n wallMs?: number;\n /** Id of the model / aparteni that produced this response. */\n modelId?: string;\n /** Compute device used for this response ('webgpu' | 'wasm' | …). */\n device?: string;\n /**\n * Per-call breakdown when a single turn used several provider calls\n * (e.g. a tool-use round-trip or an aparteni hot-swap). Absent for a plain\n * single-call turn. Each entry is the AparteUsage of one provider call.\n */\n phases?: AparteUsage[];\n}\n\n/**\n * Normalized stream event map — extensible via declaration merging.\n *\n * @example\n * // In a plugin or app:\n * declare module '@aparte/core' {\n * interface AparteStreamEventMap {\n * tool_call: { name: string; arguments: string }\n * }\n * }\n */\nexport interface AparteStreamEventMap {\n text: { delta: string }\n thinking: { delta: string }\n /** Emitted when the stream ends. May carry token usage reported by the provider. */\n done: { usage?: AparteUsage }\n error: { message: string }\n tool_use: AparteToolCall\n}\n\n/** Discriminated union derived from AparteStreamEventMap — stays in sync automatically. */\nexport type AparteStreamEvent = {\n [K in keyof AparteStreamEventMap]: { type: K } & AparteStreamEventMap[K]\n}[keyof AparteStreamEventMap]\n\nexport type AparteChatResponse = ReadableStream<AparteStreamEvent> | string;\n","// AparteCore Standard Error Codes\nexport enum AparteErrorCode {\n // Configuration Errors (Dev/Host responsibility)\n CONFIG_NO_PROVIDER = 'CONFIG_NO_PROVIDER',\n CONFIG_MISSING_KEY = 'CONFIG_MISSING_KEY',\n CONFIG_INVALID_MODEL = 'CONFIG_INVALID_MODEL',\n\n // Usage Errors (User responsibility)\n USAGE_RATE_LIMIT = 'USAGE_RATE_LIMIT', // HTTP 429\n USAGE_CONTEXT_EXCEEDED = 'USAGE_CONTEXT_EXCEEDED', // HTTP 400\n USAGE_BAD_REQUEST = 'USAGE_BAD_REQUEST', // HTTP 400\n\n // Network & Infrastructure\n NET_OFFLINE = 'NET_OFFLINE',\n NET_TIMEOUT = 'NET_TIMEOUT',\n NET_ERROR = 'NET_ERROR',\n\n // Provider Errors (External Service)\n PROVIDER_ERROR = 'PROVIDER_ERROR', // HTTP 5xx\n PROVIDER_UNAVAILABLE = 'PROVIDER_UNAVAILABLE', // HTTP 503\n PROVIDER_POLICY = 'PROVIDER_POLICY', // Moderation / Policy\n\n // Internal\n UNKNOWN_ERROR = 'UNKNOWN_ERROR'\n}\n\nexport class AparteError extends Error {\n constructor(\n public override message: string,\n public code: AparteErrorCode,\n public data?: Record<string, unknown>,\n public originalError?: unknown,\n public httpStatus?: number\n ) {\n super(message);\n this.name = 'AparteError';\n }\n\n static from(error: unknown, defaultCode = AparteErrorCode.UNKNOWN_ERROR, defaultStatus?: number): AparteError {\n if (error instanceof AparteError) return error;\n\n const message = error instanceof Error ? error.message : String(error);\n\n // Try to infer status from error object if possible\n const status = (error as { status?: number; statusCode?: number })?.status || (error as { status?: number; statusCode?: number })?.statusCode || defaultStatus;\n\n return new AparteError(message, defaultCode, undefined, error, status);\n }\n}\n","/**\n * Icon Provider Interface\n * \n * Defines the contract for icon plugins.\n * Returns HTML strings: SVG, <i> tags, emojis, or any valid HTML.\n * \n * @example\n * // Register an icon provider (e.g. a FontAwesome bridge)\n * AparteConfig.setIconProvider({\n * copy: () => '<i class=\"fas fa-copy\"></i>',\n * check: () => '<i class=\"fas fa-check\"></i>',\n * // ...\n * });\n */\nexport interface AparteIconProvider {\n /** Copy button icon */\n copy: () => string;\n /** Success/check icon */\n check: () => string;\n /** Send message icon */\n send: () => string;\n /** Loading/spinner indicator */\n loading: () => string;\n /** Error indicator */\n error: () => string;\n /** Expand/show more icon */\n expand: () => string;\n /** Collapse/show less icon */\n collapse: () => string;\n /** Terminal/command prompt icon */\n terminal: () => string;\n /** File attachment / paperclip icon (used by upload plugin) */\n paperclip: () => string;\n /** Image file icon (used by upload plugin) */\n image: () => string;\n /** Generic file icon (used by upload plugin) */\n file: () => string;\n /** Scroll to bottom button icon */\n scrollDown: () => string;\n /** Retry / regenerate icon */\n retry: () => string;\n /** Edit / pencil icon */\n edit: () => string;\n /** Thumbs up / positive feedback icon */\n thumbUp: () => string;\n /** Thumbs down / negative feedback icon */\n thumbDown: () => string;\n /** Previous branch arrow */\n prevBranch: () => string;\n /** Next branch arrow */\n nextBranch: () => string;\n /** Tool call indicator (wrench) — optional so existing providers stay valid */\n tool?: () => string;\n /** Close / failed / rejected (✕) — optional so existing providers stay valid */\n close?: () => string;\n /** Stop / halt streaming (■) — optional so existing providers stay valid */\n stop?: () => string;\n}\n\n/** Icon names available in the provider */\nexport type AparteIconName = keyof AparteIconProvider;\n\n/** Default textual fallbacks - zero dependency */\nexport const DEFAULT_ICON_FALLBACKS: Record<AparteIconName, string> = {\n copy: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\"/><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"/></svg>`,\n check: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>`,\n send: `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/></svg>`,\n loading: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"aparte-icon-spin\"><circle cx=\"12\" cy=\"12\" r=\"10\" stroke-opacity=\"0.25\"/><path d=\"M12 2a10 10 0 0 1 10 10\"/></svg>`,\n error: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>`,\n expand: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>`,\n collapse: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"18 15 12 9 6 15\"/></svg>`,\n terminal: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"4 17 10 11 4 5\"/><line x1=\"12\" y1=\"19\" x2=\"20\" y2=\"19\"/></svg>`,\n paperclip: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48\"/></svg>`,\n image: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/></svg>`,\n file: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z\"/><polyline points=\"14 2 14 8 20 8\"/></svg>`,\n scrollDown: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><polyline points=\"19 12 12 19 5 12\"/></svg>`,\n retry: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/><path d=\"M3 3v5h5\"/></svg>`,\n edit: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/></svg>`,\n thumbUp: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M7 10v12\"/><path d=\"M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z\"/></svg>`,\n thumbDown: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M17 14V2\"/><path d=\"M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z\"/></svg>`,\n prevBranch: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"15 18 9 12 15 6\"/></svg>`,\n nextBranch: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"9 18 15 12 9 6\"/></svg>`,\n tool: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z\"/></svg>`,\n close: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>`,\n stop: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\"><rect x=\"2\" y=\"2\" width=\"12\" height=\"12\" rx=\"2\"/></svg>`,\n};\n","/**\n * Aparte Locale Interface\n *\n * Defines all translatable strings for the chat interface.\n * The core keeps only the English default in memory.\n * Other languages are injected via AparteConfig.setLocale().\n */\n\nexport interface AparteLocale {\n // --- Input Area ---\n inputPlaceholder: string;\n sendButton: string;\n\n // --- Message Actions ---\n copy: string;\n copied: string;\n retry: string;\n\n // --- Status Indicators ---\n thinking: string;\n typing: string; // Core status: \"Typing...\"\n error: string;\n running: string; // Terminal running state\n\n // --- Terminal ---\n run: string; // \"Run\" button\n\n // --- Files ---\n file: string; // Generic \"File\" label\n\n // --- Role Names ---\n /** Display name shown above the user's messages (default: \"You\") */\n roleNameUser: string;\n /** Display name shown above the assistant's messages (default: \"Assistant\") */\n roleNameAssistant: string;\n\n // --- Aria labels (bubble / message) ---\n yourMessage: string;\n assistantResponse: string;\n messageActions: string;\n\n // --- Action buttons ---\n edit: string;\n editConfirm: string;\n editCancel: string;\n feedbackPositive: string;\n feedbackNegative: string;\n previousResponse: string;\n nextResponse: string;\n /** Approve button on a tool awaiting human approval (default: \"Approve\") */\n approveTool?: string;\n /** Reject button on a tool awaiting human approval (default: \"Reject\") */\n rejectTool?: string;\n /** Short suffix for tokens-per-second perf chip (default: \"tok/s\") */\n tokensPerSecondLabel?: string;\n /** Aria-label / tooltip for the message info (\"i\") action button (default: \"Details\") */\n messageInfo?: string;\n\n // --- Conversation list ---\n /** Default title for a new conversation (default: \"New Chat\") */\n newChat: string;\n /** Aria-label for the delete conversation button (default: \"Delete conversation\") */\n deleteConversation: string;\n /** Aria-label for the archive conversation button (default: \"Archive conversation\") */\n archiveConversation?: string;\n /** Aria-label for the unarchive conversation button (default: \"Unarchive conversation\") */\n unarchiveConversation?: string;\n\n // --- Metadata ---\n /** Direction of the text (ltr or rtl) - defaults to ltr */\n direction?: 'ltr' | 'rtl';\n\n /** Allow extensions for plugins */\n [key: string]: string | undefined;\n}\n\n/**\n * Default English Locale (Zero-dependency)\n * Hardcoded to ensure the core works out-of-the-box.\n */\nexport const DEFAULT_LOCALE: AparteLocale = {\n inputPlaceholder: \"Type a message...\",\n sendButton: \"Send\",\n copy: \"Copy\",\n copied: \"Copied!\",\n retry: \"Retry\",\n thinking: \"Thinking...\",\n typing: \"Typing...\",\n error: \"Error\",\n running: \"Running...\",\n run: \"Run\",\n file: \"File\",\n modelSelectorPlaceholder: \"Select a model...\",\n roleNameUser: \"You\",\n roleNameAssistant: \"Assistant\",\n yourMessage: \"Your message\",\n assistantResponse: \"Assistant response\",\n messageActions: \"Message actions\",\n edit: \"Edit message\",\n editConfirm: \"Send\",\n editCancel: \"Cancel\",\n feedbackPositive: \"Good response\",\n feedbackNegative: \"Bad response\",\n previousResponse: \"Previous response\",\n nextResponse: \"Next response\",\n approveTool: \"Approve\",\n rejectTool: \"Reject\",\n tokensPerSecondLabel: \"tok/s\",\n messageInfo: \"Details\",\n newChat: \"New Chat\",\n deleteConversation: \"Delete conversation\",\n archiveConversation: \"Archive conversation\",\n unarchiveConversation: \"Unarchive conversation\",\n direction: 'ltr'\n};\n","/**\n * Transport seam — separates the two concerns that providers used to bundle:\n *\n * • **Format adapter** (`AparteFormatAdapter`): the vendor-specific concern —\n * how to shape the HTTP request and how to parse the response stream into\n * unified `AparteStreamEvent`s. No network, no auth, no key.\n * • **Transport** (`AparteTransport`): WHERE the call goes and how auth is\n * handled — straight to the vendor from the browser (`DirectTransport`,\n * BYOK/local) or via your own backend that holds the key\n * (`BackendTransport`, recommended for production).\n *\n * A \"provider\" is migrating from a monolithic `chat()` (fetch + key + parse) to\n * a pure format adapter. During the migration a provider may expose EITHER the\n * adapter surface OR a legacy `chat()`; the transports handle both.\n */\nimport type { AparteChatRequest, AparteChatResponse, AparteStreamEvent } from '../types/chat.js';\nimport type { AparteAIProvider } from '../types/model-provider.js';\n\n/** A vendor HTTP request built from an Aparte request — auth is injected separately. */\nexport interface AparteVendorRequest {\n /** Appended to the transport's endpoint, e.g. `/chat/completions`. */\n path: string;\n /** Vendor-shaped JSON body. */\n body: unknown;\n /** Content headers (NOT auth — the transport adds auth). */\n headers?: Record<string, string>;\n}\n\n/** The vendor-format concern, with no transport/auth coupling. */\nexport interface AparteFormatAdapter {\n id: string;\n /** Base URL for browser-direct calls (overridable per request via config). */\n defaultEndpoint: string;\n /** Map an Aparte request to the vendor HTTP request (without auth). */\n buildRequest(request: AparteChatRequest): AparteVendorRequest;\n /** Vendor auth headers for a resolved key (browser-direct only). Most vendors. */\n authHeaders?(key: string): Record<string, string>;\n /** Vendor auth as URL query params for a resolved key (e.g. Gemini `?key=`). */\n authQuery?(key: string): Record<string, string>;\n /** Parse a streaming vendor response body into unified events. */\n parseStream(body: ReadableStream<Uint8Array>): ReadableStream<AparteStreamEvent>;\n /** Extract text from a non-streaming vendor JSON response. */\n parseText?(json: unknown): string;\n}\n\nexport interface AparteTransportContext {\n providerId: string;\n signal?: AbortSignal;\n}\n\n/**\n * Decides where a chat request goes and how auth is handled. Given a provider\n * (adapter or legacy), the request, and the already-resolved auth, it returns\n * unified events (streaming) or a string (non-streaming).\n */\nexport interface AparteTransport {\n chat(\n provider: AparteAIProvider,\n request: AparteChatRequest,\n auth: string | Record<string, string> | undefined,\n ctx: AparteTransportContext,\n ): Promise<AparteChatResponse>;\n}\n\n/** True once a provider has been refactored to the format-adapter surface. */\nexport function isFormatAdapter(\n p: AparteAIProvider,\n): p is AparteAIProvider & Required<Pick<AparteFormatAdapter, 'buildRequest' | 'parseStream' | 'defaultEndpoint'>> {\n return (\n typeof p.buildRequest === 'function' &&\n typeof p.parseStream === 'function' &&\n typeof p.defaultEndpoint === 'string' &&\n (typeof p.authHeaders === 'function' || typeof p.authQuery === 'function')\n );\n}\n\n/** Read an api key / endpoint out of the legacy `string | Record` auth shape. */\nexport function readAuth(auth: string | Record<string, string> | undefined): { key?: string; endpoint?: string } {\n if (typeof auth === 'string') return { key: auth };\n if (auth && typeof auth === 'object') return { key: auth['apiKey'], endpoint: auth['endpoint'] };\n return {};\n}\n\n/** Best-effort human message from a non-ok vendor/backend JSON error response. */\nexport async function vendorErrorMessage(response: Response, label = 'HTTP'): Promise<string> {\n const body = await response.json().catch(() => ({} as Record<string, unknown>));\n const raw = (body as { error?: { message?: string } | string })?.error;\n const msg = typeof raw === 'object' ? raw?.message : raw;\n return typeof msg === 'string' && msg ? msg : `${label} ${response.status}`;\n}\n\n/**\n * Resolve a non-streaming response to text via the adapter's `parseText`.\n * Both transports use this so `stream: false` behaves identically (previously\n * DirectTransport returned '' and BackendTransport returned raw JSON when\n * `parseText` was missing — now both fail loud).\n */\nexport async function parseNonStreamText(\n provider: { parseText?: (json: unknown) => string },\n response: Response,\n providerId: string,\n): Promise<string> {\n const json = await response.json();\n if (typeof provider.parseText !== 'function') {\n throw new Error(`Provider \"${providerId}\" cannot resolve a non-streaming response: parseText is not implemented.`);\n }\n return provider.parseText(json);\n}\n","import type { AparteChatRequest, AparteChatResponse } from '../types/chat.js';\nimport type { AparteAIProvider } from '../types/model-provider.js';\nimport type { AparteTransport, AparteTransportContext } from './types.js';\nimport { isFormatAdapter, readAuth, vendorErrorMessage, parseNonStreamText } from './types.js';\n\n/** Options for {@link DirectTransport}. */\nexport interface DirectTransportOptions {\n /**\n * Set when the key is the end-user's own (BYOK) or the model runs locally —\n * i.e. you *intend* the credential to live in the browser. This silences the\n * one-time insecure-key warning. Leave it unset for keys that must stay\n * server-side, and use `BackendTransport` for those instead.\n */\n byok?: boolean;\n}\n\n/**\n * Browser-direct transport (BYOK / local / prototyping) — the DEFAULT.\n *\n * Calls the vendor endpoint straight from the browser, injecting the resolved\n * key via the adapter's `authHeaders`. This is the pre-refactor behaviour and\n * is only safe when the user brings their own key or the model runs locally.\n * Production apps that must keep a key server-side should use `BackendTransport`.\n *\n * When a real key is sent straight from the browser and the transport was not\n * flagged `{ byok: true }`, a one-time `console.warn` names the exposure — the\n * default should be loud, not silent. Local providers (no key) never warn.\n *\n * Legacy providers (still exposing `chat()` rather than the adapter surface) are\n * delegated to untouched, so the migration can proceed provider-by-provider.\n */\nexport class DirectTransport implements AparteTransport {\n private readonly byok: boolean;\n private warnedBrowserKey = false;\n\n constructor(options: DirectTransportOptions = {}) {\n this.byok = options.byok ?? false;\n }\n\n async chat(\n provider: AparteAIProvider,\n request: AparteChatRequest,\n auth: string | Record<string, string> | undefined,\n ctx: AparteTransportContext,\n ): Promise<AparteChatResponse> {\n // A real credential is about to leave the browser toward the vendor.\n // Warn once unless the caller opted into BYOK/local on purpose. Hoisted\n // above the adapter/legacy split: a legacy `chat()` provider (or an\n // SDK bridge) carrying a key exposes it exactly the same way — the\n // transport just isn't the one attaching it. Keyless locals never warn.\n const { key, endpoint } = readAuth(auth);\n if (!this.byok && key) {\n this.warnBrowserKeyOnce(ctx.providerId);\n }\n\n // Not yet a format adapter → let the provider own its fetch+parse.\n // Forward ctx so the provider can honor the abort signal (a bridge\n // wraps it into its SDK call; TransformersProvider simply ignores it).\n if (!isFormatAdapter(provider)) {\n if (typeof provider.chat !== 'function') {\n throw new Error(`Provider \"${ctx.providerId}\" exposes neither a format-adapter surface nor chat().`);\n }\n return provider.chat(request, auth, ctx);\n }\n\n const base = endpoint || provider.defaultEndpoint;\n const built = provider.buildRequest(request);\n\n let url = `${base}${built.path}`;\n const headers: Record<string, string> = { 'Content-Type': 'application/json', ...built.headers };\n if (key && provider.authHeaders) Object.assign(headers, provider.authHeaders(key));\n if (key && provider.authQuery) {\n const qs = new URLSearchParams(provider.authQuery(key)).toString();\n url += (url.includes('?') ? '&' : '?') + qs;\n }\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(built.body),\n signal: ctx.signal,\n });\n\n if (!response.ok) {\n throw new Error(await vendorErrorMessage(response));\n }\n\n if (request.stream === false) {\n return parseNonStreamText(provider, response, ctx.providerId);\n }\n if (!response.body) {\n throw new Error(`Provider \"${ctx.providerId}\" returned no response body for a streaming request.`);\n }\n return provider.parseStream(response.body);\n }\n\n private warnBrowserKeyOnce(providerId: string): void {\n if (this.warnedBrowserKey) return;\n this.warnedBrowserKey = true;\n console.warn(\n `[Aparte] DirectTransport is sending the \"${providerId}\" API key straight from the browser — ` +\n `it is visible to anyone who opens devtools. Fine for BYOK or local models: pass ` +\n `new DirectTransport({ byok: true }) to silence this. For a server-held key, use ` +\n `BackendTransport so the key never reaches the client.`,\n );\n }\n}\n","/**\n * AparteStreamParser\n * \n * Real-time markdown stream parser for AI/LLM responses.\n * Detects code blocks, thinking blocks, and other patterns during streaming.\n * \n * Supported patterns:\n * - ```language\\ncode\\n``` → AparteCodeSegment\n * - Plain text → AparteTextSegment\n * - <think>…</think> or <thinking>…</thinking> → AparteThinkingSegment (configurable)\n */\n\nimport type {\n AparteSegment,\n AparteTextSegment,\n AparteCodeSegment,\n AparteThinkingSegment,\n AparteArtifactSegment,\n} from '../types/index.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** One inline thinking-block delimiter pair (open/close tag). */\nexport interface AparteThinkingDelimiterPair {\n start: string;\n end: string;\n}\n\nexport interface AparteStreamParserOptions {\n /**\n * Inline thinking-block delimiters — for a model that streams its reasoning\n * *inline in the content* (rather than on a separate reasoning channel).\n * Accepts one pair or several; the parser opens a thinking segment at\n * whichever pair appears first. Default recognizes BOTH common conventions:\n * `<think>…</think>` (DeepSeek-R1, QwQ, most local GGUF reasoning models) and\n * `<thinking>…</thinking>` (Claude-style). Pass your own to override.\n */\n thinkingDelimiters?: AparteThinkingDelimiterPair | AparteThinkingDelimiterPair[];\n /** Auto-generate segment IDs (default: true) */\n autoGenerateIds?: boolean;\n /** ID prefix for generated segments */\n idPrefix?: string;\n}\n\n/** Recognized out of the box so local reasoning models \"just work\" (no config). */\nconst DEFAULT_THINKING_DELIMITERS: AparteThinkingDelimiterPair[] = [\n { start: '<think>', end: '</think>' },\n { start: '<thinking>', end: '</thinking>' },\n];\n\nexport interface AparteParserState {\n /** Current parser mode */\n mode: 'text' | 'code' | 'thinking' | 'artifact';\n /** Buffer for incomplete patterns */\n buffer: string;\n /** Current code block language */\n codeLanguage?: string;\n /** Close delimiter of the thinking pair currently open (multi-delimiter support) */\n thinkingEnd?: string;\n /** Accumulated segments */\n segments: AparteSegment[];\n /** Current active segment being built */\n activeSegment: AparteSegment | null;\n /** Counter for generating IDs */\n segmentCounter: number;\n}\n\nexport interface AparteParserResult {\n /** Parsed segments */\n segments: AparteSegment[];\n /** Remaining buffer (incomplete patterns) */\n remaining: string;\n /** Current parser state */\n state: AparteParserState;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// AparteStreamParser Class\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class AparteStreamParser {\n private _options: Required<Pick<AparteStreamParserOptions, 'autoGenerateIds' | 'idPrefix'>>;\n /** Normalized to an array; the parser matches whichever pair opens first. */\n private _thinkingDelimiters: AparteThinkingDelimiterPair[];\n private _state: AparteParserState;\n\n constructor(options: AparteStreamParserOptions = {}) {\n const delims = options.thinkingDelimiters;\n this._thinkingDelimiters = delims\n ? (Array.isArray(delims) ? delims : [delims])\n : DEFAULT_THINKING_DELIMITERS;\n this._options = {\n autoGenerateIds: options.autoGenerateIds ?? true,\n idPrefix: options.idPrefix ?? 'seg'\n };\n\n this._state = this._createInitialState();\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Public API\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Parse a chunk of streamed content\n * Call this for each chunk received from the LLM\n */\n parse(chunk: string): AparteParserResult {\n this._state.buffer += chunk;\n\n const completedSegments: AparteSegment[] = [];\n\n while (this._state.buffer.length > 0) {\n const parsed = this._parseNext();\n\n if (!parsed) {\n // No complete pattern found, keep buffer for next chunk\n break;\n }\n\n if (parsed.segment) {\n completedSegments.push(parsed.segment);\n }\n\n this._state.buffer = parsed.remaining;\n }\n\n return {\n segments: completedSegments,\n remaining: this._state.buffer,\n state: { ...this._state }\n };\n }\n\n /**\n * Finalize parsing - flush any remaining content as text\n */\n finalize(): AparteSegment[] {\n const finalSegments: AparteSegment[] = [];\n\n // An unwrapped ```markdown block leaves its trailing close fence in the\n // text stream — strip a dangling ``` so it never renders as a stray\n // empty code block at the very end of the reply.\n const stripTrailingFence = (s: string): string => s.replace(/\\n?```[ \\t]*$/, '');\n\n // If we have an active segment, complete it\n if (this._state.activeSegment) {\n if ('content' in this._state.activeSegment) {\n const seg = this._state.activeSegment as { type: string; content: string };\n let content = seg.content + this._state.buffer;\n if (seg.type === 'text') content = stripTrailingFence(content);\n seg.content = content;\n }\n finalSegments.push(this._state.activeSegment);\n this._state.activeSegment = null;\n this._state.buffer = '';\n } else if (this._state.buffer.trim()) {\n // Remaining buffer becomes text segment\n finalSegments.push(this._createTextSegment(stripTrailingFence(this._state.buffer)));\n this._state.buffer = '';\n }\n\n return finalSegments;\n }\n\n /**\n * Reset parser state\n */\n reset(): void {\n this._state = this._createInitialState();\n }\n\n /**\n * Get current parser state (for debugging/inspection)\n */\n getState(): Readonly<AparteParserState> {\n return { ...this._state };\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Private Parsing Logic\n // ─────────────────────────────────────────────────────────────────────────\n\n private _parseNext(): { segment: AparteSegment | null; remaining: string } | null {\n const buffer = this._state.buffer;\n\n switch (this._state.mode) {\n case 'text':\n return this._parseTextMode(buffer);\n case 'code':\n return this._parseCodeMode(buffer);\n case 'thinking':\n return this._parseThinkingMode(buffer);\n case 'artifact':\n return this._parseArtifactMode(buffer);\n default:\n return null;\n }\n }\n\n private _parseTextMode(buffer: string): { segment: AparteSegment | null; remaining: string } | null {\n // Check for code block start: ```\n const codeBlockStart = buffer.indexOf('```');\n\n // Check for thinking block start — earliest match across all recognized\n // delimiter pairs (e.g. <think> vs <thinking>).\n let thinkingStart = -1;\n for (const p of this._thinkingDelimiters) {\n const i = buffer.indexOf(p.start);\n if (i !== -1 && (thinkingStart === -1 || i < thinkingStart)) thinkingStart = i;\n }\n\n // Check for artifact block start: <artifact ...>\n const artifactStart = buffer.indexOf('<artifact');\n\n // Determine which pattern comes first\n const patterns: { type: 'code' | 'thinking' | 'artifact'; index: number }[] = [];\n if (codeBlockStart !== -1) patterns.push({ type: 'code', index: codeBlockStart });\n if (thinkingStart !== -1) patterns.push({ type: 'thinking', index: thinkingStart });\n if (artifactStart !== -1) patterns.push({ type: 'artifact', index: artifactStart });\n\n patterns.sort((a, b) => a.index - b.index);\n\n if (patterns.length === 0) {\n // No special patterns found\n // Check if buffer might be start of a pattern (keep it for next chunk)\n if (buffer.endsWith('`') || buffer.endsWith('``') ||\n this._thinkingDelimiters.some(p => this._isPartialMatch(buffer, p.start)) ||\n this._isPartialMatch(buffer, '<artifact')) {\n return null; // Keep buffer, wait for more data\n }\n\n // No pattern starting. Just accumulate text in active segment.\n if (!this._state.activeSegment || this._state.activeSegment.type !== 'text') {\n this._state.activeSegment = this._createTextSegment('');\n }\n\n (this._state.activeSegment as AparteTextSegment).content += buffer;\n return { segment: null, remaining: '' };\n }\n\n const firstPattern = patterns[0]!;\n\n // If pattern is NOT at index 0, accumulate the text before it\n if (firstPattern.index > 0) {\n const textContent = buffer.slice(0, firstPattern.index);\n if (!this._state.activeSegment || this._state.activeSegment.type !== 'text') {\n this._state.activeSegment = this._createTextSegment(textContent);\n } else {\n (this._state.activeSegment as AparteTextSegment).content += textContent;\n }\n // Return nothing yet, let the next loop handle the pattern at index 0\n return { segment: null, remaining: buffer.slice(firstPattern.index) };\n }\n\n // Pattern is at the start index 0. Finish current text segment if any.\n let segmentToEmit: AparteSegment | null = null;\n if (this._state.activeSegment && this._state.activeSegment.type === 'text') {\n segmentToEmit = this._state.activeSegment;\n this._state.activeSegment = null;\n }\n\n if (firstPattern.type === 'code') {\n const res = this._startCodeBlock(buffer);\n return res ? { segment: segmentToEmit, remaining: res.remaining } : null;\n } else if (firstPattern.type === 'thinking') {\n const res = this._startThinkingBlock(buffer);\n return res ? { segment: segmentToEmit, remaining: res.remaining } : null;\n } else {\n const res = this._startArtifactBlock(buffer);\n if (!res) {\n // Opening tag incomplete — restore the text segment we were about to emit\n if (segmentToEmit) this._state.activeSegment = segmentToEmit;\n return null;\n }\n return { segment: segmentToEmit, remaining: res.remaining };\n }\n }\n\n private _startCodeBlock(buffer: string): { segment: AparteSegment | null; remaining: string } | null {\n // Find the newline after ```language\n const firstNewline = buffer.indexOf('\\n');\n\n if (firstNewline === -1) {\n return null; // Wait for language line to complete\n }\n\n const firstLine = buffer.slice(0, firstNewline);\n // Remove ``` and read the language tag ; a fence with no tag → 'markdown'.\n const language = firstLine.slice(3).trim() || 'markdown';\n\n // ```markdown / ```md (and a bare ``` → markdown) = the model wrapping\n // its whole reply as a \"markdown document\". Do NOT open a code block :\n // (1) it would show raw source instead of a formatted reply ;\n // (2) the inner ```bash / ```json sub-fences make _parseCodeMode close\n // the outer block at the first one — shredding the reply into\n // fragments (one stray code block per nested fence).\n // Instead, drop the fence line and keep parsing in `text` mode : the\n // inner fenced blocks then sit at top level and parse as real code\n // segments, and the whole thing renders as one coherent markdown reply.\n if (language === 'markdown' || language === 'md') {\n return { segment: null, remaining: buffer.slice(firstNewline + 1) };\n }\n\n this._state.mode = 'code';\n this._state.codeLanguage = language;\n this._state.activeSegment = this._createCodeSegment(language, '');\n\n return { segment: null, remaining: buffer.slice(firstNewline + 1) };\n }\n\n private _parseCodeMode(buffer: string): { segment: AparteSegment | null; remaining: string } | null {\n // Look for closing ```\n const closeIndex = buffer.indexOf('\\n```');\n\n if (closeIndex === -1) {\n // Check for ``` at end without newline\n if (buffer.endsWith('```')) {\n const codeContent = buffer.slice(0, -3);\n if (this._state.activeSegment && 'content' in this._state.activeSegment) {\n (this._state.activeSegment as { content: string }).content += codeContent;\n }\n const segment = this._state.activeSegment;\n this._state.mode = 'text';\n this._state.activeSegment = null;\n return { segment, remaining: '' };\n }\n\n // No closing found, accumulate content (keep last 4 chars as potential pattern)\n if (buffer.length > 4) {\n const safeContent = buffer.slice(0, -4);\n if (this._state.activeSegment && 'content' in this._state.activeSegment) {\n (this._state.activeSegment as { content: string }).content += safeContent;\n }\n return { segment: null, remaining: buffer.slice(-4) };\n }\n return null;\n }\n\n // Found closing\n const codeContent = buffer.slice(0, closeIndex);\n if (this._state.activeSegment && 'content' in this._state.activeSegment) {\n (this._state.activeSegment as { content: string }).content += codeContent;\n }\n\n const segment = this._state.activeSegment;\n this._state.mode = 'text';\n this._state.activeSegment = null;\n\n // Skip past \\n```\n const afterClose = buffer.slice(closeIndex + 4);\n // Also skip trailing newline if present\n const remaining = afterClose.startsWith('\\n') ? afterClose.slice(1) : afterClose;\n\n return { segment, remaining };\n }\n\n private _startThinkingBlock(buffer: string): { segment: AparteSegment | null; remaining: string } | null {\n const pair = this._thinkingDelimiters.find(p => buffer.startsWith(p.start));\n if (!pair) {\n return null;\n }\n\n this._state.mode = 'thinking';\n // Remember which pair opened so the matching close delimiter is used.\n this._state.thinkingEnd = pair.end;\n this._state.activeSegment = this._createThinkingSegment('');\n\n return { segment: null, remaining: buffer.slice(pair.start.length) };\n }\n\n private _parseThinkingMode(buffer: string): { segment: AparteSegment | null; remaining: string } | null {\n const endDelim = this._state.thinkingEnd ?? this._thinkingDelimiters[0]!.end;\n const closeIndex = buffer.indexOf(endDelim);\n\n if (closeIndex === -1) {\n // Check for partial match at end\n if (this._isPartialMatch(buffer, endDelim)) {\n // Keep potential match in buffer\n const partialLength = this._getPartialMatchLength(buffer, endDelim);\n const safeContent = buffer.slice(0, buffer.length - partialLength);\n if (safeContent && this._state.activeSegment && 'content' in this._state.activeSegment) {\n (this._state.activeSegment as { content: string }).content += safeContent;\n }\n // Whole buffer is a partial delimiter (nothing safe to emit yet):\n // return null so parse() keeps the buffer for the next chunk —\n // otherwise remaining === buffer spins parse()'s while-loop forever.\n return safeContent\n ? { segment: null, remaining: buffer.slice(-partialLength) }\n : null;\n }\n\n // No close, accumulate\n if (buffer.length > endDelim.length) {\n const safeContent = buffer.slice(0, -endDelim.length);\n if (this._state.activeSegment && 'content' in this._state.activeSegment) {\n (this._state.activeSegment as { content: string }).content += safeContent;\n }\n return { segment: null, remaining: buffer.slice(-endDelim.length) };\n }\n return null;\n }\n\n // Found closing\n const thinkingContent = buffer.slice(0, closeIndex);\n if (this._state.activeSegment && 'content' in this._state.activeSegment) {\n (this._state.activeSegment as { content: string }).content += thinkingContent;\n }\n\n const segment = this._state.activeSegment;\n this._state.mode = 'text';\n this._state.activeSegment = null;\n this._state.thinkingEnd = undefined;\n\n return { segment, remaining: buffer.slice(closeIndex + endDelim.length) };\n }\n\n /**\n * Start an artifact block. Buffer must start with `<artifact`.\n * The full opening tag must be present (`>`); otherwise we return null and wait\n * for more chunks.\n *\n * Supported attributes (Anthropic-style, single or double quotes):\n * - type — required (MIME type, verbatim)\n * - title — optional human label\n *\n * Any other attributes are ignored gracefully.\n */\n private _startArtifactBlock(buffer: string): { segment: AparteSegment | null; remaining: string } | null {\n if (!buffer.startsWith('<artifact')) return null;\n\n const tagEnd = buffer.indexOf('>');\n if (tagEnd === -1) return null; // wait for more\n\n const tag = buffer.slice(0, tagEnd + 1);\n const inner = tag.slice('<artifact'.length, -1); // attributes string\n\n const typeMatch = inner.match(/\\btype\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/i);\n const titleMatch = inner.match(/\\btitle\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/i);\n\n const mimeType = (typeMatch && (typeMatch[1] ?? typeMatch[2])) ?? 'text/plain';\n const title = titleMatch ? (titleMatch[1] ?? titleMatch[2]) : undefined;\n\n this._state.mode = 'artifact';\n this._state.activeSegment = this._createArtifactSegment(mimeType, title);\n\n return { segment: null, remaining: buffer.slice(tagEnd + 1) };\n }\n\n /**\n * Stream content into the active artifact segment until `</artifact>` is seen.\n * Mirrors `_parseThinkingMode` so partial matches at chunk boundaries don't\n * split the closing tag across two updates.\n */\n private _parseArtifactMode(buffer: string): { segment: AparteSegment | null; remaining: string } | null {\n const endDelim = '</artifact>';\n const closeIndex = buffer.indexOf(endDelim);\n\n if (closeIndex === -1) {\n if (this._isPartialMatch(buffer, endDelim)) {\n const partialLength = this._getPartialMatchLength(buffer, endDelim);\n const safeContent = buffer.slice(0, buffer.length - partialLength);\n if (safeContent && this._state.activeSegment && 'content' in this._state.activeSegment) {\n (this._state.activeSegment as { content: string }).content += safeContent;\n }\n // Whole buffer is a partial delimiter (nothing safe to emit yet):\n // return null so parse() keeps the buffer for the next chunk —\n // otherwise remaining === buffer spins parse()'s while-loop forever.\n return safeContent\n ? { segment: null, remaining: buffer.slice(-partialLength) }\n : null;\n }\n\n if (buffer.length > endDelim.length) {\n const safeContent = buffer.slice(0, -endDelim.length);\n if (this._state.activeSegment && 'content' in this._state.activeSegment) {\n (this._state.activeSegment as { content: string }).content += safeContent;\n }\n return { segment: null, remaining: buffer.slice(-endDelim.length) };\n }\n return null;\n }\n\n const tail = buffer.slice(0, closeIndex);\n if (this._state.activeSegment && 'content' in this._state.activeSegment) {\n (this._state.activeSegment as { content: string }).content += tail;\n }\n\n const segment = this._state.activeSegment;\n this._state.mode = 'text';\n this._state.activeSegment = null;\n\n return { segment, remaining: buffer.slice(closeIndex + endDelim.length) };\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Helpers\n // ─────────────────────────────────────────────────────────────────────────\n\n private _createInitialState(): AparteParserState {\n return {\n mode: 'text',\n buffer: '',\n segments: [],\n activeSegment: null,\n segmentCounter: 0\n };\n }\n\n private _generateId(): string {\n ++this._state.segmentCounter;\n return `${this._options.idPrefix}-${crypto.randomUUID()}`;\n }\n\n private _createTextSegment(content: string): AparteTextSegment {\n return {\n id: this._options.autoGenerateIds ? this._generateId() : '',\n type: 'text',\n content: content\n };\n }\n\n private _createCodeSegment(language: string, content: string): AparteCodeSegment {\n return {\n id: this._options.autoGenerateIds ? this._generateId() : '',\n type: 'code',\n language,\n content\n };\n }\n\n private _createThinkingSegment(content: string): AparteThinkingSegment {\n return {\n id: this._options.autoGenerateIds ? this._generateId() : '',\n type: 'thinking',\n content,\n collapsed: false,\n label: 'Thinking'\n };\n }\n\n private _createArtifactSegment(mimeType: string, title?: string): AparteArtifactSegment {\n return {\n id: this._options.autoGenerateIds ? this._generateId() : '',\n type: 'artifact',\n mimeType,\n artifactType: deriveArtifactKind(mimeType),\n title,\n content: ''\n };\n }\n\n private _isPartialMatch(buffer: string, pattern: string): boolean {\n for (let i = 1; i < pattern.length; i++) {\n if (buffer.endsWith(pattern.slice(0, i))) {\n return true;\n }\n }\n return false;\n }\n\n private _getPartialMatchLength(buffer: string, pattern: string): number {\n for (let i = pattern.length - 1; i >= 1; i--) {\n if (buffer.endsWith(pattern.slice(0, i))) {\n return i;\n }\n }\n return 0;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Convenience Functions\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Parse a complete markdown string into segments\n * Use this for non-streaming scenarios\n */\nexport function parseMarkdownToSegments(\n content: string,\n options?: AparteStreamParserOptions\n): AparteSegment[] {\n const parser = new AparteStreamParser(options);\n const result = parser.parse(content);\n const finalSegments = parser.finalize();\n return [...result.segments, ...finalSegments];\n}\n\n/**\n * Map a MIME type to a short, stable artifact kind identifier.\n *\n * Resolution order: Anthropic's vendor namespace (`application/vnd.ant.<kind>`),\n * exact standard MIMEs, then a substring rescue for parameterised or vendor\n * variants (`text/html; charset=utf-8`, `application/ld+json`, …). Unrecognised\n * MIMEs return `fallback` (default `'unknown'`) — consumers are expected to\n * guard their renderers accordingly.\n *\n * Canonical implementation. `@aparte/engine` keeps a byte-identical copy (core is\n * an OPTIONAL peer there, so it cannot import this at runtime); the engine's\n * `derive-artifact-kind-parity.test.ts` locks the two together.\n *\n * @example\n * deriveArtifactKind('application/vnd.ant.react') // 'react'\n * deriveArtifactKind('text/html; charset=utf-8') // 'html'\n * deriveArtifactKind('font/woff2', 'text') // 'text' (fallback)\n */\nexport function deriveArtifactKind(mimeType: string, fallback = 'unknown'): string {\n const m = (mimeType || '').toLowerCase().trim();\n const ant = m.match(/^application\\/vnd\\.ant\\.([a-z0-9-]+)/);\n if (ant) return ant[1]!;\n if (m === 'text/html' || m === 'application/xhtml+xml') return 'html';\n if (m === 'application/javascript' || m === 'text/javascript') return 'js';\n if (m === 'text/css') return 'css';\n if (m === 'image/svg+xml') return 'svg';\n if (m === 'application/json') return 'json';\n if (m === 'text/markdown') return 'markdown';\n if (m === 'text/csv') return 'csv';\n if (m === 'text/plain') return 'text';\n if (m.includes('react')) return 'react';\n if (m.includes('html')) return 'html';\n if (m.includes('javascript')) return 'js';\n if (m.includes('css')) return 'css';\n if (m.includes('svg')) return 'svg';\n if (m.includes('json')) return 'json';\n if (m.includes('csv')) return 'csv';\n if (m.includes('markdown')) return 'markdown';\n return fallback;\n}\n","/**\n * Aparte event stream parser — the wire format for the backend path.\n *\n * A backend that keeps the API key server-side runs the vendor's own parser\n * server-side and streams back **already-normalized `AparteStreamEvent`s, one JSON\n * object per line (NDJSON)**. This parser turns that NDJSON byte stream back\n * into a `ReadableStream<AparteStreamEvent>`, so the client stays fully\n * vendor-agnostic on the backend path (it never needs the vendor-specific\n * `parseStream`). See `transport/backend-transport.ts`.\n */\n\nimport type { AparteStreamEvent } from '../types/index.js';\n\nexport function parseAparteEventStream(\n stream: ReadableStream<Uint8Array>,\n): ReadableStream<AparteStreamEvent> {\n const decoder = new TextDecoder();\n let buffer = '';\n let sawDone = false;\n\n return new ReadableStream<AparteStreamEvent>({\n async start(controller) {\n const reader = stream.getReader();\n const emit = (line: string): void => {\n const trimmed = line.trim();\n if (!trimmed) return;\n try {\n const event = JSON.parse(trimmed) as AparteStreamEvent;\n if (event && typeof (event as { type?: unknown }).type === 'string') {\n controller.enqueue(event);\n if (event.type === 'done') sawDone = true;\n }\n } catch { /* skip a malformed / partial line */ }\n };\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n for (const line of lines) emit(line);\n }\n if (buffer.trim()) emit(buffer);\n if (!sawDone) controller.enqueue({ type: 'done' });\n } catch (err) {\n controller.enqueue({ type: 'error', message: (err as Error)?.message ?? 'Stream error' });\n } finally {\n reader.releaseLock();\n controller.close();\n }\n },\n });\n}\n","import type { AparteChatRequest, AparteChatResponse } from '../types/chat.js';\nimport type { AparteAIProvider } from '../types/model-provider.js';\nimport type { AparteTransport, AparteTransportContext } from './types.js';\nimport { vendorErrorMessage } from './types.js';\nimport { parseAparteEventStream } from '../parsers/index.js';\n\nexport interface BackendTransportOptions {\n /** Your backend chat endpoint, e.g. `/api/chat`. */\n endpoint: string;\n /** Extra headers to send with each request (session cookie is automatic). */\n headers?: Record<string, string>;\n /**\n * Override how the request is serialised to your backend. Default body is\n * `{ providerId, request }`. Return any JSON-serialisable value.\n */\n buildBody?: (request: AparteChatRequest, providerId: string) => unknown;\n}\n\n/**\n * Backend-proxied transport — RECOMMENDED for production.\n *\n * POSTs `{ providerId, request }` to YOUR endpoint. The backend resolves the API\n * key server-side, calls the vendor, runs the vendor parser **server-side**, and\n * streams back already-normalized `AparteStreamEvent`s as NDJSON (one JSON object\n * per line). The key never reaches the browser and — unlike a raw proxy — the\n * client stays fully vendor-agnostic (it never runs the vendor's `parseStream`,\n * so switching the server-side provider needs no client change). Non-streaming\n * requests get a JSON `{ text }` reply.\n *\n * This is NOT the Vercel AI SDK Data Stream Protocol; the wire format is a plain\n * NDJSON of `AparteStreamEvent` (see `parseAparteEventStream`).\n *\n * The matching `/api/chat` handler ships as a real, framework-free function —\n * {@link createAparteChatHandler} (Web `fetch` API, same @aparte adapters run\n * server-side, key held server-side). Drop it into a Next.js route handler,\n * Deno, Bun, or a Worker rather than hand-rolling one.\n */\nexport class BackendTransport implements AparteTransport {\n constructor(private readonly options: BackendTransportOptions) {}\n\n async chat(\n _provider: AparteAIProvider,\n request: AparteChatRequest,\n _auth: string | Record<string, string> | undefined,\n ctx: AparteTransportContext,\n ): Promise<AparteChatResponse> {\n // The backend owns the vendor mapping + key, so no client-side format\n // adapter is required — only the providerId, so it knows which vendor to call.\n const body = this.options.buildBody\n ? this.options.buildBody(request, ctx.providerId)\n : { providerId: ctx.providerId, request };\n\n const response = await fetch(this.options.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...this.options.headers },\n body: JSON.stringify(body),\n signal: ctx.signal,\n });\n\n if (!response.ok) {\n throw new Error(await vendorErrorMessage(response, 'Backend HTTP'));\n }\n\n if (request.stream === false) {\n const json = await response.json();\n const text = (json as { text?: unknown })?.text;\n return typeof text === 'string' ? text : (typeof json === 'string' ? json : JSON.stringify(json));\n }\n if (!response.body) {\n throw new Error('Backend returned no response body for a streaming request.');\n }\n return parseAparteEventStream(response.body);\n }\n}\n","import type { AparteChatRequest } from '../types/chat.js';\nimport type { AparteAIProvider } from '../types/model-provider.js';\nimport { isFormatAdapter } from './types.js';\n\nexport interface AparteChatHandlerOptions {\n /**\n * Your server-side format adapters, keyed by the `providerId` the client\n * sends. The same adapters as the browser (`@aparte/provider-*`), running here\n * so the vendor key never leaves the server.\n */\n providers: Record<string, AparteAIProvider>;\n /**\n * Optional gate run on EVERY request before any work — this endpoint spends\n * your server-held vendor key, so put your own auth in front of it. Return\n * `false` to reject with 401, a `Response` to reject with your own status/body\n * (e.g. 403 + a message), or `true` to proceed. Reads cookies/headers via `req`.\n */\n authorize?: (req: Request) => boolean | Response | Promise<boolean | Response>;\n /**\n * Resolve the vendor API key for a providerId — from env / a secret store.\n * Runs server-side only. Return `undefined` for keyless/local providers.\n */\n resolveKey?: (providerId: string) => string | undefined | Promise<string | undefined>;\n /**\n * The `fetch` used to call the vendor. Defaults to the global `fetch`\n * (Node 18+, Deno, Bun, Workers). Override in tests or to add a proxy.\n */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Build a framework-free `/api/chat` handler for {@link BackendTransport} — the\n * server counterpart that keeps the key off the client.\n *\n * The returned handler speaks the Web `fetch` API (`Request` → `Response`), so\n * it drops into a Next.js route handler, Deno, Bun, or a Cloudflare Worker\n * unchanged. It reads `{ providerId, request }`, runs the matching format\n * adapter **server-side** (buildRequest → auth → vendor fetch → parseStream),\n * and streams back normalized `AparteStreamEvent`s as NDJSON — the exact wire\n * format `BackendTransport` expects. The vendor key is injected here and never\n * travels to the browser.\n *\n * ```ts\n * // app/api/chat/route.ts (Next.js)\n * import { createAparteChatHandler } from '@aparte/core';\n * import { createOpenAICompatProvider, presets } from '@aparte/provider-openai-compat';\n * export const POST = createAparteChatHandler({\n * providers: { openai: createOpenAICompatProvider(presets.OPENAI) },\n * resolveKey: (id) => process.env[`${id.toUpperCase()}_KEY`],\n * });\n * ```\n */\nexport function createAparteChatHandler(\n options: AparteChatHandlerOptions,\n): (req: Request) => Promise<Response> {\n const doFetch = options.fetchImpl ?? fetch;\n\n return async function handler(req: Request): Promise<Response> {\n // Auth gate first: this route spends the server-held key, so reject\n // unauthorized callers before parsing or doing any work.\n if (options.authorize) {\n const verdict = await options.authorize(req);\n if (verdict instanceof Response) return verdict;\n if (!verdict) return jsonError(401, 'Unauthorized.');\n }\n\n let providerId: string;\n let request: AparteChatRequest;\n try {\n const parsed = (await req.json()) as { providerId?: unknown; request?: unknown };\n if (typeof parsed?.providerId !== 'string' || !parsed?.request || typeof parsed.request !== 'object') {\n return jsonError(400, 'Body must be { providerId: string, request: AparteChatRequest }.');\n }\n providerId = parsed.providerId;\n request = parsed.request as AparteChatRequest;\n } catch {\n return jsonError(400, 'Invalid JSON body.');\n }\n\n const adapter = options.providers[providerId];\n if (!adapter) {\n return jsonError(400, `Unknown providerId \"${providerId}\". Register it in the handler's providers map.`);\n }\n if (!isFormatAdapter(adapter)) {\n return jsonError(500, `Provider \"${providerId}\" is not a format adapter (needs buildRequest + parseStream).`);\n }\n\n const built = adapter.buildRequest(request);\n // Guard against an adapter returning a non-rooted path that could redirect\n // this server-key'd call to another origin (SSRF): require a single-rooted\n // path — never protocol-relative (`//host`) nor an absolute URL.\n if (!built.path.startsWith('/') || built.path.startsWith('//')) {\n return jsonError(500, `Provider \"${providerId}\" produced a non-rooted request path.`);\n }\n const key = await options.resolveKey?.(providerId);\n\n let url = `${adapter.defaultEndpoint}${built.path}`;\n const headers: Record<string, string> = { 'Content-Type': 'application/json', ...built.headers };\n if (key && adapter.authHeaders) Object.assign(headers, adapter.authHeaders(key));\n if (key && adapter.authQuery) {\n const qs = new URLSearchParams(adapter.authQuery(key)).toString();\n url += (url.includes('?') ? '&' : '?') + qs;\n }\n\n let vendor: Response;\n try {\n vendor = await doFetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(built.body),\n signal: req.signal, // a client disconnect aborts the vendor call too\n });\n } catch (err) {\n return jsonError(502, `Vendor request failed: ${(err as Error)?.message ?? 'network error'}`);\n }\n\n // Propagate a vendor error verbatim so the client surfaces the real message.\n if (!vendor.ok) {\n const text = await vendor.text().catch(() => '');\n return new Response(text || JSON.stringify({ error: { message: `HTTP ${vendor.status}` } }), {\n status: vendor.status,\n headers: { 'Content-Type': vendor.headers.get('Content-Type') ?? 'application/json' },\n });\n }\n\n // Non-streaming: resolve to { text } server-side.\n if (request.stream === false) {\n const json = await vendor.json().catch(() => ({}));\n const text = typeof adapter.parseText === 'function' ? adapter.parseText(json) : '';\n return new Response(JSON.stringify({ text }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n if (!vendor.body) {\n return jsonError(502, 'Vendor returned no response body for a streaming request.');\n }\n\n // Normalize the vendor stream to AparteStreamEvents server-side, re-emit as\n // NDJSON (one JSON object per line) — what parseAparteEventStream reads.\n const events = adapter.parseStream(vendor.body).getReader();\n const encoder = new TextEncoder();\n const ndjson = new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n const { done, value } = await events.read();\n if (done) {\n controller.close();\n return;\n }\n controller.enqueue(encoder.encode(JSON.stringify(value) + '\\n'));\n } catch (err) {\n controller.error(err);\n }\n },\n cancel(reason) {\n void events.cancel(reason);\n },\n });\n\n return new Response(ndjson, { headers: { 'Content-Type': 'application/x-ndjson' } });\n };\n}\n\nfunction jsonError(status: number, message: string): Response {\n return new Response(JSON.stringify({ error: { message } }), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n","/**\n * Zero-dependency HTML sanitizer for provider output.\n *\n * Aparte renders LLM-authored content: a markdown or highlight provider turns the\n * assistant's text into HTML that is then injected via `innerHTML`. Prompt\n * injection can make a model emit arbitrary markup (`<img onerror=…>`,\n * `<script>`, `javascript:` URLs), so that HTML is **untrusted** and must be\n * scrubbed before it touches the DOM.\n *\n * This is an allowlist sanitizer built on the browser's own parser: the HTML is\n * parsed into an inert document (scripts never execute during\n * `DOMParser.parseFromString`), the tree is rebuilt keeping only known-safe tags\n * and attributes, then re-serialized. It is deliberately conservative and covers\n * the realistic threat model (LLM-emitted markup). For hardened, audited\n * coverage, register DOMPurify via `AparteConfig.setHtmlSanitizer`.\n */\n\nexport type AparteSanitizer = (html: string) => string;\n\n/** Tags dropped wholesale — content and all (never unwrapped to text). */\nconst DANGEROUS_TAGS = new Set([\n 'script', 'style', 'iframe', 'frame', 'frameset', 'object', 'embed',\n 'applet', 'form', 'button', 'textarea', 'select', 'option', 'optgroup',\n 'link', 'meta', 'base', 'title', 'head', 'html', 'body', 'template',\n 'noscript', 'svg', 'math', 'portal',\n]);\n\n/** Tags kept as-is (with attributes filtered). Anything else is unwrapped to its children. */\nconst ALLOWED_TAGS = new Set([\n 'a', 'p', 'br', 'hr', 'div', 'span', 'pre', 'code', 'kbd', 'samp', 'var',\n 'strong', 'b', 'em', 'i', 'u', 's', 'strike', 'del', 'ins', 'mark', 'small', 'sub', 'sup',\n 'blockquote', 'q', 'cite',\n 'ul', 'ol', 'li', 'dl', 'dt', 'dd',\n 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'table', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th', 'caption', 'colgroup', 'col',\n 'img', 'figure', 'figcaption', 'picture', 'source',\n 'input', // GFM task-list checkboxes only (attributes locked down below)\n 'abbr', 'details', 'summary', 'time', 'wbr', 'address',\n]);\n\n/** Attributes allowed on any element. */\nconst GLOBAL_ATTRS = new Set([\n // `id`/`name` are intentionally NOT allowlisted — they enable DOM clobbering\n // and LLM-authored markup has no legitimate need for author-controlled ids.\n 'class', 'style', 'title', 'dir', 'lang', 'role', 'align',\n 'aria-label', 'aria-hidden', 'aria-describedby', 'aria-level',\n]);\n\n/** Extra attributes allowed on specific tags. */\nconst TAG_ATTRS: Record<string, Set<string>> = {\n a: new Set(['href', 'target', 'rel']), // legacy `name` dropped — obsolete + a DOM-clobbering vector\n img: new Set(['src', 'alt', 'width', 'height', 'loading', 'srcset', 'sizes', 'decoding']),\n source: new Set(['src', 'srcset', 'type', 'media', 'sizes']),\n input: new Set(['type', 'checked', 'disabled']),\n td: new Set(['colspan', 'rowspan', 'headers']),\n th: new Set(['colspan', 'rowspan', 'scope', 'headers']),\n col: new Set(['span', 'width']),\n colgroup: new Set(['span']),\n ol: new Set(['start', 'type', 'reversed']),\n time: new Set(['datetime']),\n details: new Set(['open']),\n};\n\n/** URL-bearing attributes whose scheme must be validated. */\nconst URL_ATTRS = new Set(['href', 'src']);\n/** Schemes allowed in href/src. */\nconst SAFE_URL = /^(?:https?:|mailto:|tel:|ftp:|sms:)/i;\n/** In-page / relative references (no explicit scheme). */\nconst RELATIVE_URL = /^(?:[#/.?]|[a-z0-9._~%+-]+(?:[/?#]|$))/i;\n/** data: URLs are only honoured for images, and only for image media types. */\nconst SAFE_DATA_IMG = /^data:image\\/(?:png|jpe?g|gif|webp|avif|bmp|x-icon|svg\\+xml)?[;,]/i;\n/** Whitespace + C0 control chars, used to obfuscate a scheme (e.g. \" javascript:\" or \"java\\tscript:\"). */\n// eslint-disable-next-line no-control-regex -- stripping C0 control chars is intentional (anti-obfuscation)\nconst CONTROL_WS = /[\\u0000-\\u0020]+/g;\n\n/**\n * True when a URL is safe to place in a `href`/`src` attribute. Exported so a\n * streaming renderer (which bypasses the one-shot `sanitizeHtml`) can apply the\n * same URL policy live. `tag` is the host element ('a', 'img', …) — `data:image`\n * URLs are only allowed on `img`.\n */\nexport function isSafeUrl(value: string, tag: string): boolean {\n const v = value.replace(CONTROL_WS, '');\n if (!v) return true; // empty href/src is harmless\n if (SAFE_URL.test(v)) return true;\n if (tag === 'img' && SAFE_DATA_IMG.test(v)) return true;\n // Any other explicit scheme (javascript:, vbscript:, data: on non-img, …) is rejected.\n if (/^[a-z][a-z0-9+.-]*:/i.test(v)) return false;\n return RELATIVE_URL.test(v);\n}\n\n/**\n * Inert, presentational inline-style properties a syntax highlighter / markdown\n * renderer actually emits. An allowlist (not a blocklist) so layout & positioning\n * properties — `position`, `z-index`, `inset`, `top/left/right/bottom`, `width`,\n * `height`, `transform`, … — are dropped by default: those are what a hostile\n * `style` would need to build a full-viewport click-jacking overlay.\n */\nconst SAFE_STYLE_PROPS = new Set([\n 'color', 'background-color',\n 'font-style', 'font-weight', 'font-family', 'font-size',\n 'font-variant', 'font-variant-ligatures',\n 'text-decoration', 'text-decoration-line', 'text-decoration-color', 'text-decoration-style',\n 'white-space',\n]);\n\n/**\n * Keep only allowlisted inline-style declarations, and reject any `url()` beacon,\n * legacy `expression()`, or scheme even on an allowlisted property.\n */\nfunction scrubStyle(value: string): string | null {\n const kept: string[] = [];\n for (const decl of value.split(';')) {\n const trimmed = decl.trim();\n const idx = trimmed.indexOf(':');\n if (idx === -1) continue;\n const prop = trimmed.slice(0, idx).trim().toLowerCase();\n const val = trimmed.slice(idx + 1);\n if (!val.trim() || !SAFE_STYLE_PROPS.has(prop)) continue;\n if (/url\\s*\\(|expression\\s*\\(|javascript:|vbscript:|[<>]/i.test(val)) continue;\n kept.push(trimmed); // preserve the original declaration text (no reformatting)\n }\n return kept.length ? kept.join('; ') : null;\n}\n\nfunction copyAttributes(src: Element, dest: Element, tag: string): void {\n const extra = TAG_ATTRS[tag];\n for (const attr of Array.from(src.attributes)) {\n const name = attr.name.toLowerCase();\n const value = attr.value;\n // Event handlers and form-action overrides are never allowed.\n if (name.startsWith('on') || name.startsWith('formaction')) continue;\n // data-* attributes are inert (no execution path) and widely emitted by\n // markdown/highlight tooling — allowed, matching DOMPurify's default.\n if (!name.startsWith('data-') && !GLOBAL_ATTRS.has(name) && !(extra && extra.has(name))) continue;\n if (URL_ATTRS.has(name) && !isSafeUrl(value, tag)) continue;\n if (name === 'srcset' && /javascript:|vbscript:/i.test(value)) continue;\n if (name === 'style') {\n const scrubbed = scrubStyle(value);\n if (scrubbed === null) continue;\n dest.setAttribute('style', scrubbed);\n continue;\n }\n dest.setAttribute(name, value);\n }\n // Harden external links opened in a new tab against reverse-tabnabbing.\n if (tag === 'a' && dest.getAttribute('target') === '_blank') {\n dest.setAttribute('rel', 'noopener noreferrer');\n }\n}\n\nfunction sanitizeChildren(src: Node, dest: Node, doc: Document): void {\n for (const child of Array.from(src.childNodes)) {\n if (child.nodeType === 3 /* TEXT_NODE */) {\n dest.appendChild(doc.createTextNode(child.nodeValue ?? ''));\n continue;\n }\n if (child.nodeType !== 1 /* ELEMENT_NODE */) continue; // drop comments, CDATA, PIs\n const el = child as Element;\n const tag = el.tagName.toLowerCase();\n if (DANGEROUS_TAGS.has(tag)) continue; // remove entirely — do not surface its text\n if (!ALLOWED_TAGS.has(tag)) {\n sanitizeChildren(el, dest, doc); // unknown-but-benign → unwrap, keep sanitized children\n continue;\n }\n const clean = doc.createElement(tag);\n copyAttributes(el, clean, tag);\n sanitizeChildren(el, clean, doc);\n dest.appendChild(clean);\n }\n}\n\n/**\n * Best-effort scrub for environments WITHOUT a DOM parser (SSR/Node). A regex\n * pass cannot match a real HTML parser and has known evasions (split attributes,\n * unclosed tags, entity tricks): it is a safety net, **not** a security boundary.\n * For untrusted content on a non-browser runtime, register a real sanitizer\n * (e.g. DOMPurify + jsdom) via `AparteConfig.setHtmlSanitizer`.\n */\nfunction fallbackScrub(html: string): string {\n return html\n .replace(/<\\s*(script|style|iframe|object|embed|form|svg|math|applet|template)\\b[\\s\\S]*?<\\s*\\/\\s*\\1\\s*>/gi, '')\n .replace(/<\\s*(script|style|iframe|object|embed|applet|template|link|meta|base|frame|frameset)\\b[^>]*>/gi, '')\n .replace(/\\son[a-z]+\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]+)/gi, '')\n .replace(/(?:href|src|xlink:href)\\s*=\\s*(?:\"\\s*(?:javascript|vbscript|data):[^\"]*\"|'\\s*(?:javascript|vbscript|data):[^']*'|(?:javascript|vbscript|data):[^\\s>]*)/gi, '');\n}\n\n/**\n * The built-in sanitizer. Parses `html` with the platform DOMParser, rebuilds an\n * allowlisted tree, and re-serializes it. Falls back to a regex scrub only when\n * no DOMParser exists (non-browser runtime).\n */\nexport const defaultSanitizer: AparteSanitizer = (html: string): string => {\n if (!html) return html;\n if (typeof DOMParser === 'undefined') return fallbackScrub(html);\n let doc: Document;\n try {\n doc = new DOMParser().parseFromString(html, 'text/html');\n } catch {\n return fallbackScrub(html);\n }\n const container = doc.createElement('div');\n sanitizeChildren(doc.body, container, doc);\n return container.innerHTML;\n};\n","/**\n * AparteConfig\n * \n * Central configuration singleton for Aparte.\n * Manages providers for Markdown rendering, Syntax Highlighting, Icons, and Skeleton loading.\n * \n * \"Invisible but Flexible\": Works out-of-the-box with sensible defaults,\n * but allows complete customization via dependency injection.\n */\n\nimport { AparteIconProvider, AparteIconName, DEFAULT_ICON_FALLBACKS } from './icon-provider.js';\nimport { AparteAvatarProvider } from './avatar-provider.js';\nimport { AparteLocale, DEFAULT_LOCALE } from './locale.js';\nimport { AparteAction, AparteActionZone } from './action-provider.js';\nimport { AparteSkeletonProvider, AparteSkeletonType } from './skeleton-provider.js';\nimport type { AparteStatusRenderer } from './status-renderer.js';\nimport type { AparteErrorRenderer } from './error-renderer.js';\nimport type { AparteAttachmentRenderer } from './attachment-renderer.js';\nimport type { AparteSiblingNavRenderer } from './sibling-nav-renderer.js';\nimport type { AparteBubbleShellRenderer } from './bubble-shell-renderer.js';\nimport type { AparteAIProvider, AparteAIModel, AparteModelConfig } from '../types/model-provider.js';\nimport type { AparteTransport } from '../transport/index.js';\nimport { DirectTransport } from '../transport/index.js';\nimport type { AparteTool, AparteToolHandler, AparteToolRenderer } from '../types/tools.js';\nimport type { AparteBubbleActionsConfig, AparteBubbleActionName } from '../types/models.js';\nimport type { ConversationManager } from '../conversations/conversation-manager.js';\nimport { defaultSanitizer, type AparteSanitizer } from './sanitize.js';\nimport type { AparteElicitationPresenter, AparteElicitationRequest, AparteElicitationResult } from '../elicitation/types.js';\n\nexport type AparteMarkdownProvider = (raw: string) => string;\nexport type AparteHighlightProvider =\n | ((code: string, lang: string) => string)\n | ((code: string, lang: string) => Promise<string>);\nexport type AparteSystemPromptVarsProvider = () => Record<string, string>;\nexport type AparteLocaleProvider = AparteLocale;\nexport type AparteKeyProvider = (providerId: string) => string | Promise<string | undefined> | undefined;\n\nexport interface AparteModelPreference {\n provider: string;\n model: string;\n}\n\nexport interface AparteModelPreferenceProvider {\n /** Called whenever the selected provider+model changes */\n save: (provider: string, model: string) => void;\n /** Called on init to restore the previously saved selection. Return null if nothing stored. */\n load: () => AparteModelPreference | null;\n}\n\n/**\n * Incremental Markdown renderer bound to a target element. Created once per\n * streaming message: tokens are fed via `write()` and parsed + appended to the\n * DOM incrementally (O(n) total — no full re-parse / innerHTML rebuild on every\n * token). `end()` flushes any pending text when the stream completes.\n */\nexport interface AparteStreamingMarkdownRenderer {\n /** Append a chunk of Markdown text — parsed incrementally, appended as DOM nodes. */\n write(chunk: string): void;\n /** Finalize the stream (flush any pending text). */\n end(): void;\n}\n\n/**\n * Factory for an {@link AparteStreamingMarkdownRenderer}: given a target element,\n * returns a renderer that appends parsed Markdown into it. Supplied by an\n * opt-in provider package (e.g. `@aparte/provider-streaming-markdown`); when none\n * is registered the chat falls back to the one-shot `AparteMarkdownProvider`.\n */\nexport type AparteStreamingMarkdownProvider = (target: HTMLElement) => AparteStreamingMarkdownRenderer;\n\n/**\n * Builds the HTML document used as an artifact preview iframe `srcdoc` for a\n * given artifact kind (react/html/svg/js/css/…). Supplied by the consuming app\n * — e.g. a React/Babel/Tailwind live preview that loads those libs from a CDN.\n * Core ships only a CDN-free fallback (svg/css/html/js render offline; other\n * kinds degrade to a read-only code view), so the engine stays zero-network and\n * framework-agnostic. The app opts into richer previews via\n * {@link AparteConfigClass.setArtifactPreviewBuilder}.\n */\nexport type AparteArtifactPreviewBuilder = (kind: string, body: string, title: string) => string;\n\nexport class AparteConfigClass {\n private _markdownProvider?: AparteMarkdownProvider;\n private _streamingMarkdownProvider?: AparteStreamingMarkdownProvider;\n private _highlightProvider?: AparteHighlightProvider;\n // HTML sanitizer applied to markdown/highlight PROVIDER output (untrusted,\n // LLM-authored) before it is injected via innerHTML. Default = built-in\n // zero-dep allowlist sanitizer; `null` disables it (trusted content only).\n private _sanitizer: AparteSanitizer | null = defaultSanitizer;\n private _systemPromptTemplate?: string;\n private _systemPromptVarsProvider?: AparteSystemPromptVarsProvider;\n private _skeletonProvider?: AparteSkeletonProvider;\n private _statusRenderer?: AparteStatusRenderer;\n private _errorRenderer?: AparteErrorRenderer;\n private _attachmentRenderer?: AparteAttachmentRenderer;\n private _siblingNavRenderer?: AparteSiblingNavRenderer;\n private _bubbleShellRenderer?: AparteBubbleShellRenderer;\n private _iconProvider?: AparteIconProvider;\n private _avatarProvider?: AparteAvatarProvider;\n private _keyProvider?: AparteKeyProvider;\n private _artifactPreviewBuilder?: AparteArtifactPreviewBuilder;\n private _locale: AparteLocale = DEFAULT_LOCALE;\n private _actions: AparteAction[] = [];\n private _listeners: Set<() => void> = new Set();\n\n // AI Provider Management (BYORK)\n private _aiProviders: Map<string, AparteAIProvider> = new Map();\n private _modelConfig: AparteModelConfig = {};\n /** Opt-in: gate the composer (block send + grey out) until a model is selected. */\n private _requireModelSelection = false;\n // Transport: where chat requests go + how auth is handled (DirectTransport = browser-direct).\n private _transport: AparteTransport = new DirectTransport();\n private _modelPreferenceProvider?: AparteModelPreferenceProvider;\n\n // Conversation persistence (optional, agnostic)\n private _conversationManager?: ConversationManager;\n\n // Human-in-the-loop: presents typed input requests (ask_question,\n // tool approval, forms). Set by the <aparte-elicitation> Web Component.\n private _elicitationPresenter?: AparteElicitationPresenter;\n\n // Tool Registry\n private _tools: Map<string, { tool: AparteTool; handler: AparteToolHandler }> = new Map();\n private _toolRenderers: Map<string, AparteToolRenderer> = new Map();\n\n // Bubble Actions\n private _bubbleActionsConfig: AparteBubbleActionsConfig = { copy: true, retry: true, edit: true, feedback: false };\n\n // ─────────────────────────────────────────────────────────────────────────\n // Provider Setters (Dependency Injection)\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Register a custom action button. `zones` places it in the composer toolbar\n * and/or the message (bubble) toolbar. Re-registering the same id overwrites\n * it. Notifies mounted elements so they re-render.\n */\n registerAction(action: AparteAction): void {\n const existing = this._actions.findIndex(a => a.id === action.id);\n if (existing !== -1) {\n console.warn(`[AparteConfig] Action with ID \"${action.id}\" already registered. Overwriting.`);\n this._actions[existing] = action;\n } else {\n this._actions.push(action);\n }\n this._notify();\n }\n\n /** All registered actions for a zone, sorted by `order` (lower first). */\n getActions(zone: AparteActionZone): AparteAction[] {\n return this._actions\n .filter(a => a.zones.includes(zone))\n .sort((a, b) => (a.order || 0) - (b.order || 0));\n }\n\n /** Remove a custom action by id (from every zone); notifies mounted elements if it existed. */\n unregisterAction(id: string): void {\n const before = this._actions.length;\n this._actions = this._actions.filter(a => a.id !== id);\n if (this._actions.length !== before) this._notify();\n }\n\n /**\n * Show or hide a composer action button by id.\n * Triggers a config update so all mounted composer elements react immediately.\n */\n setActionHidden(id: string, hidden: boolean): void {\n const action = this._actions.find(a => a.id === id);\n if (action) {\n action.composer = { ...action.composer, hidden };\n this._notify();\n }\n }\n\n /**\n * Configure which action buttons appear in message bubbles.\n * Unset keys keep their defaults (copy=true, retry=true, edit=true, feedback=false).\n *\n * @example\n * AparteConfig.setBubbleActions({ feedback: true }) // enable feedback, keep rest\n * AparteConfig.setBubbleActions({ retry: false }) // disable retry only\n * AparteConfig.setBubbleActions({ copy: false, retry: false, edit: false }) // hide all\n * // Explicit per-role ordered sets (replace the flag defaults for that role):\n * AparteConfig.setBubbleActions({ user: ['edit', 'copy'], assistant: ['copy', 'thumbUp', 'thumbDown', 'retry'] })\n */\n setBubbleActions(config: AparteBubbleActionsConfig): void {\n this._bubbleActionsConfig = { ...this._bubbleActionsConfig, ...config };\n this._notify();\n }\n\n /** Returns the resolved bubble actions config (flag defaults applied; per-role sets passed through). */\n getBubbleActions(): {\n copy: boolean;\n retry: boolean;\n edit: boolean;\n feedback: boolean;\n user?: AparteBubbleActionName[];\n assistant?: AparteBubbleActionName[];\n } {\n return {\n copy: this._bubbleActionsConfig.copy ?? true,\n retry: this._bubbleActionsConfig.retry ?? true,\n edit: this._bubbleActionsConfig.edit ?? true,\n feedback: this._bubbleActionsConfig.feedback ?? false,\n user: this._bubbleActionsConfig.user,\n assistant: this._bubbleActionsConfig.assistant,\n };\n }\n\n /**\n * Set a custom Markdown renderer (e.g., marked, maritime-it)\n */\n setMarkdownProvider(fn: AparteMarkdownProvider): void {\n this._markdownProvider = fn;\n }\n\n /**\n * Set an incremental (streaming) Markdown renderer provider. Optional —\n * when set, the chat bubble uses it to render the assistant message\n * token-by-token DURING streaming (incremental parse + DOM append, O(n)),\n * instead of re-parsing the whole string on every token. The one-shot\n * `setMarkdownProvider` is still used for finished / re-rendered messages.\n */\n setStreamingMarkdownProvider(fn: AparteStreamingMarkdownProvider): void {\n this._streamingMarkdownProvider = fn;\n }\n\n /**\n * Create an incremental Markdown renderer bound to `target`, or `null` when\n * no streaming-markdown provider is registered (the caller then falls back\n * to the one-shot `renderMarkdown`).\n */\n createStreamingMarkdownRenderer(target: HTMLElement): AparteStreamingMarkdownRenderer | null {\n return this._streamingMarkdownProvider ? this._streamingMarkdownProvider(target) : null;\n }\n\n /**\n * Set a custom Syntax Highlighter (e.g., prism, highlight.js, shiki)\n * Supports both synchronous and asynchronous renderers.\n */\n setHighlightProvider(fn: AparteHighlightProvider): void {\n this._highlightProvider = fn;\n }\n\n /**\n * Whether a syntax-highlight provider is registered. Lets consumers (e.g.\n * the bubble) skip the highlight pass — and avoid replacing already-rendered\n * code blocks with the plain fallback — when no highlighter is installed.\n */\n hasHighlightProvider(): boolean {\n return !!this._highlightProvider;\n }\n\n /**\n * Replace or disable the HTML sanitizer applied to markdown/highlight\n * provider output before it is injected into the DOM. Defaults to a built-in\n * zero-dependency allowlist sanitizer.\n *\n * @param sanitizer A sanitizer function (e.g. a DOMPurify wrapper) for\n * hardened coverage, or `null` to DISABLE sanitization. Disabling exposes\n * you to XSS from LLM-authored content — only do so for content you fully\n * trust and have already sanitized upstream.\n * @example AparteConfig.setHtmlSanitizer((html) => DOMPurify.sanitize(html));\n */\n setHtmlSanitizer(sanitizer: AparteSanitizer | null): void {\n this._sanitizer = sanitizer;\n }\n\n /**\n * Run the active sanitizer over provider-produced HTML. Public so a\n * streaming-markdown provider (which appends DOM directly, bypassing\n * `renderMarkdown`) can apply the same policy. Returns the input unchanged\n * only when sanitization was disabled via `setHtmlSanitizer(null)`.\n */\n sanitizeHtml(html: string): string {\n return this._sanitizer ? this._sanitizer(html) : html;\n }\n\n /**\n * Set the system prompt template. Supports `{{key}}` placeholders resolved via setSystemPromptVarsProvider.\n * Pass undefined to clear.\n */\n setSystemPrompt(template: string | undefined): void {\n this._systemPromptTemplate = template;\n }\n\n /** Get the raw system prompt template (with unresolved placeholders). */\n getSystemPromptTemplate(): string | undefined {\n return this._systemPromptTemplate;\n }\n\n /**\n * Register a function that returns a map of variable name → value.\n * Called at request time to resolve `{{key}}` placeholders in the system prompt.\n * Example: () => ({ 'settings.lang': 'French' })\n */\n setSystemPromptVarsProvider(fn: AparteSystemPromptVarsProvider): void {\n this._systemPromptVarsProvider = fn;\n }\n\n /**\n * Resolve the system prompt template by substituting all `{{key}}` placeholders.\n * Returns null if no template is set or the template is empty after trimming.\n */\n resolveSystemPrompt(): string | null {\n if (!this._systemPromptTemplate?.trim()) return null;\n const vars = this._systemPromptVarsProvider ? this._systemPromptVarsProvider() : {};\n const resolved = this._systemPromptTemplate.replace(/\\{\\{([^}]+)\\}\\}/g, (_, key) => vars[key.trim()] ?? '');\n return resolved.trim() || null;\n }\n\n /**\n * Set a custom Skeleton generator for loading states\n * @param provider Object implementing AparteSkeletonProvider interface\n */\n setSkeletonProvider(provider: AparteSkeletonProvider): void {\n this._skeletonProvider = provider;\n }\n\n /**\n * Set a custom Icon provider (e.g., Lucide, FontAwesome, Material)\n * @param provider Object implementing AparteIconProvider interface\n */\n setIconProvider(provider: AparteIconProvider): void {\n this._iconProvider = provider;\n // Notify so already-rendered components (bubble action bars, composer\n // controls re-rendered by consumers) can pick up the new icon set —\n // e.g. a live skin switch. setBubbleActions already notifies.\n this._notify();\n }\n\n /**\n * Register an artifact preview builder (app-level). When set, the artifact\n * renderer uses it to build the preview iframe `srcdoc`; when unset, core's\n * CDN-free fallback is used. This is how the product opts into a\n * React/Babel/Tailwind live preview without leaking those into core.\n */\n setArtifactPreviewBuilder(builder: AparteArtifactPreviewBuilder): void {\n this._artifactPreviewBuilder = builder;\n }\n\n /** The registered artifact preview builder, or undefined for the core fallback. */\n getArtifactPreviewBuilder(): AparteArtifactPreviewBuilder | undefined {\n return this._artifactPreviewBuilder;\n }\n\n /**\n * Get the current icon provider, or a proxy that falls back to DEFAULT_ICON_FALLBACKS\n */\n getIconProvider(): AparteIconProvider {\n if (this._iconProvider) return this._iconProvider;\n // Build a fallback provider from DEFAULT_ICON_FALLBACKS\n return Object.fromEntries(\n Object.entries(DEFAULT_ICON_FALLBACKS).map(([k, v]) => [k, () => v])\n ) as unknown as AparteIconProvider;\n }\n\n /**\n * Set a custom avatar renderer. Lets framework consumers (Angular,\n * React, Vue, …) mount live components in place of the default\n * avatar text/image. Cleared by passing `null`.\n */\n setAvatarProvider(provider: AparteAvatarProvider | null): void {\n this._avatarProvider = provider ?? undefined;\n // Notify like every other live-renderer swap so mounted bubbles re-render.\n this._notify();\n }\n\n /** Returns the registered avatar provider, or null if none. */\n getAvatarProvider(): AparteAvatarProvider | null {\n return this._avatarProvider ?? null;\n }\n\n /**\n * Set a custom typing-indicator renderer. Replaces the inner markup of\n * `<aparte-chat-status>` (avatar + animated dots + text) while the element keeps\n * owning show/hide. Return a string or an HTMLElement. Cleared by passing\n * `null`. Notifies mounted components so a live skin switch re-renders.\n */\n setStatusRenderer(renderer: AparteStatusRenderer | null): void {\n this._statusRenderer = renderer ?? undefined;\n this._notify();\n }\n\n /** Returns the registered status renderer, or null if none. */\n getStatusRenderer(): AparteStatusRenderer | null {\n return this._statusRenderer ?? null;\n }\n\n /**\n * Set a custom error renderer. Drives the content of error bubbles (the\n * built-in `error` segment) — return a string or an HTMLElement, e.g. a\n * friendly message with a retry button. The bubble also carries `data-error`\n * on its `.aparte-message` while errored, for CSS theming. Cleared with `null`.\n * Notifies mounted components so a live change re-renders.\n */\n setErrorRenderer(renderer: AparteErrorRenderer | null): void {\n this._errorRenderer = renderer ?? undefined;\n this._notify();\n }\n\n /** Returns the registered error renderer, or null if none. */\n getErrorRenderer(): AparteErrorRenderer | null {\n return this._errorRenderer ?? null;\n }\n\n /**\n * Set a custom attachment renderer. Replaces the chip rendered for each\n * attachment on a user message (default: image thumbnail / file chip) — return\n * a string or an HTMLElement, e.g. a PDF preview. You own the interactions for\n * custom output (see {@link AparteAttachmentRenderer}). Cleared with `null`.\n * Notifies mounted components so a live change re-renders.\n */\n setAttachmentRenderer(renderer: AparteAttachmentRenderer | null): void {\n this._attachmentRenderer = renderer ?? undefined;\n this._notify();\n }\n\n /** Returns the registered attachment renderer, or null if none. */\n getAttachmentRenderer(): AparteAttachmentRenderer | null {\n return this._attachmentRenderer ?? null;\n }\n\n /**\n * Set a custom sibling (branch) position indicator. Replaces the `‹ N / M ›`\n * counter between the prev/next arrows — e.g. dots. Return a string or an\n * HTMLElement (see {@link AparteSiblingNavRenderer}); the arrows keep their\n * behavior. Cleared with `null`. Notifies mounted components.\n */\n setSiblingNavRenderer(renderer: AparteSiblingNavRenderer | null): void {\n this._siblingNavRenderer = renderer ?? undefined;\n this._notify();\n }\n\n /** Returns the registered sibling-nav renderer, or null if none. */\n getSiblingNavRenderer(): AparteSiblingNavRenderer | null {\n return this._siblingNavRenderer ?? null;\n }\n\n /**\n * Set a custom bubble shell renderer (advanced) — replaces the structural\n * skeleton of `<aparte-chat-bubble>` while keeping its behavior. The shell must\n * honor the class-hook contract (root `.aparte-message`, region hooks) — see\n * {@link AparteBubbleShellRenderer}. For a fully custom element use `renderBubble`\n * (wrapper) instead. Cleared with `null`. Notifies mounted components.\n */\n setBubbleShellRenderer(renderer: AparteBubbleShellRenderer | null): void {\n this._bubbleShellRenderer = renderer ?? undefined;\n this._notify();\n }\n\n /** Returns the registered bubble-shell renderer, or null if none. */\n getBubbleShellRenderer(): AparteBubbleShellRenderer | null {\n return this._bubbleShellRenderer ?? null;\n }\n\n /**\n * Set the current locale\n * @param locale AparteLocale object defining all strings\n */\n setLocale(locale: AparteLocale): void {\n this._locale = locale;\n // A runtime language switch must propagate to already-mounted components,\n // same as every other live setter.\n this._notify();\n }\n\n /**\n * Get the current locale\n */\n getLocale(): AparteLocale {\n return this._locale;\n }\n\n /**\n * Extend the current locale with partial translations.\n * Useful for plugins to register their own strings.\n * @param translations Partial locale object to merge\n */\n extendLocale(translations: Partial<AparteLocale>): void {\n this._locale = { ...this._locale, ...translations };\n this._notify();\n }\n\n /**\n * Get icon HTML string by name\n * Falls back to textual representation if no provider is set\n */\n getIcon(name: AparteIconName): string {\n if (this._iconProvider && this._iconProvider[name]) {\n return this._iconProvider[name]();\n }\n return DEFAULT_ICON_FALLBACKS[name];\n }\n\n /**\n * Set a custom Key provider (e.g., AparteVault override)\n */\n setKeyProvider(provider: AparteKeyProvider): void {\n this._keyProvider = provider;\n }\n\n /**\n * Get API key for a provider\n */\n async getKey(providerId: string): Promise<string | undefined> {\n if (this._keyProvider) {\n return await this._keyProvider(providerId);\n }\n return undefined;\n }\n\n /**\n * Refresh models for a specific provider\n * Orchestrates: Key Retrieval -> Fetch -> Return\n * This keeps UI components unaware of keys.\n */\n async refreshProviderModels(providerId: string): Promise<AparteAIModel[]> {\n const provider = this._aiProviders.get(providerId);\n if (!provider || !provider.fetchModels) return [];\n\n try {\n const apiKey = await this.getKey(providerId);\n // apiKey may be undefined for keyless local providers (e.g. LMStudio) — provider handles it\n return await provider.fetchModels(apiKey);\n } catch (error) {\n console.warn(`[AparteConfig] Failed to refresh models for ${providerId}`, error);\n return [];\n }\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // AI Provider Management (BYORK - Bring Your Own Key)\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Register one or more AI providers (e.g., OpenRouter, Gemini, Anthropic)\n * @param providers AparteAIProvider implementations\n * @example\n * AparteConfig.registerAIProvider(OpenRouterProvider);\n * AparteConfig.registerAIProvider(GeminiProvider, AnthropicProvider);\n */\n registerAIProvider(...providers: AparteAIProvider[]): void {\n for (const provider of providers) {\n if (!provider) continue;\n if (this._aiProviders.has(provider.id)) {\n console.warn(`[AparteConfig] AI Provider \"${provider.id}\" already registered. Overwriting.`);\n }\n this._aiProviders.set(provider.id, provider);\n }\n this._notify(); // Notify when providers change\n }\n\n /**\n * Unregister an AI provider\n */\n unregisterAIProvider(id: string): void {\n this._aiProviders.delete(id);\n // Notify like registerAIProvider does, so a mounted model-selector drops\n // the removed provider instead of showing a stale list.\n this._notify();\n }\n\n /**\n * Get all registered AI providers\n * Optionally filtered by enabled providers in config\n */\n getAIProviders(): AparteAIProvider[] {\n const all = Array.from(this._aiProviders.values());\n const enabled = this._modelConfig.enabledProviders;\n if (enabled?.length) {\n const result = all.filter(p => enabled.includes(p.id));\n return result;\n }\n return all;\n }\n\n /**\n * Get a specific AI provider by ID\n */\n getAIProvider(id: string): AparteAIProvider | undefined {\n return this._aiProviders.get(id);\n }\n\n /**\n * Set the transport that decides where chat requests go and how auth is\n * handled. Defaults to {@link DirectTransport} (browser-direct — BYOK/local).\n * Use a `BackendTransport` to keep API keys server-side (recommended for\n * production).\n */\n setTransport(transport: AparteTransport): void {\n this._transport = transport;\n }\n\n /** Get the active transport (DirectTransport by default). */\n getTransport(): AparteTransport {\n return this._transport;\n }\n\n /**\n * Register a model preference provider for agnostic persistence.\n * The host app decides how/where to store the selected provider & model.\n * @example\n * AparteConfig.setModelPreferenceProvider({\n * save: (p, m) => localStorage.setItem('model', JSON.stringify({p, m})),\n * load: () => JSON.parse(localStorage.getItem('model') ?? 'null')\n * });\n */\n setModelPreferenceProvider(provider: AparteModelPreferenceProvider): void {\n this._modelPreferenceProvider = provider;\n }\n\n /**\n * Restore previously saved model preference via the registered provider.\n * Should be called once at app startup, before any component mounts.\n * No-op if no provider is registered or nothing was saved.\n */\n restoreModelPreference(): AparteModelPreference | null {\n if (!this._modelPreferenceProvider) return null;\n const pref = this._modelPreferenceProvider.load();\n if (pref?.provider && pref?.model) {\n // Apply silently without triggering save again\n this._modelConfig = { ...this._modelConfig, defaultProvider: pref.provider, defaultModel: pref.model };\n this._notify();\n return pref;\n }\n return null;\n }\n\n /**\n * Set model selection configuration\n */\n setModelConfig(config: AparteModelConfig): void {\n this._modelConfig = { ...this._modelConfig, ...config };\n\n // Auto-save preference if a provider is registered and we have a full selection\n if (this._modelPreferenceProvider && this._modelConfig.defaultProvider && this._modelConfig.defaultModel) {\n this._modelPreferenceProvider.save(this._modelConfig.defaultProvider, this._modelConfig.defaultModel);\n }\n\n this._notify();\n }\n\n /**\n * Subscribe to configuration changes\n * @returns Unsubscribe function\n */\n subscribe(callback: () => void): () => void {\n this._listeners.add(callback);\n return () => this._listeners.delete(callback);\n }\n\n private _notify(): void {\n // Each listener is isolated: one throwing/slow subscriber must not abort\n // the loop and starve the others of the notification.\n this._listeners.forEach(cb => {\n try { cb(); } catch (err) {\n console.error('[AparteConfig] A config-change listener threw', err);\n }\n });\n // Also dispatch a browser event for maximum agnosticism. `config` lets\n // listeners ignore changes to a config that isn't theirs — components\n // resolving to a different instance (or the global) skip the rebuild\n // instead of every bubble on the page reacting to every config's change.\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent('aparte-config-change', {\n detail: { config: this, modelConfig: this._modelConfig },\n }));\n }\n }\n\n /**\n * Get current model configuration\n */\n getModelConfig(): AparteModelConfig {\n return { ...this._modelConfig };\n }\n\n /**\n * True when the model config has BOTH a provider and a model selected — i.e.\n * the chat can actually send. Used by the composer's `require-model` gate.\n */\n hasSelectedModel(): boolean {\n return !!(this._modelConfig.defaultProvider && this._modelConfig.defaultModel);\n }\n\n /**\n * Opt-in UX: when enabled, `<aparte-composer>` blocks sending and greys out\n * until {@link hasSelectedModel} is true (e.g. while the model selector is\n * still fetching its list). Off by default so single-model / backend setups\n * that never select a model are unaffected.\n */\n setRequireModelSelection(required: boolean): void {\n if (this._requireModelSelection === required) return;\n this._requireModelSelection = required;\n this._notify();\n }\n\n /** Whether the composer should gate on model selection (see {@link setRequireModelSelection}). */\n getRequireModelSelection(): boolean {\n return this._requireModelSelection;\n }\n\n /**\n * Get the currently selected model object, if available synchronously.\n * Returns undefined if no provider/model is selected, or if the provider's\n * models are only available asynchronously (fetchModels).\n */\n getCurrentModel(): AparteAIModel | undefined {\n const { defaultProvider, defaultModel } = this._modelConfig;\n if (!defaultProvider || !defaultModel) return undefined;\n const provider = this._aiProviders.get(defaultProvider);\n if (!provider) return undefined;\n const models = provider.getModels();\n if (models instanceof Promise) return undefined;\n return (models as AparteAIModel[]).find(m => m.id === defaultModel);\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Public Rendering Methods (with Fallbacks)\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Get translated string by key\n */\n t(key: keyof AparteLocale): string {\n const val = this._locale[key] || DEFAULT_LOCALE[key];\n // Ensure we always return a string for template interpolation\n // For optional properties like 'direction', this might need specific handling or casting\n return (val === undefined) ? '' : val;\n }\n\n /**\n * Render Markdown to HTML\n * Fallback: Escapes HTML and converts newlines to <br>\n */\n renderMarkdown(raw: string): string {\n if (this._markdownProvider) {\n try {\n // Provider output is untrusted (LLM-authored) → sanitize before it hits innerHTML.\n return this.sanitizeHtml(this._markdownProvider(raw));\n } catch (error) {\n console.warn('[AparteConfig] Markdown provider failed, using fallback:', error);\n }\n }\n // The default renderer already HTML-escapes — no sanitization needed.\n return this._defaultMarkdownRenderer(raw);\n }\n\n /**\n * Highlight code block\n * Fallback: Returns raw code wrapped in <pre><code>\n * Supports Promise if provider is async/streaming\n */\n async highlightCode(code: string, lang: string): Promise<string> {\n if (this._highlightProvider) {\n try {\n const result = this._highlightProvider(code, lang);\n const html = result instanceof Promise ? await result : result;\n // Highlighter output is derived from LLM code blocks → sanitize before innerHTML.\n return this.sanitizeHtml(html);\n } catch (error) {\n console.warn('[AparteConfig] Highlight provider failed, using fallback:', error);\n }\n }\n return this._defaultHighlightRenderer(code);\n }\n\n /**\n * Get HTML for a skeleton loader\n * Fallback: Simple CSS-animated box\n */\n getSkeleton(type: AparteSkeletonType): string {\n if (this._skeletonProvider) {\n return this._skeletonProvider.getSkeleton(type);\n }\n return this._defaultSkeletonRenderer(type);\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Tool Registry\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Register a tool and its handler together.\n * The handler is called when the AI invokes the tool during streaming.\n * @example\n * AparteConfig.registerTool(askQuestionTool, askQuestionHandler);\n */\n registerTool(tool: AparteTool, handler: AparteToolHandler): void {\n this._tools.set(tool.name, { tool, handler });\n }\n\n /** Unregister a tool by name */\n unregisterTool(name: string): void {\n this._tools.delete(name);\n }\n\n /** Get all registered tool definitions (to pass in AparteChatRequest) */\n getTools(): AparteTool[] {\n return Array.from(this._tools.values()).map(e => e.tool);\n }\n\n /** Get the handler for a tool by name */\n getToolHandler(name: string): AparteToolHandler | undefined {\n return this._tools.get(name)?.handler;\n }\n\n /**\n * Register a per-tool segment renderer.\n * Controls what appears in the chat bubble when the AI calls this tool.\n * Use this instead of the generic `tool_call` segment renderer for tool-specific UI.\n *\n * @example\n * // Hide the segment entirely (UI-only tool like ask_question)\n * AparteConfig.registerToolRenderer('ask_question', { render: () => '' });\n *\n * @example\n * // Custom pill for a web-search tool\n * AparteConfig.registerToolRenderer('web_search', { render: (seg) => `<div class=\"tool-pill\">Searching...</div>` });\n */\n registerToolRenderer(toolName: string, renderer: AparteToolRenderer): void {\n this._toolRenderers.set(toolName, renderer);\n }\n\n /** Unregister a per-tool renderer */\n unregisterToolRenderer(toolName: string): void {\n this._toolRenderers.delete(toolName);\n }\n\n /** Get the renderer for a specific tool name. Returns undefined if none registered. */\n getToolRenderer(toolName: string): AparteToolRenderer | undefined {\n return this._toolRenderers.get(toolName);\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Conversation Manager (optional, agnostic persistence layer)\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Register a ConversationManager so any UI controller can persist & load\n * conversations without coupling to a framework wrapper.\n */\n setConversationManager(manager: ConversationManager): void {\n this._conversationManager = manager;\n }\n\n /** Returns the registered ConversationManager, or undefined if none. */\n getConversationManager(): ConversationManager | undefined {\n return this._conversationManager;\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Elicitation (human-in-the-loop typed input)\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Register the presenter that renders typed input requests (a choice, a\n * confirmation, a text field, a form) and resolves with the user's answer.\n * The `<aparte-elicitation>` Web Component registers itself here; an app can\n * override with its own framework-native presenter. Pass `null` to clear.\n */\n setElicitationPresenter(presenter: AparteElicitationPresenter | null): void {\n this._elicitationPresenter = presenter ?? undefined;\n }\n\n /** The registered elicitation presenter, or undefined if none. */\n getElicitationPresenter(): AparteElicitationPresenter | undefined {\n return this._elicitationPresenter;\n }\n\n /**\n * Ask the user for typed input mid-run and await their response. This is the\n * generic primitive behind `ask_question` and tool approval — the KIND of\n * question is the schema, not a bespoke tool. Resolves `accept` with the\n * value, `decline` when the user declines, or `cancel` when the turn is\n * cancelled. With no presenter registered it resolves `cancel` (nothing can\n * present it) rather than hanging.\n */\n requestUserInput(request: AparteElicitationRequest): Promise<AparteElicitationResult> {\n if (!this._elicitationPresenter) return Promise.resolve({ action: 'cancel' });\n return this._elicitationPresenter(request);\n }\n\n /**\n * Reset ALL configuration back to defaults — providers, registries, model\n * selection and bubble actions. Previously left `_aiProviders` / `_tools` /\n * `_toolRenderers` / `_modelConfig` behind, which leaked across SPA\n * navigations (registries only ever grew). Now a full reset.\n */\n reset(): void {\n this._markdownProvider = undefined;\n this._streamingMarkdownProvider = undefined;\n this._highlightProvider = undefined;\n this._systemPromptTemplate = undefined;\n this._systemPromptVarsProvider = undefined;\n this._skeletonProvider = undefined;\n this._statusRenderer = undefined;\n this._errorRenderer = undefined;\n this._attachmentRenderer = undefined;\n this._siblingNavRenderer = undefined;\n this._bubbleShellRenderer = undefined;\n this._iconProvider = undefined;\n this._avatarProvider = undefined;\n this._artifactPreviewBuilder = undefined;\n this._keyProvider = undefined;\n this._conversationManager = undefined;\n this._elicitationPresenter = undefined;\n this._locale = DEFAULT_LOCALE;\n this._actions = [];\n this._sanitizer = defaultSanitizer;\n // Registries — the leak the audit flagged.\n this._aiProviders.clear();\n this._tools.clear();\n this._toolRenderers.clear();\n this._modelConfig = {};\n this._requireModelSelection = false;\n this._modelPreferenceProvider = undefined;\n this._bubbleActionsConfig = { copy: true, retry: true, edit: true, feedback: false };\n this._notify();\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Default Fallbacks (Zero-dependency)\n // ─────────────────────────────────────────────────────────────────────────\n\n private _defaultMarkdownRenderer(raw: string): string {\n // Simple security: Escape HTML tags\n const escaped = this._escapeHtml(raw);\n // Convert newlines to breaks\n return escaped.replace(/\\n/g, '<br>');\n }\n\n private _defaultHighlightRenderer(code: string): string {\n return `<pre><code>${this._escapeHtml(code)}</code></pre>`;\n }\n\n private _defaultSkeletonRenderer(type: AparteSkeletonType): string {\n // Minimal fallback - no CSS animations, no heavy styling\n // Register a richer skeleton via setSkeletonProvider for shimmer effects\n const fallbacks: Record<AparteSkeletonType, string> = {\n message: '<div class=\"aparte-skeleton-fallback\" style=\"padding:16px;color:#9ca3af;\">Loading message...</div>',\n code: '<div class=\"aparte-skeleton-fallback\" style=\"padding:16px;background:#1e293b;color:#64748b;border-radius:8px;\">Loading code...</div>',\n text: '<div class=\"aparte-skeleton-fallback\" style=\"padding:8px;color:#9ca3af;\">Loading...</div>',\n thinking: '<div class=\"aparte-skeleton-fallback\" style=\"padding:8px;color:#9ca3af;\">Thinking...</div>',\n input: '<div class=\"aparte-skeleton-fallback\" style=\"padding:12px;color:#9ca3af;\">...</div>',\n list: '<div class=\"aparte-skeleton-fallback\" style=\"padding:16px;color:#9ca3af;\">Loading items...</div>',\n };\n return fallbacks[type] || fallbacks.text;\n }\n\n private _escapeHtml(text: string): string {\n return text\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Export Singleton\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst GLOBAL_CONFIG_KEY = '__APARTE_CONFIG_SINGLETON__';\n\nfunction getGlobalConfig(): AparteConfigClass {\n if (typeof window !== 'undefined') {\n if (!(window as unknown as Record<string, AparteConfigClass>)[GLOBAL_CONFIG_KEY]) {\n (window as unknown as Record<string, AparteConfigClass>)[GLOBAL_CONFIG_KEY] = new AparteConfigClass();\n }\n return (window as unknown as Record<string, AparteConfigClass>)[GLOBAL_CONFIG_KEY]!;\n }\n // Fallback for non-browser environments (e.g., SSR, tests)\n return new AparteConfigClass();\n}\n\n/**\n * Global configuration singleton for Aparte.\n * Use this to register providers and configure behavior.\n */\nexport const AparteConfig = getGlobalConfig();\n\n// Inject default styles for skeletons if needed (optional)\n// Note: In a real app we might want to use a stylesheet or shadow DOM styles\n","/**\n * Per-instance config resolution (Phase 2, step 1 — the seam only).\n *\n * `AparteConfig` is a global singleton: one config for the whole page. That makes\n * two independent chats on one page impossible (changing the model of one\n * changes the other) and couples every component to global state.\n *\n * This module adds an OPTIONAL instance boundary without touching behaviour:\n * a host element can carry its own {@link AparteConfigClass} instance, and any\n * component resolves \"its\" config by walking up to the nearest such host —\n * falling back to the global singleton when there is none. Until a caller\n * attaches an instance config, `resolveConfig` always returns the global, so\n * single-chat apps are unaffected.\n *\n * Step 2 migrates the ~23 component read-sites from `AparteConfig.x()` to\n * `resolveConfig(this).x()`. This step just ships the mechanism + tests.\n */\n\nimport { AparteConfig, AparteConfigClass } from './aparte-config.js';\n\n/** Marks an element as an instance-config boundary (used by `closest()`). */\nexport const APARTE_HOST_ATTR = 'data-aparte-host';\n\n/** Non-enumerable slot holding the instance config on a boundary element. */\nconst CONFIG_SLOT = Symbol.for('aparte.instanceConfig');\n\ninterface ConfigHost {\n [CONFIG_SLOT]?: AparteConfigClass;\n}\n\n/**\n * Attach an instance config to `el`, making it a resolution boundary: every\n * component inside `el` resolves to `config` instead of the global singleton.\n * Idempotent; pass the same element to replace its config.\n */\nexport function attachConfig(el: HTMLElement, config: AparteConfigClass): void {\n (el as unknown as ConfigHost)[CONFIG_SLOT] = config;\n el.setAttribute(APARTE_HOST_ATTR, '');\n}\n\n/**\n * Remove the instance-config boundary from `el` (e.g. in `disconnectedCallback`).\n * Components under `el` fall back to the next boundary up, or the global.\n */\nexport function detachConfig(el: HTMLElement): void {\n delete (el as unknown as ConfigHost)[CONFIG_SLOT];\n el.removeAttribute(APARTE_HOST_ATTR);\n}\n\n/**\n * Resolve the config governing `el`: the nearest ancestor boundary's instance\n * config (including `el` itself), or the global {@link AparteConfig} when none is\n * present. Cheap — a single `closest()` — but callers should cache the result at\n * `connectedCallback` rather than call it per render.\n */\nexport function resolveConfig(el: Element | null | undefined): AparteConfigClass {\n const host = el?.closest?.(`[${APARTE_HOST_ATTR}]`) as (Element & ConfigHost) | null | undefined;\n return host?.[CONFIG_SLOT] ?? AparteConfig;\n}\n\n// ─── Render context ──────────────────────────────────────────────────────────\n// Segment/tool renderers are plain functions — `render(segment)` receives no\n// element to resolve from. Instead, the component invoking them (e.g. the\n// bubble) wraps the call in `runWithConfig(resolveConfig(this), …)`, and the\n// renderer reads `contextConfig()`. Synchronous only: async continuations must\n// capture the config BEFORE awaiting (`const cfg = contextConfig()` at the top).\n\nlet _renderConfig: AparteConfigClass | null = null;\n\n/** Run `fn` with `config` as the ambient render config (restored after). */\nexport function runWithConfig<T>(config: AparteConfigClass, fn: () => T): T {\n const prev = _renderConfig;\n _renderConfig = config;\n try {\n return fn();\n } finally {\n _renderConfig = prev;\n }\n}\n\n/**\n * The ambient render config set by {@link runWithConfig}, else the config\n * resolved from `el` (when provided), else the global singleton. Capture it\n * synchronously at the top of a renderer — never after an `await`.\n */\nexport function contextConfig(el?: Element | null): AparteConfigClass {\n return _renderConfig ?? resolveConfig(el);\n}\n","/**\n * Skeleton Provider Interface\n * \n * Defines the contract for skeleton/loading state plugins.\n * Returns HTML strings for various loading states.\n * \n * @example\n * // Register a skeleton provider (e.g. an Angular ngx-skeleton-loader bridge)\n * AparteConfig.setSkeletonProvider({\n * message: () => '<ngx-skeleton-loader count=\"3\"></ngx-skeleton-loader>',\n * code: () => '<ngx-skeleton-loader appearance=\"line\" count=\"5\"></ngx-skeleton-loader>',\n * // ...\n * });\n */\n\n/** Skeleton types available in the provider */\nexport type AparteSkeletonType =\n | 'message' // Chat message loading state\n | 'code' // Code block loading state\n | 'thinking' // AI thinking indicator\n | 'input' // Input field loading\n | 'list' // List items loading\n | 'text'; // Generic text loading\n\nexport interface AparteSkeletonProvider {\n /** Get skeleton HTML for a specific type */\n getSkeleton: (type: AparteSkeletonType) => string;\n}\n\n/** \n * Minimal fallback skeletons - zero CSS animations, just structure\n * Used when no skeleton plugin is installed\n */\nexport const DEFAULT_SKELETON_FALLBACKS: Record<AparteSkeletonType, string> = {\n message: '<div class=\"aparte-skeleton-fallback\" style=\"padding:16px;color:#9ca3af;\">Loading...</div>',\n code: '<div class=\"aparte-skeleton-fallback\" style=\"padding:16px;background:#1e293b;color:#64748b;border-radius:8px;\">Loading code...</div>',\n thinking: '<div class=\"aparte-skeleton-fallback\" style=\"padding:8px;color:#9ca3af;\">Thinking...</div>',\n input: '<div class=\"aparte-skeleton-fallback\" style=\"padding:12px;color:#9ca3af;\">...</div>',\n list: '<div class=\"aparte-skeleton-fallback\" style=\"padding:16px;color:#9ca3af;\">Loading items...</div>',\n text: '<div class=\"aparte-skeleton-fallback\" style=\"padding:8px;color:#9ca3af;\">...</div>',\n};\n","/**\n * Aparte Segment Renderers\n * Built-in renderers for core segment types\n */\n\nimport type {\n AparteSegmentBase,\n AparteSegmentRenderer,\n AparteTextSegment,\n AparteThinkingSegment,\n AparteCodeSegment,\n AparteTerminalSegment,\n AparteErrorSegment,\n AparteProgressSegment,\n AparteFileTreeSegment,\n AparteFileNode,\n AparteArtifactSegment,\n AparteToolCallSegment,\n} from '../types/index.js';\n// Renderers are plain functions: they read the ambient config set by the\n// invoking component (runWithConfig), falling back to an element when one is\n// in scope for late executions (event handlers, window-event callbacks) —\n// see config-context.ts. `contextConfig()` with no element = ambient or global.\nimport { contextConfig } from '../config/index.js';\nimport type { AparteStreamingMarkdownRenderer } from '../config/index.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction escapeHtml(str: string): string {\n let out = '';\n for (let i = 0; i < str.length; i++) {\n const ch = str[i];\n if (ch === '&') out += '&amp;';\n else if (ch === '<') out += '&lt;';\n else if (ch === '>') out += '&gt;';\n else if (ch === '\"') out += '&quot;';\n else if (ch === \"'\") out += '&#039;';\n else out += ch;\n }\n return out;\n}\n\n/**\n * Strip leading/trailing markdown code fences (``` or ~~~, optional lang tag).\n * Also strips any content that appears after the closing fence (small models\n * frequently duplicate lines after the closing ``` block).\n *\n * Char-based scanner — no regex.\n */\nfunction stripCodeFences(content: string): string {\n let s = content;\n\n // Opening fence: leading ``` or ~~~ (3+) optionally followed by language tag,\n // then a newline. Walk the start of the string only.\n if (s.startsWith('```') || s.startsWith('~~~')) {\n const fenceChar = s[0];\n let i = 0;\n while (i < s.length && s[i] === fenceChar) i++;\n // Skip language tag chars (anything until newline)\n while (i < s.length && s[i] !== '\\n') i++;\n // Skip the newline itself if present\n if (i < s.length && s[i] === '\\n') i++;\n s = s.slice(i);\n }\n\n // Closing fence: scan forward to find a line that is exclusively `````/`~~~`+\n // optionally followed by trailing whitespace; cut there + everything after.\n const closeAt = findClosingFence(s);\n if (closeAt !== -1) s = s.slice(0, closeAt);\n\n return s.trim();\n}\n\n/** Find the byte offset where a closing fence line begins, or -1 if none. */\nfunction findClosingFence(s: string): number {\n let i = 0;\n while (i < s.length) {\n // Find start of next line\n const lineStart = i;\n // Skip leading whitespace on this line (indentation)\n let k = lineStart;\n while (k < s.length && (s[k] === ' ' || s[k] === '\\t')) k++;\n if (k < s.length && (s[k] === '`' || s[k] === '~')) {\n const fenceChar = s[k];\n let runs = 0;\n while (k < s.length && s[k] === fenceChar) { runs++; k++; }\n if (runs >= 3) {\n // The rest of the line should be whitespace only — otherwise\n // it's not a closing fence (e.g. inline backtick text).\n let onlyWs = true;\n while (k < s.length && s[k] !== '\\n') {\n if (s[k] !== ' ' && s[k] !== '\\t' && s[k] !== '\\r') { onlyWs = false; break; }\n k++;\n }\n if (onlyWs) {\n // Cut at start of this line; if a `\\n` precedes, drop it too\n let cut = lineStart;\n if (cut > 0 && s[cut - 1] === '\\n') cut--;\n return cut;\n }\n }\n }\n // Advance to next line\n while (i < s.length && s[i] !== '\\n') i++;\n if (i < s.length) i++;\n }\n return -1;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Renderer Registry\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst renderers = new Map<string, AparteSegmentRenderer>();\nlet styleElement: HTMLStyleElement | null = null;\n\n/**\n * Register a segment renderer\n */\nexport function registerSegmentRenderer<T extends AparteSegmentBase>(\n renderer: AparteSegmentRenderer<T>\n): void {\n renderers.set(renderer.type, renderer as AparteSegmentRenderer);\n injectRendererStyles();\n}\n\n/**\n * Unregister a segment renderer\n */\nexport function unregisterSegmentRenderer(type: string): void {\n renderers.delete(type);\n}\n\n/**\n * Get renderer for a segment type\n */\nexport function getSegmentRenderer(type: string): AparteSegmentRenderer | undefined {\n return renderers.get(type);\n}\n\n/**\n * Get all registered renderers\n */\nexport function getAllRenderers(): readonly AparteSegmentRenderer[] {\n return Array.from(renderers.values());\n}\n\n/**\n * Collect all renderer styles\n */\nexport function collectRendererStyles(): string {\n return Array.from(renderers.values())\n .map(r => r.getStyles?.() || '')\n .filter(Boolean)\n .join('\\n');\n}\n\n/**\n * Inject renderer styles into the document head\n */\nexport function injectRendererStyles(): void {\n if (typeof document === 'undefined') return;\n\n if (!styleElement) {\n styleElement = document.createElement('style');\n styleElement.id = 'aparte-renderer-styles';\n document.head.appendChild(styleElement);\n }\n\n styleElement.textContent = collectRendererStyles();\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Text Renderer\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Text segment renderer.\n *\n * During streaming, if an incremental Markdown provider is registered\n * (`@aparte/provider-streaming-markdown`), each new chunk is fed to a persistent\n * incremental parser that *appends* DOM nodes — O(n) over the whole message,\n * with no per-token re-parse or `innerHTML` rebuild (that per-token render +\n * GPU paint storm is what starves the model's WebGPU decode). It also renders\n * partial Markdown live — `**bold` shows bold before the closing `**` arrives.\n *\n * A NON-streaming update (`isStreaming === false`) flushes the parser with\n * `end()` — which emits its buffered token-lookahead tail, e.g. a trailing\n * emoji — and re-renders once with the one-shot `renderMarkdown` for full\n * fidelity. `populateBubbleFromMessage` stamps `isStreaming: false` on the\n * segments of a settled message so a load / re-sync takes this safe path.\n */\ntype TextStreamHost = HTMLElement & {\n /**\n * Incremental-render state for this segment element:\n * `undefined` → not started · `null` → no provider (one-shot fallback)\n * object → active incremental renderer + chars already written.\n */\n _aparteSmd?: { renderer: AparteStreamingMarkdownRenderer; written: number } | null;\n};\n\nconst textRenderer: AparteSegmentRenderer<AparteTextSegment> = {\n type: 'text',\n render: (segment) => `<div class=\"segment segment-text\" data-segment-id=\"${escapeHtml(segment.id)}\"><div class=\"segment-content\">${contextConfig().renderMarkdown(segment.content)}</div></div>`,\n update: (el, segment) => {\n const contentEl = el.querySelector('.segment-content');\n if (!contentEl) return;\n const host = el as TextStreamHost;\n const streaming = segment.isStreaming !== false;\n\n if (streaming) {\n // Lazily create an incremental renderer on the first streaming update.\n if (host._aparteSmd === undefined) {\n const renderer = contextConfig().createStreamingMarkdownRenderer(contentEl as HTMLElement);\n if (renderer) {\n contentEl.textContent = ''; // drop the skeleton — smd appends from scratch\n host._aparteSmd = { renderer, written: 0 };\n } else {\n host._aparteSmd = null; // no provider → one-shot fallback below\n }\n }\n const smd = host._aparteSmd;\n if (smd) {\n const delta = segment.content.slice(smd.written);\n if (delta) {\n smd.renderer.write(delta);\n smd.written = segment.content.length;\n }\n return;\n }\n // smd === null → fall through to the one-shot render.\n } else if (host._aparteSmd) {\n // Stream finished — flush the incremental parser (emits its buffered\n // trailing characters), then re-render once below with the one-shot\n // provider for full Markdown fidelity.\n host._aparteSmd.renderer.end();\n host._aparteSmd = undefined;\n }\n\n contentEl.innerHTML = contextConfig().renderMarkdown(segment.content);\n },\n getStyles: () => ``\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Thinking Renderer\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst thinkingRenderer: AparteSegmentRenderer<AparteThinkingSegment> = {\n type: 'thinking',\n render: (segment) => `<details class=\"segment segment-thinking\" data-segment-id=\"${escapeHtml(segment.id)}\" ${segment.collapsed ? '' : 'open'}><summary class=\"thinking-header\"><span class=\"thinking-label\">${escapeHtml(segment.label || contextConfig().t('thinking'))}</span><span class=\"thinking-toggle\"></span></summary><div class=\"thinking-content\">${escapeHtml(segment.content)}</div></details>`,\n update: (el, segment) => {\n // collapsed state is managed by _applySegmentUpdate based on explicit updates only —\n // never override what the user set by clicking <summary>\n const contentEl = el.querySelector('.thinking-content');\n if (contentEl) contentEl.textContent = segment.content;\n },\n getStyles: () => ``\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Code Renderer\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst codeRenderer: AparteSegmentRenderer<AparteCodeSegment> = {\n type: 'code',\n render: (segment) => `\n <div class=\"segment segment-code\" data-segment-id=\"${escapeHtml(segment.id)}\">\n <div class=\"code-header\">\n ${segment.filename\n ? `<span class=\"code-filename\">${escapeHtml(segment.filename)}</span>`\n : `<span class=\"code-header-filler\"></span>`}\n <span class=\"code-language\">${escapeHtml(segment.language || '')}</span>\n <button class=\"code-copy\" data-action=\"copy\" title=\"${contextConfig().t('copy')}\">\n ${contextConfig().getIcon('copy')}\n </button>\n </div>\n <div class=\"code-content-wrapper\">\n <pre><code class=\"language-${escapeHtml(segment.language || 'text')}\">${escapeHtml(segment.content)}</code></pre>\n </div>\n </div>\n `,\n setup: (element, segment) => {\n // Async highlight: replace plain <pre><code> with highlighted HTML once ready\n const wrapper = element.querySelector('.code-content-wrapper');\n if (wrapper) {\n void contextConfig().highlightCode(segment.content, segment.language || '').then(html => {\n wrapper.innerHTML = html;\n }).catch(() => { /* best-effort: a failed highlight degrades silently */ });\n }\n\n const copyBtn = element.querySelector('.code-copy');\n if (copyBtn) {\n copyBtn.addEventListener('click', () => {\n // Late execution (user click) — the ambient render config is\n // gone; resolve from the connected element instead.\n void navigator.clipboard.writeText(segment.content || '').catch(() => { /* best-effort: a failed clipboard write degrades silently */ });\n copyBtn.innerHTML = contextConfig(copyBtn).getIcon('check');\n copyBtn.setAttribute('title', contextConfig(copyBtn).t('copied'));\n setTimeout(() => {\n copyBtn.innerHTML = contextConfig(copyBtn).getIcon('copy');\n copyBtn.setAttribute('title', contextConfig(copyBtn).t('copy'));\n }, 1500);\n });\n }\n },\n update: (element, segment) => {\n if (segment.isStreaming) {\n // During streaming: update raw text only to avoid firing highlight on every token.\n // The code-content-wrapper may contain either the plain <pre><code> (initial render)\n // or highlighted HTML (from a previous async highlight). Update the innermost\n // <code> element if present; otherwise fall back to the wrapper itself.\n const codeEl = element.querySelector('.code-content-wrapper code');\n if (codeEl) {\n codeEl.textContent = segment.content;\n } else {\n const wrapper = element.querySelector('.code-content-wrapper');\n if (wrapper) wrapper.innerHTML = `<pre><code class=\"language-${escapeHtml(segment.language || 'text')}\">${escapeHtml(segment.content)}</code></pre>`;\n }\n } else {\n // Streaming complete — run the highlight provider for polished output.\n const wrapper = element.querySelector('.code-content-wrapper');\n if (wrapper) {\n void contextConfig().highlightCode(segment.content, segment.language || '').then(html => {\n wrapper.innerHTML = html;\n }).catch(() => { /* best-effort: a failed highlight degrades silently */ });\n }\n }\n },\n getStyles: () => ``\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Terminal Renderer\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst terminalRenderer: AparteSegmentRenderer<AparteTerminalSegment> = {\n type: 'terminal',\n render: (segment) => `\n <div class=\"segment segment-terminal\" data-segment-id=\"${escapeHtml(segment.id)}\">\n <div class=\"terminal-command-block\">\n <div class=\"terminal-icon\">\n ${contextConfig().getIcon('terminal')}\n </div>\n <code class=\"terminal-command\">${escapeHtml(segment.command || '')}</code>\n <div class=\"terminal-actions\">\n ${segment.isRunning\n ? `<span class=\"terminal-running\"><span class=\"spinner\"></span>${contextConfig().t('running')}</span>`\n : `<button class=\"terminal-run-btn\" data-action=\"run\" aria-label=\"${contextConfig().t('run')}\" title=\"${contextConfig().t('run')}\">${contextConfig().t('run')}</button>`}\n <button class=\"terminal-copy-btn\" data-action=\"copy\" aria-label=\"${contextConfig().t('copy')}\" title=\"${contextConfig().t('copy')}\">\n ${contextConfig().getIcon('copy')}\n </button>\n </div>\n </div>\n ${segment.output ? `<div class=\"terminal-output\">${escapeHtml(segment.output)}</div>` : ''}\n ${segment.exitCode !== undefined && segment.exitCode !== 0\n ? `<div class=\"terminal-error\">Command failed with exit code ${segment.exitCode}</div>`\n : ''}\n </div>\n `,\n setup: (element) => {\n const copyBtn = element.querySelector('.terminal-copy-btn');\n const command = element.querySelector('.terminal-command');\n if (copyBtn && command) {\n copyBtn.addEventListener('click', () => {\n // Late execution (user click) — resolve from the element.\n void navigator.clipboard.writeText(command.textContent || '').catch(() => { /* best-effort: a failed clipboard write degrades silently */ });\n copyBtn.innerHTML = contextConfig(copyBtn).getIcon('check');\n copyBtn.setAttribute('title', contextConfig(copyBtn).t('copied'));\n setTimeout(() => {\n copyBtn.innerHTML = contextConfig(copyBtn).getIcon('copy');\n copyBtn.setAttribute('title', contextConfig(copyBtn).t('copy'));\n }, 1500);\n });\n }\n\n // Run button dispatches a custom event\n const runBtn = element.querySelector('.terminal-run-btn');\n if (runBtn) {\n runBtn.addEventListener('click', () => {\n const segmentId = element.getAttribute('data-segment-id');\n element.dispatchEvent(new CustomEvent('aparte-terminal-run', {\n bubbles: true,\n composed: true,\n detail: {\n segmentId,\n command: command?.textContent || ''\n }\n }));\n });\n }\n },\n getStyles: () => ``\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error Renderer\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst errorRenderer: AparteSegmentRenderer<AparteErrorSegment> = {\n type: 'error',\n render: (segment) => {\n // A registered error renderer (AparteConfig.setErrorRenderer) owns the error\n // UI — the one place to customize it, string or live HTMLElement.\n const custom = contextConfig().getErrorRenderer?.();\n if (custom) {\n const out = custom({ message: segment.content, details: segment.details });\n if (out instanceof HTMLElement) {\n // Tag the root so in-place segment updates can still target it.\n out.setAttribute('data-segment-id', segment.id);\n return out;\n }\n return out;\n }\n return `\n <div class=\"segment segment-error\" data-segment-id=\"${escapeHtml(segment.id)}\">\n <div class=\"error-icon-wrapper\">\n ${contextConfig().getIcon('error') || '⚠'}\n </div>\n <div class=\"error-content\">\n <div class=\"error-title\">Error</div>\n <div class=\"error-message\">${escapeHtml(segment.content)}</div>\n ${segment.details ? `<div class=\"error-details\">${escapeHtml(segment.details)}</div>` : ''}\n </div>\n </div>\n `;\n },\n getStyles: () => ``\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Progress Renderer\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst progressRenderer: AparteSegmentRenderer<AparteProgressSegment> = {\n type: 'progress',\n render: (segment) => {\n const label = escapeHtml(segment.label || 'Progress');\n const pct = Math.round(segment.percent || 0);\n return `<div class=\"segment segment-progress\" data-segment-id=\"${escapeHtml(segment.id)}\"><div class=\"progress-header\"><span class=\"progress-label\">${label}</span><span class=\"progress-value\">${pct}%</span></div><div class=\"progress-bar\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" aria-valuenow=\"${pct}\" aria-label=\"${label}\"><div class=\"progress-fill\" style=\"width: ${pct}%\"></div></div></div>`;\n },\n getStyles: () => ``\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// File Tree Renderer\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction renderFileNode(node: AparteFileNode, depth = 0): string {\n const indent = depth * 16;\n const icon = node.type === 'directory' ? '📁' : '📄';\n const statusClass = node.status ? `file-status-${escapeHtml(node.status)}` : '';\n\n let html = `<div class=\"file-node ${statusClass}\" style=\"padding-left: ${indent}px\"><span class=\"file-icon\">${icon}</span><span class=\"file-name\">${escapeHtml(node.name)}</span></div>`;\n\n if (node.children) {\n for (const child of node.children) {\n html += renderFileNode(child, depth + 1);\n }\n }\n\n return html;\n}\n\nconst fileTreeRenderer: AparteSegmentRenderer<AparteFileTreeSegment> = {\n type: 'file-tree',\n render: (segment) => {\n let filesHtml = '';\n if (segment.files) {\n for (const file of segment.files) {\n filesHtml += renderFileNode(file, 0);\n }\n }\n\n return `<div class=\"segment segment-file-tree\" data-segment-id=\"${escapeHtml(segment.id)}\">${segment.title ? `<div class=\"file-tree-title\">${escapeHtml(segment.title)}</div>` : ''}<div class=\"file-tree-content\">${filesHtml}</div></div>`;\n },\n getStyles: () => ``\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Exports\n// ─────────────────────────────────────────────────────────────────────────────\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Tool Call Renderer (default — shown when no per-tool renderer is registered)\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst toolCallRenderer: AparteSegmentRenderer<AparteToolCallSegment> = {\n type: 'tool_call',\n render: (segment) => {\n const name = segment.toolCall?.name ?? 'tool';\n const status = segment.status ?? 'pending';\n const toolCallId = segment.toolCall?.id ?? '';\n\n // Human-in-the-loop gate — built-in Approve/Reject. Shown even when a\n // per-tool renderer exists: approval precedes the tool's own UI.\n if (status === 'awaiting-approval') {\n const loc = contextConfig().getLocale();\n const approve = loc.approveTool ?? 'Approve';\n const reject = loc.rejectTool ?? 'Reject';\n return `\n <div class=\"segment segment-tool-call\" data-segment-id=\"${escapeHtml(segment.id)}\" data-status=\"awaiting-approval\" data-tool-call-id=\"${escapeAttr(toolCallId)}\">\n <span class=\"tool-pill\">\n <span class=\"tool-pill-icon\">${contextConfig().getIcon('tool')}</span>\n <span class=\"tool-pill-name\">${escapeHtml(name)}</span>\n </span>\n <span class=\"tool-approval\" role=\"group\" aria-label=\"${escapeAttr(name)}\">\n <button type=\"button\" class=\"tool-approve-btn\" data-tool-decision=\"approve\" aria-label=\"${escapeAttr(approve)}\">${escapeHtml(approve)}</button>\n <button type=\"button\" class=\"tool-reject-btn\" data-tool-decision=\"reject\" aria-label=\"${escapeAttr(reject)}\">${escapeHtml(reject)}</button>\n </span>\n </div>\n `;\n }\n\n // Delegate to a per-tool renderer if one is registered\n const customRenderer = contextConfig().getToolRenderer(segment.toolCall?.name);\n if (customRenderer) {\n const html = customRenderer.render(segment);\n if (html) return html;\n }\n\n // Status/tool glyphs come from the icon provider (fallbacks: ✓ / ✕ / 🔧)\n // so icon packs and skins restyle the pill like everything else.\n const statusIcon = status === 'resolved' ? contextConfig().getIcon('check') : (status === 'aborted' || status === 'rejected') ? contextConfig().getIcon('close') : '';\n const spinner = status === 'pending'\n ? `<span class=\"tool-pill-spinner\" aria-hidden=\"true\"></span>`\n : '';\n return `\n <div class=\"segment segment-tool-call\" data-segment-id=\"${escapeHtml(segment.id)}\" data-status=\"${status}\">\n <span class=\"tool-pill\">\n <span class=\"tool-pill-icon\">${contextConfig().getIcon('tool')}</span>\n <span class=\"tool-pill-name\">${escapeHtml(name)}</span>\n ${spinner}\n ${statusIcon ? `<span class=\"tool-pill-status\">${statusIcon}</span>` : ''}\n </span>\n </div>\n `;\n },\n setup: (element, segment) => {\n // Built-in approval gate: wire Approve/Reject → aparte-tool-decision.\n if (segment.status === 'awaiting-approval') {\n const toolCallId = segment.toolCall?.id;\n if (!toolCallId) return;\n const decide = (approved: boolean) => element.dispatchEvent(new CustomEvent('aparte-tool-decision', {\n bubbles: true, composed: true, detail: { toolCallId, approved }\n }));\n element.querySelector('[data-tool-decision=\"approve\"]')?.addEventListener('click', () => decide(true));\n element.querySelector('[data-tool-decision=\"reject\"]')?.addEventListener('click', () => decide(false));\n return;\n }\n // Delegate setup to per-tool renderer if registered\n const customRenderer = contextConfig().getToolRenderer(segment.toolCall?.name);\n customRenderer?.setup?.(element, segment);\n },\n getStyles: () => `\n .segment-tool-call { display: flex; padding: 2px 0; }\n .tool-pill {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 3px 10px 3px 7px;\n border-radius: 99px;\n font-size: 0.78rem;\n font-weight: 500;\n border: 1px solid var(--aparte-border, rgba(0,0,0,0.12));\n background: var(--aparte-surface, #f8f8f8);\n color: var(--aparte-text-secondary, rgba(0,0,0,0.55));\n user-select: none;\n }\n [data-status=\"resolved\"] .tool-pill {\n border-color: var(--aparte-success-border, rgba(34,197,94,0.3));\n background: var(--aparte-success-surface, rgba(34,197,94,0.06));\n color: var(--aparte-success, rgb(21,128,61));\n }\n [data-status=\"aborted\"] .tool-pill,\n [data-status=\"rejected\"] .tool-pill {\n border-color: var(--aparte-error-border, rgba(239,68,68,0.3));\n background: var(--aparte-error-surface, rgba(239,68,68,0.06));\n color: var(--aparte-error, rgb(185,28,28));\n }\n .tool-approval { display: inline-flex; gap: 6px; margin-left: 8px; vertical-align: middle; }\n .tool-approve-btn, .tool-reject-btn {\n font: inherit; font-size: 0.78rem; font-weight: 600; line-height: 1;\n padding: 4px 12px; border-radius: 99px; cursor: pointer;\n border: 1px solid var(--aparte-border, rgba(0,0,0,0.12));\n background: var(--aparte-surface, #f8f8f8);\n }\n .tool-approve-btn { color: var(--aparte-success, rgb(21,128,61)); border-color: var(--aparte-success-border, rgba(34,197,94,0.4)); }\n .tool-reject-btn { color: var(--aparte-error, rgb(185,28,28)); border-color: var(--aparte-error-border, rgba(239,68,68,0.4)); }\n .tool-approve-btn:hover { background: var(--aparte-success-surface, rgba(34,197,94,0.1)); }\n .tool-reject-btn:hover { background: var(--aparte-error-surface, rgba(239,68,68,0.1)); }\n .tool-pill-spinner {\n width: 10px; height: 10px;\n border: 1.5px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n display: inline-block;\n animation: tool-spin 0.7s linear infinite;\n }\n .tool-pill-status { font-size: 0.75rem; }\n @keyframes tool-spin { to { transform: rotate(360deg); } }\n `\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Pipeline Waiting Renderer — pulsing dots between pipeline phases\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst pipelineWaitingRenderer: AparteSegmentRenderer = {\n type: 'pipeline-waiting',\n render: (segment) => {\n return `\n <div class=\"segment segment-pipeline-waiting\" data-segment-id=\"${escapeHtml(segment.id)}\" aria-label=\"Generating…\" role=\"status\">\n <span class=\"pw-dot\"></span>\n <span class=\"pw-dot\"></span>\n <span class=\"pw-dot\"></span>\n </div>`;\n },\n update: () => { /* nothing to update */ },\n setup: (el) => {\n // Auto-remove when a sibling segment appears after this element.\n // This makes it a true \"last-child only\" segment — no manual removeSegment needed.\n const parent = el.parentElement;\n if (!parent) return;\n const observer = new MutationObserver(() => {\n if (el.nextElementSibling) {\n observer.disconnect();\n el.remove();\n }\n });\n observer.observe(parent, { childList: true });\n },\n getStyles: () => `\n .segment-pipeline-waiting {\n display: flex;\n align-items: center;\n gap: 5px;\n padding: 6px 2px;\n min-height: 28px;\n }\n .pw-dot {\n display: inline-block;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--aparte-text-muted, #6b7280);\n opacity: 0.3;\n animation: pw-pulse 1.2s ease-in-out infinite;\n }\n .pw-dot:nth-child(2) { animation-delay: 0.2s; }\n .pw-dot:nth-child(3) { animation-delay: 0.4s; }\n @keyframes pw-pulse {\n 0%, 80%, 100% { opacity: 0.3; transform: scale(0.85); }\n 40% { opacity: 1; transform: scale(1.1); }\n }\n `\n};\n\nexport function registerDefaultRenderers(): void {\n registerSegmentRenderer(textRenderer);\n registerSegmentRenderer(thinkingRenderer);\n registerSegmentRenderer(codeRenderer);\n registerSegmentRenderer(terminalRenderer);\n registerSegmentRenderer(errorRenderer);\n registerSegmentRenderer(progressRenderer);\n registerSegmentRenderer(fileTreeRenderer);\n registerSegmentRenderer(toolCallRenderer);\n registerSegmentRenderer(artifactRenderer);\n registerSegmentRenderer(pipelineWaitingRenderer);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Artifact Renderer — INLINE CARD with Code/Preview tabs\n//\n// Replaces the previous \"pill that opens a side panel\" UX. The artifact now\n// lives directly inside the chat as a card the user can interact with:\n// - Code tab: syntax-highlighted source (always available)\n// - Preview tab: sandboxed iframe (only for previewable kinds)\n// - Actions: copy, download\n//\n// During streaming the Code tab is active and the iframe is not built. As soon\n// as `isStreaming` flips to false, the card switches to Preview (when\n// previewable) and lazily builds the srcdoc.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst PREVIEWABLE_KINDS: ReadonlySet<string> = new Set(['react', 'html', 'svg', 'js', 'css']);\n/** Binary file kinds — output of orchestrator's sandbox path. They are\n * code-only here (downloaded by FileGenService listener side-channel). */\nconst BINARY_FILE_KINDS: ReadonlySet<string> = new Set(['pdf', 'xlsx', 'docx']);\n\nconst artifactRenderer: AparteSegmentRenderer<AparteArtifactSegment> = {\n type: 'artifact',\n render: (segment: AparteArtifactSegment) => {\n const kind = (segment.artifactType || 'unknown').toLowerCase();\n // Binary file kinds (xlsx/pdf/docx) follow a separate UX track : the\n // generated JS is implementation noise the user doesn't care about,\n // so we hide it. Streaming → terminal-like progress, then\n // `aparte-file-gen-ready` swaps to a file card with download + preview.\n if (BINARY_FILE_KINDS.has(kind)) {\n return renderBinaryFileArtifact(segment, kind);\n }\n const title = segment.title?.trim() || labelForKind(kind);\n const displayLang = languageForKind(kind);\n const isStreaming = !!segment.isStreaming;\n const previewable = PREVIEWABLE_KINDS.has(kind);\n const isBinary = BINARY_FILE_KINDS.has(kind);\n const cleanContent = stripCodeFences(segment.content || '');\n // Default tab: code while streaming OR for non-previewable kinds.\n // Preview otherwise (final stage of a previewable artifact).\n const initialTab: 'code' | 'preview' =\n isStreaming || !previewable ? 'code' : 'preview';\n\n const buildPreview = contextConfig().getArtifactPreviewBuilder() ?? buildSafePreviewDocument;\n const previewSrcdoc = !isStreaming && previewable\n ? buildPreview(kind, cleanContent, title)\n : '';\n\n return `\n <div class=\"segment segment-artifact-card\"\n data-segment-id=\"${escapeHtml(segment.id)}\"\n data-artifact-type=\"${escapeHtml(kind)}\"\n data-streaming=\"${isStreaming ? 'true' : 'false'}\"\n data-tab=\"${initialTab}\"\n data-previewable=\"${previewable ? 'true' : 'false'}\"\n data-binary=\"${isBinary ? 'true' : 'false'}\">\n <header class=\"aparte-art-card__header\">\n <div class=\"aparte-art-card__title-block\">\n <span class=\"aparte-art-card__kind\" data-kind=\"${escapeHtml(kind)}\">${escapeHtml(displayLang)}</span>\n <span class=\"aparte-art-card__title\">${escapeHtml(title)}</span>\n ${isStreaming ? '<span class=\"aparte-art-card__pulse\" aria-label=\"Streaming\"></span>' : ''}\n </div>\n <div class=\"aparte-art-card__actions\">\n <button type=\"button\" class=\"aparte-art-card__btn\" data-action=\"copy\" title=\"${contextConfig().t('copy')}\" aria-label=\"Copy\">\n ${contextConfig().getIcon('copy')}\n </button>\n <button type=\"button\" class=\"aparte-art-card__btn\" data-action=\"download\" title=\"Download\" aria-label=\"Download\" ${isStreaming ? 'disabled' : ''}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 2v9m0 0l-3-3m3 3l3-3M2 13h12\"/></svg>\n </button>\n </div>\n </header>\n <nav class=\"aparte-art-card__tabs\" role=\"tablist\">\n ${previewable ? `<button type=\"button\" role=\"tab\" data-tab-target=\"preview\" ${initialTab === 'preview' ? 'aria-selected=\"true\"' : ''} ${isStreaming ? 'disabled' : ''}>Preview</button>` : ''}\n <button type=\"button\" role=\"tab\" data-tab-target=\"code\" ${initialTab === 'code' ? 'aria-selected=\"true\"' : ''}>Code</button>\n </nav>\n <div class=\"aparte-art-card__body\">\n <div class=\"aparte-art-card__pane\" data-pane=\"code\">\n <div class=\"code-content-wrapper\">\n <pre><code class=\"language-${escapeHtml(displayLang)}\">${escapeHtml(cleanContent)}</code></pre>\n </div>\n </div>\n ${previewable ? `\n <div class=\"aparte-art-card__pane\" data-pane=\"preview\">\n ${previewSrcdoc ? `<iframe class=\"aparte-art-card__frame\" sandbox=\"allow-scripts\" referrerpolicy=\"no-referrer\" loading=\"lazy\" title=\"${escapeHtml(title)}\" srcdoc=\"${escapeAttr(previewSrcdoc)}\"></iframe>` : '<div class=\"aparte-art-card__pending\">Generating preview…</div>'}\n </div>\n ` : ''}\n </div>\n </div>\n `;\n },\n setup: (element: HTMLElement, segment: AparteArtifactSegment) => {\n const kind = (segment.artifactType || '').toLowerCase();\n if (BINARY_FILE_KINDS.has(kind)) {\n setupBinaryFileArtifact(element, segment, kind);\n return;\n }\n // Async highlight on the code pane\n const wrapper = element.querySelector('.code-content-wrapper');\n if (wrapper) {\n const displayLang = languageForKind(kind);\n const cleanContent = stripCodeFences(segment.content || '');\n void contextConfig().highlightCode(cleanContent, displayLang).then(html => {\n wrapper.innerHTML = html;\n }).catch(() => { /* best-effort: a failed highlight degrades silently */ });\n }\n\n // Tab switching\n element.querySelectorAll<HTMLButtonElement>('[data-tab-target]').forEach(btn => {\n btn.addEventListener('click', () => {\n const target = btn.getAttribute('data-tab-target');\n if (!target) return;\n element.setAttribute('data-tab', target);\n element.querySelectorAll<HTMLButtonElement>('[data-tab-target]').forEach(b => {\n b.setAttribute('aria-selected', b === btn ? 'true' : 'false');\n });\n });\n });\n\n // Copy\n const copyBtn = element.querySelector<HTMLButtonElement>('[data-action=\"copy\"]');\n if (copyBtn) {\n copyBtn.addEventListener('click', () => {\n // Late execution (user click) — resolve from the element.\n const code = stripCodeFences(segment.content || '');\n void navigator.clipboard.writeText(code).catch(() => { /* best-effort: a failed clipboard write degrades silently */ });\n const original = copyBtn.innerHTML;\n copyBtn.innerHTML = contextConfig(copyBtn).getIcon('check');\n copyBtn.setAttribute('title', contextConfig(copyBtn).t('copied'));\n setTimeout(() => {\n copyBtn.innerHTML = original;\n copyBtn.setAttribute('title', contextConfig(copyBtn).t('copy'));\n }, 1500);\n });\n }\n\n // Download — emits aparte-artifact-download for the host app to handle\n // (binary kinds are handled by FileGenService side-channel; for\n // previewable/text kinds we trigger a download from raw content).\n const dlBtn = element.querySelector<HTMLButtonElement>('[data-action=\"download\"]');\n if (dlBtn) {\n dlBtn.addEventListener('click', () => {\n if (dlBtn.disabled) return;\n const kind = (segment.artifactType || '').toLowerCase();\n const isBinary = BINARY_FILE_KINDS.has(kind);\n if (isBinary) {\n // Re-dispatch the artifact-ready event so FileGenService\n // re-runs the sandbox and downloads the file.\n element.dispatchEvent(new CustomEvent('aparte-artifact-redownload', {\n bubbles: true,\n composed: true,\n detail: {\n segmentId: segment.id,\n mimeType: segment.mimeType,\n artifactType: segment.artifactType,\n title: segment.title,\n content: stripCodeFences(segment.content || ''),\n },\n }));\n return;\n }\n downloadTextArtifact(segment);\n });\n }\n },\n update: (element: HTMLElement, segment: AparteArtifactSegment) => {\n const isStreaming = !!segment.isStreaming;\n const kind = (segment.artifactType || '').toLowerCase();\n if (BINARY_FILE_KINDS.has(kind)) {\n updateBinaryFileArtifact(element, segment, isStreaming);\n return;\n }\n const previewable = PREVIEWABLE_KINDS.has(kind);\n const wasStreaming = element.getAttribute('data-streaming') === 'true';\n const cleanContent = stripCodeFences(segment.content || '');\n\n // 1. Live-update the code pane during streaming\n const codeEl = element.querySelector('.code-content-wrapper code');\n if (codeEl) {\n codeEl.textContent = cleanContent;\n } else {\n const wrapper = element.querySelector('.code-content-wrapper');\n if (wrapper) {\n const displayLang = languageForKind(kind);\n wrapper.innerHTML = `<pre><code class=\"language-${escapeHtml(displayLang)}\">${escapeHtml(cleanContent)}</code></pre>`;\n }\n }\n // Debounced syntax-highlight during streaming so the user sees\n // colors progressively rather than only at stream-end.\n if (isStreaming) {\n const segId = element.getAttribute('data-segment-id') ?? segment.id;\n debounceHighlight(element, '.code-content-wrapper', cleanContent, languageForKind(kind), segId);\n }\n\n // 2. On stream-completion: highlight + build preview iframe + auto-switch\n if (wasStreaming && !isStreaming) {\n element.setAttribute('data-streaming', 'false');\n\n // Re-run syntax highlight now that content is final\n const wrapper = element.querySelector('.code-content-wrapper');\n if (wrapper) {\n const displayLang = languageForKind(kind);\n void contextConfig().highlightCode(cleanContent, displayLang).then(html => {\n wrapper.innerHTML = html;\n }).catch(() => { /* best-effort: a failed highlight degrades silently */ });\n }\n\n // Enable previously-disabled buttons (download, preview tab)\n element.querySelectorAll<HTMLButtonElement>('button[disabled]').forEach(b => {\n b.disabled = false;\n });\n\n // Build preview srcdoc lazily\n if (previewable) {\n const pane = element.querySelector('.aparte-art-card__pane[data-pane=\"preview\"]');\n if (pane) {\n const title = segment.title?.trim() || labelForKind(kind);\n const buildPreview = contextConfig().getArtifactPreviewBuilder() ?? buildSafePreviewDocument;\n const srcdoc = buildPreview(kind, cleanContent, title);\n pane.innerHTML = `<iframe class=\"aparte-art-card__frame\" sandbox=\"allow-scripts\" referrerpolicy=\"no-referrer\" loading=\"lazy\" title=\"${escapeAttr(title)}\" srcdoc=\"${escapeAttr(srcdoc)}\"></iframe>`;\n }\n // Auto-switch to preview only if user hasn't explicitly chosen code\n const currentTab = element.getAttribute('data-tab');\n if (currentTab !== 'code') {\n element.setAttribute('data-tab', 'preview');\n element.querySelectorAll<HTMLButtonElement>('[data-tab-target]').forEach(b => {\n b.setAttribute('aria-selected', b.getAttribute('data-tab-target') === 'preview' ? 'true' : 'false');\n });\n }\n }\n }\n },\n getStyles: () => `\n .segment-artifact-card {\n display: flex; flex-direction: column;\n margin: 8px 0;\n border: 1px solid var(--aparte-border, rgba(0,0,0,0.12));\n border-radius: 12px;\n background: var(--aparte-surface, #fff);\n overflow: hidden;\n font: inherit;\n color: var(--aparte-text, inherit);\n }\n .aparte-art-card__header {\n display: flex; align-items: center; justify-content: space-between;\n padding: 8px 10px;\n border-bottom: 1px solid var(--aparte-border, rgba(0,0,0,0.08));\n background: var(--aparte-surface-2, rgba(0,0,0,0.02));\n min-height: 36px;\n }\n .aparte-art-card__title-block { display: flex; align-items: center; gap: 8px; min-width: 0; }\n .aparte-art-card__kind {\n font-size: 0.7rem; font-weight: 600;\n text-transform: uppercase; letter-spacing: 0.04em;\n padding: 2px 6px; border-radius: 4px;\n background: var(--aparte-surface, #fff);\n border: 1px solid var(--aparte-border, rgba(0,0,0,0.1));\n color: var(--aparte-text-muted, rgba(0,0,0,0.6));\n }\n .aparte-art-card__title {\n font-size: 0.85rem; font-weight: 500;\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n }\n .aparte-art-card__pulse {\n width: 8px; height: 8px; border-radius: 50%;\n background: var(--aparte-accent, #3b82f6);\n animation: aparte-art-card-pulse 1.2s ease-in-out infinite;\n }\n @keyframes aparte-art-card-pulse {\n 0%, 100% { opacity: 0.35; transform: scale(0.85); }\n 50% { opacity: 1; transform: scale(1.1); }\n }\n .aparte-art-card__actions { display: flex; gap: 4px; }\n .aparte-art-card__btn {\n display: inline-flex; align-items: center; justify-content: center;\n width: 28px; height: 28px;\n background: transparent;\n border: 1px solid transparent;\n border-radius: 6px;\n color: var(--aparte-text-muted, rgba(0,0,0,0.55));\n cursor: pointer;\n transition: background 0.12s, border-color 0.12s, color 0.12s;\n }\n .aparte-art-card__btn:hover:not(:disabled) {\n background: var(--aparte-surface-hover, rgba(0,0,0,0.04));\n border-color: var(--aparte-border, rgba(0,0,0,0.1));\n color: var(--aparte-text, inherit);\n }\n .aparte-art-card__btn:disabled { opacity: 0.4; cursor: not-allowed; }\n .aparte-art-card__tabs {\n display: flex; gap: 2px;\n padding: 4px 8px 0;\n border-bottom: 1px solid var(--aparte-border, rgba(0,0,0,0.08));\n background: var(--aparte-surface-2, rgba(0,0,0,0.02));\n }\n .aparte-art-card__tabs button {\n padding: 6px 12px;\n font-size: 0.78rem;\n background: transparent;\n border: 1px solid transparent;\n border-bottom: none;\n border-radius: 6px 6px 0 0;\n color: var(--aparte-text-muted, rgba(0,0,0,0.6));\n cursor: pointer;\n transition: background 0.12s, color 0.12s;\n }\n .aparte-art-card__tabs button[aria-selected=\"true\"] {\n background: var(--aparte-surface, #fff);\n border-color: var(--aparte-border, rgba(0,0,0,0.08));\n color: var(--aparte-text, inherit);\n font-weight: 500;\n }\n .aparte-art-card__tabs button:disabled { opacity: 0.4; cursor: not-allowed; }\n .aparte-art-card__body {\n position: relative;\n min-height: 80px;\n max-height: 600px;\n overflow: hidden;\n }\n .aparte-art-card__pane { display: none; height: 100%; }\n .segment-artifact-card[data-tab=\"code\"] .aparte-art-card__pane[data-pane=\"code\"] { display: block; }\n .segment-artifact-card[data-tab=\"preview\"] .aparte-art-card__pane[data-pane=\"preview\"] { display: block; }\n .aparte-art-card__pane[data-pane=\"code\"] {\n max-height: 600px; overflow: auto;\n }\n .aparte-art-card__pane[data-pane=\"code\"] pre {\n margin: 0; padding: 12px;\n font-size: 0.82rem;\n background: var(--aparte-code-bg, #fafafa);\n }\n .aparte-art-card__frame {\n display: block;\n width: 100%; height: 480px;\n border: 0;\n background: #fff;\n }\n .aparte-art-card__pending {\n display: flex; align-items: center; justify-content: center;\n height: 120px;\n color: var(--aparte-text-muted, rgba(0,0,0,0.5));\n font-size: 0.85rem;\n font-style: italic;\n }\n /* ── Binary file artifact (xlsx/pdf/docx) ──────────────────── */\n .segment-artifact-file {\n display: flex; flex-direction: column;\n margin: 8px 0;\n border: 1px solid var(--aparte-border, rgba(0,0,0,0.12));\n border-radius: 12px;\n background: var(--aparte-surface, #fff);\n overflow: hidden;\n color: var(--aparte-text, inherit);\n font: inherit;\n }\n .aparte-art-file__card {\n display: flex; align-items: center; gap: 12px;\n padding: 12px 14px;\n background: var(--aparte-surface-2, rgba(0,0,0,0.02));\n border-bottom: 1px solid var(--aparte-border, rgba(0,0,0,0.08));\n }\n .aparte-art-file__body {\n position: relative;\n }\n .aparte-art-file__code-pane {\n max-height: 360px; overflow: auto;\n background: var(--aparte-code-bg, #fafafa);\n }\n .aparte-art-file__code-pane pre {\n margin: 0; padding: 12px;\n font-size: 0.82rem;\n font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;\n }\n .aparte-art-file__preview-pane {\n max-height: 460px;\n overflow: auto;\n background: #fff;\n /* Preview is a document view — force light scheme regardless of\n the app theme, with a dark text colour so cells stay readable. */\n color: #1f2937;\n }\n .aparte-art-file__icon {\n width: 40px; height: 40px;\n border-radius: 8px;\n display: flex; align-items: center; justify-content: center;\n background: linear-gradient(135deg, #1d6f42, #0f5132);\n color: #fff;\n font-weight: 700;\n font-size: 0.78rem;\n letter-spacing: 0.04em;\n flex-shrink: 0;\n }\n .aparte-art-file__icon[data-kind=\"pdf\"] { background: linear-gradient(135deg, #c0392b, #7d1f17); }\n .aparte-art-file__icon[data-kind=\"docx\"] { background: linear-gradient(135deg, #1e5288, #0f3060); }\n .aparte-art-file__meta { flex: 1 1 auto; min-width: 0; }\n .aparte-art-file__meta-name {\n font-weight: 600; font-size: 0.92rem;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n }\n .aparte-art-file__meta-sub {\n font-size: 0.78rem;\n color: var(--aparte-text-muted, rgba(0,0,0,0.55));\n }\n .aparte-art-file__actions { display: flex; gap: 6px; flex-shrink: 0; }\n .aparte-art-file__btn {\n border: 1px solid var(--aparte-border, rgba(0,0,0,0.12));\n background: var(--aparte-surface, #fff);\n color: var(--aparte-text, inherit);\n padding: 6px 10px;\n border-radius: 6px;\n font-size: 0.8rem;\n cursor: pointer;\n transition: background 0.12s ease;\n }\n .aparte-art-file__btn:hover { background: var(--aparte-surface-2, rgba(0,0,0,0.05)); }\n .aparte-art-file__btn--primary {\n background: var(--aparte-primary, #6366f1);\n color: #fff;\n border-color: var(--aparte-primary, #6366f1);\n }\n .aparte-art-file__btn--primary:hover { filter: brightness(1.07); }\n /* Hide the download button until the sandbox has produced the buffer.\n Avoids confusing the user with a disabled-but-styled-primary button\n during streaming / compiling. */\n .segment-artifact-file:not([data-state=\"ready\"]) .aparte-art-file__btn[data-action=\"download\"] {\n display: none;\n }\n .aparte-art-file__preview-pane table {\n border-collapse: collapse;\n width: 100%;\n font-size: 0.82rem;\n font-family: inherit;\n }\n .aparte-art-file__preview-pane th,\n .aparte-art-file__preview-pane td {\n border: 1px solid rgba(0,0,0,0.10);\n padding: 6px 10px;\n text-align: left;\n white-space: nowrap;\n color: #1f2937;\n background: #fff;\n }\n .aparte-art-file__preview-pane tr:nth-child(odd) td { background: #f9fafb; }\n .aparte-art-file__preview-pane tr:first-child td {\n background: #f3f4f6;\n font-weight: 600;\n position: sticky; top: 0;\n color: #111827;\n }\n .aparte-art-file__preview-empty {\n padding: 20px; text-align: center;\n color: var(--aparte-text-muted, rgba(0,0,0,0.5));\n font-size: 0.85rem; font-style: italic;\n }\n .segment-artifact-file[data-state=\"error\"] .aparte-art-file__icon {\n background: linear-gradient(135deg, #c0392b, #7d1f17);\n }\n .aparte-art-file__error {\n padding: 16px 18px;\n background: rgba(239, 68, 68, 0.05);\n border-top: 1px solid rgba(239, 68, 68, 0.2);\n color: var(--aparte-text, inherit);\n }\n .aparte-art-file__error-title {\n font-weight: 600;\n font-size: 0.9rem;\n margin-bottom: 6px;\n color: #b91c1c;\n }\n .aparte-art-file__error-msg {\n font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;\n font-size: 0.78rem;\n padding: 6px 10px;\n background: rgba(0,0,0,0.04);\n border-radius: 4px;\n margin-bottom: 8px;\n color: #7f1d1d;\n word-break: break-word;\n }\n .aparte-art-file__error-hint {\n font-size: 0.78rem;\n color: var(--aparte-text-muted, rgba(0,0,0,0.55));\n font-style: italic;\n }\n `,\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Binary file artifact helpers (xlsx/pdf/docx)\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst FILE_ICON_LABEL: Record<string, string> = {\n xlsx: 'XLS',\n pdf: 'PDF',\n docx: 'DOC',\n};\n\n/**\n * Module-level cache of sandbox-produced previews keyed by segment.id.\n * Lets re-mounts (branch switch, conversation toggle, persisted reload after\n * a previous mount) restore the preview instantly without re-running the\n * sandbox. Lifetime = current page session; cleared on full reload.\n */\ntype CachedPreview = {\n buffer: Uint8Array | ArrayBuffer;\n mime: string;\n name: string;\n bytes: number;\n previewHtml: string | null;\n};\nconst _binaryArtifactCache = new Map<string, CachedPreview>();\n/** Cap the binary-artifact cache: each entry holds a full file buffer, so an\n * unbounded Map would grow for the page's lifetime over a long session. */\nconst MAX_BINARY_ARTIFACTS = 24;\nfunction cacheBinaryArtifact(id: string, entry: CachedPreview): void {\n // delete-then-set refreshes recency; evict the oldest (first) key when full.\n _binaryArtifactCache.delete(id);\n if (_binaryArtifactCache.size >= MAX_BINARY_ARTIFACTS) {\n const oldest = _binaryArtifactCache.keys().next().value;\n if (oldest !== undefined) _binaryArtifactCache.delete(oldest);\n }\n _binaryArtifactCache.set(id, entry);\n}\n/** segmentId → cleanup for the file-gen window listeners (one live pair/segment). */\nconst _fileGenHandlers = new Map<string, () => void>();\n/** Cap the live file-gen handler map: a generation that never terminates (the\n * conversation is cleared/switched mid-flight, or no FileGenService is wired) would\n * otherwise keep its two window listeners for the page's lifetime. */\nconst MAX_FILE_GEN_HANDLERS = 32;\n\n/**\n * Tracks when an `aparte-artifact-ready` was last dispatched per segment, so\n * setup() rehydration doesn't fire a duplicate dispatch while a previous\n * sandbox run is still in flight (would trigger \"Sandbox is busy\" errors).\n * Populated both by aparte-client's natural dispatch (hook below) AND by our\n * own rehydration dispatches.\n */\nconst _lastDispatchAt = new Map<string, number>();\nconst RE_DISPATCH_DEBOUNCE_MS = 30_000;\n\n/**\n * Debounced syntax-highlight during streaming. Re-running Shiki on every\n * token chunk would saturate the main thread (50-100ms/highlight × 10\n * chunks/sec). We coalesce to one highlight every ~400ms, plus a final\n * highlight at stream-end (handled by the caller).\n */\nconst _lastHighlightAt = new Map<string, number>();\nconst HIGHLIGHT_DEBOUNCE_MS = 400;\n\n/** Cap a segmentId→timestamp throttle map so a long session can't grow it without\n * bound (one entry per streamed segment). Values are timestamps (tiny), so the cap\n * is generous; delete-then-set refreshes recency, evict the oldest key when full.\n * Evicting a stale entry costs at most one extra highlight/dispatch — never\n * incorrect, since both debounce windows are far shorter than the cap horizon. */\nconst MAX_THROTTLE_ENTRIES = 256;\nfunction markThrottle(map: Map<string, number>, id: string, at: number): void {\n map.delete(id);\n if (map.size >= MAX_THROTTLE_ENTRIES) {\n const oldest = map.keys().next().value;\n if (oldest !== undefined) map.delete(oldest);\n }\n map.set(id, at);\n}\n\nfunction debounceHighlight(\n element: HTMLElement,\n paneSelector: string,\n content: string,\n lang: string,\n segId: string,\n): void {\n const now = Date.now();\n const last = _lastHighlightAt.get(segId) ?? 0;\n if (now - last < HIGHLIGHT_DEBOUNCE_MS) return;\n markThrottle(_lastHighlightAt, segId, now);\n // May run from a window-event callback (late) — resolve from the element.\n void contextConfig(element).highlightCode(content, lang).then(html => {\n const wrapper = element.querySelector<HTMLElement>(paneSelector);\n if (wrapper) wrapper.innerHTML = html;\n }).catch(() => { /* best-effort: a failed highlight degrades silently */ });\n}\n\nif (typeof window !== 'undefined') {\n // Single shared hook — runs once on module load. Records every\n // `aparte-artifact-ready` whoever dispatched it.\n window.addEventListener('aparte-artifact-ready', (event: Event) => {\n const segId = (event as CustomEvent).detail?.segmentId as string | undefined;\n if (segId) markThrottle(_lastDispatchAt, segId, Date.now());\n });\n}\n\nfunction renderBinaryFileArtifact(segment: AparteArtifactSegment, kind: string): string {\n const title = segment.title?.trim() || labelForKind(kind);\n const iconLabel = FILE_ICON_LABEL[kind] ?? kind.toUpperCase();\n const isStreaming = !!segment.isStreaming;\n\n // Cache hit (branch switch back, re-render of an already-built segment) :\n // render in 'ready' state with the preview HTML so the user sees the\n // file immediately — no setup() round-trip required, which matters when\n // Angular reuses the DOM via trackBy and our setup may not re-fire.\n const cached = _binaryArtifactCache.get(segment.id);\n if (cached && !isStreaming) {\n const previewBody = cached.previewHtml\n ? contextConfig().sanitizeHtml(cached.previewHtml)\n : `<div class=\"aparte-art-file__preview-empty\">Preview not available for ${escapeHtml(kind)} yet</div>`;\n return `\n <div class=\"segment segment-artifact-file\"\n data-segment-id=\"${escapeHtml(segment.id)}\"\n data-artifact-type=\"${escapeHtml(kind)}\"\n data-state=\"ready\">\n <div class=\"aparte-art-file__card\">\n <div class=\"aparte-art-file__icon\" data-kind=\"${escapeHtml(kind)}\">${escapeHtml(iconLabel)}</div>\n <div class=\"aparte-art-file__meta\">\n <div class=\"aparte-art-file__meta-name\" data-role=\"file-name\">${escapeHtml(cached.name)}</div>\n <div class=\"aparte-art-file__meta-sub\" data-role=\"file-sub\">${escapeHtml(formatBytes(cached.bytes))} · ${escapeHtml(kind.toUpperCase())}</div>\n </div>\n <div class=\"aparte-art-file__actions\">\n <button type=\"button\" class=\"aparte-art-file__btn aparte-art-file__btn--primary\" data-action=\"download\">Download</button>\n </div>\n </div>\n <div class=\"aparte-art-file__body\">\n <div class=\"aparte-art-file__code-pane\" data-role=\"code-pane\" hidden>\n <pre><code class=\"language-js\"></code></pre>\n </div>\n <div class=\"aparte-art-file__preview-pane\" data-role=\"preview-pane\">${previewBody}</div>\n </div>\n </div>\n `;\n }\n\n const cleanContent = stripCodeFences(segment.content || '');\n const subText = isStreaming ? 'Generating…' : 'Rebuilding preview…';\n return `\n <div class=\"segment segment-artifact-file\"\n data-segment-id=\"${escapeHtml(segment.id)}\"\n data-artifact-type=\"${escapeHtml(kind)}\"\n data-state=\"${isStreaming ? 'streaming' : 'compiling'}\">\n <div class=\"aparte-art-file__card\">\n <div class=\"aparte-art-file__icon\" data-kind=\"${escapeHtml(kind)}\">${escapeHtml(iconLabel)}</div>\n <div class=\"aparte-art-file__meta\">\n <div class=\"aparte-art-file__meta-name\" data-role=\"file-name\">${escapeHtml(title)}</div>\n <div class=\"aparte-art-file__meta-sub\" data-role=\"file-sub\">${escapeHtml(subText)}</div>\n </div>\n <div class=\"aparte-art-file__actions\">\n <button type=\"button\" class=\"aparte-art-file__btn aparte-art-file__btn--primary\" data-action=\"download\" disabled>Download</button>\n </div>\n </div>\n <div class=\"aparte-art-file__body\">\n <div class=\"aparte-art-file__code-pane\" data-role=\"code-pane\">\n <pre><code class=\"language-js\">${escapeHtml(cleanContent)}</code></pre>\n </div>\n <div class=\"aparte-art-file__preview-pane\" data-role=\"preview-pane\" hidden></div>\n </div>\n </div>\n `;\n}\n\nfunction setupBinaryFileArtifact(element: HTMLElement, segment: AparteArtifactSegment, kind: string): void {\n // Per-element click handler reads the live cache entry so the Download\n // button works regardless of whether the buffer came from a fresh run or\n // from the module cache (branch switch / re-mount).\n if (element.dataset['aparteInit'] !== 'true') {\n element.dataset['aparteInit'] = 'true';\n\n // A branch switch replaces this element with a fresh one (aparteInit\n // unset), so setup runs again for the same segment id. Drop the prior\n // element's listeners first — one live pair per segment id — otherwise\n // the detached element leaks (its handlers keep it referenced forever).\n _fileGenHandlers.get(segment.id)?.();\n const cleanup = (): void => {\n window.removeEventListener('aparte-file-gen-ready', onReady);\n window.removeEventListener('aparte-file-gen-error', onError);\n _fileGenHandlers.delete(segment.id);\n };\n\n const onReady = (event: Event): void => {\n const detail = (event as CustomEvent).detail as {\n segmentId: string;\n filename: string;\n bytes: number;\n mime: string;\n buffer: Uint8Array | ArrayBuffer;\n previewHtml: string | null;\n };\n if (!detail || detail.segmentId !== segment.id) return;\n cacheBinaryArtifact(segment.id, {\n buffer: detail.buffer,\n mime: detail.mime,\n name: detail.filename,\n bytes: detail.bytes,\n previewHtml: detail.previewHtml,\n });\n swapToPreview(element, detail, kind);\n cleanup(); // terminal — generation delivered\n };\n window.addEventListener('aparte-file-gen-ready', onReady);\n\n // Sandbox failed — show an inline error in the card so the user gets\n // feedback instead of an indefinite \"Running sandbox…\" spinner. The\n // model often emits buggy code (drawText with array, undefined font\n // refs, etc.) — we surface that rather than hide it.\n const onError = (event: Event): void => {\n const detail = (event as CustomEvent).detail as {\n segmentId: string;\n phase?: string;\n error?: string;\n };\n if (!detail || detail.segmentId !== segment.id) return;\n showSandboxError(element, detail.phase ?? 'exec', detail.error ?? 'Unknown error');\n cleanup(); // terminal — generation failed\n };\n window.addEventListener('aparte-file-gen-error', onError);\n // Evict (and clean up the listeners of) the oldest handler when at capacity,\n // so a never-terminating generation can't leak listeners without bound.\n if (_fileGenHandlers.size >= MAX_FILE_GEN_HANDLERS) {\n const oldest = _fileGenHandlers.keys().next().value;\n if (oldest !== undefined) _fileGenHandlers.get(oldest)?.();\n }\n _fileGenHandlers.set(segment.id, cleanup);\n\n element.addEventListener('click', (ev) => {\n const target = ev.target as HTMLElement;\n const action = target.closest<HTMLElement>('[data-action]')?.getAttribute('data-action');\n if (action !== 'download') return;\n const cached = _binaryArtifactCache.get(segment.id);\n if (!cached) return;\n const blob = new Blob([cached.buffer as BlobPart], { type: cached.mime });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = cached.name;\n a.style.display = 'none';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n });\n }\n\n // ─── Rehydration : runs on EVERY setup() call. ──────────────────\n const currentState = element.getAttribute('data-state');\n if (segment.isStreaming || currentState === 'ready') return;\n\n // Cache hit (branch switch back, conversation toggle) → swap instantly,\n // no sandbox re-run.\n const cached = _binaryArtifactCache.get(segment.id);\n if (cached) {\n swapToPreview(\n element,\n { filename: cached.name, bytes: cached.bytes, previewHtml: cached.previewHtml },\n kind,\n );\n return;\n }\n\n // Dedupe : if an artifact-ready was dispatched recently for this segment\n // (either by aparte-client's natural stream-end flow OR by a previous\n // rehydration), don't fire another one — the sandbox is in flight and\n // the file-gen-ready listener will catch the result soon. Without this\n // we'd hit \"Sandbox is busy with a previous execution\" errors.\n const lastAt = _lastDispatchAt.get(segment.id) ?? 0;\n if (Date.now() - lastAt < RE_DISPATCH_DEBOUNCE_MS) return;\n markThrottle(_lastDispatchAt, segment.id, Date.now());\n\n // First time we see this segment in the current page session : kick off\n // the sandbox via FileGenService. Date.now() in the messageId bypasses\n // its `${msgId}::${segId}` dedupe so the event always reaches us.\n const reloadMessageId = `__reload__${Date.now()}`;\n queueMicrotask(() => {\n window.dispatchEvent(new CustomEvent('aparte-artifact-ready', {\n detail: {\n messageId: reloadMessageId,\n segmentId: segment.id,\n mimeType: segment.mimeType,\n artifactType: segment.artifactType,\n title: segment.title,\n content: stripCodeFences(segment.content || ''),\n },\n }));\n });\n // Re-highlight the code (visible until the sandbox finishes).\n const wrapper = element.querySelector<HTMLElement>('[data-role=\"code-pane\"]');\n if (wrapper) {\n const cleanContent = stripCodeFences(segment.content || '');\n void contextConfig(element).highlightCode(cleanContent, 'js').then(html => {\n wrapper.innerHTML = html;\n }).catch(() => { /* best-effort: a failed highlight degrades silently */ });\n }\n}\n\nfunction updateBinaryFileArtifact(element: HTMLElement, segment: AparteArtifactSegment, isStreaming: boolean): void {\n const state = element.getAttribute('data-state');\n // Once we've swapped to preview we don't touch the body again.\n if (state === 'ready') return;\n\n const cleanContent = stripCodeFences(segment.content || '');\n // Live-update the streaming code via textContent (cheap), then schedule\n // a debounced syntax-highlight so colors appear progressively without\n // saturating the main thread.\n const codeEl = element.querySelector<HTMLElement>('[data-role=\"code-pane\"] code');\n if (codeEl) {\n codeEl.textContent = cleanContent;\n }\n if (isStreaming) {\n debounceHighlight(element, '[data-role=\"code-pane\"]', cleanContent, 'js', segment.id);\n }\n\n if (!isStreaming && state === 'streaming') {\n element.setAttribute('data-state', 'compiling');\n // Re-highlight once the code is final. The pane stays visible until\n // `aparte-file-gen-ready` fires and swapToPreview() flips it.\n const wrapper = element.querySelector<HTMLElement>('[data-role=\"code-pane\"]');\n if (wrapper) {\n void contextConfig(element).highlightCode(cleanContent, 'js').then(html => {\n wrapper.innerHTML = html;\n }).catch(() => { /* best-effort: a failed highlight degrades silently */ });\n }\n const sub = element.querySelector<HTMLElement>('[data-role=\"file-sub\"]');\n if (sub) sub.textContent = 'Running sandbox…';\n }\n}\n\nfunction swapToPreview(\n element: HTMLElement,\n detail: { filename: string; bytes: number; previewHtml: string | null },\n kind: string,\n): void {\n element.setAttribute('data-state', 'ready');\n\n // Replace the code pane with the file preview.\n const codePane = element.querySelector<HTMLElement>('[data-role=\"code-pane\"]');\n if (codePane) codePane.hidden = true;\n const preview = element.querySelector<HTMLElement>('[data-role=\"preview-pane\"]');\n if (preview) {\n // previewHtml comes from the app (e.g. SheetJS table for xlsx) and is built\n // from potentially untrusted file bytes, so route it through the sanitizer\n // like every other innerHTML in this file (renderMarkdown/highlightCode do).\n preview.innerHTML = detail.previewHtml\n ? contextConfig().sanitizeHtml(detail.previewHtml)\n : `<div class=\"aparte-art-file__preview-empty\">Preview not available for ${escapeHtml(kind)} yet</div>`;\n preview.hidden = false;\n }\n\n // Update the footer card to its \"ready\" state.\n const nameEl = element.querySelector<HTMLElement>('[data-role=\"file-name\"]');\n if (nameEl) nameEl.textContent = detail.filename;\n const sub = element.querySelector<HTMLElement>('[data-role=\"file-sub\"]');\n if (sub) sub.textContent = `${formatBytes(detail.bytes)} · ${kind.toUpperCase()}`;\n const dlBtn = element.querySelector<HTMLButtonElement>('[data-action=\"download\"]');\n if (dlBtn) dlBtn.disabled = false;\n}\n\nfunction formatBytes(n: number): string {\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;\n return `${(n / (1024 * 1024)).toFixed(2)} MB`;\n}\n\nfunction showSandboxError(element: HTMLElement, phase: string, errorMsg: string): void {\n // Don't clobber a successful preview — only flip to error if still\n // pre-ready (streaming/compiling).\n const state = element.getAttribute('data-state');\n if (state === 'ready') return;\n element.setAttribute('data-state', 'error');\n\n const sub = element.querySelector<HTMLElement>('[data-role=\"file-sub\"]');\n if (sub) sub.textContent = `Error (${phase})`;\n\n const body = element.querySelector<HTMLElement>('.aparte-art-file__body');\n if (body) {\n // Truncate gnarly stack traces — first line is usually the actionable bit.\n const short = (errorMsg.split('\\n')[0] ?? '').slice(0, 240);\n body.innerHTML = `\n <div class=\"aparte-art-file__error\">\n <div class=\"aparte-art-file__error-title\">The sandbox failed during generation.</div>\n <div class=\"aparte-art-file__error-msg\">${escapeHtml(short)}</div>\n <div class=\"aparte-art-file__error-hint\">Common cause: the model produced invalid code (undefined variable, wrong argument type). Retry the request — the model may produce different code.</div>\n </div>\n `;\n }\n}\n\nfunction labelForKind(kind: string): string {\n switch (kind) {\n case 'react': return 'React component';\n case 'html': return 'HTML document';\n case 'svg': return 'SVG image';\n case 'js': return 'JavaScript snippet';\n case 'css': return 'CSS stylesheet';\n case 'json': return 'JSON document';\n case 'markdown': return 'Markdown document';\n case 'csv': return 'CSV table';\n case 'text': return 'Text file';\n case 'python': return 'Python script';\n case 'typescript': return 'TypeScript file';\n case 'bash': return 'Bash script';\n case 'sql': return 'SQL query';\n case 'pdf': return 'PDF generator';\n case 'xlsx': return 'Excel generator';\n case 'docx': return 'Word generator';\n default: return 'Artifact';\n }\n}\n\nfunction languageForKind(kind: string): string {\n if (kind === 'react') return 'jsx';\n if (kind === 'markdown') return 'md';\n if (kind === 'pdf' || kind === 'xlsx' || kind === 'docx') return 'js';\n return kind || 'text';\n}\n\nfunction downloadTextArtifact(segment: AparteArtifactSegment): void {\n const content = stripCodeFences(segment.content || '');\n const kind = (segment.artifactType || '').toLowerCase();\n const ext = ({\n react: 'jsx', html: 'html', svg: 'svg', js: 'js', css: 'css',\n json: 'json', markdown: 'md', csv: 'csv', text: 'txt',\n python: 'py', typescript: 'ts', bash: 'sh', sql: 'sql',\n } as Record<string, string>)[kind] ?? 'txt';\n const baseTitle = (segment.title ?? labelForKind(kind)).trim();\n const safeBase = slugifyForFilename(baseTitle) || 'artifact';\n const filename = `${safeBase}.${ext}`;\n const mime = segment.mimeType || 'text/plain';\n const blob = new Blob([content], { type: mime });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n a.style.display = 'none';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n}\n\n// ─── Char-based helpers (no regex) ───────────────────────────────────────────\n\nfunction escapeAttr(s: string): string {\n let out = '';\n for (let i = 0; i < s.length; i++) {\n const ch = s[i];\n if (ch === '&') out += '&amp;';\n else if (ch === '\"') out += '&quot;';\n else if (ch === '<') out += '&lt;';\n else if (ch === '>') out += '&gt;';\n else if (ch === \"'\") out += '&#039;';\n else out += ch;\n }\n return out;\n}\n\nfunction slugifyForFilename(text: string): string {\n const lower = text.trim().toLowerCase();\n let out = '';\n let prevDash = false;\n for (let i = 0; i < lower.length && out.length < 40; i++) {\n const ch = lower[i]!;\n const isAlnum = (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9');\n if (isAlnum) { out += ch; prevDash = false; continue; }\n if (!prevDash && out.length > 0) { out += '-'; prevDash = true; }\n }\n if (out.endsWith('-')) out = out.slice(0, -1);\n return out;\n}\n\n// ─── Preview document builder (CDN-FREE offline fallback) ────────────────────\n// Core ships only an OFFLINE-safe preview: svg/css/html/js render with zero\n// network, and richer kinds (react/…) degrade to a read-only code view. The\n// product opts into a CDN-powered live preview (React/Babel/Tailwind) by\n// registering a builder via `AparteConfig.setArtifactPreviewBuilder()`. Core must\n// stay framework-agnostic and zero-network, so no CDN URLs live here.\n\nfunction buildSafePreviewDocument(kind: string, body: string, title: string): string {\n switch (kind) {\n case 'html': {\n if (startsWithDoctype(body)) return body;\n return `<!doctype html><html><head><meta charset=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/><title>${escapeAttr(title)}</title></head><body>${body}</body></html>`;\n }\n case 'svg':\n return `<!doctype html><html><head><meta charset=\"utf-8\"/><title>${escapeAttr(title)}</title>\n<style>html,body{margin:0;height:100%;display:flex;align-items:center;justify-content:center;background:#fff}svg{max-width:90%;max-height:90%}</style>\n</head><body>${body}</body></html>`;\n case 'js': {\n const safeBody = escapeClosingScriptTag(body);\n return `<!doctype html><html><head><meta charset=\"utf-8\"/><title>${escapeAttr(title)}</title>\n<style>body{margin:0;font-family:ui-sans-serif,system-ui,sans-serif;padding:1rem;background:#fff;color:#0f172a}</style>\n</head><body><div id=\"root\"></div><script>\ntry { ${safeBody}\n} catch (e) { document.getElementById('root').innerHTML = '<pre style=\"color:#b91c1c\">' + (e && e.stack || e) + '</pre>'; }\n</script></body></html>`;\n }\n case 'css':\n return `<!doctype html><html><head><meta charset=\"utf-8\"/><title>${escapeAttr(title)}</title>\n<style>${body}</style></head><body>\n<div class=\"demo\">\n <h1>Heading</h1>\n <p>Paragraph with a <a href=\"#\">link</a> and <strong>strong</strong> text.</p>\n <button>Button</button>\n <input placeholder=\"Input\"/>\n <ul><li>One</li><li>Two</li><li>Three</li></ul>\n</div></body></html>`;\n default:\n // react / unknown — no live preview offline; show the code read-only.\n return `<!doctype html><html><head><meta charset=\"utf-8\"/><title>${escapeAttr(title)}</title>\n<style>body{margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;padding:1rem;background:#fff;color:#0f172a}pre{white-space:pre-wrap;word-break:break-word;margin:0}</style>\n</head><body><pre>${escapeHtml(body)}</pre></body></html>`;\n }\n}\n\n/** Char-based check for `<!doctype` (case-insensitive) at start of string,\n * ignoring leading whitespace. */\nfunction startsWithDoctype(s: string): boolean {\n let i = 0;\n while (i < s.length && (s[i] === ' ' || s[i] === '\\t' || s[i] === '\\n' || s[i] === '\\r')) i++;\n const probe = s.slice(i, i + 9).toLowerCase();\n return probe === '<!doctype';\n}\n\n/** Escape any literal `</script` inside the body so it cannot terminate the\n * outer <script> tag we wrap user code in. The HTML spec closes a script on\n * `</script` followed by whitespace, `/` or `>` (not only the exact `</script>`),\n * so match the 8-char prefix + a terminator. Char-based. */\nfunction escapeClosingScriptTag(body: string): string {\n let out = '';\n let i = 0;\n while (i < body.length) {\n if (body[i] === '<' && body.slice(i, i + 8).toLowerCase() === '</script') {\n const next = body[i + 8];\n // A real closing tag needs a terminator after `</script` (space/tab/\n // newline/form-feed, `/`, `>`) or end-of-input.\n if (next === undefined || next === '/' || next === '>' || /\\s/.test(next)) {\n out += '<\\\\/script'; // neutralise the `<` so the browser sees no tag\n i += 8;\n continue;\n }\n }\n out += body[i];\n i++;\n }\n return out;\n}\n","import type {\n AparteAttachment,\n AparteMessage,\n AparteSegment,\n AparteSiblingInfo,\n AparteUsage,\n} from '../../types/index.js';\n\n/**\n * Minimal structural type representing the imperative slice of `AparteChatBubble`\n * needed to reconcile a message into a bubble. Declared loosely so framework\n * wrappers can pass any element that exposes the same surface (custom element\n * registered cross-context, mocked test double, etc.) without dragging the\n * concrete `AparteChatBubble` class through every wrapper's typing.\n */\nexport interface SyncableBubble {\n getSegments?: () => AparteSegment[];\n setSegments?: (segments: AparteSegment[]) => void;\n updateSegment?: (segmentId: string, updates: Partial<AparteSegment>) => void;\n setContent?: (content: string) => void;\n setAttachments?: (attachments: AparteAttachment[]) => void;\n setSiblings?: (count: number, index: number) => void;\n setUsage?: (usage: AparteUsage | null | undefined) => void;\n}\n\n/**\n * Reconcile a bubble element with a message and optional sibling metadata.\n *\n * **For framework wrappers** (Angular, React, Vue, Svelte) that own bubble\n * creation through their own template engine. After the framework has\n * mounted/diffed its bubbles, call this helper for each (bubble, message,\n * siblingInfo) tuple to push the imperative state — segments, attachments,\n * and the sibling-picker — that cannot travel via attribute bindings.\n *\n * The default vanilla viewport (`AparteChatViewport` with `_frameworkManagedDOM\n * === false`) uses this helper internally, so the contract stays in lockstep\n * across the core and every wrapper.\n *\n * Idempotent. Safe to call on every render. Segment count drives the strategy:\n * - same count → reconcile content per segment (cheap, preserves user-toggled\n * <details> open/closed state on thinking blocks, etc.)\n * - count changed (new segment streamed, branch switched, etc.) → full rebuild\n * via setSegments\n *\n * Plain `content` is only pushed when no segments are present — segments take\n * precedence in the bubble's render logic.\n *\n * Sibling-picker is only shown when `siblingInfo.count > 1`; below that, the\n * bubble hides the picker on its own.\n */\nexport function populateBubbleFromMessage(\n bubble: SyncableBubble,\n message: AparteMessage,\n siblingInfo?: AparteSiblingInfo,\n): void {\n if (message.segments && message.segments.length > 0) {\n const existing = bubble.getSegments?.() ?? [];\n if (existing.length === message.segments.length) {\n // A re-sync of a message that is NOT actively streaming (load,\n // branch switch, post-completion re-render) stamps its segments\n // `isStreaming: false`. The text renderer then reconciles via the\n // one-shot Markdown path — the incremental streaming parser buffers\n // its trailing token-lookahead and would otherwise drop a final\n // glyph (e.g. an emoji). A still-streaming message is left alone so\n // its live incremental rendering (partial **bold**, …) is intact.\n const settled = message.status != null\n && message.status !== 'streaming' && message.status !== 'pending';\n for (const seg of message.segments) {\n if ('content' in seg) {\n const patch = { content: (seg as { content: unknown }).content } as Partial<AparteSegment>;\n if (settled) (patch as { isStreaming?: boolean }).isStreaming = false;\n bubble.updateSegment?.(seg.id, patch);\n }\n }\n } else {\n bubble.setSegments?.(message.segments);\n }\n } else if (message.content !== undefined) {\n bubble.setContent?.(message.content);\n }\n\n if (message.attachments && message.attachments.length > 0) {\n bubble.setAttachments?.(message.attachments);\n }\n\n if (siblingInfo && siblingInfo.count > 1) {\n bubble.setSiblings?.(siblingInfo.count, siblingInfo.index);\n }\n\n if (message.usage !== undefined) {\n bubble.setUsage?.(message.usage);\n }\n}\n","import type { AparteMessage } from '../types/index.js';\nimport type { ExportedMessageRepository } from '../runtime/message-repository.js';\n\n/**\n * Current schema version for `AparteConversation`. Bump when the persisted\n * shape evolves so adapters / migrations can branch on it.\n *\n * v1: monolithic — `messages[]` inline on the conversation row, attachment\n * `url` was a session-scoped blob URL (lost on reload), artifacts inline\n * in segments only.\n * v2: split — meta-only conversation row; messages, attachments (with real\n * `Blob`s), artifacts (gallery index), memory facts and settings each\n * get their own table. Sidebar can list without loading full payloads.\n */\nexport const APARTE_CONVERSATION_SCHEMA_VERSION = 2 as const;\n\n/**\n * A single conversation (thread) — framework-agnostic.\n */\nexport interface AparteConversation {\n id: string;\n /** Auto-generated from the first user message (≤50 chars). */\n title: string;\n createdAt: number;\n updatedAt: number;\n messages: AparteMessage[];\n /**\n * Full branching tree snapshot. When present, the conversation has branch\n * history (edits, retries, sibling responses) and is loaded via `importTree`\n * rather than the flat `messages` array.\n *\n * `messages` always mirrors the active path for backward-compatibility\n * (sidebar previews, title generation, adapters that only read flat arrays).\n */\n tree?: ExportedMessageRepository;\n /** When truthy the conversation is archived (hidden from the main list). */\n archivedAt?: number;\n /** When truthy the conversation is pinned at the top of the list. */\n pinnedAt?: number;\n /** Optional folder/tag id for organisation (V2+). */\n folderId?: string;\n /**\n * Schema version of this record. Absent on legacy data (pre-versioning):\n * adapters should treat `undefined` as version 0 and migrate on read.\n */\n schemaVersion?: number;\n}\n\n/**\n * Lightweight conversation meta — what's needed to render the sidebar without\n * loading the full message history. Returned by `loadMeta()` and stored as a\n * dedicated row by split-storage adapters.\n */\nexport interface AparteConversationMeta {\n id: string;\n title: string;\n createdAt: number;\n updatedAt: number;\n archivedAt?: number;\n pinnedAt?: number;\n folderId?: string;\n /** Truncated last message body (≤200 chars) — for sidebar previews. */\n lastMessagePreview?: string;\n /** Cached message count for the sidebar badge / archive logic. */\n messageCount?: number;\n /** Sum of input+output tokens across messages, when available. */\n totalTokens?: number;\n /** Persisted schema version of the row. */\n schemaVersion?: number;\n}\n\n/**\n * Memory fact — durable user facts stored across all conversations.\n * Surfaces in the memory panel and is consulted by the orchestrator to\n * personalise replies.\n */\nexport interface AparteMemoryFact {\n id: string;\n /** Coarse category — drives the icon/colour in the panel. */\n type: 'identity' | 'fact' | 'preference' | 'tech' | 'project' | 'style';\n content: string;\n /** Where the fact came from. `auto` = regex-detected, `manual` = user input. */\n source?: 'manual' | 'auto' | 'onboarding';\n addedAt: number;\n /** Last time the fact was selected as relevant for a reply. */\n lastUsedAt?: number;\n /** Conversation that produced an auto-detected fact (for traceability). */\n sourceConvId?: string;\n /** Message inside `sourceConvId` that triggered the auto-detection. */\n sourceMsgId?: string;\n}\n\n/**\n * Artifact gallery row — denormalised index of every artifact segment ever\n * produced, so the gallery can list/filter without scanning every message.\n * The source of truth remains the artifact segment inside its message; this\n * row is rebuilt by the storage adapter on every save.\n */\nexport interface AparteArtifactRow {\n id: string;\n convId: string;\n msgId: string;\n name: string;\n mimeType: string;\n artifactType: string;\n /** Full body — duplicated from the source segment for fast gallery preview. */\n content: string;\n title?: string;\n /** Snapshot of the message timestamp; used for \"newest first\" gallery sort. */\n updatedAt: number;\n}\n\n/**\n * Persisted attachment with the actual Blob — survives page reloads, unlike\n * the session-scoped `URL.createObjectURL()` stored on `AparteMessage`.\n * Adapters that split storage MUST keep the blob here and reconstruct the\n * `url` on demand.\n */\nexport interface AparteAttachmentRow {\n id: string;\n convId: string;\n msgId: string;\n name: string;\n mimeType: string;\n size: number;\n blob: Blob;\n}\n\n/**\n * Implement this interface to provide persistence for conversations.\n * All methods are async so any backend (IndexedDB, SQLite WASM, REST API…)\n * can be used. The ConversationManager never touches storage directly.\n *\n * The first three methods (`loadAll`, `save`, `delete`) form the minimum\n * viable adapter — everything else is optional and lets richer backends\n * expose split-storage features (fast meta listing, memory, settings,\n * artifact gallery, attachment blobs).\n */\nexport interface AparteStorageAdapter {\n /** Return all stored conversations (full payload), ordered by updatedAt desc. */\n loadAll(): Promise<AparteConversation[]>;\n /** Upsert a conversation (create or update). */\n save(conv: AparteConversation): Promise<void>;\n /** Permanently delete a conversation by id (cascades related rows). */\n delete(id: string): Promise<void>;\n /** Archive a conversation (soft-delete). Optional. */\n archive?(id: string): Promise<void>;\n /** Restore an archived conversation. Optional. */\n unarchive?(id: string): Promise<void>;\n\n // ── Split-storage extensions ────────────────────────────────────────────\n\n /**\n * Fast sidebar listing. Returns lightweight meta only (no messages).\n * Order is `updatedAt` desc. Adapters without split storage can omit\n * this — callers should fall back to `loadAll()` mapping to meta.\n */\n loadMeta?(): Promise<AparteConversationMeta[]>;\n /** Lazy-load a single conversation's full payload by id. */\n loadFull?(id: string): Promise<AparteConversation | null>;\n /** Pin/unpin (`pinnedAt`-aware sidebar sort). */\n pin?(id: string): Promise<void>;\n unpin?(id: string): Promise<void>;\n /** Update only the conversation title (avoids rewriting messages). */\n rename?(id: string, title: string): Promise<void>;\n\n // ── Memory ──────────────────────────────────────────────────────────────\n\n getMemory?(): Promise<AparteMemoryFact[]>;\n addMemoryFact?(fact: AparteMemoryFact): Promise<void>;\n updateMemoryFact?(id: string, patch: Partial<AparteMemoryFact>): Promise<void>;\n deleteMemoryFact?(id: string): Promise<void>;\n clearMemory?(): Promise<void>;\n\n // ── Settings (k/v) ──────────────────────────────────────────────────────\n\n getSetting?<T = unknown>(key: string): Promise<T | undefined>;\n setSetting?<T = unknown>(key: string, value: T): Promise<void>;\n deleteSetting?(key: string): Promise<void>;\n getAllSettings?(): Promise<Record<string, unknown>>;\n\n // ── Gallery / lazy-loaded blobs ────────────────────────────────────────\n\n /** List artifacts; optionally scoped to a single conversation. */\n loadArtifacts?(filter?: { convId?: string }): Promise<AparteArtifactRow[]>;\n /** Persisted attachment blobs for a single message. */\n loadAttachments?(msgId: string): Promise<AparteAttachmentRow[]>;\n}\n","import type { AparteMessage } from '../types/index.js';\nimport type { AparteConversation, AparteStorageAdapter } from './types.js';\nimport type { ExportedMessageRepository } from '../runtime/message-repository.js';\nimport { APARTE_CONVERSATION_SCHEMA_VERSION } from './types.js';\n\ntype Listener = (conversations: AparteConversation[]) => void;\n\nexport interface ConversationManagerOptions {\n /**\n * Opt-in history retention. When set, `updateMessages()` trims a persisted\n * conversation to at most `maxMessages` on the active path (dropping the\n * oldest history and the branches hanging off it) before writing to storage.\n * Omit for unbounded history (the default) — retention is a deliberate\n * consumer choice, never silent.\n */\n retention?: { maxMessages: number };\n}\n\n/**\n * Pure retention transform: keep at most `maxMessages` on the active path.\n *\n * Keeps the subtree rooted at the new-oldest kept message (so recent history AND\n * recent branches survive) and drops everything before it. Exported for testing.\n */\nexport function applyRetention(\n messages: AparteMessage[],\n tree: ExportedMessageRepository | undefined,\n maxMessages: number,\n): { messages: AparteMessage[]; tree: ExportedMessageRepository | undefined } {\n if (maxMessages <= 0 || messages.length <= maxMessages) return { messages, tree };\n const trimmedFlat = messages.slice(messages.length - maxMessages);\n const cutoffId = trimmedFlat[0]?.id;\n if (!cutoffId || !tree) return { messages: trimmedFlat, tree };\n\n const treeIds = new Set(tree.messages.map((m) => m.message.id));\n if (!treeIds.has(cutoffId)) return { messages: trimmedFlat, tree }; // flat/tree mismatch → don't corrupt\n\n const childrenOf = new Map<string, string[]>();\n for (const { message, parentId } of tree.messages) {\n if (parentId != null) {\n const arr = childrenOf.get(parentId) ?? [];\n arr.push(message.id);\n childrenOf.set(parentId, arr);\n }\n }\n const keep = new Set<string>();\n const queue = [cutoffId];\n while (queue.length) {\n const id = queue.pop()!;\n if (keep.has(id)) continue;\n keep.add(id);\n for (const child of childrenOf.get(id) ?? []) queue.push(child);\n }\n const trimmedTree: ExportedMessageRepository = {\n headId: tree.headId,\n messages: tree.messages\n .filter((m) => keep.has(m.message.id))\n .map((m) => (m.message.id === cutoffId ? { message: m.message, parentId: null } : m)),\n };\n return { messages: trimmedFlat, tree: trimmedTree };\n}\n\n/**\n * Framework-agnostic conversation manager.\n *\n * Usage:\n * ```ts\n * const manager = new ConversationManager(myAdapter);\n * await manager.init(); // load from storage\n * const conv = await manager.createNew(); // returns new AparteConversation\n * ```\n *\n * The manager emits synchronous notifications to registered listeners on every\n * mutation so framework wrappers (Angular signals, Vue reactive, etc.) can\n * react without polling.\n */\nexport class ConversationManager {\n private _adapter: AparteStorageAdapter;\n private _conversations: AparteConversation[] = [];\n private _activeId: string | null = null;\n private _listeners: Set<Listener> = new Set();\n private _initialized = false;\n private _retention: { maxMessages: number } | null = null;\n\n constructor(adapter: AparteStorageAdapter, options?: ConversationManagerOptions) {\n this._adapter = adapter;\n this._retention = options?.retention ?? null;\n }\n\n // ─── Initialisation ────────────────────────────────────────────────────\n\n /** Load all conversations from the adapter. Call once at app startup. */\n async init(): Promise<void> {\n this._conversations = await this._adapter.loadAll();\n this._initialized = true;\n this._notify();\n }\n\n /**\n * Whether `init()` has completed at least once. Consumers use this to\n * distinguish \"manager still hydrating from storage\" from \"manager\n * hydrated, conversations is genuinely empty\". The conversation-controller\n * relies on this to defer clearing the binding during the IndexedDB\n * hydration window (Angular APP_INITIALIZER race).\n */\n get initialized(): boolean {\n return this._initialized;\n }\n\n // ─── Read ───────────────────────────────────────────────────────────────\n\n get conversations(): AparteConversation[] {\n return this._conversations;\n }\n\n get activeId(): string | null {\n return this._activeId;\n }\n\n get active(): AparteConversation | null {\n if (!this._activeId) return null;\n return this._conversations.find(c => c.id === this._activeId) ?? null;\n }\n\n /** Active conversations (not archived), newest first. */\n get activeConversations(): AparteConversation[] {\n return this._conversations\n .filter(c => !c.archivedAt)\n .sort((a, b) => b.updatedAt - a.updatedAt);\n }\n\n /** Archived conversations, newest first. */\n get archivedConversations(): AparteConversation[] {\n return this._conversations\n .filter(c => !!c.archivedAt)\n .sort((a, b) => b.updatedAt - a.updatedAt);\n }\n\n // ─── Mutations ──────────────────────────────────────────────────────────\n\n /** Create a new empty conversation, persist it, and make it active. */\n async createNew(title = 'New Chat'): Promise<AparteConversation> {\n const now = Date.now();\n const conv: AparteConversation = {\n id: crypto.randomUUID(),\n title,\n createdAt: now,\n updatedAt: now,\n messages: [],\n schemaVersion: APARTE_CONVERSATION_SCHEMA_VERSION,\n };\n this._conversations = [conv, ...this._conversations];\n this._activeId = conv.id;\n await this._adapter.save(conv);\n this._notify();\n return conv;\n }\n\n /** Switch active conversation without touching storage. */\n select(id: string): void {\n if (!this._conversations.find(c => c.id === id)) return;\n this._activeId = id;\n this._notify();\n }\n\n /** Deselect the active conversation (no conv selected). */\n clearActive(): void {\n this._activeId = null;\n this._notify();\n }\n\n /** Append a message to a conversation. Auto-generates title from first user message. */\n async addMessage(convId: string, msg: AparteMessage): Promise<void> {\n const conv = this._find(convId);\n if (!conv) return;\n\n const isFirstUserMsg =\n msg.role === 'user' &&\n conv.messages.every(m => m.role !== 'user');\n\n const updated: AparteConversation = {\n ...conv,\n messages: [...conv.messages, msg],\n updatedAt: Date.now(),\n title: isFirstUserMsg ? this._autoTitle(msg) : conv.title,\n };\n this._replace(updated);\n await this._adapter.save(updated);\n this._notify();\n }\n\n /**\n * Replace the flat active-path messages and optionally the full branch tree.\n * `messages` is always written (active path, for sidebar/title/compat).\n * `tree` is written when provided — it carries the full branching topology.\n */\n async updateMessages(convId: string, messages: AparteMessage[], tree?: ExportedMessageRepository): Promise<void> {\n const conv = this._find(convId);\n if (!conv) return;\n // Opt-in history retention (bounds STORAGE, never the live session).\n if (this._retention) {\n ({ messages, tree } = applyRetention(messages, tree, this._retention.maxMessages));\n }\n // `updatedAt` drives the sidebar sort — bump it ONLY on a genuine\n // content change. `_persistActive()` also fires on plain navigation /\n // teardown (opening a conversation, switching away) ; without this\n // guard, merely viewing a conversation re-saves identical messages and\n // floats it to the top of the list.\n let contentChanged = true;\n try {\n contentChanged = JSON.stringify(messages) !== JSON.stringify(conv.messages);\n } catch { /* unserialisable payload → assume changed */ }\n const updated: AparteConversation = {\n ...conv,\n messages,\n updatedAt: contentChanged ? Date.now() : conv.updatedAt,\n };\n if (tree !== undefined) updated.tree = tree;\n this._replace(updated);\n await this._adapter.save(updated);\n this._notify();\n }\n\n /** Permanently delete a conversation. */\n async delete(id: string): Promise<void> {\n this._conversations = this._conversations.filter(c => c.id !== id);\n if (this._activeId === id) this._activeId = null;\n await this._adapter.delete(id);\n this._notify();\n }\n\n /** Archive a conversation (soft-delete). */\n async archive(id: string): Promise<void> {\n const conv = this._find(id);\n if (!conv) return;\n // Archiving is a metadata change, not a content change — leave\n // `updatedAt` untouched so the conv keeps its real chronological slot.\n const updated: AparteConversation = { ...conv, archivedAt: Date.now() };\n this._replace(updated);\n if (this._adapter.archive) {\n await this._adapter.archive(id);\n } else {\n await this._adapter.save(updated);\n }\n if (this._activeId === id) this._activeId = null;\n this._notify();\n }\n\n /** Restore an archived conversation. */\n async unarchive(id: string): Promise<void> {\n const conv = this._find(id);\n if (!conv) return;\n // Unarchiving is metadata-only — don't bump `updatedAt`, otherwise the\n // restored conv wrongly floats to the top instead of returning to its\n // real chronological position.\n const updated: AparteConversation = { ...conv, archivedAt: undefined };\n this._replace(updated);\n if (this._adapter.unarchive) {\n await this._adapter.unarchive(id);\n } else {\n await this._adapter.save(updated);\n }\n this._notify();\n }\n\n /** Update the title manually. The full input is preserved — UI surfaces\n * (sidebar list, topbar) are responsible for visual truncation via CSS\n * (`min-w-0` + `truncate`). Auto-titles produced internally by\n * `_autoTitle()` remain capped at a sensible length on input. */\n async updateTitle(id: string, title: string): Promise<void> {\n const conv = this._find(id);\n if (!conv) return;\n const updated: AparteConversation = { ...conv, title: title.trim(), updatedAt: Date.now() };\n this._replace(updated);\n await this._adapter.save(updated);\n this._notify();\n }\n\n // ─── Observer ───────────────────────────────────────────────────────────\n\n subscribe(listener: Listener): () => void {\n this._listeners.add(listener);\n return () => this._listeners.delete(listener);\n }\n\n // ─── Private ────────────────────────────────────────────────────────────\n\n private _find(id: string): AparteConversation | undefined {\n return this._conversations.find(c => c.id === id);\n }\n\n private _replace(updated: AparteConversation): void {\n this._conversations = this._conversations.map(c =>\n c.id === updated.id ? updated : c\n );\n }\n\n private _notify(): void {\n const snapshot = [...this._conversations];\n // Each listener is isolated: a throwing/slow listener must not break\n // the notification chain for the others. Errors are surfaced via\n // console.error so they remain debuggable in dev tools.\n this._listeners.forEach(l => {\n try {\n l(snapshot);\n } catch (err) {\n console.error('[ConversationManager] listener threw:', err);\n }\n });\n }\n\n private _autoTitle(msg: AparteMessage): string {\n const raw = msg.content;\n const text = typeof raw === 'string' ? raw : (raw ?? '').toString();\n // Auto-title from the first user message. Full content is preserved\n // (UI surfaces handle visual truncation via CSS). The user can rename\n // freely afterwards via updateTitle() — also untouched.\n return text.trim() || 'New Chat';\n }\n}\n","import type { AparteMessage, AparteAttachment } from '../types/index.js';\nimport type { ConversationManager } from './conversation-manager.js';\nimport type { AparteConversation } from './types.js';\nimport type { ExportedMessageRepository } from '../runtime/message-repository.js';\nimport { resolveConfig } from '../config/config-context.js';\n\n/**\n * Abstract binding between a chat UI and the conversation lifecycle.\n *\n * Each framework wrapper (Angular, React, Vue, Svelte) provides an\n * implementation that knows how to mutate its own message list. The\n * controller orchestrates loading, persistence and abort using only this\n * interface, keeping the conversation engine fully framework-agnostic.\n */\nexport interface AparteChatBinding {\n /** Stable id of the host element (used as `targetId` for scoped events). */\n readonly hostId: string;\n /** The element that emits/receives `aparte-send` and `aparte-path-changed`. */\n readonly host: HTMLElement;\n /** Replace the entire message list (used when switching conversations). */\n setMessages(messages: AparteMessage[]): void;\n /** Append a single message (used for the optimistic user-msg on send). */\n appendMessage(message: AparteMessage): void;\n /** Read the current message list. */\n getMessages(): AparteMessage[];\n /** Clear all messages (e.g. when starting a new conversation). */\n clearMessages(): void;\n /**\n * Export the full conversation tree for persistence.\n * Optional: only available when the binding wraps a `MessageRepository`\n * (e.g. the vanilla viewport). Returns `undefined` when not supported.\n */\n exportTree?(): ExportedMessageRepository | undefined;\n /**\n * Import a previously-persisted tree snapshot, restoring full branch\n * topology. Called by the controller after `setMessages` (which handles\n * cleanup) when the loaded conversation carries a `tree` field.\n * Optional: falls back to the flat `setMessages` load when absent.\n */\n importTree?(tree: ExportedMessageRepository): void;\n}\n\nexport interface AparteConversationControllerOptions {\n /**\n * Conversation manager. If omitted, the controller resolves it from\n * `AparteConfig.getConversationManager()` when first needed.\n */\n manager?: ConversationManager;\n /**\n * Called whenever a new conversation is created lazily (on first user\n * message in an empty thread). Useful for the parent app to sync the URL.\n */\n onConversationCreated?: (id: string) => void;\n}\n\n/**\n * Connects an `AparteChatBinding` to a `ConversationManager`.\n *\n * Responsibilities:\n * - Load messages when `setConversationId(id)` is called.\n * - Lazily create a conversation on the first user `aparte-send` if none active.\n * - Persist updates triggered by `aparte-path-changed` (branch nav, retry, edit).\n * - Dispatch `aparte-abort` when the active conversation changes mid-stream.\n *\n * Usage:\n * ```ts\n * const controller = new AparteConversationController(binding);\n * const stop = controller.bind();\n * controller.setConversationId('abc-123');\n * // …\n * stop();\n * ```\n */\nexport class AparteConversationController {\n private _binding: AparteChatBinding;\n private _options: AparteConversationControllerOptions;\n private _activeId: string | null = null;\n private _isStreaming = false;\n private _isLoadingConversation = false;\n private _ensureInFlight: Promise<string> | null = null;\n\n private _onSendCapture: ((e: Event) => void) | null = null;\n private _onPathChanged: ((e: Event) => void) | null = null;\n private _onMessageStart: (() => void) | null = null;\n private _onMessageDone: (() => void) | null = null;\n private _onMessageError: (() => void) | null = null;\n private _onMessageAborted: (() => void) | null = null;\n private _onSelectConversation: ((e: Event) => void) | null = null;\n private _unsubscribeManager: (() => void) | null = null;\n\n constructor(binding: AparteChatBinding, options: AparteConversationControllerOptions = {}) {\n this._binding = binding;\n this._options = options;\n }\n\n /** The current active conversation id (null when none selected). */\n get activeId(): string | null {\n return this._activeId;\n }\n\n /**\n * Lazily resolve the conversation manager. Returns `undefined` if none is\n * registered — the controller then runs in degraded mode: optimistic UI\n * still works (user message is appended) but no persistence happens. This\n * keeps backward compatibility with hosts that haven't opted into the\n * conversation lifecycle yet.\n */\n private _manager(): ConversationManager | undefined {\n // Explicit option first, then the config governing the host element\n // (instance config under a [data-aparte-host] boundary, else the global).\n return this._options.manager ?? resolveConfig(this._binding.host).getConversationManager();\n }\n\n /**\n * Attach event listeners to the binding host. Returns an unbind function.\n */\n bind(): () => void {\n const host = this._binding.host;\n\n // Capture-phase aparte-send so we can ensure a conversation exists and\n // append the user message BEFORE AparteClient (window-bubble listener)\n // sees the event. Without capture, AparteClient would race with us.\n this._onSendCapture = (e: Event) => {\n const evt = e as CustomEvent<{ content?: string; targetId?: string; files?: File[]; timestamp?: number }>;\n // Only handle events targeting this binding\n const targetId = evt.detail?.targetId;\n if (targetId && targetId !== this._binding.hostId) return;\n\n const content = evt.detail?.content ?? '';\n const files = evt.detail?.files;\n const attachments: AparteAttachment[] | undefined = files?.length\n ? files.map(f => ({\n id: (typeof crypto !== 'undefined' && crypto.randomUUID) ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`,\n name: f.name,\n type: f.type || 'application/octet-stream',\n url: URL.createObjectURL(f),\n size: f.size,\n // Carry the raw File so the storage adapter can persist it\n // to its attachments table; reload reconstructs `url`.\n blob: f,\n }))\n : undefined;\n const userMsg: AparteMessage = {\n id: (typeof crypto !== 'undefined' && crypto.randomUUID) ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`,\n role: 'user',\n content,\n timestamp: evt.detail?.timestamp ?? Date.now(),\n ...(attachments ? { attachments } : {}),\n };\n\n // 1. Optimistic UI: append the user message immediately so it\n // appears before any AI streaming begins.\n this._binding.appendMessage(userMsg);\n\n // 2. Ensure a conversation exists (lazy create on first send) and\n // persist the user message. Done async; AparteClient continues.\n void this._ensureConversationAndPersist(userMsg);\n };\n host.addEventListener('aparte-send', this._onSendCapture, { capture: true });\n\n // Persist after every path change (branch nav, edit, retry).\n // Note: streaming completion does NOT dispatch aparte-path-changed;\n // see _onMessageDone/Error/Aborted below for that case.\n // Guard: skip during setConversationId() — importTree() triggers\n // aparte-path-changed synchronously when restoring branch topology,\n // and persisting there would touch updatedAt causing the conv to\n // float to the top of the history list without any real modification.\n this._onPathChanged = () => {\n if (!this._isLoadingConversation) {\n this._persistActive();\n }\n };\n host.addEventListener('aparte-path-changed', this._onPathChanged);\n\n // Track streaming state so setConversationId can abort cleanly, AND\n // persist freshly-completed assistant turns to storage. Without this,\n // assistant messages would only ever live in the in-memory binding\n // because aparte-path-changed is reserved for branch operations.\n this._onMessageStart = () => { this._isStreaming = true; };\n this._onMessageDone = () => {\n this._isStreaming = false;\n // Defer one microtask so any synchronous listener that finalises\n // the message in the binding (e.g. viewport state flush) runs\n // before we snapshot getMessages().\n queueMicrotask(() => this._persistActive());\n };\n this._onMessageError = () => {\n this._isStreaming = false;\n queueMicrotask(() => this._persistActive());\n };\n this._onMessageAborted = () => {\n this._isStreaming = false;\n queueMicrotask(() => this._persistActive());\n };\n host.addEventListener('aparte-message-start', this._onMessageStart);\n host.addEventListener('aparte-message-done', this._onMessageDone);\n host.addEventListener('aparte-message-error', this._onMessageError);\n host.addEventListener('aparte-message-aborted', this._onMessageAborted);\n\n // App-level selection bus. Lets sidebars / nav menus drive the\n // active conversation without going through Angular @Input bindings,\n // and works even when the user clicks the conversation that's\n // already active (where route params don't re-emit).\n // Detail shape: { id: string | null, targetId?: string }.\n // When `targetId` is set, only the matching binding handles it\n // (multi-chat hosts). When omitted, every bound controller listens.\n this._onSelectConversation = (e: Event) => {\n const evt = e as CustomEvent<{ id: string | null; targetId?: string }>;\n const targetId = evt.detail?.targetId;\n if (targetId && targetId !== this._binding.hostId) return;\n void this.setConversationId(evt.detail?.id ?? null);\n };\n if (typeof window !== 'undefined') {\n window.addEventListener('aparte-select-conversation', this._onSelectConversation);\n }\n\n // Subscribe to manager so we can react to external mutations\n // (e.g. another component deleting/archiving the active conv).\n // Without this the binding would keep showing a phantom history\n // for an id the manager no longer knows about.\n const manager = this._manager();\n if (manager) {\n this._unsubscribeManager = manager.subscribe((convs) => {\n if (!this._activeId) return;\n const stillExists = convs.some((c) => c.id === this._activeId && !c.archivedAt);\n if (!stillExists) {\n // If a stream is in flight, kill it before clearing the binding.\n // Without this, deleting the active conv leaves the orchestrator\n // and AI provider running until they finish naturally.\n if (this._isStreaming) {\n try {\n window.dispatchEvent(new CustomEvent('aparte-abort', {\n detail: { targetId: this._binding.hostId },\n }));\n } catch {\n /* environments without window */\n }\n this._isStreaming = false;\n }\n this._activeId = null;\n this._binding.clearMessages();\n }\n });\n }\n\n return () => this.unbind();\n }\n\n /** Detach event listeners. */\n unbind(): void {\n // Persist current state before tearing down. This covers the case where\n // the host component is destroyed mid-stream (e.g. \"New Chat\" navigates\n // to a new route instance): _persistActive() would never be called\n // otherwise, losing the partially-written assistant message.\n // _persistActive() is a no-op when _activeId is null, so this is safe\n // for new instances that were never given a conversation id.\n this._persistActive();\n const host = this._binding.host;\n if (this._onSendCapture) {\n host.removeEventListener('aparte-send', this._onSendCapture, { capture: true } as EventListenerOptions);\n this._onSendCapture = null;\n }\n if (this._onPathChanged) {\n host.removeEventListener('aparte-path-changed', this._onPathChanged);\n this._onPathChanged = null;\n }\n if (this._onMessageStart) {\n host.removeEventListener('aparte-message-start', this._onMessageStart);\n this._onMessageStart = null;\n }\n if (this._onMessageDone) {\n host.removeEventListener('aparte-message-done', this._onMessageDone);\n this._onMessageDone = null;\n }\n if (this._onMessageError) {\n host.removeEventListener('aparte-message-error', this._onMessageError);\n this._onMessageError = null;\n }\n if (this._onMessageAborted) {\n host.removeEventListener('aparte-message-aborted', this._onMessageAborted);\n this._onMessageAborted = null;\n }\n if (this._onSelectConversation && typeof window !== 'undefined') {\n window.removeEventListener('aparte-select-conversation', this._onSelectConversation);\n this._onSelectConversation = null;\n }\n if (this._unsubscribeManager) {\n this._unsubscribeManager();\n this._unsubscribeManager = null;\n }\n }\n\n /**\n * Switch to (or clear) the active conversation.\n * - `null`: clears the binding and deselects in the manager.\n * - any id: loads the conversation's messages into the binding.\n *\n * Idempotent: calling with the current id forces a re-snapshot from the\n * manager (handy when external mutations could have desynchronised the\n * binding, or when a user re-selects the already-active conv from a\n * sidebar). Streams are only aborted on an actual id change so a same-id\n * reload never kills an in-flight response.\n */\n async setConversationId(id: string | null): Promise<void> {\n const isSwitch = id !== this._activeId;\n const manager = this._manager();\n // Abort any in-flight stream tied to this binding only when actually\n // switching to a different conversation.\n if (isSwitch && this._isStreaming) {\n // Persist the current state (user message + partial assistant reply)\n // BEFORE aborting. The abort event fires asynchronously; if we\n // persist after it, the binding may already be cleared/replaced.\n this._persistActive();\n try {\n window.dispatchEvent(new CustomEvent('aparte-abort', {\n detail: { targetId: this._binding.hostId },\n }));\n } catch {\n /* environments without window */\n }\n this._isStreaming = false;\n }\n\n if (id === null) {\n // Always sync the manager to null so external observers (e.g. sidebar\n // active-id highlight) reflect the cleared state — even when this\n // controller instance already has _activeId = null (new mount on root).\n if (isSwitch) this._persistActive();\n // Abort any in-flight stream when navigating away to home (null),\n // mirroring the same guard that exists for non-null switches above.\n if (isSwitch && this._isStreaming) {\n try {\n window.dispatchEvent(new CustomEvent('aparte-abort', {\n detail: { targetId: this._binding.hostId },\n }));\n } catch {\n /* environments without window */\n }\n this._isStreaming = false;\n }\n this._activeId = null;\n manager?.clearActive();\n if (isSwitch) this._binding.clearMessages();\n return;\n }\n\n const conv = manager?.conversations.find((c: AparteConversation) => c.id === id);\n if (!manager || !conv) {\n // Race-condition guard. When the host (Angular APP_INITIALIZER, route\n // resolver, or any other async init) hands us a conversationId BEFORE\n // the manager has finished hydrating from IndexedDB, the id is\n // legitimately not in `manager.conversations` yet — but it WILL be\n // there shortly after `manager.init()` resolves. Clearing the binding\n // here would wipe the persisted tree from the UI (and break the\n // history that the LLM relies on for multi-turn coherence).\n //\n // Signal: when the manager exists but `initialized` is still false\n // (i.e. `init()` has not yet completed), treat this as \"hydrating\"\n // and defer the decision. Subscribe once; when the manager next\n // emits with the id NOW present, re-run setConversationId(id) to\n // load the tree. If the id is still missing after the emit, we\n // fall back to the clear path below (truly unknown id case).\n //\n // We deliberately do NOT use `conversations.length === 0` here —\n // that conflates \"still hydrating\" with \"hydrated but empty\"\n // (first-time user, no convs yet), which would defer forever.\n if (manager && !manager.initialized) {\n const stop = manager.subscribe(() => {\n stop();\n // Only retry if the id has become known. Otherwise let the\n // next emit (or a later setConversationId call) handle it.\n if (manager.conversations.some((c: AparteConversation) => c.id === id)) {\n void this.setConversationId(id);\n } else if (this._activeId === null) {\n // Hydration finished without the id — that id is truly\n // unknown (deleted conv, stale URL). Clear now.\n console.warn('[ConversationController] id still unknown after hydration — clearing. id:', id);\n manager.clearActive();\n this._binding.clearMessages();\n }\n });\n return;\n }\n console.warn('[ConversationController] unknown id or no manager — clearing. manager:', !!manager, 'conv:', !!conv);\n // Unknown id (or no manager) — treat as clear to keep state consistent.\n this._activeId = null;\n manager?.clearActive();\n this._binding.clearMessages();\n return;\n }\n\n this._activeId = id;\n // The controller is the sole writer of `manager._activeId`. Other\n // components must dispatch `aparte-select-conversation` rather than\n // calling `manager.select()` directly.\n manager.select(id);\n // setMessages always runs first: it handles cleanup (stop streaming,\n // clear rendered ids, reset spacer) and sets the flat active-path signal.\n // importTree (below) may dispatch aparte-path-changed synchronously — flag\n // the load to prevent _onPathChanged from calling _persistActive() and\n // touching updatedAt (which would cause the conv to float to the top).\n this._isLoadingConversation = true;\n try {\n this._binding.setMessages([...conv.messages]);\n // If the conversation has a full tree snapshot and the binding supports\n // importing it, restore the branch topology on top. This is a no-op\n // for bindings that don't implement importTree.\n if (conv.tree && this._binding.importTree) {\n this._binding.importTree(conv.tree);\n }\n } finally {\n this._isLoadingConversation = false;\n }\n }\n\n // ─────────────────────────────────────────────────────────────────────\n // Internal\n // ─────────────────────────────────────────────────────────────────────\n\n /**\n * Snapshot the binding's current messages and push them to the manager.\n * No-op when there is no active conversation or no manager registered.\n * Idempotent: safe to call multiple times in a row.\n *\n * Any message whose status is 'streaming' or 'pending' at persist-time is\n * normalised to 'completed' so that reloading the conversation does not\n * re-enter streaming UI (caret, spinner, no action buttons) for content\n * that was partially written before a switch or component destruction.\n */\n private _persistActive(): void {\n if (!this._activeId) return;\n const manager = this._manager();\n if (!manager) return;\n const msgs = this._binding.getMessages().map(m =>\n (m.status === 'streaming' || m.status === 'pending')\n ? { ...m, status: 'completed' as const }\n : m\n );\n // Export the full branch tree when the binding supports it.\n // Normalize streaming/pending nodes so reloading never re-enters\n // streaming UI for content that was only partially written.\n let tree = this._binding.exportTree?.();\n if (tree) {\n tree = {\n headId: tree.headId,\n messages: tree.messages.map(({ message, parentId }) => ({\n message: (message.status === 'streaming' || message.status === 'pending')\n ? { ...message, status: 'completed' as const }\n : message,\n parentId,\n })),\n };\n }\n void manager.updateMessages(this._activeId, msgs, tree);\n }\n\n private async _ensureConversationAndPersist(userMsg: AparteMessage): Promise<void> {\n const manager = this._manager();\n if (!manager) {\n // No persistence layer wired — optimistic UI was already applied by\n // the send-handler; we have nothing more to do.\n return;\n }\n\n // Capture the active id at send-time. If the user switches\n // conversations while createNew()/addMessage() are in flight we\n // must not clobber the new active id.\n const wasActiveId = this._activeId;\n let convId = wasActiveId ?? manager.activeId;\n\n if (!convId) {\n // Coalesce concurrent sends (e.g. suggestion + quick type) into one\n // conversation creation.\n if (!this._ensureInFlight) {\n // Title kept full-length; UI surfaces handle visual truncation\n // via CSS (`min-w-0` + `truncate`). See conversation-manager\n // updateTitle() / _autoTitle() comments.\n const text = (userMsg.content ?? '').toString().trim() || 'New Chat';\n this._ensureInFlight = manager.createNew(text).then(conv => conv.id);\n }\n convId = await this._ensureInFlight;\n this._ensureInFlight = null;\n\n // Race guard: if the user switched away while createNew() was\n // resolving, the optimistic user message was already replaced\n // in the binding by setConversationId(). The freshly-created\n // conversation is an orphan; drop it and bail rather than\n // persisting a ghost message into a conv the user can't see.\n if (this._activeId !== wasActiveId) {\n void manager.delete(convId).catch(() => { /* best effort */ });\n return;\n }\n\n this._activeId = convId;\n this._options.onConversationCreated?.(convId);\n } else if (this._activeId !== null && this._activeId !== convId) {\n // Existing-conv path but the user switched between the optimistic\n // append and now. The message was visually shown in the old conv;\n // persist it there and stop — do not mutate the new active conv.\n await manager.addMessage(convId, userMsg).catch(() => { /* best effort */ });\n return;\n }\n\n await manager.addMessage(convId, userMsg);\n }\n}\n","import type {\n AparteMessage,\n AparteSegment,\n AparteUsage,\n AparteSiblingInfo,\n ApartePathChangedEventDetail,\n} from '../types/index.js';\nimport type { SyncableBubble } from '../components/bubble/bubble-sync.js';\nimport { populateBubbleFromMessage } from '../components/bubble/bubble-sync.js';\nimport type { ExportedMessageRepository } from '../runtime/message-repository.js';\nimport {\n AparteConversationController,\n type AparteChatBinding,\n} from '../conversations/conversation-controller.js';\nimport type { AparteConfigClass } from '../config/aparte-config.js';\nimport { attachConfig, detachConfig } from '../config/config-context.js';\n\n/**\n * Imperative slice of `<aparte-chat-bubble>` the host mutates directly during\n * streaming (segment add/update/remove + status). Loosely typed so any element\n * exposing the surface works (custom element, test double).\n */\ninterface StreamingBubble extends SyncableBubble {\n addSegment?(segment: AparteSegment): void;\n removeSegment?(segmentId: string): void;\n updateMessage?(updates: { status?: string }): void;\n}\n\n/**\n * Imperative slice of `<aparte-chat-viewport>` the host forwards to. Every method\n * is optional so the host degrades gracefully if a consumer passes a stripped\n * element; the real viewport implements them all.\n */\ninterface ViewportApi {\n appendToken?(messageId: string, chunk: string): void;\n completeMessage?(messageId: string): void;\n addBranch?(messageId: string): number;\n addSiblingOf?(existingId: string, newMessage: AparteMessage): string | null;\n truncateFrom?(messageId: string): void;\n truncateResponsesAfter?(userMessageId: string): void;\n getMessage?(messageId: string): AparteMessage | undefined;\n appendMessage?(message: AparteMessage): void;\n updateMessage?(messageId: string, updates: Partial<AparteMessage>): void;\n exportTree?(): ExportedMessageRepository;\n importTree?(tree: ExportedMessageRepository): void;\n clearAll?(): void;\n resetSpacer?(): void;\n configure?(config: { layoutTransitionMs?: number }): void;\n setAutoScroll?(enabled: boolean): void;\n setFrameworkManagedDOM?(managed: boolean): void;\n requestSmoothScroll?(): void;\n}\n\n/**\n * Everything `AparteChatHost` needs from the framework, expressed without any\n * framework types. Each wrapper (Angular signals, React state, Vue refs, Svelte\n * stores) implements these ~10 closures over its own reactive message list.\n */\nexport interface AparteChatHostBinding {\n /** Stable id for the host element (used as `targetId` for scoped events). */\n readonly hostId: string;\n /** The element `AparteClient` drives + that emits lifecycle/path events. */\n readonly host: HTMLElement;\n /** The `<aparte-chat-viewport>` element (branch ops, token append, bubbles). */\n readonly viewport: HTMLElement | null;\n\n /** Read the current message list (a snapshot). */\n getMessages(): AparteMessage[];\n /** Replace the framework's message list (triggers its re-render). */\n setMessages(messages: AparteMessage[]): void;\n\n /** Notify the consumer that messages changed (drives `messagesChange`). */\n onMessagesChange?(messages: AparteMessage[]): void;\n /** Notify the consumer a message was appended (drives `messageAppended`). */\n onMessageAppended?(message: AparteMessage): void;\n /** Flip the typing/“thinking” indicator. */\n onTypingChange?(isTyping: boolean): void;\n /** Streaming target id changed (drives `isStreaming`). */\n onStreamingChange?(streamingId: string | null): void;\n /** Run a callback after the framework has applied the latest render. */\n afterRender(cb: () => void): void;\n /** Reset the composer (clear text/attachments) on conversation swap. */\n resetComposer?(): void;\n}\n\nexport interface AparteChatHostOptions {\n /** Forwarded to `viewport.configure({ layoutTransitionMs })` when > 0. */\n layoutTransitionMs?: number;\n /** Initial conversation id to load when the controller is created. */\n conversationId?: string | null;\n /** Called when the controller lazily creates a conversation on first send. */\n onConversationCreated?: (id: string) => void;\n /**\n * Instance config for this chat. When provided, {@link bind} marks the host\n * element as a `[data-aparte-host]` boundary so every Aparte component inside it\n * (bubbles, composer, renderers) resolves THIS config instead of the global\n * `AparteConfig` — the seam that lets several independently-configured chats\n * coexist on one page. Omit to keep the global-config behaviour.\n */\n config?: AparteConfigClass;\n}\n\n/**\n * Framework-agnostic chat-host orchestrator.\n *\n * Owns the streaming/branch/host-method layer that previously lived inline in\n * the Angular wrapper: it installs the imperative method surface `AparteClient`\n * calls onto the host element, tracks the streaming target id from\n * `apartemessage*` lifecycle events, guards writes against orphan streams (late\n * SSE after a conversation switch), handles `aparte-path-changed` branch\n * navigation, keeps the viewport's repo in sync before branch ops, and\n * reconciles bubbles via `populateBubbleFromMessage`.\n *\n * It composes — does not replace — `AparteConversationController` (persistence /\n * load / lazy-create) which it instantiates internally from the same binding.\n *\n * Each framework provides an {@link AparteChatHostBinding} over its reactive\n * message list and calls {@link bind} after the view mounts; the framework is\n * responsible for rendering `<aparte-chat-bubble message-id=\"…\">` elements inside\n * the viewport and calling {@link syncBubbles} whenever its message list\n * changes (its idiomatic reactive hook: Angular effect, React effect, Vue\n * watch, Svelte afterUpdate).\n */\nexport class AparteChatHost {\n private readonly binding: AparteChatHostBinding;\n private readonly options: AparteChatHostOptions;\n\n /** Id of the assistant message currently streaming (null when idle). */\n private _streamingId: string | null = null;\n /** Message ids already reconciled onto a bubble (skip-rerender cache). */\n private readonly _renderedIds = new Set<string>();\n /** Sibling info from the last path-change, applied after the next render. */\n private _pendingSiblings?: AparteSiblingInfo[];\n /** Abort handle for an in-flight {@link streamTokens} loop. */\n private _streamAbort?: AbortController;\n\n private _conversationId: string | null = null;\n private _controller?: AparteConversationController;\n private _teardown: Array<() => void> = [];\n private _bound = false;\n\n constructor(binding: AparteChatHostBinding, options: AparteChatHostOptions = {}) {\n this.binding = binding;\n this.options = options;\n this._conversationId = options.conversationId ?? null;\n }\n\n // ── lifecycle ──────────────────────────────────────────────────────────\n\n /**\n * Install the host-method surface + event listeners and create the\n * conversation controller. Returns a teardown function; call once after the\n * view (viewport + composer) has mounted.\n */\n bind(): () => void {\n if (this._bound) return () => this.unbind();\n this._bound = true;\n\n const host = this.binding.host as HTMLElement & Record<string, unknown>;\n if (!host.id) host.id = this.binding.hostId;\n\n // Mark this host as an instance-config boundary FIRST, so everything\n // below (and every component inside, which resolves live) reads the\n // instance config rather than the global singleton.\n if (this.options.config) attachConfig(this.binding.host, this.options.config);\n\n const vp = this._vp();\n vp?.setFrameworkManagedDOM?.(true);\n if (this.options.layoutTransitionMs && this.options.layoutTransitionMs > 0) {\n vp?.configure?.({ layoutTransitionMs: this.options.layoutTransitionMs });\n }\n\n this._installHostMethods(host);\n this._installLifecycleListeners(host);\n this._installViewportListeners();\n this._createController(host);\n\n return () => this.unbind();\n }\n\n unbind(): void {\n if (!this._bound) return;\n this._bound = false;\n this.stopTokenStream();\n for (const off of this._teardown.splice(0)) {\n try { off(); } catch { /* ignore */ }\n }\n this._unbindController?.();\n this._unbindController = undefined;\n this._controller = undefined;\n if (this.options.config) detachConfig(this.binding.host);\n }\n\n // ── streaming state ────────────────────────────────────────────────────\n\n get streamingId(): string | null { return this._streamingId; }\n get isStreaming(): boolean { return this._streamingId !== null; }\n\n /** The internally-managed conversation controller (created in {@link bind}). */\n get conversationController(): AparteConversationController | undefined {\n return this._controller;\n }\n\n /** Clear the bubble-render cache (e.g. when the host clears messages externally). */\n clearRenderCache(): void {\n this._renderedIds.clear();\n }\n\n private _setStreamingId(id: string | null): void {\n if (this._streamingId === id) return;\n this._streamingId = id;\n this.binding.onStreamingChange?.(id);\n }\n\n // ── conversation ───────────────────────────────────────────────────────\n\n /** Load / switch the active conversation via the controller. */\n setConversationId(id: string | null): Promise<void> {\n this._conversationId = id;\n return this._controller?.setConversationId(id) ?? Promise.resolve();\n }\n\n private _unbindController?: () => void;\n\n private _createController(host: HTMLElement): void {\n const convBinding: AparteChatBinding = {\n hostId: this.binding.hostId,\n host,\n // Controller-driven flushes mirror ConversationManager state, not\n // user intent — deliberately NOT emitted as `messagesChange` to\n // avoid a feedback loop with hosts using a two-way `messages` input.\n setMessages: (msgs) => {\n this._beginConversationSwap();\n this._vp()?.resetSpacer?.();\n this.binding.setMessages([...msgs]);\n },\n appendMessage: (msg) => this.appendMessage(msg),\n getMessages: () => this.binding.getMessages(),\n clearMessages: () => {\n this._beginConversationSwap();\n this.binding.setMessages([]);\n this._vp()?.clearAll?.();\n },\n exportTree: () => {\n this.syncRepoFromMessages();\n return this._vp()?.exportTree?.();\n },\n importTree: (tree) => { this._vp()?.importTree?.(tree); },\n };\n\n this._controller = new AparteConversationController(convBinding, {\n onConversationCreated: (id) => {\n this._conversationId = id;\n this.options.onConversationCreated?.(id);\n },\n });\n this._unbindController = this._controller.bind();\n void this._controller.setConversationId(this._conversationId);\n }\n\n /** Drop transient streaming state so a new conversation starts clean. */\n private _beginConversationSwap(): void {\n this.stopTokenStream();\n this._setStreamingId(null);\n this.binding.onTypingChange?.(false);\n this._renderedIds.clear();\n this.binding.resetComposer?.();\n }\n\n // ── host method surface (also installed on the host element) ───────────\n\n /**\n * Append a message optimistically (streaming writes target the new last).\n *\n * Deliberately does NOT call `onMessagesChange` (unlike the other mutations):\n * `setMessages` updates the wrapper's OWN render list, but `onMessagesChange`\n * is the OUTBOUND \"the user changed the list\" notification. During an\n * optimistic append the wrapper's local list can lag a controlled parent's\n * (the parent added a user message that hasn't propagated back yet), so\n * echoing the local list to the parent would overwrite the parent's\n * authoritative list and drop that message — a real race the Angular wrapper\n * spec guards (\"optimistic-append + parent-push race\"). `onMessageAppended`\n * is the append-specific signal instead.\n */\n appendMessage(message: AparteMessage): void {\n if (message.role === 'user') this._vp()?.setAutoScroll?.(true);\n this.binding.setMessages([...this.binding.getMessages(), message]);\n this.binding.onMessageAppended?.(message);\n }\n\n /** Atomic partial update of a message by id. */\n updateMessage(messageId: string, updates: Partial<AparteMessage>): void {\n const msgs = this.binding.getMessages();\n const index = msgs.findIndex((m) => m.id === messageId);\n if (index === -1) return;\n const next = [...msgs];\n next[index] = { ...next[index]!, ...updates };\n this.binding.setMessages(next);\n this.binding.onMessagesChange?.(next);\n // Immediate visual feedback for a status change (e.g. streaming class).\n if (updates.status) {\n this._bubbleById(messageId)?.updateMessage?.({ status: updates.status });\n }\n }\n\n /** Append/replace the last message content (streaming text). */\n updateLastMessage(content: string, options?: { append?: boolean }): void {\n if (options?.append) this.binding.onTypingChange?.(false);\n const msgs = this.binding.getMessages();\n const last = msgs[msgs.length - 1];\n if (!last) return;\n if (this._isOrphan(last.id)) return;\n const newContent = options?.append ? (last.content || '') + content : content;\n const next = [...msgs.slice(0, -1), { ...last, content: newContent }];\n this.binding.setMessages(next);\n this.binding.onMessagesChange?.(next);\n }\n\n /** Add a segment to the last message (immediate bubble + state sync). */\n addSegment(segment: AparteSegment): void {\n const msgs = this.binding.getMessages();\n const last = msgs[msgs.length - 1];\n if (last && this._isOrphan(last.id)) return;\n this.binding.onTypingChange?.(false);\n this._lastBubble()?.addSegment?.(segment);\n if (!last) return;\n const segments = [...(last.segments || []), segment];\n const next = [...msgs.slice(0, -1), { ...last, segments }];\n this.binding.setMessages(next);\n this.binding.onMessagesChange?.(next);\n }\n\n /** Update a segment in the last message in place. */\n updateSegment(segmentId: string, updates: Partial<AparteSegment>): void {\n const msgs = this.binding.getMessages();\n const last = msgs[msgs.length - 1];\n if (last && this._isOrphan(last.id)) return;\n this._lastBubble()?.updateSegment?.(segmentId, updates);\n if (!last || !last.segments) return;\n const segments = last.segments.map((s) =>\n s.id === segmentId ? ({ ...s, ...updates } as AparteSegment) : s,\n );\n const next = [...msgs.slice(0, -1), { ...last, segments }];\n this.binding.setMessages(next);\n this.binding.onMessagesChange?.(next);\n }\n\n /** Remove a transient segment (e.g. pipeline-waiting indicator). */\n removeSegment(segmentId: string): void {\n this._lastBubble()?.removeSegment?.(segmentId);\n const msgs = this.binding.getMessages();\n const last = msgs[msgs.length - 1];\n if (!last?.segments) return;\n const segments = last.segments.filter((s) => s.id !== segmentId);\n if (segments.length === last.segments.length) return;\n const next = [...msgs.slice(0, -1), { ...last, segments }];\n this.binding.setMessages(next);\n this.binding.onMessagesChange?.(next);\n }\n\n /** Append text to a segment's content in the last message. */\n appendToSegment(segmentId: string, content: string): void {\n const msgs = this.binding.getMessages();\n const last = msgs[msgs.length - 1];\n if (!last || !last.segments) return;\n if (this._isOrphan(last.id)) return;\n const segments = last.segments.map((s) =>\n s.id === segmentId && 'content' in s\n ? ({ ...s, content: (s as { content: string }).content + content } as AparteSegment)\n : s,\n );\n const next = [...msgs.slice(0, -1), { ...last, segments }];\n this.binding.setMessages(next);\n this.binding.onMessagesChange?.(next);\n }\n\n /** Read the current message list. */\n getMessages(): AparteMessage[] { return this.binding.getMessages(); }\n\n /** Clear all messages + reset viewport state. */\n clearMessages(): void {\n this._beginConversationSwap();\n this.binding.setMessages([]);\n this._vp()?.clearAll?.();\n }\n\n /** Create a new branch from a message (returns the new sibling index). */\n addBranch(messageId: string): number {\n this.syncRepoFromMessages();\n return this._vp()?.addBranch?.(messageId) ?? 0;\n }\n\n /** Add a sibling of an existing message (returns the new id). */\n addSiblingOf(existingId: string, newMessage: AparteMessage): string | null {\n this.syncRepoFromMessages();\n return this._vp()?.addSiblingOf?.(existingId, newMessage) ?? null;\n }\n\n /** Remove a message and all descendants (edit flow). */\n truncateFrom(messageId: string): void {\n this._vp()?.truncateFrom?.(messageId);\n const msgs = this.binding.getMessages();\n const idx = msgs.findIndex((m) => m.id === messageId);\n const next = idx >= 0 ? msgs.slice(0, idx) : msgs;\n this.binding.setMessages(next);\n this.binding.onMessagesChange?.(next);\n }\n\n /** Keep up to and including a user message, drop later responses (retry). */\n truncateResponsesAfter(userMessageId: string): void {\n this._vp()?.truncateResponsesAfter?.(userMessageId);\n const msgs = this.binding.getMessages();\n const idx = msgs.findIndex((m) => m.id === userMessageId);\n const next = idx >= 0 ? msgs.slice(0, idx + 1) : msgs;\n this.binding.setMessages(next);\n this.binding.onMessagesChange?.(next);\n }\n\n // ── manual token streaming (agnostic core of injectTokenStream) ─────────\n\n /**\n * Drive a manual token stream into the viewport. Sets the streaming guard,\n * appends each token, completes the message at the end. Per-framework\n * wrappers adapt their native stream type (RxJS Observable, AsyncIterable,\n * ReadableStream) into this.\n */\n async streamTokens(messageId: string, tokens: AsyncIterable<string>): Promise<void> {\n this.stopTokenStream();\n this._setStreamingId(messageId);\n const ac = new AbortController();\n this._streamAbort = ac;\n const iterator = tokens[Symbol.asyncIterator]();\n // Resolve as soon as the stream is aborted, so an IDLE source (e.g. the\n // Angular Observable→AsyncIterable adapter parked on a pending next())\n // still unwinds promptly: racing next() against this lets the loop break\n // and call iterator.return() — which unsubscribes — instead of leaving a\n // zombie subscription parked on a next() that never resolves.\n const aborted = new Promise<'aborted'>((resolve) => {\n if (ac.signal.aborted) { resolve('aborted'); return; }\n ac.signal.addEventListener('abort', () => resolve('aborted'), { once: true });\n });\n try {\n for (;;) {\n const res = await Promise.race([iterator.next(), aborted]);\n if (res === 'aborted' || ac.signal.aborted || res.done) break;\n this._vp()?.appendToken?.(messageId, res.value);\n }\n if (!ac.signal.aborted) this._vp()?.completeMessage?.(messageId);\n this._setStreamingId(null);\n } catch (err) {\n this._setStreamingId(null);\n throw err;\n } finally {\n // On abort the source may still be mid-flight; return() triggers its\n // cleanup (generator finally / subscription teardown).\n if (ac.signal.aborted) { try { await iterator.return?.(); } catch { /* ignore */ } }\n if (this._streamAbort === ac) this._streamAbort = undefined;\n }\n }\n\n /** Abort an in-flight {@link streamTokens} loop. */\n stopTokenStream(): void {\n if (this._streamAbort) {\n this._streamAbort.abort();\n this._streamAbort = undefined;\n this._setStreamingId(null);\n }\n }\n\n // ── reconciliation ─────────────────────────────────────────────────────\n\n /**\n * Reconcile rendered bubbles with the current message list. The framework\n * calls this from its reactive message-change hook after rendering; the\n * last message is always re-synced (streaming), others only once.\n */\n syncBubbles(): void {\n const msgs = this.binding.getMessages();\n msgs.forEach((message, index) => {\n const isLast = index === msgs.length - 1;\n if (this._renderedIds.has(message.id) && !isLast) return;\n const bubble = this._bubbleById(message.id);\n if (!bubble) return;\n populateBubbleFromMessage(bubble, message);\n this._renderedIds.add(message.id);\n });\n }\n\n /**\n * Rebuild the viewport's internal repo from the message list before branch\n * operations (streaming fills the framework state but not the repo).\n */\n syncRepoFromMessages(): void {\n const vp = this._vp();\n if (!vp) return;\n for (const msg of this.binding.getMessages()) {\n if (!vp.getMessage?.(msg.id)) {\n vp.appendMessage?.(msg);\n } else {\n const updates: Partial<AparteMessage> = {};\n if (msg.content !== undefined) updates.content = msg.content;\n if (msg.segments !== undefined) updates.segments = msg.segments;\n if (msg.status !== undefined) updates.status = msg.status;\n if (msg.usage !== undefined) updates.usage = msg.usage;\n if (Object.keys(updates).length > 0) vp.updateMessage?.(msg.id, updates);\n }\n }\n }\n\n private _applyPendingSiblings(): void {\n if (!this._pendingSiblings) return;\n const msgs = this.binding.getMessages();\n for (const sib of this._pendingSiblings) {\n if (sib.count <= 1) continue;\n const bubble = this._bubbleById(sib.id);\n if (!bubble) continue;\n const message = msgs.find((m) => m.id === sib.id);\n if (message) populateBubbleFromMessage(bubble, message, sib);\n }\n this._pendingSiblings = undefined;\n }\n\n // ── internals ──────────────────────────────────────────────────────────\n\n /** Orphan-stream guard: a stream is in flight but the last msg isn't it. */\n private _isOrphan(lastId: string): boolean {\n return this._streamingId !== null && lastId !== this._streamingId;\n }\n\n private _vp(): ViewportApi | null {\n return (this.binding.viewport as unknown as ViewportApi | null) ?? null;\n }\n\n private _bubbleById(id: string): StreamingBubble | null {\n const vp = this.binding.viewport;\n if (!vp) return null;\n return vp.querySelector(\n `aparte-chat-bubble[message-id=\"${id}\"]`,\n ) as unknown as StreamingBubble | null;\n }\n\n private _lastBubble(): StreamingBubble | null {\n const vp = this.binding.viewport;\n if (!vp) return null;\n const all = vp.querySelectorAll('aparte-chat-bubble');\n return (all.length ? all[all.length - 1] : null) as unknown as StreamingBubble | null;\n }\n\n private _installHostMethods(host: Record<string, unknown>): void {\n // Bracket notation: these come from the index signature, and consumers\n // compiled with `noPropertyAccessFromIndexSignature` reject dot access.\n host['appendMessage'] = (m: AparteMessage) => this.appendMessage(m);\n host['updateMessage'] = (id: string, u: Partial<AparteMessage>) => this.updateMessage(id, u);\n host['updateLastMessage'] = (c: string, o?: { append?: boolean }) => this.updateLastMessage(c, o);\n host['addSegment'] = (s: AparteSegment) => this.addSegment(s);\n host['updateSegment'] = (id: string, u: Partial<AparteSegment>) => this.updateSegment(id, u);\n host['removeSegment'] = (id: string) => this.removeSegment(id);\n host['appendToSegment'] = (id: string, c: string) => this.appendToSegment(id, c);\n host['getMessages'] = () => this.getMessages();\n host['addBranch'] = (id: string) => this.addBranch(id);\n host['addSiblingOf'] = (id: string, m: AparteMessage) => this.addSiblingOf(id, m);\n host['truncateFrom'] = (id: string) => this.truncateFrom(id);\n host['truncateResponsesAfter'] = (id: string) => this.truncateResponsesAfter(id);\n }\n\n private _installLifecycleListeners(host: HTMLElement): void {\n const onStart = (e: Event) => {\n const d = (e as CustomEvent)?.detail as { messageId?: string } | undefined;\n if (d?.messageId) this._setStreamingId(d.messageId);\n };\n const onDone = (e: Event) => {\n this.binding.onTypingChange?.(false);\n this._setStreamingId(null);\n const d = (e as CustomEvent)?.detail as\n | { messageId?: string; usage?: AparteUsage }\n | undefined;\n if (!d?.messageId || !d.usage) return;\n const next = this.binding.getMessages().map((m) =>\n m.id === d.messageId ? { ...m, usage: d.usage } : m,\n );\n this.binding.setMessages(next);\n this.binding.onMessagesChange?.(next);\n this._bubbleById(d.messageId)?.setUsage?.(d.usage);\n };\n const onEnd = () => {\n this.binding.onTypingChange?.(false);\n this._setStreamingId(null);\n };\n host.addEventListener('aparte-message-start', onStart);\n host.addEventListener('aparte-message-done', onDone);\n host.addEventListener('aparte-message-error', onEnd);\n host.addEventListener('aparte-message-aborted', onEnd);\n this._teardown.push(() => {\n host.removeEventListener('aparte-message-start', onStart);\n host.removeEventListener('aparte-message-done', onDone);\n host.removeEventListener('aparte-message-error', onEnd);\n host.removeEventListener('aparte-message-aborted', onEnd);\n });\n }\n\n private _installViewportListeners(): void {\n const vp = this.binding.viewport;\n if (!vp) return;\n\n const onPathChanged = (e: Event) => {\n const evt = e as CustomEvent<ApartePathChangedEventDetail>;\n const incoming = evt.detail.messages as AparteMessage[];\n // New bubbles created by the framework's diff are empty; force a\n // full re-sync so segments/content land on the right elements.\n this._renderedIds.clear();\n // `usage` lives on the framework state (written by `aparte-message-done`)\n // and may not have reached the rebuilt path — carry it forward by id\n // so the stats popover survives every branch / retry / edit.\n const prev = this.binding.getMessages();\n const merged = incoming.map((m) => {\n if (m.usage !== undefined) return m;\n const old = prev.find((p) => p.id === m.id);\n return old?.usage !== undefined ? { ...m, usage: old.usage } : m;\n });\n this.binding.setMessages(merged);\n this.binding.onMessagesChange?.(merged);\n if (evt.detail.siblings.some((s) => s.count > 1)) {\n this._pendingSiblings = evt.detail.siblings;\n this.binding.afterRender(() => this._applyPendingSiblings());\n }\n };\n // capture=true fires before the viewport's own bubbling handler so the\n // repo is fresh when the navigate op reads it.\n const onBranchNavigate = () => this.syncRepoFromMessages();\n\n vp.addEventListener('aparte-path-changed', onPathChanged);\n vp.addEventListener('aparte-branch-navigate', onBranchNavigate, { capture: true });\n this._teardown.push(() => {\n vp.removeEventListener('aparte-path-changed', onPathChanged);\n vp.removeEventListener('aparte-branch-navigate', onBranchNavigate, { capture: true });\n });\n }\n}\n","/**\n * Exhaustiveness guard for discriminated-union `switch`es. The argument narrows to\n * `never` only when every variant has a matching `case`, so adding a new union\n * member without handling it becomes a TYPECHECK error here instead of being\n * silently ignored at runtime. The throw is the runtime backstop (should be\n * unreachable in well-typed code).\n */\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled union member: ${JSON.stringify(value)}`);\n}\n","/**\n * stream-adapter.ts — the DOM half of the structured-stream agent loop.\n *\n * `@aparte/engine`'s `runStreamAgent` is the headless loop: it emits high-level,\n * DOM-free run events. This adapter is the other half — it turns each event into\n * the exact `targetElement.*` calls + CustomEvents that `AparteClient._streamLoop`\n * performs today, using the **real** `AparteStreamParser` and tool renderers. So\n * `runStreamAgent` (engine, pure Node) + this adapter (core, DOM) reproduce\n * `_streamLoop` byte-for-byte.\n *\n * DEPENDENCY DIRECTION: `@aparte/core` is the zero-dep leaf — it must NEVER import\n * `@aparte/engine` (engine peer-deps core; importing back would create a build\n * cycle). So the run-event contract is **mirrored** here as\n * {@link AparteStreamRunEvent}, structurally identical to engine's `StreamRunEvent`.\n * `@aparte/engine`'s `runStreamAgent` emits objects that satisfy this type; a\n * consumer wires the two together via the injectable\n * `AparteClientOptions.streamRunner` seam — the same pattern as `approvalResolver`\n * (HITL) and `compactionSelector`. Keep the two unions in sync by hand.\n */\n\nimport { AparteStreamParser } from '../parsers/aparte-stream-parser.js';\nimport type { AparteConfigClass } from '../config/aparte-config.js';\nimport type { AparteSegment, AparteMessage, AparteStreamEvent } from '../types/index.js';\nimport type {\n AparteThinkingSegment,\n AparteToolCallSegment,\n AparteArtifactSegment,\n AparteCodeSegment,\n} from '../types/segments.js';\nimport type { AparteUsage, AparteChatRequest } from '../types/chat.js';\n\n/**\n * DOM-free run events emitted by `@aparte/engine`'s `runStreamAgent`, mirrored here\n * so core need not import engine. **Structurally identical** to engine's\n * `StreamRunEvent` — kept in sync manually (the boundary cost of the zero-dep\n * leaf). See `packages/engine/src/agent/stream-events.ts` for the source of truth\n * and the per-event `_streamLoop` mapping notes.\n */\nexport type AparteStreamRunEvent =\n | { type: 'run-start' }\n | { type: 'turn-start' }\n | { type: 'text-delta'; delta: string; reduced?: boolean }\n | { type: 'text-flush' }\n | { type: 'thinking-delta'; delta: string }\n | { type: 'artifact-open'; id: string; mimeType: string; kind: string; title: string }\n | { type: 'artifact-chunk'; id: string; content: string }\n | { type: 'artifact-close'; id: string; content: string; inline: boolean }\n | { type: 'artifact-ready'; id: string; mimeType: string; kind: string; title: string; content: string }\n | { type: 'tool-start'; toolCallId: string; name: string; input: unknown }\n | { type: 'tool-awaiting-approval'; toolCallId: string; name: string; input: unknown }\n | { type: 'tool-approved'; toolCallId: string }\n | { type: 'tool-rejected'; toolCallId: string; reason: string }\n | { type: 'tool-resolved'; toolCallId: string; result: string }\n | { type: 'tool-aborted'; toolCallId: string }\n | { type: 'turn-limit-exceeded'; scope: 'global' | 'tool'; limit: number; toolCallId?: string }\n | { type: 'phase-advance'; index: number }\n | { type: 'run-aborted' }\n | { type: 'run-done'; usage?: AparteUsage };\n\n/** Synchronous, ordered event sink — mirrors engine's `StreamRunEmitter`. */\nexport type AparteStreamRunEmitter = (event: AparteStreamRunEvent) => void;\n\n/**\n * Options for an injected {@link AparteStreamRunner} — structurally identical to\n * engine's `StreamRunOptions` (the mirror boundary again). `AparteClient` builds\n * these from its config/provider/transport and hands them to the runner.\n */\nexport interface AparteStreamRunOptions {\n messageId: string;\n baseRequest: AparteChatRequest;\n /** Calls the transport; returns the structured stream or a plain string. */\n transportCall: (request: AparteChatRequest) => Promise<AsyncIterable<AparteStreamEvent> | string>;\n /** Resolves a tool's handler by name (mirrors `AparteConfig.getToolHandler`). */\n toolLookup: (name: string) => ((call: { id: string; name: string; input: Record<string, unknown> }, signal: AbortSignal) => Promise<{ content: string }>) | undefined;\n /** Resolves a tool's loop config by name (maxTurns / needsApproval). */\n toolConfigLookup?: (name: string) => { maxTurns?: number; needsApproval?: boolean } | undefined;\n /** HITL approval resolver for `needsApproval` tools. */\n approvalResolver?: (toolCallId: string, signal: AbortSignal) => Promise<{ approved: boolean; payload?: unknown }>;\n /** The DOM adapter (from {@link createStreamAdapter}). */\n emitter: AparteStreamRunEmitter;\n /** Single abort signal (the client composes `_isAborted` + the controller). */\n signal: AbortSignal;\n maxTurns?: number;\n toolTimeoutMs?: number;\n idGen?: (prefix: string) => string;\n}\n\n/**\n * A headless structured-stream loop injected via `AparteClientOptions.streamRunner`\n * — the seam by which a consumer swaps `_streamLoop`'s inline loop for\n * `@aparte/engine`'s `runStreamAgent` (core stays the zero-dep leaf; it never\n * imports engine). Structurally identical to `runStreamAgent`; wire it with a\n * cast at the injection site if the duck-typed shapes don't line up exactly.\n */\nexport type AparteStreamRunner = (opts: AparteStreamRunOptions) => Promise<AparteUsage | undefined>;\n\n/**\n * The imperative surface the adapter drives (subset of `AparteChatTargetElement`).\n * Every method is optional so a partial/mock target degrades gracefully.\n */\nexport interface StreamAdapterTarget {\n updateMessage?(id: string, updates: Partial<AparteMessage>): void;\n updateLastMessage?(content: string, options?: { append?: boolean }): void;\n addSegment?(segment: AparteSegment): void;\n updateSegment?(segmentId: string, updates: Partial<AparteSegment>): void;\n typeName?(text: string): void;\n setUsage?(id: string, usage: AparteUsage): void;\n dispatchEvent(event: Event): boolean;\n}\n\nexport interface CreateStreamAdapterOptions {\n /** The chat target element the events are rendered onto. */\n target: StreamAdapterTarget;\n /** Config for tool-renderer lookup + per-tool style injection. */\n config: AparteConfigClass;\n /** The streamed assistant message id (carried in run/artifact events). */\n messageId: string;\n /**\n * Code-fence promotion hint (`baseRequest._meta.artifactHint`). When set, the\n * first `code` segment produced by the text parser is promoted to an artifact\n * — the one `_streamLoop` mechanism that stays adapter-side (it needs the\n * parser). Absent for the raw / XML / create_artifact modes.\n */\n artifactHint?: { mimeType: string; kind: string };\n}\n\n/** `_dispatchLifecycleEvent` (aparte-client.ts) — a bubbling/composed CustomEvent. */\nfunction dispatchLifecycleEvent(target: StreamAdapterTarget, name: string, detail: unknown): void {\n target.dispatchEvent(new CustomEvent(name, { bubbles: true, composed: true, detail }));\n}\n\n/**\n * `_dispatchArtifactLifecycle` (aparte-client.ts) — fires `aparte-artifact-start`\n * once per segment id, `aparte-artifact-delta` when the body grew, and\n * `aparte-artifact-ready` when `isFinal`. `progress` tracks per-id broadcast length.\n */\nfunction dispatchArtifactLifecycle(\n target: StreamAdapterTarget,\n messageId: string,\n segment: { id: string; content?: string; mimeType?: string; artifactType?: string; title?: string },\n progress: Map<string, number>,\n isFinal: boolean,\n): void {\n const id = segment.id;\n const content = segment.content ?? '';\n const seen = progress.get(id);\n\n if (seen === undefined) {\n target.dispatchEvent(new CustomEvent('aparte-artifact-start', {\n bubbles: true, composed: true,\n detail: { messageId, segmentId: id, mimeType: segment.mimeType, artifactType: segment.artifactType, title: segment.title },\n }));\n progress.set(id, 0);\n }\n\n const lastLen = progress.get(id) ?? 0;\n if (content.length > lastLen) {\n const chunk = content.slice(lastLen);\n target.dispatchEvent(new CustomEvent('aparte-artifact-delta', {\n bubbles: true, composed: true,\n detail: { segmentId: id, chunk },\n }));\n progress.set(id, content.length);\n }\n\n if (isFinal) {\n target.dispatchEvent(new CustomEvent('aparte-artifact-ready', {\n bubbles: true, composed: true,\n detail: { messageId, segmentId: id, mimeType: segment.mimeType, artifactType: segment.artifactType, title: segment.title, content },\n }));\n }\n}\n\n/**\n * Build the event → DOM adapter for one streamed message. Returns a synchronous\n * {@link AparteStreamRunEmitter} to hand to `runStreamAgent` as its `emitter`.\n * Reproduces `_streamLoop`'s `targetElement.*` call sequence exactly (validated\n * by the engine parity test against the real loop).\n */\nexport function createStreamAdapter(opts: CreateStreamAdapterOptions): AparteStreamRunEmitter {\n const { target, config, messageId, artifactHint } = opts;\n\n // Per-turn streaming state (reset on `turn-start`, mirroring `_streamLoop`\n // creating a fresh parser / maps each outer iteration).\n let parser = new AparteStreamParser();\n let streaming = new Set<string>();\n let thinkingId: string | null = null;\n let thinkingContent = '';\n let thinkingCollapsed = false;\n let artifactProgress = new Map<string, number>();\n let artifactPromoted = false;\n // id → open-segment meta, so chunk/close can rebuild the full segment for the\n // artifact-lifecycle dispatch (which reads mimeType/artifactType/title).\n let artifactMeta = new Map<string, { mimeType: string; artifactType: string; title: string }>();\n\n return (e: AparteStreamRunEvent): void => {\n switch (e.type) {\n case 'run-start':\n target.updateMessage?.(messageId, { status: 'streaming' });\n break;\n\n case 'turn-start':\n parser = new AparteStreamParser();\n streaming = new Set();\n thinkingId = null;\n thinkingContent = '';\n thinkingCollapsed = false;\n artifactProgress = new Map();\n artifactPromoted = false;\n artifactMeta = new Map();\n break;\n\n case 'thinking-delta': {\n thinkingContent += e.delta;\n if (!thinkingId) {\n const seg: AparteThinkingSegment = {\n id: `think-${crypto.randomUUID()}`,\n type: 'thinking',\n content: thinkingContent,\n collapsed: true,\n label: 'Thinking',\n };\n thinkingId = seg.id;\n streaming.add(seg.id);\n target.addSegment?.(seg);\n } else {\n target.updateSegment?.(thinkingId, { content: thinkingContent });\n }\n break;\n }\n\n case 'text-delta': {\n // Collapse the thinking block when response text starts.\n if (thinkingId && !thinkingCollapsed) {\n target.updateSegment?.(thinkingId, { collapsed: true });\n thinkingCollapsed = true;\n }\n // Reduced pre-tag path (XML mode): render only completed segments;\n // leave the trailing active segment for the next tag-free delta\n // (mirrors _streamLoop :1300-1313). No artifact promotion here —\n // pre-tag text is plain chat.\n if (e.reduced) {\n const r = parser.parse(e.delta);\n for (const segment of r.segments) {\n if (!streaming.has(segment.id)) {\n target.addSegment?.(segment);\n streaming.add(segment.id);\n }\n }\n if (r.segments.length === 0 && !parser.getState().activeSegment) {\n if (target.typeName) target.typeName(e.delta);\n else target.updateLastMessage?.(e.delta, { append: true });\n }\n break;\n }\n const result = parser.parse(e.delta);\n for (let segment of result.segments) {\n // Artifact-hint promotion: first code fence → artifact.\n if (artifactHint && !artifactPromoted && segment.type === 'code') {\n const codeSeg = segment as AparteCodeSegment;\n const promoted: AparteArtifactSegment = {\n id: codeSeg.id,\n type: 'artifact',\n mimeType: artifactHint.mimeType,\n artifactType: artifactHint.kind,\n title: codeSeg.filename ?? artifactHint.kind,\n content: codeSeg.content,\n };\n segment = promoted;\n artifactPromoted = true;\n }\n if (!streaming.has(segment.id)) {\n target.addSegment?.(segment);\n streaming.add(segment.id);\n } else if ('content' in segment) {\n target.updateSegment?.(segment.id, { content: (segment as { content: string }).content });\n }\n if (segment.type === 'artifact') {\n dispatchArtifactLifecycle(target, messageId, segment as AparteArtifactSegment, artifactProgress, true);\n }\n }\n const active = parser.getState().activeSegment;\n if (active) {\n if (!streaming.has(active.id)) {\n target.addSegment?.(active);\n streaming.add(active.id);\n if (active.type === 'artifact') {\n dispatchArtifactLifecycle(target, messageId, active as AparteArtifactSegment, artifactProgress, false);\n }\n } else {\n target.updateSegment?.(active.id, { content: (active as { content: string }).content });\n if (active.type === 'artifact') {\n dispatchArtifactLifecycle(target, messageId, active as AparteArtifactSegment, artifactProgress, false);\n }\n }\n } else if (result.segments.length === 0) {\n if (target.typeName) target.typeName(e.delta);\n else target.updateLastMessage?.(e.delta, { append: true });\n }\n break;\n }\n\n case 'text-flush': {\n const finals = parser.finalize();\n // Finalize-time code-fence promotion (stream ended without ```).\n if (artifactHint && !artifactPromoted) {\n const codeIdx = finals.findIndex(s => s.type === 'code');\n if (codeIdx !== -1) {\n const codeSeg = finals[codeIdx] as AparteCodeSegment;\n const promoted: AparteArtifactSegment = {\n id: codeSeg.id,\n type: 'artifact',\n mimeType: artifactHint.mimeType,\n artifactType: artifactHint.kind,\n title: codeSeg.filename ?? artifactHint.kind,\n content: codeSeg.content,\n };\n finals[codeIdx] = promoted;\n artifactPromoted = true;\n if (streaming.has(promoted.id)) {\n target.updateSegment?.(promoted.id, promoted as Partial<AparteSegment>);\n }\n }\n }\n for (const s of finals) {\n if (!streaming.has(s.id)) target.addSegment?.(s);\n else if ('content' in s) target.updateSegment?.(s.id, { content: (s as { content: string }).content });\n if (s.type === 'artifact') {\n dispatchArtifactLifecycle(target, messageId, s as AparteArtifactSegment, artifactProgress, true);\n }\n }\n break;\n }\n\n case 'artifact-open': {\n const seg: AparteArtifactSegment = {\n id: e.id, type: 'artifact',\n mimeType: e.mimeType, artifactType: e.kind, title: e.title,\n content: '',\n };\n target.addSegment?.(seg);\n streaming.add(e.id);\n artifactMeta.set(e.id, { mimeType: e.mimeType, artifactType: e.kind, title: e.title });\n dispatchArtifactLifecycle(target, messageId, seg, artifactProgress, false);\n break;\n }\n\n case 'artifact-chunk': {\n const meta = artifactMeta.get(e.id);\n target.updateSegment?.(e.id, { content: e.content });\n dispatchArtifactLifecycle(target, messageId, { id: e.id, content: e.content, ...meta }, artifactProgress, false);\n break;\n }\n\n case 'artifact-close': {\n const meta = artifactMeta.get(e.id);\n target.updateSegment?.(e.id, { content: e.content, inline: e.inline } as Partial<AparteSegment>);\n dispatchArtifactLifecycle(target, messageId, { id: e.id, content: e.content, ...meta }, artifactProgress, true);\n break;\n }\n\n case 'artifact-ready': {\n // One-shot create_artifact: full content up-front, no open/chunk.\n const seg: AparteArtifactSegment = {\n id: e.id, type: 'artifact',\n mimeType: e.mimeType, artifactType: e.kind, title: e.title,\n content: e.content,\n };\n target.addSegment?.(seg);\n dispatchArtifactLifecycle(target, messageId, seg, artifactProgress, true);\n break;\n }\n\n case 'tool-start': {\n const toolSeg: AparteToolCallSegment = {\n id: `tool-${e.toolCallId}`,\n type: 'tool_call',\n toolCall: { id: e.toolCallId, name: e.name, input: e.input as Record<string, unknown> },\n status: 'pending',\n };\n const renderer = config.getToolRenderer(e.name);\n if (renderer) {\n // Inject per-tool styles once into document.head.\n if (renderer.getStyles) {\n const styles = renderer.getStyles();\n if (styles) {\n const styleId = `aparte-tool-renderer-${e.name}`;\n if (!document.getElementById(styleId)) {\n const styleEl = document.createElement('style');\n styleEl.id = styleId;\n styleEl.textContent = styles;\n document.head.appendChild(styleEl);\n }\n }\n }\n const html = renderer.render(toolSeg);\n if (html) target.addSegment?.(toolSeg);\n } else {\n target.addSegment?.(toolSeg);\n }\n break;\n }\n\n case 'tool-awaiting-approval':\n target.updateSegment?.(`tool-${e.toolCallId}`, { status: 'awaiting-approval' });\n dispatchLifecycleEvent(target, 'aparte-tool-approval-request', { toolCallId: e.toolCallId, toolName: e.name, input: e.input });\n break;\n\n case 'tool-approved':\n target.updateSegment?.(`tool-${e.toolCallId}`, { status: 'pending' });\n break;\n\n case 'tool-rejected':\n target.updateSegment?.(`tool-${e.toolCallId}`, { status: 'rejected', result: e.reason });\n break;\n\n case 'tool-resolved':\n target.updateSegment?.(`tool-${e.toolCallId}`, { status: 'resolved', result: e.result });\n break;\n\n case 'tool-aborted':\n target.updateSegment?.(`tool-${e.toolCallId}`, { status: 'aborted' });\n break;\n\n case 'turn-limit-exceeded':\n if (e.scope === 'global') {\n target.addSegment?.({\n id: `max-turns-${crypto.randomUUID()}`,\n type: 'error',\n content: `Stopped after ${e.limit} tool calls to prevent an infinite loop.`,\n details: 'MAX_TURNS_EXCEEDED',\n });\n } else if (e.toolCallId) {\n target.updateSegment?.(`tool-${e.toolCallId}`, { status: 'aborted' });\n }\n break;\n\n case 'phase-advance':\n target.addSegment?.({ id: `pw-${crypto.randomUUID()}`, type: 'pipeline-waiting' } as AparteSegment);\n break;\n\n case 'run-aborted':\n dispatchLifecycleEvent(target, 'aparte-message-aborted', { messageId });\n break;\n\n case 'run-done':\n target.updateMessage?.(messageId, { status: 'completed' });\n if (e.usage) {\n try { target.setUsage?.(messageId, e.usage); } catch { /* target may not implement setUsage */ }\n }\n break;\n }\n };\n}\n\n/**\n * Bridge a `ReadableStream` (what the transport returns) to the\n * `AsyncIterable<AparteStreamEvent>` `runStreamAgent` consumes, cancelling the\n * reader **synchronously** on abort so a user \"stop\" cuts the in-flight read.\n * Used by the `_streamLoop` seam when building `transportCall` for an injected\n * runner. Mirrors `_streamLoop`'s `reader.read()` loop + `reader.cancel()`.\n */\nexport function readableToAsyncIterable(\n stream: ReadableStream<AparteStreamEvent>,\n signal: AbortSignal,\n): AsyncIterable<AparteStreamEvent> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<AparteStreamEvent> {\n const reader = stream.getReader();\n const onAbort = () => { try { void reader.cancel(); } catch { /* best effort */ } };\n if (signal.aborted) onAbort();\n else signal.addEventListener('abort', onAbort, { once: true });\n return {\n async next(): Promise<IteratorResult<AparteStreamEvent>> {\n const { done, value } = await reader.read();\n if (done) return { done: true, value: undefined };\n return { done: false, value };\n },\n async return(): Promise<IteratorResult<AparteStreamEvent>> {\n signal.removeEventListener('abort', onAbort);\n try { reader.releaseLock(); } catch { /* best effort */ }\n return { done: true, value: undefined };\n },\n };\n },\n };\n}\n","import { AparteConfig, AparteConfigClass } from '../config/aparte-config.js';\nimport { AparteStreamParser, deriveArtifactKind } from '../parsers/aparte-stream-parser.js';\nimport { registerDefaultRenderers } from '../renderers/segment-renderers.js';\nimport { assertNever } from '../utils/assert-never.js';\nimport { createStreamAdapter, readableToAsyncIterable } from './stream-adapter.js';\nimport type { AparteStreamRunner, StreamAdapterTarget } from './stream-adapter.js';\nimport type { AparteSegment, AparteStreamEvent, AparteMessage, AparteErrorSegment } from '../types/index.js';\nimport type { AparteAIProvider } from '../types/model-provider.js';\nimport type { AparteThinkingSegment } from '../types/segments.js';\nimport type { AparteToolCallSegment } from '../types/segments.js';\nimport type { AparteToolCall, AparteTool } from '../types/tools.js';\nimport { AparteChatRequest, AparteChatMessage, AparteContentPart, AparteUsage, AparteRequestMeta, AparteArtifactHint, contentToText } from '../types/chat.js';\nimport { AparteError, AparteErrorCode } from '../types/errors.js';\n\n/**\n * The imperative surface AparteClient drives on a chat target element\n * (`<aparte-chat-viewport>` directly, or a framework host via AparteChatHost).\n * Every method is optional so a partial/mock target degrades gracefully — the\n * client always calls them through optional chaining. Mirrors the shape the\n * wrappers and `AparteChatHost` already conform to.\n */\n/** Mutable state for streaming a Claude-style `<artifact>` XML block out of the\n * text stream — owned by _streamLoop, fed to _feedXmlArtifactDelta per delta. */\ninterface XmlArtifactStreamState {\n state: 'normal' | 'scanning' | 'in-artifact';\n scanBuf: string;\n closeBuf: string;\n segId: string | null;\n content: string;\n mime: string;\n kind: string;\n title: string;\n}\n\ninterface AparteChatTargetElement extends HTMLElement {\n appendMessage?(message: AparteMessage): void;\n updateMessage?(id: string, updates: Partial<AparteMessage>): void;\n updateLastMessage?(content: string, options?: { append?: boolean }): void;\n addSegment?(segment: AparteSegment): void;\n updateSegment?(segmentId: string, updates: Partial<AparteSegment>): void;\n removeSegment?(segmentId: string): void;\n getMessages?(): AparteMessage[];\n addSiblingOf?(existingId: string, newMessage: AparteMessage): string | null;\n truncateFrom?(id: string): void;\n truncateResponsesAfter?(userMessageId: string): void;\n typeName?(text: string): void;\n}\n\n/** Timeout (ms) for a tool handler to resolve before it is aborted */\nconst TOOL_HANDLER_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Resolves a human-in-the-loop tool approval for a `needsApproval` tool call.\n * Resolves `{ approved, payload? }`; the `signal` aborts a pending decision.\n */\nexport type AparteToolApprovalResolver = (\n toolCallId: string,\n signal: AbortSignal,\n) => Promise<{ approved: boolean; payload?: unknown }>;\n\n/**\n * Decides how a conversation is compacted: which messages are summarized away\n * (`drop`) and which are preserved verbatim (`keep`). Pure — no LLM call.\n *\n * The default selector drops the whole history (summarize everything, replace\n * all), which is the built-in behaviour. Inject a budget-aware selector (e.g.\n * wrapping `@aparte/engine`'s `compactConversation`) so the compaction badge and\n * the `compact()` action share one selection and only the old turns are sent to\n * the summarizer — the budget is closed over by the consumer, not core's to know.\n */\nexport type AparteCompactionSelector = (\n messages: AparteMessage[],\n) => { keep: AparteMessage[]; drop: AparteMessage[] };\n\n/**\n * Configuration options for AparteClient\n */\nexport interface AparteClientOptions {\n /**\n * Function to resolve API keys for a given provider.\n * Can return a string (key) or a full configuration object.\n */\n keyResolver?: (providerId: string) => string | Record<string, string> | Promise<string | Record<string, string> | undefined | null> | undefined | null;\n\n /**\n * Custom human-in-the-loop approval resolver for tools marked\n * `needsApproval`. Defaults to a global `document` `aparte-tool-decision`\n * listener (the built-in Approve/Reject gate). Inject this to run multiple\n * isolated clients on one page, or to drive approval from a headless source\n * (CLI / webhook) with no DOM.\n */\n approvalResolver?: AparteToolApprovalResolver;\n\n /**\n * Custom compaction selection strategy. Defaults to dropping the entire\n * history (summarize all, replace all — the built-in behaviour). Inject a\n * budget-aware selector so only old turns are summarized and recent ones are\n * kept verbatim. See {@link AparteCompactionSelector}.\n */\n compactionSelector?: AparteCompactionSelector;\n\n /**\n * Optional headless stream-loop runner. When set, `_streamLoop` delegates the\n * agentic loop to it and renders via the core adapter\n * ({@link createStreamAdapter}); when absent, the built-in inline loop runs.\n * Inject `@aparte/engine`'s `runStreamAgent` here so a backend/cloud path\n * shares one tested loop — core stays the zero-dep leaf and never imports\n * engine. Same injection pattern as {@link approvalResolver} /\n * {@link compactionSelector}. See {@link AparteStreamRunner}.\n */\n streamRunner?: AparteStreamRunner;\n\n /**\n * Optional request interceptor to modify the chat request before sending.\n */\n requestInterceptor?: (request: AparteChatRequest) => AparteChatRequest | Promise<AparteChatRequest>;\n\n /**\n * Whether to automatically register default segment renderers.\n * @default true\n */\n autoRegister?: boolean;\n\n /**\n * Conversation history strategy:\n * - 'viewport' (default) — collects completed messages from the viewport\n * - 'none' — sends only the current message (original behavior)\n * - function — custom: receives viewport messages, returns AparteChatMessage[]\n */\n history?: 'viewport' | 'none' | ((viewportMessages: AparteMessage[]) => AparteChatMessage[]);\n\n /**\n * Optional resolver that returns the host element exposing `appendMessage`.\n * Use this when the default event-bubble walk cannot reach the host\n * (e.g. Angular re-renders detach the input element mid-flight).\n *\n * @example\n * targetResolver: () => document.querySelector('aparte-chat')\n */\n targetResolver?: () => HTMLElement | null;\n\n /**\n * Scope this client to a specific target element id.\n * When set, the client will only handle `aparte-send`, `aparte-retry`, `aparte-edit`\n * and `aparte-abort` events whose `detail.targetId` matches this id.\n * This allows multiple AparteClient instances (one per conversation) to coexist\n * on the same page without interfering with each other.\n *\n * @example\n * // Two independent conversations:\n * new AparteClient({ scopeToTargetId: 'chat-left' }).start();\n * new AparteClient({ scopeToTargetId: 'chat-right' }).start();\n */\n scopeToTargetId?: string;\n\n /**\n * Maximum number of agentic tool-call loop turns before the loop is forcibly\n * stopped and an error segment is shown. Prevents infinite loops.\n * Individual tools can override this via `AparteTool.maxTurns`.\n * @default 10\n */\n maxTurns?: number;\n\n /**\n * Controls which files attached by the user are injected as raw content\n * parts in the LLM request.\n *\n * - `'all'` (default) — images as base64, text files as code-fenced text\n * - `'images-only'` — only images are injected; text/binary documents\n * are left for the application layer (e.g. a RAG pipeline) to handle\n * - `'none'` — no files are injected as content parts; ALL file\n * types (including images) are routed to the application layer. Used\n * when images go through a captioning pipeline before RAG ingest, so\n * the chat context only ever sees retrieved text — never raw image\n * bytes. Saves bytes on every turn and matches the offline-first\n * intent (read file once at upload, retrieve text-only forever after).\n *\n * Set `'images-only'` when a `requestInterceptor` retrieves relevant\n * document chunks and injects them as a system message instead, to avoid\n * flooding the context window with full file contents.\n */\n rawFileInject?: 'all' | 'images-only' | 'none';\n\n /**\n * Config this client reads (providers, model selection, tools, system\n * prompt). Defaults to the global `AparteConfig` singleton. Pass a host's\n * instance config when scoping a client to one chat among several\n * (pairs with `scopeToTargetId`).\n */\n config?: AparteConfigClass;\n}\n\n/**\n * AparteClient\n *\n * The \"Automatic Transmission\" for Aparte.\n * Connects the UI events (aparte-send) to the AI Providers (chat).\n * Handles:\n * - Listening to send events\n * - Resolving API keys\n * - Calling the appropriate Provider\n * - Streaming the response back to the UI\n * - Tool use: awaiting handlers and re-calling the provider\n *\n * @example\n * ```typescript\n * const client = new AparteClient({\n * keyResolver: (providerId) => process.env[providerId.toUpperCase() + '_KEY']\n * });\n * client.start();\n * ```\n */\nexport class AparteClient {\n private _boundHandler: ((e: Event) => void) | null = null;\n private _boundAbortHandler: (() => void) | null = null;\n private _boundCompactHandler: (() => void) | null = null;\n private _boundRetryHandler: ((e: Event) => void) | null = null;\n private _boundEditHandler: ((e: Event) => void) | null = null;\n private _activeToolControllers: Set<AbortController> = new Set();\n private _isAborted = false;\n /** Aborts the in-flight vendor/transport fetch when the user stops a stream. */\n private _streamController: AbortController | null = null;\n private options: AparteClientOptions;\n /** Config read by this client — an instance config, or the global default. */\n private readonly _config: AparteConfigClass;\n\n constructor(options: AparteClientOptions = {}) {\n this.options = {\n autoRegister: true,\n ...options\n };\n this._config = options.config ?? AparteConfig;\n\n if (this.options.autoRegister) {\n registerDefaultRenderers();\n }\n\n this._setupListeners();\n }\n\n /**\n * Sets up the event listeners.\n * This is called once in the constructor.\n */\n private _setupListeners(): void {\n if (this._boundHandler) return; // Already set up\n\n this._boundHandler = (e: Event) => {\n const event = e as CustomEvent;\n if (event.type !== 'aparte-send') return;\n // Scope guard: ignore events not for this instance\n if (this.options.scopeToTargetId) {\n const evtTargetId = (event.detail as { targetId?: string })?.targetId as string | undefined;\n if (evtTargetId && evtTargetId !== this.options.scopeToTargetId) return;\n }\n // Reset abort flag and cancel any tool calls from a previous turn\n this._isAborted = false;\n for (const controller of this._activeToolControllers) {\n controller.abort();\n }\n this._activeToolControllers.clear();\n\n void this._handleSend(event);\n };\n }\n\n /**\n * Start listening for aparte-send events on the window.\n */\n start(): void {\n if (!this._boundHandler) {\n this._setupListeners();\n }\n if (this._boundHandler) {\n window.addEventListener('aparte-send', this._boundHandler);\n }\n if (!this._boundAbortHandler) {\n this._boundAbortHandler = (e?: Event) => {\n // Scope guard\n if (this.options.scopeToTargetId) {\n const evtTargetId = ((e as CustomEvent)?.detail as { targetId?: string })?.targetId as string | undefined;\n if (evtTargetId && evtTargetId !== this.options.scopeToTargetId) return;\n }\n this.abort();\n };\n }\n window.addEventListener('aparte-abort', this._boundAbortHandler);\n if (!this._boundCompactHandler) {\n this._boundCompactHandler = () => { void this.compact(); };\n }\n window.addEventListener('aparte-compact', this._boundCompactHandler);\n\n if (!this._boundRetryHandler) {\n this._boundRetryHandler = (e: Event) => {\n const evt = e as CustomEvent;\n if (this.options.scopeToTargetId) {\n const evtTargetId = (evt.detail as { targetId?: string })?.targetId as string | undefined;\n if (evtTargetId && evtTargetId !== this.options.scopeToTargetId) return;\n }\n void this._handleRetry(evt);\n };\n }\n window.addEventListener('aparte-retry', this._boundRetryHandler);\n\n if (!this._boundEditHandler) {\n this._boundEditHandler = (e: Event) => {\n const evt = e as CustomEvent;\n if (this.options.scopeToTargetId) {\n const evtTargetId = (evt.detail as { targetId?: string })?.targetId as string | undefined;\n if (evtTargetId && evtTargetId !== this.options.scopeToTargetId) return;\n }\n void this._handleEdit(evt);\n };\n }\n window.addEventListener('aparte-edit', this._boundEditHandler);\n }\n\n /**\n * Stop listening.\n */\n stop(): void {\n if (!this._boundHandler) return;\n window.removeEventListener('aparte-send', this._boundHandler);\n this._boundHandler = null;\n if (this._boundAbortHandler) {\n window.removeEventListener('aparte-abort', this._boundAbortHandler);\n this._boundAbortHandler = null;\n }\n if (this._boundCompactHandler) {\n window.removeEventListener('aparte-compact', this._boundCompactHandler);\n this._boundCompactHandler = null;\n }\n if (this._boundRetryHandler) {\n window.removeEventListener('aparte-retry', this._boundRetryHandler);\n this._boundRetryHandler = null;\n }\n if (this._boundEditHandler) {\n window.removeEventListener('aparte-edit', this._boundEditHandler);\n this._boundEditHandler = null;\n }\n }\n\n /**\n * Abort the current streaming response and all active tool calls.\n * Dispatches `aparte-message-aborted` on the target element.\n */\n abort(): void {\n this._isAborted = true;\n this._streamController?.abort();\n for (const controller of this._activeToolControllers) {\n controller.abort();\n }\n this._activeToolControllers.clear();\n }\n\n /**\n * Human-in-the-loop: wait for an `aparte-tool-decision` event matching this\n * tool call (dispatched by the built-in Approve/Reject UI or an app-level\n * approval surface). Resolves `{ approved, payload }` — `approved` is `true`\n * only on an explicit approve, and `payload` carries any arbitrary data a\n * custom approval UI attached to the decision (the built-in gate sends\n * none). The `signal` (an AbortController registered in\n * `_activeToolControllers`) lets `abort()` cleanly resolve a pending\n * approval to `{ approved: false }` — there is no timeout, since a human may\n * take any amount of time to decide.\n */\n private _awaitToolDecision(toolCallId: string, signal: AbortSignal): Promise<{ approved: boolean; payload?: unknown }> {\n return new Promise<{ approved: boolean; payload?: unknown }>((resolve) => {\n if (signal.aborted) { resolve({ approved: false }); return; }\n const cleanup = () => {\n document.removeEventListener('aparte-tool-decision', onDecision as EventListener);\n signal.removeEventListener('abort', onAbort);\n };\n const onDecision = (e: Event) => {\n const detail = (e as CustomEvent).detail as { toolCallId?: string; approved?: boolean; payload?: unknown } | undefined;\n if (detail?.toolCallId !== toolCallId) return;\n cleanup();\n resolve({ approved: detail?.approved === true, payload: detail?.payload });\n };\n const onAbort = () => { cleanup(); resolve({ approved: false }); };\n document.addEventListener('aparte-tool-decision', onDecision as EventListener);\n signal.addEventListener('abort', onAbort, { once: true });\n });\n }\n\n /**\n * Resolve the auth for a provider: `options.keyResolver` takes precedence,\n * then the AparteConfig key channel (`setKeyProvider`) so a key registered\n * there reaches the request. One key source on the happy path.\n */\n private async _resolveAuth(providerId: string): Promise<string | Record<string, string> | undefined> {\n if (this.options.keyResolver) {\n const resolved = await this.options.keyResolver(providerId);\n if (resolved) return resolved;\n }\n const key = await this._config.getKey(providerId);\n return key || undefined;\n }\n\n /**\n * The shared tail of send / retry / edit: attach the current tools, run the\n * request interceptor, honour `toolChoice: 'none'`, reset the abort flag,\n * dispatch `aparte-message-start`, run the agentic `_streamLoop`, then dispatch\n * `aparte-message-done` or route the error to the lifecycle handler.\n *\n * Callers own only their turn-specific prep (target resolution, history\n * building, appending the assistant placeholder) and hand the fully-built\n * `messages` here — one place for the provider→interceptor→toolChoice→stream\n * sequence, so it can't drift between the three entry points.\n */\n private async _streamTurn(\n targetElement: AparteChatTargetElement,\n messageId: string,\n provider: AparteAIProvider,\n messages: AparteChatMessage[],\n modelId: string,\n authConfig: string | Record<string, string> | undefined,\n opts?: { temperature?: number },\n ): Promise<void> {\n const registeredTools = this._toolsForCurrentModel();\n let baseRequest: AparteChatRequest = {\n messages,\n modelId,\n stream: true,\n tools: registeredTools.length ? registeredTools : undefined,\n ...(opts?.temperature !== undefined ? { temperature: opts.temperature } : {}),\n };\n if (this.options.requestInterceptor) {\n baseRequest = await this.options.requestInterceptor(baseRequest);\n }\n // toolChoice: 'none' — strip tools so the model never sees them.\n if (baseRequest.toolChoice === 'none') {\n baseRequest = { ...baseRequest, tools: undefined };\n }\n\n this._isAborted = false;\n this._dispatchLifecycleEvent(targetElement, 'aparte-message-start', { messageId, role: 'assistant' });\n try {\n const usage = await this._streamLoop(targetElement, messageId, provider, baseRequest, authConfig);\n this._dispatchLifecycleEvent(targetElement, 'aparte-message-done', { messageId, role: 'assistant', usage });\n } catch (error: unknown) {\n const aparteError = AparteError.from(error, AparteErrorCode.UNKNOWN_ERROR);\n this._handleLifecycleError(targetElement, messageId, aparteError);\n }\n }\n\n /**\n * Compact the current conversation: summarize all messages via the AI,\n * clear the viewport, then inject the summary as a single context message.\n *\n * Triggered programmatically or by dispatching `window.dispatchEvent(new CustomEvent('aparte-compact'))`.\n * Dispatches `aparte-compact-done` on window when complete, or `aparte-compact-error` on failure.\n */\n async compact(): Promise<void> {\n // 1. Resolve target element\n let target: AparteChatTargetElement | null = null;\n if (this.options.targetResolver) {\n target = this.options.targetResolver() as AparteChatTargetElement | null;\n }\n if (!target) {\n // Walk the DOM for any element exposing getMessages\n const candidates = document.querySelectorAll<AparteChatTargetElement>('aparte-chat, [data-aparte-chat]');\n for (const el of Array.from(candidates)) {\n if (typeof el.getMessages === 'function') {\n target = el;\n break;\n }\n }\n }\n if (!target) {\n window.dispatchEvent(new CustomEvent('aparte-compact-error', { detail: { error: 'No aparte-chat target found' } }));\n return;\n }\n\n const messages: AparteMessage[] = target.getMessages?.() ?? [];\n if (messages.length === 0) {\n window.dispatchEvent(new CustomEvent('aparte-compact-done', { detail: { skipped: true } }));\n return;\n }\n\n // Decide what to summarize (`drop`) vs preserve verbatim (`keep`).\n // Default: drop everything (summarize all, replace all — legacy behaviour).\n const selector = this.options.compactionSelector\n ?? ((m: AparteMessage[]) => ({ keep: [] as AparteMessage[], drop: m }));\n const { keep, drop } = selector(messages);\n if (drop.length === 0) {\n // Nothing old enough to summarize (e.g. already within budget).\n window.dispatchEvent(new CustomEvent('aparte-compact-done', { detail: { skipped: true } }));\n return;\n }\n\n // 2. Resolve provider + model\n const config = this._config.getModelConfig();\n const providerId = config.defaultProvider;\n if (!providerId) {\n window.dispatchEvent(new CustomEvent('aparte-compact-error', { detail: { error: 'No provider configured' } }));\n return;\n }\n const provider = this._config.getAIProvider(providerId);\n if (!provider) {\n window.dispatchEvent(new CustomEvent('aparte-compact-error', { detail: { error: `Provider '${providerId}' not found` } }));\n return;\n }\n\n // 3. Dispatch start so host can show loading state\n window.dispatchEvent(new CustomEvent('aparte-compact-start'));\n\n try {\n // 4. Resolve auth\n const authConfig = await this._resolveAuth(providerId);\n\n // 5. Build summarize request — only over the dropped (old) turns\n const historyMessages: AparteChatMessage[] = drop\n .filter(m => m.role === 'user' || (m.role === 'assistant' && m.status === 'completed'))\n .map(m => ({\n role: m.role as 'user' | 'assistant',\n content: this._extractText(m)\n }))\n .filter(m => contentToText(m.content).length > 0);\n\n const summarizeRequest: AparteChatRequest = {\n messages: [\n {\n role: 'system',\n content:\n 'You are a conversation summarizer. ' +\n 'Create a concise but complete summary of the conversation below. ' +\n 'Capture: key topics, conclusions, decisions, ongoing tasks, and any context needed to continue the conversation. ' +\n 'Write in third person. Be factual and brief. No preamble, just the summary.'\n },\n ...historyMessages,\n {\n role: 'user',\n content: 'Please summarize this conversation.'\n }\n ],\n modelId: config.defaultModel || '',\n stream: false\n };\n\n // 6. Call provider (non-streaming)\n const response = await this._config.getTransport().chat(provider, summarizeRequest, authConfig, { providerId: provider.id });\n let summary: string;\n if (typeof response === 'string') {\n summary = response;\n } else {\n // Collect stream fallback\n const reader = (response as ReadableStream<AparteStreamEvent>).getReader();\n const chunks: string[] = [];\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value.type === 'text') chunks.push(value.delta);\n }\n reader.releaseLock();\n summary = chunks.join('');\n }\n\n if (!summary.trim()) {\n throw new Error('Empty summary returned by model');\n }\n\n // 7. Clear viewport and inject summary\n window.dispatchEvent(new CustomEvent('aparte-reset'));\n\n // Small delay to let clearAll() finish DOM cleanup\n await new Promise<void>(resolve => setTimeout(resolve, 50));\n\n target.appendMessage?.({\n id: crypto.randomUUID(),\n role: 'assistant',\n content: `📝 **Conversation summary**\\n\\n${summary}`,\n timestamp: Date.now(),\n status: 'completed'\n });\n\n // 7b. Re-append the preserved recent turns verbatim after the summary.\n // (No-op with the default selector, which keeps nothing.)\n for (const kept of keep) {\n target.appendMessage?.(kept);\n }\n\n // 8. Done\n window.dispatchEvent(new CustomEvent('aparte-compact-done', { detail: { summary, kept: keep.length } }));\n\n } catch (err: unknown) {\n console.error('[AparteClient] compact() failed:', err);\n const message = err instanceof Error ? err.message : String(err);\n window.dispatchEvent(new CustomEvent('aparte-compact-error', { detail: { error: message } }));\n }\n }\n\n /**\n * Handle aparte-retry — add a sibling branch to the assistant message and re-stream\n * using the same conversation history minus the retried reply.\n */\n /**\n * Registered tools, gated by capability: only returned when the current model\n * declares `function_calling` support (else `[]`). Single source for the gate so\n * send / retry / edit can't drift — the drift is exactly what shipped `tools` on\n * the initial send while retry/edit correctly omitted them.\n */\n private _toolsForCurrentModel(): AparteTool[] {\n const supportsFunctionCalling =\n this._config.getCurrentModel()?.capabilities?.includes('function_calling') ?? false;\n return supportsFunctionCalling ? this._config.getTools() : [];\n }\n\n private async _handleRetry(event: CustomEvent): Promise<void> {\n const { messageId, targetId } = event.detail ?? {};\n if (!messageId) return;\n\n const targetElement = this._resolveTarget<AparteChatTargetElement>(targetId);\n if (!targetElement) {\n console.warn('[AparteClient] aparte-retry — no target found');\n return;\n }\n\n // Build history BEFORE calling addSiblingOf (getMessages() returns active path)\n const allMessages: AparteMessage[] = targetElement.getMessages?.() ?? [];\n const retryIdx = allMessages.findIndex(m => m.id === messageId);\n const retryMsg = retryIdx >= 0 ? allMessages[retryIdx] : undefined;\n // For user messages: include the user message in history (AI needs to see the question).\n // For assistant messages: exclude it (we are regenerating that response).\n const sliceEnd = retryMsg?.role === 'user' ? retryIdx + 1 : retryIdx;\n const historyMessages = retryIdx > 0 ? allMessages.slice(0, sliceEnd) : allMessages;\n\n // Create new sibling message and get its ID for streaming\n const newMsg: AparteMessage = {\n id: crypto.randomUUID(),\n role: 'assistant',\n content: '',\n status: 'pending',\n timestamp: Date.now(),\n };\n const newMessageId = targetElement.addSiblingOf?.(messageId, newMsg) ?? newMsg.id;\n\n const config = this._config.getModelConfig();\n const providerId = config.defaultProvider;\n if (!providerId) return;\n const provider = this._config.getAIProvider(providerId);\n if (!provider) return;\n\n const authConfig = await this._resolveAuth(providerId);\n\n const chatMessages = this._messagesToChatMessages(historyMessages);\n\n // Add system prompts\n const systemMessages: import('../types/chat.js').AparteChatMessage[] = [];\n const userSystemPrompt = this._config.resolveSystemPrompt();\n if (userSystemPrompt) systemMessages.push({ role: 'system', content: userSystemPrompt });\n\n // Retry must produce a DIFFERENT answer than the greedy (byte-identical)\n // re-run: temperature > 0 opts into sampling (the worker turns on\n // do_sample); variation comes from the in-decoder RNG, no seed needed.\n await this._streamTurn(\n targetElement, newMessageId, provider,\n [...systemMessages, ...chatMessages], config.defaultModel || '',\n authConfig, { temperature: 0.4 },\n );\n }\n\n /**\n * Handle aparte-edit — update the user message in place, truncate all subsequent\n * messages, then re-stream a fresh assistant response.\n */\n private async _handleEdit(event: CustomEvent): Promise<void> {\n const { messageId, content: newContent, targetId } = event.detail ?? {};\n if (!messageId || newContent === undefined) return;\n\n const targetElement = this._resolveTarget<AparteChatTargetElement>(targetId);\n if (!targetElement) {\n console.warn('[AparteClient] aparte-edit — no target found');\n return;\n }\n\n // 1. Update the user message content\n targetElement.updateMessage?.(messageId, { content: newContent });\n\n // 2. Collect all messages, find index of edited message\n const allMessages: AparteMessage[] = targetElement.getMessages?.() ?? [];\n const editIdx = allMessages.findIndex(m => m.id === messageId);\n\n // 3. Remove ALL previous responses to the edited user message.\n // truncateResponsesAfter clears every sibling branch so the new\n // response starts as the only child (sibling count = 1).\n // Fall back to truncateFrom on the active next message for older hosts.\n if (targetElement.truncateResponsesAfter) {\n targetElement.truncateResponsesAfter(messageId);\n } else {\n const nextAssistantId = editIdx >= 0 && editIdx + 1 < allMessages.length\n ? allMessages[editIdx + 1]?.id\n : undefined;\n if (nextAssistantId) {\n targetElement.truncateFrom?.(nextAssistantId);\n }\n }\n\n // 4. Build new history up to and including the edited user message\n const historyMessages = editIdx >= 0 ? allMessages.slice(0, editIdx + 1) : allMessages;\n const chatMessages = this._messagesToChatMessages(historyMessages);\n\n const systemMessages: import('../types/chat.js').AparteChatMessage[] = [];\n const userSystemPrompt = this._config.resolveSystemPrompt();\n if (userSystemPrompt) systemMessages.push({ role: 'system', content: userSystemPrompt });\n\n const config = this._config.getModelConfig();\n const providerId = config.defaultProvider;\n if (!providerId) return;\n const provider = this._config.getAIProvider(providerId);\n if (!provider) return;\n\n const authConfig = await this._resolveAuth(providerId);\n\n const newMessageId = crypto.randomUUID();\n targetElement.appendMessage?.({\n id: newMessageId,\n role: 'assistant',\n content: '',\n status: 'pending',\n timestamp: Date.now()\n });\n\n await this._streamTurn(\n targetElement, newMessageId, provider,\n [...systemMessages, ...chatMessages], config.defaultModel || '',\n authConfig,\n );\n }\n\n /**\n * Resolve a target element by id (from event detail.targetId) or via targetResolver / DOM scan.\n */\n private _resolveTarget<T extends HTMLElement>(targetId?: string): T | null {\n // An explicit id / resolver is TRUSTED as given (it may gain its render\n // methods later); only the implicit DOM scan must prefer a candidate that\n // can actually render — the <aparte-chat> shell matches the selector first\n // but delegates rendering to its viewport (see _asRenderTarget), so a blind\n // candidates[0] returned an unusable shell and retry/edit silently no-op'd.\n if (targetId) {\n const el = document.getElementById(targetId) as HTMLElement | null;\n if (el) return this._asRenderTarget<T>(el) ?? (el as unknown as T);\n }\n if (this.options.targetResolver) {\n const el = this.options.targetResolver() as HTMLElement | null;\n if (el) return this._asRenderTarget<T>(el) ?? (el as unknown as T);\n }\n const candidates = document.querySelectorAll<HTMLElement>('aparte-chat, aparte-chat-viewport, [data-aparte-chat]');\n for (const candidate of candidates) {\n const target = this._asRenderTarget<T>(candidate);\n if (target) return target;\n }\n return (candidates[0] as unknown as T | undefined) ?? null;\n }\n\n /**\n * Resolve an element to a usable render target: itself when it exposes\n * `appendMessage`, else the viewport it delegates to. The `<aparte-chat>`\n * shell matches the host selectors/id but owns no `appendMessage` (it forwards\n * rendering to its `.viewport`), so returning the bare shell would make\n * send / retry / edit silently no-op. Returns null when neither can render.\n */\n private _asRenderTarget<T extends HTMLElement>(el: HTMLElement | null | undefined): T | null {\n if (!el) return null;\n if (typeof (el as { appendMessage?: unknown }).appendMessage === 'function') return el as unknown as T;\n const viewport = (el as { viewport?: HTMLElement | null }).viewport;\n if (viewport && typeof (viewport as { appendMessage?: unknown }).appendMessage === 'function') {\n return viewport as unknown as T;\n }\n return null;\n }\n\n /**\n * Convert AparteMessage[] to AparteChatMessage[] for re-submission.\n */\n private _messagesToChatMessages(messages: AparteMessage[]): import('../types/chat.js').AparteChatMessage[] {\n // Use _extractText (not m.content): assistant replies stream their text\n // into `segments`, leaving `content` as ''. Without flattening, retry/edit\n // would send empty assistant turns and the model answers the wrong question.\n return messages\n .filter(m => m.role === 'user' || m.role === 'assistant')\n .map(m => ({\n role: m.role as 'user' | 'assistant',\n content: this._extractText(m)\n }));\n }\n\n private async _handleSend(event: CustomEvent): Promise<void> {\n const { content, modelId, providerId: explicitProviderId } = event.detail;\n\n // 1. Use targetId from event detail — the most reliable path.\n // The composer sets detail.targetId = host.id (set by AparteChatComponent).\n // document.getElementById works even when the composer is temporarily detached.\n let targetElement: AparteChatTargetElement | null = null;\n const targetId = (event.detail as { targetId?: string })?.targetId as string | undefined;\n if (targetId) {\n const byId = document.getElementById(targetId) as AparteChatTargetElement | null;\n if (byId && typeof byId.appendMessage === 'function') {\n targetElement = byId;\n } else {\n console.warn('[AparteClient] ⚠️ targetId present but element not found or missing appendMessage:', targetId);\n }\n }\n\n // 2. User-supplied resolver (e.g. provided via APARTE_CLIENT_OPTIONS)\n if (!targetElement && this.options.targetResolver) {\n const resolved = this.options.targetResolver() as AparteChatTargetElement | null;\n if (resolved && typeof resolved.appendMessage === 'function') {\n targetElement = resolved;\n }\n }\n\n // 3. Walk up the event bubble chain as last resort\n if (!targetElement) {\n let walker: AparteChatTargetElement | null = event.target as AparteChatTargetElement | null;\n while (walker && typeof walker.appendMessage !== 'function') {\n walker = walker.parentElement as AparteChatTargetElement | null;\n }\n if (walker) {\n targetElement = walker;\n }\n }\n\n // 4. Same-page DOM scan. The composer/input and the viewport are usually\n // SIBLINGS (the viewport fills the scroll area, the composer docks\n // below it) — exactly the documented flat quick-start layout — so the\n // parentElement walk above never reaches the viewport. Mirror the\n // retry/edit resolver's scan so a bare `<aparte-chat-viewport>` works\n // out of the box without a targetResolver.\n if (!targetElement) {\n // Prefer the first candidate that can actually render. The <aparte-chat>\n // shell matches the selector first but delegates to its viewport (no\n // appendMessage of its own), so a blind querySelector returned an\n // unusable shell and the send silently no-op'd. _asRenderTarget skips\n // to the shell's viewport (or the bare viewport) — see also _resolveTarget.\n const candidates = document.querySelectorAll<HTMLElement>(\n 'aparte-chat, aparte-chat-viewport, [data-aparte-chat]',\n );\n for (const candidate of candidates) {\n const resolved = this._asRenderTarget<AparteChatTargetElement>(candidate);\n if (resolved) {\n targetElement = resolved;\n break;\n }\n }\n }\n\n if (!targetElement) {\n console.warn('[AparteClient] ⚠️ No target element found with appendMessage support. Provide a targetResolver in AparteClientOptions or ensure the composer has a `target` attribute.');\n return;\n }\n\n const messageId = crypto.randomUUID();\n const config = this._config.getModelConfig();\n const providerId = explicitProviderId || config.defaultProvider;\n\n // 1. Initial Checks (Sync Errors)\n if (!providerId) {\n this._handleLifecycleError(targetElement, messageId, new AparteError(\n 'No provider selected. Please configure a provider.',\n AparteErrorCode.CONFIG_NO_PROVIDER\n ));\n return;\n }\n\n const provider = this._config.getAIProvider(providerId);\n if (!provider) {\n this._handleLifecycleError(targetElement, messageId, new AparteError(\n `Provider '${providerId}' is not registered.`,\n AparteErrorCode.CONFIG_MISSING_KEY,\n { providerId }\n ));\n return;\n }\n\n // 2. Prepare Atomic Assistant Message\n targetElement.appendMessage?.({\n id: messageId,\n role: 'assistant',\n content: '',\n status: 'pending',\n timestamp: Date.now()\n });\n\n // Resolve auth + build the request (key channel + file injection are\n // send-specific). A failure here routes to the lifecycle error exactly\n // like a stream failure inside _streamTurn.\n let authConfig: string | Record<string, string> | undefined;\n let messages: AparteChatMessage[];\n try {\n authConfig = await this._resolveAuth(providerId);\n\n const rawFiles: File[] = Array.isArray(event.detail?.files) ? event.detail.files : [];\n // rawFileInject controls what reaches the LLM as raw content:\n // 'none' → nothing inline. RAG handles all file types (incl. images).\n // 'images-only' → images inline, text/docs to RAG via requestInterceptor.\n // 'all' (default) → images + text files inline. Default for cloud SaaS providers.\n const filesToInject =\n this.options.rawFileInject === 'none' ? [] :\n this.options.rawFileInject === 'images-only' ? rawFiles.filter(f => f.type.startsWith('image/')) :\n rawFiles;\n const contentParts = filesToInject.length > 0 ? await this._filesToContentParts(filesToInject) : [];\n messages = this._buildMessages(content, targetElement, contentParts.length > 0 ? contentParts : undefined);\n } catch (error: unknown) {\n this._handleLifecycleError(targetElement, messageId, AparteError.from(error, AparteErrorCode.UNKNOWN_ERROR));\n return;\n }\n\n await this._streamTurn(\n targetElement, messageId, provider,\n messages, modelId || config.defaultModel || '',\n authConfig,\n );\n }\n\n /**\n * Build the initial messages array, prepending system prompts and conversation history.\n */\n private _buildMessages(userContent: string, target?: AparteChatTargetElement, parts?: AparteContentPart[]): AparteChatMessage[] {\n const messages: AparteChatMessage[] = [];\n\n // 1. User-defined system prompt (with resolved variables)\n const userSystemPrompt = this._config.resolveSystemPrompt();\n if (userSystemPrompt) {\n messages.push({ role: 'system', content: userSystemPrompt });\n }\n\n const historyOption = this.options.history ?? 'viewport';\n const viewportMessages: AparteMessage[] = target?.getMessages?.() ?? [];\n\n if (historyOption === 'viewport') {\n messages.push(...this._toHistoryMessages(viewportMessages));\n } else if (typeof historyOption === 'function') {\n messages.push(...historyOption(viewportMessages));\n }\n\n const userMsg: AparteChatMessage['content'] = (parts && parts.length > 0)\n ? [{ type: 'text' as const, text: userContent }, ...parts]\n : userContent;\n messages.push({ role: 'user', content: userMsg });\n return messages;\n }\n\n private _toHistoryMessages(messages: AparteMessage[]): AparteChatMessage[] {\n // Exclude trailing unanswered user messages: the current user message is\n // already added explicitly at the end of _buildMessages, so including it\n // from the viewport would cause a duplicate.\n // Find the last completed assistant response and cut there.\n let cutoff = 0;\n for (let i = 0; i < messages.length; i++) {\n const m = messages[i]!;\n if (m.role === 'assistant' && m.status === 'completed') {\n cutoff = i + 1;\n }\n }\n\n return messages\n .slice(0, cutoff)\n .filter(m => {\n if (m.role === 'user') return m.status !== 'error';\n if (m.role === 'assistant') return m.status === 'completed';\n return false;\n })\n .map(m => ({\n role: m.role as 'user' | 'assistant',\n content: this._extractText(m)\n }))\n .filter(m => m.content.length > 0);\n }\n\n private _extractText(message: AparteMessage): string {\n if (typeof message.content === 'string' && message.content) return message.content;\n if (!message.segments) return '';\n return message.segments\n .filter(s => s.type === 'text' || s.type === 'code')\n .map(s => (s as { content?: string }).content ?? '')\n .join('\\n')\n .trim();\n }\n\n /**\n * Convert an array of File objects to AparteContentPart[].\n * - Images → AparteImagePart (base64 data URL)\n * - Text files (txt, md, json, csv, xml, html, css, js, ts, …) → AparteTextPart\n * injected as a fenced block so all models (including local) can read them.\n * - Other binary files → silently ignored.\n */\n private async _filesToContentParts(files: File[]): Promise<AparteContentPart[]> {\n const TEXT_TYPES = /^(text\\/|application\\/(json|xml|javascript|typescript|x-yaml|yaml|toml|csv|markdown))/i;\n const TEXT_EXTENSIONS = /\\.(txt|md|markdown|json|jsonl|csv|tsv|xml|html|htm|css|js|mjs|cjs|ts|tsx|jsx|py|rb|java|c|cpp|h|go|rs|php|sh|bash|zsh|fish|yaml|yml|toml|ini|env|log|svg|graphql|gql)$/i;\n\n const parts: (AparteContentPart | null)[] = await Promise.all(\n files.map((file): Promise<AparteContentPart | null> => {\n if (file.type.startsWith('image/')) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve({ type: 'image', image: reader.result as string, mimeType: file.type });\n reader.onerror = () => reject(new Error(`Failed to read file: ${file.name}`));\n reader.readAsDataURL(file);\n });\n }\n\n if (TEXT_TYPES.test(file.type) || TEXT_EXTENSIONS.test(file.name)) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const content = reader.result as string;\n const ext = file.name.split('.').pop() ?? '';\n resolve({\n type: 'text',\n text: `\\`\\`\\`${ext}\\n// File: ${file.name}\\n${content}\\n\\`\\`\\``,\n });\n };\n reader.onerror = () => reject(new Error(`Failed to read file: ${file.name}`));\n reader.readAsText(file);\n });\n }\n\n return Promise.resolve(null);\n })\n );\n\n return parts.filter((p): p is AparteContentPart => p !== null);\n }\n\n /**\n * Stream loop: runs one provider.chat() call and repeats if a tool was called.\n * Maintains a running messages array to inject tool_call / tool_result turns.\n */\n /**\n * Feed one text delta to the Claude-style `<artifact>` XML streamer. Scans for\n * `<artifact …>` / `</artifact>`, routing chat text through the text parser and\n * artifact content into a dedicated artifact segment (handling tags split across\n * deltas). Mutates `xml` in place. Extracted from _streamLoop.\n */\n private _feedXmlArtifactDelta(\n delta: string,\n xml: XmlArtifactStreamState,\n ctx: {\n targetElement: AparteChatTargetElement;\n messageId: string;\n textParser: AparteStreamParser;\n streamingSegmentIds: Set<string>;\n artifactProgress: Map<string, number>;\n artifactXmlHint: AparteArtifactHint;\n },\n ): void {\n const { targetElement, messageId, textParser, streamingSegmentIds, artifactProgress, artifactXmlHint } = ctx;\n let remaining = delta;\n\n while (remaining.length > 0) {\n if (xml.state === 'normal') {\n const tagStart = remaining.indexOf('<artifact');\n if (tagStart === -1) {\n // Pure chat text — route through normal text parser\n const r = textParser.parse(remaining);\n for (const seg of r.segments) {\n if (!streamingSegmentIds.has(seg.id)) {\n targetElement.addSegment?.(seg);\n streamingSegmentIds.add(seg.id);\n } else if ('content' in seg) {\n targetElement.updateSegment?.(seg.id, { content: (seg as { content?: string }).content });\n }\n }\n const active = textParser.getState().activeSegment;\n if (active) {\n if (!streamingSegmentIds.has(active.id)) {\n targetElement.addSegment?.(active);\n streamingSegmentIds.add(active.id);\n } else {\n targetElement.updateSegment?.(active.id, { content: (active as { content?: string }).content });\n }\n } else if (!r.segments.length) {\n if (targetElement.typeName) targetElement.typeName(remaining);\n else targetElement.updateLastMessage?.(remaining, { append: true });\n }\n remaining = '';\n } else {\n // Emit chat text before the opening tag\n const before = remaining.slice(0, tagStart);\n if (before) {\n const r = textParser.parse(before);\n for (const seg of r.segments) {\n if (!streamingSegmentIds.has(seg.id)) {\n targetElement.addSegment?.(seg);\n streamingSegmentIds.add(seg.id);\n }\n }\n if (!r.segments.length && !textParser.getState().activeSegment) {\n if (targetElement.typeName) targetElement.typeName(before);\n else targetElement.updateLastMessage?.(before, { append: true });\n }\n }\n xml.scanBuf = remaining.slice(tagStart);\n remaining = '';\n xml.state = 'scanning';\n }\n } else if (xml.state === 'scanning') {\n // Accumulate until we have the full opening tag (ends with >)\n xml.scanBuf += remaining;\n remaining = '';\n const gtIdx = xml.scanBuf.indexOf('>');\n if (gtIdx !== -1) {\n const tag = xml.scanBuf.slice(0, gtIdx + 1);\n // Parse mimeType and title attributes (single or double quotes)\n const mimeMatch = /mimeType=['\"]([^'\"]+)['\"]/.exec(tag);\n const titleMatch = /title=['\"]([^'\"]+)['\"]/.exec(tag);\n xml.mime = mimeMatch?.[1] ?? artifactXmlHint.mimeType;\n xml.title = titleMatch?.[1] ?? artifactXmlHint.kind;\n xml.kind = deriveArtifactKind(xml.mime, artifactXmlHint.kind);\n xml.segId = `artifact-xml-${crypto.randomUUID()}`;\n xml.content = '';\n const openSeg: import('../types/segments.js').AparteArtifactSegment = {\n id: xml.segId, type: 'artifact',\n mimeType: xml.mime, artifactType: xml.kind,\n title: xml.title, content: '',\n };\n targetElement.addSegment?.(openSeg);\n streamingSegmentIds.add(xml.segId);\n this._dispatchArtifactLifecycle(targetElement, messageId, openSeg, artifactProgress, false);\n xml.state = 'in-artifact';\n remaining = xml.scanBuf.slice(gtIdx + 1);\n xml.scanBuf = '';\n }\n } else { // in-artifact\n const CLOSE = '</artifact>';\n const combined = xml.closeBuf + remaining;\n const closeIdx = combined.indexOf(CLOSE);\n if (closeIdx !== -1) {\n // Closing tag found — finalize the artifact\n xml.content += combined.slice(0, closeIdx);\n const lineCount = xml.content.split('\\n').length;\n const isInline = lineCount < 15;\n const finalSeg: import('../types/segments.js').AparteArtifactSegment = {\n id: xml.segId!, type: 'artifact',\n mimeType: xml.mime, artifactType: xml.kind,\n title: xml.title, content: xml.content,\n inline: isInline,\n };\n targetElement.updateSegment?.(xml.segId!, { content: xml.content, inline: isInline } as Partial<import('../types/segments.js').AparteArtifactSegment>);\n this._dispatchArtifactLifecycle(targetElement, messageId, finalSeg, artifactProgress, true);\n xml.state = 'normal';\n xml.closeBuf = '';\n remaining = combined.slice(closeIdx + CLOSE.length);\n } else {\n // Buffer a tail chunk to handle closing tag split across deltas\n const safeLen = Math.max(0, combined.length - CLOSE.length + 1);\n const safe = combined.slice(0, safeLen);\n xml.content += safe;\n xml.closeBuf = combined.slice(safeLen);\n remaining = '';\n if (xml.segId) {\n targetElement.updateSegment?.(xml.segId, { content: xml.content });\n this._dispatchArtifactLifecycle(targetElement, messageId, {\n id: xml.segId, type: 'artifact',\n mimeType: xml.mime, artifactType: xml.kind,\n title: xml.title, content: xml.content,\n } as import('../types/segments.js').AparteArtifactSegment, artifactProgress, false);\n }\n }\n }\n }\n }\n\n /**\n * Turn-1 forced tool call. When `toolChoice = { name, input }`\n * (orchestrator-driven), execute the handler directly instead of consulting\n * the LLM, render the tool segment, inject the result as `tool_result`, and\n * strip `toolChoice` for the follow-up turn. Returns the (possibly-updated)\n * request and whether the loop should skip to the next turn (handler missing\n * or aborted). Extracted from `_streamLoop`. `messages` is mutated in place.\n */\n private async _maybeRunSyntheticTool(\n baseRequest: AparteChatRequest,\n turns: number,\n messages: AparteChatMessage[],\n targetElement: AparteChatTargetElement,\n ): Promise<{ baseRequest: AparteChatRequest; skip: boolean }> {\n const toolChoice = baseRequest.toolChoice;\n if (!(turns === 1 && toolChoice && typeof toolChoice === 'object' && toolChoice.input !== undefined)) {\n return { baseRequest, skip: false };\n }\n\n const syntheticId = crypto.randomUUID();\n const syntheticCall: AparteToolCall = { id: syntheticId, name: toolChoice.name, input: toolChoice.input };\n\n // Render the tool segment so the UI shows the tool was called.\n const toolSeg: AparteToolCallSegment = {\n id: `tool-${syntheticId}`,\n type: 'tool_call',\n toolCall: syntheticCall,\n status: 'pending',\n };\n const toolRenderer = this._config.getToolRenderer(toolChoice.name);\n if (toolRenderer) {\n const html = toolRenderer.render(toolSeg);\n if (html) targetElement.addSegment?.(toolSeg);\n } else {\n targetElement.addSegment?.(toolSeg);\n }\n\n const handler = this._config.getToolHandler(toolChoice.name);\n if (!handler) {\n console.warn(`[AparteClient] No handler for synthetic tool \"${toolChoice.name}\"`);\n targetElement.updateSegment?.(toolSeg.id, { status: 'aborted' });\n return { baseRequest, skip: true };\n }\n\n const controller = new AbortController();\n this._activeToolControllers.add(controller);\n const timeout = setTimeout(() => controller.abort(), TOOL_HANDLER_TIMEOUT_MS);\n try {\n const result = await handler(syntheticCall, controller.signal);\n targetElement.updateSegment?.(toolSeg.id, { status: 'resolved', result: result.content });\n messages.push({ role: 'tool_call', content: '', toolCalls: [syntheticCall] });\n messages.push({ role: 'tool_result', content: result.content, toolCallId: syntheticId });\n // Strip toolChoice + tools from the follow-up LLM call — it should just answer.\n return { baseRequest: { ...baseRequest, toolChoice: 'none', tools: undefined }, skip: false };\n } catch (err: unknown) {\n if ((err as { name?: string })?.name === 'AbortError') {\n targetElement.updateSegment?.(toolSeg.id, { status: 'aborted' });\n return { baseRequest, skip: true };\n }\n throw err;\n } finally {\n clearTimeout(timeout);\n this._activeToolControllers.delete(controller);\n }\n }\n\n /**\n * Handle one `tool_use` stream event from {@link _streamLoop}: the built-in\n * `create_artifact`, per-tool renderer selection, the human-in-the-loop\n * approval gate, and running the registered handler (timeout / abort).\n * Mutates the shared `messages` / `toolCallsThisTurn` history in place and\n * returns whether the agentic loop should keep going.\n */\n private async _handleToolUseEvent(\n event: { id: string; name: string; input: Record<string, unknown> },\n ctx: {\n targetElement: AparteChatTargetElement;\n messageId: string;\n messages: AparteChatMessage[];\n toolCallsThisTurn: AparteToolCall[];\n precedingText: string;\n artifactProgress: Map<string, number>;\n turns: number;\n globalMaxTurns: number;\n },\n ): Promise<{ continueLoop: boolean }> {\n const { targetElement, messageId, messages, toolCallsThisTurn, precedingText, artifactProgress, turns, globalMaxTurns } = ctx;\n let continueLoop = true;\n\n toolCallsThisTurn.push({ id: event.id, name: event.name, input: event.input });\n\n // ── Built-in: create_artifact ─────────────────────────────────\n // When the LLM calls create_artifact, bypass the generic handler:\n // create an AparteArtifactSegment directly (isolated from chat text),\n // dispatch artifact lifecycle events, and inject a success tool_result\n // so the LLM can continue with a conversational reply.\n if (event.name === 'create_artifact') {\n const input = event.input as {\n mimeType?: string;\n title?: string;\n content?: string;\n };\n const mimeType = input.mimeType ?? 'text/plain';\n const kind = mimeType.includes('react') ? 'react'\n : mimeType.includes('html') ? 'html'\n : mimeType.includes('javascript') ? 'js'\n : mimeType.includes('css') ? 'css'\n : mimeType.includes('svg') ? 'svg'\n : mimeType.includes('json') ? 'json'\n : mimeType.includes('csv') ? 'csv'\n : mimeType.includes('markdown') ? 'markdown'\n : 'text';\n const artifactSeg: import('../types/segments.js').AparteArtifactSegment = {\n id: `artifact-${event.id}`,\n type: 'artifact',\n mimeType,\n artifactType: kind,\n title: input.title ?? kind,\n content: input.content ?? '',\n };\n targetElement.addSegment?.(artifactSeg);\n this._dispatchArtifactLifecycle(targetElement, messageId, artifactSeg, artifactProgress, true);\n\n messages.push({\n role: 'tool_call',\n content: '',\n toolCalls: [{ id: event.id, name: event.name, input: event.input }],\n });\n messages.push({\n role: 'tool_result',\n content: 'Artifact created successfully.',\n toolCallId: event.id,\n });\n return { continueLoop: true };\n }\n // ── End built-in create_artifact ──────────────────────────────\n\n const toolSeg: AparteToolCallSegment = {\n id: `tool-${event.id}`,\n type: 'tool_call',\n toolCall: { id: event.id, name: event.name, input: event.input },\n status: 'pending'\n };\n\n // Check for a per-tool renderer override\n const toolRenderer = this._config.getToolRenderer(event.name);\n if (toolRenderer) {\n // Inject per-tool styles once\n if (toolRenderer.getStyles) {\n const styles = toolRenderer.getStyles();\n if (styles) {\n const styleId = `aparte-tool-renderer-${event.name}`;\n if (!document.getElementById(styleId)) {\n const el = document.createElement('style');\n el.id = styleId;\n el.textContent = styles;\n document.head.appendChild(el);\n }\n }\n }\n const html = toolRenderer.render(toolSeg);\n // Only add segment if the renderer produces visible output\n if (html) {\n targetElement.addSegment?.(toolSeg);\n }\n // No DOM setup here — segment bubble handles it via its own renderer\n } else {\n // Fallback: generic tool_call segment renderer (pill + spinner)\n targetElement.addSegment?.(toolSeg);\n }\n\n // Check per-tool maxTurns override\n const toolDef = this._config.getTools().find(t => t.name === event.name);\n const effectiveMaxTurns = toolDef?.maxTurns ?? globalMaxTurns;\n if (turns >= effectiveMaxTurns) {\n console.warn(`[AparteClient] Tool \"${event.name}\" maxTurns (${effectiveMaxTurns}) reached.`);\n targetElement.updateSegment?.(toolSeg.id, { status: 'aborted' });\n return { continueLoop: false };\n }\n\n // Find and run the registered handler\n const handler = this._config.getToolHandler(event.name);\n if (handler) {\n // The input the handler runs with. A human approval step may\n // override it via the decision payload (see below); with no\n // approval it is exactly what the model requested.\n let effectiveInput = event.input;\n // Human-in-the-loop: pause for approval before running, if required.\n if (toolDef?.needsApproval) {\n const approvalController = new AbortController();\n this._activeToolControllers.add(approvalController);\n targetElement.updateSegment?.(toolSeg.id, { status: 'awaiting-approval' });\n targetElement.dispatchEvent?.(new CustomEvent('aparte-tool-approval-request', {\n bubbles: true, composed: true,\n detail: { toolCallId: event.id, toolName: event.name, input: event.input }\n }));\n let decision: { approved: boolean; payload?: unknown };\n const resolveApproval = this.options.approvalResolver\n ?? ((id: string, sig: AbortSignal) => this._awaitToolDecision(id, sig));\n try {\n decision = await resolveApproval(event.id, approvalController.signal);\n } finally {\n this._activeToolControllers.delete(approvalController);\n }\n if (!decision.approved) {\n const rejection = 'Tool execution was rejected by the user.';\n targetElement.updateSegment?.(toolSeg.id, { status: 'rejected', result: rejection });\n const existingToolCallMsg = messages.find(\n m => m.role === 'tool_call' && m.toolCalls?.some(tc => tc.id === event.id)\n );\n if (!existingToolCallMsg) {\n messages.push({\n role: 'tool_call',\n content: '',\n toolCalls: toolCallsThisTurn,\n precedingText: precedingText.trim() || undefined\n });\n }\n messages.push({ role: 'tool_result', content: rejection, toolCallId: event.id });\n return { continueLoop: false };\n }\n // Approved → optionally let the human's payload edit the\n // arguments, then restore pending and run the handler.\n if (decision.payload && typeof decision.payload === 'object' && !Array.isArray(decision.payload)) {\n effectiveInput = { ...event.input, ...(decision.payload as Record<string, unknown>) };\n }\n targetElement.updateSegment?.(toolSeg.id, { status: 'pending' });\n }\n\n const controller = new AbortController();\n this._activeToolControllers.add(controller);\n const timeout = setTimeout(() => controller.abort(), TOOL_HANDLER_TIMEOUT_MS);\n\n try {\n const result = await handler(\n { id: event.id, name: event.name, input: effectiveInput },\n controller.signal\n );\n targetElement.updateSegment?.(toolSeg.id, { status: 'resolved', result: result.content });\n\n // Inject tool_call + tool_result into message history for re-call\n const existingToolCallMsg = messages.find(\n m => m.role === 'tool_call' && m.toolCalls?.some(tc => tc.id === event.id)\n );\n if (!existingToolCallMsg) {\n messages.push({\n role: 'tool_call',\n content: '',\n toolCalls: toolCallsThisTurn,\n precedingText: precedingText.trim() || undefined\n });\n }\n messages.push({\n role: 'tool_result',\n content: result.content,\n toolCallId: event.id\n });\n } catch (err: unknown) {\n if ((err as { name?: string })?.name === 'AbortError') {\n targetElement.updateSegment?.(toolSeg.id, { status: 'aborted' });\n continueLoop = false;\n } else {\n throw err;\n }\n } finally {\n clearTimeout(timeout);\n this._activeToolControllers.delete(controller);\n }\n } else {\n console.warn(`[AparteClient] No handler registered for tool \"${event.name}\"`);\n targetElement.updateSegment?.(toolSeg.id, { status: 'aborted' });\n continueLoop = false;\n }\n return { continueLoop };\n }\n\n private async _streamLoop(\n targetElement: AparteChatTargetElement,\n messageId: string,\n provider: AparteAIProvider,\n baseRequest: AparteChatRequest,\n authConfig: string | Record<string, string> | undefined\n ): Promise<AparteUsage | undefined> {\n // Fetch-level abort: aborting this controller (via `abort()`) cuts the\n // in-flight vendor request, so a user \"stop\" halts server-side generation\n // rather than only stopping client-side reading of the stream.\n const streamController = new AbortController();\n this._streamController = streamController;\n\n // ── Injected stream runner ───────────────────────────────────────────\n // When a headless runner is injected (e.g. @aparte/engine's\n // runStreamAgent), delegate the loop to it and render via the core\n // adapter. Absent → the inline loop below runs (core standalone,\n // zero-dep). Both paths produce the same targetElement calls (proven by\n // the engine `stream-parity` suite).\n if (this.options.streamRunner) {\n return this._runViaStreamRunner(this.options.streamRunner, targetElement, messageId, provider, baseRequest, authConfig, streamController);\n }\n\n const messages: AparteChatMessage[] = [...baseRequest.messages];\n let continueLoop = true;\n let turns = 0;\n const globalMaxTurns = this.options.maxTurns ?? 10;\n let lastUsage: AparteUsage | undefined;\n\n // ── Pipeline mode ─────────────────────────────────────────────────\n // _meta.pipeline runs each phase as one LLM turn: the system message +\n // artifact hint are injected per phase, and reply N is context for N+1.\n // (Typed via AparteRequestMeta — no local shape / cast needed.)\n const pipeline = baseRequest._meta?.pipeline;\n let pipelineIndex = 0;\n\n this._updateMessage(targetElement, messageId, { status: 'streaming' });\n\n // Inject prefix segments (e.g. an orchestrator thinking block) before streaming.\n for (const seg of baseRequest._meta?.prefixSegments ?? []) {\n targetElement.addSegment?.(seg);\n }\n\n while (continueLoop) {\n if (this._isAborted) {\n this._dispatchLifecycleEvent(targetElement, 'aparte-message-aborted', { messageId });\n break;\n }\n\n turns++;\n if (turns > globalMaxTurns) {\n console.warn(`[AparteClient] maxTurns (${globalMaxTurns}) exceeded — stopping loop.`);\n targetElement.addSegment?.({\n id: `max-turns-${crypto.randomUUID()}`,\n type: 'error',\n content: `Stopped after ${globalMaxTurns} tool calls to prevent an infinite loop.`,\n details: 'MAX_TURNS_EXCEEDED'\n });\n break;\n }\n\n // Turn-1 forced tool call (orchestrator-driven toolChoice) — runs the\n // handler directly instead of the LLM. `skip` = this turn is done\n // (handler missing / aborted); otherwise fall through with the request\n // stripped of toolChoice for the follow-up. See _maybeRunSyntheticTool.\n const synthetic = await this._maybeRunSyntheticTool(baseRequest, turns, messages, targetElement);\n baseRequest = synthetic.baseRequest;\n if (synthetic.skip) { continueLoop = false; continue; }\n\n // ── Build per-phase request when pipeline is active ───────────────\n let phaseMessages: AparteChatMessage[] = messages;\n let phaseMeta: AparteRequestMeta | undefined = baseRequest._meta;\n if (pipeline && pipelineIndex < pipeline.length) {\n const phase = pipeline[pipelineIndex]!;\n phaseMessages = [{ role: 'system', content: phase.system } as AparteChatMessage, ...messages];\n if (phase.mode === 'artifact') {\n phaseMeta = { ...phaseMeta, artifactRaw: { mimeType: phase.mimeType, kind: phase.kind } };\n } else {\n // Ensure no stale artifactRaw leaks into a text phase\n const { artifactRaw: _dropped, pipeline: _p, ...restMeta } = (phaseMeta ?? {}) as AparteRequestMeta;\n phaseMeta = restMeta;\n }\n }\n const request: AparteChatRequest = { ...baseRequest, messages: phaseMessages, _meta: phaseMeta };\n const response = await this._config.getTransport().chat(provider, request, authConfig, { providerId: provider.id, signal: streamController.signal });\n\n if (typeof response === 'string') {\n this._updateMessage(targetElement, messageId, { content: response, status: 'completed' });\n return undefined;\n }\n\n // Streaming mode\n const reader = (response as ReadableStream<AparteStreamEvent>).getReader();\n const textParser = new AparteStreamParser();\n const streamingSegmentIds = new Set<string>();\n /**\n * Lifecycle bookkeeping for artifact segments. Maps a segment id to the\n * length of content already broadcast via `aparte-artifact-delta`. Used to\n * compute incremental chunks without forcing the parser to expose deltas.\n */\n const artifactProgress = new Map<string, number>();\n let thinkingSegmentId: string | null = null;\n let thinkingContent = '';\n let thinkingCollapsed = false;\n // Extract artifact hint once — used in both streaming and finalize promotion\n const artifactHint = baseRequest._meta?.artifactHint;\n let artifactPromoted = false; // promote only the first code segment\n\n // ── artifactRaw mode (turn 2 of multi-turn) ──────────────────────\n // Entire stream is raw code → routed directly into an artifact segment.\n const artifactRawHint = request._meta?.artifactRaw;\n let rawSegId: string | null = null;\n let rawContent = '';\n\n if (artifactRawHint) {\n // Create the artifact segment immediately (pill during streaming)\n rawSegId = `artifact-raw-${crypto.randomUUID()}`;\n const rawSeg: import('../types/segments.js').AparteArtifactSegment = {\n id: rawSegId, type: 'artifact',\n mimeType: artifactRawHint.mimeType,\n artifactType: artifactRawHint.kind,\n title: artifactRawHint.kind,\n content: '',\n };\n targetElement.addSegment?.(rawSeg);\n streamingSegmentIds.add(rawSegId);\n this._dispatchArtifactLifecycle(targetElement, messageId, rawSeg, artifactProgress, false);\n }\n // ── END artifactRaw ──────────────────────────────────────────────\n\n // ── XML artifact streaming state (Claude-like) — fed to _feedXmlArtifactDelta ──\n const artifactXmlHint = baseRequest._meta?.artifactXml;\n const xmlCtx: XmlArtifactStreamState = {\n state: 'normal', scanBuf: '', closeBuf: '', segId: null, content: '', mime: '', kind: '', title: '',\n };\n\n // Accumulated text before a tool call in this turn\n let precedingText = '';\n // Tool calls emitted during this turn\n const toolCallsThisTurn: AparteToolCall[] = [];\n\n try {\n while (true) {\n // Honor abort INSIDE the SSE event loop too (not only between\n // tool-call turns). Without this, late events buffered after\n // a `aparte-abort` (e.g. after the user switches conversation\n // mid-stream) keep mutating the target's last message — which\n // may now belong to a different conversation, causing the\n // user message in the new conv to be overwritten by the\n // assistant reply from the old one.\n if (this._isAborted) {\n try { void reader.cancel(); } catch { /* best effort */ }\n this._dispatchLifecycleEvent(targetElement, 'aparte-message-aborted', { messageId });\n continueLoop = false;\n break;\n }\n const { done, value: event } = await reader.read();\n if (done) break;\n\n switch (event.type) {\n case 'thinking': {\n thinkingContent += event.delta;\n if (!thinkingSegmentId) {\n const seg: AparteThinkingSegment = {\n id: `think-${crypto.randomUUID()}`,\n type: 'thinking',\n content: thinkingContent,\n collapsed: true,\n label: 'Thinking'\n };\n thinkingSegmentId = seg.id;\n streamingSegmentIds.add(seg.id);\n targetElement.addSegment?.(seg);\n } else {\n targetElement.updateSegment?.(thinkingSegmentId, { content: thinkingContent });\n }\n break;\n }\n case 'text': {\n // Collapse thinking block when the response text starts\n if (thinkingSegmentId && !thinkingCollapsed) {\n targetElement.updateSegment?.(thinkingSegmentId, { collapsed: true });\n thinkingCollapsed = true;\n }\n precedingText += event.delta;\n\n // ── artifactRaw: whole stream → artifact segment ──────────────\n if (artifactRawHint && rawSegId) {\n rawContent += event.delta;\n targetElement.updateSegment?.(rawSegId, { content: rawContent });\n this._dispatchArtifactLifecycle(targetElement, messageId, {\n id: rawSegId, type: 'artifact',\n mimeType: artifactRawHint.mimeType,\n artifactType: artifactRawHint.kind,\n title: artifactRawHint.kind,\n content: rawContent,\n } as import('../types/segments.js').AparteArtifactSegment, artifactProgress, false);\n break;\n }\n\n // XML artifact streaming (Claude-like) — extracted to _feedXmlArtifactDelta.\n if (artifactXmlHint) {\n this._feedXmlArtifactDelta(event.delta, xmlCtx, {\n targetElement, messageId, textParser, streamingSegmentIds, artifactProgress, artifactXmlHint,\n });\n break;\n }\n const result = textParser.parse(event.delta);\n for (let segment of result.segments) {\n // Artifact hint promotion: promote first code fence → artifact\n if (artifactHint && !artifactPromoted && segment.type === 'code') {\n const codeSeg = segment as import('../types/segments.js').AparteCodeSegment;\n const promoted: import('../types/segments.js').AparteArtifactSegment = {\n id: codeSeg.id,\n type: 'artifact',\n mimeType: artifactHint.mimeType,\n artifactType: artifactHint.kind,\n title: codeSeg.filename ?? artifactHint.kind,\n content: codeSeg.content,\n };\n segment = promoted;\n artifactPromoted = true;\n }\n if (!streamingSegmentIds.has(segment.id)) {\n targetElement.addSegment?.(segment);\n streamingSegmentIds.add(segment.id);\n } else if ('content' in segment) {\n // Segment was already streaming — sync the final content\n targetElement.updateSegment?.(segment.id, { content: (segment as { content?: string }).content });\n }\n if (segment.type === 'artifact') {\n this._dispatchArtifactLifecycle(targetElement, messageId, segment, artifactProgress, true);\n }\n }\n const active = textParser.getState().activeSegment;\n if (active) {\n if (!streamingSegmentIds.has(active.id)) {\n targetElement.addSegment?.(active);\n streamingSegmentIds.add(active.id);\n if (active.type === 'artifact') {\n this._dispatchArtifactLifecycle(targetElement, messageId, active, artifactProgress, false);\n }\n } else {\n targetElement.updateSegment?.(active.id, { content: (active as { content?: string }).content });\n if (active.type === 'artifact') {\n this._dispatchArtifactLifecycle(targetElement, messageId, active, artifactProgress, false);\n }\n }\n } else if (result.segments.length === 0) {\n if (targetElement.typeName) targetElement.typeName(event.delta);\n else targetElement.updateLastMessage?.(event.delta, { append: true });\n }\n break;\n }\n case 'tool_use': {\n const toolResult = await this._handleToolUseEvent(\n { id: event.id, name: event.name, input: event.input },\n { targetElement, messageId, messages, toolCallsThisTurn, precedingText, artifactProgress, turns, globalMaxTurns },\n );\n if (!toolResult.continueLoop) continueLoop = false;\n break;\n }\n case 'error':\n throw new Error(event.message);\n case 'done':\n if (event.usage) lastUsage = event.usage;\n break;\n default:\n assertNever(event);\n }\n }\n\n // Finalize text parser\n const finals = textParser.finalize();\n\n // ── artifactRaw finalize ──────────────────────────────────────\n if (artifactRawHint && rawSegId) {\n const lineCount = rawContent.split('\\n').length;\n const isInline = lineCount < 15;\n targetElement.updateSegment?.(rawSegId, { content: rawContent, inline: isInline } as Partial<import('../types/segments.js').AparteArtifactSegment>);\n this._dispatchArtifactLifecycle(targetElement, messageId, {\n id: rawSegId, type: 'artifact',\n mimeType: artifactRawHint.mimeType, artifactType: artifactRawHint.kind,\n title: artifactRawHint.kind, content: rawContent, inline: isInline,\n } as import('../types/segments.js').AparteArtifactSegment, artifactProgress, true);\n }\n // ── END artifactRaw finalize ──────────────────────────────────\n\n // ── XML artifact finalize ─────────────────────────────────────\n // If the stream ended while still inside an <artifact> tag\n // (model truncated — common on small models with low maxTokens),\n // flush whatever was buffered and render the partial artifact.\n if (artifactXmlHint && xmlCtx.state === 'in-artifact' && xmlCtx.segId) {\n xmlCtx.content += xmlCtx.closeBuf;\n const lineCount = xmlCtx.content.split('\\n').length;\n const isInline = lineCount < 15;\n targetElement.updateSegment?.(xmlCtx.segId, { content: xmlCtx.content, inline: isInline } as Partial<import('../types/segments.js').AparteArtifactSegment>);\n this._dispatchArtifactLifecycle(targetElement, messageId, {\n id: xmlCtx.segId, type: 'artifact',\n mimeType: xmlCtx.mime, artifactType: xmlCtx.kind,\n title: xmlCtx.title, content: xmlCtx.content, inline: isInline,\n } as import('../types/segments.js').AparteArtifactSegment, artifactProgress, true);\n console.warn('[AparteClient] XML artifact finalized without closing tag — content may be partial.');\n }\n // ── END XML artifact finalize ─────────────────────────────────\n\n // ── Artifact hint promotion (finalize) ───────────────────────\n // Handles the case where the code fence was not yet finalized\n // during streaming (e.g. stream ended without closing ```).\n if (artifactHint && !artifactPromoted) {\n const codeIdx = finals.findIndex(s => s.type === 'code');\n if (codeIdx !== -1) {\n const codeSeg = finals[codeIdx] as import('../types/segments.js').AparteCodeSegment;\n const promoted: import('../types/segments.js').AparteArtifactSegment = {\n id: codeSeg.id,\n type: 'artifact',\n mimeType: artifactHint.mimeType,\n artifactType: artifactHint.kind,\n title: codeSeg.filename ?? artifactHint.kind,\n content: codeSeg.content,\n };\n finals[codeIdx] = promoted;\n artifactPromoted = true;\n // Already in DOM as code block → re-render as artifact pill\n if (streamingSegmentIds.has(promoted.id)) {\n targetElement.updateSegment?.(promoted.id, promoted);\n }\n }\n }\n // ────────────────────────────────────────────────────────────\n\n for (const s of finals) {\n if (!streamingSegmentIds.has(s.id)) {\n targetElement.addSegment?.(s);\n } else if ('content' in s) {\n // finalize() appended the residual buffer — sync to DOM\n targetElement.updateSegment?.(s.id, { content: (s as { content?: string }).content });\n }\n if (s.type === 'artifact') {\n this._dispatchArtifactLifecycle(targetElement, messageId, s, artifactProgress, true);\n }\n }\n\n // Stop looping — or advance to the next pipeline phase\n if (toolCallsThisTurn.length === 0) {\n if (pipeline && pipelineIndex < pipeline.length - 1) {\n // Inject this turn's assistant reply as context for the next phase\n if (precedingText.trim()) {\n messages.push({ role: 'assistant', content: precedingText.trim() });\n }\n pipelineIndex++;\n // Show pulsing dots while we wait for the next phase.\n // The segment removes itself automatically via MutationObserver\n // when the next segment appears — no manual cleanup needed.\n const pwId = `pw-${crypto.randomUUID()}`;\n targetElement.addSegment?.({ id: pwId, type: 'pipeline-waiting' });\n // continueLoop stays true — next iteration handles the new phase\n } else {\n continueLoop = false;\n }\n }\n\n } finally {\n reader.releaseLock();\n }\n }\n\n this._updateMessage(targetElement, messageId, { status: 'completed' });\n // Push usage onto the live bubble so the info action (stats popover)\n // is available immediately, even for consumers that don't listen for\n // `aparte-message-done`.\n if (lastUsage) {\n try {\n (targetElement as { setUsage?: (id: string, u: AparteUsage) => void })\n .setUsage?.(messageId, lastUsage);\n } catch { /* viewport may not implement setUsage */ }\n }\n return lastUsage;\n }\n\n /**\n * Delegate `_streamLoop`'s agentic loop to an injected {@link AparteStreamRunner},\n * rendering its DOM-free events through {@link createStreamAdapter}.\n * Builds the runner's dependencies from this client's config / provider /\n * transport; the adapter reproduces the inline loop's `targetElement.*` calls\n * (validated by the engine `stream-parity` suite). Leading writes (status\n * streaming, prefix segments) mirror the inline path; the runner's `run-start`\n * re-asserts `streaming` idempotently.\n */\n private async _runViaStreamRunner(\n streamRunner: AparteStreamRunner,\n targetElement: AparteChatTargetElement,\n messageId: string,\n provider: AparteAIProvider,\n baseRequest: AparteChatRequest,\n authConfig: string | Record<string, string> | undefined,\n streamController: AbortController,\n ): Promise<AparteUsage | undefined> {\n const signal = streamController.signal;\n\n // Leading writes (mirror inline :1034-1042).\n this._updateMessage(targetElement, messageId, { status: 'streaming' });\n const prefixSegments = baseRequest._meta?.['prefixSegments'] as AparteSegment[] | undefined;\n if (prefixSegments?.length) {\n for (const seg of prefixSegments) targetElement.addSegment?.(seg);\n }\n\n const artifactHint = baseRequest._meta?.artifactHint;\n const emitter = createStreamAdapter({\n target: targetElement as StreamAdapterTarget,\n config: this._config,\n messageId,\n artifactHint,\n });\n\n const transportCall = async (request: AparteChatRequest): Promise<AsyncIterable<AparteStreamEvent> | string> => {\n const response = await this._config.getTransport().chat(provider, request, authConfig, { providerId: provider.id, signal });\n return typeof response === 'string'\n ? response\n : readableToAsyncIterable(response as ReadableStream<AparteStreamEvent>, signal);\n };\n const toolLookup = (name: string) => this._config.getToolHandler(name);\n const toolConfigLookup = (name: string) => {\n const tool = this._config.getTools().find(t => t.name === name);\n return tool ? { maxTurns: tool.maxTurns, needsApproval: tool.needsApproval } : undefined;\n };\n const approvalResolver = this.options.approvalResolver\n ?? ((id: string, sig: AbortSignal) => this._awaitToolDecision(id, sig));\n\n const usage = await streamRunner({\n messageId,\n baseRequest,\n transportCall,\n toolLookup,\n toolConfigLookup,\n approvalResolver,\n emitter,\n signal,\n maxTurns: this.options.maxTurns,\n // Match the inline loop's id conventions: prefixed artifact ids, but a\n // BARE uuid for the synthetic tool (the adapter renders `tool-<id>`).\n idGen: (prefix) => (prefix === 'synthetic-tool' ? crypto.randomUUID() : `${prefix}-${crypto.randomUUID()}`),\n });\n return usage ?? undefined;\n }\n\n /**\n * Updates an existing message or appends a new error if ID lookup fails\n */\n private _updateMessage(target: AparteChatTargetElement, messageId: string, updates: Partial<AparteMessage>) {\n if (target.updateMessage) {\n target.updateMessage(messageId, updates);\n }\n }\n\n /**\n * Gracefully handles errors by updating the current message instead of duplicating it\n */\n private _handleLifecycleError(target: AparteChatTargetElement, messageId: string, error: AparteError) {\n // A valid AparteErrorSegment: the renderer keys off `id` + `content`, so the\n // old `code`/`data` fields were dead here (the full error — including\n // `error.data` — still reaches consumers via the `aparte-message-error` event\n // below). The error code is preserved as `details`.\n const errorSegment: AparteErrorSegment = {\n id: `error-${crypto.randomUUID()}`,\n type: 'error',\n content: error.message,\n details: error.code,\n };\n\n if (target.updateMessage) {\n // ATOMIC UPDATE (V2)\n target.updateMessage(messageId, {\n status: 'error',\n segments: [errorSegment]\n });\n } else if (target.appendMessage) {\n // FALLBACK (V1) - Still try to be smart\n target.appendMessage({\n id: messageId,\n role: 'assistant',\n status: 'error',\n timestamp: Date.now(),\n segments: [errorSegment]\n });\n }\n\n this._dispatchLifecycleEvent(target, 'aparte-message-error', { messageId, error });\n }\n\n private _dispatchLifecycleEvent(target: HTMLElement, name: string, detail: Record<string, unknown>) {\n target.dispatchEvent(new CustomEvent(name, {\n bubbles: true,\n composed: true,\n // Tag every lifecycle event with the target's id so several chats on\n // one page stay isolated — a composer reacts only to its own host's\n // turn (id-less single-instance pages still broadcast).\n detail: { targetId: target.id || undefined, ...detail },\n }));\n }\n\n /**\n * Dispatch the artifact lifecycle (`aparte-artifact-start` / `delta` / `ready`)\n * on the host bubble element. Idempotent for `start` (fires once per segment id)\n * and emits `delta` only when the body actually grew. `isFinal=true` fires `ready`.\n */\n private _dispatchArtifactLifecycle(\n target: HTMLElement,\n messageId: string,\n segment: import('../types/segments.js').AparteArtifactSegment,\n progress: Map<string, number>,\n isFinal: boolean\n ): void {\n const id = segment.id as string;\n const content = (segment.content as string) ?? '';\n const seen = progress.get(id);\n\n if (seen === undefined) {\n // First time we see this artifact → start\n target.dispatchEvent(new CustomEvent('aparte-artifact-start', {\n bubbles: true,\n composed: true,\n detail: {\n messageId,\n segmentId: id,\n mimeType: segment.mimeType,\n artifactType: segment.artifactType,\n title: segment.title,\n },\n }));\n progress.set(id, 0);\n }\n\n const lastLen = progress.get(id) ?? 0;\n if (content.length > lastLen) {\n const chunk = content.slice(lastLen);\n target.dispatchEvent(new CustomEvent('aparte-artifact-delta', {\n bubbles: true,\n composed: true,\n detail: { segmentId: id, chunk },\n }));\n progress.set(id, content.length);\n }\n\n if (isFinal) {\n target.dispatchEvent(new CustomEvent('aparte-artifact-ready', {\n bubbles: true,\n composed: true,\n detail: {\n messageId,\n segmentId: id,\n mimeType: segment.mimeType,\n artifactType: segment.artifactType,\n title: segment.title,\n content,\n },\n }));\n }\n }\n}\n","/**\n * MessageRepository — Conversation Tree Storage\n *\n * Manages the branching conversation tree while keeping AparteMessage pure\n * (no topology fields on the message itself). Inspired by assistant-ui's\n * MessageRepository but adapted for the Aparte vanilla-TS stack.\n *\n * Internal structure: a doubly-linked tree where each node holds a pointer\n * to its parent (prev) and its active child (next). `children` stores all\n * sibling IDs. `head` is always the active leaf.\n *\n * getMessages() walks head → root via `prev` — O(depth), not O(n).\n */\n\nimport type { AparteMessage } from '../types/index.js';\n\n// ─── Internal node (never exposed outside) ──────────────────────────────────\n\ntype RepoNode = {\n /** Parent node (null = root) */\n prev: RepoNode | null;\n /** Currently active child */\n next: RepoNode | null;\n /** IDs of all children (branches) */\n children: string[];\n /** The message data */\n current: AparteMessage;\n /** Depth in the tree = index in the flat active-path array */\n level: number;\n};\n\ntype RepoRoot = {\n next: RepoNode | null;\n children: string[];\n};\n\n/** Exported format — compatible with serialisation */\nexport type ExportedMessageRepository = {\n headId: string | null;\n messages: Array<{ message: AparteMessage; parentId: string | null }>;\n};\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Walk `next` chain to find the deepest active node */\nfunction findHead(node: RepoNode | RepoRoot): RepoNode | null {\n if (node.next) return findHead(node.next);\n if ('current' in node) return node as RepoNode;\n return null;\n}\n\n// ─── Repository ──────────────────────────────────────────────────────────────\n\nexport class MessageRepository {\n private _nodes = new Map<string, RepoNode>();\n private _head: RepoNode | null = null;\n private _root: RepoRoot = { next: null, children: [] };\n\n // ─── Public read API ────────────────────────────────────────────────────\n\n /** ID of the currently active leaf message */\n get headId(): string | null {\n return this._head?.current.id ?? null;\n }\n\n /**\n * Returns the messages on the currently active path, root → head.\n * O(depth).\n */\n getMessages(): AparteMessage[] {\n const depth = this._head?.level ?? -1;\n if (depth < 0) return [];\n const messages = new Array<AparteMessage>(depth + 1);\n for (let cur: RepoNode | null = this._head; cur; cur = cur.prev) {\n messages[cur.level] = cur.current;\n }\n return messages;\n }\n\n /**\n * Returns metadata about a specific message, or `undefined` if not found.\n *\n * Read methods (`getMessage`, `getMessageById`, `getBranches`) all return\n * `undefined` / `[]` for unknown ids — never throw — so callers can do a\n * single existence check at the top of a flow rather than wrapping every\n * call in try/catch.\n */\n getMessage(id: string): { parentId: string | null; message: AparteMessage; index: number } | undefined {\n const node = this._nodes.get(id);\n if (!node) return undefined;\n return {\n parentId: node.prev?.current.id ?? null,\n message: node.current,\n index: node.level,\n };\n }\n\n /**\n * Returns the mutable message reference, or `undefined` if not found.\n * Mutations to the returned object are reflected in `getMessages()`.\n */\n getMessageById(id: string): AparteMessage | undefined {\n return this._nodes.get(id)?.current;\n }\n\n /**\n * Returns the IDs of all siblings of `messageId`\n * (i.e. all children of its parent, including itself).\n * Use this to build branch-picker UI.\n */\n getBranches(messageId: string): string[] {\n const node = this._nodes.get(messageId);\n if (!node) return [];\n const parent = node.prev ?? this._root;\n return [...parent.children];\n }\n\n // ─── Public write API ───────────────────────────────────────────────────\n\n /**\n * Add a new message as a child of `parentId` (null = root).\n * If a message with the same id already exists, updates its data\n * and re-links it under the correct parent.\n */\n addOrUpdateMessage(parentId: string | null, message: AparteMessage): void {\n const existing = this._nodes.get(message.id);\n const parentNode = parentId ? this._nodes.get(parentId) : undefined;\n\n if (parentId !== null && parentNode === undefined) {\n throw new Error(`MessageRepository: parent \"${parentId}\" not found.`);\n }\n\n // After the guard above, parentNode is either a valid RepoNode or undefined (null parentId case)\n const parent: RepoNode | null = parentNode ?? null;\n\n if (existing) {\n existing.current = message;\n this._relink(parent, existing);\n return;\n }\n\n const node: RepoNode = {\n prev: parent,\n next: null,\n children: [],\n current: message,\n level: parent ? parent.level + 1 : 0,\n };\n\n this._nodes.set(message.id, node);\n this._link(parent, node);\n\n // Auto-advance head to new node if it was the previous active leaf\n if (this._head === parent || (this._head === null && parent === null)) {\n this._head = node;\n }\n }\n\n /**\n * Switch the active branch to `messageId`.\n * Updates `next` on the parent chain so getMessages() reflects the change.\n * Advances `head` to the deepest active node in the new branch.\n *\n * Silent no-op for unknown ids — mutation methods (`switchToBranch`,\n * `resetHead`, `updateMessage`, `clearChildren`) never throw.\n */\n switchToBranch(messageId: string): void {\n const node = this._nodes.get(messageId);\n if (!node) return;\n const parentOrRoot = node.prev ?? this._root;\n parentOrRoot.next = node;\n this._head = findHead(node);\n }\n\n /**\n * Remove all descendants of `messageId` (inclusive) and set `head` to\n * the parent of `messageId`. Used by edit/truncate flows.\n *\n * @param messageId The first message to remove\n * @param newParentId Optional explicit parent to set head to\n */\n resetHead(messageId: string): void {\n const node = this._nodes.get(messageId);\n if (!node) return;\n\n // Delete all descendants first\n this._deleteDescendants(node);\n\n // Cut the node from its parent, clearing next BEFORE any findHead call.\n // Landing head on the parent with no active child is intentional for the\n // truncate/edit purpose (regenerate from the parent). Any OTHER sibling\n // branches of `messageId` are deliberately left in place — use\n // `clearChildren(parentId)` when you want to drop every branch instead.\n const parentOrRoot = node.prev ?? this._root;\n parentOrRoot.children = parentOrRoot.children.filter(id => id !== messageId);\n if (parentOrRoot.next === node) {\n parentOrRoot.next = null;\n }\n this._nodes.delete(messageId);\n\n // Move head to parent (null if the root was deleted)\n this._head = node.prev;\n }\n\n /**\n * Update a message's data without changing its position in the tree.\n */\n updateMessage(messageId: string, updates: Partial<AparteMessage>): void {\n const node = this._nodes.get(messageId);\n if (!node) return;\n node.current = { ...node.current, ...updates };\n }\n\n /**\n * Remove ALL children of `parentId` (and their descendants) and set head\n * to `parentId`. Used by edit to discard every previous response before\n * re-generating — ensures the new response starts with sibling count = 1.\n */\n clearChildren(parentId: string): void {\n const parent = this._nodes.get(parentId);\n if (!parent) return;\n // deleteDescendants only handles children-of-node, so iterate manually\n for (const childId of [...parent.children]) {\n const child = this._nodes.get(childId);\n if (child) {\n this._deleteDescendants(child);\n this._nodes.delete(childId);\n }\n }\n parent.children = [];\n parent.next = null;\n this._head = parent;\n }\n\n /** Remove all messages and reset to empty state. */\n clear(): void {\n this._nodes.clear();\n this._head = null;\n this._root = { next: null, children: [] };\n }\n\n /**\n * Import a previously-exported snapshot, rebuilding the full tree and\n * restoring the active head. Replaces any existing content (calls `clear()`\n * first). Messages must be in topological order (parent before child) —\n * this is guaranteed by `export()` since `_nodes` is an insertion-order\n * Map and parents are always inserted before their children.\n */\n import(exported: ExportedMessageRepository): void {\n this.clear();\n for (const { message, parentId } of exported.messages) {\n this.addOrUpdateMessage(parentId, message);\n }\n if (exported.headId) {\n this.switchToBranch(exported.headId);\n }\n }\n\n /**\n * Export the full tree for serialisation or debugging.\n * Compatible with the assistant-ui ExportedMessageRepository format.\n */\n export(): ExportedMessageRepository {\n const messages: Array<{ message: AparteMessage; parentId: string | null }> = [];\n this._nodes.forEach((node) => {\n messages.push({\n message: node.current,\n parentId: node.prev?.current.id ?? null,\n });\n });\n return { headId: this.headId, messages };\n }\n\n // ─── Private helpers ────────────────────────────────────────────────────\n\n private _link(parent: RepoNode | null, child: RepoNode): void {\n const parentOrRoot: RepoNode | RepoRoot = parent ?? this._root;\n parentOrRoot.children = [...parentOrRoot.children, child.current.id];\n // Auto-set active child if parent has none\n if (parentOrRoot.next === null) {\n parentOrRoot.next = child;\n }\n child.prev = parent;\n }\n\n private _relink(newParent: RepoNode | null, child: RepoNode): void {\n // Cut from old parent\n const oldParentOrRoot: RepoNode | RepoRoot = child.prev ?? this._root;\n if (oldParentOrRoot !== (newParent ?? this._root)) {\n oldParentOrRoot.children = oldParentOrRoot.children.filter(id => id !== child.current.id);\n if (oldParentOrRoot.next === child) {\n // The moved child was the active one — fall back to a remaining\n // sibling (or none). NOT `findHead(oldParentOrRoot)`: `next` still\n // points at `child` here, so findHead would walk into the subtree\n // being moved away and leave a dangling pointer.\n const nextId = oldParentOrRoot.children[0];\n oldParentOrRoot.next = nextId ? this._nodes.get(nextId) ?? null : null;\n }\n }\n // Attach to new parent\n child.prev = newParent;\n const newParentOrRoot: RepoNode | RepoRoot = newParent ?? this._root;\n if (!newParentOrRoot.children.includes(child.current.id)) {\n newParentOrRoot.children = [...newParentOrRoot.children, child.current.id];\n }\n if (newParentOrRoot.next === null) {\n newParentOrRoot.next = child;\n }\n this._updateLevels(child, newParent ? newParent.level + 1 : 0);\n }\n\n private _updateLevels(node: RepoNode, newLevel: number): void {\n node.level = newLevel;\n for (const childId of node.children) {\n const childNode = this._nodes.get(childId);\n if (childNode) this._updateLevels(childNode, newLevel + 1);\n }\n }\n\n private _deleteDescendants(node: RepoNode): void {\n for (const childId of node.children) {\n const child = this._nodes.get(childId);\n if (child) {\n this._deleteDescendants(child);\n this._nodes.delete(childId);\n }\n }\n node.children = [];\n node.next = null;\n }\n}\n","/**\n * Vanilla-DOM helpers shared by the framework wrappers' `AparteUi` pass-through\n * component. They live in core (zero-dep) so the four wrappers don't each carry\n * a byte-identical copy — only the framework-specific mounting differs.\n */\n\n/** The custom events aparté elements actually dispatch (verified against core). */\nexport const DEFAULT_UI_EVENTS: readonly string[] = [\n 'aparte-send',\n 'aparte-action',\n 'aparte-retry',\n 'aparte-edit',\n 'aparte-branch-navigate',\n 'aparte-composer-change',\n 'aparte-path-changed',\n];\n\n/**\n * Apply props to an aparté custom element. aparté elements are\n * **attribute-driven** (`observedAttributes`): assigning a property is either a\n * silent no-op (nothing observes it) or throws outright on a getter-only\n * accessor — `<aparte-composer>`'s `placeholder`/`disabled` are exactly that.\n * So primitives go through `setAttribute`; only values an attribute cannot carry\n * (objects, functions) are handed over as properties. Keys starting with `--`\n * become CSS variables; `on…` function values are ignored (event forwarding is\n * the wrapper's job).\n *\n * `transformValue` is applied to object/function values before they're set as\n * properties — Vue passes `toRaw` to unwrap its reactive proxy (a deep proxy\n * breaks Maps/class internals on a plain custom element); it defaults to identity.\n */\nexport function applyElementProps(\n el: HTMLElement,\n props: Record<string, unknown>,\n transformValue: (value: unknown) => unknown = (value) => value,\n): void {\n for (const [key, value] of Object.entries(props)) {\n if (key.startsWith('--')) {\n el.style.setProperty(key, String(value));\n } else if (key.startsWith('on') && typeof value === 'function') {\n // Event handlers belong on the wrapper's event forwarding, not here.\n } else if (value === null || value === undefined || value === false) {\n el.removeAttribute(key);\n } else if (value === true) {\n el.setAttribute(key, '');\n } else if (typeof value === 'object' || typeof value === 'function') {\n (el as unknown as Record<string, unknown>)[key] = transformValue(value);\n } else {\n el.setAttribute(key, String(value));\n }\n }\n}\n","/**\n * buildElicitationPanel — turns an {@link AparteElicitationSchema} into a DOM panel\n * that the presenter mounts into the composer. Zero-dependency; covers all four\n * schema kinds (enum, boolean, string, object). The presenter reads `getContent`\n * on submit and `isComplete` to gate the send button (via `onChange`).\n */\n\nimport type {\n AparteElicitationSchema,\n AparteElicitationField,\n AparteElicitationEnumField,\n AparteElicitationBooleanField,\n AparteElicitationStringField,\n} from './types.js';\n\nexport interface BuiltElicitationPanel {\n readonly el: HTMLElement;\n /** The current response content, shaped to match the schema. */\n getContent(): unknown;\n /** True when every required field has a usable value. */\n isComplete(): boolean;\n /** Focus the first input (called after mount). */\n focus(): void;\n}\n\ninterface BuiltField {\n readonly el: HTMLElement;\n getValue(): unknown;\n isComplete(): boolean;\n focus(): void;\n}\n\nfunction el<K extends keyof HTMLElementTagNameMap>(tag: K, className?: string, text?: string): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text != null) node.textContent = text;\n return node;\n}\n\nfunction fieldHeader(parent: HTMLElement, field: AparteElicitationField): void {\n if (field.title) parent.appendChild(el('p', 'aparte-elic-title', field.title));\n if (field.description) parent.appendChild(el('p', 'aparte-elic-desc', field.description));\n}\n\n// ─── enum ────────────────────────────────────────────────────────────────────\n\nfunction buildEnumField(field: AparteElicitationEnumField, onChange: () => void): BuiltField {\n const wrap = el('div', 'aparte-elic-field aparte-elic-enum');\n fieldHeader(wrap, field);\n const list = el('div', 'aparte-elic-options');\n const name = `elic-${crypto.randomUUID()}`;\n const type = field.multiple ? 'checkbox' : 'radio';\n const allowOther = field.allowOther ?? true;\n const defaults = new Set(Array.isArray(field.default) ? field.default : field.default != null ? [field.default] : []);\n\n const buildOption = (value: string, label: string, description?: string, recommended?: boolean): HTMLElement => {\n const row = el('label', 'aparte-elic-option' + (recommended ? ' aparte-elic-option--recommended' : ''));\n const control = el('input', 'aparte-elic-control');\n control.type = type;\n control.name = name;\n control.value = value;\n if (defaults.has(value)) control.checked = true;\n const body = el('span', 'aparte-elic-option-body');\n body.appendChild(el('span', 'aparte-elic-option-title', label));\n if (description) body.appendChild(el('span', 'aparte-elic-option-desc', description));\n row.append(control, body);\n return row;\n };\n\n for (const opt of field.options) {\n list.appendChild(buildOption(opt.value, opt.label ?? opt.value, opt.description, opt.recommended));\n }\n\n // Free-text \"Other…\" fallback.\n let otherText: HTMLInputElement | null = null;\n if (allowOther) {\n const row = el('label', 'aparte-elic-option aparte-elic-option--other');\n const control = el('input', 'aparte-elic-control');\n control.type = type;\n control.name = name;\n control.value = '__other__';\n const body = el('span', 'aparte-elic-option-body');\n body.appendChild(el('span', 'aparte-elic-option-title', 'Other…'));\n otherText = el('input', 'aparte-elic-other-input');\n otherText.type = 'text';\n otherText.placeholder = 'Type your answer…';\n otherText.style.display = 'none';\n otherText.setAttribute('aria-label', 'Custom answer');\n body.appendChild(otherText);\n row.append(control, body);\n list.appendChild(row);\n }\n wrap.appendChild(list);\n\n const controls = () => Array.from(list.querySelectorAll<HTMLInputElement>('.aparte-elic-control'));\n const other = () => list.querySelector<HTMLInputElement>('input[value=\"__other__\"]');\n list.addEventListener('change', () => {\n const o = other();\n if (o && otherText) {\n otherText.style.display = o.checked ? '' : 'none';\n if (o.checked) otherText.focus();\n }\n onChange();\n });\n otherText?.addEventListener('input', onChange);\n\n const getValue = (): string | string[] => {\n const otherVal = other()?.checked && otherText?.value.trim() ? otherText.value.trim() : '';\n if (field.multiple) {\n const vals = controls().filter(c => c.checked && c.value !== '__other__').map(c => c.value);\n if (otherVal) vals.push(otherVal);\n return vals;\n }\n const checked = controls().find(c => c.checked);\n if (!checked) return '';\n return checked.value === '__other__' ? otherVal : checked.value;\n };\n\n return {\n el: wrap,\n getValue,\n isComplete: () => {\n const v = getValue();\n return field.multiple ? (v as string[]).length > 0 : v !== '';\n },\n focus: () => controls()[0]?.focus(),\n };\n}\n\n// ─── boolean ───────────────────────────────────────────────────────────────\n\nfunction buildBooleanField(field: AparteElicitationBooleanField, onChange: () => void): BuiltField {\n const wrap = el('div', 'aparte-elic-field aparte-elic-boolean');\n fieldHeader(wrap, field);\n const list = el('div', 'aparte-elic-options');\n const name = `elic-${crypto.randomUUID()}`;\n const mk = (val: 'true' | 'false', label: string): void => {\n const row = el('label', 'aparte-elic-option');\n const control = el('input', 'aparte-elic-control');\n control.type = 'radio';\n control.name = name;\n control.value = val;\n if (field.default != null && String(field.default) === val) control.checked = true;\n const body = el('span', 'aparte-elic-option-body');\n body.appendChild(el('span', 'aparte-elic-option-title', label));\n row.append(control, body);\n list.appendChild(row);\n };\n mk('true', field.trueLabel ?? 'Yes');\n mk('false', field.falseLabel ?? 'No');\n wrap.appendChild(list);\n list.addEventListener('change', onChange);\n\n const checked = () => list.querySelector<HTMLInputElement>('input:checked');\n return {\n el: wrap,\n getValue: () => checked()?.value === 'true',\n isComplete: () => checked() != null,\n focus: () => list.querySelector<HTMLInputElement>('input')?.focus(),\n };\n}\n\n// ─── string ────────────────────────────────────────────────────────────────\n\nfunction buildStringField(field: AparteElicitationStringField, onChange: () => void): BuiltField {\n const wrap = el('div', 'aparte-elic-field aparte-elic-string');\n fieldHeader(wrap, field);\n const input = field.multiline\n ? el('textarea', 'aparte-elic-text')\n : el('input', 'aparte-elic-text');\n if (!field.multiline) (input as HTMLInputElement).type = 'text';\n if (field.placeholder) input.setAttribute('placeholder', field.placeholder);\n if (field.default) (input as HTMLInputElement | HTMLTextAreaElement).value = field.default;\n if (field.maxLength != null) input.setAttribute('maxlength', String(field.maxLength));\n input.setAttribute('aria-label', field.title ?? field.description ?? 'Your answer');\n wrap.appendChild(input);\n input.addEventListener('input', onChange);\n\n const required = field.required ?? true;\n const value = () => (input as HTMLInputElement | HTMLTextAreaElement).value;\n return {\n el: wrap,\n getValue: () => value(),\n isComplete: () => {\n const v = value().trim();\n if (!required && v === '') return true;\n return v.length >= (field.minLength ?? 1);\n },\n focus: () => input.focus(),\n };\n}\n\nfunction buildField(field: AparteElicitationField, onChange: () => void): BuiltField {\n switch (field.type) {\n case 'enum': return buildEnumField(field, onChange);\n case 'boolean': return buildBooleanField(field, onChange);\n case 'string': return buildStringField(field, onChange);\n }\n}\n\n// ─── panel ─────────────────────────────────────────────────────────────────\n\nexport function buildElicitationPanel(\n message: string,\n schema: AparteElicitationSchema,\n onChange: () => void,\n): BuiltElicitationPanel {\n const panel = el('div', 'aparte-elic-panel');\n if (message) panel.appendChild(el('p', 'aparte-elic-message', message));\n\n if (schema.type === 'object') {\n const entries = Object.entries(schema.properties);\n const requiredKeys = new Set(schema.required ?? entries.map(([k]) => k));\n const fields = entries.map(([key, field]) => {\n const built = buildField(field, onChange);\n // Ensure a title so each field in a form is labelled.\n if (!field.title && !built.el.querySelector('.aparte-elic-title')) {\n built.el.insertBefore(el('p', 'aparte-elic-title', key), built.el.firstChild);\n }\n panel.appendChild(built.el);\n return { key, field: built, required: requiredKeys.has(key) };\n });\n return {\n el: panel,\n getContent: () => Object.fromEntries(fields.map(f => [f.key, f.field.getValue()])),\n isComplete: () => fields.every(f => !f.required || f.field.isComplete()),\n focus: () => fields[0]?.field.focus(),\n };\n }\n\n const field = buildField(schema, onChange);\n panel.appendChild(field.el);\n return {\n el: panel,\n getContent: () => field.getValue(),\n isComplete: () => field.isComplete(),\n focus: () => field.focus(),\n };\n}\n","/**\n * Elicitation — public surface.\n *\n * `requestUserInput(req)` is the free-function entry a tool handler calls: it\n * resolves the config governing `req.target` (the instance the request belongs\n * to, or the global default) and delegates to that config's presenter. This is\n * what `ask_question` and tool-approval adapters use — they never touch window\n * events, so there is no stringly-typed contract to drift.\n */\n\nimport { resolveConfig } from '../config/config-context.js';\nimport type { AparteElicitationRequest, AparteElicitationResult } from './types.js';\n\nexport type {\n AparteElicitationSchema,\n AparteElicitationField,\n AparteElicitationEnumField,\n AparteElicitationBooleanField,\n AparteElicitationStringField,\n AparteElicitationObjectSchema,\n AparteElicitationRequest,\n AparteElicitationResult,\n AparteElicitationPresenter,\n} from './types.js';\nexport { buildElicitationPanel } from './panel.js';\nexport type { BuiltElicitationPanel } from './panel.js';\n\n/**\n * Ask the user for typed input and await their response, on the config\n * governing `request.target` (its instance config, or the global default).\n * Resolves `accept` / `decline` / `cancel`.\n */\nexport function requestUserInput(request: AparteElicitationRequest): Promise<AparteElicitationResult> {\n return resolveConfig(request.target ?? null).requestUserInput(request);\n}\n"],"names":["AparteErrorCode","codeContent","segment","el","kind","cached","field"],"mappings":"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"}