{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-F7R7J4HK.cjs","../src/components/tickets/ticket-live-provider.tsx","../src/components/tickets/types.ts"],"names":[],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-F7R7J4HK.cjs","sourcesContent":[null,"'use client';\n\n/**\n * TicketLiveProvider — the ONE client home for support-ticket realtime.\n *\n * DOMAIN logic only — connection lifecycle (subscribed-confirm, server\n * retry grace, failed-reconnect pacing, hidden-tab suspend/resume) is\n * owned entirely by the common SSE client (`createSseSubscription`);\n * this provider consumes its consolidated `onConnectedChange` signal.\n *\n * Owns:\n *   - the unread summary — delivered EXCLUSIVELY by the stream\n *     (`ticket-summary` frames on connect + debounced after events) and\n *     by `ticket-read` responses. The client holds NO summary fetch\n *     path: resync-on-(re)connect is served by the SERVER (every\n *     connect pushes a fresh summary);\n *   - query invalidation on events (list + open drawer refresh live);\n *   - `markRead` with local write-through zeroing for 0-latency,\n *     reconciled by the response's server-computed summary;\n *   - the ticket-contract `no-stream` (204) retry policy.\n *\n * There is NO polling anywhere and NO summary endpoint. Hosts whose\n * stream endpoint 404s (terminal) or is absent get no unread indication\n * — the indication IS a realtime feature.\n *\n * Invalidation semantics on events — deliberately `invalidateQueries`,\n * NOT `setQueryData` patches: the ticket hooks run `gcTime: 0`, so\n * there is usually no cache entry to patch, and `TicketsCacheSlot`\n * shape rules apply when there is.\n */\n\nimport { useQueryClient } from '@tanstack/react-query';\nimport React from 'react';\nimport { useChatRuntime } from '../../contexts/chat-runtime-context';\nimport { embedAuthedFetch } from '../../utils/embed-authed-fetch';\nimport { createSseSubscription, type SseSubscription, type SseTransportStatus } from '../../utils/sse-subscription';\nimport { useChatIdentity } from '../chat/hooks/use-chat-identity';\nimport type { TicketMessageStreamData, TicketStreamEvent, TicketUnreadSummary } from './types';\n\n// Ticket realtime is its OWN api surface — deliberately NOT under\n// `/api/chat/agent/*` (that prefix is the chat agent's tool surface;\n// ticket streaming/read-receipts are unrelated to chat).\nconst TICKET_STREAM_ENDPOINT = '/api/tickets/stream';\nconst TICKET_READ_ENDPOINT = '/api/tickets/read';\n\nconst EMPTY_SUMMARY: TicketUnreadSummary = { totalUnread: 0, tickets: {}, latestUnreadAt: {} };\n\n/** ISO stamps can carry mixed timezone offsets (server `+00:00` vs an\n *  optimistic local `Z`) — lexicographic string comparison mis-orders\n *  those, so every stamp comparison goes through parsed epoch ms.\n *  Missing/unparseable stamps sort oldest. */\nconst stampMs = (stamp: string | null | undefined): number => {\n  if (!stamp) return 0;\n  const ms = Date.parse(stamp);\n  return Number.isNaN(ms) ? 0 : ms;\n};\n\n/** Trailing debounce for event-burst invalidations (an agent pasting 5\n *  messages must not fire 5 list refetches). */\nconst INVALIDATE_DEBOUNCE_MS = 1_500;\n\nexport interface TicketLiveContextValue {\n  /** Viewer has a resolved, non-anon chat identity. Surfaces (e.g. the\n   *  header cell) hide themselves when false — computed here so they\n   *  don't each spin up their own identity resolution. */\n  authed: boolean;\n  /** Realtime-subscribed (server-confirmed), not merely HTTP-open. */\n  connected: boolean;\n  /** Unread total across tickets — drives the header count badge. */\n  unreadTotal: number;\n  /** Per-ticket unread counts (missing key = 0) — drives row dots.\n   *  The currently-open ticket is masked to 0. */\n  unreadByTicket: Record<string, number>;\n  /** The ticket with the NEWEST unread reply (open ticket excluded) —\n   *  the header cell routes to it via the SSOT deep link\n   *  (`buildTicketOpenHref`). Null when nothing is unread. */\n  nextUnreadTicketId: string | null;\n  /** Mark a ticket read: POSTs the receipt, zeroes the ticket locally\n   *  (0-latency), then applies the response's server-computed summary. */\n  markRead: (ticketExternalId: string) => Promise<void>;\n  /** Drawer open/close registration — suppresses unread bumps for the\n   *  open ticket and masks it in the derived map. */\n  setOpenTicketId: (ticketExternalId: string | null) => void;\n  /** Hint that the viewer just created a ticket — reconnects the stream\n   *  (fresh ownership set + fresh server-pushed summary). */\n  notifyTicketCreated: () => void;\n}\n\nconst TicketLiveContext = React.createContext<TicketLiveContextValue | null>(null);\n\nexport function useOptionalTicketLive(): TicketLiveContextValue | null {\n  return React.useContext(TicketLiveContext);\n}\n\nexport function useTicketLive(): TicketLiveContextValue {\n  const ctx = React.useContext(TicketLiveContext);\n  if (!ctx) {\n    throw new Error('useTicketLive must be used within <TicketLiveProvider>');\n  }\n  return ctx;\n}\n\nfunction isTicketMessageData(value: unknown): value is TicketMessageStreamData {\n  return (\n    typeof value === 'object' &&\n    value !== null &&\n    typeof (value as TicketMessageStreamData).ticket_external_id === 'string'\n  );\n}\n\nfunction isSummaryData(value: unknown): value is TicketUnreadSummary {\n  return (\n    typeof value === 'object' &&\n    value !== null &&\n    typeof (value as TicketUnreadSummary).totalUnread === 'number' &&\n    typeof (value as TicketUnreadSummary).tickets === 'object'\n  );\n}\n\nexport interface TicketLiveProviderProps {\n  children: React.ReactNode;\n  /**\n   * Whether ticket realtime is available to this host at all (feature flag,\n   * session, entitlement). `false` provides NO context — `useOptionalTicketLive()`\n   * reads `null`, `useTicketLive()` throws, and no identity or stream request is\n   * made: byte-for-byte what a host got by not mounting this at all.\n   *\n   * It exists so a host never has to mount this CONDITIONALLY. A host that writes\n   * `enabled ? <TicketLiveProvider>{children}</TicketLiveProvider> : <>{children}</>`\n   * changes the element TYPE at that position the moment its flag answers, and React\n   * tears down and remounts everything below. In openframe-frontend that wrapper sat\n   * around the whole `AppLayout`, so every cold load remounted the app shell and the\n   * page under it — visibly, as a chat drawer opened by a deep link replaying its\n   * open animation. Mount it always and pass the answer here instead.\n   *\n   * One consequence of mounting it always: a `ChatRuntime` must be above it even\n   * while disabled. The identity resolver reads one unconditionally (Rules of\n   * Hooks) and throws without it; `enabled` gates the request, not the read.\n   */\n  enabled?: boolean;\n}\n\nexport function TicketLiveProvider({ children, enabled = true }: TicketLiveProviderProps) {\n  const runtime = useChatRuntime();\n  const identity = useChatIdentity(enabled);\n  const queryClient = useQueryClient();\n\n  const streamUrl = runtime?.endpoints.ticketStreamUrl ?? TICKET_STREAM_ENDPOINT;\n  const readUrl = runtime?.endpoints.ticketReadUrl ?? TICKET_READ_ENDPOINT;\n\n  // Gates the stream below (via `streamEnabled`) as well as every surface that\n  // reads `authed` — one predicate, so a disabled provider is also a silent one.\n  const authed = enabled && identity.authTier !== 'anon' && !!identity.user?.email;\n\n  const [connected, setConnected] = React.useState(false);\n  // The SINGLE unread source — written ONLY from server-computed\n  // summaries (stream frames + markRead responses) plus the two\n  // 0-latency optimistic writes (event bump, markRead zero), both\n  // reconciled by the next server summary.\n  const [summary, setSummary] = React.useState<TicketUnreadSummary>(EMPTY_SUMMARY);\n  // State + ref for the open drawer: state drives the derived map,\n  // the ref lets stream callbacks read it without re-subscribing.\n  const [openTicketIdState, setOpenTicketIdState] = React.useState<string | null>(null);\n  const openTicketIdRef = React.useRef<string | null>(null);\n\n  // ---- Debounced (trailing) invalidation of ticket queries on events ----\n  const pendingTicketIdsRef = React.useRef<Set<string>>(new Set());\n  const invalidateTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n  const flushInvalidations = React.useCallback(() => {\n    invalidateTimerRef.current = null;\n    const ids = Array.from(pendingTicketIdsRef.current);\n    pendingTicketIdsRef.current.clear();\n    void queryClient.invalidateQueries({ queryKey: ['tickets'] });\n    for (const id of ids) {\n      void queryClient.invalidateQueries({ queryKey: ['ticket-engagements', id] });\n    }\n  }, [queryClient]);\n  const scheduleInvalidation = React.useCallback(\n    (ticketId?: string) => {\n      if (ticketId) pendingTicketIdsRef.current.add(ticketId);\n      if (invalidateTimerRef.current) return;\n      invalidateTimerRef.current = setTimeout(flushInvalidations, INVALIDATE_DEBOUNCE_MS);\n    },\n    [flushInvalidations],\n  );\n\n  // ---- Optimistic unread writes (reconciled by server summaries) ----\n  const bumpUnread = React.useCallback((ticketId: string, createdAt?: string | null) => {\n    setSummary(prev => {\n      const stamp = createdAt ?? new Date().toISOString();\n      const existing = prev.latestUnreadAt[ticketId];\n      return {\n        totalUnread: prev.totalUnread + 1,\n        tickets: { ...prev.tickets, [ticketId]: (prev.tickets[ticketId] ?? 0) + 1 },\n        latestUnreadAt: {\n          ...prev.latestUnreadAt,\n          [ticketId]: existing && stampMs(existing) > stampMs(stamp) ? existing : stamp,\n        },\n      };\n    });\n  }, []);\n\n  const zeroUnread = React.useCallback((ticketId: string) => {\n    setSummary(prev => {\n      const current = prev.tickets[ticketId] ?? 0;\n      if (current === 0) return prev;\n      const tickets = { ...prev.tickets };\n      delete tickets[ticketId];\n      const latestUnreadAt = { ...prev.latestUnreadAt };\n      delete latestUnreadAt[ticketId];\n      return { totalUnread: Math.max(0, prev.totalUnread - current), tickets, latestUnreadAt };\n    });\n  }, []);\n\n  // ---- Stream lifecycle (owned by the SSE CLIENT — the provider only\n  // consumes its consolidated signals) ----\n  const subscriptionRef = React.useRef<SseSubscription | null>(null);\n  const noStreamRef = React.useRef(false);\n\n  // Stable handler refs so the subscription effect doesn't churn.\n  const handlersRef = React.useRef<{\n    onEvent: (eventName: string, data: unknown) => void;\n    onStatus: (status: SseTransportStatus) => void;\n    onConnected: (connected: boolean) => void;\n  }>({ onEvent: () => {}, onStatus: () => {}, onConnected: () => {} });\n\n  const handleConnected = (isConnected: boolean) => {\n    setConnected(isConnected);\n    if (isConnected) {\n      // Event-gap healing on every (re)connect: the SERVER pushes a\n      // fresh summary; the open drawer refetches through the ACL'd path.\n      const openId = openTicketIdRef.current;\n      if (openId) {\n        void queryClient.invalidateQueries({ queryKey: ['ticket-engagements', openId] });\n      }\n    }\n  };\n\n  const handleEvent = (eventName: string, data: unknown) => {\n    if (eventName === 'ticket-summary') {\n      const frame = data as TicketStreamEvent | null;\n      if (isSummaryData(frame?.data)) {\n        setSummary({\n          totalUnread: frame.data.totalUnread,\n          tickets: frame.data.tickets ?? {},\n          latestUnreadAt: frame.data.latestUnreadAt ?? {},\n        });\n      }\n      return;\n    }\n\n    if (eventName === 'ticket-resync') {\n      // Ownership gained / degraded payload — a fresh ticket-summary\n      // frame follows from the server; refresh the query surfaces.\n      scheduleInvalidation();\n      return;\n    }\n\n    if (eventName === 'ticket-status') {\n      // Meaningful ticket-row change (close/reopen/pipeline move). The\n      // server pushes an updated summary right after (closures count as\n      // unread server-side); here we just refresh the query surfaces so\n      // the status badge flips live.\n      const frame = data as TicketStreamEvent | null;\n      const ticketId = (frame?.data as { ticket_external_id?: string } | undefined)?.ticket_external_id;\n      scheduleInvalidation(ticketId);\n      return;\n    }\n\n    if (eventName === 'ticket-message') {\n      const frame = data as TicketStreamEvent | null;\n      const payload = frame?.data;\n      if (!isTicketMessageData(payload)) {\n        scheduleInvalidation();\n        return;\n      }\n      // Always invalidate (own replies from another tab render live);\n      // bump unread ONLY on the server-stamped predicate, and never for\n      // the ticket whose drawer is open. The server's debounced summary\n      // push reconciles moments later.\n      scheduleInvalidation(payload.ticket_external_id);\n      if (payload.countsAsUnread && payload.ticket_external_id !== openTicketIdRef.current) {\n        bumpUnread(payload.ticket_external_id, payload.hubspot_created_at);\n      }\n    }\n  };\n\n  const handleStatus = (status: SseTransportStatus) => {\n    // The client owns connection lifecycle (confirm timeout, server\n    // retry grace, failed-reconnect pacing, hidden-suspend). The only\n    // transport status with DOMAIN meaning is the ticket contract's\n    // `no-stream` (204 = zero owned tickets): remember it so a domain\n    // signal (own create_ticket, window focus) can retry.\n    if (status === 'open') noStreamRef.current = false;\n    if (status === 'no-stream') noStreamRef.current = true;\n  };\n\n  // Publish the three handlers into the stable ref. In an unconditional effect\n  // rather than in the render body: every caller is an SSE transport callback,\n  // which can only fire after a commit, and a render attempt React discards\n  // must not be able to leave a handler behind that closes over state the user\n  // never saw. Declared BEFORE the subscription effect so it is filled first\n  // in the same flush.\n  React.useEffect(() => {\n    handlersRef.current.onConnected = handleConnected;\n    handlersRef.current.onEvent = handleEvent;\n    handlersRef.current.onStatus = handleStatus;\n  });\n\n  // Mount/unmount of the subscription — gated on auth + a usable URL.\n  const streamEnabled = authed && !!streamUrl;\n  React.useEffect(() => {\n    if (!streamEnabled) return undefined;\n    if (typeof window === 'undefined') return undefined;\n\n    subscriptionRef.current = createSseSubscription({\n      url: streamUrl,\n      onEvent: (name, data) => handlersRef.current.onEvent(name, data),\n      onStatusChange: status => handlersRef.current.onStatus(status),\n      onConnectedChange: isConnected => handlersRef.current.onConnected(isConnected),\n      // Long-lived per-user stream: suspend while hidden (scale relief),\n      // resume + reconnect on visible/online — all inside the client.\n      pauseWhenHidden: true,\n    });\n\n    const retryIfIdle = () => {\n      // After a 204 (no-stream), a focus signal is the retry trigger.\n      if (noStreamRef.current) {\n        noStreamRef.current = false;\n        subscriptionRef.current?.reconnectNow();\n      }\n    };\n    window.addEventListener('focus', retryIfIdle);\n\n    return () => {\n      window.removeEventListener('focus', retryIfIdle);\n      if (invalidateTimerRef.current) {\n        clearTimeout(invalidateTimerRef.current);\n        invalidateTimerRef.current = null;\n      }\n      subscriptionRef.current?.close();\n      subscriptionRef.current = null;\n      setConnected(false);\n      setSummary(EMPTY_SUMMARY);\n    };\n    // handlersRef is stable; streamUrl/auth changes remount the stream.\n  }, [streamEnabled, streamUrl]);\n\n  // ---- Public API ----\n  const markRead = React.useCallback(\n    async (ticketExternalId: string) => {\n      try {\n        const response = await embedAuthedFetch(readUrl, {\n          method: 'POST',\n          body: JSON.stringify({ ticket_id: ticketExternalId }),\n        });\n        if (!response.ok) return;\n        // 0-latency local zero, then reconcile with the response's\n        // server-computed summary (THE truth — includes closure\n        // accounting and other tickets).\n        zeroUnread(ticketExternalId);\n        const body = (await response.json().catch(() => null)) as { summary?: TicketUnreadSummary } | null;\n        if (isSummaryData(body?.summary)) {\n          setSummary({\n            totalUnread: body.summary.totalUnread,\n            tickets: body.summary.tickets ?? {},\n            latestUnreadAt: body.summary.latestUnreadAt ?? {},\n          });\n        }\n      } catch {\n        // Non-fatal — the receipt lands on the next markRead; unread\n        // state reconciles via the next server summary push.\n      }\n    },\n    [readUrl, zeroUnread],\n  );\n\n  const setOpenTicketId = React.useCallback((ticketExternalId: string | null) => {\n    openTicketIdRef.current = ticketExternalId;\n    setOpenTicketIdState(ticketExternalId);\n  }, []);\n\n  const notifyTicketCreated = React.useCallback(() => {\n    // Fresh connect → fresh ownership set + fresh server summary\n    // (covers both the no-stream 204 wait state and an already-live\n    // stream whose set predates the new ticket).\n    noStreamRef.current = false;\n    subscriptionRef.current?.reconnectNow();\n  }, []);\n\n  // Derived unread map — open ticket masked to 0 so a server summary\n  // landing inside the markRead debounce window can't transiently\n  // re-badge it.\n  const value = React.useMemo<TicketLiveContextValue>(() => {\n    let unreadByTicket = summary.tickets;\n    let unreadTotal = summary.totalUnread;\n    if (openTicketIdState && unreadByTicket[openTicketIdState]) {\n      const masked = unreadByTicket[openTicketIdState];\n      unreadByTicket = { ...unreadByTicket };\n      delete unreadByTicket[openTicketIdState];\n      unreadTotal = Math.max(0, unreadTotal - masked);\n    }\n    // Ticket with the NEWEST unread reply (compared as parsed epoch ms\n    // via `stampMs` — mixed timezone offsets break string ordering) —\n    // header cell routes there. Falls back to any unread key if a\n    // timestamp is missing.\n    let nextUnreadTicketId: string | null = null;\n    let nextStampMs = 0;\n    for (const id of Object.keys(unreadByTicket)) {\n      const ms = stampMs(summary.latestUnreadAt[id]);\n      if (nextUnreadTicketId === null || ms > nextStampMs) {\n        nextUnreadTicketId = id;\n        nextStampMs = ms;\n      }\n    }\n    return {\n      authed,\n      connected,\n      unreadTotal,\n      unreadByTicket,\n      nextUnreadTicketId,\n      markRead,\n      setOpenTicketId,\n      notifyTicketCreated,\n    };\n  }, [summary, authed, connected, openTicketIdState, markRead, setOpenTicketId, notifyTicketCreated]);\n\n  return <TicketLiveContext.Provider value={enabled ? value : null}>{children}</TicketLiveContext.Provider>;\n}\n","/**\n * Wire shape of a row returned by `POST /api/chat/agent/find-ticket`.\n * Mirrors the executor's projection at `lib/data/hubspot-tools.ts`\n * (`FIND_TICKET_SELECT` / `FindTicketResult`).\n *\n * Cross-repo duplication is INTENTIONAL: this lib ships independently\n * of the hub, so we can't import `FindTicketResult` from\n * `hubspot-tools.ts` directly. If the server adds a column to\n * `FIND_TICKET_SELECT`, also add it here. The smoke test in §F of the\n * plan covers the happy path; a wire-contract test belongs in the hub.\n *\n * `find-ticket` returns `customer_emails: string[]` (jsonb array), NOT\n * a single `customer_email`. The list is server-self-scoped to the\n * caller's session email; the array is exposed for admin/staff\n * surfaces, which the ticket center doesn't render.\n */\nexport interface TicketData {\n  id: string;\n  /** HubSpot ticket id (display number, e.g. \"1234\"). */\n  external_id: string;\n  subject: string | null;\n  /** Short (≤400 char) HTML-stripped preview of the ticket body —\n   *  used for the list-card subtitle when needed. */\n  preview: string | null;\n  /** Longer (≤4k char) sanitized body. INCLUDES every appended\n   *  `content_addendum` comment because the `update_ticket` executor\n   *  reads + re-writes the `content` property server-side with a\n   *  `---` separator. Render this in the drawer's description block\n   *  and the user sees both the original message + every comment they\n   *  (or staff) added. */\n  body: string | null;\n  /** Canonical OPEN | CLOSED (HubSpot pipeline derived). */\n  status: string | null;\n  /** Human label like \"New\" / \"Working on it\" / \"Waiting on contact\" /\n   *  \"Closed\". Drives the badge text; canonical status drives color. */\n  pipeline_stage_label: string | null;\n  clickup_task_id: string | null;\n  /** Snapshot of the linked ClickUp delivery task — populated server-side\n   *  via the `clickup_tasks` mirror when `clickup_task_id` is set. Drives\n   *  the \"Linked delivery\" card surface on the ticket drawer (status\n   *  badge + ClickUp deep link). `null` when no link OR the ClickUp row\n   *  was deleted / not yet synced. */\n  clickup: TicketClickupSummary | null;\n  priority: string | null;\n  customer_emails: string[];\n  customer_company: string | null;\n  /** HubSpot contact's display name. Drives the customer attribution\n   *  on the drawer when the viewer is NOT the customer themselves\n   *  (admin browsing / multi-contact second viewer). Conversations\n   *  API messages don't carry per-message sender info on Custom\n   *  Channels, so this is the only reliable source for \"what's the\n   *  customer's name.\" */\n  customer_name: string | null;\n  /** HubSpot owner id of the agent assigned to this ticket. Carried as\n   *  raw id for debugging; rendering goes through `assignedOwner`. Null\n   *  when unassigned. */\n  assigned_to: string | null;\n  /** Resolved assigned-owner profile — name + email + avatar. Populated\n   *  server-side via `attachOwnerProfiles` which joins through the\n   *  `hubspot_owners` mirror to `profiles` by email. Drives the\n   *  \"Assigned to\" attribution in the drawer header. Null when\n   *  unassigned OR the owner couldn't be resolved (rare — only when\n   *  the agent was deleted from HubSpot between the ticket update and\n   *  the next owners reconcile). */\n  assignedOwner: TicketAssignedOwner | null;\n  hubspot_updated_at: string;\n}\n\n/** Resolved profile of a ticket's assigned agent — surfaced in the\n *  drawer header. Subset of the server's `MirroredOwnerProfile`\n *  trimmed to just the rendering fields. */\nexport interface TicketAssignedOwner {\n  name: string | null;\n  email: string | null;\n  avatarUrl: string | null;\n}\n\n/** Compact projection of a linked ClickUp task — matches the server's\n *  `ClickupSummary` and aligns with `DeliveryItem` so the linked-card\n *  on a ticket can render through the same `DeliveryRow` primitive used\n *  on `/bug-fixes-and-enhancements`. */\nexport interface TicketClickupSummary {\n  /** ClickUp task external_id (e.g. \"86ad4e022\"). Used as the\n   *  `?focus=<id>` URL param to scroll the public delivery page to\n   *  this row. */\n  external_id: string;\n  title: string | null;\n  description: string | null;\n  /** ClickUp status name — e.g. \"complete\" / \"working\" / \"design approved\"\n   *  / \"waiting for release\". Used as the badge label. */\n  status: string | null;\n  /** ClickUp's per-status hex color (e.g. \"#008844\"). Forwarded to the\n   *  badge so colors match the ClickUp board exactly. */\n  status_color: string | null;\n  /** Bucket — `'backlog' | 'working' | 'complete' | 'unknown'`. Used as\n   *  a fallback when status_color is missing. */\n  status_category: string | null;\n  /** ClickUp custom item label (`'Bug'` / `'Request'`) — drives the\n   *  type badge (\"BUG-FIX\" / \"ENHANCEMENT\"). */\n  task_type: string | null;\n  custom_item_id: number | null;\n  /** Every ClickUp list the task is associated with. UI joins with \", \". */\n  list_names: string[];\n  /** Unix-ms timestamps so the row's \"ACTIVE X ago\" subtitle uses the\n   *  shared `getRelativeTime()` helper. */\n  date_opened: number | null;\n  date_updated: number | null;\n  date_closed: number | null;\n  /** Direct https://app.clickup.com/t/<id> deep link. Kept on the wire\n   *  for admin surfaces; the customer-facing linked card navigates\n   *  internally instead. */\n  clickup_url: string | null;\n  /** Composed server-side via the SAME `buildDevSectionUrl` helper the\n   *  chat-inline delivery card uses. Carries `?search=<id>` so the\n   *  delivery list filters to that single task on landing. */\n  delivery_href: string;\n  /** Target platform name for the host's `useNavLink` to decide\n   *  same-tab vs new-tab on cross-platform links. */\n  delivery_target_platform: string;\n  /** Release version label set by the delivery team, e.g. \"0.9\" / \"1.0\".\n   *  Shown beside the status when present. */\n  target_version: string | null;\n}\n\n/**\n * Optimistic placeholder a `submitTicket` call prepends to the list\n * BEFORE the server roundtrip resolves. Drawer is hidden until the\n * real id arrives. The wrapper destructures `_optimistic` before\n * forwarding to `<ChatTicketItem>` so the DOM doesn't see an unknown\n * prop.\n */\nexport interface OptimisticTicket extends TicketData {\n  _optimistic: true;\n}\n\nexport type AnyTicket = TicketData | OptimisticTicket;\n\nexport function isOptimistic(t: AnyTicket): t is OptimisticTicket {\n  return (t as OptimisticTicket)._optimistic === true;\n}\n\n/**\n * Shape of a single `['tickets', …]` TanStack-Query cache slot.\n * Mirrors `FindTicketResponse` in `hooks/use-tickets-list.ts` — kept\n * here because the cache-mutation call sites in `useTicketActions` and\n * `<HelpCenterList>` would otherwise have to redeclare the shape inline.\n *\n * A 2026-05-29 prod regression (`t.map is not a function` on\n * close/reopen) was caused by assuming the cache held a bare\n * `TicketData[]` instead of this wrapper — every helper that calls\n * `queryClient.setQueriesData` / `getQueriesData` on `['tickets']`\n * MUST type the value through this shape and project / reassemble\n * `tickets` explicitly.\n */\nexport interface TicketsCacheSlot {\n  tickets?: TicketData[];\n  count?: number;\n  totalCount?: number;\n  page?: number;\n  pageSize?: number;\n  totalPages?: number;\n  scope?: 'self' | 'all';\n}\n\n/**\n * Stable server-side error codes the ticket-action helpers route\n * through `mapTicketActionError`. Anything else is treated as a generic\n * server error.\n *\n * Reply-specific codes (`HUBSPOT_5XX` / `HUBSPOT_400_VALIDATION` /\n * `HUBSPOT_404_THREAD` / `HUBSPOT_REPLY_UNKNOWN`) drive the drawer\n * banner that appears above the composer when a customer reply fails.\n * Distinct from `HUBSPOT_DISCONNECTED` (whole-system) and\n * `TICKET_NOT_FOUND` (terminal-row) — the reply codes are per-attempt\n * + retryable except for `HUBSPOT_404_THREAD`.\n */\nexport type TicketActionErrorCode =\n  | 'PROPOSAL_NOT_CLAIMABLE'\n  | 'TICKET_NOT_FOUND'\n  | 'TICKET_OWNERSHIP_DENIED'\n  | 'HUBSPOT_DISCONNECTED'\n  | 'RATE_LIMITED'\n  | 'INVALID_TOOL_ARGS'\n  | 'HUBSPOT_5XX'\n  | 'HUBSPOT_400_VALIDATION'\n  | 'HUBSPOT_404_THREAD'\n  | 'HUBSPOT_REPLY_UNKNOWN'\n  | 'UNKNOWN';\n\nexport interface MappedTicketActionError {\n  code: TicketActionErrorCode;\n  /** Human-readable copy safe to show in a toast. */\n  message: string;\n  /** When true, the form should disable submit + show the\n   *  support-down banner. Set only for HUBSPOT_DISCONNECTED. */\n  supportSystemDown: boolean;\n  /** When true, the helper should remove the affected row optimistically\n   *  (TICKET_NOT_FOUND). */\n  removeRowFromCache: boolean;\n  /** Retry hint surfaced from a 429 response. Caller decides whether\n   *  to mention it in the toast. */\n  retryAfterSeconds?: number;\n}\n\n/**\n * Defensive client-side cap on ticket text (initial content + comment\n * addendums). HubSpot Note engagements accept more, but a 100KB paste\n * should fail fast at the UI rather than burning a server round-trip.\n * Both the open-ticket form and the per-row comment textarea import\n * this so a future server-side hardening only touches one place.\n */\nexport const TICKET_TEXT_MAX_CHARS = 5000;\n\n/**\n * Wire contract of the ticket live stream (`GET /api/chat/agent/\n * ticket-stream`, SSE). Producer (the hub route's `mapEvent`) imports\n * THIS type — same producer/consumer SSOT pattern as\n * `ConversationEngagementWire = TicketEngagement`. A schema drift here\n * would be a silent serialization bug; keep one named contract.\n *\n * Frames carry METADATA ONLY — never message bodies. Content is always\n * refetched through the ACL'd `list-engagements` path (one read path).\n *\n * Event names (`eventType`):\n *   - `ticket-message` — a message row landed in the mirror for one of\n *     the viewer's tickets. `data` carries the metadata.\n *   - `ticket-status`  — a MEANINGFUL ticket-row change (status /\n *     pipeline stage / closure) landed in the mirror. Server-side\n *     old-vs-new diffing drops no-op writes (sync stamps). Clients\n *     invalidate + refetch the summary (closures count as unread\n *     server-side via `closed_at` vs the read receipt).\n *   - `ticket-summary` — the server-computed `TicketUnreadSummary`.\n *     THE stream is the summary webservice: pushed on connect (server-\n *     side resync-on-reconnect) and re-pushed (debounced) after every\n *     delivered event. Clients hold NO summary fetch path.\n *   - `ticket-resync`  — degraded frame: something happened that the\n *     server couldn't fully describe (truncated Realtime payload, or\n *     the connection's ownership set just gained tickets). A fresh\n *     `ticket-summary` follows it; clients invalidate queries.\n *\n * The server also emits `status` frames (`subscribed` / `retrying` /\n * `reconnect_failed`) from the shared SSE utility — clients derive\n * `connected` from those, NOT from the HTTP stream being open.\n */\nexport type TicketStreamEventType = 'ticket-message' | 'ticket-status' | 'ticket-summary' | 'ticket-resync';\n\n/** Metadata payload of a `ticket-message` frame. */\nexport type TicketMessageStreamData = {\n  ticket_external_id: string;\n  /** Mirror row's HubSpot message id. */\n  message_id: string;\n  /** Server-stamped via the same direction predicate the wire mapper\n   *  uses. Clients never re-derive author classification. */\n  authorRole: 'customer' | 'support';\n  /** Server-stamped via the visibility+direction unread predicate\n   *  (`countsAsUnread` in the hub's conversations DAL). ONLY frames\n   *  with `true` may bump client-side unread state. */\n  countsAsUnread: boolean;\n  hubspot_created_at: string | null;\n};\n\n/** Metadata payload of a `ticket-status` frame. */\nexport type TicketStatusStreamData = {\n  ticket_external_id: string;\n  /** Canonical OPEN | CLOSED after the change. */\n  status: string | null;\n  pipeline_stage_label: string | null;\n  closed_at: string | null;\n};\n\nexport interface TicketStreamEvent {\n  eventType: TicketStreamEventType;\n  /** Human-readable label (SSE envelope compat) — clients ignore it. */\n  message: string;\n  /** `ticket-message` → TicketMessageStreamData; `ticket-status` →\n   *  TicketStatusStreamData; `ticket-summary` → TicketUnreadSummary;\n   *  absent on `ticket-resync`. */\n  data?: TicketMessageStreamData | TicketStatusStreamData | TicketUnreadSummary;\n}\n\n/**\n * The SINGLE unread source, delivered EXCLUSIVELY over the stream\n * (`ticket-summary` frames on connect + after events) and in\n * `ticket-read` responses. There is no summary fetch endpoint. Header\n * badge total AND per-row dots both read this via the\n * `TicketLiveProvider` map; missing keys mean 0.\n */\nexport type TicketUnreadSummary = {\n  totalUnread: number;\n  tickets: Record<string, number>;\n  /** ISO timestamp of the NEWEST unread message per ticket (same keys as\n   *  `tickets`). Drives \"route to the most recent update\" on the header\n   *  cell: the ticket with the max value here is `nextUnreadTicketId`. */\n  latestUnreadAt: Record<string, string>;\n};\n\n/**\n * Trailing debounce for `markRead` while the drawer is open and new\n * messages stream in — flushed on drawer close so a fast open/close\n * still persists the receipt. Named beside its sibling cadences so\n * every timing knob lives in one file.\n */\nexport const TICKET_MARK_READ_DEBOUNCE_MS = 2000;\n\n/**\n * Ticket deep-link SSOT — DEFINED in the server-safe param-keys module\n * (`utils/dev-sections/dev-section-param-keys.ts`, beside its sibling\n * `devSectionAnchorId`) because producers span both worlds: the hub's\n * SERVER-side chat-ref builder and the client header cell. Re-exported\n * here so ticket-surface consumers keep one import home.\n */\nexport { TICKET_OPEN_PARAM, buildTicketOpenHref } from '../../utils/dev-sections/dev-section-param-keys';\n\n/**\n * Centralized toast copy. Keep all wording here so QA / localization\n * can find every user-visible string in one file.\n */\nexport const TOAST_COPY = {\n  open_success: { title: 'Ticket opened', description: 'We received your message and will follow up shortly.' },\n  open_mirror_pending: { title: 'Ticket opened', description: 'Syncing — your ticket will appear momentarily.' },\n  close_success: { title: 'Ticket closed' },\n  reopen_success: { title: 'Ticket reopened' },\n  comment_success: { title: 'Comment added' },\n  attach_success: { title: 'Files attached' },\n  // Failure variants are constructed dynamically from MappedTicketActionError.\n} as const;\n"]}