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