@stigmer/react 3.7.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/attachment/AttachmentChipList.d.ts.map +1 -1
  2. package/attachment/AttachmentChipList.js +30 -1
  3. package/attachment/AttachmentChipList.js.map +1 -1
  4. package/attachment/attachment-utils.d.ts +11 -0
  5. package/attachment/attachment-utils.d.ts.map +1 -1
  6. package/attachment/attachment-utils.js +23 -0
  7. package/attachment/attachment-utils.js.map +1 -1
  8. package/attachment/clipboard.d.ts +51 -0
  9. package/attachment/clipboard.d.ts.map +1 -0
  10. package/attachment/clipboard.js +89 -0
  11. package/attachment/clipboard.js.map +1 -0
  12. package/attachment/index.d.ts +6 -1
  13. package/attachment/index.d.ts.map +1 -1
  14. package/attachment/index.js +4 -1
  15. package/attachment/index.js.map +1 -1
  16. package/attachment/prepare-image.d.ts +43 -0
  17. package/attachment/prepare-image.d.ts.map +1 -0
  18. package/attachment/prepare-image.js +162 -0
  19. package/attachment/prepare-image.js.map +1 -0
  20. package/attachment/useAttachments.d.ts.map +1 -1
  21. package/attachment/useAttachments.js +23 -3
  22. package/attachment/useAttachments.js.map +1 -1
  23. package/attachment/vision-fit.d.ts +50 -0
  24. package/attachment/vision-fit.d.ts.map +1 -0
  25. package/attachment/vision-fit.js +76 -0
  26. package/attachment/vision-fit.js.map +1 -0
  27. package/channel/AgentChannelsPanel.d.ts.map +1 -1
  28. package/channel/AgentChannelsPanel.js +21 -2
  29. package/channel/AgentChannelsPanel.js.map +1 -1
  30. package/channel/ChannelConversationsDialog.d.ts +12 -4
  31. package/channel/ChannelConversationsDialog.d.ts.map +1 -1
  32. package/channel/ChannelConversationsDialog.js +18 -24
  33. package/channel/ChannelConversationsDialog.js.map +1 -1
  34. package/channel/providers.d.ts +23 -0
  35. package/channel/providers.d.ts.map +1 -1
  36. package/channel/providers.js +4 -0
  37. package/channel/providers.js.map +1 -1
  38. package/channel/useSaveAgentChannel.d.ts +5 -4
  39. package/channel/useSaveAgentChannel.d.ts.map +1 -1
  40. package/channel/useSaveAgentChannel.js +18 -4
  41. package/channel/useSaveAgentChannel.js.map +1 -1
  42. package/composer/ComposerToolbar.d.ts +1 -1
  43. package/composer/ComposerToolbar.d.ts.map +1 -1
  44. package/composer/SessionComposer.d.ts +4 -2
  45. package/composer/SessionComposer.d.ts.map +1 -1
  46. package/composer/SessionComposer.js +51 -5
  47. package/composer/SessionComposer.js.map +1 -1
  48. package/conversation/ConversationAttentionBanner.d.ts +22 -0
  49. package/conversation/ConversationAttentionBanner.d.ts.map +1 -0
  50. package/conversation/ConversationAttentionBanner.js +25 -0
  51. package/conversation/ConversationAttentionBanner.js.map +1 -0
  52. package/conversation/ConversationComposer.d.ts +41 -0
  53. package/conversation/ConversationComposer.d.ts.map +1 -0
  54. package/conversation/ConversationComposer.js +57 -0
  55. package/conversation/ConversationComposer.js.map +1 -0
  56. package/conversation/ConversationControlBanner.d.ts +52 -0
  57. package/conversation/ConversationControlBanner.d.ts.map +1 -0
  58. package/conversation/ConversationControlBanner.js +59 -0
  59. package/conversation/ConversationControlBanner.js.map +1 -0
  60. package/conversation/ConversationListPane.d.ts +68 -0
  61. package/conversation/ConversationListPane.d.ts.map +1 -0
  62. package/conversation/ConversationListPane.js +120 -0
  63. package/conversation/ConversationListPane.js.map +1 -0
  64. package/conversation/ConversationTimelineView.d.ts +46 -0
  65. package/conversation/ConversationTimelineView.d.ts.map +1 -0
  66. package/conversation/ConversationTimelineView.js +166 -0
  67. package/conversation/ConversationTimelineView.js.map +1 -0
  68. package/conversation/ConversationsWorkbench.d.ts +63 -0
  69. package/conversation/ConversationsWorkbench.d.ts.map +1 -0
  70. package/conversation/ConversationsWorkbench.js +165 -0
  71. package/conversation/ConversationsWorkbench.js.map +1 -0
  72. package/conversation/conversationPresentation.d.ts +137 -0
  73. package/conversation/conversationPresentation.d.ts.map +1 -0
  74. package/conversation/conversationPresentation.js +230 -0
  75. package/conversation/conversationPresentation.js.map +1 -0
  76. package/conversation/index.d.ts +26 -0
  77. package/conversation/index.d.ts.map +1 -0
  78. package/conversation/index.js +14 -0
  79. package/conversation/index.js.map +1 -0
  80. package/conversation/polling.d.ts +39 -0
  81. package/conversation/polling.d.ts.map +1 -0
  82. package/conversation/polling.js +39 -0
  83. package/conversation/polling.js.map +1 -0
  84. package/conversation/useConversation.d.ts +61 -0
  85. package/conversation/useConversation.d.ts.map +1 -0
  86. package/conversation/useConversation.js +153 -0
  87. package/conversation/useConversation.js.map +1 -0
  88. package/conversation/useConversationList.d.ts +98 -0
  89. package/conversation/useConversationList.d.ts.map +1 -0
  90. package/conversation/useConversationList.js +200 -0
  91. package/conversation/useConversationList.js.map +1 -0
  92. package/conversation/useConversationParticipation.d.ts +63 -0
  93. package/conversation/useConversationParticipation.d.ts.map +1 -0
  94. package/conversation/useConversationParticipation.js +84 -0
  95. package/conversation/useConversationParticipation.js.map +1 -0
  96. package/conversation/useConversationTimeline.d.ts +71 -0
  97. package/conversation/useConversationTimeline.d.ts.map +1 -0
  98. package/conversation/useConversationTimeline.js +152 -0
  99. package/conversation/useConversationTimeline.js.map +1 -0
  100. package/conversation/useConversationsWantsHumanCount.d.ts +42 -0
  101. package/conversation/useConversationsWantsHumanCount.d.ts.map +1 -0
  102. package/conversation/useConversationsWantsHumanCount.js +40 -0
  103. package/conversation/useConversationsWantsHumanCount.js.map +1 -0
  104. package/index.d.ts +6 -2
  105. package/index.d.ts.map +1 -1
  106. package/index.js +11 -2
  107. package/index.js.map +1 -1
  108. package/internal/tooltip.d.ts.map +1 -1
  109. package/internal/tooltip.js +7 -1
  110. package/internal/tooltip.js.map +1 -1
  111. package/internal/useAutoScroll.d.ts +25 -4
  112. package/internal/useAutoScroll.d.ts.map +1 -1
  113. package/internal/useAutoScroll.js +55 -15
  114. package/internal/useAutoScroll.js.map +1 -1
  115. package/internal/useFetch.d.ts.map +1 -1
  116. package/internal/useFetch.js +14 -4
  117. package/internal/useFetch.js.map +1 -1
  118. package/models/ModelSelector.d.ts +24 -5
  119. package/models/ModelSelector.d.ts.map +1 -1
  120. package/models/ModelSelector.js +31 -9
  121. package/models/ModelSelector.js.map +1 -1
  122. package/models/registry.d.ts +3 -2
  123. package/models/registry.d.ts.map +1 -1
  124. package/models/registry.js.map +1 -1
  125. package/models/service-tier.d.ts +1 -1
  126. package/models/service-tier.d.ts.map +1 -1
  127. package/models/service-tier.js +1 -1
  128. package/models/service-tier.js.map +1 -1
  129. package/package.json +4 -4
  130. package/schedule/ScheduleDetailView.js +1 -1
  131. package/schedule/ScheduleDetailView.js.map +1 -1
  132. package/schedule/ScheduleForm.d.ts.map +1 -1
  133. package/schedule/ScheduleForm.js +7 -6
  134. package/schedule/ScheduleForm.js.map +1 -1
  135. package/sharing/useSaveAgentShare.d.ts +10 -1
  136. package/sharing/useSaveAgentShare.d.ts.map +1 -1
  137. package/sharing/useSaveAgentShare.js +9 -0
  138. package/sharing/useSaveAgentShare.js.map +1 -1
  139. package/sidebar/WorkspaceSidebar.d.ts +12 -4
  140. package/sidebar/WorkspaceSidebar.d.ts.map +1 -1
  141. package/sidebar/WorkspaceSidebar.js +19 -7
  142. package/sidebar/WorkspaceSidebar.js.map +1 -1
  143. package/src/attachment/AttachmentChipList.tsx +45 -1
  144. package/src/attachment/__tests__/AttachmentChipList.test.tsx +103 -0
  145. package/src/attachment/__tests__/attachment-utils.test.ts +54 -0
  146. package/src/attachment/__tests__/clipboard.test.ts +110 -0
  147. package/src/attachment/__tests__/prepare-image.browser.test.ts +166 -0
  148. package/src/attachment/__tests__/prepare-image.test.ts +32 -0
  149. package/src/attachment/__tests__/vision-fit.test.ts +93 -0
  150. package/src/attachment/attachment-utils.ts +27 -0
  151. package/src/attachment/clipboard.ts +102 -0
  152. package/src/attachment/index.ts +13 -0
  153. package/src/attachment/prepare-image.ts +179 -0
  154. package/src/attachment/useAttachments.ts +26 -2
  155. package/src/attachment/vision-fit.ts +90 -0
  156. package/src/channel/AgentChannelsPanel.tsx +33 -2
  157. package/src/channel/ChannelConversationsDialog.tsx +22 -25
  158. package/src/channel/__tests__/AgentChannelsPanel.test.tsx +36 -5
  159. package/src/channel/__tests__/ChannelConversationsDialog.test.tsx +38 -1
  160. package/src/channel/__tests__/useSaveAgentChannel.test.tsx +26 -0
  161. package/src/channel/providers.ts +27 -0
  162. package/src/channel/useSaveAgentChannel.ts +18 -4
  163. package/src/composer/ComposerToolbar.tsx +1 -1
  164. package/src/composer/SessionComposer.tsx +75 -6
  165. package/src/composer/__tests__/SessionComposer-paste.test.tsx +263 -0
  166. package/src/composer/__tests__/SessionComposer-serviceTier.test.tsx +4 -3
  167. package/src/composer/__tests__/SessionComposer-uploadGate.test.tsx +239 -0
  168. package/src/conversation/ConversationAttentionBanner.tsx +77 -0
  169. package/src/conversation/ConversationComposer.tsx +156 -0
  170. package/src/conversation/ConversationControlBanner.tsx +193 -0
  171. package/src/conversation/ConversationListPane.tsx +459 -0
  172. package/src/conversation/ConversationTimelineView.tsx +435 -0
  173. package/src/conversation/ConversationsWorkbench.tsx +378 -0
  174. package/src/conversation/__tests__/ConversationAttentionBanner.test.tsx +82 -0
  175. package/src/conversation/__tests__/ConversationComposer.test.tsx +160 -0
  176. package/src/conversation/__tests__/ConversationControlBanner.test.tsx +190 -0
  177. package/src/conversation/__tests__/ConversationListPane.test.tsx +372 -0
  178. package/src/conversation/__tests__/ConversationTimelineView.layout.test.tsx +160 -0
  179. package/src/conversation/__tests__/ConversationTimelineView.test.tsx +405 -0
  180. package/src/conversation/__tests__/ConversationsWorkbench.test.tsx +860 -0
  181. package/src/conversation/__tests__/a11y/conversation-surfaces.a11y.test.tsx +235 -0
  182. package/src/conversation/__tests__/a11y/harness.tsx +100 -0
  183. package/src/conversation/__tests__/conversation-tooltips.layout.test.tsx +167 -0
  184. package/src/conversation/__tests__/conversationPollingComposition.test.tsx +138 -0
  185. package/src/conversation/__tests__/conversationPresentation.test.ts +289 -0
  186. package/src/conversation/__tests__/useConversation.test.tsx +243 -0
  187. package/src/conversation/__tests__/useConversationList.test.tsx +492 -0
  188. package/src/conversation/__tests__/useConversationParticipation.test.tsx +190 -0
  189. package/src/conversation/__tests__/useConversationTimeline.test.tsx +258 -0
  190. package/src/conversation/__tests__/useConversationsWantsHumanCount.test.tsx +102 -0
  191. package/src/conversation/conversationPresentation.ts +305 -0
  192. package/src/conversation/index.ts +80 -0
  193. package/src/conversation/polling.ts +41 -0
  194. package/src/conversation/useConversation.ts +221 -0
  195. package/src/conversation/useConversationList.ts +328 -0
  196. package/src/conversation/useConversationParticipation.ts +178 -0
  197. package/src/conversation/useConversationTimeline.ts +244 -0
  198. package/src/conversation/useConversationsWantsHumanCount.ts +86 -0
  199. package/src/index.ts +78 -1
  200. package/src/internal/__tests__/fake-timer-slices.ts +29 -0
  201. package/src/internal/__tests__/useAutoScroll.layout.test.tsx +143 -0
  202. package/src/internal/__tests__/useAutoScroll.test.tsx +62 -5
  203. package/src/internal/__tests__/useFetch-poll-under-render-pressure.test.tsx +79 -0
  204. package/src/internal/tooltip.tsx +6 -1
  205. package/src/internal/useAutoScroll.ts +66 -20
  206. package/src/internal/useFetch.ts +15 -4
  207. package/src/models/ModelSelector.tsx +81 -49
  208. package/src/models/__tests__/ModelSelector-serviceTier.test.tsx +105 -21
  209. package/src/models/registry.ts +3 -2
  210. package/src/models/service-tier.ts +1 -1
  211. package/src/schedule/ScheduleDetailView.tsx +1 -1
  212. package/src/schedule/ScheduleForm.tsx +7 -6
  213. package/src/schedule/__tests__/scheduleCreation.test.tsx +2 -2
  214. package/src/sharing/__tests__/useSaveAgentShare.test.tsx +55 -1
  215. package/src/sharing/__tests__/useShareToolReadiness.test.tsx +1 -0
  216. package/src/sharing/useSaveAgentShare.ts +19 -1
  217. package/src/sidebar/WorkspaceSidebar.tsx +62 -3
  218. package/src/sidebar/__tests__/WorkspaceSidebar.test.tsx +56 -1
  219. package/styles.css +1 -1
@@ -0,0 +1,165 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useMemo, useState } from "react";
4
+ import { MessagesSquare } from "lucide-react";
5
+ import { cn } from "@stigmer/theme";
6
+ import { ChannelConversationListFilter, } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/conversation_io_pb";
7
+ import { ChannelSendOutcome } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/message_io_pb";
8
+ import { channelProviderOf } from "../channel/providers.js";
9
+ import { useOrgAgentChannelList } from "../channel/useOrgAgentChannelList.js";
10
+ import { EmptyState } from "../empty-state/EmptyState.js";
11
+ import { ConversationAttentionBanner } from "./ConversationAttentionBanner.js";
12
+ import { ConversationComposer } from "./ConversationComposer.js";
13
+ import { ConversationControlBanner } from "./ConversationControlBanner.js";
14
+ import { ConversationListPane } from "./ConversationListPane.js";
15
+ import { ConversationTimelineView } from "./ConversationTimelineView.js";
16
+ import { conversationContactOf, conversationLabelOf, outboundItemIdOf, serviceWindowOf, } from "./conversationPresentation.js";
17
+ import { useConversation } from "./useConversation.js";
18
+ import { useConversationList } from "./useConversationList.js";
19
+ import { useConversationParticipation } from "./useConversationParticipation.js";
20
+ import { useConversationTimeline } from "./useConversationTimeline.js";
21
+ /**
22
+ * The Conversations console surface (channel-conversations T04), fully
23
+ * composed: the org-wide inbox on the left; the open conversation's
24
+ * timeline, participation banners, and staff composer on the right.
25
+ *
26
+ * Everything under it is independently importable — hosts that need a
27
+ * different layout compose `ConversationListPane`,
28
+ * `ConversationTimelineView`, the banners, and the hooks directly.
29
+ */
30
+ export function ConversationsWorkbench({ org, selected, onSelectionChange, currentIdentityAccountId, headerAccessory, channelHref, now, className, }) {
31
+ const [channelFilter, setChannelFilter] = useState("");
32
+ const [listFilter, setListFilter] = useState(ChannelConversationListFilter.channel_conversation_list_filter_unspecified);
33
+ const { channels } = useOrgAgentChannelList(org || null);
34
+ const list = useConversationList({
35
+ org: org || null,
36
+ agentChannelId: channelFilter,
37
+ filter: listFilter,
38
+ });
39
+ const detail = useConversation(selected?.agentChannelId ?? "", selected?.conversationKey ?? "");
40
+ const timeline = useConversationTimeline(selected?.agentChannelId ?? "", selected?.conversationKey ?? "");
41
+ // DD-012 D-a: a command answer is fresher than any in-flight poll on
42
+ // BOTH surfaces rendering this conversation — fan it out to the detail
43
+ // seam (banners, composer state) and the list seam (the inbox row), so
44
+ // your own takeover never lags the inbox (F-06).
45
+ const detailApply = detail.applyServerState;
46
+ const listApply = list.applyServerState;
47
+ const adoptConversation = useCallback((fresh) => {
48
+ detailApply(fresh);
49
+ listApply(fresh);
50
+ }, [detailApply, listApply]);
51
+ const participation = useConversationParticipation({
52
+ agentChannelId: selected?.agentChannelId ?? "",
53
+ conversationKey: selected?.conversationKey ?? "",
54
+ onConversation: adoptConversation,
55
+ });
56
+ const selectedChannel = useMemo(() => selected
57
+ ? channels.find((c) => c.metadata?.id === selected.agentChannelId) ?? null
58
+ : null, [channels, selected]);
59
+ const descriptor = channelProviderOf(selectedChannel?.spec?.providerConfig?.case);
60
+ // DD-007 D-e: the handback guard arms on the row's server-derived
61
+ // awaiting_reply fact — bounce-aware since T08 (a staff reply the
62
+ // provider later failed does NOT count as an answer), and platform
63
+ // acknowledgments never stamp it. Never re-derive this from timeline
64
+ // authorship: that derivation read a bounced staff reply as "answered"
65
+ // and went silent in exactly the scenario the guard exists for (F-28)
66
+ // — the client-side twin of the re-derivation DD-011 A-1 removed
67
+ // server-side. No loading gap: the Hand back button renders from this
68
+ // same row, so the guard and the control it protects appear together.
69
+ const unansweredCustomer = detail.conversation?.awaitingReply ?? false;
70
+ const composerDisabledReason = descriptor && !descriptor.supportsStaffReplies
71
+ ? `Staff replies aren't available on ${descriptor.label} channels yet — the agent keeps serving this conversation.`
72
+ : detail.awaitingCustomer
73
+ ? "The customer hasn't written yet — staff replies unlock with their first message."
74
+ : null;
75
+ // DD-014 D-b/D-e: the closed-window forecast, computed fresh on EVERY
76
+ // render — deliberately NOT memoized on [detail.conversation]: the
77
+ // row's reference is intentionally stable across identical polls
78
+ // (DD-010) while the window is wall-clock-anchored, so a memo keyed on
79
+ // the row would freeze this warning forever on a quiet conversation.
80
+ // The 5s detail poll's re-render is the refresh cadence; the workbench
81
+ // test "the 5s detail poll alone carries the advisory over the
82
+ // boundary" is the guard. The advisory names the rule (DD-014 D-c's
83
+ // one home for it); the failed tick relays the provider's verdict.
84
+ const serviceWindow = detail.conversation
85
+ ? serviceWindowOf(detail.conversation, descriptor?.id ?? null, now ?? new Date())
86
+ : null;
87
+ const composerAdvisory = serviceWindow === "closed" && descriptor
88
+ ? `${descriptor.label} closes free-form replies 24 hours after the customer's last message — a reply sent now will probably fail. The window reopens when the customer writes again.`
89
+ : null;
90
+ const handleSelect = useCallback((conversation) => onSelectionChange({
91
+ agentChannelId: conversation.agentChannelId,
92
+ conversationKey: conversation.conversationKey,
93
+ }), [onSelectionChange]);
94
+ // F-05: an accepted reply's ledger item is only visible once the next
95
+ // timeline read answers, so the composer's busy state must span
96
+ // click-to-visible, not end at the RPC. The settling record names the
97
+ // item the reply output promised (`ob:<outbound_message_id>`) and the
98
+ // selection it belongs to — carrying the selection makes stale state
99
+ // from a previously open conversation structurally inert (the derived
100
+ // id is null there) instead of needing a reset effect.
101
+ const [replySettling, setReplySettling] = useState(null);
102
+ const selectionKey = selected
103
+ ? `${selected.agentChannelId}:${selected.conversationKey}`
104
+ : "";
105
+ const settlingItemId = replySettling !== null && replySettling.selectionKey === selectionKey
106
+ ? replySettling.itemId
107
+ : null;
108
+ useEffect(() => {
109
+ if (settlingItemId === null)
110
+ return;
111
+ if (timeline.items.some((item) => item.itemId === settlingItemId)) {
112
+ setReplySettling(null);
113
+ }
114
+ }, [timeline.items, settlingItemId]);
115
+ const handleSend = useCallback(async (text) => {
116
+ const output = await participation.reply(text);
117
+ // The ledger row is committed before reply answers, so one head
118
+ // refresh renders the REAL item (with its true status) — the SDK
119
+ // never fabricates an optimistic item that might misstate what
120
+ // the provider did. Until that item lands, the composer stays
121
+ // busy; even if this refetch fails, the head poll delivers the
122
+ // item and clears the hold. A refusal never settles: it restores
123
+ // the draft for editing, and holding the composer busy would
124
+ // fight the correction its notice asks for.
125
+ if (output.outcome !== ChannelSendOutcome.refused &&
126
+ output.outboundMessageId !== "") {
127
+ setReplySettling({
128
+ selectionKey,
129
+ itemId: outboundItemIdOf(output.outboundMessageId),
130
+ });
131
+ }
132
+ timeline.refetch();
133
+ return output;
134
+ }, [participation.reply, timeline.refetch, selectionKey]);
135
+ const detailLabel = detail.conversation
136
+ ? conversationLabelOf(detail.conversation, descriptor?.id ?? null)
137
+ : selected?.conversationKey ?? "";
138
+ // The customer's reachable address (e.g. the WhatsApp number a display
139
+ // name hides) — the call-back path belongs beside the channel name.
140
+ const detailContact = detail.conversation
141
+ ? conversationContactOf(detail.conversation, descriptor?.id ?? null)
142
+ : null;
143
+ const channelName = selectedChannel
144
+ ? selectedChannel.metadata?.name || selectedChannel.metadata?.slug
145
+ : null;
146
+ const selectedChannelHref = selectedChannel && channelHref ? channelHref(selectedChannel) : null;
147
+ const headerAccessoryNode = typeof headerAccessory === "function"
148
+ ? headerAccessory({ channel: selectedChannel })
149
+ : headerAccessory;
150
+ return (_jsxs("div", { "aria-label": "Conversations workbench", className: cn("flex min-h-0 flex-1", className), children: [_jsx(ConversationListPane, { className: "w-80 shrink-0 border-r border-border", conversations: list.conversations, isLoading: list.isLoading, error: list.error, hasMore: list.hasMore, loadMore: list.loadMore, isLoadingMore: list.isLoadingMore, channels: channels, channelFilter: channelFilter, onChannelFilterChange: setChannelFilter, filter: listFilter, onFilterChange: setListFilter, selected: selected, onSelect: handleSelect, now: now }), selected === null ? (_jsx("div", { className: "flex min-w-0 flex-1 items-center justify-center p-6", children: _jsx(EmptyState, { variant: "first-use", icon: _jsx(MessagesSquare, { className: "size-8" }), title: "Select a conversation", description: "Pick a conversation to read its thread, reply as your business, or take over from the agent." }) })) : (
151
+ // Keyed by conversation identity (DD-014: `key` remounts are the
152
+ // clean-reset pattern): every open starts a fresh detail column, so
153
+ // auto-scroll opens following at the newest message and the
154
+ // composer's draft and notice can never travel from one customer's
155
+ // conversation into another's (F-22). The data hooks live above
156
+ // this column, so the remount refetches nothing; the inbox pane
157
+ // stays outside it so its scroll position and filter survive.
158
+ // Channel ids never contain ":", so the joined key is unambiguous.
159
+ _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-2.5", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h2", { className: "truncate text-sm font-semibold text-foreground", children: detailLabel }), (detailContact !== null || channelName) && (_jsxs("p", { className: "truncate text-xs text-muted-foreground", children: [detailContact, detailContact !== null && channelName && " · ", channelName &&
160
+ // The path from a conversation to its channel (F-11):
161
+ // the channel name links to the host's channel surface
162
+ // when a route exists.
163
+ (selectedChannelHref ? (_jsx("a", { href: selectedChannelHref, className: "hover:text-foreground hover:underline", children: channelName })) : (channelName))] }))] }), headerAccessoryNode] }), detail.conversation && (_jsx(ConversationAttentionBanner, { conversation: detail.conversation, participation: participation })), detail.conversation && (_jsx(ConversationControlBanner, { conversation: detail.conversation, participation: participation, unansweredCustomer: unansweredCustomer, supportsStaffReplies: descriptor?.supportsStaffReplies ?? true, currentIdentityAccountId: currentIdentityAccountId })), _jsx(ConversationTimelineView, { items: timeline.items, isLoading: timeline.isLoading, error: timeline.error, hasOlder: timeline.hasOlder, loadOlder: timeline.loadOlder, isLoadingOlder: timeline.isLoadingOlder, provider: descriptor?.id ?? null, now: now }), _jsx(ConversationComposer, { onSend: handleSend, isSending: participation.pendingCommands.has("reply") || settlingItemId !== null, disabledReason: composerDisabledReason, advisory: composerAdvisory })] }, `${selected.agentChannelId}:${selected.conversationKey}`))] }));
164
+ }
165
+ //# sourceMappingURL=ConversationsWorkbench.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConversationsWorkbench.js","sourceRoot":"","sources":["../../src/conversation/ConversationsWorkbench.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ import { type ChannelConversation, type ConversationTimelineItem } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/conversation_io_pb";
2
+ import type { ChannelProviderId } from "../channel/providers.js";
3
+ /**
4
+ * Pure render vocabulary for the conversation surface — the single place
5
+ * the wire enums become words and layout facts (the providerPresentation
6
+ * convention). No React, no I/O; every component and every test reads the
7
+ * same mapping.
8
+ */
9
+ /** Who authored a timeline item, in render vocabulary. */
10
+ export type ConversationAuthorKind = "customer" | "agent" | "teammate" | "platform" | "unknown";
11
+ /** Map the wire author onto the render vocabulary. */
12
+ export declare function authorKindOf(item: ConversationTimelineItem): ConversationAuthorKind;
13
+ /** True for org-internal items (escalations, attention clears, notes). */
14
+ export declare function isInternalItem(item: ConversationTimelineItem): boolean;
15
+ /**
16
+ * The platform's own send attempt, in render vocabulary. `null` on items
17
+ * that carry no attempt (inbound messages, internal events).
18
+ *
19
+ * `"suppressed"` and `"failed"` are items the customer NEVER saw and
20
+ * `"pending"`/`"delivering"` are items the customer has not seen YET —
21
+ * a renderer that shows any of them as a plain sent bubble lies to staff.
22
+ */
23
+ export type SendAttemptKind = "pending" | "delivering" | "delivered" | "failed" | "suppressed";
24
+ /** Map the wire delivery status onto the render vocabulary. */
25
+ export declare function sendAttemptOf(item: ConversationTimelineItem): SendAttemptKind | null;
26
+ /**
27
+ * The provider's receipt report, in render vocabulary. `null` until the
28
+ * provider reports (and always on items with no receipt axis).
29
+ *
30
+ * A SECOND axis beside {@link sendAttemptOf}, never collapsed into it
31
+ * (channel-conversations DD-004 D-d): "we handed it to WhatsApp" and
32
+ * "it reached the phone" are different facts — an item can be
33
+ * `delivered` + `receipt_failed`.
34
+ */
35
+ export type ReceiptKind = "sent" | "delivered" | "read" | "failed";
36
+ /** Map the wire receipt state onto the render vocabulary. */
37
+ export declare function receiptOf(item: ConversationTimelineItem): ReceiptKind | null;
38
+ /**
39
+ * Placeholder copy for inbound items whose body the platform cannot
40
+ * render (`text` empty, `provider_message_type` names the kind). Media
41
+ * retrieval is a named non-goal of this surface — the placeholder tells
42
+ * staff something arrived and what shape it was.
43
+ *
44
+ * Returns `null` when the item needs no placeholder (it has text, or it
45
+ * is not a provider-typed inbound item — e.g. an attention-clear event,
46
+ * whose copy is the renderer's own vocabulary).
47
+ */
48
+ export declare function inboundPlaceholderOf(item: ConversationTimelineItem): string | null;
49
+ /**
50
+ * The list-row label for a conversation: the provider-reported display
51
+ * name when one exists, else a provider-aware fallback.
52
+ *
53
+ * The fallback is provider-aware because conversation keys are not
54
+ * uniformly meaningful: WhatsApp's key is the customer's phone number
55
+ * (a real label), while Slack's key is a thread timestamp like
56
+ * `1723012345.678900` (noise a human cannot use).
57
+ */
58
+ export declare function conversationLabelOf(conversation: ChannelConversation, provider: ChannelProviderId | null): string;
59
+ /**
60
+ * The customer's reachable address, when the label hides it: the fact a
61
+ * staffer needs to call or message the customer outside the platform.
62
+ *
63
+ * Provider-aware like {@link conversationLabelOf}, because conversation
64
+ * keys are not uniformly addresses: WhatsApp's key IS the customer's
65
+ * phone number (the wa_id), and when a display name wins the label slot
66
+ * that number would otherwise appear nowhere on the surface. Slack's
67
+ * key is a thread timestamp — not a way to reach anyone — so there is
68
+ * nothing to show. Unknown providers show nothing rather than guess.
69
+ *
70
+ * Returns `null` when the label already shows the key (no display
71
+ * name), so the same fact never renders twice. The key returns
72
+ * verbatim — no fabricated `+` prefix: the wire value is the truth this
73
+ * module reports, and formatting belongs to hosts that know their
74
+ * display rules.
75
+ */
76
+ export declare function conversationContactOf(conversation: ChannelConversation, provider: ChannelProviderId | null): string | null;
77
+ /** The service-window estimate, in render vocabulary. */
78
+ export type ServiceWindowState = "open" | "closed";
79
+ /**
80
+ * Estimate whether the provider's free-form reply window is open
81
+ * (channel-conversations DD-014 D-a). Provider-aware like
82
+ * {@link conversationLabelOf}: WhatsApp is the only windowed provider, so
83
+ * every other provider answers `null` — no window, no claim — as does a
84
+ * conversation whose customer has never written
85
+ * (`last_customer_message_at` unset; that conversation takes the
86
+ * composer's disabled branch instead).
87
+ *
88
+ * Client-derived on purpose, and NOT a divergence from DD-011 A-1's
89
+ * "the predicate is expressed once, in SQL" rule: the window is
90
+ * wall-clock-anchored — it flips with NO row change — so a
91
+ * server-computed boolean would be stale by construction between polls.
92
+ * Time-decaying presentation derived from wire instants is this
93
+ * surface's established class (relative timestamps, day grouping).
94
+ *
95
+ * Honesty caveat (DD-014 D-a): the anchor is the platform's receipt
96
+ * instant, minutes AFTER Meta's own clock started the window, so the
97
+ * estimate can claim "open" briefly after Meta closed it — which is why
98
+ * consumers warn and never block (D-b). For the same reason the exact
99
+ * 24-hour tie reads "closed": erring a moment early offsets an anchor
100
+ * that always errs late.
101
+ */
102
+ export declare function serviceWindowOf(conversation: ChannelConversation, provider: ChannelProviderId | null, now: Date): ServiceWindowState | null;
103
+ /**
104
+ * How strongly the awaiting-reply indicator renders. Two strengths, not
105
+ * one, because "waiting" means different things per holder (DD-011 D-a).
106
+ */
107
+ export type AwaitingIndicator = "strong" | "muted";
108
+ /**
109
+ * The awaiting-reply indicator for a conversation (channel-conversations
110
+ * DD-011 D-a): `"strong"` when a human holds it — the agent will not
111
+ * answer, so a person must (F-13's exact case) — and `"muted"` when the
112
+ * agent holds it: the agent is about to answer, and if its turn dies the
113
+ * conversation stays visibly waiting, which is the built-in recovery
114
+ * path (DD-011 D-b). `null` when the customer's last message has been
115
+ * answered (or the customer has not written).
116
+ *
117
+ * The fact itself is server-derived (`awaiting_reply` rides the wire as
118
+ * a boolean precisely so no client re-implements the NULL-and-compare
119
+ * rule); this helper only maps fact × holder onto render strength.
120
+ */
121
+ export declare function awaitingIndicatorOf(conversation: ChannelConversation): AwaitingIndicator | null;
122
+ /**
123
+ * The timeline item id an accepted staff reply appears under: the
124
+ * outbound ledger's `ob:` namespace over the reply output's
125
+ * `outbound_message_id` — the contract
126
+ * `useConversationParticipation.reply` documents ("the item will
127
+ * appear on the timeline as `ob:<that id>`"). The workbench's
128
+ * post-send busy state watches for exactly this id (F-05).
129
+ */
130
+ export declare function outboundItemIdOf(outboundMessageId: string): string;
131
+ /**
132
+ * Newest-first timeline order: `(at, item_id)` descending — the exact
133
+ * comparator the server pages with, so client-side merges can never
134
+ * disagree with server pages about order.
135
+ */
136
+ export declare function compareTimelineItemsNewestFirst(a: ConversationTimelineItem, b: ConversationTimelineItem): number;
137
+ //# sourceMappingURL=conversationPresentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversationPresentation.d.ts","sourceRoot":"","sources":["../../src/conversation/conversationPresentation.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,mBAAmB,EAIxB,KAAK,wBAAwB,EAC9B,MAAM,uEAAuE,CAAC;AAG/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE;;;;;GAKG;AAEH,0DAA0D;AAC1D,MAAM,MAAM,sBAAsB,GAC9B,UAAU,GACV,OAAO,GACP,UAAU,GACV,UAAU,GACV,SAAS,CAAC;AAEd,sDAAsD;AACtD,wBAAgB,YAAY,CAAC,IAAI,EAAE,wBAAwB,GAAG,sBAAsB,CAanF;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,IAAI,EAAE,wBAAwB,GAAG,OAAO,CAEtE;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,YAAY,CAAC;AAEjB,+DAA+D;AAC/D,wBAAgB,aAAa,CAAC,IAAI,EAAE,wBAAwB,GAAG,eAAe,GAAG,IAAI,CAepF;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnE,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,IAAI,EAAE,wBAAwB,GAAG,WAAW,GAAG,IAAI,CAa5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM,GAAG,IAAI,CAwBlF;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,mBAAmB,EACjC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,GACjC,MAAM,CAIR;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,mBAAmB,EACjC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,GACjC,MAAM,GAAG,IAAI,CAIf;AAYD,yDAAyD;AACzD,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,mBAAmB,EACjC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,EAClC,GAAG,EAAE,IAAI,GACR,kBAAkB,GAAG,IAAI,CAM3B;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEnD;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,mBAAmB,GAChC,iBAAiB,GAAG,IAAI,CAK1B;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,CAAC,EAAE,wBAAwB,EAC3B,CAAC,EAAE,wBAAwB,GAC1B,MAAM,CAIR"}
@@ -0,0 +1,230 @@
1
+ import { timestampDate } from "@bufbuild/protobuf/wkt";
2
+ import { ConversationControl, ConversationItemAuthor, ConversationLane, } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/conversation_io_pb";
3
+ import { ChannelDeliveryStatus } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/delivery_pb";
4
+ import { ChannelReceiptState } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/outbound_pb";
5
+ /** Map the wire author onto the render vocabulary. */
6
+ export function authorKindOf(item) {
7
+ switch (item.author) {
8
+ case ConversationItemAuthor.author_customer:
9
+ return "customer";
10
+ case ConversationItemAuthor.author_agent:
11
+ return "agent";
12
+ case ConversationItemAuthor.author_teammate:
13
+ return "teammate";
14
+ case ConversationItemAuthor.author_platform:
15
+ return "platform";
16
+ default:
17
+ return "unknown";
18
+ }
19
+ }
20
+ /** True for org-internal items (escalations, attention clears, notes). */
21
+ export function isInternalItem(item) {
22
+ return item.lane === ConversationLane.lane_internal;
23
+ }
24
+ /** Map the wire delivery status onto the render vocabulary. */
25
+ export function sendAttemptOf(item) {
26
+ switch (item.deliveryStatus) {
27
+ case ChannelDeliveryStatus.pending:
28
+ return "pending";
29
+ case ChannelDeliveryStatus.delivering:
30
+ return "delivering";
31
+ case ChannelDeliveryStatus.delivered:
32
+ return "delivered";
33
+ case ChannelDeliveryStatus.failed:
34
+ return "failed";
35
+ case ChannelDeliveryStatus.suppressed:
36
+ return "suppressed";
37
+ default:
38
+ return null;
39
+ }
40
+ }
41
+ /** Map the wire receipt state onto the render vocabulary. */
42
+ export function receiptOf(item) {
43
+ switch (item.receiptState) {
44
+ case ChannelReceiptState.receipt_sent:
45
+ return "sent";
46
+ case ChannelReceiptState.receipt_delivered:
47
+ return "delivered";
48
+ case ChannelReceiptState.receipt_read:
49
+ return "read";
50
+ case ChannelReceiptState.receipt_failed:
51
+ return "failed";
52
+ default:
53
+ return null;
54
+ }
55
+ }
56
+ /**
57
+ * Placeholder copy for inbound items whose body the platform cannot
58
+ * render (`text` empty, `provider_message_type` names the kind). Media
59
+ * retrieval is a named non-goal of this surface — the placeholder tells
60
+ * staff something arrived and what shape it was.
61
+ *
62
+ * Returns `null` when the item needs no placeholder (it has text, or it
63
+ * is not a provider-typed inbound item — e.g. an attention-clear event,
64
+ * whose copy is the renderer's own vocabulary).
65
+ */
66
+ export function inboundPlaceholderOf(item) {
67
+ if (item.text !== "" || item.providerMessageType === "")
68
+ return null;
69
+ switch (item.providerMessageType) {
70
+ case "text":
71
+ return null;
72
+ case "image":
73
+ return "Photo";
74
+ case "video":
75
+ return "Video";
76
+ case "audio":
77
+ return "Voice message";
78
+ case "document":
79
+ return "Document";
80
+ case "sticker":
81
+ return "Sticker";
82
+ case "location":
83
+ return "Location";
84
+ case "contacts":
85
+ return "Contact card";
86
+ default:
87
+ // The provider's vocabulary passes through verbatim (never
88
+ // re-encoded), so an unknown kind still names itself.
89
+ return `Unsupported message (${item.providerMessageType})`;
90
+ }
91
+ }
92
+ /**
93
+ * The list-row label for a conversation: the provider-reported display
94
+ * name when one exists, else a provider-aware fallback.
95
+ *
96
+ * The fallback is provider-aware because conversation keys are not
97
+ * uniformly meaningful: WhatsApp's key is the customer's phone number
98
+ * (a real label), while Slack's key is a thread timestamp like
99
+ * `1723012345.678900` (noise a human cannot use).
100
+ */
101
+ export function conversationLabelOf(conversation, provider) {
102
+ if (conversation.displayName !== "")
103
+ return conversation.displayName;
104
+ if (provider === "slack")
105
+ return "Slack thread";
106
+ return conversation.conversationKey;
107
+ }
108
+ /**
109
+ * The customer's reachable address, when the label hides it: the fact a
110
+ * staffer needs to call or message the customer outside the platform.
111
+ *
112
+ * Provider-aware like {@link conversationLabelOf}, because conversation
113
+ * keys are not uniformly addresses: WhatsApp's key IS the customer's
114
+ * phone number (the wa_id), and when a display name wins the label slot
115
+ * that number would otherwise appear nowhere on the surface. Slack's
116
+ * key is a thread timestamp — not a way to reach anyone — so there is
117
+ * nothing to show. Unknown providers show nothing rather than guess.
118
+ *
119
+ * Returns `null` when the label already shows the key (no display
120
+ * name), so the same fact never renders twice. The key returns
121
+ * verbatim — no fabricated `+` prefix: the wire value is the truth this
122
+ * module reports, and formatting belongs to hosts that know their
123
+ * display rules.
124
+ */
125
+ export function conversationContactOf(conversation, provider) {
126
+ if (provider !== "whatsapp")
127
+ return null;
128
+ if (conversation.displayName === "")
129
+ return null;
130
+ return conversation.conversationKey;
131
+ }
132
+ /**
133
+ * Meta's customer-service window: a business can send free-form WhatsApp
134
+ * messages for 24 hours after the customer's last inbound message; after
135
+ * that, only template messages deliver. The constant lives beside its
136
+ * only consumer and is promoted to `ChannelProviderDescriptor` only if a
137
+ * second windowed provider ever appears (the second-sighting rule,
138
+ * DD-014 D-a).
139
+ */
140
+ const WHATSAPP_SERVICE_WINDOW_MS = 24 * 60 * 60 * 1000;
141
+ /**
142
+ * Estimate whether the provider's free-form reply window is open
143
+ * (channel-conversations DD-014 D-a). Provider-aware like
144
+ * {@link conversationLabelOf}: WhatsApp is the only windowed provider, so
145
+ * every other provider answers `null` — no window, no claim — as does a
146
+ * conversation whose customer has never written
147
+ * (`last_customer_message_at` unset; that conversation takes the
148
+ * composer's disabled branch instead).
149
+ *
150
+ * Client-derived on purpose, and NOT a divergence from DD-011 A-1's
151
+ * "the predicate is expressed once, in SQL" rule: the window is
152
+ * wall-clock-anchored — it flips with NO row change — so a
153
+ * server-computed boolean would be stale by construction between polls.
154
+ * Time-decaying presentation derived from wire instants is this
155
+ * surface's established class (relative timestamps, day grouping).
156
+ *
157
+ * Honesty caveat (DD-014 D-a): the anchor is the platform's receipt
158
+ * instant, minutes AFTER Meta's own clock started the window, so the
159
+ * estimate can claim "open" briefly after Meta closed it — which is why
160
+ * consumers warn and never block (D-b). For the same reason the exact
161
+ * 24-hour tie reads "closed": erring a moment early offsets an anchor
162
+ * that always errs late.
163
+ */
164
+ export function serviceWindowOf(conversation, provider, now) {
165
+ if (provider !== "whatsapp")
166
+ return null;
167
+ const anchor = conversation.lastCustomerMessageAt;
168
+ if (anchor === undefined)
169
+ return null;
170
+ const elapsedMs = now.getTime() - timestampDate(anchor).getTime();
171
+ return elapsedMs >= WHATSAPP_SERVICE_WINDOW_MS ? "closed" : "open";
172
+ }
173
+ /**
174
+ * The awaiting-reply indicator for a conversation (channel-conversations
175
+ * DD-011 D-a): `"strong"` when a human holds it — the agent will not
176
+ * answer, so a person must (F-13's exact case) — and `"muted"` when the
177
+ * agent holds it: the agent is about to answer, and if its turn dies the
178
+ * conversation stays visibly waiting, which is the built-in recovery
179
+ * path (DD-011 D-b). `null` when the customer's last message has been
180
+ * answered (or the customer has not written).
181
+ *
182
+ * The fact itself is server-derived (`awaiting_reply` rides the wire as
183
+ * a boolean precisely so no client re-implements the NULL-and-compare
184
+ * rule); this helper only maps fact × holder onto render strength.
185
+ */
186
+ export function awaitingIndicatorOf(conversation) {
187
+ if (!conversation.awaitingReply)
188
+ return null;
189
+ return conversation.control === ConversationControl.control_human
190
+ ? "strong"
191
+ : "muted";
192
+ }
193
+ /**
194
+ * The timeline item id an accepted staff reply appears under: the
195
+ * outbound ledger's `ob:` namespace over the reply output's
196
+ * `outbound_message_id` — the contract
197
+ * `useConversationParticipation.reply` documents ("the item will
198
+ * appear on the timeline as `ob:<that id>`"). The workbench's
199
+ * post-send busy state watches for exactly this id (F-05).
200
+ */
201
+ export function outboundItemIdOf(outboundMessageId) {
202
+ return `ob:${outboundMessageId}`;
203
+ }
204
+ /**
205
+ * Newest-first timeline order: `(at, item_id)` descending — the exact
206
+ * comparator the server pages with, so client-side merges can never
207
+ * disagree with server pages about order.
208
+ */
209
+ export function compareTimelineItemsNewestFirst(a, b) {
210
+ const byInstant = compareInstants(b.at, a.at);
211
+ if (byInstant !== 0)
212
+ return byInstant;
213
+ return a.itemId < b.itemId ? 1 : a.itemId > b.itemId ? -1 : 0;
214
+ }
215
+ // BigInt() call form, not a 0n literal: the web console compiles SDK
216
+ // sources under a pre-ES2020 target where the literal is a syntax error.
217
+ const ZERO_SECONDS = BigInt(0);
218
+ function compareInstants(a, b) {
219
+ // A missing instant sorts oldest — the server always stamps `at`, so
220
+ // this only orders a defensively-handled corrupt item to the top of
221
+ // history rather than crashing the sort.
222
+ const aSeconds = a?.seconds ?? ZERO_SECONDS;
223
+ const bSeconds = b?.seconds ?? ZERO_SECONDS;
224
+ if (aSeconds !== bSeconds)
225
+ return aSeconds < bSeconds ? -1 : 1;
226
+ const aNanos = a?.nanos ?? 0;
227
+ const bNanos = b?.nanos ?? 0;
228
+ return aNanos - bNanos;
229
+ }
230
+ //# sourceMappingURL=conversationPresentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversationPresentation.js","sourceRoot":"","sources":["../../src/conversation/conversationPresentation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export { ConversationsWorkbench } from "./ConversationsWorkbench.js";
2
+ export type { ConversationHeaderContext, ConversationsWorkbenchProps, } from "./ConversationsWorkbench.js";
3
+ export { ConversationListPane } from "./ConversationListPane.js";
4
+ export type { ConversationIdentity, ConversationListPaneProps, } from "./ConversationListPane.js";
5
+ export { ConversationTimelineView } from "./ConversationTimelineView.js";
6
+ export type { ConversationTimelineViewProps } from "./ConversationTimelineView.js";
7
+ export { ConversationControlBanner } from "./ConversationControlBanner.js";
8
+ export type { ConversationControlBannerProps } from "./ConversationControlBanner.js";
9
+ export { ConversationAttentionBanner } from "./ConversationAttentionBanner.js";
10
+ export type { ConversationAttentionBannerProps } from "./ConversationAttentionBanner.js";
11
+ export { ConversationComposer } from "./ConversationComposer.js";
12
+ export type { ConversationComposerProps } from "./ConversationComposer.js";
13
+ export { useConversation } from "./useConversation.js";
14
+ export type { UseConversationOptions, UseConversationReturn, } from "./useConversation.js";
15
+ export { useConversationList } from "./useConversationList.js";
16
+ export type { UseConversationListOptions, UseConversationListReturn, } from "./useConversationList.js";
17
+ export { useConversationTimeline } from "./useConversationTimeline.js";
18
+ export type { UseConversationTimelineOptions, UseConversationTimelineReturn, } from "./useConversationTimeline.js";
19
+ export { useConversationParticipation } from "./useConversationParticipation.js";
20
+ export type { ConversationCommand, UseConversationParticipationOptions, UseConversationParticipationReturn, } from "./useConversationParticipation.js";
21
+ export { useConversationsWantsHumanCount } from "./useConversationsWantsHumanCount.js";
22
+ export type { UseConversationsWantsHumanCountOptions, UseConversationsWantsHumanCountReturn, } from "./useConversationsWantsHumanCount.js";
23
+ export { authorKindOf, awaitingIndicatorOf, compareTimelineItemsNewestFirst, conversationContactOf, conversationLabelOf, inboundPlaceholderOf, isInternalItem, receiptOf, sendAttemptOf, serviceWindowOf, } from "./conversationPresentation.js";
24
+ export type { AwaitingIndicator, ConversationAuthorKind, ReceiptKind, SendAttemptKind, ServiceWindowState, } from "./conversationPresentation.js";
25
+ export { CONVERSATION_BADGE_POLL_INTERVAL_MS, CONVERSATION_DETAIL_POLL_INTERVAL_MS, CONVERSATION_LIST_POLL_INTERVAL_MS, } from "./polling.js";
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/conversation/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EACV,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,YAAY,EACV,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,YAAY,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAEnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,YAAY,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AAErF,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,YAAY,EAAE,gCAAgC,EAAE,MAAM,kCAAkC,CAAC;AAEzF,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,YAAY,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAE3E,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EACV,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,YAAY,EACV,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EACV,8BAA8B,EAC9B,6BAA6B,GAC9B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AACjF,YAAY,EACV,mBAAmB,EACnB,mCAAmC,EACnC,kCAAkC,GACnC,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,+BAA+B,EAAE,MAAM,sCAAsC,CAAC;AACvF,YAAY,EACV,sCAAsC,EACtC,qCAAqC,GACtC,MAAM,sCAAsC,CAAC;AAE9C,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,+BAA+B,EAC/B,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,SAAS,EACT,aAAa,EACb,eAAe,GAChB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,iBAAiB,EACjB,sBAAsB,EACtB,WAAW,EACX,eAAe,EACf,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EACL,mCAAmC,EACnC,oCAAoC,EACpC,kCAAkC,GACnC,MAAM,cAAc,CAAC"}
@@ -0,0 +1,14 @@
1
+ export { ConversationsWorkbench } from "./ConversationsWorkbench.js";
2
+ export { ConversationListPane } from "./ConversationListPane.js";
3
+ export { ConversationTimelineView } from "./ConversationTimelineView.js";
4
+ export { ConversationControlBanner } from "./ConversationControlBanner.js";
5
+ export { ConversationAttentionBanner } from "./ConversationAttentionBanner.js";
6
+ export { ConversationComposer } from "./ConversationComposer.js";
7
+ export { useConversation } from "./useConversation.js";
8
+ export { useConversationList } from "./useConversationList.js";
9
+ export { useConversationTimeline } from "./useConversationTimeline.js";
10
+ export { useConversationParticipation } from "./useConversationParticipation.js";
11
+ export { useConversationsWantsHumanCount } from "./useConversationsWantsHumanCount.js";
12
+ export { authorKindOf, awaitingIndicatorOf, compareTimelineItemsNewestFirst, conversationContactOf, conversationLabelOf, inboundPlaceholderOf, isInternalItem, receiptOf, sendAttemptOf, serviceWindowOf, } from "./conversationPresentation.js";
13
+ export { CONVERSATION_BADGE_POLL_INTERVAL_MS, CONVERSATION_DETAIL_POLL_INTERVAL_MS, CONVERSATION_LIST_POLL_INTERVAL_MS, } from "./polling.js";
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/conversation/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAMrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAMjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAGzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAG3E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAG/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAGjE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAMvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAM/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAMvE,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AAOjF,OAAO,EAAE,+BAA+B,EAAE,MAAM,sCAAsC,CAAC;AAMvF,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,+BAA+B,EAC/B,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,SAAS,EACT,aAAa,EACb,eAAe,GAChB,MAAM,+BAA+B,CAAC;AASvC,OAAO,EACL,mCAAmC,EACnC,oCAAoC,EACpC,kCAAkC,GACnC,MAAM,cAAc,CAAC"}