@athenaintel/react 0.57.1 → 0.57.2

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 (31) hide show
  1. package/README.md +19 -0
  2. package/dist/chat/AthenaChat.cjs +47 -2
  3. package/dist/chat/AthenaChat.cjs.map +1 -1
  4. package/dist/chat/AthenaChat.js +47 -2
  5. package/dist/chat/AthenaChat.js.map +1 -1
  6. package/dist/provider/AthenaProvider.cjs +7 -0
  7. package/dist/provider/AthenaProvider.cjs.map +1 -1
  8. package/dist/provider/AthenaProvider.js +9 -2
  9. package/dist/provider/AthenaProvider.js.map +1 -1
  10. package/dist/react/package.json.cjs +1 -1
  11. package/dist/react/package.json.js +1 -1
  12. package/dist/runtime/statewire-run-config.cjs +15 -0
  13. package/dist/runtime/statewire-run-config.cjs.map +1 -1
  14. package/dist/runtime/statewire-run-config.d.ts +13 -0
  15. package/dist/runtime/statewire-run-config.js +15 -0
  16. package/dist/runtime/statewire-run-config.js.map +1 -1
  17. package/dist/threads/SessionList.cjs +22 -19
  18. package/dist/threads/SessionList.cjs.map +1 -1
  19. package/dist/threads/SessionList.js +22 -19
  20. package/dist/threads/SessionList.js.map +1 -1
  21. package/dist/threads/live-session-rows.cjs +207 -0
  22. package/dist/threads/live-session-rows.cjs.map +1 -0
  23. package/dist/threads/live-session-rows.d.ts +175 -0
  24. package/dist/threads/live-session-rows.js +207 -0
  25. package/dist/threads/live-session-rows.js.map +1 -0
  26. package/dist/threads/statewire-thread-list.cjs +173 -20
  27. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  28. package/dist/threads/statewire-thread-list.d.ts +44 -1
  29. package/dist/threads/statewire-thread-list.js +174 -21
  30. package/dist/threads/statewire-thread-list.js.map +1 -1
  31. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useOfflineSessionList } from "./offline-session-list.js";
3
3
  import { QueryClient, QueryClientProvider, useQueryClient } from "@tanstack/react-query";
4
- import { useAuiState } from "@assistant-ui/react";
4
+ import { useAuiState, useAuiEvent } from "@assistant-ui/react";
5
5
  import { useContext, createContext, useState, useRef, useCallback, useMemo, useEffect } from "react";
6
6
  import { useAccountIdentityQuery } from "../auth/account-identity.js";
7
7
  import { AthenaAuthContext } from "../auth/AthenaAuthContext.js";
@@ -13,6 +13,7 @@ import { mintThreadId } from "./mint-thread-id.js";
13
13
  import { useAthenaStatewireThreadId } from "./thread-id-context.js";
14
14
  import { createThreadPrefetcher } from "./thread-prefetcher.js";
15
15
  import { useIndexTriggeredRefresh } from "./index-triggered-refresh.js";
16
+ import { isAgentHomeIndexEntry, sortNewestFirst, withIndexedSessions, localSessionSend, isLocalSendReported, settledLocalSends, LOCAL_SEND_TTL_MS, inSessionListScope, withLocalSends, withoutLocalSends as withoutLocalSends$1 } from "./live-session-rows.js";
16
17
  import { useSessionListTabShare, sessionListShareKey } from "./session-list-tab-share.js";
17
18
  import { normalizeThreadTitle, isSerializedContentPartsTitle } from "./title.js";
18
19
  import { useRestartAfterAuthRefusal } from "../runtime/auth-refusal-restart.js";
@@ -21,7 +22,7 @@ import { useThreadArchive } from "./use-thread-archive.js";
21
22
  import { useThreadFavorite, withFavoriteOverrides, withFavoriteOverride } from "./use-thread-favorite.js";
22
23
  import { useThreadReadState, withReadStateOverrides } from "./use-thread-read-state.js";
23
24
  import { useThreadRename, withTitleOverrides, withTitleOverride } from "./use-thread-rename.js";
24
- import { deriveConversationAttention, summaryFromConversationIndexEntry, patchConversationListCaches, conversationListQueryKeys, conversationListFilterKey, removeConversationFromListCaches } from "../agent-runtime/src/conversation-list.js";
25
+ import { deriveConversationAttention, summaryFromConversationIndexEntry, patchConversationListCaches, conversationListFilterKey, conversationListQueryKeys, removeConversationFromListCaches } from "../agent-runtime/src/conversation-list.js";
25
26
  import { useConversationList } from "../agent-runtime/src/use-conversation-list.js";
26
27
  import { useConversationIndex, conversationIndexUrlFromStatewireUrl } from "../agent-runtime/src/conversation-index.js";
27
28
  const AthenaStatewireThreadListContext = createContext(null);
@@ -32,14 +33,6 @@ function useRefreshThreadList() {
32
33
  var _a;
33
34
  return ((_a = useAthenaStatewireThreadList()) == null ? void 0 : _a.refresh) ?? null;
34
35
  }
35
- const AGENT_HOME_EXCLUDED_START_CHANNELS = /* @__PURE__ */ new Set([
36
- "task",
37
- "agent_orchestration"
38
- ]);
39
- function isAgentHomeIndexEntry(entry, homeAgentId) {
40
- if (!homeAgentId || entry.collab_agent_id !== homeAgentId) return false;
41
- return !AGENT_HOME_EXCLUDED_START_CHANNELS.has(entry.start_channel ?? "");
42
- }
43
36
  function indexedThreadsToRefreshFor({
44
37
  entries,
45
38
  listedThreadIds,
@@ -98,9 +91,16 @@ function withPinnedConversations({
98
91
  return unlisted.length === 0 ? conversations : [...conversations, ...unlisted];
99
92
  }
100
93
  const AthenaSessionsLiveContext = createContext(null);
94
+ const AthenaThreadListSendContext = createContext(null);
95
+ const POLL_REFRESH_AFTER_SEND_MS = [3e3, 1e4];
101
96
  const THREAD_LIST_PAGE_SIZE = 50;
102
97
  const EMPTY_THREAD_IDS = /* @__PURE__ */ new Set();
98
+ const EMPTY_LOCAL_SENDS = /* @__PURE__ */ new Map();
103
99
  const MAX_PINNED_PAGES = 10;
100
+ function withoutLocalSends(state, threadIds) {
101
+ const sends = withoutLocalSends$1(state.sends, threadIds);
102
+ return sends === state.sends ? state : { ...state, sends };
103
+ }
104
104
  function StatewireThreadListState({
105
105
  backendUrl,
106
106
  syncUrl,
@@ -249,6 +249,34 @@ function StatewireThreadListState({
249
249
  () => foldConversationIndex(listedConversations, indexByThread),
250
250
  [listedConversations, indexByThread]
251
251
  );
252
+ const homeAgentApplied = list.homeAgentApplied;
253
+ const scope = useMemo(
254
+ () => ({ appId, additionalAppIds: listedAppIds, homeAgentId, homeAgentApplied }),
255
+ [appId, listedAppIds, homeAgentId, homeAgentApplied]
256
+ );
257
+ const searching = trimmedSearch.length > 0;
258
+ const serverConversations = useMemo(
259
+ () => sortNewestFirst(
260
+ withIndexedSessions({
261
+ conversations: mergedConversations,
262
+ loadedConversations: list.conversations,
263
+ entries: index.entries,
264
+ scope,
265
+ searching,
266
+ hasResolvedFirstPage: list.hasResolvedFirstPage,
267
+ hasNextPage: list.hasNextPage
268
+ })
269
+ ),
270
+ [
271
+ mergedConversations,
272
+ list.conversations,
273
+ index.entries,
274
+ scope,
275
+ searching,
276
+ list.hasResolvedFirstPage,
277
+ list.hasNextPage
278
+ ]
279
+ );
252
280
  const queryClient = useQueryClient();
253
281
  const patchEveryList = useCallback(
254
282
  (threadId, patch) => patchConversationListCaches(queryClient, {
@@ -262,7 +290,7 @@ function StatewireThreadListState({
262
290
  backendUrl,
263
291
  principalId: principal,
264
292
  getAuth,
265
- rows: mergedConversations,
293
+ rows: serverConversations,
266
294
  onServerTruth: ({ threadId, isFavorited }) => {
267
295
  patchEveryList(threadId, { isFavorited });
268
296
  if (isFavorited) void pinnedListRefresh();
@@ -273,7 +301,7 @@ function StatewireThreadListState({
273
301
  backendUrl,
274
302
  principalId: principal,
275
303
  getAuth,
276
- rows: mergedConversations,
304
+ rows: serverConversations,
277
305
  onServerTruth: ({ threadId, title }) => patchEveryList(threadId, { title })
278
306
  });
279
307
  const titleOverrides = rename.overrides;
@@ -281,7 +309,7 @@ function StatewireThreadListState({
281
309
  backendUrl,
282
310
  principalId: principal,
283
311
  getAuth,
284
- rows: mergedConversations,
312
+ rows: serverConversations,
285
313
  onServerTruth: ({ threadId, isUnread }) => patchEveryList(threadId, { isUnread })
286
314
  });
287
315
  const readOverrides = readState.overrides;
@@ -314,9 +342,110 @@ function StatewireThreadListState({
314
342
  ).filter((conversation) => !hiddenThreadIds.has(conversation.threadId)),
315
343
  [favoriteOverrides, titleOverrides, readOverrides, readCoveredUpdatedAt, hiddenThreadIds]
316
344
  );
345
+ const sendsScopeKey = `${listScopeKey}::${credentialGeneration}::${conversationListFilterKey({
346
+ ...appId ? { appId } : {},
347
+ ...listedAppIds.length > 0 ? { appIds: listedAppIds } : {},
348
+ ...homeAgentId ? { homeAgentId } : {}
349
+ })}`;
350
+ const [localSendState, setLocalSendState] = useState(() => ({ scopeKey: sendsScopeKey, sends: EMPTY_LOCAL_SENDS }));
351
+ const localSends = localSendState.scopeKey === sendsScopeKey ? localSendState.sends : EMPTY_LOCAL_SENDS;
352
+ const serverConversationsRef = useRef(serverConversations);
353
+ serverConversationsRef.current = serverConversations;
354
+ const sendsScopeKeyRef = useRef(sendsScopeKey);
355
+ sendsScopeKeyRef.current = sendsScopeKey;
356
+ const activeRef = useRef(active);
357
+ activeRef.current = active;
358
+ const indexLiveRef = useRef(index.entries !== null);
359
+ indexLiveRef.current = index.entries !== null;
360
+ const refreshAfterSendRef = useRef(() => void 0);
361
+ const pollRefreshTimersRef = useRef(/* @__PURE__ */ new Set());
362
+ useEffect(() => {
363
+ const timers = pollRefreshTimersRef.current;
364
+ return () => {
365
+ for (const timer of timers) clearTimeout(timer);
366
+ timers.clear();
367
+ };
368
+ }, []);
369
+ const noteSend = useCallback(({ threadId, text, startChannel }) => {
370
+ if (!threadId) return;
371
+ const rows = serverConversationsRef.current;
372
+ const send = localSessionSend({
373
+ threadId,
374
+ text,
375
+ sentAtMs: Date.now(),
376
+ newChat: activeRef.current.id === threadId && activeRef.current.isNew,
377
+ startChannel,
378
+ serverConversations: rows
379
+ });
380
+ const scopeKey = sendsScopeKeyRef.current;
381
+ setLocalSendState((current) => {
382
+ const sends = new Map(current.scopeKey === scopeKey ? current.sends : EMPTY_LOCAL_SENDS);
383
+ const previous = sends.get(threadId);
384
+ const serverRow = rows.find((row) => row.threadId === threadId);
385
+ sends.set(
386
+ threadId,
387
+ previous && !isLocalSendReported(previous, serverRow) ? {
388
+ ...send,
389
+ baselineUpdatedAt: previous.baselineUpdatedAt,
390
+ newChat: previous.newChat,
391
+ title: previous.title ?? send.title,
392
+ startChannel: previous.startChannel
393
+ } : send
394
+ );
395
+ return { scopeKey, sends };
396
+ });
397
+ if (!indexLiveRef.current && !send.newChat) {
398
+ for (const delay of POLL_REFRESH_AFTER_SEND_MS) {
399
+ const timer = setTimeout(() => {
400
+ pollRefreshTimersRef.current.delete(timer);
401
+ refreshAfterSendRef.current();
402
+ }, delay);
403
+ pollRefreshTimersRef.current.add(timer);
404
+ }
405
+ }
406
+ }, []);
407
+ useEffect(() => {
408
+ if (localSends.size === 0) return;
409
+ const settled = settledLocalSends({ sends: localSends, serverConversations, nowMs: Date.now() });
410
+ if (settled.length > 0) {
411
+ setLocalSendState((current) => withoutLocalSends(current, settled));
412
+ return;
413
+ }
414
+ let oldestMs = Number.POSITIVE_INFINITY;
415
+ for (const send of localSends.values()) oldestMs = Math.min(oldestMs, send.sentAtMs);
416
+ const timer = setTimeout(
417
+ () => setLocalSendState(
418
+ (current) => withoutLocalSends(
419
+ current,
420
+ [...current.sends.values()].filter((send) => Date.now() - send.sentAtMs >= LOCAL_SEND_TTL_MS).map((send) => send.threadId)
421
+ )
422
+ ),
423
+ Math.max(0, oldestMs + LOCAL_SEND_TTL_MS - Date.now()) + 1
424
+ );
425
+ return () => clearTimeout(timer);
426
+ }, [localSends, serverConversations]);
427
+ const showPendingSessions = useMemo(
428
+ () => !searching && inSessionListScope(
429
+ { app_id: appId ?? null, collab_agent_id: homeAgentId ?? null, start_channel: null },
430
+ scope
431
+ ),
432
+ [searching, appId, homeAgentId, scope]
433
+ );
434
+ const liveRows = useMemo(
435
+ () => withLocalSends({
436
+ conversations: serverConversations,
437
+ sends: localSends,
438
+ appId,
439
+ showPendingSessions
440
+ }),
441
+ [serverConversations, localSends, appId, showPendingSessions]
442
+ );
317
443
  const data = useMemo(
318
- () => applyOverrides(mergedConversations).map(threadSummaryFromConversation),
319
- [applyOverrides, mergedConversations]
444
+ () => sortNewestFirst(applyOverrides(liveRows.conversations)).map((conversation) => {
445
+ const summary = threadSummaryFromConversation(conversation);
446
+ return liveRows.pendingThreadIds.has(conversation.threadId) ? { ...summary, isPending: true } : summary;
447
+ }),
448
+ [applyOverrides, liveRows]
320
449
  );
321
450
  const isLoading = list.isLoading;
322
451
  const lastActiveConversationRef = useRef(null);
@@ -352,6 +481,7 @@ function StatewireThreadListState({
352
481
  void listRefresh();
353
482
  if (!pinnedFetchingRef.current) void pinnedListRefresh();
354
483
  }, [listRefresh, pinnedListRefresh]);
484
+ refreshAfterSendRef.current = refresh;
355
485
  const listFetchNextPage = list.fetchNextPage;
356
486
  const fetchNextPage = useCallback(() => {
357
487
  void listFetchNextPage();
@@ -360,7 +490,6 @@ function StatewireThreadListState({
360
490
  () => new Set(list.conversations.map((conversation) => conversation.threadId)),
361
491
  [list.conversations]
362
492
  );
363
- const homeAgentApplied = list.homeAgentApplied;
364
493
  const unlistedIndexedThreads = useMemo(
365
494
  () => index.entries === null ? [] : indexedThreadsToRefreshFor({
366
495
  entries: index.entries,
@@ -404,14 +533,14 @@ function StatewireThreadListState({
404
533
  prefetchScopeRef.current = prefetchScope;
405
534
  const prefetcher = useMemo(() => {
406
535
  if (!snapshotCache) return null;
407
- const scope = prefetchScope;
536
+ const scope2 = prefetchScope;
408
537
  return createThreadPrefetcher({
409
538
  cache: snapshotCache,
410
539
  fetchState: async (threadId) => withCredentialRenewal(
411
540
  getAuth,
412
541
  (auth) => getThreadState(backendUrl, auth, threadId, { skipCache: false })
413
542
  ),
414
- isCurrent: () => prefetchScopeRef.current === scope
543
+ isCurrent: () => prefetchScopeRef.current === scope2
415
544
  });
416
545
  }, [snapshotCache, prefetchScope, backendUrl, getAuth]);
417
546
  const activeIdRef = useRef(active.id);
@@ -426,7 +555,7 @@ function StatewireThreadListState({
426
555
  const newThread = useCallback(() => {
427
556
  setActive({ id: mintThreadId(), isNew: true });
428
557
  }, []);
429
- const isPersisted = data.some((thread) => thread.id === active.id) || indexByThread.has(active.id);
558
+ const isPersisted = mergedConversations.some((conversation) => conversation.threadId === active.id) || indexByThread.has(active.id);
430
559
  useEffect(() => {
431
560
  if (isPersisted) {
432
561
  setActive((current) => current.isNew ? { ...current, isNew: false } : current);
@@ -534,7 +663,7 @@ function StatewireThreadListState({
534
663
  tabShare
535
664
  ]
536
665
  );
537
- return /* @__PURE__ */ jsx(AthenaStatewireThreadListContext.Provider, { value, children: /* @__PURE__ */ jsx(AthenaSessionsLiveContext.Provider, { value: live, children }) });
666
+ return /* @__PURE__ */ jsx(AthenaStatewireThreadListContext.Provider, { value, children: /* @__PURE__ */ jsx(AthenaSessionsLiveContext.Provider, { value: live, children: /* @__PURE__ */ jsx(AthenaThreadListSendContext.Provider, { value: noteSend, children }) }) });
538
667
  }
539
668
  function AthenaStatewireThreadListProvider({
540
669
  backendUrl,
@@ -583,7 +712,7 @@ function StatewireThreadTitlePoller() {
583
712
  return (((_a2 = s.optional.thread) == null ? void 0 : _a2.messages.length) ?? 0) > 0;
584
713
  });
585
714
  const titleSource = (list == null ? void 0 : list.titleSource) ?? "poll";
586
- const entry = list == null ? void 0 : list.threads.find((thread) => thread.id === activeThreadId);
715
+ const entry = list == null ? void 0 : list.threads.find((thread) => thread.id === activeThreadId && !thread.isPending);
587
716
  const title = ((_a = entry == null ? void 0 : entry.title) == null ? void 0 : _a.trim()) ?? "";
588
717
  const hasTitle = title.length > 0 && !isSerializedContentPartsTitle(title);
589
718
  const visitRef = useRef(null);
@@ -618,10 +747,34 @@ function StatewireThreadTitlePoller() {
618
747
  }, [activeThreadId, hasMessages, hasTitle, titleSource]);
619
748
  return null;
620
749
  }
750
+ function StatewireThreadListSendSync({
751
+ startChannel = null
752
+ } = {}) {
753
+ const noteSend = useContext(AthenaThreadListSendContext);
754
+ const threadId = useAthenaStatewireThreadId();
755
+ const typedRef = useRef(/* @__PURE__ */ new Map());
756
+ useEffect(() => {
757
+ typedRef.current.clear();
758
+ }, [threadId]);
759
+ useAuiEvent({ scope: "thread", event: "composer.textChange" }, (event) => {
760
+ if (event.origin === "send") return;
761
+ typedRef.current.set(event.messageId ?? "", event.text ?? "");
762
+ });
763
+ useAuiEvent({ scope: "thread", event: "composer.send" }, (event) => {
764
+ const composer = event.messageId ?? "";
765
+ const typed = typedRef.current.get(composer) ?? null;
766
+ typedRef.current.delete(composer);
767
+ if (!noteSend || !threadId) return;
768
+ noteSend({ threadId, text: event.chars > 0 ? typed : null, startChannel });
769
+ });
770
+ return null;
771
+ }
621
772
  export {
622
773
  AthenaSessionsLiveContext,
623
774
  AthenaStatewireThreadListContext,
624
775
  AthenaStatewireThreadListProvider,
776
+ AthenaThreadListSendContext,
777
+ StatewireThreadListSendSync,
625
778
  StatewireThreadTitlePoller,
626
779
  TITLE_POLL_DELAY_MS,
627
780
  foldConversationIndex,