@flamingo-stack/openframe-frontend-core 0.0.692 → 0.0.693-snapshot.20261002120603

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 (205) hide show
  1. package/dist/chat-protocol/events.d.ts +3 -0
  2. package/dist/chat-protocol/events.d.ts.map +1 -1
  3. package/dist/chat-protocol/frames.d.ts +6 -0
  4. package/dist/chat-protocol/frames.d.ts.map +1 -1
  5. package/dist/chat-protocol/index.cjs +26 -1
  6. package/dist/chat-protocol/index.cjs.map +1 -1
  7. package/dist/chat-protocol/index.d.ts +1 -0
  8. package/dist/chat-protocol/index.d.ts.map +1 -1
  9. package/dist/chat-protocol/index.js +23 -2
  10. package/dist/chat-protocol/index.js.map +1 -1
  11. package/dist/chat-protocol/leading-frames.d.ts.map +1 -1
  12. package/dist/chat-protocol/progress.d.ts +35 -0
  13. package/dist/chat-protocol/progress.d.ts.map +1 -0
  14. package/dist/{chunk-GDZ7IC5B.js → chunk-234IIQIS.js} +2 -2
  15. package/dist/{chunk-KDCIYRZV.cjs → chunk-2EQED5SZ.cjs} +34 -34
  16. package/dist/{chunk-KDCIYRZV.cjs.map → chunk-2EQED5SZ.cjs.map} +1 -1
  17. package/dist/{chunk-JUNOY5HK.js → chunk-2HX6AADM.js} +15 -17
  18. package/dist/chunk-2HX6AADM.js.map +1 -0
  19. package/dist/{chunk-X5XFKI6N.js → chunk-33L2IR62.js} +3 -3
  20. package/dist/{chunk-A5NHAHV6.js → chunk-42LIHMRA.js} +4 -4
  21. package/dist/{chunk-MIZU2OTO.cjs → chunk-63BLVGXZ.cjs} +4 -4
  22. package/dist/{chunk-MIZU2OTO.cjs.map → chunk-63BLVGXZ.cjs.map} +1 -1
  23. package/dist/{chunk-TEIHEFXP.js → chunk-6ZSDKDOV.js} +8 -8
  24. package/dist/{chunk-UVUNBXS6.js → chunk-7IINE3MK.js} +48 -5
  25. package/dist/chunk-7IINE3MK.js.map +1 -0
  26. package/dist/{chunk-N3526CTH.cjs → chunk-7TO3CWPM.cjs} +3 -3
  27. package/dist/{chunk-N3526CTH.cjs.map → chunk-7TO3CWPM.cjs.map} +1 -1
  28. package/dist/{chunk-TXFGG47A.js → chunk-7VO6NC67.js} +4 -4
  29. package/dist/{chunk-SC44YJCF.js → chunk-A7RKT5SO.js} +2 -2
  30. package/dist/{chunk-Y7ZEVTLC.js → chunk-BW6YBATM.js} +141 -71
  31. package/dist/chunk-BW6YBATM.js.map +1 -0
  32. package/dist/{chunk-GZRU2A4L.cjs → chunk-C27F6GBZ.cjs} +790 -720
  33. package/dist/chunk-C27F6GBZ.cjs.map +1 -0
  34. package/dist/{chunk-DVSPRY2V.cjs → chunk-ESMDIJIN.cjs} +3 -3
  35. package/dist/{chunk-DVSPRY2V.cjs.map → chunk-ESMDIJIN.cjs.map} +1 -1
  36. package/dist/{chunk-EXWS2QSD.cjs → chunk-FNGX5WZM.cjs} +497 -474
  37. package/dist/chunk-FNGX5WZM.cjs.map +1 -0
  38. package/dist/{chunk-RK47E3PP.js → chunk-GWQCUC53.js} +4 -4
  39. package/dist/{chunk-AVAZ6BD6.js → chunk-HBMGSGZY.js} +6 -6
  40. package/dist/{chunk-HM7ZGABI.js → chunk-HEBLLLLD.js} +2 -2
  41. package/dist/{chunk-7BNWMOPM.cjs → chunk-HJZ3UTUE.cjs} +83 -83
  42. package/dist/{chunk-7BNWMOPM.cjs.map → chunk-HJZ3UTUE.cjs.map} +1 -1
  43. package/dist/{chunk-ATREVNIN.cjs → chunk-JB25CCOB.cjs} +47 -47
  44. package/dist/{chunk-ATREVNIN.cjs.map → chunk-JB25CCOB.cjs.map} +1 -1
  45. package/dist/{chunk-Q25VYAEL.cjs → chunk-JISKS7RJ.cjs} +26 -26
  46. package/dist/{chunk-Q25VYAEL.cjs.map → chunk-JISKS7RJ.cjs.map} +1 -1
  47. package/dist/{chunk-WTYQCT4A.cjs → chunk-K5GSLGV2.cjs} +18 -18
  48. package/dist/{chunk-WTYQCT4A.cjs.map → chunk-K5GSLGV2.cjs.map} +1 -1
  49. package/dist/{chunk-CG7OWW7X.cjs → chunk-K5K2FRQK.cjs} +19 -19
  50. package/dist/{chunk-CG7OWW7X.cjs.map → chunk-K5K2FRQK.cjs.map} +1 -1
  51. package/dist/{chunk-CKUPANVU.cjs → chunk-KBSL4FCL.cjs} +9 -9
  52. package/dist/{chunk-CKUPANVU.cjs.map → chunk-KBSL4FCL.cjs.map} +1 -1
  53. package/dist/{chunk-SDORFSFK.cjs → chunk-KDUDL4PB.cjs} +37 -37
  54. package/dist/{chunk-SDORFSFK.cjs.map → chunk-KDUDL4PB.cjs.map} +1 -1
  55. package/dist/{chunk-5XPK2QGU.cjs → chunk-KJHIFF6H.cjs} +16 -16
  56. package/dist/{chunk-5XPK2QGU.cjs.map → chunk-KJHIFF6H.cjs.map} +1 -1
  57. package/dist/{chunk-MCNQKS6I.js → chunk-LHYOESIQ.js} +4 -4
  58. package/dist/{chunk-DUURFYGS.cjs → chunk-LPFU6U7O.cjs} +5 -5
  59. package/dist/{chunk-DUURFYGS.cjs.map → chunk-LPFU6U7O.cjs.map} +1 -1
  60. package/dist/{chunk-ZAV2F4VX.js → chunk-N7HFUBOV.js} +3 -3
  61. package/dist/{chunk-53O6PQ3F.cjs → chunk-NDLFK57D.cjs} +7 -7
  62. package/dist/{chunk-53O6PQ3F.cjs.map → chunk-NDLFK57D.cjs.map} +1 -1
  63. package/dist/{chunk-Z7QCNCBN.js → chunk-O4RDXDVS.js} +12 -12
  64. package/dist/{chunk-F6LXGP6S.cjs → chunk-Q2B2MGFU.cjs} +7 -7
  65. package/dist/{chunk-F6LXGP6S.cjs.map → chunk-Q2B2MGFU.cjs.map} +1 -1
  66. package/dist/{chunk-CKO6NKNE.js → chunk-QP54LMXJ.js} +38 -15
  67. package/dist/{chunk-CKO6NKNE.js.map → chunk-QP54LMXJ.js.map} +1 -1
  68. package/dist/{chunk-QRA2JXE4.cjs → chunk-RE6PGOSS.cjs} +6 -8
  69. package/dist/chunk-RE6PGOSS.cjs.map +1 -0
  70. package/dist/{chunk-C2SVJVEB.js → chunk-SH5GPYSV.js} +2 -2
  71. package/dist/{chunk-KQZMM3QJ.cjs → chunk-SJFB4Z4L.cjs} +105 -105
  72. package/dist/{chunk-KQZMM3QJ.cjs.map → chunk-SJFB4Z4L.cjs.map} +1 -1
  73. package/dist/{chunk-7EPGJOKN.js → chunk-TA3JJFEO.js} +4 -4
  74. package/dist/{chunk-LYJJH4D6.js → chunk-TBR3UTFY.js} +6 -6
  75. package/dist/{chunk-USKQURNF.cjs → chunk-TG3VBMDG.cjs} +3 -3
  76. package/dist/{chunk-USKQURNF.cjs.map → chunk-TG3VBMDG.cjs.map} +1 -1
  77. package/dist/{chunk-IQTQIBZ3.js → chunk-TV5SZJ3N.js} +12 -12
  78. package/dist/{chunk-RLNNV46K.js → chunk-TXSQLORA.js} +6 -6
  79. package/dist/{chunk-LYQULSK4.js → chunk-UCXZRJIE.js} +7 -7
  80. package/dist/{chunk-I27PWVWE.cjs → chunk-WT4HURQN.cjs} +46 -3
  81. package/dist/chunk-WT4HURQN.cjs.map +1 -0
  82. package/dist/{chunk-XAOAYDGR.js → chunk-WZJWIZVH.js} +4 -4
  83. package/dist/{chunk-PKZGXRU2.js → chunk-XKNKGBUX.js} +10 -8
  84. package/dist/{chunk-PKZGXRU2.js.map → chunk-XKNKGBUX.js.map} +1 -1
  85. package/dist/{chunk-LNKLB7GM.cjs → chunk-XNTQHC26.cjs} +28 -28
  86. package/dist/{chunk-LNKLB7GM.cjs.map → chunk-XNTQHC26.cjs.map} +1 -1
  87. package/dist/{chunk-2YVTGPM7.js → chunk-XZILGCXJ.js} +2 -2
  88. package/dist/{chunk-DKJDO7YM.cjs → chunk-Y5AFMYYN.cjs} +46 -44
  89. package/dist/chunk-Y5AFMYYN.cjs.map +1 -0
  90. package/dist/{chunk-FTK3S57R.cjs → chunk-ZCPMWXIB.cjs} +25 -25
  91. package/dist/{chunk-FTK3S57R.cjs.map → chunk-ZCPMWXIB.cjs.map} +1 -1
  92. package/dist/{chunk-RTKF5AYA.cjs → chunk-ZUGAHQDB.cjs} +7 -7
  93. package/dist/{chunk-RTKF5AYA.cjs.map → chunk-ZUGAHQDB.cjs.map} +1 -1
  94. package/dist/components/case-studies/index.cjs +21 -21
  95. package/dist/components/case-studies/index.js +8 -8
  96. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  97. package/dist/components/chat/chat-typing-indicator.d.ts.map +1 -1
  98. package/dist/components/chat/cycling-phrase.d.ts.map +1 -1
  99. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  100. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  101. package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
  102. package/dist/components/chat/index.cjs +5 -5
  103. package/dist/components/chat/index.js +4 -4
  104. package/dist/components/chat/stream/chat-dialog-store.d.ts.map +1 -1
  105. package/dist/components/chat/stream/chat-stream-reducer.d.ts +4 -0
  106. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -1
  107. package/dist/components/chat/types/component.types.d.ts +2 -0
  108. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  109. package/dist/components/chat/types/unified-chat-state.types.d.ts +5 -0
  110. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  111. package/dist/components/contact/index.cjs +9 -9
  112. package/dist/components/contact/index.js +8 -8
  113. package/dist/components/docs/index.cjs +12 -12
  114. package/dist/components/docs/index.js +11 -11
  115. package/dist/components/embeds/index.cjs +9 -9
  116. package/dist/components/embeds/index.js +8 -8
  117. package/dist/components/faq/index.cjs +10 -10
  118. package/dist/components/faq/index.js +9 -9
  119. package/dist/components/features/index.cjs +8 -8
  120. package/dist/components/features/index.js +7 -7
  121. package/dist/components/help-center-pages/index.cjs +107 -107
  122. package/dist/components/help-center-pages/index.js +22 -22
  123. package/dist/components/index.cjs +69 -69
  124. package/dist/components/index.js +22 -22
  125. package/dist/components/meeting-scheduler/index.cjs +59 -59
  126. package/dist/components/meeting-scheduler/index.js +9 -9
  127. package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
  128. package/dist/components/navigation/index.cjs +11 -11
  129. package/dist/components/navigation/index.js +10 -10
  130. package/dist/components/onboarding-guides/index.cjs +12 -12
  131. package/dist/components/onboarding-guides/index.js +11 -11
  132. package/dist/components/related-content/index.cjs +6 -6
  133. package/dist/components/related-content/index.js +5 -5
  134. package/dist/components/tickets/index.cjs +13 -13
  135. package/dist/components/tickets/index.js +12 -12
  136. package/dist/components/ui/index.cjs +8 -8
  137. package/dist/components/ui/index.js +7 -7
  138. package/dist/components/ui/markdown/engine.d.ts +0 -21
  139. package/dist/components/ui/markdown/engine.d.ts.map +1 -1
  140. package/dist/hooks/index.cjs +6 -6
  141. package/dist/hooks/index.cjs.map +1 -1
  142. package/dist/hooks/index.js +5 -5
  143. package/dist/hooks/use-authed-asset-src.d.ts.map +1 -1
  144. package/dist/index.cjs +15 -15
  145. package/dist/index.cjs.map +1 -1
  146. package/dist/index.js +12 -12
  147. package/dist/tailwind.config.cjs +14 -1
  148. package/dist/tailwind.config.cjs.map +1 -1
  149. package/dist/tailwind.config.js +14 -1
  150. package/dist/tailwind.config.js.map +1 -1
  151. package/eslint-config/README.md +13 -0
  152. package/eslint-config/base.js +15 -0
  153. package/eslint-config/webview-performance.js +75 -0
  154. package/package.json +1 -1
  155. package/src/chat-protocol/__tests__/progress.test.ts +39 -0
  156. package/src/chat-protocol/events.ts +3 -0
  157. package/src/chat-protocol/frames.ts +7 -0
  158. package/src/chat-protocol/index.ts +2 -0
  159. package/src/chat-protocol/leading-frames.ts +4 -1
  160. package/src/chat-protocol/progress.ts +55 -0
  161. package/src/components/chat/chat-message-enhanced.tsx +1 -1
  162. package/src/components/chat/chat-message-list.tsx +65 -15
  163. package/src/components/chat/chat-typing-indicator.tsx +18 -29
  164. package/src/components/chat/cycling-phrase.tsx +1 -11
  165. package/src/components/chat/embeddable-chat.tsx +14 -6
  166. package/src/components/chat/hooks/use-sse-chat-adapter.ts +2 -0
  167. package/src/components/chat/hooks/use-unified-chat.ts +1 -0
  168. package/src/components/chat/stream/__tests__/chat-stream-reducer-progress.test.ts +40 -0
  169. package/src/components/chat/stream/chat-dialog-store.ts +1 -0
  170. package/src/components/chat/stream/chat-stream-reducer.ts +13 -0
  171. package/src/components/chat/types/component.types.ts +2 -0
  172. package/src/components/chat/types/unified-chat-state.types.ts +6 -0
  173. package/src/components/navigation/app-layout-drawer.tsx +53 -7
  174. package/src/components/ui/__tests__/__snapshots__/markdown-parity.test.tsx.snap +29 -29
  175. package/src/components/ui/markdown/engine.tsx +56 -12
  176. package/src/hooks/use-authed-asset-src.ts +58 -6
  177. package/tailwind.config.ts +13 -0
  178. package/dist/chunk-DKJDO7YM.cjs.map +0 -1
  179. package/dist/chunk-EXWS2QSD.cjs.map +0 -1
  180. package/dist/chunk-GZRU2A4L.cjs.map +0 -1
  181. package/dist/chunk-I27PWVWE.cjs.map +0 -1
  182. package/dist/chunk-JUNOY5HK.js.map +0 -1
  183. package/dist/chunk-QRA2JXE4.cjs.map +0 -1
  184. package/dist/chunk-UVUNBXS6.js.map +0 -1
  185. package/dist/chunk-Y7ZEVTLC.js.map +0 -1
  186. /package/dist/{chunk-GDZ7IC5B.js.map → chunk-234IIQIS.js.map} +0 -0
  187. /package/dist/{chunk-X5XFKI6N.js.map → chunk-33L2IR62.js.map} +0 -0
  188. /package/dist/{chunk-A5NHAHV6.js.map → chunk-42LIHMRA.js.map} +0 -0
  189. /package/dist/{chunk-TEIHEFXP.js.map → chunk-6ZSDKDOV.js.map} +0 -0
  190. /package/dist/{chunk-TXFGG47A.js.map → chunk-7VO6NC67.js.map} +0 -0
  191. /package/dist/{chunk-SC44YJCF.js.map → chunk-A7RKT5SO.js.map} +0 -0
  192. /package/dist/{chunk-RK47E3PP.js.map → chunk-GWQCUC53.js.map} +0 -0
  193. /package/dist/{chunk-AVAZ6BD6.js.map → chunk-HBMGSGZY.js.map} +0 -0
  194. /package/dist/{chunk-HM7ZGABI.js.map → chunk-HEBLLLLD.js.map} +0 -0
  195. /package/dist/{chunk-MCNQKS6I.js.map → chunk-LHYOESIQ.js.map} +0 -0
  196. /package/dist/{chunk-ZAV2F4VX.js.map → chunk-N7HFUBOV.js.map} +0 -0
  197. /package/dist/{chunk-Z7QCNCBN.js.map → chunk-O4RDXDVS.js.map} +0 -0
  198. /package/dist/{chunk-C2SVJVEB.js.map → chunk-SH5GPYSV.js.map} +0 -0
  199. /package/dist/{chunk-7EPGJOKN.js.map → chunk-TA3JJFEO.js.map} +0 -0
  200. /package/dist/{chunk-LYJJH4D6.js.map → chunk-TBR3UTFY.js.map} +0 -0
  201. /package/dist/{chunk-IQTQIBZ3.js.map → chunk-TV5SZJ3N.js.map} +0 -0
  202. /package/dist/{chunk-RLNNV46K.js.map → chunk-TXSQLORA.js.map} +0 -0
  203. /package/dist/{chunk-LYQULSK4.js.map → chunk-UCXZRJIE.js.map} +0 -0
  204. /package/dist/{chunk-XAOAYDGR.js.map → chunk-WZJWIZVH.js.map} +0 -0
  205. /package/dist/{chunk-2YVTGPM7.js.map → chunk-XZILGCXJ.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/chat-protocol/frames.ts","../../src/chat-protocol/events.ts","../../src/chat-protocol/encode.ts","../../src/chat-protocol/leading-frames.ts","../../src/chat-protocol/decode.ts","../../src/components/chat/types/message.types.ts","../../src/utils/url-safety.ts","../../src/chat-protocol/card-marker.ts","../../src/chat-protocol/wire-narrow.ts","../../src/chat-protocol/source-metadata.ts","../../src/chat-protocol/nats-decoder.ts","../../src/chat-protocol/confirm-tool.ts","../../src/chat-protocol/ip-normalize.ts","../../src/chat-protocol/env-flag.ts"],"names":["text","cards"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * SSE wire-protocol frame shapes — the byte-level contract between the\n * hub's chat stream emitter (`multi-platform-hub/lib/utils/claude-util.ts`)\n * and the lib's SSE decoder (`./decode.ts`).\n *\n * Wire format (custom byte framing, NOT standard SSE `data:` lines):\n *\n * [JSON frame]\\0 [JSON frame]\\0 … \\x1E <raw UTF-8 answer deltas> \\x1F [JSON usage trailer]\n *\n * 1. LEADING frames — each a JSON object terminated by `\\0`\n * (FRAME_TERMINATOR). Status, metadata, routing, live thinking\n * deltas, usage:start, tool approval frames.\n * 2. ONE `\\x1E` byte (END_OF_LEADING) — flips the stream into raw\n * UTF-8 answer-text mode.\n * 3. `\\x1F` (TRAILER_SENTINEL) + one JSON usage frame running to\n * stream end (no terminator).\n *\n * Server-safe: no React, no browser APIs beyond TextEncoder/TextDecoder.\n */\n\n// =============================================================================\n// Sentinel constants\n// =============================================================================\n\n/** Terminates every leading JSON frame. */\nexport const FRAME_TERMINATOR = '\\0';\n/** Record Separator — end of leading frames, start of raw answer text. */\nexport const END_OF_LEADING = '\\x1E';\n/** Unit Separator — start of the trailing usage JSON frame. */\nexport const TRAILER_SENTINEL = '\\x1F';\n\n// =============================================================================\n// Routing types (moved verbatim from the hub's `lib/chat/wire-frames.ts`;\n// `RouteComplexity` is lib-local so the lib never imports hub code)\n// =============================================================================\n\n/** Server-side routing complexity tiers. Mirror of the hub's\n * `lib/constants/ai-models.ts` union — kept as a lib-local literal type\n * so the wire SSOT has zero hub imports. The DECODER accepts any string\n * (older/newer servers may ship values outside this union). */\nexport type RouteComplexity = 'trivial' | 'default' | 'complex' | 'deep';\n\n/**\n * Routing decision frame. Emitted once per turn as a LEADING frame\n * (between metadata and the `\\x1E` end-of-leading-frames sentinel) when\n * the server's `decideRoute()` produces a `Route`.\n *\n * Field names use the `routed*` prefix on PURPOSE — the client's\n * catch-all leading-frame branch inspects `meta.model` / `meta.modelLabel`.\n * A bare `model` field on this frame would OVERWRITE the metadata frame's\n * modelLabel and break the <ModelDisplay> badge. The `routed` prefix\n * sidesteps that collision.\n *\n * Older clients that don't recognize `kind: 'routing'` silently drop\n * the frame (the catch-all only matches frames carrying known fields\n * like `sources` / `modelLabel`).\n */\nexport interface RoutingFrame {\n kind: 'routing';\n routedComplexity: RouteComplexity;\n routedModel: string;\n routedThinkingBudget: number;\n}\n\n/**\n * Optional field on the trailing usage frame's `breakdown`. Surfaces\n * per-route attribution to the admin test console so an operator can\n * see \"this turn ran on Haiku/trivial/$0.0005\" without re-parsing the\n * stream. Additive: clients that ignore unknown keys keep working.\n */\nexport interface RoutedAnswerBreakdown {\n model: string;\n complexity: RouteComplexity;\n thinkingBudget: number;\n}\n\n// =============================================================================\n// Leading frames\n// =============================================================================\n\n/** First frame on every stream — lets the shell render \"Thinking…\"\n * within ~200ms, before any upstream byte arrives. */\nexport interface StatusThinkingFrame {\n status: 'thinking';\n}\n\n/** Per-turn metadata frame. The emitter sends `options.metadata`\n * verbatim. All fields optional — the decoder's catch-all treats ANY\n * frame that didn't match a `kind` discriminant as metadata-ish. */\nexport interface ChatMetadataFrame {\n /** Retrieval sources for the source-chip strip. Host-defined row\n * shape (`ChatSource` on the adapter side) — opaque at wire level. */\n sources?: unknown[];\n /** Raw model id (e.g. 'claude-sonnet-x'). Presence alone triggers the\n * client's meta merge; the value itself is not stored. */\n model?: string;\n modelLabel?: string;\n provider?: string;\n contextWindowMaxTokens?: number;\n /** Per-message viewport-positioning hint ('top' | 'bottom'). */\n scrollAnchor?: string;\n /** SERVER-minted conversation trace id (`chat_conversations.id`),\n * echoed on every turn's leading metadata frame. The client never\n * generates ids — the first turn of a session goes out id-less, the\n * server mints one and echoes it here, and the adapter persists the\n * echoed id as its ONLY local state (see\n * `chat-conversation-storage.ts` on the consumer side). */\n conversationId?: string;\n}\n\n/** Live adaptive-thinking delta. WIRE IS ALREADY DELTA (Anthropic\n * thinking_delta slices) — decoders must NOT diff or re-accumulate. */\nexport interface ThinkingDeltaFrame {\n kind: 'thinking-delta';\n text: string;\n}\n\n/** Usage frame emitted as a LEADING frame right after Anthropic's\n * message_start (stage='start'). */\nexport interface UsageStartFrame {\n kind: 'usage';\n stage: 'start';\n input_tokens: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n}\n\n/** Model preamble prose written BEFORE a tool_use block — emitted as a\n * standalone leading frame so it isn't stranded behind the approval\n * card (the `\\x1E` sentinel is suppressed on tool turns). */\nexport interface TextLeadingFrame {\n kind: 'text-leading';\n text: string;\n}\n\n/** Tool invocation failed server-side (proposal persist error,\n * validation, ownership denial …). */\nexport interface ToolErrorFrame {\n kind: 'tool_error';\n toolName?: string;\n toolUseId?: string;\n message: string;\n}\n\nexport interface ApprovalRequestField {\n label: string;\n value: string;\n}\n\n/** The model called a write tool; the server persisted a proposal and\n * ships a card-ready payload. Shape mirrors the hub's `onToolUse`\n * return in `lib/data/doc-chat-utils.ts`. */\nexport interface ApprovalRequestFrame {\n kind: 'approval_request';\n proposalId: string;\n toolName: string;\n title?: string;\n fields?: ApprovalRequestField[];\n preamble?: string | null;\n args?: Record<string, unknown>;\n expiresAt?: string;\n ttlSeconds?: number;\n}\n\n/** A single write-tool turn produced MULTIPLE proposals (batch\n * operations — \"delete both\", \"close all three\"). The SERVER is the\n * authority on the grouping: it collects the turn's proposals and\n * ships ONE batch frame instead of N `approval_request` frames, so\n * clients render the bulk-approval component without any adjacency\n * heuristics. Single-proposal turns keep the classic\n * `approval_request` frame. */\nexport interface ApprovalBatchFrame {\n kind: 'approval_batch';\n /** Stable batch anchor — client-side only (locates the batch segment\n * for status flips). MUST NOT equal any row's `proposalId`: the\n * click-time optimistic status flip keys on this id, and an anchor\n * that doubles as a row id would falsely tick that row's execution\n * as succeeded before its confirm ran (masking a failed write). The\n * server emits `batch:<first proposalId>`. */\n batchId: string;\n proposals: Array<{\n proposalId: string;\n toolName: string;\n title?: string;\n fields?: ApprovalRequestField[];\n expiresAt?: string;\n ttlSeconds?: number;\n }>;\n}\n\n/** Server-driven post-approve / post-reject frame (confirm-tool route).\n * Mirror of the hub's `DecisionResolvedFrame` in\n * `lib/data/chat-agent-utils.ts` (tool_name widened to string — the\n * known-write-tool whitelist is host-side knowledge). */\nexport interface DecisionResolvedFrame {\n kind: 'decision_resolved';\n /** The proposal this decision resolved — locates the SOURCE approval\n * card so the shell can flip its status + append the receipt. */\n proposalId: string;\n ok: boolean;\n action: 'approved' | 'rejected';\n tool_name?: string;\n result?: {\n ticket_id?: string;\n status?: string | null;\n mirror_synced?: boolean;\n };\n /** True when the server WILL pipe an auto-continuation turn after\n * this frame. */\n willAutoContinue: boolean;\n /** Pre-rendered receipt copy from the source strategy's\n * `receiptRenderer` — client renders verbatim. */\n receiptText?: string;\n}\n\n/** Every leading frame the emitter can produce. */\nexport type SseLeadingFrame =\n | StatusThinkingFrame\n | ChatMetadataFrame\n | RoutingFrame\n | ThinkingDeltaFrame\n | UsageStartFrame\n | TextLeadingFrame\n | ToolErrorFrame\n | ApprovalRequestFrame\n | ApprovalBatchFrame\n | DecisionResolvedFrame;\n\n// =============================================================================\n// Trailing usage frame\n// =============================================================================\n\n/** Card/chip emission counts for admin adoption tooling. */\nexport interface UsageTelemetry {\n cards: number;\n chips: number;\n sentences: number;\n answerLen: number;\n}\n\n/** Trailing usage frame — `\\x1F` + JSON, runs to stream end (no\n * terminator). `stage: 'display'` is a legacy alias the decoder treats\n * identically to `'end'`. */\nexport interface SseTrailingUsageFrame {\n kind: 'usage';\n stage: 'end' | 'display';\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n hit_rate_pct?: number;\n telemetry?: UsageTelemetry;\n /** Cross-call usage breakdown (Haiku rewriter / classifier /\n * summarizer + routed-answer attribution). */\n breakdown?: {\n haikuRewriter?: { input: number; output: number };\n haikuClassifier?: { input: number; output: number };\n haikuSummarizer?: { input: number; output: number };\n routedAnswer?: RoutedAnswerBreakdown;\n };\n /** Gated deep-debug payload (only present when the request carried\n * the privileged debug secret). */\n debug?: Record<string, unknown>;\n}\n","/**\n * Normalized chat stream events — the transport-agnostic event union BOTH\n * decoders (SSE byte framing in `./decode.ts`, NATS chunks in\n * `./nats-decoder.ts`) emit. Phase 3 of the chat unification makes the\n * reducer consume these directly; today the SSE adapter maps them back to\n * legacy `MessageSegment` yields.\n *\n * Server-safe: no React, no browser APIs.\n */\n\n// The ask card's option shape is the SEGMENT's shape — the decoder hands the\n// rows straight to the accumulator, so restating them here would be two\n// declarations of one wire contract. Type-only import from a React-free\n// module; `nats-decoder.ts` already depends on the same file for MESSAGE_TYPE.\nimport type { ChatRef } from '../components/chat/chat-ref.types';\nimport type { AskOptionData, ChatSource } from '../components/chat/types/message.types';\n// The wire-frame shapes these events carry through are defined ONCE in\n// `./frames.ts` — reuse them here rather than restating their fields.\nimport type { ApprovalRequestField, DecisionResolvedFrame, UsageTelemetry } from './frames';\n\n/** Optional envelope on every event. `seq` carries the transport's\n * stream sequence (JetStream `streamSeq` on NATS; unused on SSE). */\ninterface ChatStreamEventBase {\n seq?: number;\n}\n\n/** Stream entered answer-text mode (SSE: the `\\x1E` sentinel or the\n * JSON-parse-failure fallback; NATS: MESSAGE_START).\n * `implicit` is true ONLY for the SSE fallback path where a\n * non-JSON leading buffer flips straight into text mode — consumers\n * that flush coalesced thinking on turn-start must SKIP the flush for\n * implicit starts (legacy adapter parity: the fallback path never\n * flushed pending thinking before the text). */\nexport interface TurnStartEvent extends ChatStreamEventBase {\n type: 'turn-start';\n implicit?: boolean;\n}\n\nexport interface TurnEndEvent extends ChatStreamEventBase {\n type: 'turn-end';\n}\n\n/** Answer text delta. `leading: true` marks SSE `text-leading` frames\n * (model preamble prose emitted BEFORE the tool approval card). */\nexport interface TextDeltaEvent extends ChatStreamEventBase {\n type: 'text-delta';\n text: string;\n leading?: boolean;\n}\n\n/** Thinking delta — APPEND-ONLY contract: each event carries the next\n * verbatim slice; consumers accumulate. Decoders never diff. */\nexport interface ThinkingDeltaEvent extends ChatStreamEventBase {\n type: 'thinking-delta';\n text: string;\n}\n\n/** Clarification card (NATS `ASK` chunk) — the assistant asking which reading\n * of an ambiguous question to answer. NOT a delta: the chunk carries the\n * finished card, so consumers push it whole instead of coalescing. `text` is\n * the intro sentence the same chunk rides along with; consumers render it as\n * ordinary answer text BEFORE the card. NATS-only — the SSE frame grammar has\n * no ask frame — but it lives in the shared union because the reducer is\n * transport-agnostic. */\nexport interface AskEvent extends ChatStreamEventBase {\n type: 'ask';\n text?: string;\n question: string;\n options: AskOptionData[];\n}\n\nexport interface StatusEvent extends ChatStreamEventBase {\n type: 'status';\n phase: 'thinking';\n}\n\n/** NATS tool execution progress (EXECUTING_TOOL / EXECUTED_TOOL). */\nexport interface ToolExecutionEvent extends ChatStreamEventBase {\n type: 'tool-execution';\n data: {\n type: 'EXECUTING_TOOL' | 'EXECUTED_TOOL';\n integratedToolType: string;\n toolFunction: string;\n toolTitle?: string;\n /**\n * Human-readable \"why this tool is running\" line, rendered as the body of\n * the tool card. Carried by the EXECUTING chunk ONLY — the EXECUTED one\n * never repeats it, and the accumulator's merge keeps whatever the\n * EXECUTING segment held (`toolData.toolExplanation ?? existing`). Omitting\n * it from this contract blanks the card for the whole run, live and on\n * replay, which is exactly what happened while it was missing here.\n */\n toolExplanation?: string;\n parameters?: Record<string, unknown>;\n result?: string;\n success?: boolean;\n toolExecutionRequestId?: string;\n };\n}\n\n/** Single tool call inside a batch approval request (NATS). */\nexport interface ApprovalToolCall {\n toolExecutionRequestId: string;\n toolName: string;\n toolTitle?: string;\n toolExplanation?: string;\n toolType?: string;\n requiresApproval: boolean;\n approvalType?: string | null;\n toolCallArguments?: Record<string, unknown> | null;\n}\n\n/** A tool call awaits user approval. SSE fills `command`/`fields`\n * (card-ready payload); NATS fills `command`/`explanation` or\n * `toolCalls` (batch form). */\nexport interface ApprovalRequestEvent extends ChatStreamEventBase {\n type: 'approval-request';\n requestId: string;\n /** SSE: the write tool's name. NATS: approval tier (USER/ADMIN/…). */\n approvalType?: string;\n command?: string;\n explanation?: string;\n fields?: ApprovalRequestField[];\n toolCalls?: ApprovalToolCall[];\n status?: 'pending';\n}\n\n/** An approval request was resolved (SSE `decision_resolved` frame /\n * NATS APPROVAL_RESULT chunk). */\nexport interface ApprovalResolvedEvent extends ChatStreamEventBase {\n type: 'approval-resolved';\n requestId?: string;\n status: 'approved' | 'rejected';\n ok?: boolean;\n toolName?: string;\n approvalType?: string;\n resolvedByName?: string | null;\n receiptText?: string;\n result?: DecisionResolvedFrame['result'];\n willAutoContinue?: boolean;\n}\n\n/** The client is offered a handoff of this ticket to a human technician\n * (NATS `ESCALATION_OFFER` chunk in state PENDING). Distinct from\n * `approval-request`: it resolves through the ticket-escalation mutations,\n * never the tool-approval flow, and it is raised by four different origins\n * (Fae's own tool call, the client's header button, a deterministic\n * trigger, or a deferred surfacing at turn end) that all render alike. */\nexport interface EscalationOfferEvent extends ChatStreamEventBase {\n type: 'escalation-offer';\n offerId: string;\n text: string;\n origin?: string;\n}\n\n/** Wire vocabulary of `EscalationOfferData.state`. */\nexport const ESCALATION_STATE = {\n PENDING: 'PENDING',\n APPROVED: 'APPROVED',\n DECLINED: 'DECLINED',\n SUPERSEDED: 'SUPERSEDED',\n} as const;\n\n/** Terminal wire state → the shared `ChatApprovalStatus` vocabulary; `null`\n * for PENDING or anything unrecognized. Shared by BOTH decoders (live\n * chunks and persisted rows) so a state can never mean two things. */\nexport function escalationResolvedStatus(state: unknown): EscalationOfferResolvedEvent['status'] | null {\n switch (state) {\n case ESCALATION_STATE.APPROVED:\n return 'approved';\n case ESCALATION_STATE.DECLINED:\n return 'rejected';\n case ESCALATION_STATE.SUPERSEDED:\n return 'cancelled';\n default:\n return null;\n }\n}\n\n/** An escalation offer reached a terminal state. The wire's SUPERSEDED\n * (the client typed over a pending offer) maps onto `cancelled` so the\n * whole stack keeps ONE status vocabulary (`ChatApprovalStatus`). The\n * resolved chunk carries no text — consumers flip the existing block. */\nexport interface EscalationOfferResolvedEvent extends ChatStreamEventBase {\n type: 'escalation-offer-resolved';\n offerId: string;\n status: 'approved' | 'rejected' | 'cancelled';\n resolvedByName?: string | null;\n}\n\n/** The conversation was handed off to a human technician (`TICKET_ESCALATED`).\n * A first-class block rather than something inferred from an offer's state,\n * so paths that raise no offer at all — the inactivity auto-escalation — still\n * produce a receipt. `text` is nullable on the wire; consumers supply the\n * fallback copy. */\nexport interface TicketEscalatedEvent extends ChatStreamEventBase {\n type: 'ticket-escalated';\n ticketId: string;\n ticketNumber?: number;\n reason: string;\n text?: string;\n}\n\n/** Ticket lifecycle receipt (`TICKET_EVENT`) — the ticket was resolved,\n * reopened, etc. `kind` is an OPEN vocabulary (RESOLVED/REOPENED today):\n * an unknown kind still decodes and renders as a neutral line rather than\n * being dropped, so the backend can add kinds without a client release.\n * Arrives standalone (outside MESSAGE_START/END), like `ticket-escalated`. */\nexport interface TicketEventEvent extends ChatStreamEventBase {\n type: 'ticket-event';\n kind: string;\n actorId?: string;\n actorName?: string;\n /** Who acted — e.g. an AI agent vs a human technician. Open string. */\n actorType?: string;\n reason?: string;\n /** Kind-token the ticket reopened INTO (AI_ASSISTANCE / TECH_REQUIRED / ...). */\n targetStatusKind?: string;\n}\n\n/**\n * Per-answer source metadata — the documents an answer cited, plus the video\n * and entity-card references its `[card://type:id]` markers expand with.\n *\n * Unlike `ChatMetadataEvent`, this one is DECODED, not passthrough: the payload\n * comes from a remote MCP server's tool output rather than from our own\n * backend's frame, so it is validated at the protocol boundary (see\n * `source-metadata.ts`) and never reaches a consumer half-checked.\n *\n * Both fields are optional and never both absent — the decoder returns `null`\n * instead of emitting an empty event, so applying one can't blank metadata an\n * earlier chunk of the same turn already supplied.\n */\nexport interface SourcesEvent extends ChatStreamEventBase {\n type: 'sources';\n sources?: ChatSource[];\n refs?: ChatRef[];\n}\n\n/** Per-turn metadata. Raw wire values pass through UNVALIDATED — the\n * consumer replicates the legacy truthiness/typeof gates (so a\n * malformed frame degrades identically to the pre-SSOT parser). */\nexport interface ChatMetadataEvent extends ChatStreamEventBase {\n type: 'metadata';\n provider?: string | null;\n modelLabel?: string | null;\n /** Raw model id (SSE `meta.model`; NATS `modelName`). */\n modelName?: string | null;\n contextWindowMaxTokens?: number | null;\n sources?: unknown;\n scrollAnchor?: unknown;\n /** Server-minted conversation id (`ChatMetadataFrame.conversationId`),\n * passed through raw like every other catch-all field — the consumer\n * applies the `typeof === 'string' && truthy` gate. */\n conversationId?: string | null;\n routing?: {\n routedComplexity: string;\n routedModel?: string;\n routedThinkingBudget: number | null;\n };\n}\n\n/** SSE usage frames — raw wire keys (snake_case) preserved. */\nexport interface UsageEvent extends ChatStreamEventBase {\n type: 'usage';\n stage: 'start' | 'end';\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n hit_rate_pct?: number;\n telemetry?: UsageTelemetry;\n /** Left OPAQUE on purpose: the consumer (`chat-stream-reducer`'s\n * `applySseUsage`) re-validates every nested field with the legacy\n * truthiness/typeof gates, so a malformed frame degrades identically\n * to the pre-SSOT parser. Typing it would imply guarantees the wire\n * does not make. */\n breakdown?: unknown;\n debug?: unknown;\n}\n\n/** NATS TOKEN_USAGE chunk (camelCase backend shape). */\nexport interface TokenUsageEvent extends ChatStreamEventBase {\n type: 'token-usage';\n inputTokensSize: number;\n outputTokensSize: number;\n totalTokensSize: number;\n contextSize: number;\n}\n\nexport interface CompactionEvent extends ChatStreamEventBase {\n type: 'compaction';\n phase: 'start' | 'end';\n summary?: string;\n}\n\nexport interface ErrorEvent extends ChatStreamEventBase {\n type: 'error';\n title: string;\n details?: string;\n}\n\n/**\n * Decode {@link ErrorEvent.details} into the sub-line an error card renders.\n *\n * `details` is EITHER a JSON envelope (`{ error: { message } }`) or an already-\n * human string, and both the live reducer and the history replay have to agree\n * on which — a card that reads one way live and another on refresh is the whole\n * class of bug this module's shared decoders exist to prevent. Lives here,\n * beside the event it decodes, because it is the wire contract, not a rendering\n * choice.\n *\n * Gates:\n * - not JSON → the raw string (it was already human-readable)\n * - JSON, string message → that message\n * - JSON, anything else → `undefined`, so the card shows its title alone\n *\n * That last gate is the fix for a real defect: the message was previously\n * copied out of the envelope UNCHECKED, so a server that nested an object\n * under `error.message` put that object into a `string` field and the card\n * rendered the literal text \"[object Object]\" under the title. Valid JSON that\n * simply lacks `error.message` still yields `undefined` (unchanged) rather than\n * dumping the raw envelope at the user.\n */\nexport function errorDetailsMessage(details: string | undefined): string | undefined {\n if (!details) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(details);\n } catch {\n return details;\n }\n if (typeof parsed !== 'object' || parsed === null) return undefined;\n const error: unknown = (parsed as { error?: unknown }).error;\n if (typeof error !== 'object' || error === null) return undefined;\n const message: unknown = (error as { message?: unknown }).message;\n return typeof message === 'string' ? message : undefined;\n}\n\n/** Non-assistant message on the stream (NATS): the user's own message\n * echo (`message-request`), an operator direct message, or a system\n * line. */\n/**\n * Dialog participant owner type. The two members below are the ones clients\n * BRANCH ON (admin-vs-not decides echo dedup and author styling); the union\n * stays open (`string & {}`) because the wire may carry other roles that the\n * client treats as \"not admin\". Always compare against `CHAT_OWNER_ADMIN`,\n * never a bare `'ADMIN'` literal — a typo in a literal silently disables\n * dedup instead of failing to compile.\n */\nexport type ChatOwnerType = 'ADMIN' | 'CLIENT' | (string & {});\n\n/** Canonical ADMIN owner-type token (see `ChatOwnerType`). */\nexport const CHAT_OWNER_ADMIN = 'ADMIN';\n\nexport interface ParticipantEvent extends ChatStreamEventBase {\n type: 'participant';\n kind: 'message-request' | 'direct-message' | 'system';\n text: string;\n ownerType?: ChatOwnerType;\n displayName?: string;\n userId?: string;\n contextItems?: Array<{ type: string; id: string; label: string }>;\n}\n\nexport interface DialogClosedEvent extends ChatStreamEventBase {\n type: 'dialog-closed';\n}\n\nexport type ChatStreamEvent =\n | TurnStartEvent\n | TurnEndEvent\n | TextDeltaEvent\n | ThinkingDeltaEvent\n | AskEvent\n | StatusEvent\n | ToolExecutionEvent\n | ApprovalRequestEvent\n | ApprovalResolvedEvent\n | EscalationOfferEvent\n | EscalationOfferResolvedEvent\n | TicketEscalatedEvent\n | TicketEventEvent\n | ChatMetadataEvent\n | SourcesEvent\n | UsageEvent\n | TokenUsageEvent\n | CompactionEvent\n | ErrorEvent\n | ParticipantEvent\n | DialogClosedEvent;\n","/**\n * SSE wire-protocol encoders — the emit side of the framing contract in\n * `./frames.ts`. Server-safe (TextEncoder only). The hub's stream route\n * consumes these so emitter and decoder can never drift.\n */\n\nimport {\n FRAME_TERMINATOR,\n END_OF_LEADING,\n TRAILER_SENTINEL,\n type SseLeadingFrame,\n type SseTrailingUsageFrame,\n} from './frames';\n\nconst encoder = new TextEncoder();\n\n/** Matches every framing sentinel byte: `\\0`, `\\x1E`, `\\x1F`. THE one\n * regex — `stripSentinelBytes` is its only consumer. */\nconst SENTINEL_BYTES_RE = /[\\u0000\\u001E\\u001F]/g;\n\n/**\n * Remove the SSE framing sentinel bytes (`\\0`, `\\x1E`, `\\x1F`) from model\n * text. Exported as a STANDALONE helper — not just folded into\n * `encodeTextDelta` — because producers must strip at the point where they\n * first take custody of the text, BEFORE any local accumulation.\n *\n * Concretely: the hub's `claude-util.ts` accumulates `state.answerText` for\n * persistence + telemetry. If stripping only happened at encode time, the\n * persisted/telemetered answer would diverge, character for character, from\n * the bytes the user actually received whenever the model echoes a control\n * byte. Both sides call this, so there is exactly ONE definition of \"which\n * bytes are framing\" in the whole system — do not re-declare the regex.\n *\n * Idempotent and per-character, so it distributes over concatenation:\n * `strip(a + b) === strip(a) + strip(b)` — safe to apply per-delta or on the\n * accumulated string interchangeably.\n */\nexport function stripSentinelBytes(text: string): string {\n return text.replace(SENTINEL_BYTES_RE, '');\n}\n\n/** Encode one leading frame: JSON + `\\0` terminator. */\nexport function encodeLeadingFrame(frame: SseLeadingFrame): Uint8Array {\n return encoder.encode(JSON.stringify(frame) + FRAME_TERMINATOR);\n}\n\n/** Encode the single `\\x1E` end-of-leading-frames sentinel. */\nexport function encodeEndOfLeading(): Uint8Array {\n return encoder.encode(END_OF_LEADING);\n}\n\n/**\n * Encode a raw answer-text delta. STRIPS the framing sentinel bytes via\n * `stripSentinelBytes` so model-echoed control bytes can no longer\n * mis-frame the stream (a literal `\\x1F` in the answer used to flip the\n * client into trailer mode and swallow the rest of the answer PLUS the real\n * usage trailer — characterized by golden fixture (d)). Forward-looking\n * encoder fix only; the decoder's behavior is unchanged.\n *\n * Producers that ALSO accumulate the text locally (persistence, telemetry)\n * must call `stripSentinelBytes` on their own copy — see its docblock.\n */\nexport function encodeTextDelta(text: string): Uint8Array {\n return encoder.encode(stripSentinelBytes(text));\n}\n\n/** Encode the trailing usage frame: `\\x1F` + JSON, no terminator\n * (runs to stream end). */\nexport function encodeTrailingUsageFrame(frame: SseTrailingUsageFrame): Uint8Array {\n return encoder.encode(TRAILER_SENTINEL + JSON.stringify(frame));\n}\n","/**\n * Leading-frame → `ChatStreamEvent` table — the ONE mapping both transports read.\n *\n * The SSE decoder (`./decode.ts`) reaches it while walking the hub's byte\n * framing. It lives outside that decoder so a second transport can reuse the\n * table rather than re-implement it and drift the moment the hub adds a frame\n * kind.\n *\n * Server-safe: no React, no browser APIs.\n */\n\nimport type { ChatStreamEvent } from './events';\n\n/**\n * Escape `<` so markdown renderers that pass HTML through (rehypeRaw)\n * don't treat XML-like tokens in Claude's thinking output as elements.\n * `<` → `&lt;` preserves the visible character without breaking\n * blockquote `>` markers. Per-character, so it distributes over\n * concatenation: escape(a + b) === escape(a) + escape(b) — callers may\n * apply it per-delta or on the accumulated string interchangeably.\n */\nexport function escapeThinkingTags(text: string): string {\n return text.replace(/</g, '&lt;');\n}\n\n/** Wire string field, or `undefined` for anything else. Consumers apply\n * their own `typeof === 'string' && truthy` gate on top, so dropping a\n * wrong-typed value here is indistinguishable from passing it through. */\nfunction frameStr(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\n/** Wire number field, or `undefined` for anything else. Exported because the\n * SSE decoder's TRAILING usage frame must coerce its token counts through the\n * same gate this table already applies to the LEADING `usage:start` frame —\n * two gates would be two contracts for one wire field. */\nexport function frameNum(value: unknown): number | undefined {\n return typeof value === 'number' ? value : undefined;\n}\n\n/**\n * Map one parsed leading frame to normalized events, replicating the\n * legacy parser's else-if chain ORDER and its exact truthiness/typeof\n * gates. Frames that matched a branch but failed its inner validation\n * (e.g. `routing` without a string `routedComplexity`) produce NO event,\n * exactly like the legacy no-op.\n *\n * Every branch pushes AT MOST ONE event — `guideFrameEvent` relies on that\n * to hand a single event back to `decodeNatsChunk`'s per-chunk contract.\n */\nexport function mapLeadingFrame(meta: Record<string, unknown>, out: ChatStreamEvent[]): void {\n if (meta.status === 'thinking') {\n out.push({ type: 'status', phase: 'thinking' });\n } else if (meta.kind === 'thinking-delta' && typeof meta.text === 'string') {\n // Wire is ALREADY delta — emit verbatim, append-only contract.\n out.push({ type: 'thinking-delta', text: meta.text });\n } else if (meta.kind === 'usage' && meta.stage === 'start') {\n out.push({\n type: 'usage',\n stage: 'start',\n input_tokens: frameNum(meta.input_tokens),\n cache_read_input_tokens: frameNum(meta.cache_read_input_tokens),\n cache_creation_input_tokens: frameNum(meta.cache_creation_input_tokens),\n });\n } else if (meta.kind === 'decision_resolved' && typeof meta.action === 'string') {\n const status = meta.action === 'rejected' ? 'rejected' : 'approved';\n const toolName = typeof meta.tool_name === 'string' ? meta.tool_name : undefined;\n const result = meta.result ?? null;\n out.push({\n type: 'approval-resolved',\n status,\n ok: meta.ok === true,\n willAutoContinue: meta.willAutoContinue === true,\n ...(toolName ? { toolName } : {}),\n ...(result ? { result } : {}),\n ...(typeof meta.receiptText === 'string' ? { receiptText: meta.receiptText } : {}),\n requestId: typeof meta.proposalId === 'string' ? meta.proposalId : undefined,\n });\n } else if (meta.kind === 'approval_batch' && meta.batchId && Array.isArray(meta.proposals)) {\n // Server-grouped multi-proposal turn → ONE batch event carrying a\n // tool-call row per proposal. `toolExecutionRequestId` is the row's\n // PROPOSAL id (each row resolves through its own per-proposal\n // confirm); the batch's `requestId` is the stable anchor the shell\n // uses for status flips. Field rows ride as the row's expandable\n // args so per-proposal detail stays reachable inside the batch.\n const toolCalls = meta.proposals\n .filter((p): p is Record<string, unknown> => typeof p === 'object' && p !== null)\n .filter(p => typeof p.proposalId === 'string')\n .map(p => {\n const rawFields = Array.isArray(p.fields)\n ? (p.fields as Array<{ label?: string; value?: string }>).filter(f => f && f.label && f.value)\n : [];\n // Prefer human-readable identity labels for the row's\n // disambiguator; opaque-id labels (\"Task\", \"Ticket\") only as\n // the last-resort first field.\n const detail = rawFields.find(f => /^(title|subject|name)$/i.test(String(f.label))) ?? rawFields[0];\n const base = typeof p.title === 'string' && p.title.length > 0 ? p.title : String(p.toolName ?? 'Tool call');\n return {\n toolExecutionRequestId: String(p.proposalId),\n toolName: String(p.toolName ?? 'tool'),\n toolTitle: detail ? `${base} — ${detail.value}` : base,\n requiresApproval: true,\n toolCallArguments:\n rawFields.length > 0 ? Object.fromEntries(rawFields.map(f => [String(f.label), String(f.value)])) : null,\n };\n });\n if (toolCalls.length > 0) {\n out.push({\n type: 'approval-request',\n requestId: String(meta.batchId),\n approvalType: 'chat',\n toolCalls,\n status: 'pending',\n });\n }\n } else if (meta.kind === 'approval_request' && meta.proposalId) {\n const proposalId = String(meta.proposalId);\n const toolName = String(meta.toolName ?? 'tool');\n const headline = typeof meta.title === 'string' && meta.title.length > 0 ? meta.title : toolName;\n const rawFields = Array.isArray(meta.fields) ? (meta.fields as Array<{ label?: string; value?: string }>) : [];\n const fields: Array<{ label: string; value: string }> = [];\n for (const f of rawFields) {\n if (!f || !f.label || !f.value) continue;\n fields.push({ label: f.label, value: f.value });\n }\n out.push({\n type: 'approval-request',\n requestId: proposalId,\n approvalType: toolName,\n command: headline,\n fields,\n status: 'pending',\n });\n } else if (meta.kind === 'text-leading' && typeof meta.text === 'string') {\n out.push({ type: 'text-delta', text: meta.text, leading: true });\n } else if (meta.kind === 'tool_error') {\n const msg =\n typeof meta.message === 'string' && meta.message.length > 0\n ? meta.message\n : 'Could not complete the requested action right now.';\n out.push({ type: 'error', title: msg });\n } else if (meta.kind === 'routing') {\n if (typeof meta.routedComplexity === 'string') {\n out.push({\n type: 'metadata',\n routing: {\n routedComplexity: meta.routedComplexity,\n ...(typeof meta.routedModel === 'string' ? { routedModel: meta.routedModel } : {}),\n routedThinkingBudget: typeof meta.routedThinkingBudget === 'number' ? meta.routedThinkingBudget : null,\n },\n });\n }\n } else {\n // Catch-all metadata-ish frame. Raw values pass through UNVALIDATED\n // (possibly undefined) so the consumer can replicate the legacy\n // presence/truthiness gates exactly — including the `model`-presence\n // trigger whose value is never stored.\n out.push({\n type: 'metadata',\n sources: meta.sources,\n provider: frameStr(meta.provider),\n modelLabel: frameStr(meta.modelLabel),\n modelName: frameStr(meta.model),\n contextWindowMaxTokens: frameNum(meta.contextWindowMaxTokens),\n scrollAnchor: meta.scrollAnchor,\n conversationId: frameStr(meta.conversationId),\n });\n }\n}\n","/**\n * SSE wire-protocol decoder — a timer-free synchronous state machine,\n * mechanically extracted from `useSseChatAdapter`'s inline\n * `createDocStreamFn` parser. Byte-for-byte behavior parity with the\n * legacy parser is the contract (the golden fixtures in\n * `src/components/chat/hooks/__tests__/sse-stream-golden.test.ts` pin it\n * through the full hook path), including its quirks:\n *\n * - A leading `\\0`-terminated block that fails JSON.parse flips the\n * stream into text mode and the WHOLE buffer (including the `\\0`)\n * is emitted as answer text (legacy no-frame stream fallback). This\n * transition is marked `turn-start { implicit: true }`.\n * - In text mode only `\\x1F` is scanned — literal `\\0` / `\\x1E` bytes\n * pass through into the answer; the FIRST literal `\\x1F` flips into\n * trailer mode and everything after is captured as the trailer\n * (golden fixture (d) characterizes this mis-framing; the fix lives\n * in `encode.ts`'s sentinel stripping, NOT here).\n * - The `\\x1E` sentinel and the `\\x1F` trailer can arrive in ONE TCP\n * chunk (fixed-answer responses) — the post-sentinel slice is\n * re-scanned for the trailer sentinel.\n * - Multi-byte UTF-8 across chunk boundaries survives via a single\n * streaming TextDecoder (`{ stream: true }` on every push; no final\n * flush — a trailing partial code point is dropped, as legacy did).\n * - `end()` parses the accumulated trailer (malformed → silently\n * ignored) and drops any un-terminated leading buffer, as legacy did.\n * It is IDEMPOTENT (a deliberate deviation from legacy): repeat calls\n * emit nothing, so an adapter that ends in both its completion path\n * and its `finally` cannot double-count the usage frame.\n *\n * State flow: leading → (sentinel | parse-failure) → text → (\\x1F) →\n * trailer-accumulate → end().\n *\n * Server-safe: no React, no timers, TextDecoder only.\n */\n\nimport type { ChatStreamEvent } from './events';\nimport type { UsageTelemetry } from './frames';\nimport { FRAME_TERMINATOR, END_OF_LEADING, TRAILER_SENTINEL } from './frames';\n// The frame table lives in its own module so it is reusable outside this\n// decoder. `frameNum` comes from there too: the trailing usage frame's token\n// counts must pass the SAME typeof gate the leading `usage:start` frame's do.\nimport { mapLeadingFrame, frameNum } from './leading-frames';\n\nexport interface SseFrameDecoder {\n /** Feed raw response bytes; returns the events they produced. */\n push(bytes: Uint8Array): ChatStreamEvent[];\n /**\n * Signal end-of-stream; parses the trailing usage frame if present.\n * IDEMPOTENT — every call after the first returns `[]`. Adapters\n * routinely call this from BOTH their completion path and a `finally`,\n * and a re-emitted `usage`/`stage:'end'` event would double-count token\n * usage (displayed cost doubles).\n */\n end(): ChatStreamEvent[];\n}\n\n// `escapeThinkingTags` moved to `./leading-frames` alongside the frame table,\n// and is re-exported here: consumers have imported it from this module since it\n// was written, and the `chat-protocol` barrel re-exports it from here.\nexport { escapeThinkingTags } from './leading-frames';\n\n/** Narrow a parsed leading frame to something the frame table can read. */\nfunction isFrameObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Narrow the trailer's `telemetry` blob to a real {@link UsageTelemetry}.\n *\n * Every field of that interface is a REQUIRED `number`, so a frame carrying a\n * partial blob (or non-numeric counts) is not one — passing it through would\n * hand consumers `undefined` where the type promises a number, the same shape\n * of lie that NaN'd the `breakdown.haiku*` sums. A blob that fails the gate is\n * dropped, matching this decoder's \"malformed trailer → silently ignored\"\n * contract; nothing downstream distinguishes absent from malformed.\n */\nfunction frameTelemetry(value: unknown): UsageTelemetry | undefined {\n if (!isFrameObject(value)) return undefined;\n const { cards, chips, sentences, answerLen } = value;\n if (\n typeof cards !== 'number' ||\n typeof chips !== 'number' ||\n typeof sentences !== 'number' ||\n typeof answerLen !== 'number'\n ) {\n return undefined;\n }\n return { cards, chips, sentences, answerLen };\n}\n\nexport function createSseFrameDecoder(): SseFrameDecoder {\n const textDecoder = new TextDecoder();\n let buffer = '';\n let inText = false;\n let inTrailer = false;\n let trailerBuffer = '';\n let ended = false;\n\n function push(bytes: Uint8Array): ChatStreamEvent[] {\n const out: ChatStreamEvent[] = [];\n const chunk = textDecoder.decode(bytes, { stream: true });\n\n if (!inText) {\n buffer += chunk;\n while (!inText) {\n const recIdx = buffer.indexOf(END_OF_LEADING);\n const nullIdx = buffer.indexOf(FRAME_TERMINATOR);\n if (recIdx !== -1 && (nullIdx === -1 || recIdx < nullIdx)) {\n inText = true;\n out.push({ type: 'turn-start' });\n const after = buffer.slice(recIdx + 1);\n buffer = '';\n if (after) {\n // The `after` slice may ALSO contain the `\\x1F` trailing-\n // usage sentinel — common for fixed-answer responses where\n // the whole frame sequence arrives in ONE TCP chunk.\n const unitIdx = after.indexOf(TRAILER_SENTINEL);\n if (unitIdx === -1) {\n out.push({ type: 'text-delta', text: after });\n } else {\n const textBefore = after.slice(0, unitIdx);\n const trailerAfter = after.slice(unitIdx + 1);\n if (textBefore) {\n out.push({ type: 'text-delta', text: textBefore });\n }\n inTrailer = true;\n trailerBuffer = trailerAfter;\n }\n }\n break;\n }\n if (nullIdx === -1) break; // need more bytes\n const metaStr = buffer.slice(0, nullIdx);\n const remaining = buffer.slice(nullIdx + 1);\n let meta: unknown;\n try {\n meta = JSON.parse(metaStr);\n } catch {\n // Not JSON — start of answer body. The WHOLE buffer (including\n // the `\\0`) becomes answer text; `implicit` tells consumers\n // this was the fallback path, not the `\\x1E` sentinel.\n inText = true;\n out.push({ type: 'turn-start', implicit: true });\n if (buffer.length > 0) {\n out.push({ type: 'text-delta', text: buffer });\n buffer = '';\n }\n break;\n }\n // A JSON scalar where a frame object was expected has no fields to\n // read, so it falls through the table's catch-all exactly as an empty\n // frame does — which is what the old `any` typing produced for every\n // `meta.<field>` lookup. (`null` used to THROW here instead.)\n mapLeadingFrame(isFrameObject(meta) ? meta : {}, out);\n buffer = remaining;\n }\n } else if (inTrailer) {\n trailerBuffer += chunk;\n } else {\n // Text mode: only the `\\x1F` trailer sentinel is scanned. The\n // unconditional emit (even for an empty decode of a partial\n // multi-byte code point) mirrors the legacy per-chunk yield.\n const sepIdx = chunk.indexOf(TRAILER_SENTINEL);\n if (sepIdx === -1) {\n out.push({ type: 'text-delta', text: chunk });\n } else {\n const before = chunk.slice(0, sepIdx);\n const after = chunk.slice(sepIdx + 1);\n if (before) out.push({ type: 'text-delta', text: before });\n inTrailer = true;\n trailerBuffer = after;\n }\n }\n return out;\n }\n\n function end(): ChatStreamEvent[] {\n // Idempotency guard: a second end() must emit NOTHING. The trailer is\n // also cleared so no later push()/end() pair can replay it.\n if (ended) return [];\n ended = true;\n const out: ChatStreamEvent[] = [];\n if (trailerBuffer.length > 0) {\n const raw = trailerBuffer;\n trailerBuffer = '';\n try {\n // Off the wire this is `unknown`, NOT the frame shape it claims to be.\n // A JSON scalar (or `null`) reaches the `kind` read below as a non-\n // object and simply fails the gate — no throw, no event.\n const meta: unknown = JSON.parse(raw);\n if (isFrameObject(meta) && meta.kind === 'usage' && (meta.stage === 'end' || meta.stage === 'display')) {\n // Token counts pass `frameNum`, the SAME gate the leading\n // `usage:start` frame's counts already pass. Un-gated, a wire value\n // of the wrong type (a string count from a server that stringifies\n // big integers) landed in a `number` field and rode straight into\n // `applySseUsage`'s `event.input_tokens ?? null` — which only\n // null-checks — and on into the displayed token totals.\n out.push({\n type: 'usage',\n stage: 'end',\n input_tokens: frameNum(meta.input_tokens),\n output_tokens: frameNum(meta.output_tokens),\n cache_read_input_tokens: frameNum(meta.cache_read_input_tokens),\n cache_creation_input_tokens: frameNum(meta.cache_creation_input_tokens),\n hit_rate_pct: frameNum(meta.hit_rate_pct),\n telemetry: frameTelemetry(meta.telemetry),\n // `breakdown` and `debug` are declared `unknown` on UsageEvent on\n // purpose — `applySseUsage` re-validates every nested field — so\n // they pass through raw, exactly as before.\n breakdown: meta.breakdown,\n debug: meta.debug,\n });\n }\n } catch {\n // Malformed trailer — silently ignore (legacy parity).\n }\n }\n return out;\n }\n\n return { push, end };\n}\n","/**\n * Message-related types\n * Contains all message structures, segments, and content types\n */\n\nimport type { ChatRef } from '../chat-ref.types';\nimport type { AssistantType, AuthorType, ChatApprovalStatus, MessageOwner } from './chat.types';\n\n// ========== Message Type Definitions ==========\n\nexport const MESSAGE_TYPE = {\n TEXT: 'TEXT',\n THINKING: 'THINKING',\n ASK: 'ASK',\n /** Per-answer source metadata (documents, videos, entity cards).\n *\n * TWO names for ONE payload, on purpose. `SOURCES` is the contract the\n * backend is moving to; `GUIDE` is the envelope it ships in today, reused\n * from Guide Mode v2 so a released frontend keeps working during the v2→v3\n * rollout. Both decode through the same path, so dropping `GUIDE` later is a\n * deletion here and nothing else. */\n GUIDE: 'GUIDE',\n SOURCES: 'SOURCES',\n EXECUTING_TOOL: 'EXECUTING_TOOL',\n EXECUTED_TOOL: 'EXECUTED_TOOL',\n APPROVAL_REQUEST: 'APPROVAL_REQUEST',\n APPROVAL_RESULT: 'APPROVAL_RESULT',\n ESCALATION_OFFER: 'ESCALATION_OFFER',\n TICKET_ESCALATED: 'TICKET_ESCALATED',\n TICKET_EVENT: 'TICKET_EVENT',\n ERROR: 'ERROR',\n MESSAGE_START: 'MESSAGE_START',\n MESSAGE_END: 'MESSAGE_END',\n MESSAGE_REQUEST: 'MESSAGE_REQUEST',\n AI_METADATA: 'AI_METADATA',\n TOKEN_USAGE: 'TOKEN_USAGE',\n CONTEXT_COMPACTION_START: 'CONTEXT_COMPACTION_START',\n CONTEXT_COMPACTION_END: 'CONTEXT_COMPACTION_END',\n DIRECT_MESSAGE: 'DIRECT_MESSAGE',\n SYSTEM: 'SYSTEM',\n DIALOG_CLOSED: 'DIALOG_CLOSED',\n} as const;\n\nexport type MessageType = (typeof MESSAGE_TYPE)[keyof typeof MESSAGE_TYPE];\n\n// ========== Scroll Anchor (per-message render hint) ==========\n\n/** Per-message viewport-positioning hint sent on the per-turn metadata\n * leading frame at the START of every assistant response. The chat\n * message-list reads it to override the default `use-stick-to-bottom`\n * tail behaviour for a single message. Field is OPTIONAL — when omitted\n * (or set to `'bottom'`) the chat tails as today. Only `'top'` opts in\n * to the alternative behaviour (used by display-action answers whose\n * body is a long article and should be read top-down). */\nexport const SCROLL_ANCHOR = { TOP: 'top', BOTTOM: 'bottom' } as const;\n\nexport type ScrollAnchor = (typeof SCROLL_ANCHOR)[keyof typeof SCROLL_ANCHOR];\n\n// ========== Tool Execution Types ==========\n\nexport interface ToolExecutionData {\n type: 'EXECUTING_TOOL' | 'EXECUTED_TOOL';\n integratedToolType: string;\n toolFunction: string;\n /** Backend-issued human-readable title (mirrors `PendingToolCallData.toolTitle`). */\n toolTitle?: string;\n /**\n * Backend-issued human-readable explanation of what the tool is doing and why\n * (mirrors `PendingToolCallData.toolExplanation`). Only sent on `EXECUTING_TOOL`;\n * the accumulator restores it onto the merged `EXECUTED_TOOL` segment.\n */\n toolExplanation?: string;\n parameters?: Record<string, unknown>;\n result?: string;\n success?: boolean;\n /**\n * Backend-issued id (matches `PendingToolCallData.toolExecutionRequestId`).\n * When present, lets the accumulator merge this execution event into the\n * matching approval batch row instead of emitting a standalone segment.\n */\n toolExecutionRequestId?: string;\n}\n\n/**\n * Snapshot of an in-flight tool kept between the `EXECUTING_TOOL` and\n * `EXECUTED_TOOL` events. The backend only sends `toolTitle` on\n * `EXECUTING_TOOL`; carrying this state lets the accumulator restore it onto\n * the merged `EXECUTED_TOOL` segment instead of falling back to the raw\n * `toolFunction`.\n */\nexport interface ExecutingToolState {\n integratedToolType: string;\n toolFunction: string;\n /** Mirrors {@link ToolExecutionData.toolTitle}; absent on `EXECUTED_TOOL`. */\n toolTitle?: string;\n /** Mirrors {@link ToolExecutionData.toolExplanation}; absent on `EXECUTED_TOOL`. */\n toolExplanation?: string;\n parameters?: Record<string, unknown>;\n}\n\n// ========== Approval Request Types ==========\n\nexport interface ApprovalRequestField {\n /** Short label — e.g. \"Subject\", \"Priority\". Rendered in a muted\n * caps style above the value. */\n label: string;\n /** Free-text value. Wraps and line-breaks are preserved\n * (`whitespace-pre-wrap`). */\n value: string;\n}\n\nexport interface ApprovalRequestData {\n command: string;\n /** Structured field list — preferred over `explanation`. When set,\n * the approval card renders a vertical label/value stack with\n * proper spacing. Falls back to `explanation` (a single paragraph)\n * when omitted. Keep BOTH when you want hosts on older lib\n * versions to still see the prose; new hosts should send only\n * `fields`. */\n fields?: ApprovalRequestField[];\n explanation?: string;\n icon?: ReactNode;\n requestId?: string;\n approvalRequestId?: string;\n approvalType?: string;\n}\n\nexport interface ApprovalResultData {\n approvalRequestId: string;\n approved: boolean;\n approvalType?: string;\n /** Display name of the user who resolved the request; null/absent for system actions. */\n resolvedByName?: string | null;\n}\n\n// ========== Escalation Offer Types ==========\n\nexport interface EscalationOfferData {\n offerId: string;\n /** Backend-fixed card copy; the client never composes it. */\n text: string;\n /** Why the offer was raised (`TOOL`, `MANUAL`, a trigger reason). Carried\n * for telemetry — every origin renders the same card. */\n origin?: string;\n}\n\n// ========== Ticket Escalated Types ==========\n\n/** Open union: the wire currently defines only `INACTIVITY`, and the block\n * renders from `text` rather than branching on this, so a reason added\n * server-side needs no client change. */\nexport type TicketEscalationReason = 'INACTIVITY' | (string & {});\n\n/** The handoff receipt — a real block on the wire, not something the client\n * infers from an escalation offer's state. */\nexport interface TicketEscalatedData {\n ticketId: string;\n ticketNumber?: number;\n reason: TicketEscalationReason;\n /** Backend-authored explanation; the card falls back to generic copy when\n * the wire omits it (the field is nullable). */\n text?: string;\n}\n\n// ========== Ticket Event Types ==========\n\n/** Known `TicketEventData.kind` values. The vocabulary is OPEN on the wire:\n * an unknown kind still renders (as a neutral info line) rather than being\n * dropped, so the backend can add lifecycle kinds without a client release. */\nexport const TICKET_EVENT_KIND = {\n RESOLVED: 'RESOLVED',\n REOPENED: 'REOPENED',\n} as const;\n\nexport type TicketEventKind = (typeof TICKET_EVENT_KIND)[keyof typeof TICKET_EVENT_KIND] | (string & {});\n\n/** Ticket lifecycle receipt (`TICKET_EVENT`) — the ticket was resolved,\n * reopened, etc. Same field names live (NATS chunk) and persisted\n * (GraphQL `TicketEventData` inside `messageData`), so ONE mapper covers\n * both paths. All actor fields are optional: the event stays renderable\n * when the backend can't attribute it. */\nexport interface TicketEventData {\n kind: TicketEventKind;\n actorId?: string;\n actorName?: string;\n /** Who acted — e.g. an AI agent vs a human. Open string like `kind`. */\n actorType?: string;\n reason?: string;\n /** Kind-token of the status the ticket reopened INTO (AI_ASSISTANCE /\n * TECH_REQUIRED / CUSTOM / ...) — same open vocabulary the lifecycle\n * statuses use. Picks the REOPENED card's subtitle deterministically;\n * absent on RESOLVED events and older backends. */\n targetStatusKind?: string;\n}\n\n/**\n * Single tool call inside a batch approval request.\n * Mirrors backend PendingToolCallDto.\n */\nexport interface PendingToolCallData {\n toolExecutionRequestId: string;\n toolName: string;\n toolTitle?: string;\n toolExplanation?: string;\n toolType?: string;\n requiresApproval: boolean;\n approvalType?: string | null;\n toolCallArguments?: Record<string, unknown> | null;\n}\n\n/**\n * Per-tool execution state inside an approval batch.\n * Populated by EXECUTING_TOOL / EXECUTED_TOOL chunks that carry a\n * `toolExecutionRequestId` matching one of the batch's tool calls.\n */\nexport interface ApprovalBatchExecutionState {\n status: 'executing' | 'done';\n result?: string;\n success?: boolean;\n}\n\nexport interface ApprovalBatchData {\n approvalRequestId: string;\n /** Highest approval type required across the batch (e.g. ADMIN beats CLIENT). */\n approvalType: string;\n toolCalls: PendingToolCallData[];\n /**\n * Keyed by `PendingToolCallData.toolExecutionRequestId`. Absent before\n * approval; rows without an entry render as \"queued\" (loader) once the\n * batch itself is approved.\n */\n executions?: Record<string, ApprovalBatchExecutionState>;\n}\n\n/** Approve/reject handler stamped onto approval segments. MAY resolve a\n * boolean success flag — `false` means the confirm FAILED (expired\n * proposal, network error); batch approve-all loops use it to tick the\n * row's failure cross. `void` (legacy transports) is treated as\n * success. Single source of truth for this signature — component\n * props, accumulator callbacks, and adapters all reference it. */\nexport type ApprovalResolutionHandler = (requestId?: string) => void | boolean | Promise<void | boolean>;\n\n// ========== Message Segment Types ==========\n\nexport type TextSegment = {\n type: 'text';\n text: string;\n};\n\nexport type ThinkingSegment = {\n type: 'thinking';\n text: string;\n};\n\n/** One reading the assistant offers in an `ask` card. `label` is BOTH the row's\n * headline and the exact text sent back when the row is picked — the backend's\n * guide classifier resolves the user's next message against the labels it\n * offered, so the reply must be the label verbatim. `description` is a short\n * clarifying line rendered under it. */\nexport type AskOptionData = {\n label: string;\n description?: string;\n};\n\n/** Clarification card — the assistant asking WHICH reading of an ambiguous\n * question it should answer, rendered as a heading plus a list of clickable\n * options instead of prose bullets. NATS-only (the `ASK` chunk); the intro\n * sentence riding the same chunk becomes an ordinary `text` segment in front\n * of the card, so it goes through the normal markdown body pipeline. Unlike\n * the three delta streams an ask arrives whole — it is never coalesced. */\nexport type AskSegment = {\n type: 'ask';\n question: string;\n options: AskOptionData[];\n};\n\nexport type ToolExecutionSegment = {\n type: 'tool_execution';\n data: ToolExecutionData;\n};\n\nexport type ApprovalRequestSegment = {\n type: 'approval_request';\n data: ApprovalRequestData & { approvalType?: string };\n status?: ChatApprovalStatus;\n /** Display name of the user who resolved the request; baked into the client\n * variant's full-text status pill (\"Approved by {name}\"). */\n resolvedByName?: string | null;\n onApprove?: ApprovalResolutionHandler;\n onReject?: ApprovalResolutionHandler;\n};\n\nexport type ApprovalBatchSegment = {\n type: 'approval_batch';\n data: ApprovalBatchData;\n status?: ChatApprovalStatus;\n /** Display name of the user who resolved the request; set when the batch is resolved (null/absent for system actions). */\n resolvedByName?: string | null;\n onApprove?: ApprovalResolutionHandler;\n onReject?: ApprovalResolutionHandler;\n};\n\n/**\n * Ticket-escalation offer block. Visually the client approval card (same\n * Figma component), but a SEPARATE segment type on purpose: it resolves\n * through the ticket-escalation GraphQL mutations rather than the tool\n * approval endpoint, and hosts that hide pending tool approvals from the\n * thread must still render this one inline.\n *\n * `cancelled` is the wire's SUPERSEDED — the client typed over the offer.\n */\nexport type EscalationOfferSegment = {\n type: 'escalation_offer';\n data: EscalationOfferData;\n status?: ChatApprovalStatus;\n resolvedByName?: string | null;\n onApprove?: ApprovalResolutionHandler;\n onReject?: ApprovalResolutionHandler;\n};\n\n/**\n * The conversation was handed off to a human technician. Decoded from the\n * `TICKET_ESCALATED` block, so it appears for every escalation path the\n * backend emits it for — including the inactivity auto-escalation, which\n * raises no offer and therefore has no offer state to infer from.\n */\nexport type TicketEscalatedSegment = {\n type: 'ticket_escalated';\n data: TicketEscalatedData;\n};\n\n/**\n * Ticket lifecycle receipt (resolved / reopened / an unknown future kind).\n * Arrives as a standalone `TICKET_EVENT` chunk outside MESSAGE_START/END —\n * same delivery shape as `ticket_escalated`.\n *\n * `streamSeq` is the chunk's JetStream sequence, kept ON the segment because\n * it is the event's only stable identity: the accumulator upserts on it so a\n * redelivered chunk (catch-up over hydrated history) replaces rather than\n * stacks a second card, while two genuinely distinct events of the same kind\n * (resolve → reopen → resolve) keep distinct sequences and both render.\n */\nexport type TicketEventSegment = {\n type: 'ticket_event';\n data: TicketEventData;\n streamSeq?: number;\n /** When THIS event happened (history: the persisted row's `createdAt`;\n * live: chunk arrival). Kept beside `streamSeq`, not in `data`: the\n * upsert's payload-equality fallback must keep matching a redelivered\n * chunk to its hydrated twin, and their times come from different clocks.\n * Without it the card inherits the surrounding assistant bubble's\n * timestamp — the FIRST row of the whole turn, so every lifecycle card\n * reads the same stale time. */\n occurredAt?: Date;\n};\n\nexport type ErrorSegment = {\n type: 'error';\n title: string;\n details?: string;\n};\n\nexport type ContextCompactionSegment = {\n type: 'context_compaction';\n status: 'started' | 'completed';\n summary?: string;\n};\n\nexport type MessageSegment =\n | TextSegment\n | ThinkingSegment\n | AskSegment\n | ToolExecutionSegment\n | ApprovalRequestSegment\n | ApprovalBatchSegment\n | EscalationOfferSegment\n | TicketEscalatedSegment\n | TicketEventSegment\n | ErrorSegment\n | ContextCompactionSegment;\n\nexport type MessageContent = string | MessageSegment[];\n\n// ========== Message Data Types (from GraphQL/API) ==========\n\nexport interface MessageDataBase {\n type: MessageType;\n}\n\nexport interface TextMessageData extends MessageDataBase {\n type: 'TEXT';\n text?: string;\n /** Entity references the user attached to the message. Wire shape is\n * `[{ type, id }]`; rows missing `id` are dropped by the history\n * processor, so `id` is optional here. */\n contextItems?: Array<{ type?: string; id?: string }>;\n}\n\nexport interface ThinkingMessageData extends MessageDataBase {\n type: 'THINKING';\n text?: string;\n}\n\n/**\n * A document cited by an assistant answer, rendered as a chip under it.\n *\n * Lives here, beside `Message`, because BOTH transports produce it now: the SSE\n * adapter reads it off the per-turn metadata frame, and the NATS path decodes it\n * out of a `GUIDE`/`SOURCES` chunk. It used to be declared inside\n * `use-sse-chat-adapter`, which made \"a Guide answer's citations\" structurally a\n * property of one transport — `use-sse-chat-adapter` still re-exports the name\n * so existing imports keep working.\n */\nexport interface ChatSource {\n /** 1-based citation number. `[1]` in the answer text refers to this. */\n index: number;\n name: string;\n path: string;\n documentType: string;\n externalUrl?: string;\n /** Platform that owns the destination at `externalUrl`. */\n targetPlatform?: string | null;\n /** Primary-key value for single-row chips. */\n id?: string;\n /** Per-row items for grouped chips. */\n items?: Array<{\n /** The row's own citation number. Present when the group was built\n * client-side from flat sources (`groupSourcesByTable`), so `[N]` in the\n * answer still resolves to a row; absent on a server-grouped chip, whose\n * rows share the group's one number. */\n index?: number;\n id: string;\n documentType: string;\n name: string;\n externalUrl?: string;\n targetPlatform?: string | null;\n /** In-app doc-tree path for markdown / data-room-doc rows so the\n * grouped chip's anchor can trigger an in-page doc-tree swap via\n * `handleChatNavClick` (parity with single-row chips + cards). */\n path?: string | null;\n }>;\n /** RagTableConfig.id for this source. */\n sourceRepo?: string;\n /** Optional display label override returned by the chat API. */\n label?: string;\n}\n\n/** Persisted source-metadata row. `payload` is the SAME object the live chunk\n * carries, so history and realtime share one decoder (`sourceMetadataEvent`)\n * and cannot drift apart. Left as an open record here rather than typed\n * structurally: it is unvalidated wire data, and the decoder is what turns it\n * into `ChatSource[]` / `ChatRef[]`. */\nexport interface SourceMetadataMessageData extends MessageDataBase {\n type: 'GUIDE' | 'SOURCES';\n payload?: unknown;\n}\n\n/** Persisted `ASK` row (GraphQL `AskData`). `text` is the intro sentence, which\n * history replays as a text segment ahead of the card — same split the live\n * `ASK` chunk carries. */\nexport interface AskMessageData extends MessageDataBase {\n type: 'ASK';\n text?: string;\n question?: string;\n /** WIRE shape — `label` is optional here because the backend can (and does)\n * ship label-less rows; `normalizeAskOptions` drops them, which is what\n * turns this into the card's `AskOptionData[]`. */\n options?: Array<{ label?: string; description?: string }>;\n}\n\nexport interface ExecutingToolMessageData extends MessageDataBase {\n type: 'EXECUTING_TOOL';\n integratedToolType?: string;\n toolFunction?: string;\n /** Backend-issued human-readable title (wire field, mirrors `ChunkData.title`). */\n title?: string;\n /** Backend-issued human-readable explanation (what/why) of the tool call. */\n toolExplanation?: string;\n parameters?: Record<string, unknown>;\n toolExecutionRequestId?: string;\n}\n\nexport interface ExecutedToolMessageData extends MessageDataBase {\n type: 'EXECUTED_TOOL';\n integratedToolType?: string;\n toolFunction?: string;\n /** Backend-issued human-readable title (wire field, mirrors `ChunkData.title`). */\n title?: string;\n parameters?: Record<string, unknown>;\n result?: string;\n success?: boolean;\n toolExecutionRequestId?: string;\n}\n\nexport interface ApprovalRequestMessageData extends MessageDataBase {\n type: 'APPROVAL_REQUEST';\n approvalRequestId?: string;\n approvalType?: string;\n command?: string;\n explanation?: string;\n /** Present when the approval is a batch of tool calls (new format). */\n toolCalls?: PendingToolCallData[];\n}\n\nexport interface ApprovalResultMessageData extends MessageDataBase {\n type: 'APPROVAL_RESULT';\n approvalRequestId?: string;\n approved?: boolean;\n approvalType?: string;\n /** Display name of the user who resolved the request; null/absent for system actions. */\n resolvedByName?: string | null;\n}\n\n/**\n * Persisted escalation-offer row. The PENDING row carries `text`/`origin`;\n * the resolution is a SECOND row with the same `offerId`, state\n * APPROVED/DECLINED/SUPERSEDED and no text — matched on replay by id.\n */\nexport interface EscalationOfferMessageData extends MessageDataBase {\n type: 'ESCALATION_OFFER';\n offerId?: string;\n state?: string;\n text?: string;\n origin?: string;\n resolvedByUserId?: string | null;\n resolvedByName?: string | null;\n}\n\nexport interface TicketEscalatedMessageData extends MessageDataBase {\n type: 'TICKET_ESCALATED';\n ticketId?: string;\n ticketNumber?: number;\n reason?: TicketEscalationReason;\n text?: string;\n}\n\n/** Persisted ticket lifecycle row — same field names as the live chunk\n * (see `TicketEventData`), decoded by the SAME mapper on replay. Fields are\n * wire-shaped (`| null`): every one is a nullable GraphQL scalar. */\nexport interface TicketEventMessageData extends MessageDataBase {\n type: 'TICKET_EVENT';\n kind?: string | null;\n actorId?: string | null;\n actorName?: string | null;\n actorType?: string | null;\n reason?: string | null;\n targetStatusKind?: string | null;\n}\n\nexport interface ErrorMessageData extends MessageDataBase {\n type: 'ERROR';\n error?: string;\n details?: string;\n}\n\nexport interface AIMetadataMessageData extends MessageDataBase {\n type: 'AI_METADATA';\n modelName?: string;\n providerName?: string;\n provider?: string;\n contextWindow?: number;\n}\n\nexport interface TokenUsageData {\n inputTokensSize: number;\n outputTokensSize: number;\n totalTokensSize: number;\n contextSize: number;\n}\n\nexport interface SystemMessageData extends MessageDataBase {\n type: 'SYSTEM';\n text?: string;\n}\n\n/**\n * Human-technician takeover row. A REALTIME-only concept: the history\n * processors have no branch for it, so a persisted `DIRECT_MESSAGE` row\n * contributes nothing on reload (pinned by the golden characterization test).\n * Declared here so the wire shape is nameable without `any`.\n */\nexport interface DirectMessageData extends MessageDataBase {\n type: 'DIRECT_MESSAGE';\n text?: string;\n}\n\nexport interface ContextCompactionStartMessageData extends MessageDataBase {\n type: 'CONTEXT_COMPACTION_START';\n}\n\nexport interface ContextCompactionEndMessageData extends MessageDataBase {\n type: 'CONTEXT_COMPACTION_END';\n summary?: string;\n}\n\nexport type MessageData =\n | TextMessageData\n | ThinkingMessageData\n | AskMessageData\n | SourceMetadataMessageData\n | ExecutingToolMessageData\n | ExecutedToolMessageData\n | ApprovalRequestMessageData\n | ApprovalResultMessageData\n | EscalationOfferMessageData\n | TicketEscalatedMessageData\n | TicketEventMessageData\n | ErrorMessageData\n | AIMetadataMessageData\n | SystemMessageData\n | DirectMessageData\n | ContextCompactionStartMessageData\n | ContextCompactionEndMessageData;\n\n// ========== Historical Message Types ==========\n\nexport interface HistoricalMessage {\n id: string;\n dialogId?: string;\n chatType?: string;\n createdAt: string;\n owner?: MessageOwner;\n messageData?: MessageData | MessageData[];\n /** Persisted stream sequence of this row's last chunk (the backend's\n * `lastChunkStreamSeq`). Passed through to the processed message's\n * `streamSeq` so `mergeHistoryWithRealtime` can decide coverage per-role\n * (a synthetic is covered only by a persisted row of its OWN role reaching\n * its seq). Optional — absent for rows the backend doesn't stamp (e.g. user\n * MESSAGE_REQUEST rows), which then don't participate in seq coverage. */\n lastChunkStreamSeq?: number | null;\n}\n\n// ========== Processed Message Types ==========\n\nexport interface ProcessedMessage {\n id: string;\n role: 'user' | 'assistant' | 'error'; // Limited to display roles\n content: MessageContent;\n name?: string;\n assistantType?: AssistantType;\n authorType?: AuthorType;\n timestamp: Date;\n avatar?: string;\n /** Persisted last-chunk stream sequence carried through from\n * `HistoricalMessage.lastChunkStreamSeq` (for assistant turns: the MAX\n * across the grouped rows). Hosts stamp it onto the rendered message's\n * `streamSeq` so the history/realtime merge can do per-role seq coverage.\n * Absent when the source row(s) carried no seq. */\n streamSeq?: number;\n /** Documents this answer cited — see `Message.sources`, the same field.\n * Replayed from the turn's persisted `GUIDE`/`SOURCES` row, so a reloaded\n * answer carries the citations the live one did. */\n sources?: ChatSource[];\n /** Entity references this answer's metadata described — see `Message.refs`. */\n refs?: ChatRef[];\n}\n\n// ========== Base Message Interface ==========\n\nimport type { ReactNode } from 'react';\nimport type { ChatContextItem } from './context-item.types';\n\nexport interface Message {\n id: string;\n role: 'user' | 'assistant' | 'error'; // Limited to display roles\n content: MessageContent;\n name?: string;\n assistantType?: AssistantType;\n authorType?: AuthorType;\n timestamp?: Date;\n avatar?: string | null;\n /** Highest CONTENT chunk streamSeq that composed this message. Stamped on\n * realtime synthetics so `mergeHistoryWithRealtime` can decide history\n * coverage per-message (see `MergeableChatMessage.streamSeq`). */\n streamSeq?: number;\n /** Entity-context items attached to this (user) message via the composer's\n * context picker. When present the message bubble renders the context\n * chips beneath its text (Figma node 31:28709). Optional — omitted for\n * assistant messages and turns sent without context. */\n contextItems?: ChatContextItem[];\n /** Per-message viewport-positioning hint. OPTIONAL — when omitted (the\n * default for every LLM Q&A / browse / search / find / Discuss path)\n * the chat tails as today via `use-stick-to-bottom`. Only `'top'` opts\n * in to the alternative top-anchor behaviour (display-action answers\n * whose body is a long article). The server is the sole decision-\n * maker — set on the metadata leading frame. */\n scrollAnchor?: ScrollAnchor;\n /** When true the message is part of the API conversation history (sent\n * to the LLM so it has context) but is NOT rendered in the chat UI.\n *\n * Used for \"synthetic continuation\" turns: when the user clicks Approve\n * on a tool proposal, the host auto-fires a follow-up `sendMessage`\n * with `hidden: true` carrying a directive like \"the user just\n * approved <tool>; ask follow-up questions per protocol\". The LLM's\n * response IS rendered (as a normal assistant message); only the\n * trigger prompt is suppressed so the chat reads naturally:\n *\n * user: \"open a ticket\"\n * assistant: preamble + approval card\n * [user clicks Approve]\n * assistant: \"Now to triage faster, can you share...\" ← auto-fires\n *\n * Without this flag the trigger prompt would surface as a confusing\n * bubble like \"(continue per protocol)\" between the approval card\n * and the AI's follow-up. */\n hidden?: boolean;\n /**\n * Documents this answer cited, rendered as chips beneath it.\n *\n * Per MESSAGE, not per conversation: an answer's citations belong to the\n * answer, and a thread can hold several answers that each cited something\n * different. Ordering is a rendering concern — see `splitCitedSources`.\n */\n sources?: ChatSource[];\n /**\n * Entity references this answer's metadata described — videos and cards.\n *\n * Presence here does NOT mean \"render it\": only the `[card://type:id]`\n * markers actually written into the answer text are rendered, and these\n * supply the data those markers expand with. The metadata frame routinely\n * describes more than the model chose to cite.\n */\n refs?: ChatRef[];\n}\n","/**\n * URL-safety primitives for the wire DECODER and the render-time guard.\n *\n * `UNSAFE_URL_CHARS` is shared by both: \"does this string carry characters that\n * have no business in an href\" had two copies once, and a security predicate\n * kept in sync by a comment drifts.\n *\n * `isSameOriginPath` answers \"does this path stay on our origin\" for the\n * render-time `safeHref` — the ONE gate every chat href passes. The wire decoder\n * does not judge links at all: it carries them, because resolving a link is a\n * client decision (only the rendering page knows where it is embedded), and a\n * second, stricter gate there only dropped links it could not resolve itself.\n *\n * Pure and dependency-free, so the server-safe `chat-protocol` leaf and the\n * client-side card utils can both import it directly.\n */\n\n/** Control, zero-width and line-separator characters. Never legitimate in a URL\n * we are about to render, and the classic way a payload smuggles one href past\n * a reader's eye. */\nexport const UNSAFE_URL_CHARS = /[\\u0000-\\u001f\\u007f\\u200b-\\u200d\\u2028\\u2029\\ufeff]/;\n\n/** Opaque base used only to ask the parser which origin a path lands on. */\nconst SAME_ORIGIN_PROBE = 'https://_sameorigin_.invalid';\n\n/**\n * Does this candidate stay on the CURRENT origin?\n *\n * Decided by RESOLVING it, never by inspecting the prefix. A prefix test is\n * what a reader reaches for — `startsWith('//')` — and it is not sufficient:\n * the URL parser treats a backslash as a slash in the authority position, so a\n * single-slash path can still resolve to a third-party host while passing every\n * \"starts with exactly one slash\" check. Any future parser quirk falls to the\n * same class. Asking the parser cannot disagree with what the browser will\n * actually do with the href.\n */\nexport function isSameOriginPath(candidate: string): boolean {\n try {\n return new URL(candidate, SAME_ORIGIN_PROBE).origin === SAME_ORIGIN_PROBE;\n } catch {\n return false;\n }\n}\n","/**\n * The `[card://<type>:<id>]` marker grammar — ONE definition, two readers.\n *\n * The model writes these markers into the answer body, and the metadata frame\n * describes the same references separately. Two parsers therefore have to agree\n * on what counts as a marker: the payload decoder (`source-metadata.ts`) and the\n * body renderer (`chat-message-enhanced.tsx`). When they disagree the failure is\n * silent and one-sided — the stricter side drops a reference the other accepted,\n * and the reader is left looking at raw `[card://…]` text in a sentence.\n *\n * The id half is deliberately permissive: the backend validates it as \"anything\n * but `]` and newlines\", and a client that narrows that is the side that breaks.\n * `)` is excluded on top because the body form also terminates on it (a marker\n * written as a markdown link target).\n *\n * Server-safe: no React, no browser APIs.\n */\n\n/** Entity-type half. A closed vocabulary of slug-shaped names. */\nconst CARD_TYPE = '[a-zA-Z0-9_-]+';\n\n/** Id half — see the permissiveness note above. */\nconst CARD_ID = '[^\\\\]\\\\r\\\\n)]+';\n\n/**\n * A marker standing ALONE, anchored — for validating a `ref` field in the\n * metadata payload, where the whole string must be the marker and nothing else.\n */\nexport const CARD_REFERENCE = new RegExp(`^\\\\[card://(${CARD_TYPE}):(${CARD_ID})\\\\]$`);\n\n/**\n * A marker EMBEDDED in answer text, unanchored and global.\n *\n * Terminates on `]` or `)` so a marker used as a markdown link target\n * (`[card://x:y](…)`) is matched too. Being global, it carries `lastIndex`\n * state — reset it before each scan.\n */\nexport function createCardMarkerScanner(): RegExp {\n return new RegExp(`\\\\[card://(${CARD_TYPE}):(${CARD_ID})[\\\\])]`, 'g');\n}\n\n/** One `[card://<type>:<id>]` reference, split into its two halves. */\nexport interface CardReference {\n type: string;\n id: string;\n}\n\n/**\n * Every card reference EMBEDDED in a text, in order of first appearance,\n * each once. The server reads the references an answer cites through this\n * (the hub's MCP `ask_guide` returns them as `cards[]`), so a host and the\n * body renderer can never disagree about which markers a text carries.\n */\nexport function extractCardReferences(text: string): CardReference[] {\n const seen = new Set<string>();\n const out: CardReference[] = [];\n for (const match of text.matchAll(createCardMarkerScanner())) {\n const key = `${match[1]}:${match[2]}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push({ type: match[1], id: match[2] });\n }\n return out;\n}\n","/**\n * Narrowing primitives for untrusted hub JSON — ONE owner, shared by every\n * client that talks to a hub route (`use-chat-history-hydration`, the\n * conversation-list client, the NATS decoder).\n *\n * `unwrapEnvelope` encodes the response contract in one place. The hub's\n * route-base `successResponse` is `NextResponse.json(data)` — a RAW body, which\n * is the shape these clients normally see. The `{ data }` branch is defensive,\n * for an embedder or gateway that proxies our routes inside an envelope of its\n * own; it is not the hub's shape. (A payload whose own top-level key is `data`\n * would be unwrapped by mistake — none of ours has one.)\n */\n\n/** `typeof null === 'object'`, so the null check is the whole point. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** route-base `{ data }` envelope, with a raw-body fallback. */\nexport function unwrapEnvelope(payload: unknown): unknown {\n return isRecord(payload) && 'data' in payload ? payload.data : payload;\n}\n","/**\n * Source-metadata payload → `{ sources, refs }`.\n *\n * ONE decoder for two arrival paths: the live `GUIDE`/`SOURCES` NATS chunk and\n * the persisted row of the same name in dialog history. That is the whole point\n * of putting it here rather than in either consumer — a reloaded thread has to\n * render identically to the live turn, and two parsers is exactly how that stops\n * being true.\n *\n * Everything is validated, and anything that fails is DROPPED rather than\n * repaired: this payload is assembled from a remote MCP server's tool output,\n * so a malformed row means an upstream contract slip, and rendering a chip with\n * a blank title or an unsafe href is worse than rendering nothing.\n *\n * LINKS are carried, not judged. A source's `externalUrl` is passed through as\n * the producer sent it — a root-relative path included — because resolving a\n * link is a CLIENT decision: only the page rendering the chip knows where it is\n * embedded. The chat runtime resolves a relative href against the embedder's\n * `defaultContentOrigin` in embed mode and keeps it relative in host mode\n * (`resolveHrefForRuntime`), and every chip href passes the ONE safety gate,\n * `safeHref`, at render (`buildSourceRowCta`). This decoder used to be a second,\n * STRICTER gate that dropped what it could not resolve on its own — every\n * relative link, and any `http:` one — leaving chips with a name and no\n * destination. That is how broken links were made; it is not how they were\n * prevented.\n *\n * MEDIA urls (`<video src>`, posters) are still required to be absolute https:\n * they have no render-time gate and no navigation runtime to resolve them.\n *\n * Server-safe: no React, no browser APIs beyond `URL`.\n */\n\nimport type { ChatRef } from '../components/chat/chat-ref.types';\nimport type { ChatSource } from '../components/chat/types/message.types';\nimport { UNSAFE_URL_CHARS } from '../utils/url-safety';\nimport { CARD_REFERENCE } from './card-marker';\nimport type { SourcesEvent } from './events';\nimport { isRecord } from './wire-narrow';\n\n/** YouTube ids are exactly 11 chars of the URL-safe alphabet. */\nconst YOUTUBE_ID = /^[A-Za-z0-9_-]{11}$/;\n\n/** Video URL fields carried through verbatim, once validated as https. */\nconst VIDEO_URL_KEYS = ['videoUrl', 'highlightVideoUrl', 'videoPoster', 'highlightVideoPoster'] as const;\n\nfunction text(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\n/**\n * `null` survives, anything non-string becomes `undefined`.\n *\n * The distinction is load-bearing for `targetPlatform` and `path`, where the\n * backend uses an explicit `null` to mean \"this row has no destination\" — which\n * downstream navigation treats differently from \"the field was not sent\".\n */\nfunction nullableText(value: unknown): string | null | undefined {\n return value === null ? null : text(value);\n}\n\n/** An absolute https URL, or nothing — for MEDIA sources only (see the module\n * note). Links are carried as text and judged once, at render. */\nfunction httpsUrl(value: unknown): string | undefined {\n const candidate = text(value);\n if (!candidate || UNSAFE_URL_CHARS.test(candidate)) return undefined;\n try {\n return new URL(candidate).protocol === 'https:' ? candidate : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * A YouTube video id, from either a bare id or any of the watch/short/embed URL\n * shapes. Returns the ID, never a URL — the player takes an id.\n */\nexport function youtubeVideoId(value: unknown): string | undefined {\n const candidate = text(value);\n if (!candidate) return undefined;\n if (YOUTUBE_ID.test(candidate)) return candidate;\n\n const url = httpsUrl(candidate);\n if (!url) return undefined;\n const parsed = new URL(url);\n const hostname = parsed.hostname.toLowerCase();\n const isYoutubeHost = hostname === 'youtu.be' || hostname === 'youtube.com' || hostname.endsWith('.youtube.com');\n if (!isYoutubeHost) return undefined;\n\n const fromPath =\n hostname === 'youtu.be'\n ? parsed.pathname.split('/').filter(Boolean)[0]\n : /^\\/(?:embed|v|shorts)\\/([A-Za-z0-9_-]{11})(?:\\/|$)/.exec(parsed.pathname)?.[1];\n const id = fromPath ?? parsed.searchParams.get('v') ?? undefined;\n return id && YOUTUBE_ID.test(id) ? id : undefined;\n}\n\nfunction sourceItems(value: unknown): NonNullable<ChatSource['items']> | undefined {\n if (!Array.isArray(value)) return undefined;\n const items = value.filter(isRecord).flatMap(item => {\n const id = text(item.id);\n const documentType = text(item.documentType);\n const name = text(item.name);\n // A grouped row without these three has nothing to render OR navigate with.\n if (!id || !documentType || !name) return [];\n\n const externalUrl = text(item.externalUrl);\n const targetPlatform = nullableText(item.targetPlatform);\n const path = nullableText(item.path);\n return [\n {\n id,\n documentType,\n name,\n ...(externalUrl ? { externalUrl } : {}),\n ...(targetPlatform !== undefined ? { targetPlatform } : {}),\n ...(path !== undefined ? { path } : {}),\n },\n ];\n });\n return items.length > 0 ? items : undefined;\n}\n\n/**\n * Cited documents, keyed by their citation number.\n *\n * `index` and `name` are required because they ARE the chip: the number the\n * answer's `[1]` refers to, and the text on it. `path` is NOT required — it is\n * one of several ways a chip resolves a destination (an `externalUrl` or a\n * grouped item can carry it instead), and a source is still worth showing when\n * it has none.\n *\n * A duplicate `index` keeps the first occurrence: two chips answering to the\n * same `[1]` is not a state the strip can render meaningfully.\n */\nfunction sources(value: unknown): ChatSource[] {\n if (!Array.isArray(value)) return [];\n const claimed = new Set<number>();\n const decoded: ChatSource[] = [];\n\n for (const candidate of value) {\n if (!isRecord(candidate)) continue;\n const index = candidate.index;\n const name = text(candidate.name);\n const isUsableIndex = typeof index === 'number' && Number.isInteger(index) && index > 0 && !claimed.has(index);\n if (!isUsableIndex || !name) continue;\n claimed.add(index);\n\n const path = text(candidate.path);\n const documentType = text(candidate.documentType);\n const externalUrl = text(candidate.externalUrl);\n const targetPlatform = nullableText(candidate.targetPlatform);\n const id = text(candidate.id);\n const sourceRepo = text(candidate.sourceRepo);\n const label = text(candidate.label);\n const items = sourceItems(candidate.items);\n\n decoded.push({\n index,\n name,\n // Both are typed non-optional on `ChatSource` and predate this decoder;\n // '' is the established \"no value\" for them across the SSE path.\n path: path ?? '',\n documentType: documentType ?? '',\n ...(externalUrl ? { externalUrl } : {}),\n ...(targetPlatform !== undefined ? { targetPlatform } : {}),\n ...(id ? { id } : {}),\n ...(sourceRepo ? { sourceRepo } : {}),\n ...(label ? { label } : {}),\n ...(items ? { items } : {}),\n });\n }\n return decoded;\n}\n\n/**\n * Player metadata for a video ref, in the key vocabulary the card dispatcher\n * already reads (`itemVideoMetadata` / `decodeVideoMarkerId` in\n * `entity-cards/dispatch.tsx`).\n *\n * `youtubeUrl` holds a bare video ID, not a URL. The name is inaccurate and\n * predates this decoder — but it is the established key on both the producing\n * and the consuming side, and a more honest name here would simply be a field\n * nothing reads. Renaming it is a separate change that has to move both ends at\n * once.\n *\n * `videoUrl` and `youtubeUrl` stay mutually exclusive: they select different\n * players (a `<video>`/HLS source vs an embed facade), so carrying both would\n * make the renderer guess.\n */\nfunction videoMetadata(value: unknown, url: string | null): Record<string, string> {\n const metadata: Record<string, string> = {};\n if (isRecord(value)) {\n for (const key of VIDEO_URL_KEYS) {\n const validated = httpsUrl(value[key]);\n if (validated) metadata[key] = validated;\n }\n const declaredId = youtubeVideoId(value.youtubeUrl);\n if (declaredId) metadata.youtubeUrl = declaredId;\n }\n\n // The top-level `url` is the authority when present: it is what the backend\n // resolved for THIS video, while `metadata` is passthrough from the row.\n if (url) {\n const idFromUrl = youtubeVideoId(url);\n if (idFromUrl) {\n metadata.youtubeUrl = idFromUrl;\n delete metadata.videoUrl;\n } else {\n metadata.videoUrl = url;\n delete metadata.youtubeUrl;\n }\n }\n return metadata;\n}\n\n/** Video references. `ref` must parse AND be of type `video` — a mistyped row\n * would otherwise render through the entity-card path with no player. */\nfunction videos(value: unknown): ChatRef[] {\n if (!Array.isArray(value)) return [];\n return value.filter(isRecord).flatMap(video => {\n const match = typeof video.ref === 'string' ? CARD_REFERENCE.exec(video.ref) : null;\n if (!match || match[1] !== 'video') return [];\n\n const id = match[2];\n const url = httpsUrl(video.url) ?? null;\n const sourceRepo = text(video.sourceRepo);\n const metadata = videoMetadata(video.metadata, url);\n return [\n {\n type: 'video',\n id,\n title: text(video.title) ?? id,\n url,\n ...(sourceRepo ? { sourceRepo } : {}),\n ...(Object.keys(metadata).length > 0 ? { metadata } : {}),\n },\n ];\n });\n}\n\n/**\n * Entity-card references, enriched from `sources` where the two describe the\n * same row.\n *\n * A card carries only `type` + `id` on the wire; its human title and link live\n * on the matching source. Without the match the card still renders — with its\n * id as the title, which is ugly but honest, and better than dropping a card\n * the answer text explicitly points at.\n *\n * `entityType`/`entityId` are cross-checked against the parsed `ref` because\n * they are three statements of the same fact, and a disagreement means the row\n * was assembled wrongly upstream.\n */\nfunction cards(value: unknown, decodedSources: ChatSource[]): ChatRef[] {\n if (!Array.isArray(value)) return [];\n return value.filter(isRecord).flatMap(card => {\n const match = typeof card.ref === 'string' ? CARD_REFERENCE.exec(card.ref) : null;\n // `video` is handled by `videos()`; a card claiming that type would render\n // the wrong component.\n if (!match || match[1] === 'video') return [];\n\n const type = text(card.entityType);\n const id = text(card.entityId);\n if (!type || !id || type !== match[1] || id !== match[2]) return [];\n\n const source = decodedSources.find(candidate => candidate.id === id && candidate.documentType === type);\n return [\n {\n type,\n id,\n title: source?.name ?? id,\n url: source?.externalUrl ?? null,\n ...(source?.sourceRepo ? { sourceRepo: source.sourceRepo } : {}),\n ...(source?.targetPlatform !== undefined ? { targetPlatform: source.targetPlatform } : {}),\n ...(source?.path ? { metadata: { path: source.path } } : {}),\n },\n ];\n });\n}\n\n/** Last `type:id` wins nothing — the FIRST occurrence is kept, so a payload that\n * lists a card twice cannot flip which copy the body expands to. */\nfunction deduplicateRefs(refs: ChatRef[]): ChatRef[] {\n const byIdentity = new Map<string, ChatRef>();\n for (const ref of refs) {\n const identity = `${ref.type}:${ref.id}`;\n if (!byIdentity.has(identity)) byIdentity.set(identity, ref);\n }\n return [...byIdentity.values()];\n}\n\n/**\n * Decode one source-metadata payload into its event, or `null` when there is\n * nothing usable in it.\n *\n * Returning `null` for an empty result matters: the reducer stamps this onto a\n * message, and an event carrying two empty arrays would overwrite metadata that\n * an earlier chunk of the same turn had already supplied.\n */\nexport function sourceMetadataEvent(payload: unknown): SourcesEvent | null {\n if (!isRecord(payload)) return null;\n\n const decodedSources = sources(payload.sources);\n const refs = deduplicateRefs([...videos(payload.videos), ...cards(payload.cards, decodedSources)]);\n if (decodedSources.length === 0 && refs.length === 0) return null;\n\n return {\n type: 'sources',\n ...(decodedSources.length > 0 ? { sources: decodedSources } : {}),\n ...(refs.length > 0 ? { refs } : {}),\n };\n}\n\n/** What one assistant answer carries: its citations and its expandable refs. */\nexport interface SourceMetadata {\n sources?: ChatSource[];\n refs?: ChatRef[];\n}\n\n/**\n * Fold one decoded chunk into an answer's accumulated metadata.\n *\n * A single turn can call several remote tools, each returning its own metadata,\n * so this merges rather than replaces — and it is shared by the live reducer and\n * the history replay precisely because those two must agree on what a reloaded\n * answer shows.\n *\n * First writer wins per identity (`index` for a source, `type:id` for a ref),\n * the same rule the decoder applies within one payload.\n *\n * KNOWN LIMIT: citation numbers are assigned per tool result, so two tool calls\n * in one turn can both claim `[1]`. The later source is dropped rather than\n * renumbered — by then the numbers are already written into the answer text,\n * and renumbering would point `[1]` at a document the sentence is not about.\n */\nexport function mergeSourceMetadata(previous: SourceMetadata | null, event: SourcesEvent): SourceMetadata {\n const byIndex = new Map<number, ChatSource>();\n for (const source of [...(previous?.sources ?? []), ...(event.sources ?? [])]) {\n if (!byIndex.has(source.index)) byIndex.set(source.index, source);\n }\n const byIdentity = new Map<string, ChatRef>();\n for (const ref of [...(previous?.refs ?? []), ...(event.refs ?? [])]) {\n const identity = `${ref.type}:${ref.id}`;\n if (!byIdentity.has(identity)) byIdentity.set(identity, ref);\n }\n return {\n ...(byIndex.size > 0 ? { sources: [...byIndex.values()] } : {}),\n ...(byIdentity.size > 0 ? { refs: [...byIdentity.values()] } : {}),\n };\n}\n","/**\n * NATS chunk → normalized `ChatStreamEvent` decoder.\n *\n * THE live NATS reading path — the ONLY chunk parser in the codebase. Every\n * consumer (this lib's `useNatsChatAdapter`, the hub, the product app) feeds\n * raw chunks through `decodeNatsChunk` into `createChatStreamReducer`. The\n * legacy `components/chat/utils/chunk-parser.ts` (`parseChunkToAction`) that\n * this module superseded was DELETED — do not reintroduce a second decoder.\n *\n * Chunks map onto the transport-agnostic event union, with the JetStream\n * `streamSeq` lifted into the `seq` envelope (the reducer's idempotency gate\n * keys off it).\n *\n * Behavior is pinned by `__tests__/nats-decoder-golden.test.ts`.\n *\n * Server-safe: no React, no browser APIs.\n */\n\nimport { MESSAGE_TYPE } from '../components/chat/types/message.types';\nimport type { AskOptionData } from '../components/chat/types/message.types';\nimport { ESCALATION_STATE, escalationResolvedStatus } from './events';\nimport type { ApprovalToolCall, ChatStreamEvent } from './events';\nimport { sourceMetadataEvent } from './source-metadata';\nimport { isRecord } from './wire-narrow';\n\n/** Minimal structural view of a NATS chunk (see `ChunkData` in\n * `src/components/chat/types/network.types.ts`). */\ntype NatsChunk = Record<string, unknown>;\n\n/** Wire string field, or `undefined` when the backend sent anything else. */\nfunction str(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\n/** Wire string field with a default — replaces the old `data.x || 'y'`, which\n * also let non-strings through under an `any` chunk type. */\nfunction strOr(value: unknown, fallback = ''): string {\n const s = str(value);\n return s ? s : fallback;\n}\n\n/** Wire number field with a default. */\nfunction numOr(value: unknown, fallback = 0): number {\n return typeof value === 'number' ? value : fallback;\n}\n\n/** Coerce the wire's `toolCalls[]` into the batch-approval shape, dropping\n * non-object entries and defaulting every field. */\nfunction normalizeToolCalls(raw: unknown): ApprovalToolCall[] {\n if (!Array.isArray(raw)) return [];\n return raw.filter(isRecord).map(item => ({\n toolExecutionRequestId: String(item.toolExecutionRequestId ?? ''),\n toolName: String(item.toolName ?? ''),\n toolTitle: typeof item.toolTitle === 'string' ? item.toolTitle : undefined,\n toolExplanation: typeof item.toolExplanation === 'string' ? item.toolExplanation : undefined,\n toolType: typeof item.toolType === 'string' ? item.toolType : undefined,\n requiresApproval: item.requiresApproval === true,\n approvalType: typeof item.approvalType === 'string' ? item.approvalType : null,\n toolCallArguments: isRecord(item.toolCallArguments) ? item.toolCallArguments : null,\n }));\n}\n\n/** Coerce the wire's ask `options[]` into the card's row shape. Rows without a\n * usable `label` are dropped: the label is what gets SENT when the row is\n * picked, so a blank one would post an empty reply. */\nexport function normalizeAskOptions(raw: unknown): AskOptionData[] {\n if (!Array.isArray(raw)) return [];\n return raw\n .filter(isRecord)\n .map(item => ({\n label: typeof item.label === 'string' ? item.label.trim() : '',\n description: typeof item.description === 'string' ? item.description : undefined,\n }))\n .filter(option => option.label.length > 0);\n}\n\n/**\n * Parse one raw NATS chunk into a normalized event. Returns `null` for\n * unknown/malformed chunks — an unrecognized `type`, a missing required\n * field, or a non-object payload are all tolerated as no-ops rather than\n * throwing, so a backend that adds a chunk type can't break the stream.\n */\nexport function decodeNatsChunk(chunk: unknown): ChatStreamEvent | null {\n if (!chunk || typeof chunk !== 'object') return null;\n\n const data = chunk as NatsChunk;\n const type = String(data.type || '');\n // JetStream stream sequence → the generic `seq` envelope.\n const seq: { seq?: number } = typeof data.streamSeq === 'number' ? { seq: data.streamSeq } : {};\n\n switch (type) {\n case MESSAGE_TYPE.MESSAGE_START:\n return { type: 'turn-start', ...seq };\n\n case MESSAGE_TYPE.MESSAGE_END:\n return { type: 'turn-end', ...seq };\n\n case MESSAGE_TYPE.AI_METADATA: {\n const providerName = data.providerName || data.provider;\n if (typeof data.modelName === 'string' && typeof providerName === 'string') {\n return {\n type: 'metadata',\n modelLabel: str(data.modelDisplayName),\n modelName: data.modelName,\n provider: providerName,\n contextWindowMaxTokens: typeof data.contextWindow === 'number' ? data.contextWindow : 0,\n ...seq,\n };\n }\n return null;\n }\n\n case MESSAGE_TYPE.TEXT:\n if (typeof data.text === 'string') {\n return { type: 'text-delta', text: data.text, ...seq };\n }\n return null;\n\n case MESSAGE_TYPE.THINKING:\n if (typeof data.text === 'string') {\n return { type: 'thinking-delta', text: data.text, ...seq };\n }\n return null;\n\n // Two chunk names, one payload and one decoder. `SOURCES` is the contract\n // the backend is moving to; `GUIDE` is the v2 envelope it ships in during\n // the rollout (see `MESSAGE_TYPE.GUIDE`). The chunk arrives BEFORE or\n // between the answer's text deltas, so the reducer — not this decoder —\n // owns attaching it to the right turn.\n case MESSAGE_TYPE.GUIDE:\n case MESSAGE_TYPE.SOURCES: {\n const event = sourceMetadataEvent(data.payload);\n return event ? { ...event, ...seq } : null;\n }\n\n // An ask card is only an ask card with something to pick: a question and at\n // least one option. Anything less is dropped rather than rendered as an\n // empty card the user can't answer — the backend already falls back to a\n // complete hardcoded card when the model returns a partial one.\n case MESSAGE_TYPE.ASK: {\n const question = typeof data.question === 'string' ? data.question.trim() : '';\n const options = normalizeAskOptions(data.options);\n if (!question || options.length === 0) return null;\n return {\n type: 'ask',\n ...(typeof data.text === 'string' && data.text ? { text: data.text } : {}),\n question,\n options,\n ...seq,\n };\n }\n\n case MESSAGE_TYPE.EXECUTING_TOOL:\n return {\n type: 'tool-execution',\n data: {\n type: 'EXECUTING_TOOL',\n integratedToolType: strOr(data.integratedToolType),\n toolFunction: strOr(data.toolFunction),\n toolTitle: typeof data.title === 'string' ? data.title : undefined,\n // The human-readable \"why this tool is running\" line rendered inside\n // the tool card. It rides ONLY the EXECUTING chunk — the EXECUTED one\n // never carries it, and the accumulator's merge preserves whatever\n // the EXECUTING segment held. Dropping it here therefore blanks the\n // card for the whole lifetime of the run.\n toolExplanation: typeof data.toolExplanation === 'string' ? data.toolExplanation : undefined,\n parameters: isRecord(data.parameters) ? data.parameters : undefined,\n toolExecutionRequestId:\n typeof data.toolExecutionRequestId === 'string' ? data.toolExecutionRequestId : undefined,\n },\n ...seq,\n };\n\n case MESSAGE_TYPE.EXECUTED_TOOL:\n return {\n type: 'tool-execution',\n data: {\n type: 'EXECUTED_TOOL',\n integratedToolType: strOr(data.integratedToolType),\n toolFunction: strOr(data.toolFunction),\n toolTitle: typeof data.title === 'string' ? data.title : undefined,\n parameters: isRecord(data.parameters) ? data.parameters : undefined,\n result: str(data.result),\n success: typeof data.success === 'boolean' ? data.success : undefined,\n toolExecutionRequestId:\n typeof data.toolExecutionRequestId === 'string' ? data.toolExecutionRequestId : undefined,\n },\n ...seq,\n };\n\n case MESSAGE_TYPE.APPROVAL_REQUEST: {\n const requestId = strOr(data.approvalRequestId) || strOr(data.approval_request_id);\n const approvalType = strOr(data.approvalType, 'USER');\n const toolCalls = normalizeToolCalls(data.toolCalls);\n\n if (toolCalls.length > 0) {\n return { type: 'approval-request', requestId, approvalType, toolCalls, ...seq };\n }\n\n return {\n type: 'approval-request',\n requestId,\n approvalType,\n command: strOr(data.command),\n explanation: str(data.explanation),\n ...seq,\n };\n }\n\n case MESSAGE_TYPE.APPROVAL_RESULT: {\n // Realtime chunks carry the resolver's name as `displayName`; the\n // persisted GraphQL message exposes the same value as\n // `resolvedByName`. Accept either so realtime and history-replay\n // render \"by {name}\" identically.\n const resolvedByName =\n typeof data.resolvedByName === 'string'\n ? data.resolvedByName\n : typeof data.displayName === 'string'\n ? data.displayName\n : undefined;\n return {\n type: 'approval-resolved',\n requestId: strOr(data.approvalRequestId) || strOr(data.approval_request_id),\n status: data.approved === true ? 'approved' : 'rejected',\n approvalType: strOr(data.approvalType, 'CLIENT'),\n resolvedByName,\n ...seq,\n };\n }\n\n case MESSAGE_TYPE.ESCALATION_OFFER: {\n const offerId = typeof data.offerId === 'string' ? data.offerId : '';\n if (!offerId) return null;\n if (data.state === ESCALATION_STATE.PENDING) {\n return {\n type: 'escalation-offer',\n offerId,\n text: typeof data.text === 'string' ? data.text : '',\n origin: typeof data.origin === 'string' ? data.origin : undefined,\n ...seq,\n };\n }\n const status = escalationResolvedStatus(data.state);\n if (!status) return null;\n // Same realtime/history field split as APPROVAL_RESULT: the chunk names\n // the resolver `displayName`, the persisted row `resolvedByName`.\n return {\n type: 'escalation-offer-resolved',\n offerId,\n status,\n resolvedByName:\n typeof data.resolvedByName === 'string'\n ? data.resolvedByName\n : typeof data.displayName === 'string'\n ? data.displayName\n : undefined,\n ...seq,\n };\n }\n\n case MESSAGE_TYPE.TICKET_ESCALATED: {\n const ticketId = typeof data.ticketId === 'string' ? data.ticketId : '';\n const reason = typeof data.reason === 'string' ? data.reason : '';\n // Both are non-null on the wire; a payload missing either is malformed\n // rather than a variant to render.\n if (!ticketId || !reason) return null;\n return {\n type: 'ticket-escalated',\n ticketId,\n reason,\n ticketNumber: typeof data.ticketNumber === 'number' ? data.ticketNumber : undefined,\n text: typeof data.text === 'string' ? data.text : undefined,\n ...seq,\n };\n }\n\n case MESSAGE_TYPE.TICKET_EVENT: {\n // `kind` is the only required field, and deliberately an OPEN string:\n // unknown kinds must render (as a neutral line), not be dropped.\n const kind = typeof data.kind === 'string' ? data.kind.trim() : '';\n if (!kind) return null;\n // This chunk names its own JetStream sequence `sequenceId` in the\n // payload (the persisted row's `lastChunkStreamSeq` equals it — the\n // dedupe key). Prefer the transport-stamped `streamSeq` envelope like\n // every other chunk; fall back to the payload copy when absent.\n const eventSeq: { seq?: number } =\n seq.seq !== undefined ? seq : typeof data.sequenceId === 'number' ? { seq: data.sequenceId } : {};\n return {\n type: 'ticket-event',\n kind,\n actorId: typeof data.actorId === 'string' ? data.actorId : undefined,\n actorName: typeof data.actorName === 'string' ? data.actorName : undefined,\n actorType: typeof data.actorType === 'string' ? data.actorType : undefined,\n reason: typeof data.reason === 'string' && data.reason.trim() ? data.reason : undefined,\n targetStatusKind:\n typeof data.targetStatusKind === 'string' && data.targetStatusKind.trim() ? data.targetStatusKind : undefined,\n ...eventSeq,\n };\n }\n\n case MESSAGE_TYPE.ERROR:\n return {\n type: 'error',\n title: strOr(data.error, 'An error occurred'),\n details: str(data.details),\n ...seq,\n };\n\n case MESSAGE_TYPE.MESSAGE_REQUEST:\n return {\n type: 'participant',\n kind: 'message-request',\n text: String(data.text || ''),\n ownerType: typeof data.ownerType === 'string' ? data.ownerType : undefined,\n displayName: typeof data.displayName === 'string' ? data.displayName : undefined,\n userId: typeof data.userId === 'string' ? data.userId : undefined,\n // Wire shape carries no label; fall back to the id (parity with\n // the history path in `process-historical-messages.ts`).\n contextItems: Array.isArray(data.contextItems)\n ? (data.contextItems as Array<{ type?: unknown; id?: unknown }>)\n .filter(it => typeof it?.type === 'string' && typeof it?.id === 'string')\n .map(it => ({\n type: it.type as string,\n id: it.id as string,\n label: it.id as string,\n }))\n : undefined,\n ...seq,\n };\n\n case MESSAGE_TYPE.TOKEN_USAGE:\n return {\n type: 'token-usage',\n inputTokensSize: numOr(data.inputTokensSize),\n outputTokensSize: numOr(data.outputTokensSize),\n totalTokensSize: numOr(data.totalTokensSize),\n contextSize: numOr(data.contextSize),\n ...seq,\n };\n\n case MESSAGE_TYPE.CONTEXT_COMPACTION_START:\n return { type: 'compaction', phase: 'start', ...seq };\n\n case MESSAGE_TYPE.CONTEXT_COMPACTION_END:\n return {\n type: 'compaction',\n phase: 'end',\n summary: typeof data.text === 'string' ? data.text : undefined,\n ...seq,\n };\n\n case MESSAGE_TYPE.SYSTEM:\n if (typeof data.text === 'string') {\n return { type: 'participant', kind: 'system', text: data.text, ...seq };\n }\n return null;\n\n case MESSAGE_TYPE.DIRECT_MESSAGE:\n if (typeof data.text === 'string') {\n return {\n type: 'participant',\n kind: 'direct-message',\n text: data.text,\n ownerType: typeof data.ownerType === 'string' ? data.ownerType : undefined,\n displayName: typeof data.displayName === 'string' ? data.displayName : undefined,\n userId: typeof data.userId === 'string' ? data.userId : undefined,\n ...seq,\n };\n }\n return null;\n\n case MESSAGE_TYPE.DIALOG_CLOSED:\n return { type: 'dialog-closed', ...seq };\n\n default:\n return null;\n }\n}\n","/**\n * The hub's confirm-tool contract — request shape and error envelope.\n *\n * Resolving a Product Guide proposal is a POST to `endpoints.approvalToolUrl`\n * whose response is an ordinary guide stream (a `decision_resolved` frame, then\n * the hub's auto-continuation). Two callers make that call today: the SSE chat\n * adapter, and any host replaying the same route into a different transport's\n * conversation (the product app resolves guide cards inside a NATS Mingo\n * dialog). They MUST send byte-identical bodies — a proposal is single-use, so a\n * caller that drifts on a field name does not degrade, it fails the write — and\n * they must surface the same server copy, because the hub's errors are written\n * for the end user (\"This approval expired — ask again to get a fresh one\").\n *\n * Hence both live here rather than at either call site.\n *\n * Server-safe: no React, no browser APIs.\n */\n\n/** What the user did with the card. */\nexport type ApprovalToolAction = 'approve' | 'reject';\n\n/** Client-side shape of a confirm; `buildConfirmToolBody` renames it to the\n * wire's own casing so no caller hand-writes `proposal_id`. */\nexport interface ConfirmToolRequest {\n /** The hub-minted proposal id carried by the card. */\n proposalId: string;\n action: ApprovalToolAction;\n /**\n * The hub's conversation id, quoted back verbatim. The hub rejects a confirm\n * without it: a proposal only means something inside the conversation that\n * minted it.\n */\n conversationId: string | null | undefined;\n}\n\n/** Build the confirm-tool request body. The ONE place that knows the wire\n * spells the id `proposal_id` while everything else is camelCase. */\nexport function buildConfirmToolBody(request: ConfirmToolRequest): Record<string, unknown> {\n return {\n proposal_id: request.proposalId,\n action: request.action,\n conversationId: request.conversationId,\n };\n}\n\n/**\n * Pull the user-facing copy out of a failed chat/confirm response.\n *\n * The route-base envelope is `{error, code}`; `error` is written for the end\n * user, so it beats any status-code copy the client could invent. Returns null\n * when the body is not that envelope (HTML error page, empty body, gateway\n * response) — the caller then supplies its own generic line.\n *\n * Consumes the body: call it once, on a response you have already decided is a\n * failure.\n */\nexport async function readServerErrorMessage(response: { json: () => Promise<unknown> }): Promise<string | null> {\n try {\n const body = (await response.json()) as { error?: unknown } | null;\n if (typeof body?.error === 'string' && body.error.length > 0) return body.error;\n } catch {\n /* non-JSON error body — the caller falls through to its generic copy */\n }\n return null;\n}\n","/**\n * `normalizeIpForBucketKey` — THE cross-repo normalizer for turning a raw\n * client-IP candidate (`x-forwarded-for` hop, socket remote address, proxy\n * header) into a stable rate-limit / dedup bucket key.\n *\n * WHY IT LIVES HERE: producer and consumer sit in DIFFERENT repos (the app\n * emits the candidate, the hub buckets it) and each had grown its own\n * normalizer. They disagreed — one unwrapped `::ffff:1.2.3.4` to the bare\n * IPv4 and stripped `%zone`, the other kept the zone and only lower-cased the\n * mapped form — so ONE visitor could land in TWO buckets depending on which\n * side normalized. Both repos already depend on this package, and\n * `./chat-protocol` is its server-safe subpath (no React, no browser APIs),\n * so this is the one place both sides can share.\n *\n * Contract:\n * - input longer than `IP_BUCKET_KEY_MAX_LENGTH` → `null` (an unbounded\n * header must never become an unbounded map key);\n * - `[…]` brackets and a trailing `]:port` are stripped, as is a bare\n * `:port` on an IPv4 (`203.0.113.4:8080`) — an unbracketed IPv6 with a\n * bare port stays ambiguous and is not stripped;\n * - a `%zone` suffix is stripped (link-local scope is per-host, not part of\n * the peer's identity);\n * - IPv4-mapped IPv6 (`::ffff:203.0.113.4`) collapses to the bare IPv4, so\n * the same peer buckets identically over either stack;\n * - IPv6 is lower-cased;\n * - IPv4 octets are range-checked and REJECTED when zero-padded (`.04`\n * would otherwise bucket separately from `.4`), and IPv6 is\n * charset/shape-checked (one `::` at most, ≤8 groups, an embedded IPv4\n * only in the last group, no dangling separator); anything else returns\n * `null` rather than a junk bucket key.\n *\n * It does NOT canonicalize IPv6 zero-compression (`2001:db8::1` vs\n * `2001:0db8:0:0:0:0:0:1`) — peers do not spell their own address two ways\n * within one deployment, and a full expander is more surface than the bucket\n * key warrants.\n */\n\n/** Longest accepted candidate. A full IPv6 + zone fits well inside this. */\nexport const IP_BUCKET_KEY_MAX_LENGTH = 64;\n\nfunction isIpv4(value: string): boolean {\n const parts = value.split('.');\n if (parts.length !== 4) return false;\n for (const part of parts) {\n if (!/^\\d{1,3}$/.test(part)) return false;\n // Leading zeros are a SECOND spelling of the same address (`203.0.113.04`),\n // so accepting them verbatim splits one peer across two buckets. Reject\n // rather than canonicalize: no legitimate emitter zero-pads, and some\n // resolvers read a `0`-prefixed octet as octal, i.e. it is not even\n // reliably the same address.\n if (part.length > 1 && part.startsWith('0')) return false;\n if (Number(part) > 255) return false;\n }\n return true;\n}\n\nfunction isIpv6(value: string): boolean {\n if (!value.includes(':')) return false;\n // Charset gate first: hex digits, separators, and the dots of a trailing\n // embedded IPv4. Anything else (a hostname, a header injection) is out.\n if (!/^[0-9a-f:.]+$/.test(value)) return false;\n if (value.includes(':::')) return false;\n // A dangling separator (`2001:db8::1:`) leaves an EMPTY trailing group that\n // is not part of a `::` compression — malformed, not an address.\n if (value.endsWith(':') && !value.endsWith('::')) return false;\n if (value.startsWith(':') && !value.startsWith('::')) return false;\n const compressions = value.split('::').length - 1;\n if (compressions > 1) return false;\n const groups = value.split(':');\n if (groups.length > 8) return false;\n for (let i = 0; i < groups.length; i += 1) {\n const group = groups[i];\n if (group === '') continue;\n if (group.includes('.')) {\n // An embedded IPv4 occupies the LAST two groups; anywhere else\n // (`1.2.3.4::1`) it is malformed.\n if (i !== groups.length - 1) return false;\n if (!isIpv4(group)) return false;\n continue;\n }\n if (!/^[0-9a-f]{1,4}$/.test(group)) return false;\n }\n // Uncompressed form must be exactly 8 groups (a trailing embedded IPv4\n // occupies two, so 7 written groups is also complete).\n if (compressions === 0) {\n const embedsIpv4 = groups[groups.length - 1]?.includes('.') ?? false;\n if (groups.length !== (embedsIpv4 ? 7 : 8)) return false;\n }\n return true;\n}\n\n/**\n * Normalize one IP candidate to its canonical bucket key, or `null` when the\n * value is not a usable address.\n */\nexport function normalizeIpForBucketKey(value: string): string | null {\n if (typeof value !== 'string') return null;\n let candidate = value.trim();\n if (candidate === '' || candidate.length > IP_BUCKET_KEY_MAX_LENGTH) return null;\n\n // `[2001:db8::1]` / `[::1]:443` — the bracketed forms a proxy emits so the\n // port is unambiguous.\n if (candidate.startsWith('[')) {\n const close = candidate.indexOf(']');\n if (close === -1) return null;\n const trailer = candidate.slice(close + 1);\n if (trailer !== '' && !/^:\\d{1,5}$/.test(trailer)) return null;\n candidate = candidate.slice(1, close);\n }\n\n // `%eth0` / `%25eth0` scope id — host-local, never part of peer identity.\n const zone = candidate.indexOf('%');\n if (zone !== -1) candidate = candidate.slice(0, zone);\n if (candidate === '') return null;\n\n // UNBRACKETED `203.0.113.4:8080` — Azure App Service / Front Door and several\n // CDNs write the client hop that way. Rejecting it left the caller with NO\n // candidate at all, collapsing every visitor into the single egress bucket:\n // exactly the silent regression this module exists to prevent. Stripped ONLY\n // when the remainder is a valid IPv4; an unbracketed IPv6 with a bare port\n // (`::1:443`) is genuinely ambiguous — `:443` is equally a final group — so\n // that shape is left to the IPv6 path.\n const portSplit = candidate.lastIndexOf(':');\n if (portSplit > 0 && /^\\d{1,5}$/.test(candidate.slice(portSplit + 1))) {\n const head = candidate.slice(0, portSplit);\n if (isIpv4(head)) candidate = head;\n }\n\n if (isIpv4(candidate)) return candidate;\n\n const lowered = candidate.toLowerCase();\n\n // IPv4-mapped IPv6 → the bare IPv4 (same peer, one bucket). EVERY spelling\n // is handled: the compressed `::ffff:1.2.3.4` that dual-stack listeners\n // actually emit, the written-out `0:0:0:0:0:ffff:1.2.3.4` that a hop doing\n // its own (non-compressing) formatting can produce, and the ZERO-PADDED\n // `0000:0000:0000:0000:0000:ffff:1.2.3.4` that a fixed-width formatter\n // produces — leading zeros in an IPv6 group are purely cosmetic, so all\n // three name the same peer. Matching only some of them would split one peer\n // across two buckets — the exact failure this module exists to prevent.\n const mapped = /^(?:(?:0{1,4}:){5}|::)ffff:(\\d{1,3}(?:\\.\\d{1,3}){3})$/.exec(lowered);\n if (mapped) return isIpv4(mapped[1]) ? mapped[1] : null;\n\n if (isIpv6(lowered)) return lowered;\n return null;\n}\n","/**\n * `envFlagEnabled` — THE cross-repo predicate for reading a boolean env flag.\n *\n * WHY IT LIVES HERE: the same flag is read on BOTH sides of the guide-chat\n * seam and each side had grown its own reading of it. The hub parsed the\n * value (`'0'` / `'false'` / `'off'` mean OFF); the producing app used bare\n * `Boolean(process.env.X)`, under which every non-empty string — including\n * `'0'` and `'false'` — is TRUE.\n *\n * That divergence failed OPEN on the one flag that gates a security boundary.\n * `TRUSTED_INGRESS_SETS_REAL_IP` is an operator's assertion that a reverse\n * proxy OVERWRITES the client-IP headers; without it those headers are\n * attacker-writable and must not be read. An operator writing `=0` to mean\n * \"off\" got the hub honoring that while the producer read `x-real-ip` /\n * `x-forwarded-for` anyway and forwarded the value as `x-chat-ip`, which the\n * hub then trusts verbatim under the service token. A rate-limit-bucket\n * spoof, reachable through a plausible operator typo.\n *\n * Both repos already depend on this package and `./chat-protocol` is its\n * server-safe subpath (no React, no browser APIs), so this is the one place\n * both sides can share — same reasoning that moved `normalizeIpForBucketKey`\n * here. Keep it as ONE definition: a second copy is what caused the drift.\n *\n * Contract:\n * - unset, empty, or whitespace-only → `false`;\n * - `'0'`, `'false'`, `'off'` → `false` (case- and\n * surrounding-whitespace-insensitive);\n * - any other non-empty value → `true`.\n *\n * `process.env` is read PER CALL, never module-hoisted, so tests, preview and\n * prod all observe the live value rather than whatever was present at\n * module-eval time.\n */\nexport function envFlagEnabled(name: string): boolean {\n const raw = process.env[name];\n if (!raw) return false;\n const v = raw.trim().toLowerCase();\n return v !== '' && v !== '0' && v !== 'false' && v !== 'off';\n}\n"]}
1
+ {"version":3,"sources":["../../src/chat-protocol/frames.ts","../../src/chat-protocol/events.ts","../../src/chat-protocol/progress.ts","../../src/chat-protocol/encode.ts","../../src/chat-protocol/leading-frames.ts","../../src/chat-protocol/decode.ts","../../src/components/chat/types/message.types.ts","../../src/utils/url-safety.ts","../../src/chat-protocol/card-marker.ts","../../src/chat-protocol/wire-narrow.ts","../../src/chat-protocol/source-metadata.ts","../../src/chat-protocol/nats-decoder.ts","../../src/chat-protocol/confirm-tool.ts","../../src/chat-protocol/ip-normalize.ts","../../src/chat-protocol/env-flag.ts"],"names":["text","cards"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * SSE wire-protocol frame shapes — the byte-level contract between the\n * hub's chat stream emitter (`multi-platform-hub/lib/utils/claude-util.ts`)\n * and the lib's SSE decoder (`./decode.ts`).\n *\n * Wire format (custom byte framing, NOT standard SSE `data:` lines):\n *\n * [JSON frame]\\0 [JSON frame]\\0 … \\x1E <raw UTF-8 answer deltas> \\x1F [JSON usage trailer]\n *\n * 1. LEADING frames — each a JSON object terminated by `\\0`\n * (FRAME_TERMINATOR). Status, metadata, routing, live thinking\n * deltas, usage:start, tool approval frames.\n * 2. ONE `\\x1E` byte (END_OF_LEADING) — flips the stream into raw\n * UTF-8 answer-text mode.\n * 3. `\\x1F` (TRAILER_SENTINEL) + one JSON usage frame running to\n * stream end (no terminator).\n *\n * Server-safe: no React, no browser APIs beyond TextEncoder/TextDecoder.\n */\n\nimport type { ChatProgressStage } from './progress';\n\n// =============================================================================\n// Sentinel constants\n// =============================================================================\n\n/** Terminates every leading JSON frame. */\nexport const FRAME_TERMINATOR = '\\0';\n/** Record Separator — end of leading frames, start of raw answer text. */\nexport const END_OF_LEADING = '\\x1E';\n/** Unit Separator — start of the trailing usage JSON frame. */\nexport const TRAILER_SENTINEL = '\\x1F';\n\n// =============================================================================\n// Routing types (moved verbatim from the hub's `lib/chat/wire-frames.ts`;\n// `RouteComplexity` is lib-local so the lib never imports hub code)\n// =============================================================================\n\n/** Server-side routing complexity tiers. Mirror of the hub's\n * `lib/constants/ai-models.ts` union — kept as a lib-local literal type\n * so the wire SSOT has zero hub imports. The DECODER accepts any string\n * (older/newer servers may ship values outside this union). */\nexport type RouteComplexity = 'trivial' | 'default' | 'complex' | 'deep';\n\n/**\n * Routing decision frame. Emitted once per turn as a LEADING frame\n * (between metadata and the `\\x1E` end-of-leading-frames sentinel) when\n * the server's `decideRoute()` produces a `Route`.\n *\n * Field names use the `routed*` prefix on PURPOSE — the client's\n * catch-all leading-frame branch inspects `meta.model` / `meta.modelLabel`.\n * A bare `model` field on this frame would OVERWRITE the metadata frame's\n * modelLabel and break the <ModelDisplay> badge. The `routed` prefix\n * sidesteps that collision.\n *\n * Older clients that don't recognize `kind: 'routing'` silently drop\n * the frame (the catch-all only matches frames carrying known fields\n * like `sources` / `modelLabel`).\n */\nexport interface RoutingFrame {\n kind: 'routing';\n routedComplexity: RouteComplexity;\n routedModel: string;\n routedThinkingBudget: number;\n}\n\n/**\n * Optional field on the trailing usage frame's `breakdown`. Surfaces\n * per-route attribution to the admin test console so an operator can\n * see \"this turn ran on Haiku/trivial/$0.0005\" without re-parsing the\n * stream. Additive: clients that ignore unknown keys keep working.\n */\nexport interface RoutedAnswerBreakdown {\n model: string;\n complexity: RouteComplexity;\n thinkingBudget: number;\n}\n\n// =============================================================================\n// Leading frames\n// =============================================================================\n\n/** First frame on every stream — lets the shell render \"Thinking…\"\n * within ~200ms, before any upstream byte arrives. */\nexport interface StatusThinkingFrame {\n status: 'thinking';\n /** What the turn is doing right now (`./progress`): a fixed vocabulary, shown\n * as one line while the user waits. May be sent again as the stage moves. */\n stage?: ChatProgressStage;\n /** How many of the thing the stage works on (sources searched, results read). */\n count?: number;\n}\n\n/** Per-turn metadata frame. The emitter sends `options.metadata`\n * verbatim. All fields optional — the decoder's catch-all treats ANY\n * frame that didn't match a `kind` discriminant as metadata-ish. */\nexport interface ChatMetadataFrame {\n /** Retrieval sources for the source-chip strip. Host-defined row\n * shape (`ChatSource` on the adapter side) — opaque at wire level. */\n sources?: unknown[];\n /** Raw model id (e.g. 'claude-sonnet-x'). Presence alone triggers the\n * client's meta merge; the value itself is not stored. */\n model?: string;\n modelLabel?: string;\n provider?: string;\n contextWindowMaxTokens?: number;\n /** Per-message viewport-positioning hint ('top' | 'bottom'). */\n scrollAnchor?: string;\n /** SERVER-minted conversation trace id (`chat_conversations.id`),\n * echoed on every turn's leading metadata frame. The client never\n * generates ids — the first turn of a session goes out id-less, the\n * server mints one and echoes it here, and the adapter persists the\n * echoed id as its ONLY local state (see\n * `chat-conversation-storage.ts` on the consumer side). */\n conversationId?: string;\n}\n\n/** Live adaptive-thinking delta. WIRE IS ALREADY DELTA (Anthropic\n * thinking_delta slices) — decoders must NOT diff or re-accumulate. */\nexport interface ThinkingDeltaFrame {\n kind: 'thinking-delta';\n text: string;\n}\n\n/** Usage frame emitted as a LEADING frame right after Anthropic's\n * message_start (stage='start'). */\nexport interface UsageStartFrame {\n kind: 'usage';\n stage: 'start';\n input_tokens: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n}\n\n/** Model preamble prose written BEFORE a tool_use block — emitted as a\n * standalone leading frame so it isn't stranded behind the approval\n * card (the `\\x1E` sentinel is suppressed on tool turns). */\nexport interface TextLeadingFrame {\n kind: 'text-leading';\n text: string;\n}\n\n/** Tool invocation failed server-side (proposal persist error,\n * validation, ownership denial …). */\nexport interface ToolErrorFrame {\n kind: 'tool_error';\n toolName?: string;\n toolUseId?: string;\n message: string;\n}\n\nexport interface ApprovalRequestField {\n label: string;\n value: string;\n}\n\n/** The model called a write tool; the server persisted a proposal and\n * ships a card-ready payload. Shape mirrors the hub's `onToolUse`\n * return in `lib/data/doc-chat-utils.ts`. */\nexport interface ApprovalRequestFrame {\n kind: 'approval_request';\n proposalId: string;\n toolName: string;\n title?: string;\n fields?: ApprovalRequestField[];\n preamble?: string | null;\n args?: Record<string, unknown>;\n expiresAt?: string;\n ttlSeconds?: number;\n}\n\n/** A single write-tool turn produced MULTIPLE proposals (batch\n * operations — \"delete both\", \"close all three\"). The SERVER is the\n * authority on the grouping: it collects the turn's proposals and\n * ships ONE batch frame instead of N `approval_request` frames, so\n * clients render the bulk-approval component without any adjacency\n * heuristics. Single-proposal turns keep the classic\n * `approval_request` frame. */\nexport interface ApprovalBatchFrame {\n kind: 'approval_batch';\n /** Stable batch anchor — client-side only (locates the batch segment\n * for status flips). MUST NOT equal any row's `proposalId`: the\n * click-time optimistic status flip keys on this id, and an anchor\n * that doubles as a row id would falsely tick that row's execution\n * as succeeded before its confirm ran (masking a failed write). The\n * server emits `batch:<first proposalId>`. */\n batchId: string;\n proposals: Array<{\n proposalId: string;\n toolName: string;\n title?: string;\n fields?: ApprovalRequestField[];\n expiresAt?: string;\n ttlSeconds?: number;\n }>;\n}\n\n/** Server-driven post-approve / post-reject frame (confirm-tool route).\n * Mirror of the hub's `DecisionResolvedFrame` in\n * `lib/data/chat-agent-utils.ts` (tool_name widened to string — the\n * known-write-tool whitelist is host-side knowledge). */\nexport interface DecisionResolvedFrame {\n kind: 'decision_resolved';\n /** The proposal this decision resolved — locates the SOURCE approval\n * card so the shell can flip its status + append the receipt. */\n proposalId: string;\n ok: boolean;\n action: 'approved' | 'rejected';\n tool_name?: string;\n result?: {\n ticket_id?: string;\n status?: string | null;\n mirror_synced?: boolean;\n };\n /** True when the server WILL pipe an auto-continuation turn after\n * this frame. */\n willAutoContinue: boolean;\n /** Pre-rendered receipt copy from the source strategy's\n * `receiptRenderer` — client renders verbatim. */\n receiptText?: string;\n}\n\n/** Every leading frame the emitter can produce. */\nexport type SseLeadingFrame =\n | StatusThinkingFrame\n | ChatMetadataFrame\n | RoutingFrame\n | ThinkingDeltaFrame\n | UsageStartFrame\n | TextLeadingFrame\n | ToolErrorFrame\n | ApprovalRequestFrame\n | ApprovalBatchFrame\n | DecisionResolvedFrame;\n\n// =============================================================================\n// Trailing usage frame\n// =============================================================================\n\n/** Card/chip emission counts for admin adoption tooling. */\nexport interface UsageTelemetry {\n cards: number;\n chips: number;\n sentences: number;\n answerLen: number;\n}\n\n/** Trailing usage frame — `\\x1F` + JSON, runs to stream end (no\n * terminator). `stage: 'display'` is a legacy alias the decoder treats\n * identically to `'end'`. */\nexport interface SseTrailingUsageFrame {\n kind: 'usage';\n stage: 'end' | 'display';\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n hit_rate_pct?: number;\n telemetry?: UsageTelemetry;\n /** Cross-call usage breakdown (Haiku rewriter / classifier /\n * summarizer + routed-answer attribution). */\n breakdown?: {\n haikuRewriter?: { input: number; output: number };\n haikuClassifier?: { input: number; output: number };\n haikuSummarizer?: { input: number; output: number };\n routedAnswer?: RoutedAnswerBreakdown;\n };\n /** Gated deep-debug payload (only present when the request carried\n * the privileged debug secret). */\n debug?: Record<string, unknown>;\n}\n","/**\n * Normalized chat stream events — the transport-agnostic event union BOTH\n * decoders (SSE byte framing in `./decode.ts`, NATS chunks in\n * `./nats-decoder.ts`) emit. Phase 3 of the chat unification makes the\n * reducer consume these directly; today the SSE adapter maps them back to\n * legacy `MessageSegment` yields.\n *\n * Server-safe: no React, no browser APIs.\n */\n\n// The ask card's option shape is the SEGMENT's shape — the decoder hands the\n// rows straight to the accumulator, so restating them here would be two\n// declarations of one wire contract. Type-only import from a React-free\n// module; `nats-decoder.ts` already depends on the same file for MESSAGE_TYPE.\nimport type { ChatRef } from '../components/chat/chat-ref.types';\nimport type { AskOptionData, ChatSource } from '../components/chat/types/message.types';\n// The wire-frame shapes these events carry through are defined ONCE in\n// `./frames.ts` — reuse them here rather than restating their fields.\nimport type { ApprovalRequestField, DecisionResolvedFrame, UsageTelemetry } from './frames';\nimport type { ChatProgress } from './progress';\n\n/** Optional envelope on every event. `seq` carries the transport's\n * stream sequence (JetStream `streamSeq` on NATS; unused on SSE). */\ninterface ChatStreamEventBase {\n seq?: number;\n}\n\n/** Stream entered answer-text mode (SSE: the `\\x1E` sentinel or the\n * JSON-parse-failure fallback; NATS: MESSAGE_START).\n * `implicit` is true ONLY for the SSE fallback path where a\n * non-JSON leading buffer flips straight into text mode — consumers\n * that flush coalesced thinking on turn-start must SKIP the flush for\n * implicit starts (legacy adapter parity: the fallback path never\n * flushed pending thinking before the text). */\nexport interface TurnStartEvent extends ChatStreamEventBase {\n type: 'turn-start';\n implicit?: boolean;\n}\n\nexport interface TurnEndEvent extends ChatStreamEventBase {\n type: 'turn-end';\n}\n\n/** Answer text delta. `leading: true` marks SSE `text-leading` frames\n * (model preamble prose emitted BEFORE the tool approval card). */\nexport interface TextDeltaEvent extends ChatStreamEventBase {\n type: 'text-delta';\n text: string;\n leading?: boolean;\n}\n\n/** Thinking delta — APPEND-ONLY contract: each event carries the next\n * verbatim slice; consumers accumulate. Decoders never diff. */\nexport interface ThinkingDeltaEvent extends ChatStreamEventBase {\n type: 'thinking-delta';\n text: string;\n}\n\n/** Clarification card (NATS `ASK` chunk) — the assistant asking which reading\n * of an ambiguous question to answer. NOT a delta: the chunk carries the\n * finished card, so consumers push it whole instead of coalescing. `text` is\n * the intro sentence the same chunk rides along with; consumers render it as\n * ordinary answer text BEFORE the card. NATS-only — the SSE frame grammar has\n * no ask frame — but it lives in the shared union because the reducer is\n * transport-agnostic. */\nexport interface AskEvent extends ChatStreamEventBase {\n type: 'ask';\n text?: string;\n question: string;\n options: AskOptionData[];\n}\n\nexport interface StatusEvent extends ChatStreamEventBase {\n type: 'status';\n phase: 'thinking';\n /** What the turn is doing right now, when the server says (`./progress`). */\n progress?: ChatProgress;\n}\n\n/** NATS tool execution progress (EXECUTING_TOOL / EXECUTED_TOOL). */\nexport interface ToolExecutionEvent extends ChatStreamEventBase {\n type: 'tool-execution';\n data: {\n type: 'EXECUTING_TOOL' | 'EXECUTED_TOOL';\n integratedToolType: string;\n toolFunction: string;\n toolTitle?: string;\n /**\n * Human-readable \"why this tool is running\" line, rendered as the body of\n * the tool card. Carried by the EXECUTING chunk ONLY — the EXECUTED one\n * never repeats it, and the accumulator's merge keeps whatever the\n * EXECUTING segment held (`toolData.toolExplanation ?? existing`). Omitting\n * it from this contract blanks the card for the whole run, live and on\n * replay, which is exactly what happened while it was missing here.\n */\n toolExplanation?: string;\n parameters?: Record<string, unknown>;\n result?: string;\n success?: boolean;\n toolExecutionRequestId?: string;\n };\n}\n\n/** Single tool call inside a batch approval request (NATS). */\nexport interface ApprovalToolCall {\n toolExecutionRequestId: string;\n toolName: string;\n toolTitle?: string;\n toolExplanation?: string;\n toolType?: string;\n requiresApproval: boolean;\n approvalType?: string | null;\n toolCallArguments?: Record<string, unknown> | null;\n}\n\n/** A tool call awaits user approval. SSE fills `command`/`fields`\n * (card-ready payload); NATS fills `command`/`explanation` or\n * `toolCalls` (batch form). */\nexport interface ApprovalRequestEvent extends ChatStreamEventBase {\n type: 'approval-request';\n requestId: string;\n /** SSE: the write tool's name. NATS: approval tier (USER/ADMIN/…). */\n approvalType?: string;\n command?: string;\n explanation?: string;\n fields?: ApprovalRequestField[];\n toolCalls?: ApprovalToolCall[];\n status?: 'pending';\n}\n\n/** An approval request was resolved (SSE `decision_resolved` frame /\n * NATS APPROVAL_RESULT chunk). */\nexport interface ApprovalResolvedEvent extends ChatStreamEventBase {\n type: 'approval-resolved';\n requestId?: string;\n status: 'approved' | 'rejected';\n ok?: boolean;\n toolName?: string;\n approvalType?: string;\n resolvedByName?: string | null;\n receiptText?: string;\n result?: DecisionResolvedFrame['result'];\n willAutoContinue?: boolean;\n}\n\n/** The client is offered a handoff of this ticket to a human technician\n * (NATS `ESCALATION_OFFER` chunk in state PENDING). Distinct from\n * `approval-request`: it resolves through the ticket-escalation mutations,\n * never the tool-approval flow, and it is raised by four different origins\n * (Fae's own tool call, the client's header button, a deterministic\n * trigger, or a deferred surfacing at turn end) that all render alike. */\nexport interface EscalationOfferEvent extends ChatStreamEventBase {\n type: 'escalation-offer';\n offerId: string;\n text: string;\n origin?: string;\n}\n\n/** Wire vocabulary of `EscalationOfferData.state`. */\nexport const ESCALATION_STATE = {\n PENDING: 'PENDING',\n APPROVED: 'APPROVED',\n DECLINED: 'DECLINED',\n SUPERSEDED: 'SUPERSEDED',\n} as const;\n\n/** Terminal wire state → the shared `ChatApprovalStatus` vocabulary; `null`\n * for PENDING or anything unrecognized. Shared by BOTH decoders (live\n * chunks and persisted rows) so a state can never mean two things. */\nexport function escalationResolvedStatus(state: unknown): EscalationOfferResolvedEvent['status'] | null {\n switch (state) {\n case ESCALATION_STATE.APPROVED:\n return 'approved';\n case ESCALATION_STATE.DECLINED:\n return 'rejected';\n case ESCALATION_STATE.SUPERSEDED:\n return 'cancelled';\n default:\n return null;\n }\n}\n\n/** An escalation offer reached a terminal state. The wire's SUPERSEDED\n * (the client typed over a pending offer) maps onto `cancelled` so the\n * whole stack keeps ONE status vocabulary (`ChatApprovalStatus`). The\n * resolved chunk carries no text — consumers flip the existing block. */\nexport interface EscalationOfferResolvedEvent extends ChatStreamEventBase {\n type: 'escalation-offer-resolved';\n offerId: string;\n status: 'approved' | 'rejected' | 'cancelled';\n resolvedByName?: string | null;\n}\n\n/** The conversation was handed off to a human technician (`TICKET_ESCALATED`).\n * A first-class block rather than something inferred from an offer's state,\n * so paths that raise no offer at all — the inactivity auto-escalation — still\n * produce a receipt. `text` is nullable on the wire; consumers supply the\n * fallback copy. */\nexport interface TicketEscalatedEvent extends ChatStreamEventBase {\n type: 'ticket-escalated';\n ticketId: string;\n ticketNumber?: number;\n reason: string;\n text?: string;\n}\n\n/** Ticket lifecycle receipt (`TICKET_EVENT`) — the ticket was resolved,\n * reopened, etc. `kind` is an OPEN vocabulary (RESOLVED/REOPENED today):\n * an unknown kind still decodes and renders as a neutral line rather than\n * being dropped, so the backend can add kinds without a client release.\n * Arrives standalone (outside MESSAGE_START/END), like `ticket-escalated`. */\nexport interface TicketEventEvent extends ChatStreamEventBase {\n type: 'ticket-event';\n kind: string;\n actorId?: string;\n actorName?: string;\n /** Who acted — e.g. an AI agent vs a human technician. Open string. */\n actorType?: string;\n reason?: string;\n /** Kind-token the ticket reopened INTO (AI_ASSISTANCE / TECH_REQUIRED / ...). */\n targetStatusKind?: string;\n}\n\n/**\n * Per-answer source metadata — the documents an answer cited, plus the video\n * and entity-card references its `[card://type:id]` markers expand with.\n *\n * Unlike `ChatMetadataEvent`, this one is DECODED, not passthrough: the payload\n * comes from a remote MCP server's tool output rather than from our own\n * backend's frame, so it is validated at the protocol boundary (see\n * `source-metadata.ts`) and never reaches a consumer half-checked.\n *\n * Both fields are optional and never both absent — the decoder returns `null`\n * instead of emitting an empty event, so applying one can't blank metadata an\n * earlier chunk of the same turn already supplied.\n */\nexport interface SourcesEvent extends ChatStreamEventBase {\n type: 'sources';\n sources?: ChatSource[];\n refs?: ChatRef[];\n}\n\n/** Per-turn metadata. Raw wire values pass through UNVALIDATED — the\n * consumer replicates the legacy truthiness/typeof gates (so a\n * malformed frame degrades identically to the pre-SSOT parser). */\nexport interface ChatMetadataEvent extends ChatStreamEventBase {\n type: 'metadata';\n provider?: string | null;\n modelLabel?: string | null;\n /** Raw model id (SSE `meta.model`; NATS `modelName`). */\n modelName?: string | null;\n contextWindowMaxTokens?: number | null;\n sources?: unknown;\n scrollAnchor?: unknown;\n /** Server-minted conversation id (`ChatMetadataFrame.conversationId`),\n * passed through raw like every other catch-all field — the consumer\n * applies the `typeof === 'string' && truthy` gate. */\n conversationId?: string | null;\n routing?: {\n routedComplexity: string;\n routedModel?: string;\n routedThinkingBudget: number | null;\n };\n}\n\n/** SSE usage frames — raw wire keys (snake_case) preserved. */\nexport interface UsageEvent extends ChatStreamEventBase {\n type: 'usage';\n stage: 'start' | 'end';\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n hit_rate_pct?: number;\n telemetry?: UsageTelemetry;\n /** Left OPAQUE on purpose: the consumer (`chat-stream-reducer`'s\n * `applySseUsage`) re-validates every nested field with the legacy\n * truthiness/typeof gates, so a malformed frame degrades identically\n * to the pre-SSOT parser. Typing it would imply guarantees the wire\n * does not make. */\n breakdown?: unknown;\n debug?: unknown;\n}\n\n/** NATS TOKEN_USAGE chunk (camelCase backend shape). */\nexport interface TokenUsageEvent extends ChatStreamEventBase {\n type: 'token-usage';\n inputTokensSize: number;\n outputTokensSize: number;\n totalTokensSize: number;\n contextSize: number;\n}\n\nexport interface CompactionEvent extends ChatStreamEventBase {\n type: 'compaction';\n phase: 'start' | 'end';\n summary?: string;\n}\n\nexport interface ErrorEvent extends ChatStreamEventBase {\n type: 'error';\n title: string;\n details?: string;\n}\n\n/**\n * Decode {@link ErrorEvent.details} into the sub-line an error card renders.\n *\n * `details` is EITHER a JSON envelope (`{ error: { message } }`) or an already-\n * human string, and both the live reducer and the history replay have to agree\n * on which — a card that reads one way live and another on refresh is the whole\n * class of bug this module's shared decoders exist to prevent. Lives here,\n * beside the event it decodes, because it is the wire contract, not a rendering\n * choice.\n *\n * Gates:\n * - not JSON → the raw string (it was already human-readable)\n * - JSON, string message → that message\n * - JSON, anything else → `undefined`, so the card shows its title alone\n *\n * That last gate is the fix for a real defect: the message was previously\n * copied out of the envelope UNCHECKED, so a server that nested an object\n * under `error.message` put that object into a `string` field and the card\n * rendered the literal text \"[object Object]\" under the title. Valid JSON that\n * simply lacks `error.message` still yields `undefined` (unchanged) rather than\n * dumping the raw envelope at the user.\n */\nexport function errorDetailsMessage(details: string | undefined): string | undefined {\n if (!details) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(details);\n } catch {\n return details;\n }\n if (typeof parsed !== 'object' || parsed === null) return undefined;\n const error: unknown = (parsed as { error?: unknown }).error;\n if (typeof error !== 'object' || error === null) return undefined;\n const message: unknown = (error as { message?: unknown }).message;\n return typeof message === 'string' ? message : undefined;\n}\n\n/** Non-assistant message on the stream (NATS): the user's own message\n * echo (`message-request`), an operator direct message, or a system\n * line. */\n/**\n * Dialog participant owner type. The two members below are the ones clients\n * BRANCH ON (admin-vs-not decides echo dedup and author styling); the union\n * stays open (`string & {}`) because the wire may carry other roles that the\n * client treats as \"not admin\". Always compare against `CHAT_OWNER_ADMIN`,\n * never a bare `'ADMIN'` literal — a typo in a literal silently disables\n * dedup instead of failing to compile.\n */\nexport type ChatOwnerType = 'ADMIN' | 'CLIENT' | (string & {});\n\n/** Canonical ADMIN owner-type token (see `ChatOwnerType`). */\nexport const CHAT_OWNER_ADMIN = 'ADMIN';\n\nexport interface ParticipantEvent extends ChatStreamEventBase {\n type: 'participant';\n kind: 'message-request' | 'direct-message' | 'system';\n text: string;\n ownerType?: ChatOwnerType;\n displayName?: string;\n userId?: string;\n contextItems?: Array<{ type: string; id: string; label: string }>;\n}\n\nexport interface DialogClosedEvent extends ChatStreamEventBase {\n type: 'dialog-closed';\n}\n\nexport type ChatStreamEvent =\n | TurnStartEvent\n | TurnEndEvent\n | TextDeltaEvent\n | ThinkingDeltaEvent\n | AskEvent\n | StatusEvent\n | ToolExecutionEvent\n | ApprovalRequestEvent\n | ApprovalResolvedEvent\n | EscalationOfferEvent\n | EscalationOfferResolvedEvent\n | TicketEscalatedEvent\n | TicketEventEvent\n | ChatMetadataEvent\n | SourcesEvent\n | UsageEvent\n | TokenUsageEvent\n | CompactionEvent\n | ErrorEvent\n | ParticipantEvent\n | DialogClosedEvent;\n","/**\n * What a turn is doing before its first answer token: the named stages a server\n * reports while it works, and the line a client shows for each.\n *\n * A retrieval chat spends seconds before any text exists (reading the question,\n * searching, reading what it found). A generic spinner for that whole time says\n * nothing; a line naming the real step (\"Searching 28 sources\") does, and people\n * wait more willingly for it. The stage is a FIXED vocabulary on the wire (never\n * model text, a rewritten query or a source name), so it is safe on a public\n * chat and the wording stays the client's: `chatProgressLabel` is the one place\n * the copy lives.\n *\n * Wire: the existing `{ status: 'thinking' }` leading frame, with an optional\n * `stage` and `count`. A client that predates the stage reads the frame exactly\n * as before; a client that knows it shows the line. A stage it does not know is\n * dropped (the frame is still a plain \"thinking\").\n *\n * Server-safe: no React, no browser APIs.\n */\n\nexport const CHAT_PROGRESS_STAGES = ['understanding', 'searching', 'searching_again', 'reading'] as const;\n\nexport type ChatProgressStage = (typeof CHAT_PROGRESS_STAGES)[number];\n\nexport interface ChatProgress {\n stage: ChatProgressStage;\n /** How many of the thing the stage works on: sources searched, results read. */\n count?: number;\n}\n\nexport function isChatProgressStage(value: unknown): value is ChatProgressStage {\n return typeof value === 'string' && (CHAT_PROGRESS_STAGES as readonly string[]).includes(value);\n}\n\n/** A parsed status frame's progress, or null when it carries no stage this client knows. */\nexport function chatProgressOf(frame: { stage?: unknown; count?: unknown }): ChatProgress | null {\n if (!isChatProgressStage(frame.stage)) return null;\n const count =\n typeof frame.count === 'number' && Number.isFinite(frame.count) && frame.count > 0\n ? Math.floor(frame.count)\n : undefined;\n return count === undefined ? { stage: frame.stage } : { stage: frame.stage, count };\n}\n\nconst PROGRESS_LABEL: Record<ChatProgressStage, (count: number | undefined) => string> = {\n understanding: () => 'Understanding your question',\n searching: count => (count ? `Searching ${count} ${count === 1 ? 'source' : 'sources'}` : 'Searching'),\n searching_again: () => 'Searching more broadly',\n reading: count => (count ? `Reading ${count} ${count === 1 ? 'result' : 'results'}` : 'Reading the results'),\n};\n\n/** The one line shown for a stage. Specific when the server gave a real count, never an invented one. */\nexport function chatProgressLabel(progress: ChatProgress): string {\n return PROGRESS_LABEL[progress.stage](progress.count);\n}\n","/**\n * SSE wire-protocol encoders — the emit side of the framing contract in\n * `./frames.ts`. Server-safe (TextEncoder only). The hub's stream route\n * consumes these so emitter and decoder can never drift.\n */\n\nimport {\n FRAME_TERMINATOR,\n END_OF_LEADING,\n TRAILER_SENTINEL,\n type SseLeadingFrame,\n type SseTrailingUsageFrame,\n} from './frames';\n\nconst encoder = new TextEncoder();\n\n/** Matches every framing sentinel byte: `\\0`, `\\x1E`, `\\x1F`. THE one\n * regex — `stripSentinelBytes` is its only consumer. */\nconst SENTINEL_BYTES_RE = /[\\u0000\\u001E\\u001F]/g;\n\n/**\n * Remove the SSE framing sentinel bytes (`\\0`, `\\x1E`, `\\x1F`) from model\n * text. Exported as a STANDALONE helper — not just folded into\n * `encodeTextDelta` — because producers must strip at the point where they\n * first take custody of the text, BEFORE any local accumulation.\n *\n * Concretely: the hub's `claude-util.ts` accumulates `state.answerText` for\n * persistence + telemetry. If stripping only happened at encode time, the\n * persisted/telemetered answer would diverge, character for character, from\n * the bytes the user actually received whenever the model echoes a control\n * byte. Both sides call this, so there is exactly ONE definition of \"which\n * bytes are framing\" in the whole system — do not re-declare the regex.\n *\n * Idempotent and per-character, so it distributes over concatenation:\n * `strip(a + b) === strip(a) + strip(b)` — safe to apply per-delta or on the\n * accumulated string interchangeably.\n */\nexport function stripSentinelBytes(text: string): string {\n return text.replace(SENTINEL_BYTES_RE, '');\n}\n\n/** Encode one leading frame: JSON + `\\0` terminator. */\nexport function encodeLeadingFrame(frame: SseLeadingFrame): Uint8Array {\n return encoder.encode(JSON.stringify(frame) + FRAME_TERMINATOR);\n}\n\n/** Encode the single `\\x1E` end-of-leading-frames sentinel. */\nexport function encodeEndOfLeading(): Uint8Array {\n return encoder.encode(END_OF_LEADING);\n}\n\n/**\n * Encode a raw answer-text delta. STRIPS the framing sentinel bytes via\n * `stripSentinelBytes` so model-echoed control bytes can no longer\n * mis-frame the stream (a literal `\\x1F` in the answer used to flip the\n * client into trailer mode and swallow the rest of the answer PLUS the real\n * usage trailer — characterized by golden fixture (d)). Forward-looking\n * encoder fix only; the decoder's behavior is unchanged.\n *\n * Producers that ALSO accumulate the text locally (persistence, telemetry)\n * must call `stripSentinelBytes` on their own copy — see its docblock.\n */\nexport function encodeTextDelta(text: string): Uint8Array {\n return encoder.encode(stripSentinelBytes(text));\n}\n\n/** Encode the trailing usage frame: `\\x1F` + JSON, no terminator\n * (runs to stream end). */\nexport function encodeTrailingUsageFrame(frame: SseTrailingUsageFrame): Uint8Array {\n return encoder.encode(TRAILER_SENTINEL + JSON.stringify(frame));\n}\n","/**\n * Leading-frame → `ChatStreamEvent` table — the ONE mapping both transports read.\n *\n * The SSE decoder (`./decode.ts`) reaches it while walking the hub's byte\n * framing. It lives outside that decoder so a second transport can reuse the\n * table rather than re-implement it and drift the moment the hub adds a frame\n * kind.\n *\n * Server-safe: no React, no browser APIs.\n */\n\nimport type { ChatStreamEvent } from './events';\nimport { chatProgressOf } from './progress';\n\n/**\n * Escape `<` so markdown renderers that pass HTML through (rehypeRaw)\n * don't treat XML-like tokens in Claude's thinking output as elements.\n * `<` → `&lt;` preserves the visible character without breaking\n * blockquote `>` markers. Per-character, so it distributes over\n * concatenation: escape(a + b) === escape(a) + escape(b) — callers may\n * apply it per-delta or on the accumulated string interchangeably.\n */\nexport function escapeThinkingTags(text: string): string {\n return text.replace(/</g, '&lt;');\n}\n\n/** Wire string field, or `undefined` for anything else. Consumers apply\n * their own `typeof === 'string' && truthy` gate on top, so dropping a\n * wrong-typed value here is indistinguishable from passing it through. */\nfunction frameStr(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\n/** Wire number field, or `undefined` for anything else. Exported because the\n * SSE decoder's TRAILING usage frame must coerce its token counts through the\n * same gate this table already applies to the LEADING `usage:start` frame —\n * two gates would be two contracts for one wire field. */\nexport function frameNum(value: unknown): number | undefined {\n return typeof value === 'number' ? value : undefined;\n}\n\n/**\n * Map one parsed leading frame to normalized events, replicating the\n * legacy parser's else-if chain ORDER and its exact truthiness/typeof\n * gates. Frames that matched a branch but failed its inner validation\n * (e.g. `routing` without a string `routedComplexity`) produce NO event,\n * exactly like the legacy no-op.\n *\n * Every branch pushes AT MOST ONE event — `guideFrameEvent` relies on that\n * to hand a single event back to `decodeNatsChunk`'s per-chunk contract.\n */\nexport function mapLeadingFrame(meta: Record<string, unknown>, out: ChatStreamEvent[]): void {\n if (meta.status === 'thinking') {\n // The frame may name the stage the turn is in; an unknown stage is a plain \"thinking\".\n const progress = chatProgressOf(meta);\n out.push(progress ? { type: 'status', phase: 'thinking', progress } : { type: 'status', phase: 'thinking' });\n } else if (meta.kind === 'thinking-delta' && typeof meta.text === 'string') {\n // Wire is ALREADY delta — emit verbatim, append-only contract.\n out.push({ type: 'thinking-delta', text: meta.text });\n } else if (meta.kind === 'usage' && meta.stage === 'start') {\n out.push({\n type: 'usage',\n stage: 'start',\n input_tokens: frameNum(meta.input_tokens),\n cache_read_input_tokens: frameNum(meta.cache_read_input_tokens),\n cache_creation_input_tokens: frameNum(meta.cache_creation_input_tokens),\n });\n } else if (meta.kind === 'decision_resolved' && typeof meta.action === 'string') {\n const status = meta.action === 'rejected' ? 'rejected' : 'approved';\n const toolName = typeof meta.tool_name === 'string' ? meta.tool_name : undefined;\n const result = meta.result ?? null;\n out.push({\n type: 'approval-resolved',\n status,\n ok: meta.ok === true,\n willAutoContinue: meta.willAutoContinue === true,\n ...(toolName ? { toolName } : {}),\n ...(result ? { result } : {}),\n ...(typeof meta.receiptText === 'string' ? { receiptText: meta.receiptText } : {}),\n requestId: typeof meta.proposalId === 'string' ? meta.proposalId : undefined,\n });\n } else if (meta.kind === 'approval_batch' && meta.batchId && Array.isArray(meta.proposals)) {\n // Server-grouped multi-proposal turn → ONE batch event carrying a\n // tool-call row per proposal. `toolExecutionRequestId` is the row's\n // PROPOSAL id (each row resolves through its own per-proposal\n // confirm); the batch's `requestId` is the stable anchor the shell\n // uses for status flips. Field rows ride as the row's expandable\n // args so per-proposal detail stays reachable inside the batch.\n const toolCalls = meta.proposals\n .filter((p): p is Record<string, unknown> => typeof p === 'object' && p !== null)\n .filter(p => typeof p.proposalId === 'string')\n .map(p => {\n const rawFields = Array.isArray(p.fields)\n ? (p.fields as Array<{ label?: string; value?: string }>).filter(f => f && f.label && f.value)\n : [];\n // Prefer human-readable identity labels for the row's\n // disambiguator; opaque-id labels (\"Task\", \"Ticket\") only as\n // the last-resort first field.\n const detail = rawFields.find(f => /^(title|subject|name)$/i.test(String(f.label))) ?? rawFields[0];\n const base = typeof p.title === 'string' && p.title.length > 0 ? p.title : String(p.toolName ?? 'Tool call');\n return {\n toolExecutionRequestId: String(p.proposalId),\n toolName: String(p.toolName ?? 'tool'),\n toolTitle: detail ? `${base} — ${detail.value}` : base,\n requiresApproval: true,\n toolCallArguments:\n rawFields.length > 0 ? Object.fromEntries(rawFields.map(f => [String(f.label), String(f.value)])) : null,\n };\n });\n if (toolCalls.length > 0) {\n out.push({\n type: 'approval-request',\n requestId: String(meta.batchId),\n approvalType: 'chat',\n toolCalls,\n status: 'pending',\n });\n }\n } else if (meta.kind === 'approval_request' && meta.proposalId) {\n const proposalId = String(meta.proposalId);\n const toolName = String(meta.toolName ?? 'tool');\n const headline = typeof meta.title === 'string' && meta.title.length > 0 ? meta.title : toolName;\n const rawFields = Array.isArray(meta.fields) ? (meta.fields as Array<{ label?: string; value?: string }>) : [];\n const fields: Array<{ label: string; value: string }> = [];\n for (const f of rawFields) {\n if (!f || !f.label || !f.value) continue;\n fields.push({ label: f.label, value: f.value });\n }\n out.push({\n type: 'approval-request',\n requestId: proposalId,\n approvalType: toolName,\n command: headline,\n fields,\n status: 'pending',\n });\n } else if (meta.kind === 'text-leading' && typeof meta.text === 'string') {\n out.push({ type: 'text-delta', text: meta.text, leading: true });\n } else if (meta.kind === 'tool_error') {\n const msg =\n typeof meta.message === 'string' && meta.message.length > 0\n ? meta.message\n : 'Could not complete the requested action right now.';\n out.push({ type: 'error', title: msg });\n } else if (meta.kind === 'routing') {\n if (typeof meta.routedComplexity === 'string') {\n out.push({\n type: 'metadata',\n routing: {\n routedComplexity: meta.routedComplexity,\n ...(typeof meta.routedModel === 'string' ? { routedModel: meta.routedModel } : {}),\n routedThinkingBudget: typeof meta.routedThinkingBudget === 'number' ? meta.routedThinkingBudget : null,\n },\n });\n }\n } else {\n // Catch-all metadata-ish frame. Raw values pass through UNVALIDATED\n // (possibly undefined) so the consumer can replicate the legacy\n // presence/truthiness gates exactly — including the `model`-presence\n // trigger whose value is never stored.\n out.push({\n type: 'metadata',\n sources: meta.sources,\n provider: frameStr(meta.provider),\n modelLabel: frameStr(meta.modelLabel),\n modelName: frameStr(meta.model),\n contextWindowMaxTokens: frameNum(meta.contextWindowMaxTokens),\n scrollAnchor: meta.scrollAnchor,\n conversationId: frameStr(meta.conversationId),\n });\n }\n}\n","/**\n * SSE wire-protocol decoder — a timer-free synchronous state machine,\n * mechanically extracted from `useSseChatAdapter`'s inline\n * `createDocStreamFn` parser. Byte-for-byte behavior parity with the\n * legacy parser is the contract (the golden fixtures in\n * `src/components/chat/hooks/__tests__/sse-stream-golden.test.ts` pin it\n * through the full hook path), including its quirks:\n *\n * - A leading `\\0`-terminated block that fails JSON.parse flips the\n * stream into text mode and the WHOLE buffer (including the `\\0`)\n * is emitted as answer text (legacy no-frame stream fallback). This\n * transition is marked `turn-start { implicit: true }`.\n * - In text mode only `\\x1F` is scanned — literal `\\0` / `\\x1E` bytes\n * pass through into the answer; the FIRST literal `\\x1F` flips into\n * trailer mode and everything after is captured as the trailer\n * (golden fixture (d) characterizes this mis-framing; the fix lives\n * in `encode.ts`'s sentinel stripping, NOT here).\n * - The `\\x1E` sentinel and the `\\x1F` trailer can arrive in ONE TCP\n * chunk (fixed-answer responses) — the post-sentinel slice is\n * re-scanned for the trailer sentinel.\n * - Multi-byte UTF-8 across chunk boundaries survives via a single\n * streaming TextDecoder (`{ stream: true }` on every push; no final\n * flush — a trailing partial code point is dropped, as legacy did).\n * - `end()` parses the accumulated trailer (malformed → silently\n * ignored) and drops any un-terminated leading buffer, as legacy did.\n * It is IDEMPOTENT (a deliberate deviation from legacy): repeat calls\n * emit nothing, so an adapter that ends in both its completion path\n * and its `finally` cannot double-count the usage frame.\n *\n * State flow: leading → (sentinel | parse-failure) → text → (\\x1F) →\n * trailer-accumulate → end().\n *\n * Server-safe: no React, no timers, TextDecoder only.\n */\n\nimport type { ChatStreamEvent } from './events';\nimport type { UsageTelemetry } from './frames';\nimport { FRAME_TERMINATOR, END_OF_LEADING, TRAILER_SENTINEL } from './frames';\n// The frame table lives in its own module so it is reusable outside this\n// decoder. `frameNum` comes from there too: the trailing usage frame's token\n// counts must pass the SAME typeof gate the leading `usage:start` frame's do.\nimport { mapLeadingFrame, frameNum } from './leading-frames';\n\nexport interface SseFrameDecoder {\n /** Feed raw response bytes; returns the events they produced. */\n push(bytes: Uint8Array): ChatStreamEvent[];\n /**\n * Signal end-of-stream; parses the trailing usage frame if present.\n * IDEMPOTENT — every call after the first returns `[]`. Adapters\n * routinely call this from BOTH their completion path and a `finally`,\n * and a re-emitted `usage`/`stage:'end'` event would double-count token\n * usage (displayed cost doubles).\n */\n end(): ChatStreamEvent[];\n}\n\n// `escapeThinkingTags` moved to `./leading-frames` alongside the frame table,\n// and is re-exported here: consumers have imported it from this module since it\n// was written, and the `chat-protocol` barrel re-exports it from here.\nexport { escapeThinkingTags } from './leading-frames';\n\n/** Narrow a parsed leading frame to something the frame table can read. */\nfunction isFrameObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Narrow the trailer's `telemetry` blob to a real {@link UsageTelemetry}.\n *\n * Every field of that interface is a REQUIRED `number`, so a frame carrying a\n * partial blob (or non-numeric counts) is not one — passing it through would\n * hand consumers `undefined` where the type promises a number, the same shape\n * of lie that NaN'd the `breakdown.haiku*` sums. A blob that fails the gate is\n * dropped, matching this decoder's \"malformed trailer → silently ignored\"\n * contract; nothing downstream distinguishes absent from malformed.\n */\nfunction frameTelemetry(value: unknown): UsageTelemetry | undefined {\n if (!isFrameObject(value)) return undefined;\n const { cards, chips, sentences, answerLen } = value;\n if (\n typeof cards !== 'number' ||\n typeof chips !== 'number' ||\n typeof sentences !== 'number' ||\n typeof answerLen !== 'number'\n ) {\n return undefined;\n }\n return { cards, chips, sentences, answerLen };\n}\n\nexport function createSseFrameDecoder(): SseFrameDecoder {\n const textDecoder = new TextDecoder();\n let buffer = '';\n let inText = false;\n let inTrailer = false;\n let trailerBuffer = '';\n let ended = false;\n\n function push(bytes: Uint8Array): ChatStreamEvent[] {\n const out: ChatStreamEvent[] = [];\n const chunk = textDecoder.decode(bytes, { stream: true });\n\n if (!inText) {\n buffer += chunk;\n while (!inText) {\n const recIdx = buffer.indexOf(END_OF_LEADING);\n const nullIdx = buffer.indexOf(FRAME_TERMINATOR);\n if (recIdx !== -1 && (nullIdx === -1 || recIdx < nullIdx)) {\n inText = true;\n out.push({ type: 'turn-start' });\n const after = buffer.slice(recIdx + 1);\n buffer = '';\n if (after) {\n // The `after` slice may ALSO contain the `\\x1F` trailing-\n // usage sentinel — common for fixed-answer responses where\n // the whole frame sequence arrives in ONE TCP chunk.\n const unitIdx = after.indexOf(TRAILER_SENTINEL);\n if (unitIdx === -1) {\n out.push({ type: 'text-delta', text: after });\n } else {\n const textBefore = after.slice(0, unitIdx);\n const trailerAfter = after.slice(unitIdx + 1);\n if (textBefore) {\n out.push({ type: 'text-delta', text: textBefore });\n }\n inTrailer = true;\n trailerBuffer = trailerAfter;\n }\n }\n break;\n }\n if (nullIdx === -1) break; // need more bytes\n const metaStr = buffer.slice(0, nullIdx);\n const remaining = buffer.slice(nullIdx + 1);\n let meta: unknown;\n try {\n meta = JSON.parse(metaStr);\n } catch {\n // Not JSON — start of answer body. The WHOLE buffer (including\n // the `\\0`) becomes answer text; `implicit` tells consumers\n // this was the fallback path, not the `\\x1E` sentinel.\n inText = true;\n out.push({ type: 'turn-start', implicit: true });\n if (buffer.length > 0) {\n out.push({ type: 'text-delta', text: buffer });\n buffer = '';\n }\n break;\n }\n // A JSON scalar where a frame object was expected has no fields to\n // read, so it falls through the table's catch-all exactly as an empty\n // frame does — which is what the old `any` typing produced for every\n // `meta.<field>` lookup. (`null` used to THROW here instead.)\n mapLeadingFrame(isFrameObject(meta) ? meta : {}, out);\n buffer = remaining;\n }\n } else if (inTrailer) {\n trailerBuffer += chunk;\n } else {\n // Text mode: only the `\\x1F` trailer sentinel is scanned. The\n // unconditional emit (even for an empty decode of a partial\n // multi-byte code point) mirrors the legacy per-chunk yield.\n const sepIdx = chunk.indexOf(TRAILER_SENTINEL);\n if (sepIdx === -1) {\n out.push({ type: 'text-delta', text: chunk });\n } else {\n const before = chunk.slice(0, sepIdx);\n const after = chunk.slice(sepIdx + 1);\n if (before) out.push({ type: 'text-delta', text: before });\n inTrailer = true;\n trailerBuffer = after;\n }\n }\n return out;\n }\n\n function end(): ChatStreamEvent[] {\n // Idempotency guard: a second end() must emit NOTHING. The trailer is\n // also cleared so no later push()/end() pair can replay it.\n if (ended) return [];\n ended = true;\n const out: ChatStreamEvent[] = [];\n if (trailerBuffer.length > 0) {\n const raw = trailerBuffer;\n trailerBuffer = '';\n try {\n // Off the wire this is `unknown`, NOT the frame shape it claims to be.\n // A JSON scalar (or `null`) reaches the `kind` read below as a non-\n // object and simply fails the gate — no throw, no event.\n const meta: unknown = JSON.parse(raw);\n if (isFrameObject(meta) && meta.kind === 'usage' && (meta.stage === 'end' || meta.stage === 'display')) {\n // Token counts pass `frameNum`, the SAME gate the leading\n // `usage:start` frame's counts already pass. Un-gated, a wire value\n // of the wrong type (a string count from a server that stringifies\n // big integers) landed in a `number` field and rode straight into\n // `applySseUsage`'s `event.input_tokens ?? null` — which only\n // null-checks — and on into the displayed token totals.\n out.push({\n type: 'usage',\n stage: 'end',\n input_tokens: frameNum(meta.input_tokens),\n output_tokens: frameNum(meta.output_tokens),\n cache_read_input_tokens: frameNum(meta.cache_read_input_tokens),\n cache_creation_input_tokens: frameNum(meta.cache_creation_input_tokens),\n hit_rate_pct: frameNum(meta.hit_rate_pct),\n telemetry: frameTelemetry(meta.telemetry),\n // `breakdown` and `debug` are declared `unknown` on UsageEvent on\n // purpose — `applySseUsage` re-validates every nested field — so\n // they pass through raw, exactly as before.\n breakdown: meta.breakdown,\n debug: meta.debug,\n });\n }\n } catch {\n // Malformed trailer — silently ignore (legacy parity).\n }\n }\n return out;\n }\n\n return { push, end };\n}\n","/**\n * Message-related types\n * Contains all message structures, segments, and content types\n */\n\nimport type { ChatRef } from '../chat-ref.types';\nimport type { AssistantType, AuthorType, ChatApprovalStatus, MessageOwner } from './chat.types';\n\n// ========== Message Type Definitions ==========\n\nexport const MESSAGE_TYPE = {\n TEXT: 'TEXT',\n THINKING: 'THINKING',\n ASK: 'ASK',\n /** Per-answer source metadata (documents, videos, entity cards).\n *\n * TWO names for ONE payload, on purpose. `SOURCES` is the contract the\n * backend is moving to; `GUIDE` is the envelope it ships in today, reused\n * from Guide Mode v2 so a released frontend keeps working during the v2→v3\n * rollout. Both decode through the same path, so dropping `GUIDE` later is a\n * deletion here and nothing else. */\n GUIDE: 'GUIDE',\n SOURCES: 'SOURCES',\n EXECUTING_TOOL: 'EXECUTING_TOOL',\n EXECUTED_TOOL: 'EXECUTED_TOOL',\n APPROVAL_REQUEST: 'APPROVAL_REQUEST',\n APPROVAL_RESULT: 'APPROVAL_RESULT',\n ESCALATION_OFFER: 'ESCALATION_OFFER',\n TICKET_ESCALATED: 'TICKET_ESCALATED',\n TICKET_EVENT: 'TICKET_EVENT',\n ERROR: 'ERROR',\n MESSAGE_START: 'MESSAGE_START',\n MESSAGE_END: 'MESSAGE_END',\n MESSAGE_REQUEST: 'MESSAGE_REQUEST',\n AI_METADATA: 'AI_METADATA',\n TOKEN_USAGE: 'TOKEN_USAGE',\n CONTEXT_COMPACTION_START: 'CONTEXT_COMPACTION_START',\n CONTEXT_COMPACTION_END: 'CONTEXT_COMPACTION_END',\n DIRECT_MESSAGE: 'DIRECT_MESSAGE',\n SYSTEM: 'SYSTEM',\n DIALOG_CLOSED: 'DIALOG_CLOSED',\n} as const;\n\nexport type MessageType = (typeof MESSAGE_TYPE)[keyof typeof MESSAGE_TYPE];\n\n// ========== Scroll Anchor (per-message render hint) ==========\n\n/** Per-message viewport-positioning hint sent on the per-turn metadata\n * leading frame at the START of every assistant response. The chat\n * message-list reads it to override the default `use-stick-to-bottom`\n * tail behaviour for a single message. Field is OPTIONAL — when omitted\n * (or set to `'bottom'`) the chat tails as today. Only `'top'` opts in\n * to the alternative behaviour (used by display-action answers whose\n * body is a long article and should be read top-down). */\nexport const SCROLL_ANCHOR = { TOP: 'top', BOTTOM: 'bottom' } as const;\n\nexport type ScrollAnchor = (typeof SCROLL_ANCHOR)[keyof typeof SCROLL_ANCHOR];\n\n// ========== Tool Execution Types ==========\n\nexport interface ToolExecutionData {\n type: 'EXECUTING_TOOL' | 'EXECUTED_TOOL';\n integratedToolType: string;\n toolFunction: string;\n /** Backend-issued human-readable title (mirrors `PendingToolCallData.toolTitle`). */\n toolTitle?: string;\n /**\n * Backend-issued human-readable explanation of what the tool is doing and why\n * (mirrors `PendingToolCallData.toolExplanation`). Only sent on `EXECUTING_TOOL`;\n * the accumulator restores it onto the merged `EXECUTED_TOOL` segment.\n */\n toolExplanation?: string;\n parameters?: Record<string, unknown>;\n result?: string;\n success?: boolean;\n /**\n * Backend-issued id (matches `PendingToolCallData.toolExecutionRequestId`).\n * When present, lets the accumulator merge this execution event into the\n * matching approval batch row instead of emitting a standalone segment.\n */\n toolExecutionRequestId?: string;\n}\n\n/**\n * Snapshot of an in-flight tool kept between the `EXECUTING_TOOL` and\n * `EXECUTED_TOOL` events. The backend only sends `toolTitle` on\n * `EXECUTING_TOOL`; carrying this state lets the accumulator restore it onto\n * the merged `EXECUTED_TOOL` segment instead of falling back to the raw\n * `toolFunction`.\n */\nexport interface ExecutingToolState {\n integratedToolType: string;\n toolFunction: string;\n /** Mirrors {@link ToolExecutionData.toolTitle}; absent on `EXECUTED_TOOL`. */\n toolTitle?: string;\n /** Mirrors {@link ToolExecutionData.toolExplanation}; absent on `EXECUTED_TOOL`. */\n toolExplanation?: string;\n parameters?: Record<string, unknown>;\n}\n\n// ========== Approval Request Types ==========\n\nexport interface ApprovalRequestField {\n /** Short label — e.g. \"Subject\", \"Priority\". Rendered in a muted\n * caps style above the value. */\n label: string;\n /** Free-text value. Wraps and line-breaks are preserved\n * (`whitespace-pre-wrap`). */\n value: string;\n}\n\nexport interface ApprovalRequestData {\n command: string;\n /** Structured field list — preferred over `explanation`. When set,\n * the approval card renders a vertical label/value stack with\n * proper spacing. Falls back to `explanation` (a single paragraph)\n * when omitted. Keep BOTH when you want hosts on older lib\n * versions to still see the prose; new hosts should send only\n * `fields`. */\n fields?: ApprovalRequestField[];\n explanation?: string;\n icon?: ReactNode;\n requestId?: string;\n approvalRequestId?: string;\n approvalType?: string;\n}\n\nexport interface ApprovalResultData {\n approvalRequestId: string;\n approved: boolean;\n approvalType?: string;\n /** Display name of the user who resolved the request; null/absent for system actions. */\n resolvedByName?: string | null;\n}\n\n// ========== Escalation Offer Types ==========\n\nexport interface EscalationOfferData {\n offerId: string;\n /** Backend-fixed card copy; the client never composes it. */\n text: string;\n /** Why the offer was raised (`TOOL`, `MANUAL`, a trigger reason). Carried\n * for telemetry — every origin renders the same card. */\n origin?: string;\n}\n\n// ========== Ticket Escalated Types ==========\n\n/** Open union: the wire currently defines only `INACTIVITY`, and the block\n * renders from `text` rather than branching on this, so a reason added\n * server-side needs no client change. */\nexport type TicketEscalationReason = 'INACTIVITY' | (string & {});\n\n/** The handoff receipt — a real block on the wire, not something the client\n * infers from an escalation offer's state. */\nexport interface TicketEscalatedData {\n ticketId: string;\n ticketNumber?: number;\n reason: TicketEscalationReason;\n /** Backend-authored explanation; the card falls back to generic copy when\n * the wire omits it (the field is nullable). */\n text?: string;\n}\n\n// ========== Ticket Event Types ==========\n\n/** Known `TicketEventData.kind` values. The vocabulary is OPEN on the wire:\n * an unknown kind still renders (as a neutral info line) rather than being\n * dropped, so the backend can add lifecycle kinds without a client release. */\nexport const TICKET_EVENT_KIND = {\n RESOLVED: 'RESOLVED',\n REOPENED: 'REOPENED',\n} as const;\n\nexport type TicketEventKind = (typeof TICKET_EVENT_KIND)[keyof typeof TICKET_EVENT_KIND] | (string & {});\n\n/** Ticket lifecycle receipt (`TICKET_EVENT`) — the ticket was resolved,\n * reopened, etc. Same field names live (NATS chunk) and persisted\n * (GraphQL `TicketEventData` inside `messageData`), so ONE mapper covers\n * both paths. All actor fields are optional: the event stays renderable\n * when the backend can't attribute it. */\nexport interface TicketEventData {\n kind: TicketEventKind;\n actorId?: string;\n actorName?: string;\n /** Who acted — e.g. an AI agent vs a human. Open string like `kind`. */\n actorType?: string;\n reason?: string;\n /** Kind-token of the status the ticket reopened INTO (AI_ASSISTANCE /\n * TECH_REQUIRED / CUSTOM / ...) — same open vocabulary the lifecycle\n * statuses use. Picks the REOPENED card's subtitle deterministically;\n * absent on RESOLVED events and older backends. */\n targetStatusKind?: string;\n}\n\n/**\n * Single tool call inside a batch approval request.\n * Mirrors backend PendingToolCallDto.\n */\nexport interface PendingToolCallData {\n toolExecutionRequestId: string;\n toolName: string;\n toolTitle?: string;\n toolExplanation?: string;\n toolType?: string;\n requiresApproval: boolean;\n approvalType?: string | null;\n toolCallArguments?: Record<string, unknown> | null;\n}\n\n/**\n * Per-tool execution state inside an approval batch.\n * Populated by EXECUTING_TOOL / EXECUTED_TOOL chunks that carry a\n * `toolExecutionRequestId` matching one of the batch's tool calls.\n */\nexport interface ApprovalBatchExecutionState {\n status: 'executing' | 'done';\n result?: string;\n success?: boolean;\n}\n\nexport interface ApprovalBatchData {\n approvalRequestId: string;\n /** Highest approval type required across the batch (e.g. ADMIN beats CLIENT). */\n approvalType: string;\n toolCalls: PendingToolCallData[];\n /**\n * Keyed by `PendingToolCallData.toolExecutionRequestId`. Absent before\n * approval; rows without an entry render as \"queued\" (loader) once the\n * batch itself is approved.\n */\n executions?: Record<string, ApprovalBatchExecutionState>;\n}\n\n/** Approve/reject handler stamped onto approval segments. MAY resolve a\n * boolean success flag — `false` means the confirm FAILED (expired\n * proposal, network error); batch approve-all loops use it to tick the\n * row's failure cross. `void` (legacy transports) is treated as\n * success. Single source of truth for this signature — component\n * props, accumulator callbacks, and adapters all reference it. */\nexport type ApprovalResolutionHandler = (requestId?: string) => void | boolean | Promise<void | boolean>;\n\n// ========== Message Segment Types ==========\n\nexport type TextSegment = {\n type: 'text';\n text: string;\n};\n\nexport type ThinkingSegment = {\n type: 'thinking';\n text: string;\n};\n\n/** One reading the assistant offers in an `ask` card. `label` is BOTH the row's\n * headline and the exact text sent back when the row is picked — the backend's\n * guide classifier resolves the user's next message against the labels it\n * offered, so the reply must be the label verbatim. `description` is a short\n * clarifying line rendered under it. */\nexport type AskOptionData = {\n label: string;\n description?: string;\n};\n\n/** Clarification card — the assistant asking WHICH reading of an ambiguous\n * question it should answer, rendered as a heading plus a list of clickable\n * options instead of prose bullets. NATS-only (the `ASK` chunk); the intro\n * sentence riding the same chunk becomes an ordinary `text` segment in front\n * of the card, so it goes through the normal markdown body pipeline. Unlike\n * the three delta streams an ask arrives whole — it is never coalesced. */\nexport type AskSegment = {\n type: 'ask';\n question: string;\n options: AskOptionData[];\n};\n\nexport type ToolExecutionSegment = {\n type: 'tool_execution';\n data: ToolExecutionData;\n};\n\nexport type ApprovalRequestSegment = {\n type: 'approval_request';\n data: ApprovalRequestData & { approvalType?: string };\n status?: ChatApprovalStatus;\n /** Display name of the user who resolved the request; baked into the client\n * variant's full-text status pill (\"Approved by {name}\"). */\n resolvedByName?: string | null;\n onApprove?: ApprovalResolutionHandler;\n onReject?: ApprovalResolutionHandler;\n};\n\nexport type ApprovalBatchSegment = {\n type: 'approval_batch';\n data: ApprovalBatchData;\n status?: ChatApprovalStatus;\n /** Display name of the user who resolved the request; set when the batch is resolved (null/absent for system actions). */\n resolvedByName?: string | null;\n onApprove?: ApprovalResolutionHandler;\n onReject?: ApprovalResolutionHandler;\n};\n\n/**\n * Ticket-escalation offer block. Visually the client approval card (same\n * Figma component), but a SEPARATE segment type on purpose: it resolves\n * through the ticket-escalation GraphQL mutations rather than the tool\n * approval endpoint, and hosts that hide pending tool approvals from the\n * thread must still render this one inline.\n *\n * `cancelled` is the wire's SUPERSEDED — the client typed over the offer.\n */\nexport type EscalationOfferSegment = {\n type: 'escalation_offer';\n data: EscalationOfferData;\n status?: ChatApprovalStatus;\n resolvedByName?: string | null;\n onApprove?: ApprovalResolutionHandler;\n onReject?: ApprovalResolutionHandler;\n};\n\n/**\n * The conversation was handed off to a human technician. Decoded from the\n * `TICKET_ESCALATED` block, so it appears for every escalation path the\n * backend emits it for — including the inactivity auto-escalation, which\n * raises no offer and therefore has no offer state to infer from.\n */\nexport type TicketEscalatedSegment = {\n type: 'ticket_escalated';\n data: TicketEscalatedData;\n};\n\n/**\n * Ticket lifecycle receipt (resolved / reopened / an unknown future kind).\n * Arrives as a standalone `TICKET_EVENT` chunk outside MESSAGE_START/END —\n * same delivery shape as `ticket_escalated`.\n *\n * `streamSeq` is the chunk's JetStream sequence, kept ON the segment because\n * it is the event's only stable identity: the accumulator upserts on it so a\n * redelivered chunk (catch-up over hydrated history) replaces rather than\n * stacks a second card, while two genuinely distinct events of the same kind\n * (resolve → reopen → resolve) keep distinct sequences and both render.\n */\nexport type TicketEventSegment = {\n type: 'ticket_event';\n data: TicketEventData;\n streamSeq?: number;\n /** When THIS event happened (history: the persisted row's `createdAt`;\n * live: chunk arrival). Kept beside `streamSeq`, not in `data`: the\n * upsert's payload-equality fallback must keep matching a redelivered\n * chunk to its hydrated twin, and their times come from different clocks.\n * Without it the card inherits the surrounding assistant bubble's\n * timestamp — the FIRST row of the whole turn, so every lifecycle card\n * reads the same stale time. */\n occurredAt?: Date;\n};\n\nexport type ErrorSegment = {\n type: 'error';\n title: string;\n details?: string;\n};\n\nexport type ContextCompactionSegment = {\n type: 'context_compaction';\n status: 'started' | 'completed';\n summary?: string;\n};\n\nexport type MessageSegment =\n | TextSegment\n | ThinkingSegment\n | AskSegment\n | ToolExecutionSegment\n | ApprovalRequestSegment\n | ApprovalBatchSegment\n | EscalationOfferSegment\n | TicketEscalatedSegment\n | TicketEventSegment\n | ErrorSegment\n | ContextCompactionSegment;\n\nexport type MessageContent = string | MessageSegment[];\n\n// ========== Message Data Types (from GraphQL/API) ==========\n\nexport interface MessageDataBase {\n type: MessageType;\n}\n\nexport interface TextMessageData extends MessageDataBase {\n type: 'TEXT';\n text?: string;\n /** Entity references the user attached to the message. Wire shape is\n * `[{ type, id }]`; rows missing `id` are dropped by the history\n * processor, so `id` is optional here. */\n contextItems?: Array<{ type?: string; id?: string }>;\n}\n\nexport interface ThinkingMessageData extends MessageDataBase {\n type: 'THINKING';\n text?: string;\n}\n\n/**\n * A document cited by an assistant answer, rendered as a chip under it.\n *\n * Lives here, beside `Message`, because BOTH transports produce it now: the SSE\n * adapter reads it off the per-turn metadata frame, and the NATS path decodes it\n * out of a `GUIDE`/`SOURCES` chunk. It used to be declared inside\n * `use-sse-chat-adapter`, which made \"a Guide answer's citations\" structurally a\n * property of one transport — `use-sse-chat-adapter` still re-exports the name\n * so existing imports keep working.\n */\nexport interface ChatSource {\n /** 1-based citation number. `[1]` in the answer text refers to this. */\n index: number;\n name: string;\n path: string;\n documentType: string;\n externalUrl?: string;\n /** Platform that owns the destination at `externalUrl`. */\n targetPlatform?: string | null;\n /** Primary-key value for single-row chips. */\n id?: string;\n /** Per-row items for grouped chips. */\n items?: Array<{\n /** The row's own citation number. Present when the group was built\n * client-side from flat sources (`groupSourcesByTable`), so `[N]` in the\n * answer still resolves to a row; absent on a server-grouped chip, whose\n * rows share the group's one number. */\n index?: number;\n id: string;\n documentType: string;\n name: string;\n externalUrl?: string;\n targetPlatform?: string | null;\n /** In-app doc-tree path for markdown / data-room-doc rows so the\n * grouped chip's anchor can trigger an in-page doc-tree swap via\n * `handleChatNavClick` (parity with single-row chips + cards). */\n path?: string | null;\n }>;\n /** RagTableConfig.id for this source. */\n sourceRepo?: string;\n /** Optional display label override returned by the chat API. */\n label?: string;\n}\n\n/** Persisted source-metadata row. `payload` is the SAME object the live chunk\n * carries, so history and realtime share one decoder (`sourceMetadataEvent`)\n * and cannot drift apart. Left as an open record here rather than typed\n * structurally: it is unvalidated wire data, and the decoder is what turns it\n * into `ChatSource[]` / `ChatRef[]`. */\nexport interface SourceMetadataMessageData extends MessageDataBase {\n type: 'GUIDE' | 'SOURCES';\n payload?: unknown;\n}\n\n/** Persisted `ASK` row (GraphQL `AskData`). `text` is the intro sentence, which\n * history replays as a text segment ahead of the card — same split the live\n * `ASK` chunk carries. */\nexport interface AskMessageData extends MessageDataBase {\n type: 'ASK';\n text?: string;\n question?: string;\n /** WIRE shape — `label` is optional here because the backend can (and does)\n * ship label-less rows; `normalizeAskOptions` drops them, which is what\n * turns this into the card's `AskOptionData[]`. */\n options?: Array<{ label?: string; description?: string }>;\n}\n\nexport interface ExecutingToolMessageData extends MessageDataBase {\n type: 'EXECUTING_TOOL';\n integratedToolType?: string;\n toolFunction?: string;\n /** Backend-issued human-readable title (wire field, mirrors `ChunkData.title`). */\n title?: string;\n /** Backend-issued human-readable explanation (what/why) of the tool call. */\n toolExplanation?: string;\n parameters?: Record<string, unknown>;\n toolExecutionRequestId?: string;\n}\n\nexport interface ExecutedToolMessageData extends MessageDataBase {\n type: 'EXECUTED_TOOL';\n integratedToolType?: string;\n toolFunction?: string;\n /** Backend-issued human-readable title (wire field, mirrors `ChunkData.title`). */\n title?: string;\n parameters?: Record<string, unknown>;\n result?: string;\n success?: boolean;\n toolExecutionRequestId?: string;\n}\n\nexport interface ApprovalRequestMessageData extends MessageDataBase {\n type: 'APPROVAL_REQUEST';\n approvalRequestId?: string;\n approvalType?: string;\n command?: string;\n explanation?: string;\n /** Present when the approval is a batch of tool calls (new format). */\n toolCalls?: PendingToolCallData[];\n}\n\nexport interface ApprovalResultMessageData extends MessageDataBase {\n type: 'APPROVAL_RESULT';\n approvalRequestId?: string;\n approved?: boolean;\n approvalType?: string;\n /** Display name of the user who resolved the request; null/absent for system actions. */\n resolvedByName?: string | null;\n}\n\n/**\n * Persisted escalation-offer row. The PENDING row carries `text`/`origin`;\n * the resolution is a SECOND row with the same `offerId`, state\n * APPROVED/DECLINED/SUPERSEDED and no text — matched on replay by id.\n */\nexport interface EscalationOfferMessageData extends MessageDataBase {\n type: 'ESCALATION_OFFER';\n offerId?: string;\n state?: string;\n text?: string;\n origin?: string;\n resolvedByUserId?: string | null;\n resolvedByName?: string | null;\n}\n\nexport interface TicketEscalatedMessageData extends MessageDataBase {\n type: 'TICKET_ESCALATED';\n ticketId?: string;\n ticketNumber?: number;\n reason?: TicketEscalationReason;\n text?: string;\n}\n\n/** Persisted ticket lifecycle row — same field names as the live chunk\n * (see `TicketEventData`), decoded by the SAME mapper on replay. Fields are\n * wire-shaped (`| null`): every one is a nullable GraphQL scalar. */\nexport interface TicketEventMessageData extends MessageDataBase {\n type: 'TICKET_EVENT';\n kind?: string | null;\n actorId?: string | null;\n actorName?: string | null;\n actorType?: string | null;\n reason?: string | null;\n targetStatusKind?: string | null;\n}\n\nexport interface ErrorMessageData extends MessageDataBase {\n type: 'ERROR';\n error?: string;\n details?: string;\n}\n\nexport interface AIMetadataMessageData extends MessageDataBase {\n type: 'AI_METADATA';\n modelName?: string;\n providerName?: string;\n provider?: string;\n contextWindow?: number;\n}\n\nexport interface TokenUsageData {\n inputTokensSize: number;\n outputTokensSize: number;\n totalTokensSize: number;\n contextSize: number;\n}\n\nexport interface SystemMessageData extends MessageDataBase {\n type: 'SYSTEM';\n text?: string;\n}\n\n/**\n * Human-technician takeover row. A REALTIME-only concept: the history\n * processors have no branch for it, so a persisted `DIRECT_MESSAGE` row\n * contributes nothing on reload (pinned by the golden characterization test).\n * Declared here so the wire shape is nameable without `any`.\n */\nexport interface DirectMessageData extends MessageDataBase {\n type: 'DIRECT_MESSAGE';\n text?: string;\n}\n\nexport interface ContextCompactionStartMessageData extends MessageDataBase {\n type: 'CONTEXT_COMPACTION_START';\n}\n\nexport interface ContextCompactionEndMessageData extends MessageDataBase {\n type: 'CONTEXT_COMPACTION_END';\n summary?: string;\n}\n\nexport type MessageData =\n | TextMessageData\n | ThinkingMessageData\n | AskMessageData\n | SourceMetadataMessageData\n | ExecutingToolMessageData\n | ExecutedToolMessageData\n | ApprovalRequestMessageData\n | ApprovalResultMessageData\n | EscalationOfferMessageData\n | TicketEscalatedMessageData\n | TicketEventMessageData\n | ErrorMessageData\n | AIMetadataMessageData\n | SystemMessageData\n | DirectMessageData\n | ContextCompactionStartMessageData\n | ContextCompactionEndMessageData;\n\n// ========== Historical Message Types ==========\n\nexport interface HistoricalMessage {\n id: string;\n dialogId?: string;\n chatType?: string;\n createdAt: string;\n owner?: MessageOwner;\n messageData?: MessageData | MessageData[];\n /** Persisted stream sequence of this row's last chunk (the backend's\n * `lastChunkStreamSeq`). Passed through to the processed message's\n * `streamSeq` so `mergeHistoryWithRealtime` can decide coverage per-role\n * (a synthetic is covered only by a persisted row of its OWN role reaching\n * its seq). Optional — absent for rows the backend doesn't stamp (e.g. user\n * MESSAGE_REQUEST rows), which then don't participate in seq coverage. */\n lastChunkStreamSeq?: number | null;\n}\n\n// ========== Processed Message Types ==========\n\nexport interface ProcessedMessage {\n id: string;\n role: 'user' | 'assistant' | 'error'; // Limited to display roles\n content: MessageContent;\n name?: string;\n assistantType?: AssistantType;\n authorType?: AuthorType;\n timestamp: Date;\n avatar?: string;\n /** Persisted last-chunk stream sequence carried through from\n * `HistoricalMessage.lastChunkStreamSeq` (for assistant turns: the MAX\n * across the grouped rows). Hosts stamp it onto the rendered message's\n * `streamSeq` so the history/realtime merge can do per-role seq coverage.\n * Absent when the source row(s) carried no seq. */\n streamSeq?: number;\n /** Documents this answer cited — see `Message.sources`, the same field.\n * Replayed from the turn's persisted `GUIDE`/`SOURCES` row, so a reloaded\n * answer carries the citations the live one did. */\n sources?: ChatSource[];\n /** Entity references this answer's metadata described — see `Message.refs`. */\n refs?: ChatRef[];\n}\n\n// ========== Base Message Interface ==========\n\nimport type { ReactNode } from 'react';\nimport type { ChatContextItem } from './context-item.types';\n\nexport interface Message {\n id: string;\n role: 'user' | 'assistant' | 'error'; // Limited to display roles\n content: MessageContent;\n name?: string;\n assistantType?: AssistantType;\n authorType?: AuthorType;\n timestamp?: Date;\n avatar?: string | null;\n /** Highest CONTENT chunk streamSeq that composed this message. Stamped on\n * realtime synthetics so `mergeHistoryWithRealtime` can decide history\n * coverage per-message (see `MergeableChatMessage.streamSeq`). */\n streamSeq?: number;\n /** Entity-context items attached to this (user) message via the composer's\n * context picker. When present the message bubble renders the context\n * chips beneath its text (Figma node 31:28709). Optional — omitted for\n * assistant messages and turns sent without context. */\n contextItems?: ChatContextItem[];\n /** Per-message viewport-positioning hint. OPTIONAL — when omitted (the\n * default for every LLM Q&A / browse / search / find / Discuss path)\n * the chat tails as today via `use-stick-to-bottom`. Only `'top'` opts\n * in to the alternative top-anchor behaviour (display-action answers\n * whose body is a long article). The server is the sole decision-\n * maker — set on the metadata leading frame. */\n scrollAnchor?: ScrollAnchor;\n /** When true the message is part of the API conversation history (sent\n * to the LLM so it has context) but is NOT rendered in the chat UI.\n *\n * Used for \"synthetic continuation\" turns: when the user clicks Approve\n * on a tool proposal, the host auto-fires a follow-up `sendMessage`\n * with `hidden: true` carrying a directive like \"the user just\n * approved <tool>; ask follow-up questions per protocol\". The LLM's\n * response IS rendered (as a normal assistant message); only the\n * trigger prompt is suppressed so the chat reads naturally:\n *\n * user: \"open a ticket\"\n * assistant: preamble + approval card\n * [user clicks Approve]\n * assistant: \"Now to triage faster, can you share...\" ← auto-fires\n *\n * Without this flag the trigger prompt would surface as a confusing\n * bubble like \"(continue per protocol)\" between the approval card\n * and the AI's follow-up. */\n hidden?: boolean;\n /**\n * Documents this answer cited, rendered as chips beneath it.\n *\n * Per MESSAGE, not per conversation: an answer's citations belong to the\n * answer, and a thread can hold several answers that each cited something\n * different. Ordering is a rendering concern — see `splitCitedSources`.\n */\n sources?: ChatSource[];\n /**\n * Entity references this answer's metadata described — videos and cards.\n *\n * Presence here does NOT mean \"render it\": only the `[card://type:id]`\n * markers actually written into the answer text are rendered, and these\n * supply the data those markers expand with. The metadata frame routinely\n * describes more than the model chose to cite.\n */\n refs?: ChatRef[];\n}\n","/**\n * URL-safety primitives for the wire DECODER and the render-time guard.\n *\n * `UNSAFE_URL_CHARS` is shared by both: \"does this string carry characters that\n * have no business in an href\" had two copies once, and a security predicate\n * kept in sync by a comment drifts.\n *\n * `isSameOriginPath` answers \"does this path stay on our origin\" for the\n * render-time `safeHref` — the ONE gate every chat href passes. The wire decoder\n * does not judge links at all: it carries them, because resolving a link is a\n * client decision (only the rendering page knows where it is embedded), and a\n * second, stricter gate there only dropped links it could not resolve itself.\n *\n * Pure and dependency-free, so the server-safe `chat-protocol` leaf and the\n * client-side card utils can both import it directly.\n */\n\n/** Control, zero-width and line-separator characters. Never legitimate in a URL\n * we are about to render, and the classic way a payload smuggles one href past\n * a reader's eye. */\nexport const UNSAFE_URL_CHARS = /[\\u0000-\\u001f\\u007f\\u200b-\\u200d\\u2028\\u2029\\ufeff]/;\n\n/** Opaque base used only to ask the parser which origin a path lands on. */\nconst SAME_ORIGIN_PROBE = 'https://_sameorigin_.invalid';\n\n/**\n * Does this candidate stay on the CURRENT origin?\n *\n * Decided by RESOLVING it, never by inspecting the prefix. A prefix test is\n * what a reader reaches for — `startsWith('//')` — and it is not sufficient:\n * the URL parser treats a backslash as a slash in the authority position, so a\n * single-slash path can still resolve to a third-party host while passing every\n * \"starts with exactly one slash\" check. Any future parser quirk falls to the\n * same class. Asking the parser cannot disagree with what the browser will\n * actually do with the href.\n */\nexport function isSameOriginPath(candidate: string): boolean {\n try {\n return new URL(candidate, SAME_ORIGIN_PROBE).origin === SAME_ORIGIN_PROBE;\n } catch {\n return false;\n }\n}\n","/**\n * The `[card://<type>:<id>]` marker grammar — ONE definition, two readers.\n *\n * The model writes these markers into the answer body, and the metadata frame\n * describes the same references separately. Two parsers therefore have to agree\n * on what counts as a marker: the payload decoder (`source-metadata.ts`) and the\n * body renderer (`chat-message-enhanced.tsx`). When they disagree the failure is\n * silent and one-sided — the stricter side drops a reference the other accepted,\n * and the reader is left looking at raw `[card://…]` text in a sentence.\n *\n * The id half is deliberately permissive: the backend validates it as \"anything\n * but `]` and newlines\", and a client that narrows that is the side that breaks.\n * `)` is excluded on top because the body form also terminates on it (a marker\n * written as a markdown link target).\n *\n * Server-safe: no React, no browser APIs.\n */\n\n/** Entity-type half. A closed vocabulary of slug-shaped names. */\nconst CARD_TYPE = '[a-zA-Z0-9_-]+';\n\n/** Id half — see the permissiveness note above. */\nconst CARD_ID = '[^\\\\]\\\\r\\\\n)]+';\n\n/**\n * A marker standing ALONE, anchored — for validating a `ref` field in the\n * metadata payload, where the whole string must be the marker and nothing else.\n */\nexport const CARD_REFERENCE = new RegExp(`^\\\\[card://(${CARD_TYPE}):(${CARD_ID})\\\\]$`);\n\n/**\n * A marker EMBEDDED in answer text, unanchored and global.\n *\n * Terminates on `]` or `)` so a marker used as a markdown link target\n * (`[card://x:y](…)`) is matched too. Being global, it carries `lastIndex`\n * state — reset it before each scan.\n */\nexport function createCardMarkerScanner(): RegExp {\n return new RegExp(`\\\\[card://(${CARD_TYPE}):(${CARD_ID})[\\\\])]`, 'g');\n}\n\n/** One `[card://<type>:<id>]` reference, split into its two halves. */\nexport interface CardReference {\n type: string;\n id: string;\n}\n\n/**\n * Every card reference EMBEDDED in a text, in order of first appearance,\n * each once. The server reads the references an answer cites through this\n * (the hub's MCP `ask_guide` returns them as `cards[]`), so a host and the\n * body renderer can never disagree about which markers a text carries.\n */\nexport function extractCardReferences(text: string): CardReference[] {\n const seen = new Set<string>();\n const out: CardReference[] = [];\n for (const match of text.matchAll(createCardMarkerScanner())) {\n const key = `${match[1]}:${match[2]}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push({ type: match[1], id: match[2] });\n }\n return out;\n}\n","/**\n * Narrowing primitives for untrusted hub JSON — ONE owner, shared by every\n * client that talks to a hub route (`use-chat-history-hydration`, the\n * conversation-list client, the NATS decoder).\n *\n * `unwrapEnvelope` encodes the response contract in one place. The hub's\n * route-base `successResponse` is `NextResponse.json(data)` — a RAW body, which\n * is the shape these clients normally see. The `{ data }` branch is defensive,\n * for an embedder or gateway that proxies our routes inside an envelope of its\n * own; it is not the hub's shape. (A payload whose own top-level key is `data`\n * would be unwrapped by mistake — none of ours has one.)\n */\n\n/** `typeof null === 'object'`, so the null check is the whole point. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** route-base `{ data }` envelope, with a raw-body fallback. */\nexport function unwrapEnvelope(payload: unknown): unknown {\n return isRecord(payload) && 'data' in payload ? payload.data : payload;\n}\n","/**\n * Source-metadata payload → `{ sources, refs }`.\n *\n * ONE decoder for two arrival paths: the live `GUIDE`/`SOURCES` NATS chunk and\n * the persisted row of the same name in dialog history. That is the whole point\n * of putting it here rather than in either consumer — a reloaded thread has to\n * render identically to the live turn, and two parsers is exactly how that stops\n * being true.\n *\n * Everything is validated, and anything that fails is DROPPED rather than\n * repaired: this payload is assembled from a remote MCP server's tool output,\n * so a malformed row means an upstream contract slip, and rendering a chip with\n * a blank title or an unsafe href is worse than rendering nothing.\n *\n * LINKS are carried, not judged. A source's `externalUrl` is passed through as\n * the producer sent it — a root-relative path included — because resolving a\n * link is a CLIENT decision: only the page rendering the chip knows where it is\n * embedded. The chat runtime resolves a relative href against the embedder's\n * `defaultContentOrigin` in embed mode and keeps it relative in host mode\n * (`resolveHrefForRuntime`), and every chip href passes the ONE safety gate,\n * `safeHref`, at render (`buildSourceRowCta`). This decoder used to be a second,\n * STRICTER gate that dropped what it could not resolve on its own — every\n * relative link, and any `http:` one — leaving chips with a name and no\n * destination. That is how broken links were made; it is not how they were\n * prevented.\n *\n * MEDIA urls (`<video src>`, posters) are still required to be absolute https:\n * they have no render-time gate and no navigation runtime to resolve them.\n *\n * Server-safe: no React, no browser APIs beyond `URL`.\n */\n\nimport type { ChatRef } from '../components/chat/chat-ref.types';\nimport type { ChatSource } from '../components/chat/types/message.types';\nimport { UNSAFE_URL_CHARS } from '../utils/url-safety';\nimport { CARD_REFERENCE } from './card-marker';\nimport type { SourcesEvent } from './events';\nimport { isRecord } from './wire-narrow';\n\n/** YouTube ids are exactly 11 chars of the URL-safe alphabet. */\nconst YOUTUBE_ID = /^[A-Za-z0-9_-]{11}$/;\n\n/** Video URL fields carried through verbatim, once validated as https. */\nconst VIDEO_URL_KEYS = ['videoUrl', 'highlightVideoUrl', 'videoPoster', 'highlightVideoPoster'] as const;\n\nfunction text(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\n/**\n * `null` survives, anything non-string becomes `undefined`.\n *\n * The distinction is load-bearing for `targetPlatform` and `path`, where the\n * backend uses an explicit `null` to mean \"this row has no destination\" — which\n * downstream navigation treats differently from \"the field was not sent\".\n */\nfunction nullableText(value: unknown): string | null | undefined {\n return value === null ? null : text(value);\n}\n\n/** An absolute https URL, or nothing — for MEDIA sources only (see the module\n * note). Links are carried as text and judged once, at render. */\nfunction httpsUrl(value: unknown): string | undefined {\n const candidate = text(value);\n if (!candidate || UNSAFE_URL_CHARS.test(candidate)) return undefined;\n try {\n return new URL(candidate).protocol === 'https:' ? candidate : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * A YouTube video id, from either a bare id or any of the watch/short/embed URL\n * shapes. Returns the ID, never a URL — the player takes an id.\n */\nexport function youtubeVideoId(value: unknown): string | undefined {\n const candidate = text(value);\n if (!candidate) return undefined;\n if (YOUTUBE_ID.test(candidate)) return candidate;\n\n const url = httpsUrl(candidate);\n if (!url) return undefined;\n const parsed = new URL(url);\n const hostname = parsed.hostname.toLowerCase();\n const isYoutubeHost = hostname === 'youtu.be' || hostname === 'youtube.com' || hostname.endsWith('.youtube.com');\n if (!isYoutubeHost) return undefined;\n\n const fromPath =\n hostname === 'youtu.be'\n ? parsed.pathname.split('/').filter(Boolean)[0]\n : /^\\/(?:embed|v|shorts)\\/([A-Za-z0-9_-]{11})(?:\\/|$)/.exec(parsed.pathname)?.[1];\n const id = fromPath ?? parsed.searchParams.get('v') ?? undefined;\n return id && YOUTUBE_ID.test(id) ? id : undefined;\n}\n\nfunction sourceItems(value: unknown): NonNullable<ChatSource['items']> | undefined {\n if (!Array.isArray(value)) return undefined;\n const items = value.filter(isRecord).flatMap(item => {\n const id = text(item.id);\n const documentType = text(item.documentType);\n const name = text(item.name);\n // A grouped row without these three has nothing to render OR navigate with.\n if (!id || !documentType || !name) return [];\n\n const externalUrl = text(item.externalUrl);\n const targetPlatform = nullableText(item.targetPlatform);\n const path = nullableText(item.path);\n return [\n {\n id,\n documentType,\n name,\n ...(externalUrl ? { externalUrl } : {}),\n ...(targetPlatform !== undefined ? { targetPlatform } : {}),\n ...(path !== undefined ? { path } : {}),\n },\n ];\n });\n return items.length > 0 ? items : undefined;\n}\n\n/**\n * Cited documents, keyed by their citation number.\n *\n * `index` and `name` are required because they ARE the chip: the number the\n * answer's `[1]` refers to, and the text on it. `path` is NOT required — it is\n * one of several ways a chip resolves a destination (an `externalUrl` or a\n * grouped item can carry it instead), and a source is still worth showing when\n * it has none.\n *\n * A duplicate `index` keeps the first occurrence: two chips answering to the\n * same `[1]` is not a state the strip can render meaningfully.\n */\nfunction sources(value: unknown): ChatSource[] {\n if (!Array.isArray(value)) return [];\n const claimed = new Set<number>();\n const decoded: ChatSource[] = [];\n\n for (const candidate of value) {\n if (!isRecord(candidate)) continue;\n const index = candidate.index;\n const name = text(candidate.name);\n const isUsableIndex = typeof index === 'number' && Number.isInteger(index) && index > 0 && !claimed.has(index);\n if (!isUsableIndex || !name) continue;\n claimed.add(index);\n\n const path = text(candidate.path);\n const documentType = text(candidate.documentType);\n const externalUrl = text(candidate.externalUrl);\n const targetPlatform = nullableText(candidate.targetPlatform);\n const id = text(candidate.id);\n const sourceRepo = text(candidate.sourceRepo);\n const label = text(candidate.label);\n const items = sourceItems(candidate.items);\n\n decoded.push({\n index,\n name,\n // Both are typed non-optional on `ChatSource` and predate this decoder;\n // '' is the established \"no value\" for them across the SSE path.\n path: path ?? '',\n documentType: documentType ?? '',\n ...(externalUrl ? { externalUrl } : {}),\n ...(targetPlatform !== undefined ? { targetPlatform } : {}),\n ...(id ? { id } : {}),\n ...(sourceRepo ? { sourceRepo } : {}),\n ...(label ? { label } : {}),\n ...(items ? { items } : {}),\n });\n }\n return decoded;\n}\n\n/**\n * Player metadata for a video ref, in the key vocabulary the card dispatcher\n * already reads (`itemVideoMetadata` / `decodeVideoMarkerId` in\n * `entity-cards/dispatch.tsx`).\n *\n * `youtubeUrl` holds a bare video ID, not a URL. The name is inaccurate and\n * predates this decoder — but it is the established key on both the producing\n * and the consuming side, and a more honest name here would simply be a field\n * nothing reads. Renaming it is a separate change that has to move both ends at\n * once.\n *\n * `videoUrl` and `youtubeUrl` stay mutually exclusive: they select different\n * players (a `<video>`/HLS source vs an embed facade), so carrying both would\n * make the renderer guess.\n */\nfunction videoMetadata(value: unknown, url: string | null): Record<string, string> {\n const metadata: Record<string, string> = {};\n if (isRecord(value)) {\n for (const key of VIDEO_URL_KEYS) {\n const validated = httpsUrl(value[key]);\n if (validated) metadata[key] = validated;\n }\n const declaredId = youtubeVideoId(value.youtubeUrl);\n if (declaredId) metadata.youtubeUrl = declaredId;\n }\n\n // The top-level `url` is the authority when present: it is what the backend\n // resolved for THIS video, while `metadata` is passthrough from the row.\n if (url) {\n const idFromUrl = youtubeVideoId(url);\n if (idFromUrl) {\n metadata.youtubeUrl = idFromUrl;\n delete metadata.videoUrl;\n } else {\n metadata.videoUrl = url;\n delete metadata.youtubeUrl;\n }\n }\n return metadata;\n}\n\n/** Video references. `ref` must parse AND be of type `video` — a mistyped row\n * would otherwise render through the entity-card path with no player. */\nfunction videos(value: unknown): ChatRef[] {\n if (!Array.isArray(value)) return [];\n return value.filter(isRecord).flatMap(video => {\n const match = typeof video.ref === 'string' ? CARD_REFERENCE.exec(video.ref) : null;\n if (!match || match[1] !== 'video') return [];\n\n const id = match[2];\n const url = httpsUrl(video.url) ?? null;\n const sourceRepo = text(video.sourceRepo);\n const metadata = videoMetadata(video.metadata, url);\n return [\n {\n type: 'video',\n id,\n title: text(video.title) ?? id,\n url,\n ...(sourceRepo ? { sourceRepo } : {}),\n ...(Object.keys(metadata).length > 0 ? { metadata } : {}),\n },\n ];\n });\n}\n\n/**\n * Entity-card references, enriched from `sources` where the two describe the\n * same row.\n *\n * A card carries only `type` + `id` on the wire; its human title and link live\n * on the matching source. Without the match the card still renders — with its\n * id as the title, which is ugly but honest, and better than dropping a card\n * the answer text explicitly points at.\n *\n * `entityType`/`entityId` are cross-checked against the parsed `ref` because\n * they are three statements of the same fact, and a disagreement means the row\n * was assembled wrongly upstream.\n */\nfunction cards(value: unknown, decodedSources: ChatSource[]): ChatRef[] {\n if (!Array.isArray(value)) return [];\n return value.filter(isRecord).flatMap(card => {\n const match = typeof card.ref === 'string' ? CARD_REFERENCE.exec(card.ref) : null;\n // `video` is handled by `videos()`; a card claiming that type would render\n // the wrong component.\n if (!match || match[1] === 'video') return [];\n\n const type = text(card.entityType);\n const id = text(card.entityId);\n if (!type || !id || type !== match[1] || id !== match[2]) return [];\n\n const source = decodedSources.find(candidate => candidate.id === id && candidate.documentType === type);\n return [\n {\n type,\n id,\n title: source?.name ?? id,\n url: source?.externalUrl ?? null,\n ...(source?.sourceRepo ? { sourceRepo: source.sourceRepo } : {}),\n ...(source?.targetPlatform !== undefined ? { targetPlatform: source.targetPlatform } : {}),\n ...(source?.path ? { metadata: { path: source.path } } : {}),\n },\n ];\n });\n}\n\n/** Last `type:id` wins nothing — the FIRST occurrence is kept, so a payload that\n * lists a card twice cannot flip which copy the body expands to. */\nfunction deduplicateRefs(refs: ChatRef[]): ChatRef[] {\n const byIdentity = new Map<string, ChatRef>();\n for (const ref of refs) {\n const identity = `${ref.type}:${ref.id}`;\n if (!byIdentity.has(identity)) byIdentity.set(identity, ref);\n }\n return [...byIdentity.values()];\n}\n\n/**\n * Decode one source-metadata payload into its event, or `null` when there is\n * nothing usable in it.\n *\n * Returning `null` for an empty result matters: the reducer stamps this onto a\n * message, and an event carrying two empty arrays would overwrite metadata that\n * an earlier chunk of the same turn had already supplied.\n */\nexport function sourceMetadataEvent(payload: unknown): SourcesEvent | null {\n if (!isRecord(payload)) return null;\n\n const decodedSources = sources(payload.sources);\n const refs = deduplicateRefs([...videos(payload.videos), ...cards(payload.cards, decodedSources)]);\n if (decodedSources.length === 0 && refs.length === 0) return null;\n\n return {\n type: 'sources',\n ...(decodedSources.length > 0 ? { sources: decodedSources } : {}),\n ...(refs.length > 0 ? { refs } : {}),\n };\n}\n\n/** What one assistant answer carries: its citations and its expandable refs. */\nexport interface SourceMetadata {\n sources?: ChatSource[];\n refs?: ChatRef[];\n}\n\n/**\n * Fold one decoded chunk into an answer's accumulated metadata.\n *\n * A single turn can call several remote tools, each returning its own metadata,\n * so this merges rather than replaces — and it is shared by the live reducer and\n * the history replay precisely because those two must agree on what a reloaded\n * answer shows.\n *\n * First writer wins per identity (`index` for a source, `type:id` for a ref),\n * the same rule the decoder applies within one payload.\n *\n * KNOWN LIMIT: citation numbers are assigned per tool result, so two tool calls\n * in one turn can both claim `[1]`. The later source is dropped rather than\n * renumbered — by then the numbers are already written into the answer text,\n * and renumbering would point `[1]` at a document the sentence is not about.\n */\nexport function mergeSourceMetadata(previous: SourceMetadata | null, event: SourcesEvent): SourceMetadata {\n const byIndex = new Map<number, ChatSource>();\n for (const source of [...(previous?.sources ?? []), ...(event.sources ?? [])]) {\n if (!byIndex.has(source.index)) byIndex.set(source.index, source);\n }\n const byIdentity = new Map<string, ChatRef>();\n for (const ref of [...(previous?.refs ?? []), ...(event.refs ?? [])]) {\n const identity = `${ref.type}:${ref.id}`;\n if (!byIdentity.has(identity)) byIdentity.set(identity, ref);\n }\n return {\n ...(byIndex.size > 0 ? { sources: [...byIndex.values()] } : {}),\n ...(byIdentity.size > 0 ? { refs: [...byIdentity.values()] } : {}),\n };\n}\n","/**\n * NATS chunk → normalized `ChatStreamEvent` decoder.\n *\n * THE live NATS reading path — the ONLY chunk parser in the codebase. Every\n * consumer (this lib's `useNatsChatAdapter`, the hub, the product app) feeds\n * raw chunks through `decodeNatsChunk` into `createChatStreamReducer`. The\n * legacy `components/chat/utils/chunk-parser.ts` (`parseChunkToAction`) that\n * this module superseded was DELETED — do not reintroduce a second decoder.\n *\n * Chunks map onto the transport-agnostic event union, with the JetStream\n * `streamSeq` lifted into the `seq` envelope (the reducer's idempotency gate\n * keys off it).\n *\n * Behavior is pinned by `__tests__/nats-decoder-golden.test.ts`.\n *\n * Server-safe: no React, no browser APIs.\n */\n\nimport { MESSAGE_TYPE } from '../components/chat/types/message.types';\nimport type { AskOptionData } from '../components/chat/types/message.types';\nimport { ESCALATION_STATE, escalationResolvedStatus } from './events';\nimport type { ApprovalToolCall, ChatStreamEvent } from './events';\nimport { sourceMetadataEvent } from './source-metadata';\nimport { isRecord } from './wire-narrow';\n\n/** Minimal structural view of a NATS chunk (see `ChunkData` in\n * `src/components/chat/types/network.types.ts`). */\ntype NatsChunk = Record<string, unknown>;\n\n/** Wire string field, or `undefined` when the backend sent anything else. */\nfunction str(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\n/** Wire string field with a default — replaces the old `data.x || 'y'`, which\n * also let non-strings through under an `any` chunk type. */\nfunction strOr(value: unknown, fallback = ''): string {\n const s = str(value);\n return s ? s : fallback;\n}\n\n/** Wire number field with a default. */\nfunction numOr(value: unknown, fallback = 0): number {\n return typeof value === 'number' ? value : fallback;\n}\n\n/** Coerce the wire's `toolCalls[]` into the batch-approval shape, dropping\n * non-object entries and defaulting every field. */\nfunction normalizeToolCalls(raw: unknown): ApprovalToolCall[] {\n if (!Array.isArray(raw)) return [];\n return raw.filter(isRecord).map(item => ({\n toolExecutionRequestId: String(item.toolExecutionRequestId ?? ''),\n toolName: String(item.toolName ?? ''),\n toolTitle: typeof item.toolTitle === 'string' ? item.toolTitle : undefined,\n toolExplanation: typeof item.toolExplanation === 'string' ? item.toolExplanation : undefined,\n toolType: typeof item.toolType === 'string' ? item.toolType : undefined,\n requiresApproval: item.requiresApproval === true,\n approvalType: typeof item.approvalType === 'string' ? item.approvalType : null,\n toolCallArguments: isRecord(item.toolCallArguments) ? item.toolCallArguments : null,\n }));\n}\n\n/** Coerce the wire's ask `options[]` into the card's row shape. Rows without a\n * usable `label` are dropped: the label is what gets SENT when the row is\n * picked, so a blank one would post an empty reply. */\nexport function normalizeAskOptions(raw: unknown): AskOptionData[] {\n if (!Array.isArray(raw)) return [];\n return raw\n .filter(isRecord)\n .map(item => ({\n label: typeof item.label === 'string' ? item.label.trim() : '',\n description: typeof item.description === 'string' ? item.description : undefined,\n }))\n .filter(option => option.label.length > 0);\n}\n\n/**\n * Parse one raw NATS chunk into a normalized event. Returns `null` for\n * unknown/malformed chunks — an unrecognized `type`, a missing required\n * field, or a non-object payload are all tolerated as no-ops rather than\n * throwing, so a backend that adds a chunk type can't break the stream.\n */\nexport function decodeNatsChunk(chunk: unknown): ChatStreamEvent | null {\n if (!chunk || typeof chunk !== 'object') return null;\n\n const data = chunk as NatsChunk;\n const type = String(data.type || '');\n // JetStream stream sequence → the generic `seq` envelope.\n const seq: { seq?: number } = typeof data.streamSeq === 'number' ? { seq: data.streamSeq } : {};\n\n switch (type) {\n case MESSAGE_TYPE.MESSAGE_START:\n return { type: 'turn-start', ...seq };\n\n case MESSAGE_TYPE.MESSAGE_END:\n return { type: 'turn-end', ...seq };\n\n case MESSAGE_TYPE.AI_METADATA: {\n const providerName = data.providerName || data.provider;\n if (typeof data.modelName === 'string' && typeof providerName === 'string') {\n return {\n type: 'metadata',\n modelLabel: str(data.modelDisplayName),\n modelName: data.modelName,\n provider: providerName,\n contextWindowMaxTokens: typeof data.contextWindow === 'number' ? data.contextWindow : 0,\n ...seq,\n };\n }\n return null;\n }\n\n case MESSAGE_TYPE.TEXT:\n if (typeof data.text === 'string') {\n return { type: 'text-delta', text: data.text, ...seq };\n }\n return null;\n\n case MESSAGE_TYPE.THINKING:\n if (typeof data.text === 'string') {\n return { type: 'thinking-delta', text: data.text, ...seq };\n }\n return null;\n\n // Two chunk names, one payload and one decoder. `SOURCES` is the contract\n // the backend is moving to; `GUIDE` is the v2 envelope it ships in during\n // the rollout (see `MESSAGE_TYPE.GUIDE`). The chunk arrives BEFORE or\n // between the answer's text deltas, so the reducer — not this decoder —\n // owns attaching it to the right turn.\n case MESSAGE_TYPE.GUIDE:\n case MESSAGE_TYPE.SOURCES: {\n const event = sourceMetadataEvent(data.payload);\n return event ? { ...event, ...seq } : null;\n }\n\n // An ask card is only an ask card with something to pick: a question and at\n // least one option. Anything less is dropped rather than rendered as an\n // empty card the user can't answer — the backend already falls back to a\n // complete hardcoded card when the model returns a partial one.\n case MESSAGE_TYPE.ASK: {\n const question = typeof data.question === 'string' ? data.question.trim() : '';\n const options = normalizeAskOptions(data.options);\n if (!question || options.length === 0) return null;\n return {\n type: 'ask',\n ...(typeof data.text === 'string' && data.text ? { text: data.text } : {}),\n question,\n options,\n ...seq,\n };\n }\n\n case MESSAGE_TYPE.EXECUTING_TOOL:\n return {\n type: 'tool-execution',\n data: {\n type: 'EXECUTING_TOOL',\n integratedToolType: strOr(data.integratedToolType),\n toolFunction: strOr(data.toolFunction),\n toolTitle: typeof data.title === 'string' ? data.title : undefined,\n // The human-readable \"why this tool is running\" line rendered inside\n // the tool card. It rides ONLY the EXECUTING chunk — the EXECUTED one\n // never carries it, and the accumulator's merge preserves whatever\n // the EXECUTING segment held. Dropping it here therefore blanks the\n // card for the whole lifetime of the run.\n toolExplanation: typeof data.toolExplanation === 'string' ? data.toolExplanation : undefined,\n parameters: isRecord(data.parameters) ? data.parameters : undefined,\n toolExecutionRequestId:\n typeof data.toolExecutionRequestId === 'string' ? data.toolExecutionRequestId : undefined,\n },\n ...seq,\n };\n\n case MESSAGE_TYPE.EXECUTED_TOOL:\n return {\n type: 'tool-execution',\n data: {\n type: 'EXECUTED_TOOL',\n integratedToolType: strOr(data.integratedToolType),\n toolFunction: strOr(data.toolFunction),\n toolTitle: typeof data.title === 'string' ? data.title : undefined,\n parameters: isRecord(data.parameters) ? data.parameters : undefined,\n result: str(data.result),\n success: typeof data.success === 'boolean' ? data.success : undefined,\n toolExecutionRequestId:\n typeof data.toolExecutionRequestId === 'string' ? data.toolExecutionRequestId : undefined,\n },\n ...seq,\n };\n\n case MESSAGE_TYPE.APPROVAL_REQUEST: {\n const requestId = strOr(data.approvalRequestId) || strOr(data.approval_request_id);\n const approvalType = strOr(data.approvalType, 'USER');\n const toolCalls = normalizeToolCalls(data.toolCalls);\n\n if (toolCalls.length > 0) {\n return { type: 'approval-request', requestId, approvalType, toolCalls, ...seq };\n }\n\n return {\n type: 'approval-request',\n requestId,\n approvalType,\n command: strOr(data.command),\n explanation: str(data.explanation),\n ...seq,\n };\n }\n\n case MESSAGE_TYPE.APPROVAL_RESULT: {\n // Realtime chunks carry the resolver's name as `displayName`; the\n // persisted GraphQL message exposes the same value as\n // `resolvedByName`. Accept either so realtime and history-replay\n // render \"by {name}\" identically.\n const resolvedByName =\n typeof data.resolvedByName === 'string'\n ? data.resolvedByName\n : typeof data.displayName === 'string'\n ? data.displayName\n : undefined;\n return {\n type: 'approval-resolved',\n requestId: strOr(data.approvalRequestId) || strOr(data.approval_request_id),\n status: data.approved === true ? 'approved' : 'rejected',\n approvalType: strOr(data.approvalType, 'CLIENT'),\n resolvedByName,\n ...seq,\n };\n }\n\n case MESSAGE_TYPE.ESCALATION_OFFER: {\n const offerId = typeof data.offerId === 'string' ? data.offerId : '';\n if (!offerId) return null;\n if (data.state === ESCALATION_STATE.PENDING) {\n return {\n type: 'escalation-offer',\n offerId,\n text: typeof data.text === 'string' ? data.text : '',\n origin: typeof data.origin === 'string' ? data.origin : undefined,\n ...seq,\n };\n }\n const status = escalationResolvedStatus(data.state);\n if (!status) return null;\n // Same realtime/history field split as APPROVAL_RESULT: the chunk names\n // the resolver `displayName`, the persisted row `resolvedByName`.\n return {\n type: 'escalation-offer-resolved',\n offerId,\n status,\n resolvedByName:\n typeof data.resolvedByName === 'string'\n ? data.resolvedByName\n : typeof data.displayName === 'string'\n ? data.displayName\n : undefined,\n ...seq,\n };\n }\n\n case MESSAGE_TYPE.TICKET_ESCALATED: {\n const ticketId = typeof data.ticketId === 'string' ? data.ticketId : '';\n const reason = typeof data.reason === 'string' ? data.reason : '';\n // Both are non-null on the wire; a payload missing either is malformed\n // rather than a variant to render.\n if (!ticketId || !reason) return null;\n return {\n type: 'ticket-escalated',\n ticketId,\n reason,\n ticketNumber: typeof data.ticketNumber === 'number' ? data.ticketNumber : undefined,\n text: typeof data.text === 'string' ? data.text : undefined,\n ...seq,\n };\n }\n\n case MESSAGE_TYPE.TICKET_EVENT: {\n // `kind` is the only required field, and deliberately an OPEN string:\n // unknown kinds must render (as a neutral line), not be dropped.\n const kind = typeof data.kind === 'string' ? data.kind.trim() : '';\n if (!kind) return null;\n // This chunk names its own JetStream sequence `sequenceId` in the\n // payload (the persisted row's `lastChunkStreamSeq` equals it — the\n // dedupe key). Prefer the transport-stamped `streamSeq` envelope like\n // every other chunk; fall back to the payload copy when absent.\n const eventSeq: { seq?: number } =\n seq.seq !== undefined ? seq : typeof data.sequenceId === 'number' ? { seq: data.sequenceId } : {};\n return {\n type: 'ticket-event',\n kind,\n actorId: typeof data.actorId === 'string' ? data.actorId : undefined,\n actorName: typeof data.actorName === 'string' ? data.actorName : undefined,\n actorType: typeof data.actorType === 'string' ? data.actorType : undefined,\n reason: typeof data.reason === 'string' && data.reason.trim() ? data.reason : undefined,\n targetStatusKind:\n typeof data.targetStatusKind === 'string' && data.targetStatusKind.trim() ? data.targetStatusKind : undefined,\n ...eventSeq,\n };\n }\n\n case MESSAGE_TYPE.ERROR:\n return {\n type: 'error',\n title: strOr(data.error, 'An error occurred'),\n details: str(data.details),\n ...seq,\n };\n\n case MESSAGE_TYPE.MESSAGE_REQUEST:\n return {\n type: 'participant',\n kind: 'message-request',\n text: String(data.text || ''),\n ownerType: typeof data.ownerType === 'string' ? data.ownerType : undefined,\n displayName: typeof data.displayName === 'string' ? data.displayName : undefined,\n userId: typeof data.userId === 'string' ? data.userId : undefined,\n // Wire shape carries no label; fall back to the id (parity with\n // the history path in `process-historical-messages.ts`).\n contextItems: Array.isArray(data.contextItems)\n ? (data.contextItems as Array<{ type?: unknown; id?: unknown }>)\n .filter(it => typeof it?.type === 'string' && typeof it?.id === 'string')\n .map(it => ({\n type: it.type as string,\n id: it.id as string,\n label: it.id as string,\n }))\n : undefined,\n ...seq,\n };\n\n case MESSAGE_TYPE.TOKEN_USAGE:\n return {\n type: 'token-usage',\n inputTokensSize: numOr(data.inputTokensSize),\n outputTokensSize: numOr(data.outputTokensSize),\n totalTokensSize: numOr(data.totalTokensSize),\n contextSize: numOr(data.contextSize),\n ...seq,\n };\n\n case MESSAGE_TYPE.CONTEXT_COMPACTION_START:\n return { type: 'compaction', phase: 'start', ...seq };\n\n case MESSAGE_TYPE.CONTEXT_COMPACTION_END:\n return {\n type: 'compaction',\n phase: 'end',\n summary: typeof data.text === 'string' ? data.text : undefined,\n ...seq,\n };\n\n case MESSAGE_TYPE.SYSTEM:\n if (typeof data.text === 'string') {\n return { type: 'participant', kind: 'system', text: data.text, ...seq };\n }\n return null;\n\n case MESSAGE_TYPE.DIRECT_MESSAGE:\n if (typeof data.text === 'string') {\n return {\n type: 'participant',\n kind: 'direct-message',\n text: data.text,\n ownerType: typeof data.ownerType === 'string' ? data.ownerType : undefined,\n displayName: typeof data.displayName === 'string' ? data.displayName : undefined,\n userId: typeof data.userId === 'string' ? data.userId : undefined,\n ...seq,\n };\n }\n return null;\n\n case MESSAGE_TYPE.DIALOG_CLOSED:\n return { type: 'dialog-closed', ...seq };\n\n default:\n return null;\n }\n}\n","/**\n * The hub's confirm-tool contract — request shape and error envelope.\n *\n * Resolving a Product Guide proposal is a POST to `endpoints.approvalToolUrl`\n * whose response is an ordinary guide stream (a `decision_resolved` frame, then\n * the hub's auto-continuation). Two callers make that call today: the SSE chat\n * adapter, and any host replaying the same route into a different transport's\n * conversation (the product app resolves guide cards inside a NATS Mingo\n * dialog). They MUST send byte-identical bodies — a proposal is single-use, so a\n * caller that drifts on a field name does not degrade, it fails the write — and\n * they must surface the same server copy, because the hub's errors are written\n * for the end user (\"This approval expired — ask again to get a fresh one\").\n *\n * Hence both live here rather than at either call site.\n *\n * Server-safe: no React, no browser APIs.\n */\n\n/** What the user did with the card. */\nexport type ApprovalToolAction = 'approve' | 'reject';\n\n/** Client-side shape of a confirm; `buildConfirmToolBody` renames it to the\n * wire's own casing so no caller hand-writes `proposal_id`. */\nexport interface ConfirmToolRequest {\n /** The hub-minted proposal id carried by the card. */\n proposalId: string;\n action: ApprovalToolAction;\n /**\n * The hub's conversation id, quoted back verbatim. The hub rejects a confirm\n * without it: a proposal only means something inside the conversation that\n * minted it.\n */\n conversationId: string | null | undefined;\n}\n\n/** Build the confirm-tool request body. The ONE place that knows the wire\n * spells the id `proposal_id` while everything else is camelCase. */\nexport function buildConfirmToolBody(request: ConfirmToolRequest): Record<string, unknown> {\n return {\n proposal_id: request.proposalId,\n action: request.action,\n conversationId: request.conversationId,\n };\n}\n\n/**\n * Pull the user-facing copy out of a failed chat/confirm response.\n *\n * The route-base envelope is `{error, code}`; `error` is written for the end\n * user, so it beats any status-code copy the client could invent. Returns null\n * when the body is not that envelope (HTML error page, empty body, gateway\n * response) — the caller then supplies its own generic line.\n *\n * Consumes the body: call it once, on a response you have already decided is a\n * failure.\n */\nexport async function readServerErrorMessage(response: { json: () => Promise<unknown> }): Promise<string | null> {\n try {\n const body = (await response.json()) as { error?: unknown } | null;\n if (typeof body?.error === 'string' && body.error.length > 0) return body.error;\n } catch {\n /* non-JSON error body — the caller falls through to its generic copy */\n }\n return null;\n}\n","/**\n * `normalizeIpForBucketKey` — THE cross-repo normalizer for turning a raw\n * client-IP candidate (`x-forwarded-for` hop, socket remote address, proxy\n * header) into a stable rate-limit / dedup bucket key.\n *\n * WHY IT LIVES HERE: producer and consumer sit in DIFFERENT repos (the app\n * emits the candidate, the hub buckets it) and each had grown its own\n * normalizer. They disagreed — one unwrapped `::ffff:1.2.3.4` to the bare\n * IPv4 and stripped `%zone`, the other kept the zone and only lower-cased the\n * mapped form — so ONE visitor could land in TWO buckets depending on which\n * side normalized. Both repos already depend on this package, and\n * `./chat-protocol` is its server-safe subpath (no React, no browser APIs),\n * so this is the one place both sides can share.\n *\n * Contract:\n * - input longer than `IP_BUCKET_KEY_MAX_LENGTH` → `null` (an unbounded\n * header must never become an unbounded map key);\n * - `[…]` brackets and a trailing `]:port` are stripped, as is a bare\n * `:port` on an IPv4 (`203.0.113.4:8080`) — an unbracketed IPv6 with a\n * bare port stays ambiguous and is not stripped;\n * - a `%zone` suffix is stripped (link-local scope is per-host, not part of\n * the peer's identity);\n * - IPv4-mapped IPv6 (`::ffff:203.0.113.4`) collapses to the bare IPv4, so\n * the same peer buckets identically over either stack;\n * - IPv6 is lower-cased;\n * - IPv4 octets are range-checked and REJECTED when zero-padded (`.04`\n * would otherwise bucket separately from `.4`), and IPv6 is\n * charset/shape-checked (one `::` at most, ≤8 groups, an embedded IPv4\n * only in the last group, no dangling separator); anything else returns\n * `null` rather than a junk bucket key.\n *\n * It does NOT canonicalize IPv6 zero-compression (`2001:db8::1` vs\n * `2001:0db8:0:0:0:0:0:1`) — peers do not spell their own address two ways\n * within one deployment, and a full expander is more surface than the bucket\n * key warrants.\n */\n\n/** Longest accepted candidate. A full IPv6 + zone fits well inside this. */\nexport const IP_BUCKET_KEY_MAX_LENGTH = 64;\n\nfunction isIpv4(value: string): boolean {\n const parts = value.split('.');\n if (parts.length !== 4) return false;\n for (const part of parts) {\n if (!/^\\d{1,3}$/.test(part)) return false;\n // Leading zeros are a SECOND spelling of the same address (`203.0.113.04`),\n // so accepting them verbatim splits one peer across two buckets. Reject\n // rather than canonicalize: no legitimate emitter zero-pads, and some\n // resolvers read a `0`-prefixed octet as octal, i.e. it is not even\n // reliably the same address.\n if (part.length > 1 && part.startsWith('0')) return false;\n if (Number(part) > 255) return false;\n }\n return true;\n}\n\nfunction isIpv6(value: string): boolean {\n if (!value.includes(':')) return false;\n // Charset gate first: hex digits, separators, and the dots of a trailing\n // embedded IPv4. Anything else (a hostname, a header injection) is out.\n if (!/^[0-9a-f:.]+$/.test(value)) return false;\n if (value.includes(':::')) return false;\n // A dangling separator (`2001:db8::1:`) leaves an EMPTY trailing group that\n // is not part of a `::` compression — malformed, not an address.\n if (value.endsWith(':') && !value.endsWith('::')) return false;\n if (value.startsWith(':') && !value.startsWith('::')) return false;\n const compressions = value.split('::').length - 1;\n if (compressions > 1) return false;\n const groups = value.split(':');\n if (groups.length > 8) return false;\n for (let i = 0; i < groups.length; i += 1) {\n const group = groups[i];\n if (group === '') continue;\n if (group.includes('.')) {\n // An embedded IPv4 occupies the LAST two groups; anywhere else\n // (`1.2.3.4::1`) it is malformed.\n if (i !== groups.length - 1) return false;\n if (!isIpv4(group)) return false;\n continue;\n }\n if (!/^[0-9a-f]{1,4}$/.test(group)) return false;\n }\n // Uncompressed form must be exactly 8 groups (a trailing embedded IPv4\n // occupies two, so 7 written groups is also complete).\n if (compressions === 0) {\n const embedsIpv4 = groups[groups.length - 1]?.includes('.') ?? false;\n if (groups.length !== (embedsIpv4 ? 7 : 8)) return false;\n }\n return true;\n}\n\n/**\n * Normalize one IP candidate to its canonical bucket key, or `null` when the\n * value is not a usable address.\n */\nexport function normalizeIpForBucketKey(value: string): string | null {\n if (typeof value !== 'string') return null;\n let candidate = value.trim();\n if (candidate === '' || candidate.length > IP_BUCKET_KEY_MAX_LENGTH) return null;\n\n // `[2001:db8::1]` / `[::1]:443` — the bracketed forms a proxy emits so the\n // port is unambiguous.\n if (candidate.startsWith('[')) {\n const close = candidate.indexOf(']');\n if (close === -1) return null;\n const trailer = candidate.slice(close + 1);\n if (trailer !== '' && !/^:\\d{1,5}$/.test(trailer)) return null;\n candidate = candidate.slice(1, close);\n }\n\n // `%eth0` / `%25eth0` scope id — host-local, never part of peer identity.\n const zone = candidate.indexOf('%');\n if (zone !== -1) candidate = candidate.slice(0, zone);\n if (candidate === '') return null;\n\n // UNBRACKETED `203.0.113.4:8080` — Azure App Service / Front Door and several\n // CDNs write the client hop that way. Rejecting it left the caller with NO\n // candidate at all, collapsing every visitor into the single egress bucket:\n // exactly the silent regression this module exists to prevent. Stripped ONLY\n // when the remainder is a valid IPv4; an unbracketed IPv6 with a bare port\n // (`::1:443`) is genuinely ambiguous — `:443` is equally a final group — so\n // that shape is left to the IPv6 path.\n const portSplit = candidate.lastIndexOf(':');\n if (portSplit > 0 && /^\\d{1,5}$/.test(candidate.slice(portSplit + 1))) {\n const head = candidate.slice(0, portSplit);\n if (isIpv4(head)) candidate = head;\n }\n\n if (isIpv4(candidate)) return candidate;\n\n const lowered = candidate.toLowerCase();\n\n // IPv4-mapped IPv6 → the bare IPv4 (same peer, one bucket). EVERY spelling\n // is handled: the compressed `::ffff:1.2.3.4` that dual-stack listeners\n // actually emit, the written-out `0:0:0:0:0:ffff:1.2.3.4` that a hop doing\n // its own (non-compressing) formatting can produce, and the ZERO-PADDED\n // `0000:0000:0000:0000:0000:ffff:1.2.3.4` that a fixed-width formatter\n // produces — leading zeros in an IPv6 group are purely cosmetic, so all\n // three name the same peer. Matching only some of them would split one peer\n // across two buckets — the exact failure this module exists to prevent.\n const mapped = /^(?:(?:0{1,4}:){5}|::)ffff:(\\d{1,3}(?:\\.\\d{1,3}){3})$/.exec(lowered);\n if (mapped) return isIpv4(mapped[1]) ? mapped[1] : null;\n\n if (isIpv6(lowered)) return lowered;\n return null;\n}\n","/**\n * `envFlagEnabled` — THE cross-repo predicate for reading a boolean env flag.\n *\n * WHY IT LIVES HERE: the same flag is read on BOTH sides of the guide-chat\n * seam and each side had grown its own reading of it. The hub parsed the\n * value (`'0'` / `'false'` / `'off'` mean OFF); the producing app used bare\n * `Boolean(process.env.X)`, under which every non-empty string — including\n * `'0'` and `'false'` — is TRUE.\n *\n * That divergence failed OPEN on the one flag that gates a security boundary.\n * `TRUSTED_INGRESS_SETS_REAL_IP` is an operator's assertion that a reverse\n * proxy OVERWRITES the client-IP headers; without it those headers are\n * attacker-writable and must not be read. An operator writing `=0` to mean\n * \"off\" got the hub honoring that while the producer read `x-real-ip` /\n * `x-forwarded-for` anyway and forwarded the value as `x-chat-ip`, which the\n * hub then trusts verbatim under the service token. A rate-limit-bucket\n * spoof, reachable through a plausible operator typo.\n *\n * Both repos already depend on this package and `./chat-protocol` is its\n * server-safe subpath (no React, no browser APIs), so this is the one place\n * both sides can share — same reasoning that moved `normalizeIpForBucketKey`\n * here. Keep it as ONE definition: a second copy is what caused the drift.\n *\n * Contract:\n * - unset, empty, or whitespace-only → `false`;\n * - `'0'`, `'false'`, `'off'` → `false` (case- and\n * surrounding-whitespace-insensitive);\n * - any other non-empty value → `true`.\n *\n * `process.env` is read PER CALL, never module-hoisted, so tests, preview and\n * prod all observe the live value rather than whatever was present at\n * module-eval time.\n */\nexport function envFlagEnabled(name: string): boolean {\n const raw = process.env[name];\n if (!raw) return false;\n const v = raw.trim().toLowerCase();\n return v !== '' && v !== '0' && v !== 'false' && v !== 'off';\n}\n"]}