@flamingo-stack/openframe-frontend-core 0.0.565-1861.5100.1 → 0.0.565

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 (204) hide show
  1. package/dist/chat-protocol/index.cjs.map +1 -1
  2. package/dist/chat-protocol/index.js.map +1 -1
  3. package/dist/{chunk-TA5P2M7V.cjs → chunk-26LZFWY6.cjs} +732 -812
  4. package/dist/chunk-26LZFWY6.cjs.map +1 -0
  5. package/dist/{chunk-KHEOXHF4.cjs → chunk-32QBTN5L.cjs} +5 -5
  6. package/dist/{chunk-KHEOXHF4.cjs.map → chunk-32QBTN5L.cjs.map} +1 -1
  7. package/dist/{chunk-GG6CAELN.js → chunk-3FBPPKV2.js} +4 -4
  8. package/dist/{chunk-MSUSONUK.js → chunk-5JYX2GOP.js} +3233 -3313
  9. package/dist/chunk-5JYX2GOP.js.map +1 -0
  10. package/dist/{chunk-EQEMZU45.js → chunk-5K3F5AZX.js} +4 -4
  11. package/dist/{chunk-YVOMHB73.js → chunk-5MVE77D5.js} +21 -35
  12. package/dist/chunk-5MVE77D5.js.map +1 -0
  13. package/dist/{chunk-H4RCJ4IB.js → chunk-6F47STCR.js} +3 -7
  14. package/dist/{chunk-H4RCJ4IB.js.map → chunk-6F47STCR.js.map} +1 -1
  15. package/dist/{chunk-XPLZB74T.js → chunk-6RILRC7X.js} +2 -2
  16. package/dist/{chunk-BKDSNVGH.cjs → chunk-7FH6MZ6M.cjs} +9 -9
  17. package/dist/{chunk-BKDSNVGH.cjs.map → chunk-7FH6MZ6M.cjs.map} +1 -1
  18. package/dist/{chunk-ZUVVJ24E.js → chunk-7IR5LKYC.js} +2 -2
  19. package/dist/{chunk-GG2HJQW3.cjs → chunk-AN7YK35P.cjs} +26 -26
  20. package/dist/{chunk-GG2HJQW3.cjs.map → chunk-AN7YK35P.cjs.map} +1 -1
  21. package/dist/{chunk-PZH3CZTJ.cjs → chunk-CCFBL7JU.cjs} +75 -89
  22. package/dist/chunk-CCFBL7JU.cjs.map +1 -0
  23. package/dist/{chunk-OEN3GTRL.js → chunk-DEYRHTLD.js} +2 -2
  24. package/dist/{chunk-MIKGZY7V.js → chunk-GCMASPSW.js} +5 -5
  25. package/dist/{chunk-ZYC4O76Y.js → chunk-HJ7OVIR7.js} +2 -2
  26. package/dist/{chunk-BWMVI2KD.cjs → chunk-HVS56KQE.cjs} +7 -7
  27. package/dist/{chunk-BWMVI2KD.cjs.map → chunk-HVS56KQE.cjs.map} +1 -1
  28. package/dist/{chunk-GZL5SAUI.cjs → chunk-HWQ5HMJN.cjs} +15 -19
  29. package/dist/chunk-HWQ5HMJN.cjs.map +1 -0
  30. package/dist/{chunk-NAYXROCV.cjs → chunk-ISK6LRRY.cjs} +3 -3
  31. package/dist/{chunk-NAYXROCV.cjs.map → chunk-ISK6LRRY.cjs.map} +1 -1
  32. package/dist/{chunk-4H7L4OSR.cjs → chunk-LOLVMAT3.cjs} +4 -4
  33. package/dist/{chunk-4H7L4OSR.cjs.map → chunk-LOLVMAT3.cjs.map} +1 -1
  34. package/dist/{chunk-XZNKPEWB.js → chunk-MIBKEMZY.js} +2 -2
  35. package/dist/chunk-MVWSLZG7.js +2 -0
  36. package/dist/{chunk-QWVON3LG.cjs → chunk-NCJUIT3Q.cjs} +20 -20
  37. package/dist/{chunk-QWVON3LG.cjs.map → chunk-NCJUIT3Q.cjs.map} +1 -1
  38. package/dist/{chunk-OTDVQBLI.cjs → chunk-NM5NBMMD.cjs} +31 -31
  39. package/dist/{chunk-OTDVQBLI.cjs.map → chunk-NM5NBMMD.cjs.map} +1 -1
  40. package/dist/{chunk-SQ53GVCG.js → chunk-QQFUJ6MS.js} +2 -2
  41. package/dist/{chunk-PBH2BQYR.cjs → chunk-TFEL2C6I.cjs} +86 -86
  42. package/dist/{chunk-PBH2BQYR.cjs.map → chunk-TFEL2C6I.cjs.map} +1 -1
  43. package/dist/{chunk-DLSDYLY3.cjs → chunk-TFRJ7B4X.cjs} +11 -11
  44. package/dist/{chunk-DLSDYLY3.cjs.map → chunk-TFRJ7B4X.cjs.map} +1 -1
  45. package/dist/{chunk-Y6IGG6RZ.cjs → chunk-TGT3JHVU.cjs} +39 -39
  46. package/dist/{chunk-Y6IGG6RZ.cjs.map → chunk-TGT3JHVU.cjs.map} +1 -1
  47. package/dist/{chunk-XSK7ZQ3C.js → chunk-TXLUKQBI.js} +2 -2
  48. package/dist/{chunk-LOWWLH2W.js → chunk-UK6SSMUR.js} +2 -2
  49. package/dist/chunk-ZTM35Q76.cjs +2 -0
  50. package/dist/{chunk-UGAHDPNU.cjs.map → chunk-ZTM35Q76.cjs.map} +1 -1
  51. package/dist/components/case-studies/index.cjs +8 -8
  52. package/dist/components/case-studies/index.js +2 -2
  53. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  54. package/dist/components/chat/entity-cards/employee-entry-card.d.ts.map +1 -1
  55. package/dist/components/chat/index.cjs +2 -2
  56. package/dist/components/chat/index.js +1 -1
  57. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -1
  58. package/dist/components/chat/types/message.types.d.ts +8 -0
  59. package/dist/components/chat/types/message.types.d.ts.map +1 -1
  60. package/dist/components/chat/utils/message-segment-accumulator.d.ts +1 -1
  61. package/dist/components/chat/utils/message-segment-accumulator.d.ts.map +1 -1
  62. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  63. package/dist/components/comment-card.d.ts +2 -18
  64. package/dist/components/comment-card.d.ts.map +1 -1
  65. package/dist/components/contact/index.cjs +3 -3
  66. package/dist/components/contact/index.js +2 -2
  67. package/dist/components/docs/index.cjs +5 -5
  68. package/dist/components/docs/index.js +4 -4
  69. package/dist/components/embeds/index.cjs +4 -6
  70. package/dist/components/embeds/index.cjs.map +1 -1
  71. package/dist/components/embeds/index.d.ts +0 -2
  72. package/dist/components/embeds/index.d.ts.map +1 -1
  73. package/dist/components/embeds/index.js +3 -5
  74. package/dist/components/faq/index.cjs +3 -3
  75. package/dist/components/faq/index.js +2 -2
  76. package/dist/components/features/array-entry-manager.d.ts.map +1 -1
  77. package/dist/components/features/clickup-tasks-manager.d.ts +1 -6
  78. package/dist/components/features/clickup-tasks-manager.d.ts.map +1 -1
  79. package/dist/components/features/index.cjs +2 -2
  80. package/dist/components/features/index.js +1 -1
  81. package/dist/components/help-center-pages/index.cjs +22 -22
  82. package/dist/components/help-center-pages/index.js +13 -13
  83. package/dist/components/index.cjs +204 -281
  84. package/dist/components/index.cjs.map +1 -1
  85. package/dist/components/index.d.ts +0 -1
  86. package/dist/components/index.d.ts.map +1 -1
  87. package/dist/components/index.js +570 -647
  88. package/dist/components/index.js.map +1 -1
  89. package/dist/components/meeting-scheduler/index.cjs +30 -30
  90. package/dist/components/meeting-scheduler/index.js +3 -3
  91. package/dist/components/navigation/index.cjs +2 -2
  92. package/dist/components/navigation/index.js +1 -1
  93. package/dist/components/onboarding-guides/index.cjs +5 -5
  94. package/dist/components/onboarding-guides/index.js +4 -4
  95. package/dist/components/related-content/index.cjs +3 -3
  96. package/dist/components/related-content/index.js +2 -2
  97. package/dist/components/shared/delivery/delivery-row.d.ts +1 -5
  98. package/dist/components/shared/delivery/delivery-row.d.ts.map +1 -1
  99. package/dist/components/shared/roadmap/roadmap-grid.d.ts +1 -10
  100. package/dist/components/shared/roadmap/roadmap-grid.d.ts.map +1 -1
  101. package/dist/components/tickets/index.cjs +6 -6
  102. package/dist/components/tickets/index.js +5 -5
  103. package/dist/components/ui/card.d.ts.map +1 -1
  104. package/dist/components/ui/index.cjs +2 -4
  105. package/dist/components/ui/index.cjs.map +1 -1
  106. package/dist/components/ui/index.d.ts +1 -1
  107. package/dist/components/ui/index.d.ts.map +1 -1
  108. package/dist/components/ui/index.js +1 -3
  109. package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +1 -1
  110. package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +1 -1
  111. package/dist/components/ui/table/index.d.ts +1 -1
  112. package/dist/components/ui/table/index.d.ts.map +1 -1
  113. package/dist/components/ui/table/table-row.d.ts.map +1 -1
  114. package/dist/components/ui/table/table-skeleton.d.ts +2 -15
  115. package/dist/components/ui/table/table-skeleton.d.ts.map +1 -1
  116. package/dist/components/ui/table/table.d.ts.map +1 -1
  117. package/dist/components/ui/table/types.d.ts +0 -2
  118. package/dist/components/ui/table/types.d.ts.map +1 -1
  119. package/dist/index.cjs +2 -6
  120. package/dist/index.cjs.map +1 -1
  121. package/dist/index.js +1 -5
  122. package/dist/types/employee.d.ts +2 -25
  123. package/dist/types/employee.d.ts.map +1 -1
  124. package/dist/types/index.d.ts +0 -2
  125. package/dist/types/index.d.ts.map +1 -1
  126. package/dist/types/marketing.d.ts +1 -1
  127. package/dist/types/marketing.d.ts.map +1 -1
  128. package/dist/types/profile.d.ts +1 -2
  129. package/dist/types/profile.d.ts.map +1 -1
  130. package/dist/types/team.d.ts +1 -15
  131. package/dist/types/team.d.ts.map +1 -1
  132. package/dist/types/user.d.ts +1 -3
  133. package/dist/types/user.d.ts.map +1 -1
  134. package/dist/utils/embed-url-converters.d.ts +0 -17
  135. package/dist/utils/embed-url-converters.d.ts.map +1 -1
  136. package/dist/utils/index.cjs +0 -12
  137. package/dist/utils/index.cjs.map +1 -1
  138. package/dist/utils/index.js +1 -12
  139. package/dist/utils/index.js.map +1 -1
  140. package/package.json +1 -1
  141. package/src/components/.comment-card.md +5 -4
  142. package/src/components/chat/__tests__/ticket-event.test.ts +86 -0
  143. package/src/components/chat/chat-message-enhanced.tsx +3 -1
  144. package/src/components/chat/entity-cards/employee-entry-card.tsx +0 -6
  145. package/src/components/chat/stream/chat-stream-reducer.ts +7 -1
  146. package/src/components/chat/types/message.types.ts +8 -0
  147. package/src/components/chat/utils/message-segment-accumulator.ts +10 -3
  148. package/src/components/chat/utils/process-historical-messages.ts +17 -8
  149. package/src/components/comment-card.tsx +17 -42
  150. package/src/components/embeds/index.ts +0 -2
  151. package/src/components/features/array-entry-manager.tsx +1 -2
  152. package/src/components/features/clickup-tasks-manager.tsx +1 -8
  153. package/src/components/index.ts +0 -1
  154. package/src/components/shared/delivery/delivery-row.tsx +1 -17
  155. package/src/components/shared/roadmap/roadmap-grid.tsx +1 -21
  156. package/src/components/ui/card.tsx +1 -4
  157. package/src/components/ui/index.ts +0 -1
  158. package/src/components/ui/markdown/rich/__tests__/shortcodes.test.ts +0 -44
  159. package/src/components/ui/markdown/rich/embed-overrides.tsx +0 -4
  160. package/src/components/ui/markdown/rich/shortcodes.ts +0 -12
  161. package/src/components/ui/table/index.ts +1 -1
  162. package/src/components/ui/table/table-row.tsx +2 -7
  163. package/src/components/ui/table/table-skeleton.tsx +3 -20
  164. package/src/components/ui/table/table.tsx +0 -1
  165. package/src/components/ui/table/types.ts +0 -2
  166. package/src/types/employee.ts +2 -27
  167. package/src/types/index.ts +0 -2
  168. package/src/types/marketing.ts +0 -1
  169. package/src/types/profile.ts +1 -3
  170. package/src/types/team.ts +1 -17
  171. package/src/types/user.ts +1 -4
  172. package/src/utils/embed-url-converters.ts +0 -32
  173. package/dist/chunk-5I3BSQ6D.js +0 -2
  174. package/dist/chunk-GZL5SAUI.cjs.map +0 -1
  175. package/dist/chunk-MSUSONUK.js.map +0 -1
  176. package/dist/chunk-PZH3CZTJ.cjs.map +0 -1
  177. package/dist/chunk-TA5P2M7V.cjs.map +0 -1
  178. package/dist/chunk-UGAHDPNU.cjs +0 -2
  179. package/dist/chunk-YVOMHB73.js.map +0 -1
  180. package/dist/components/comment-composer.d.ts +0 -42
  181. package/dist/components/comment-composer.d.ts.map +0 -1
  182. package/dist/components/embeds/claude-embed.d.ts +0 -31
  183. package/dist/components/embeds/claude-embed.d.ts.map +0 -1
  184. package/dist/types/department.d.ts +0 -41
  185. package/dist/types/department.d.ts.map +0 -1
  186. package/dist/types/design-doc.d.ts +0 -247
  187. package/dist/types/design-doc.d.ts.map +0 -1
  188. package/src/components/comment-composer.tsx +0 -117
  189. package/src/components/embeds/claude-embed.tsx +0 -84
  190. package/src/types/department.ts +0 -44
  191. package/src/types/design-doc.ts +0 -295
  192. package/src/utils/__tests__/claude-embed-url.test.ts +0 -42
  193. /package/dist/{chunk-GG6CAELN.js.map → chunk-3FBPPKV2.js.map} +0 -0
  194. /package/dist/{chunk-EQEMZU45.js.map → chunk-5K3F5AZX.js.map} +0 -0
  195. /package/dist/{chunk-XPLZB74T.js.map → chunk-6RILRC7X.js.map} +0 -0
  196. /package/dist/{chunk-ZUVVJ24E.js.map → chunk-7IR5LKYC.js.map} +0 -0
  197. /package/dist/{chunk-OEN3GTRL.js.map → chunk-DEYRHTLD.js.map} +0 -0
  198. /package/dist/{chunk-MIKGZY7V.js.map → chunk-GCMASPSW.js.map} +0 -0
  199. /package/dist/{chunk-ZYC4O76Y.js.map → chunk-HJ7OVIR7.js.map} +0 -0
  200. /package/dist/{chunk-XZNKPEWB.js.map → chunk-MIBKEMZY.js.map} +0 -0
  201. /package/dist/{chunk-5I3BSQ6D.js.map → chunk-MVWSLZG7.js.map} +0 -0
  202. /package/dist/{chunk-SQ53GVCG.js.map → chunk-QQFUJ6MS.js.map} +0 -0
  203. /package/dist/{chunk-XSK7ZQ3C.js.map → chunk-TXLUKQBI.js.map} +0 -0
  204. /package/dist/{chunk-LOWWLH2W.js.map → chunk-UK6SSMUR.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/chat-protocol/nats-decoder.ts","../../src/chat-protocol/confirm-tool.ts","../../src/chat-protocol/ip-normalize.ts","../../src/chat-protocol/env-flag.ts"],"names":[],"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 wire-frame shapes these events carry through are defined ONCE in\n// `./frames.ts` — reuse them here rather than restating their fields.\nimport type {\n ApprovalRequestField,\n DecisionResolvedFrame,\n UsageTelemetry,\n} from './frames'\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 { AskOptionData } from '../components/chat/types/message.types'\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/** Guide-body delta (NATS `GUIDE` chunk) — the assistant's how-to /\n * documentation answer, rendered as a titled \"OpenFrame Guide\" card rather\n * than bare prose. Same APPEND-ONLY contract as `text-delta` /\n * `thinking-delta`: each event carries the next verbatim slice and consumers\n * coalesce into the trailing `guide` segment. NATS-only today — the SSE\n * frame grammar has no guide frame — but it lives in the shared union\n * because the reducer is transport-agnostic. */\nexport interface GuideDeltaEvent extends ChatStreamEventBase {\n type: 'guide-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 /** Set when the card came from a Product Guide frame — see {@link GuideOrigin}. */\n origin?: GuideOrigin\n}\n\n/**\n * Marks an event whose payload is a Product Guide frame, whatever transport\n * carried it. It exists because ONE stream can now mix both worlds: the agent\n * re-streams the hub's frames into a NATS dialog, so a card typed the hub's way\n * (`approvalType` = the tool name, resolved through the hub's confirm route)\n * travels beside cards typed the agent's way (`approvalType` = an approval TIER\n * routed to human escalation).\n *\n * Consumers read it to keep the guide half behaving exactly as it does in the\n * hub's own chat — NOT to give it special treatment. Without it the NATS kernel\n * would have to guess from `approvalType`, and every tool the hub adds would\n * silently fall into the escalation path.\n */\nexport type GuideOrigin = 'guide'\n\n/** The only value of {@link GuideOrigin}. Lives beside the type, and beside the\n * predicate below, because both decoders and every consumer that branches on\n * provenance must compare against the same token — a bare `'guide'` literal\n * typo silently disables the branch instead of failing to compile. */\nexport const GUIDE_ORIGIN: GuideOrigin = 'guide'\n\n/** True for anything stamped as coming from the Product Guide — a stream event\n * or the `data` of a segment built from one. */\nexport function isGuideOrigin(\n source: { origin?: GuideOrigin | string } | null | undefined,\n): boolean {\n return source?.origin === GUIDE_ORIGIN\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 /** Set when the resolution came from a Product Guide frame — see {@link GuideOrigin}. */\n origin?: GuideOrigin\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(\n state: unknown,\n): 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/** 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 * Set when the metadata came from a Product Guide frame — see\n * {@link GuideOrigin}. Such an event carries ONLY `conversationId`: it exists\n * to record the hub's conversation id (every confirm-tool call must quote it\n * back), NOT to describe the dialog's model, which stays the agent's.\n */\n origin?: GuideOrigin\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/** 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 | GuideDeltaEvent\n | AskEvent\n | StatusEvent\n | ToolExecutionEvent\n | ApprovalRequestEvent\n | ApprovalResolvedEvent\n | EscalationOfferEvent\n | EscalationOfferResolvedEvent\n | TicketEscalatedEvent\n | TicketEventEvent\n | ChatMetadataEvent\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. The NATS decoder (`./nats-decoder.ts`) reaches it for `GUIDE` chunks\n * carrying a `payload`: the saas-ai-agent re-streams the Product Guide's hub\n * frames VERBATIM into the Mingo chunk stream (`GuideStreamingService`), so the\n * bytes a guide answer is made of are the same frames on both transports — only\n * the envelope differs. Decoding them twice would drift the moment the hub adds\n * a frame kind, which is why this table lives outside either decoder.\n *\n * The NATS side does NOT take every event this table can produce; see\n * `guideFrameEvent` in `./nats-decoder.ts` for which ones cross over and why.\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/**\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: any, 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: meta.input_tokens,\n cache_read_input_tokens: meta.cache_read_input_tokens,\n cache_creation_input_tokens: 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 as Array<Record<string, any>>)\n .filter((p) => 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(\n (f) => f && f.label && f.value,\n )\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 =\n rawFields.find((f) => /^(title|subject|name)$/i.test(String(f.label))) ??\n rawFields[0]\n const base =\n 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\n ? Object.fromEntries(rawFields.map((f) => [String(f.label), String(f.value)]))\n : 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 =\n typeof meta.title === 'string' && meta.title.length > 0 ? meta.title : toolName\n const rawFields = Array.isArray(meta.fields)\n ? (meta.fields as Array<{ label?: string; value?: string }>)\n : []\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:\n 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: meta.provider,\n modelLabel: meta.modelLabel,\n modelName: meta.model,\n contextWindowMaxTokens: meta.contextWindowMaxTokens,\n scrollAnchor: meta.scrollAnchor,\n conversationId: 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 { FRAME_TERMINATOR, END_OF_LEADING, TRAILER_SENTINEL } from './frames'\nimport type { ChatStreamEvent } from './events'\n// The frame table is SHARED with the NATS decoder — guide answers re-stream the\n// hub's frames verbatim inside `GUIDE` chunks — so it lives in its own module.\nimport { mapLeadingFrame } 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\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: any\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 mapLeadingFrame(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 const meta = JSON.parse(raw)\n if (meta.kind === 'usage' && (meta.stage === 'end' || meta.stage === 'display')) {\n out.push({\n type: 'usage',\n stage: 'end',\n input_tokens: meta.input_tokens,\n output_tokens: meta.output_tokens,\n cache_read_input_tokens: meta.cache_read_input_tokens,\n cache_creation_input_tokens: meta.cache_creation_input_tokens,\n hit_rate_pct: meta.hit_rate_pct,\n telemetry: meta.telemetry,\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 { AssistantType, AuthorType, ChatApprovalStatus, MessageOwner } from './chat.types'\n\n// ========== Message Type Definitions ==========\n\nexport const MESSAGE_TYPE = {\n TEXT: 'TEXT',\n THINKING: 'THINKING',\n GUIDE: 'GUIDE',\n ASK: 'ASK',\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, any>\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, any>\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?: React.ReactNode\n requestId?: string\n approvalRequestId?: string\n approvalType?: string\n /** `'guide'` when the card came from a Product Guide frame (see `GuideOrigin`\n * in `chat-protocol/events`). Carried onto the segment because hosts route\n * and filter approvals by where they came from: mingo lifts its OWN pending\n * cards into a sticky footer, and a guide card must stay inline in the turn\n * the way it does in the hub's chat. */\n origin?: 'guide'\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 =\n | typeof TICKET_EVENT_KIND[keyof typeof TICKET_EVENT_KIND]\n | (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, any> | 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 /** `'guide'` when the batch came from a Product Guide `approval_batch` frame.\n * Same contract and the same reasons as `ApprovalRequestData.origin` — the\n * hub groups multiple proposals into one card, and that card resolves\n * through the hub exactly like a single one. */\n origin?: 'guide'\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 = (\n requestId?: string,\n) => 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/** Guide answer body — the assistant's how-to/documentation reply, rendered as\n * a titled \"OpenFrame Guide\" card instead of a bare paragraph. `text` is\n * markdown, streamed in fragments like a `text` segment and coalesced by the\n * accumulator. */\nexport type GuideSegment = {\n type: 'guide'\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}\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 = TextSegment | ThinkingSegment | GuideSegment | AskSegment | ToolExecutionSegment | ApprovalRequestSegment | ApprovalBatchSegment | EscalationOfferSegment | TicketEscalatedSegment | TicketEventSegment | ErrorSegment | 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}\n\nexport interface ThinkingMessageData extends MessageDataBase {\n type: 'THINKING'\n text?: string\n}\n\nexport interface GuideMessageData extends MessageDataBase {\n type: 'GUIDE'\n text?: string\n /** Persisted Product Guide frame (GraphQL `GuideData.payload`, a JSON scalar)\n * for rows that carry a card instead of answer text — replayed through the\n * live path's `guideFrameEvent`. */\n payload?: Record<string, 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 options?: AskOptionData[]\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, any>\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, any>\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\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 | GuideMessageData\n | AskMessageData\n | ExecutingToolMessageData\n | ExecutedToolMessageData\n | ApprovalRequestMessageData\n | ApprovalResultMessageData\n | EscalationOfferMessageData\n | TicketEscalatedMessageData\n | TicketEventMessageData\n | ErrorMessageData\n | AIMetadataMessageData\n | SystemMessageData\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}\n\n// ========== Base Message Interface ==========\n\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 * 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, GUIDE_ORIGIN, escalationResolvedStatus } from './events'\nimport type { ApprovalToolCall, ChatStreamEvent } from './events'\nimport { escapeThinkingTags, mapLeadingFrame } from './leading-frames'\n\n/** Minimal structural view of a NATS chunk (see `ChunkData` in\n * `src/components/chat/types/network.types.ts`). */\ntype NatsChunk = Record<string, any>\n\n// `GUIDE_ORIGIN` moved to `./events`, beside the `GuideOrigin` type it is the\n// only value of — the SSE half reads it too, so it never belonged to the NATS\n// decoder. Re-exported here because that is the import path consumers know.\nexport { GUIDE_ORIGIN } from './events'\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\n .filter((item): item is Record<string, any> => !!item && typeof item === 'object')\n .map((item) => ({\n toolExecutionRequestId: String(item.toolExecutionRequestId ?? ''),\n toolName: String(item.toolName ?? ''),\n toolTitle: typeof item.toolTitle === 'string' ? item.toolTitle : undefined,\n toolExplanation:\n 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:\n item.toolCallArguments && typeof item.toolCallArguments === 'object'\n ? (item.toolCallArguments as Record<string, any>)\n : 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((item): item is Record<string, any> => !!item && typeof item === 'object')\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 * Events whose meaning belongs to the AGENT's dialog rather than to the guide\n * turn embedded inside it. The hub emits its own copies of these, and the Mingo\n * dialog already has authoritative ones: letting the hub's through would\n * double-count the dialog's tokens or fight the agent's own phase chunks.\n *\n * Note this is a DENYLIST, and that is the point. `./leading-frames` is the ONE\n * place a hub frame kind is taught to the client; whatever it learns to emit —\n * including kinds that do not exist yet — must reach a Mingo dialog without a\n * second edit here. An allowlist would silently swallow every card the hub adds\n * next, and the guide half would quietly lag the guide chat.\n */\nconst AGENT_OWNED_EVENTS: ReadonlySet<ChatStreamEvent['type']> = new Set([\n 'usage',\n 'token-usage',\n 'status',\n 'compaction',\n 'participant',\n 'dialog-closed',\n])\n\n/** Event types that declare `origin` (see `GuideOrigin`). Data rather than a\n * branch per type, so a future hub card opts in by adding the field to its\n * interface and its name here — the pass-through below needs no edit. */\nconst ORIGIN_BEARING_EVENTS: ReadonlySet<ChatStreamEvent['type']> = new Set([\n 'approval-request',\n 'approval-resolved',\n])\n\n/**\n * Adapt ONE Product Guide event to the NATS kernel.\n *\n * The guide half of a Mingo dialog is the hub's own stream, so it is decoded by\n * the shared table in `./leading-frames` and then passed through here — the ONE\n * place that reconciles the two kernels. Both entry points use it: frames\n * re-streamed by the agent inside `GUIDE` chunks, and the SSE response of the\n * hub's confirm-tool route, which a host replays into the same dialog.\n *\n * Pass-through is the DEFAULT; only these rules alter an event, and each exists\n * because the two kernels genuinely disagree:\n *\n * - `text-delta` becomes `guide-delta`: the body of a guide turn belongs\n * inside the \"OpenFrame Guide\" card, and a `text` segment would strand part\n * of the same answer outside it.\n * - thinking is escaped HERE. The SSE kernel escapes `<` on the way in, the\n * NATS kernel does not, and the guide's thinking is full of XML-ish tokens\n * that would otherwise render as markup.\n * - `metadata` survives ONLY to carry `conversationId`, which the hub mints\n * and every confirm-tool call must quote back. Everything else on that\n * frame (the hub's model, routing) is dropped: `applyNats` rebuilds the\n * dialog's live model from a metadata event, so letting it through would\n * relabel a Mingo turn with the hub's model, and a routing frame — which\n * carries no model at all — would blank the badge mid-answer.\n * - dialog-level events stop here (`AGENT_OWNED_EVENTS`).\n *\n * Everything else crosses over as the hub typed it, gaining only `origin`.\n * `approvalType` in particular stays the TOOL NAME, exactly as in the hub's own\n * chat: the NATS kernel gates approvals on approval TIER and escalates the rest,\n * which is an agent-side concept a hub proposal has no tier for. Rewriting the\n * tool name into a fake tier to slip past that gate would make the guide half of\n * the stream diverge from the guide chat itself.\n */\nexport function guideEventForNats(event: ChatStreamEvent): ChatStreamEvent | null {\n switch (event.type) {\n case 'text-delta':\n return { type: 'guide-delta', text: event.text }\n case 'thinking-delta':\n return { type: 'thinking-delta', text: escapeThinkingTags(event.text) }\n case 'metadata':\n return typeof event.conversationId === 'string' && event.conversationId\n ? { type: 'metadata', conversationId: event.conversationId, origin: GUIDE_ORIGIN }\n : null\n default:\n if (AGENT_OWNED_EVENTS.has(event.type)) return null\n return ORIGIN_BEARING_EVENTS.has(event.type)\n ? ({ ...event, origin: GUIDE_ORIGIN } as ChatStreamEvent)\n : event\n }\n}\n\n/**\n * Decode one re-streamed Product Guide frame (a `GUIDE` chunk's `payload`) into\n * a NATS event. The frame grammar is decoded by the shared table, never by a\n * second copy here; the kernel reconciliation is `guideEventForNats`.\n */\nexport function guideFrameEvent(frame: Record<string, unknown>): ChatStreamEvent | null {\n const events: ChatStreamEvent[] = []\n // Every branch of the table pushes at most one event, so the first is the one.\n mapLeadingFrame(frame, events)\n const event = events[0]\n return event ? guideEventForNats(event) : null\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 } =\n 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: data.modelDisplayName,\n modelName: data.modelName,\n provider: providerName,\n contextWindowMaxTokens:\n 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 shapes share this chunk type. `text` is the answer body the agent\n // streams (and persists). `payload` is a Product Guide frame re-streamed\n // verbatim from the hub — decoded through the shared frame table, with the\n // narrowing `guideFrameEvent` documents.\n case MESSAGE_TYPE.GUIDE: {\n if (typeof data.text === 'string') {\n return { type: 'guide-delta', text: data.text, ...seq }\n }\n const frame = data.payload\n if (!frame || typeof frame !== 'object' || Array.isArray(frame)) return null\n const event = guideFrameEvent(frame as Record<string, unknown>)\n if (!event) return null\n return { ...event, ...seq }\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: data.integratedToolType || '',\n toolFunction: 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: data.parameters,\n toolExecutionRequestId:\n typeof data.toolExecutionRequestId === 'string'\n ? data.toolExecutionRequestId\n : 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: data.integratedToolType || '',\n toolFunction: data.toolFunction || '',\n toolTitle: typeof data.title === 'string' ? data.title : undefined,\n parameters: data.parameters,\n result: data.result,\n success: data.success,\n toolExecutionRequestId:\n typeof data.toolExecutionRequestId === 'string'\n ? data.toolExecutionRequestId\n : undefined,\n },\n ...seq,\n }\n\n case MESSAGE_TYPE.APPROVAL_REQUEST: {\n const requestId = data.approvalRequestId || data.approval_request_id || ''\n const approvalType = 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: data.command || '',\n explanation: 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: data.approvalRequestId || data.approval_request_id || '',\n status: data.approved === true ? 'approved' : 'rejected',\n approvalType: 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\n ? seq\n : typeof data.sequenceId === 'number'\n ? { seq: data.sequenceId }\n : {}\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:\n typeof data.reason === 'string' && data.reason.trim() ? data.reason : undefined,\n targetStatusKind:\n typeof data.targetStatusKind === 'string' && data.targetStatusKind.trim()\n ? data.targetStatusKind\n : undefined,\n ...eventSeq,\n }\n }\n\n case MESSAGE_TYPE.ERROR:\n return {\n type: 'error',\n title: data.error || 'An error occurred',\n details: 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: data.inputTokensSize ?? 0,\n outputTokensSize: data.outputTokensSize ?? 0,\n totalTokensSize: data.totalTokensSize ?? 0,\n contextSize: data.contextSize ?? 0,\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:\n 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: {\n json: () => Promise<unknown>\n}): 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/encode.ts","../../src/chat-protocol/leading-frames.ts","../../src/chat-protocol/decode.ts","../../src/components/chat/types/message.types.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":[],"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 wire-frame shapes these events carry through are defined ONCE in\n// `./frames.ts` — reuse them here rather than restating their fields.\nimport type {\n ApprovalRequestField,\n DecisionResolvedFrame,\n UsageTelemetry,\n} from './frames'\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 { AskOptionData } from '../components/chat/types/message.types'\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/** Guide-body delta (NATS `GUIDE` chunk) — the assistant's how-to /\n * documentation answer, rendered as a titled \"OpenFrame Guide\" card rather\n * than bare prose. Same APPEND-ONLY contract as `text-delta` /\n * `thinking-delta`: each event carries the next verbatim slice and consumers\n * coalesce into the trailing `guide` segment. NATS-only today — the SSE\n * frame grammar has no guide frame — but it lives in the shared union\n * because the reducer is transport-agnostic. */\nexport interface GuideDeltaEvent extends ChatStreamEventBase {\n type: 'guide-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 /** Set when the card came from a Product Guide frame — see {@link GuideOrigin}. */\n origin?: GuideOrigin\n}\n\n/**\n * Marks an event whose payload is a Product Guide frame, whatever transport\n * carried it. It exists because ONE stream can now mix both worlds: the agent\n * re-streams the hub's frames into a NATS dialog, so a card typed the hub's way\n * (`approvalType` = the tool name, resolved through the hub's confirm route)\n * travels beside cards typed the agent's way (`approvalType` = an approval TIER\n * routed to human escalation).\n *\n * Consumers read it to keep the guide half behaving exactly as it does in the\n * hub's own chat — NOT to give it special treatment. Without it the NATS kernel\n * would have to guess from `approvalType`, and every tool the hub adds would\n * silently fall into the escalation path.\n */\nexport type GuideOrigin = 'guide'\n\n/** The only value of {@link GuideOrigin}. Lives beside the type, and beside the\n * predicate below, because both decoders and every consumer that branches on\n * provenance must compare against the same token — a bare `'guide'` literal\n * typo silently disables the branch instead of failing to compile. */\nexport const GUIDE_ORIGIN: GuideOrigin = 'guide'\n\n/** True for anything stamped as coming from the Product Guide — a stream event\n * or the `data` of a segment built from one. */\nexport function isGuideOrigin(\n source: { origin?: GuideOrigin | string } | null | undefined,\n): boolean {\n return source?.origin === GUIDE_ORIGIN\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 /** Set when the resolution came from a Product Guide frame — see {@link GuideOrigin}. */\n origin?: GuideOrigin\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(\n state: unknown,\n): 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/** 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 * Set when the metadata came from a Product Guide frame — see\n * {@link GuideOrigin}. Such an event carries ONLY `conversationId`: it exists\n * to record the hub's conversation id (every confirm-tool call must quote it\n * back), NOT to describe the dialog's model, which stays the agent's.\n */\n origin?: GuideOrigin\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/** 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 | GuideDeltaEvent\n | AskEvent\n | StatusEvent\n | ToolExecutionEvent\n | ApprovalRequestEvent\n | ApprovalResolvedEvent\n | EscalationOfferEvent\n | EscalationOfferResolvedEvent\n | TicketEscalatedEvent\n | TicketEventEvent\n | ChatMetadataEvent\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. The NATS decoder (`./nats-decoder.ts`) reaches it for `GUIDE` chunks\n * carrying a `payload`: the saas-ai-agent re-streams the Product Guide's hub\n * frames VERBATIM into the Mingo chunk stream (`GuideStreamingService`), so the\n * bytes a guide answer is made of are the same frames on both transports — only\n * the envelope differs. Decoding them twice would drift the moment the hub adds\n * a frame kind, which is why this table lives outside either decoder.\n *\n * The NATS side does NOT take every event this table can produce; see\n * `guideFrameEvent` in `./nats-decoder.ts` for which ones cross over and why.\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/**\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: any, 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: meta.input_tokens,\n cache_read_input_tokens: meta.cache_read_input_tokens,\n cache_creation_input_tokens: 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 as Array<Record<string, any>>)\n .filter((p) => 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(\n (f) => f && f.label && f.value,\n )\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 =\n rawFields.find((f) => /^(title|subject|name)$/i.test(String(f.label))) ??\n rawFields[0]\n const base =\n 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\n ? Object.fromEntries(rawFields.map((f) => [String(f.label), String(f.value)]))\n : 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 =\n typeof meta.title === 'string' && meta.title.length > 0 ? meta.title : toolName\n const rawFields = Array.isArray(meta.fields)\n ? (meta.fields as Array<{ label?: string; value?: string }>)\n : []\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:\n 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: meta.provider,\n modelLabel: meta.modelLabel,\n modelName: meta.model,\n contextWindowMaxTokens: meta.contextWindowMaxTokens,\n scrollAnchor: meta.scrollAnchor,\n conversationId: 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 { FRAME_TERMINATOR, END_OF_LEADING, TRAILER_SENTINEL } from './frames'\nimport type { ChatStreamEvent } from './events'\n// The frame table is SHARED with the NATS decoder — guide answers re-stream the\n// hub's frames verbatim inside `GUIDE` chunks — so it lives in its own module.\nimport { mapLeadingFrame } 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\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: any\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 mapLeadingFrame(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 const meta = JSON.parse(raw)\n if (meta.kind === 'usage' && (meta.stage === 'end' || meta.stage === 'display')) {\n out.push({\n type: 'usage',\n stage: 'end',\n input_tokens: meta.input_tokens,\n output_tokens: meta.output_tokens,\n cache_read_input_tokens: meta.cache_read_input_tokens,\n cache_creation_input_tokens: meta.cache_creation_input_tokens,\n hit_rate_pct: meta.hit_rate_pct,\n telemetry: meta.telemetry,\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 { AssistantType, AuthorType, ChatApprovalStatus, MessageOwner } from './chat.types'\n\n// ========== Message Type Definitions ==========\n\nexport const MESSAGE_TYPE = {\n TEXT: 'TEXT',\n THINKING: 'THINKING',\n GUIDE: 'GUIDE',\n ASK: 'ASK',\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, any>\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, any>\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?: React.ReactNode\n requestId?: string\n approvalRequestId?: string\n approvalType?: string\n /** `'guide'` when the card came from a Product Guide frame (see `GuideOrigin`\n * in `chat-protocol/events`). Carried onto the segment because hosts route\n * and filter approvals by where they came from: mingo lifts its OWN pending\n * cards into a sticky footer, and a guide card must stay inline in the turn\n * the way it does in the hub's chat. */\n origin?: 'guide'\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 =\n | typeof TICKET_EVENT_KIND[keyof typeof TICKET_EVENT_KIND]\n | (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, any> | 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 /** `'guide'` when the batch came from a Product Guide `approval_batch` frame.\n * Same contract and the same reasons as `ApprovalRequestData.origin` — the\n * hub groups multiple proposals into one card, and that card resolves\n * through the hub exactly like a single one. */\n origin?: 'guide'\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 = (\n requestId?: string,\n) => 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/** Guide answer body — the assistant's how-to/documentation reply, rendered as\n * a titled \"OpenFrame Guide\" card instead of a bare paragraph. `text` is\n * markdown, streamed in fragments like a `text` segment and coalesced by the\n * accumulator. */\nexport type GuideSegment = {\n type: 'guide'\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 = TextSegment | ThinkingSegment | GuideSegment | AskSegment | ToolExecutionSegment | ApprovalRequestSegment | ApprovalBatchSegment | EscalationOfferSegment | TicketEscalatedSegment | TicketEventSegment | ErrorSegment | 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}\n\nexport interface ThinkingMessageData extends MessageDataBase {\n type: 'THINKING'\n text?: string\n}\n\nexport interface GuideMessageData extends MessageDataBase {\n type: 'GUIDE'\n text?: string\n /** Persisted Product Guide frame (GraphQL `GuideData.payload`, a JSON scalar)\n * for rows that carry a card instead of answer text — replayed through the\n * live path's `guideFrameEvent`. */\n payload?: Record<string, 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 options?: AskOptionData[]\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, any>\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, any>\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\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 | GuideMessageData\n | AskMessageData\n | ExecutingToolMessageData\n | ExecutedToolMessageData\n | ApprovalRequestMessageData\n | ApprovalResultMessageData\n | EscalationOfferMessageData\n | TicketEscalatedMessageData\n | TicketEventMessageData\n | ErrorMessageData\n | AIMetadataMessageData\n | SystemMessageData\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}\n\n// ========== Base Message Interface ==========\n\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 * 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, GUIDE_ORIGIN, escalationResolvedStatus } from './events'\nimport type { ApprovalToolCall, ChatStreamEvent } from './events'\nimport { escapeThinkingTags, mapLeadingFrame } from './leading-frames'\n\n/** Minimal structural view of a NATS chunk (see `ChunkData` in\n * `src/components/chat/types/network.types.ts`). */\ntype NatsChunk = Record<string, any>\n\n// `GUIDE_ORIGIN` moved to `./events`, beside the `GuideOrigin` type it is the\n// only value of — the SSE half reads it too, so it never belonged to the NATS\n// decoder. Re-exported here because that is the import path consumers know.\nexport { GUIDE_ORIGIN } from './events'\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\n .filter((item): item is Record<string, any> => !!item && typeof item === 'object')\n .map((item) => ({\n toolExecutionRequestId: String(item.toolExecutionRequestId ?? ''),\n toolName: String(item.toolName ?? ''),\n toolTitle: typeof item.toolTitle === 'string' ? item.toolTitle : undefined,\n toolExplanation:\n 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:\n item.toolCallArguments && typeof item.toolCallArguments === 'object'\n ? (item.toolCallArguments as Record<string, any>)\n : 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((item): item is Record<string, any> => !!item && typeof item === 'object')\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 * Events whose meaning belongs to the AGENT's dialog rather than to the guide\n * turn embedded inside it. The hub emits its own copies of these, and the Mingo\n * dialog already has authoritative ones: letting the hub's through would\n * double-count the dialog's tokens or fight the agent's own phase chunks.\n *\n * Note this is a DENYLIST, and that is the point. `./leading-frames` is the ONE\n * place a hub frame kind is taught to the client; whatever it learns to emit —\n * including kinds that do not exist yet — must reach a Mingo dialog without a\n * second edit here. An allowlist would silently swallow every card the hub adds\n * next, and the guide half would quietly lag the guide chat.\n */\nconst AGENT_OWNED_EVENTS: ReadonlySet<ChatStreamEvent['type']> = new Set([\n 'usage',\n 'token-usage',\n 'status',\n 'compaction',\n 'participant',\n 'dialog-closed',\n])\n\n/** Event types that declare `origin` (see `GuideOrigin`). Data rather than a\n * branch per type, so a future hub card opts in by adding the field to its\n * interface and its name here — the pass-through below needs no edit. */\nconst ORIGIN_BEARING_EVENTS: ReadonlySet<ChatStreamEvent['type']> = new Set([\n 'approval-request',\n 'approval-resolved',\n])\n\n/**\n * Adapt ONE Product Guide event to the NATS kernel.\n *\n * The guide half of a Mingo dialog is the hub's own stream, so it is decoded by\n * the shared table in `./leading-frames` and then passed through here — the ONE\n * place that reconciles the two kernels. Both entry points use it: frames\n * re-streamed by the agent inside `GUIDE` chunks, and the SSE response of the\n * hub's confirm-tool route, which a host replays into the same dialog.\n *\n * Pass-through is the DEFAULT; only these rules alter an event, and each exists\n * because the two kernels genuinely disagree:\n *\n * - `text-delta` becomes `guide-delta`: the body of a guide turn belongs\n * inside the \"OpenFrame Guide\" card, and a `text` segment would strand part\n * of the same answer outside it.\n * - thinking is escaped HERE. The SSE kernel escapes `<` on the way in, the\n * NATS kernel does not, and the guide's thinking is full of XML-ish tokens\n * that would otherwise render as markup.\n * - `metadata` survives ONLY to carry `conversationId`, which the hub mints\n * and every confirm-tool call must quote back. Everything else on that\n * frame (the hub's model, routing) is dropped: `applyNats` rebuilds the\n * dialog's live model from a metadata event, so letting it through would\n * relabel a Mingo turn with the hub's model, and a routing frame — which\n * carries no model at all — would blank the badge mid-answer.\n * - dialog-level events stop here (`AGENT_OWNED_EVENTS`).\n *\n * Everything else crosses over as the hub typed it, gaining only `origin`.\n * `approvalType` in particular stays the TOOL NAME, exactly as in the hub's own\n * chat: the NATS kernel gates approvals on approval TIER and escalates the rest,\n * which is an agent-side concept a hub proposal has no tier for. Rewriting the\n * tool name into a fake tier to slip past that gate would make the guide half of\n * the stream diverge from the guide chat itself.\n */\nexport function guideEventForNats(event: ChatStreamEvent): ChatStreamEvent | null {\n switch (event.type) {\n case 'text-delta':\n return { type: 'guide-delta', text: event.text }\n case 'thinking-delta':\n return { type: 'thinking-delta', text: escapeThinkingTags(event.text) }\n case 'metadata':\n return typeof event.conversationId === 'string' && event.conversationId\n ? { type: 'metadata', conversationId: event.conversationId, origin: GUIDE_ORIGIN }\n : null\n default:\n if (AGENT_OWNED_EVENTS.has(event.type)) return null\n return ORIGIN_BEARING_EVENTS.has(event.type)\n ? ({ ...event, origin: GUIDE_ORIGIN } as ChatStreamEvent)\n : event\n }\n}\n\n/**\n * Decode one re-streamed Product Guide frame (a `GUIDE` chunk's `payload`) into\n * a NATS event. The frame grammar is decoded by the shared table, never by a\n * second copy here; the kernel reconciliation is `guideEventForNats`.\n */\nexport function guideFrameEvent(frame: Record<string, unknown>): ChatStreamEvent | null {\n const events: ChatStreamEvent[] = []\n // Every branch of the table pushes at most one event, so the first is the one.\n mapLeadingFrame(frame, events)\n const event = events[0]\n return event ? guideEventForNats(event) : null\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 } =\n 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: data.modelDisplayName,\n modelName: data.modelName,\n provider: providerName,\n contextWindowMaxTokens:\n 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 shapes share this chunk type. `text` is the answer body the agent\n // streams (and persists). `payload` is a Product Guide frame re-streamed\n // verbatim from the hub — decoded through the shared frame table, with the\n // narrowing `guideFrameEvent` documents.\n case MESSAGE_TYPE.GUIDE: {\n if (typeof data.text === 'string') {\n return { type: 'guide-delta', text: data.text, ...seq }\n }\n const frame = data.payload\n if (!frame || typeof frame !== 'object' || Array.isArray(frame)) return null\n const event = guideFrameEvent(frame as Record<string, unknown>)\n if (!event) return null\n return { ...event, ...seq }\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: data.integratedToolType || '',\n toolFunction: 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: data.parameters,\n toolExecutionRequestId:\n typeof data.toolExecutionRequestId === 'string'\n ? data.toolExecutionRequestId\n : 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: data.integratedToolType || '',\n toolFunction: data.toolFunction || '',\n toolTitle: typeof data.title === 'string' ? data.title : undefined,\n parameters: data.parameters,\n result: data.result,\n success: data.success,\n toolExecutionRequestId:\n typeof data.toolExecutionRequestId === 'string'\n ? data.toolExecutionRequestId\n : undefined,\n },\n ...seq,\n }\n\n case MESSAGE_TYPE.APPROVAL_REQUEST: {\n const requestId = data.approvalRequestId || data.approval_request_id || ''\n const approvalType = 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: data.command || '',\n explanation: 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: data.approvalRequestId || data.approval_request_id || '',\n status: data.approved === true ? 'approved' : 'rejected',\n approvalType: 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\n ? seq\n : typeof data.sequenceId === 'number'\n ? { seq: data.sequenceId }\n : {}\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:\n typeof data.reason === 'string' && data.reason.trim() ? data.reason : undefined,\n targetStatusKind:\n typeof data.targetStatusKind === 'string' && data.targetStatusKind.trim()\n ? data.targetStatusKind\n : undefined,\n ...eventSeq,\n }\n }\n\n case MESSAGE_TYPE.ERROR:\n return {\n type: 'error',\n title: data.error || 'An error occurred',\n details: 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: data.inputTokensSize ?? 0,\n outputTokensSize: data.outputTokensSize ?? 0,\n totalTokensSize: data.totalTokensSize ?? 0,\n contextSize: data.contextSize ?? 0,\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:\n 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: {\n json: () => Promise<unknown>\n}): 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"]}