@cossistant/react 0.0.33 → 0.1.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 (52) hide show
  1. package/README.md +3 -3
  2. package/hooks/private/store/use-store-selector.d.ts.map +1 -1
  3. package/hooks/private/store/use-store-selector.js +8 -4
  4. package/hooks/private/store/use-store-selector.js.map +1 -1
  5. package/hooks/private/use-client-query.js +5 -2
  6. package/hooks/private/use-client-query.js.map +1 -1
  7. package/hooks/private/use-rest-client.js +2 -1
  8. package/hooks/private/use-rest-client.js.map +1 -1
  9. package/hooks/use-conversation-auto-seen.d.ts.map +1 -1
  10. package/hooks/use-conversation-auto-seen.js +9 -3
  11. package/hooks/use-conversation-auto-seen.js.map +1 -1
  12. package/hooks/use-conversation-page.d.ts.map +1 -1
  13. package/hooks/use-conversation-page.js +13 -3
  14. package/hooks/use-conversation-page.js.map +1 -1
  15. package/index.d.ts +2 -2
  16. package/index.js +2 -2
  17. package/package.json +6 -10
  18. package/packages/tiny-markdown/src/types.d.ts +1 -1
  19. package/packages/types/src/api/conversation.d.ts +4 -4
  20. package/packages/types/src/api/timeline-item.d.ts +3 -3
  21. package/packages/types/src/realtime-events.d.ts +7 -7
  22. package/packages/types/src/schemas.d.ts +1 -1
  23. package/primitives/avatar/image.d.ts +1 -1
  24. package/primitives/multimodal-input.d.ts +2 -2
  25. package/primitives/multimodal-input.d.ts.map +1 -1
  26. package/provider.d.ts.map +1 -1
  27. package/provider.js +1 -7
  28. package/provider.js.map +1 -1
  29. package/realtime/provider.d.ts +0 -1
  30. package/realtime/provider.d.ts.map +1 -1
  31. package/realtime/provider.js +29 -34
  32. package/realtime/provider.js.map +1 -1
  33. package/styles.css +2 -0
  34. package/support/components/avatar.js +3 -3
  35. package/support/components/avatar.js.map +1 -1
  36. package/support/components/conversation-event.js +5 -5
  37. package/support/components/conversation-event.js.map +1 -1
  38. package/support/index.d.ts +4 -4
  39. package/support/pages/home.js +1 -1
  40. package/support/pages/home.js.map +1 -1
  41. package/support/store/support-store.d.ts +5 -5
  42. package/support/store/support-store.d.ts.map +1 -1
  43. package/support/store/support-store.js +4 -4
  44. package/support/store/support-store.js.map +1 -1
  45. package/support/{support-DmViRaga.css → support-Dc5L__HC.css} +15 -15
  46. package/support/{support-DmViRaga.css.map → support-Dc5L__HC.css.map} +1 -1
  47. package/{tailwind.css → support/support.css} +14 -14
  48. package/support-config.d.ts +31 -4
  49. package/support-config.d.ts.map +1 -1
  50. package/support-config.js +52 -4
  51. package/support-config.js.map +1 -1
  52. package/support.css +1 -2
package/README.md CHANGED
@@ -25,7 +25,7 @@ The SDK provides two CSS entrypoints to fit your setup:
25
25
  If you're using Tailwind CSS v4, import the source file to enable full theme customization:
26
26
 
27
27
  ```tsx
28
- import "@cossistant/react/tailwind.css";
28
+ import "@cossistant/react/support.css";
29
29
  ```
30
30
 
31
31
  ### Option 2: Plain CSS
@@ -33,7 +33,7 @@ import "@cossistant/react/tailwind.css";
33
33
  Import the pre-compiled CSS with no Tailwind dependency:
34
34
 
35
35
  ```tsx
36
- import "@cossistant/react/support.css";
36
+ import "@cossistant/react/styles.css";
37
37
  ```
38
38
 
39
39
  This file contains all the compiled styles and works in any React application without requiring Tailwind CSS.
@@ -44,7 +44,7 @@ This file contains all the compiled styles and works in any React application wi
44
44
 
45
45
  ```tsx
46
46
  import { SupportProvider, Support } from "@cossistant/react";
47
- import "@cossistant/react/support.css";
47
+ import "@cossistant/react/styles.css";
48
48
 
49
49
  export function App() {
50
50
  return (
@@ -1 +1 @@
1
- {"version":3,"file":"use-store-selector.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":[],"mappings":";KAEK,+BAA+B;KAE/B,UAFA,CAAA,MAAY,CAAA,GAAA;EAEZ,QAAA,EAAA,EACQ,MADE;EACF,SAAA,CAAA,QAAA,EACQ,YADR,CACqB,MADrB,CAAA,CAAA,EAAA,GAAA,GAAA,IAAA;CACqB;;;AAYlC;;;;AAE8B,iBAFd,gBAEc,CAAA,MAAA,EAAA,SAAA,CAAA,CAAA,KAAA,EADtB,UACsB,CADX,MACW,CAAA,EAAA,QAAA,EAAA,CAAA,KAAA,EAAX,MAAW,EAAA,GAAA,SAAA,EAAA,OAAA,CAAA,EAAA,CAAA,QAAA,EACR,SADQ,EAAA,IAAA,EACS,SADT,EAAA,GAAA,OAAA,CAAA,EAE3B,SAF2B;AACR,iBAGN,gBAHM,CAAA,MAAA,EAAA,SAAA,CAAA,CAAA,KAAA,EAId,UAJc,CAIH,MAJG,CAAA,GAAA,IAAA,EAAA,QAAA,EAAA,CAAA,KAAA,EAKH,MALG,GAAA,IAAA,EAAA,GAKe,SALf,EAAA,OAAA,CAAA,EAAA,CAAA,QAAA,EAMA,SANA,EAAA,IAAA,EAMiB,SANjB,EAAA,GAAA,OAAA,CAAA,EAOnB,SAPmB"}
1
+ {"version":3,"file":"use-store-selector.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":[],"mappings":";KAEK,+BAA+B;KAE/B,UAFA,CAAA,MAAY,CAAA,GAAA;EAEZ,QAAA,EAAA,EACQ,MADE;EACF,SAAA,CAAA,QAAA,EACQ,YADR,CACqB,MADrB,CAAA,CAAA,EAAA,GAAA,GAAA,IAAA;CACqB;;;AAalC;;;;AAE8B,iBAFd,gBAEc,CAAA,MAAA,EAAA,SAAA,CAAA,CAAA,KAAA,EADtB,UACsB,CADX,MACW,CAAA,EAAA,QAAA,EAAA,CAAA,KAAA,EAAX,MAAW,EAAA,GAAA,SAAA,EAAA,OAAA,CAAA,EAAA,CAAA,QAAA,EACR,SADQ,EAAA,IAAA,EACS,SADT,EAAA,GAAA,OAAA,CAAA,EAE3B,SAF2B;AACR,iBAGN,gBAHM,CAAA,MAAA,EAAA,SAAA,CAAA,CAAA,KAAA,EAId,UAJc,CAIH,MAJG,CAAA,GAAA,IAAA,EAAA,QAAA,EAAA,CAAA,KAAA,EAKH,MALG,GAAA,IAAA,EAAA,GAKe,SALf,EAAA,OAAA,CAAA,EAAA,CAAA,QAAA,EAMA,SANA,EAAA,IAAA,EAMiB,SANjB,EAAA,GAAA,OAAA,CAAA,EAOnB,SAPmB"}
@@ -1,12 +1,16 @@
1
- import { useRef, useSyncExternalStore } from "react";
1
+ import { useCallback, useRef, useSyncExternalStore } from "react";
2
2
 
3
3
  //#region src/hooks/private/store/use-store-selector.ts
4
4
  const noopSubscribe = () => () => {};
5
+ const getNull = () => null;
5
6
  function useStoreSelector(store, selector, isEqual = Object.is) {
6
7
  const selectionRef = useRef(void 0);
7
- const subscribe = store ? (onStoreChange) => store.subscribe(() => onStoreChange()) : noopSubscribe;
8
- const getSnapshot = store ? () => store.getState() : () => null;
9
- const selected = selector(useSyncExternalStore(subscribe, getSnapshot, getSnapshot));
8
+ const subscribe = useCallback((onStoreChange) => {
9
+ if (!store) return noopSubscribe();
10
+ return store.subscribe(() => onStoreChange());
11
+ }, [store]);
12
+ const getSnapshot = useCallback(() => store ? store.getState() : null, [store]);
13
+ const selected = selector(useSyncExternalStore(store ? subscribe : noopSubscribe, store ? getSnapshot : getNull, store ? getSnapshot : getNull));
10
14
  if (selectionRef.current === void 0 || !isEqual(selectionRef.current, selected)) selectionRef.current = selected;
11
15
  return selectionRef.current;
12
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-store-selector.js","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":["import { useRef, useSyncExternalStore } from \"react\";\n\ntype Subscription<TState> = (state: TState) => void;\n\ntype BasicStore<TState> = {\n\tgetState(): TState;\n\tsubscribe(listener: Subscription<TState>): () => void;\n};\n\n// No-op subscribe function for null store case\nconst noopSubscribe = () => () => {};\n\n/**\n * React hook that bridges Zustand-like stores with React components by\n * memoizing selector results and resubscribing when dependencies change.\n *\n * Overloaded to support both nullable and non-nullable stores.\n */\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState>,\n\tselector: (state: TState) => TSelected,\n\tisEqual?: (previous: TSelected, next: TSelected) => boolean\n): TSelected;\n\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState> | null,\n\tselector: (state: TState | null) => TSelected,\n\tisEqual?: (previous: TSelected, next: TSelected) => boolean\n): TSelected;\n\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState> | null,\n\tselector: (state: TState | null) => TSelected,\n\tisEqual: (previous: TSelected, next: TSelected) => boolean = Object.is\n): TSelected {\n\tconst selectionRef = useRef<TSelected>(undefined);\n\n\t// Create stable subscribe function\n\tconst subscribe = store\n\t\t? (onStoreChange: () => void) => store.subscribe(() => onStoreChange())\n\t\t: noopSubscribe;\n\n\t// Create stable getSnapshot function\n\tconst getSnapshot = store ? () => store.getState() : () => null;\n\n\t// Always call useSyncExternalStore unconditionally\n\tconst snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n\tconst selected = selector(snapshot);\n\n\tif (\n\t\tselectionRef.current === undefined ||\n\t\t!isEqual(selectionRef.current, selected)\n\t) {\n\t\tselectionRef.current = selected;\n\t}\n\n\treturn selectionRef.current as TSelected;\n}\n"],"mappings":";;;AAUA,MAAM,4BAA4B;AAoBlC,SAAgB,iBACf,OACA,UACA,UAA6D,OAAO,IACxD;CACZ,MAAM,eAAe,OAAkB,OAAU;CAGjD,MAAM,YAAY,SACd,kBAA8B,MAAM,gBAAgB,eAAe,CAAC,GACrE;CAGH,MAAM,cAAc,cAAc,MAAM,UAAU,SAAS;CAK3D,MAAM,WAAW,SAFA,qBAAqB,WAAW,aAAa,YAAY,CAEvC;AAEnC,KACC,aAAa,YAAY,UACzB,CAAC,QAAQ,aAAa,SAAS,SAAS,CAExC,cAAa,UAAU;AAGxB,QAAO,aAAa"}
1
+ {"version":3,"file":"use-store-selector.js","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from \"react\";\n\ntype Subscription<TState> = (state: TState) => void;\n\ntype BasicStore<TState> = {\n\tgetState(): TState;\n\tsubscribe(listener: Subscription<TState>): () => void;\n};\n\n// No-op subscribe function for null store case\nconst noopSubscribe = () => () => {};\nconst getNull = () => null;\n\n/**\n * React hook that bridges Zustand-like stores with React components by\n * memoizing selector results and resubscribing when dependencies change.\n *\n * Overloaded to support both nullable and non-nullable stores.\n */\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState>,\n\tselector: (state: TState) => TSelected,\n\tisEqual?: (previous: TSelected, next: TSelected) => boolean\n): TSelected;\n\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState> | null,\n\tselector: (state: TState | null) => TSelected,\n\tisEqual?: (previous: TSelected, next: TSelected) => boolean\n): TSelected;\n\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState> | null,\n\tselector: (state: TState | null) => TSelected,\n\tisEqual: (previous: TSelected, next: TSelected) => boolean = Object.is\n): TSelected {\n\tconst selectionRef = useRef<TSelected>(undefined);\n\n\t// Stable subscribe function — only recreated when store identity changes\n\tconst subscribe = useCallback(\n\t\t(onStoreChange: () => void) => {\n\t\t\tif (!store) {\n\t\t\t\treturn noopSubscribe();\n\t\t\t}\n\t\t\treturn store.subscribe(() => onStoreChange());\n\t\t},\n\t\t[store]\n\t);\n\n\t// Stable getSnapshot function — only recreated when store identity changes\n\tconst getSnapshot = useCallback(\n\t\t() => (store ? store.getState() : null),\n\t\t[store]\n\t);\n\n\t// Always call useSyncExternalStore unconditionally\n\tconst snapshot = useSyncExternalStore(\n\t\tstore ? subscribe : noopSubscribe,\n\t\tstore ? getSnapshot : getNull,\n\t\tstore ? getSnapshot : getNull\n\t);\n\n\tconst selected = selector(snapshot);\n\n\tif (\n\t\tselectionRef.current === undefined ||\n\t\t!isEqual(selectionRef.current, selected)\n\t) {\n\t\tselectionRef.current = selected;\n\t}\n\n\treturn selectionRef.current as TSelected;\n}\n"],"mappings":";;;AAUA,MAAM,4BAA4B;AAClC,MAAM,gBAAgB;AAoBtB,SAAgB,iBACf,OACA,UACA,UAA6D,OAAO,IACxD;CACZ,MAAM,eAAe,OAAkB,OAAU;CAGjD,MAAM,YAAY,aAChB,kBAA8B;AAC9B,MAAI,CAAC,MACJ,QAAO,eAAe;AAEvB,SAAO,MAAM,gBAAgB,eAAe,CAAC;IAE9C,CAAC,MAAM,CACP;CAGD,MAAM,cAAc,kBACZ,QAAQ,MAAM,UAAU,GAAG,MAClC,CAAC,MAAM,CACP;CASD,MAAM,WAAW,SANA,qBAChB,QAAQ,YAAY,eACpB,QAAQ,cAAc,SACtB,QAAQ,cAAc,QACtB,CAEkC;AAEnC,KACC,aAAa,YAAY,UACzB,CAAC,QAAQ,aAAa,SAAS,SAAS,CAExC,cAAa,UAAU;AAGxB,QAAO,aAAa"}
@@ -44,8 +44,11 @@ function useClientQuery(options) {
44
44
  const isMountedRef = useRef(true);
45
45
  const queryFnRef = useRef(queryFn);
46
46
  queryFnRef.current = queryFn;
47
- useEffect(() => () => {
48
- isMountedRef.current = false;
47
+ useEffect(() => {
48
+ isMountedRef.current = true;
49
+ return () => {
50
+ isMountedRef.current = false;
51
+ };
49
52
  }, []);
50
53
  useEffect(() => {
51
54
  argsRef.current = initialArgs;
@@ -1 +1 @@
1
- {"version":3,"file":"use-client-query.js","names":["EMPTY_DEPENDENCIES: readonly unknown[]","raw: unknown"],"sources":["../../../src/hooks/private/use-client-query.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\ntype QueryFn<TData, TArgs> = (\n\tclient: CossistantClient,\n\targs?: TArgs | undefined\n) => Promise<TData>;\n\ntype UseClientQueryOptions<TData, TArgs> = {\n\tclient: CossistantClient | null;\n\tqueryFn: QueryFn<TData, TArgs>;\n\t/**\n\t * Unique key to identify this query for deduplication.\n\t * When provided, concurrent requests with the same key will share a single\n\t * in-flight promise instead of making duplicate API calls.\n\t */\n\tqueryKey?: string;\n\tenabled?: boolean;\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n\trefetchOnMount?: boolean;\n\tinitialData?: TData;\n\tinitialArgs?: TArgs;\n\tdependencies?: readonly unknown[];\n};\n\ntype UseClientQueryResult<TData, TArgs> = {\n\tdata: TData | undefined;\n\terror: Error | null;\n\tisLoading: boolean;\n\trefetch: (args?: TArgs) => Promise<TData | undefined>;\n};\n\nfunction toError(error: unknown): Error {\n\tif (error instanceof Error) {\n\t\treturn error;\n\t}\n\n\treturn new Error(typeof error === \"string\" ? error : \"Unknown error\");\n}\n\nconst EMPTY_DEPENDENCIES: readonly unknown[] = [];\n\n/**\n * Module-level cache for in-flight requests.\n * Maps query keys to their pending promises for deduplication.\n */\nconst inFlightRequests = new Map<string, Promise<unknown>>();\n\n/**\n * Execute a query with deduplication support.\n * If a query with the same key is already in flight, returns the existing promise.\n */\nfunction executeWithDeduplication<TData>(\n\tqueryKey: string | undefined,\n\tqueryFn: () => Promise<TData>\n): Promise<TData> {\n\t// No deduplication if no key provided\n\tif (!queryKey) {\n\t\treturn queryFn();\n\t}\n\n\t// Check for existing in-flight request\n\tconst existing = inFlightRequests.get(queryKey);\n\tif (existing) {\n\t\treturn existing as Promise<TData>;\n\t}\n\n\t// Create new request and track it\n\tconst promise = queryFn().finally(() => {\n\t\t// Clean up after request completes (success or error)\n\t\tinFlightRequests.delete(queryKey);\n\t});\n\n\tinFlightRequests.set(queryKey, promise);\n\treturn promise;\n}\n\n/**\n * Lightweight data-fetching abstraction that plugs into the SDK client instead\n * of React Query. It tracks loading/error state, supports polling, window\n * focus refetching and exposes a typed refetch helper.\n */\nexport function useClientQuery<TData, TArgs = void>(\n\toptions: UseClientQueryOptions<TData, TArgs>\n): UseClientQueryResult<TData, TArgs> {\n\tconst {\n\t\tclient,\n\t\tqueryFn,\n\t\tqueryKey,\n\t\tenabled = true,\n\t\trefetchInterval = false,\n\t\trefetchOnWindowFocus = false,\n\t\trefetchOnMount = true,\n\t\tinitialData,\n\t\tinitialArgs,\n\t\tdependencies = EMPTY_DEPENDENCIES,\n\t} = options;\n\n\tconst [data, setData] = useState<TData | undefined>(initialData);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [isLoading, setIsLoading] = useState(\n\t\tinitialData === undefined && Boolean(enabled)\n\t);\n\n\tconst dataRef = useRef(data);\n\tdataRef.current = data;\n\n\tconst argsRef = useRef<TArgs | undefined>(initialArgs);\n\tconst fetchIdRef = useRef(0);\n\tconst hasMountedRef = useRef(false);\n\tconst hasFetchedRef = useRef(initialData !== undefined);\n\tconst isMountedRef = useRef(true);\n\tconst queryFnRef = useRef(queryFn);\n\n\tqueryFnRef.current = queryFn;\n\n\tuseEffect(\n\t\t() => () => {\n\t\t\tisMountedRef.current = false;\n\t\t},\n\t\t[]\n\t);\n\n\tuseEffect(() => {\n\t\targsRef.current = initialArgs;\n\t}, [initialArgs]);\n\n\tconst execute = useCallback(\n\t\tasync (args?: TArgs, ignoreEnabled = false): Promise<TData | undefined> => {\n\t\t\t// Handle null client (configuration error case)\n\t\t\tif (!client) {\n\t\t\t\treturn dataRef.current;\n\t\t\t}\n\n\t\t\tif (!(enabled || ignoreEnabled)) {\n\t\t\t\treturn dataRef.current;\n\t\t\t}\n\n\t\t\tconst nextArgs = args ?? argsRef.current;\n\t\t\targsRef.current = nextArgs;\n\n\t\t\tconst fetchId = fetchIdRef.current + 1;\n\t\t\tfetchIdRef.current = fetchId;\n\n\t\t\tsetIsLoading(true);\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\t// Use deduplication to share in-flight requests with the same key\n\t\t\t\tconst result = await executeWithDeduplication(queryKey, () =>\n\t\t\t\t\tqueryFnRef.current(client, nextArgs)\n\t\t\t\t);\n\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tdataRef.current = result;\n\t\t\t\tsetData(result);\n\t\t\t\tsetError(null);\n\t\t\t\tsetIsLoading(false);\n\t\t\t\thasFetchedRef.current = true;\n\n\t\t\t\treturn result;\n\t\t\t} catch (raw: unknown) {\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tconst normalized = toError(raw);\n\t\t\t\tsetError(normalized);\n\t\t\t\tsetIsLoading(false);\n\n\t\t\t\tthrow normalized;\n\t\t\t}\n\t\t},\n\t\t[client, enabled, queryKey]\n\t);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\tsetIsLoading(false);\n\t\t\treturn;\n\t\t}\n\n\t\tconst shouldFetchInitially = hasMountedRef.current\n\t\t\t? true\n\t\t\t: refetchOnMount || !hasFetchedRef.current;\n\n\t\thasMountedRef.current = true;\n\n\t\tif (!shouldFetchInitially) {\n\t\t\treturn;\n\t\t}\n\n\t\tvoid execute(argsRef.current);\n\t}, [enabled, execute, refetchOnMount, ...dependencies]);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\trefetchInterval === false ||\n\t\t\trefetchInterval === null ||\n\t\t\trefetchInterval <= 0 ||\n\t\t\ttypeof window === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst timer = window.setInterval(() => {\n\t\t\tvoid execute(argsRef.current);\n\t\t}, refetchInterval);\n\n\t\treturn () => {\n\t\t\twindow.clearInterval(timer);\n\t\t};\n\t}, [enabled, execute, refetchInterval]);\n\n\tuseEffect(() => {\n\t\tif (\n\t\t\t!refetchOnWindowFocus ||\n\t\t\ttypeof window === \"undefined\" ||\n\t\t\ttypeof document === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst handleRefetch = () => {\n\t\t\tif (!enabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvoid execute(argsRef.current);\n\t\t};\n\n\t\tconst onFocus = () => {\n\t\t\tvoid handleRefetch();\n\t\t};\n\n\t\tconst onVisibilityChange = () => {\n\t\t\tif (document.visibilityState === \"visible\") {\n\t\t\t\tvoid handleRefetch();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"focus\", onFocus);\n\t\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"focus\", onFocus);\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t};\n\t}, [enabled, execute, refetchOnWindowFocus]);\n\n\tconst refetch = useCallback(\n\t\tasync (args?: TArgs) => execute(args, true),\n\t\t[execute]\n\t);\n\n\treturn useMemo(\n\t\t() => ({\n\t\t\tdata,\n\t\t\terror,\n\t\t\tisLoading,\n\t\t\trefetch,\n\t\t}),\n\t\t[data, error, isLoading, refetch]\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-client-query.js","names":["EMPTY_DEPENDENCIES: readonly unknown[]","raw: unknown"],"sources":["../../../src/hooks/private/use-client-query.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\ntype QueryFn<TData, TArgs> = (\n\tclient: CossistantClient,\n\targs?: TArgs | undefined\n) => Promise<TData>;\n\ntype UseClientQueryOptions<TData, TArgs> = {\n\tclient: CossistantClient | null;\n\tqueryFn: QueryFn<TData, TArgs>;\n\t/**\n\t * Unique key to identify this query for deduplication.\n\t * When provided, concurrent requests with the same key will share a single\n\t * in-flight promise instead of making duplicate API calls.\n\t */\n\tqueryKey?: string;\n\tenabled?: boolean;\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n\trefetchOnMount?: boolean;\n\tinitialData?: TData;\n\tinitialArgs?: TArgs;\n\tdependencies?: readonly unknown[];\n};\n\ntype UseClientQueryResult<TData, TArgs> = {\n\tdata: TData | undefined;\n\terror: Error | null;\n\tisLoading: boolean;\n\trefetch: (args?: TArgs) => Promise<TData | undefined>;\n};\n\nfunction toError(error: unknown): Error {\n\tif (error instanceof Error) {\n\t\treturn error;\n\t}\n\n\treturn new Error(typeof error === \"string\" ? error : \"Unknown error\");\n}\n\nconst EMPTY_DEPENDENCIES: readonly unknown[] = [];\n\n/**\n * Module-level cache for in-flight requests.\n * Maps query keys to their pending promises for deduplication.\n */\nconst inFlightRequests = new Map<string, Promise<unknown>>();\n\n/**\n * Execute a query with deduplication support.\n * If a query with the same key is already in flight, returns the existing promise.\n */\nfunction executeWithDeduplication<TData>(\n\tqueryKey: string | undefined,\n\tqueryFn: () => Promise<TData>\n): Promise<TData> {\n\t// No deduplication if no key provided\n\tif (!queryKey) {\n\t\treturn queryFn();\n\t}\n\n\t// Check for existing in-flight request\n\tconst existing = inFlightRequests.get(queryKey);\n\tif (existing) {\n\t\treturn existing as Promise<TData>;\n\t}\n\n\t// Create new request and track it\n\tconst promise = queryFn().finally(() => {\n\t\t// Clean up after request completes (success or error)\n\t\tinFlightRequests.delete(queryKey);\n\t});\n\n\tinFlightRequests.set(queryKey, promise);\n\treturn promise;\n}\n\n/**\n * Lightweight data-fetching abstraction that plugs into the SDK client instead\n * of React Query. It tracks loading/error state, supports polling, window\n * focus refetching and exposes a typed refetch helper.\n */\nexport function useClientQuery<TData, TArgs = void>(\n\toptions: UseClientQueryOptions<TData, TArgs>\n): UseClientQueryResult<TData, TArgs> {\n\tconst {\n\t\tclient,\n\t\tqueryFn,\n\t\tqueryKey,\n\t\tenabled = true,\n\t\trefetchInterval = false,\n\t\trefetchOnWindowFocus = false,\n\t\trefetchOnMount = true,\n\t\tinitialData,\n\t\tinitialArgs,\n\t\tdependencies = EMPTY_DEPENDENCIES,\n\t} = options;\n\n\tconst [data, setData] = useState<TData | undefined>(initialData);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [isLoading, setIsLoading] = useState(\n\t\tinitialData === undefined && Boolean(enabled)\n\t);\n\n\tconst dataRef = useRef(data);\n\tdataRef.current = data;\n\n\tconst argsRef = useRef<TArgs | undefined>(initialArgs);\n\tconst fetchIdRef = useRef(0);\n\tconst hasMountedRef = useRef(false);\n\tconst hasFetchedRef = useRef(initialData !== undefined);\n\tconst isMountedRef = useRef(true);\n\tconst queryFnRef = useRef(queryFn);\n\n\tqueryFnRef.current = queryFn;\n\n\tuseEffect(() => {\n\t\tisMountedRef.current = true;\n\t\treturn () => {\n\t\t\tisMountedRef.current = false;\n\t\t};\n\t}, []);\n\n\tuseEffect(() => {\n\t\targsRef.current = initialArgs;\n\t}, [initialArgs]);\n\n\tconst execute = useCallback(\n\t\tasync (args?: TArgs, ignoreEnabled = false): Promise<TData | undefined> => {\n\t\t\t// Handle null client (configuration error case)\n\t\t\tif (!client) {\n\t\t\t\treturn dataRef.current;\n\t\t\t}\n\n\t\t\tif (!(enabled || ignoreEnabled)) {\n\t\t\t\treturn dataRef.current;\n\t\t\t}\n\n\t\t\tconst nextArgs = args ?? argsRef.current;\n\t\t\targsRef.current = nextArgs;\n\n\t\t\tconst fetchId = fetchIdRef.current + 1;\n\t\t\tfetchIdRef.current = fetchId;\n\n\t\t\tsetIsLoading(true);\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\t// Use deduplication to share in-flight requests with the same key\n\t\t\t\tconst result = await executeWithDeduplication(queryKey, () =>\n\t\t\t\t\tqueryFnRef.current(client, nextArgs)\n\t\t\t\t);\n\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tdataRef.current = result;\n\t\t\t\tsetData(result);\n\t\t\t\tsetError(null);\n\t\t\t\tsetIsLoading(false);\n\t\t\t\thasFetchedRef.current = true;\n\n\t\t\t\treturn result;\n\t\t\t} catch (raw: unknown) {\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tconst normalized = toError(raw);\n\t\t\t\tsetError(normalized);\n\t\t\t\tsetIsLoading(false);\n\n\t\t\t\tthrow normalized;\n\t\t\t}\n\t\t},\n\t\t[client, enabled, queryKey]\n\t);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\tsetIsLoading(false);\n\t\t\treturn;\n\t\t}\n\n\t\tconst shouldFetchInitially = hasMountedRef.current\n\t\t\t? true\n\t\t\t: refetchOnMount || !hasFetchedRef.current;\n\n\t\thasMountedRef.current = true;\n\n\t\tif (!shouldFetchInitially) {\n\t\t\treturn;\n\t\t}\n\n\t\tvoid execute(argsRef.current);\n\t}, [enabled, execute, refetchOnMount, ...dependencies]);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\trefetchInterval === false ||\n\t\t\trefetchInterval === null ||\n\t\t\trefetchInterval <= 0 ||\n\t\t\ttypeof window === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst timer = window.setInterval(() => {\n\t\t\tvoid execute(argsRef.current);\n\t\t}, refetchInterval);\n\n\t\treturn () => {\n\t\t\twindow.clearInterval(timer);\n\t\t};\n\t}, [enabled, execute, refetchInterval]);\n\n\tuseEffect(() => {\n\t\tif (\n\t\t\t!refetchOnWindowFocus ||\n\t\t\ttypeof window === \"undefined\" ||\n\t\t\ttypeof document === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst handleRefetch = () => {\n\t\t\tif (!enabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvoid execute(argsRef.current);\n\t\t};\n\n\t\tconst onFocus = () => {\n\t\t\tvoid handleRefetch();\n\t\t};\n\n\t\tconst onVisibilityChange = () => {\n\t\t\tif (document.visibilityState === \"visible\") {\n\t\t\t\tvoid handleRefetch();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"focus\", onFocus);\n\t\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"focus\", onFocus);\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t};\n\t}, [enabled, execute, refetchOnWindowFocus]);\n\n\tconst refetch = useCallback(\n\t\tasync (args?: TArgs) => execute(args, true),\n\t\t[execute]\n\t);\n\n\treturn useMemo(\n\t\t() => ({\n\t\t\tdata,\n\t\t\terror,\n\t\t\tisLoading,\n\t\t\trefetch,\n\t\t}),\n\t\t[data, error, isLoading, refetch]\n\t);\n}\n"],"mappings":"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"}
@@ -19,7 +19,8 @@ function isNextJSEnvironment() {
19
19
  */
20
20
  function useClient(publicKey, apiUrl = "https://api.cossistant.com/v1", wsUrl = "wss://api.cossistant.com/ws") {
21
21
  return useMemo(() => {
22
- const keyFromEnv = process.env.NEXT_PUBLIC_COSSISTANT_API_KEY || process.env.COSSISTANT_API_KEY;
22
+ const processEnv = typeof process !== "undefined" ? process.env : void 0;
23
+ const keyFromEnv = processEnv?.NEXT_PUBLIC_COSSISTANT_API_KEY || processEnv?.COSSISTANT_API_KEY;
23
24
  const keyToUse = publicKey ?? keyFromEnv;
24
25
  if (!keyToUse) {
25
26
  const envVarName = isNextJSEnvironment() ? "NEXT_PUBLIC_COSSISTANT_API_KEY" : "COSSISTANT_API_KEY";
@@ -1 +1 @@
1
- {"version":3,"file":"use-rest-client.js","names":["config: CossistantConfig","err: unknown"],"sources":["../../../src/hooks/private/use-rest-client.ts"],"sourcesContent":["\"use client\";\n\nimport { CossistantClient } from \"@cossistant/core\";\nimport type { CossistantConfig } from \"@cossistant/types\";\nimport { useMemo } from \"react\";\n\nexport type ConfigurationError = {\n\ttype: \"missing_api_key\" | \"invalid_api_key\";\n\tmessage: string;\n\tenvVarName: string;\n};\n\nexport type UseClientResult =\n\t| {\n\t\t\tclient: CossistantClient;\n\t\t\terror: null;\n\t\t\tconfigurationError: null;\n\t }\n\t| {\n\t\t\tclient: null;\n\t\t\terror: null;\n\t\t\tconfigurationError: ConfigurationError;\n\t };\n\n/**\n * Detect if running in a Next.js environment.\n */\nfunction isNextJSEnvironment(): boolean {\n\tif (typeof window !== \"undefined\") {\n\t\t// Client-side: check for Next.js data\n\t\treturn \"__NEXT_DATA__\" in window;\n\t}\n\t// Server-side: check for Next.js runtime\n\treturn typeof process !== \"undefined\" && \"__NEXT_RUNTIME\" in process.env;\n}\n\n/**\n * Creates a memoised `CossistantClient` instance using the provided endpoints\n * and public key. When no key is passed the hook falls back to environment\n * variables and surfaces missing configuration errors through the returned\n * `configurationError` field instead of throwing.\n */\nexport function useClient(\n\tpublicKey: string | undefined,\n\tapiUrl = \"https://api.cossistant.com/v1\",\n\twsUrl = \"wss://api.cossistant.com/ws\"\n): UseClientResult {\n\treturn useMemo(() => {\n\t\tconst keyFromEnv =\n\t\t\tprocess.env.NEXT_PUBLIC_COSSISTANT_API_KEY ||\n\t\t\tprocess.env.COSSISTANT_API_KEY;\n\t\tconst keyToUse = publicKey ?? keyFromEnv;\n\n\t\tif (!keyToUse) {\n\t\t\tconst isNextJS = isNextJSEnvironment();\n\t\t\tconst envVarName = isNextJS\n\t\t\t\t? \"NEXT_PUBLIC_COSSISTANT_API_KEY\"\n\t\t\t\t: \"COSSISTANT_API_KEY\";\n\n\t\t\treturn {\n\t\t\t\tclient: null,\n\t\t\t\terror: null,\n\t\t\t\tconfigurationError: {\n\t\t\t\t\ttype: \"missing_api_key\",\n\t\t\t\t\tmessage: `Public API key is required. Add ${envVarName} to your environment variables.`,\n\t\t\t\t\tenvVarName,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst config: CossistantConfig = {\n\t\t\tapiUrl,\n\t\t\twsUrl,\n\t\t\tpublicKey: keyToUse,\n\t\t};\n\n\t\ttry {\n\t\t\tconst client = new CossistantClient(config);\n\t\t\treturn { client, error: null, configurationError: null };\n\t\t} catch (err: unknown) {\n\t\t\tconst isNextJS = isNextJSEnvironment();\n\t\t\tconst envVarName = isNextJS\n\t\t\t\t? \"NEXT_PUBLIC_COSSISTANT_API_KEY\"\n\t\t\t\t: \"COSSISTANT_API_KEY\";\n\n\t\t\treturn {\n\t\t\t\tclient: null,\n\t\t\t\terror: null,\n\t\t\t\tconfigurationError: {\n\t\t\t\t\ttype: \"missing_api_key\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\terr instanceof Error\n\t\t\t\t\t\t\t? err.message\n\t\t\t\t\t\t\t: \"Failed to initialize Cossistant client\",\n\t\t\t\t\tenvVarName,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}, [publicKey, apiUrl, wsUrl]);\n}\n"],"mappings":";;;;;;;;;AA2BA,SAAS,sBAA+B;AACvC,KAAI,OAAO,WAAW,YAErB,QAAO,mBAAmB;AAG3B,QAAO,OAAO,YAAY,eAAe,oBAAoB,QAAQ;;;;;;;;AAStE,SAAgB,UACf,WACA,SAAS,iCACT,QAAQ,+BACU;AAClB,QAAO,cAAc;EACpB,MAAM,aACL,QAAQ,IAAI,kCACZ,QAAQ,IAAI;EACb,MAAM,WAAW,aAAa;AAE9B,MAAI,CAAC,UAAU;GAEd,MAAM,aADW,qBAAqB,GAEnC,mCACA;AAEH,UAAO;IACN,QAAQ;IACR,OAAO;IACP,oBAAoB;KACnB,MAAM;KACN,SAAS,mCAAmC,WAAW;KACvD;KACA;IACD;;EAGF,MAAMA,SAA2B;GAChC;GACA;GACA,WAAW;GACX;AAED,MAAI;AAEH,UAAO;IAAE,QADM,IAAI,iBAAiB,OAAO;IAC1B,OAAO;IAAM,oBAAoB;IAAM;WAChDC,KAAc;GAEtB,MAAM,aADW,qBAAqB,GAEnC,mCACA;AAEH,UAAO;IACN,QAAQ;IACR,OAAO;IACP,oBAAoB;KACnB,MAAM;KACN,SACC,eAAe,QACZ,IAAI,UACJ;KACJ;KACA;IACD;;IAEA;EAAC;EAAW;EAAQ;EAAM,CAAC"}
1
+ {"version":3,"file":"use-rest-client.js","names":["config: CossistantConfig","err: unknown"],"sources":["../../../src/hooks/private/use-rest-client.ts"],"sourcesContent":["\"use client\";\n\nimport { CossistantClient } from \"@cossistant/core\";\nimport type { CossistantConfig } from \"@cossistant/types\";\nimport { useMemo } from \"react\";\n\nexport type ConfigurationError = {\n\ttype: \"missing_api_key\" | \"invalid_api_key\";\n\tmessage: string;\n\tenvVarName: string;\n};\n\nexport type UseClientResult =\n\t| {\n\t\t\tclient: CossistantClient;\n\t\t\terror: null;\n\t\t\tconfigurationError: null;\n\t }\n\t| {\n\t\t\tclient: null;\n\t\t\terror: null;\n\t\t\tconfigurationError: ConfigurationError;\n\t };\n\n/**\n * Detect if running in a Next.js environment.\n */\nfunction isNextJSEnvironment(): boolean {\n\tif (typeof window !== \"undefined\") {\n\t\t// Client-side: check for Next.js data\n\t\treturn \"__NEXT_DATA__\" in window;\n\t}\n\t// Server-side: check for Next.js runtime\n\treturn typeof process !== \"undefined\" && \"__NEXT_RUNTIME\" in process.env;\n}\n\n/**\n * Creates a memoised `CossistantClient` instance using the provided endpoints\n * and public key. When no key is passed the hook falls back to environment\n * variables and surfaces missing configuration errors through the returned\n * `configurationError` field instead of throwing.\n */\nexport function useClient(\n\tpublicKey: string | undefined,\n\tapiUrl = \"https://api.cossistant.com/v1\",\n\twsUrl = \"wss://api.cossistant.com/ws\"\n): UseClientResult {\n\treturn useMemo(() => {\n\t\tconst processEnv = typeof process !== \"undefined\" ? process.env : undefined;\n\t\tconst keyFromEnv =\n\t\t\tprocessEnv?.NEXT_PUBLIC_COSSISTANT_API_KEY ||\n\t\t\tprocessEnv?.COSSISTANT_API_KEY;\n\t\tconst keyToUse = publicKey ?? keyFromEnv;\n\n\t\tif (!keyToUse) {\n\t\t\tconst isNextJS = isNextJSEnvironment();\n\t\t\tconst envVarName = isNextJS\n\t\t\t\t? \"NEXT_PUBLIC_COSSISTANT_API_KEY\"\n\t\t\t\t: \"COSSISTANT_API_KEY\";\n\n\t\t\treturn {\n\t\t\t\tclient: null,\n\t\t\t\terror: null,\n\t\t\t\tconfigurationError: {\n\t\t\t\t\ttype: \"missing_api_key\",\n\t\t\t\t\tmessage: `Public API key is required. Add ${envVarName} to your environment variables.`,\n\t\t\t\t\tenvVarName,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst config: CossistantConfig = {\n\t\t\tapiUrl,\n\t\t\twsUrl,\n\t\t\tpublicKey: keyToUse,\n\t\t};\n\n\t\ttry {\n\t\t\tconst client = new CossistantClient(config);\n\t\t\treturn { client, error: null, configurationError: null };\n\t\t} catch (err: unknown) {\n\t\t\tconst isNextJS = isNextJSEnvironment();\n\t\t\tconst envVarName = isNextJS\n\t\t\t\t? \"NEXT_PUBLIC_COSSISTANT_API_KEY\"\n\t\t\t\t: \"COSSISTANT_API_KEY\";\n\n\t\t\treturn {\n\t\t\t\tclient: null,\n\t\t\t\terror: null,\n\t\t\t\tconfigurationError: {\n\t\t\t\t\ttype: \"missing_api_key\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\terr instanceof Error\n\t\t\t\t\t\t\t? err.message\n\t\t\t\t\t\t\t: \"Failed to initialize Cossistant client\",\n\t\t\t\t\tenvVarName,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}, [publicKey, apiUrl, wsUrl]);\n}\n"],"mappings":";;;;;;;;;AA2BA,SAAS,sBAA+B;AACvC,KAAI,OAAO,WAAW,YAErB,QAAO,mBAAmB;AAG3B,QAAO,OAAO,YAAY,eAAe,oBAAoB,QAAQ;;;;;;;;AAStE,SAAgB,UACf,WACA,SAAS,iCACT,QAAQ,+BACU;AAClB,QAAO,cAAc;EACpB,MAAM,aAAa,OAAO,YAAY,cAAc,QAAQ,MAAM;EAClE,MAAM,aACL,YAAY,kCACZ,YAAY;EACb,MAAM,WAAW,aAAa;AAE9B,MAAI,CAAC,UAAU;GAEd,MAAM,aADW,qBAAqB,GAEnC,mCACA;AAEH,UAAO;IACN,QAAQ;IACR,OAAO;IACP,oBAAoB;KACnB,MAAM;KACN,SAAS,mCAAmC,WAAW;KACvD;KACA;IACD;;EAGF,MAAMA,SAA2B;GAChC;GACA;GACA,WAAW;GACX;AAED,MAAI;AAEH,UAAO;IAAE,QADM,IAAI,iBAAiB,OAAO;IAC1B,OAAO;IAAM,oBAAoB;IAAM;WAChDC,KAAc;GAEtB,MAAM,aADW,qBAAqB,GAEnC,mCACA;AAEH,UAAO;IACN,QAAQ;IACR,OAAO;IACP,oBAAoB;KACnB,MAAM;KACN,SACC,eAAe,QACZ,IAAI,UACJ;KACJ;KACA;IACD;;IAEA;EAAC;EAAW;EAAQ;EAAM,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-conversation-auto-seen.d.ts","names":[],"sources":["../../src/hooks/use-conversation-auto-seen.ts"],"sourcesContent":[],"mappings":";;;;cASa,+BAAA;KAED,8BAAA;EAFC;AAEb;AA4DA;UAxDS;;;;;;;;;;;;;oBAgBU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCH,uBAAA,UACN"}
1
+ {"version":3,"file":"use-conversation-auto-seen.d.ts","names":[],"sources":["../../src/hooks/use-conversation-auto-seen.ts"],"sourcesContent":[],"mappings":";;;;cASa,+BAAA;KAMD,8BAAA;EANC;AAMb;AA4DA;UAxDS;;;;;;;;;;;;;oBAgBU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCH,uBAAA,UACN"}
@@ -1,9 +1,13 @@
1
- import { hydrateConversationSeen, upsertConversationSeen } from "../realtime/seen-store.js";
1
+ import { hydrateConversationSeen as hydrateConversationSeen$1, upsertConversationSeen as upsertConversationSeen$1 } from "../realtime/seen-store.js";
2
2
  import { useWindowVisibilityFocus } from "./use-window-visibility-focus.js";
3
3
  import { useEffect, useRef } from "react";
4
+ import { CossistantAPIError } from "@cossistant/core";
4
5
 
5
6
  //#region src/hooks/use-conversation-auto-seen.ts
6
7
  const CONVERSATION_AUTO_SEEN_DELAY_MS = 2e3;
8
+ function isNotFoundError(error) {
9
+ return error instanceof CossistantAPIError && error.code === "HTTP_404";
10
+ }
7
11
  /**
8
12
  * Automatically marks timeline items as seen when:
9
13
  * - A new timeline item arrives from someone else
@@ -53,8 +57,9 @@ function useConversationAutoSeen(options) {
53
57
  }, [isWidgetOpen]);
54
58
  useEffect(() => {
55
59
  if (enabled && client && conversationId) client.getConversationSeenData({ conversationId }).then((response) => {
56
- if (response.seenData.length > 0) hydrateConversationSeen(conversationId, response.seenData);
60
+ if (response.seenData.length > 0) hydrateConversationSeen$1(conversationId, response.seenData);
57
61
  }).catch((err) => {
62
+ if (isNotFoundError(err)) return;
58
63
  console.error("Failed to fetch conversation seen data:", err);
59
64
  });
60
65
  }, [
@@ -94,13 +99,14 @@ function useConversationAutoSeen(options) {
94
99
  markSeenInFlightRef.current = true;
95
100
  client.markConversationSeen({ conversationId }).then((response) => {
96
101
  lastSeenItemIdRef.current = pendingItemId;
97
- upsertConversationSeen({
102
+ upsertConversationSeen$1({
98
103
  conversationId,
99
104
  actorType: "visitor",
100
105
  actorId: visitorId,
101
106
  lastSeenAt: new Date(response.lastSeenAt)
102
107
  });
103
108
  }).catch((err) => {
109
+ if (isNotFoundError(err)) return;
104
110
  console.error("Failed to mark conversation as seen:", err);
105
111
  }).finally(() => {
106
112
  markSeenInFlightRef.current = false;
@@ -1 +1 @@
1
- {"version":3,"file":"use-conversation-auto-seen.js","names":[],"sources":["../../src/hooks/use-conversation-auto-seen.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { useEffect, useRef } from \"react\";\nimport {\n\thydrateConversationSeen,\n\tupsertConversationSeen,\n} from \"../realtime/seen-store\";\nimport { useWindowVisibilityFocus } from \"./use-window-visibility-focus\";\n\nexport const CONVERSATION_AUTO_SEEN_DELAY_MS = 2000;\n\nexport type UseConversationAutoSeenOptions = {\n\t/**\n\t * The Cossistant client instance.\n\t */\n\tclient: CossistantClient | null;\n\n\t/**\n\t * The real conversation ID. Pass null if no conversation exists yet.\n\t */\n\tconversationId: string | null;\n\n\t/**\n\t * Current visitor ID.\n\t */\n\tvisitorId?: string;\n\n\t/**\n\t * The last timeline item in the conversation.\n\t * Used to determine if we should mark as seen.\n\t */\n\tlastTimelineItem: TimelineItem | null;\n\n\t/**\n\t * Whether to enable auto-seen tracking.\n\t * Default: true\n\t */\n\tenabled?: boolean;\n\n\t/**\n\t * Whether the support widget is currently open/visible.\n\t * This is required to ensure we only mark conversations as seen when\n\t * the widget is actually visible to the user.\n\t * Default: true\n\t */\n\tisWidgetOpen?: boolean;\n};\n\n/**\n * Automatically marks timeline items as seen when:\n * - A new timeline item arrives from someone else\n * - The page is visible\n * - The support widget is open/visible\n * - The visitor is the current user\n *\n * Also handles:\n * - Fetching and hydrating initial seen data\n * - Preventing duplicate API calls\n * - Page visibility tracking\n * - Widget visibility tracking\n *\n * @example\n * ```tsx\n * useConversationAutoSeen({\n * client,\n * conversationId: realConversationId,\n * visitorId: visitor?.id,\n * lastTimelineItem: items[items.length - 1] ?? null,\n * });\n * ```\n */\nexport function useConversationAutoSeen(\n\toptions: UseConversationAutoSeenOptions\n): void {\n\tconst {\n\t\tclient,\n\t\tconversationId,\n\t\tvisitorId,\n\t\tlastTimelineItem,\n\t\tenabled = true,\n\t\tisWidgetOpen = true,\n\t} = options;\n\n\tconst lastSeenItemIdRef = useRef<string | null>(null);\n\tconst markSeenInFlightRef = useRef(false);\n\tconst markSeenTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst { isPageVisible } = useWindowVisibilityFocus();\n\n\t// Reset seen tracking when conversation changes\n\tuseEffect(() => {\n\t\tlastSeenItemIdRef.current = null;\n\t\tmarkSeenInFlightRef.current = false;\n\t\tif (markSeenTimeoutRef.current) {\n\t\t\tclearTimeout(markSeenTimeoutRef.current);\n\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t}\n\t}, [conversationId]);\n\n\t// Clear timeout immediately when widget closes and reset tracking\n\tuseEffect(() => {\n\t\tif (!isWidgetOpen) {\n\t\t\tif (markSeenTimeoutRef.current) {\n\t\t\t\tclearTimeout(markSeenTimeoutRef.current);\n\t\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t\t}\n\t\t\tmarkSeenInFlightRef.current = false;\n\t\t\t// Reset last seen item ID so we don't skip marking when widget reopens\n\t\t\t// This ensures we check again when the widget is reopened\n\t\t\tlastSeenItemIdRef.current = null;\n\t\t}\n\t}, [isWidgetOpen]);\n\n\t// Fetch and hydrate initial seen data when conversation loads\n\tuseEffect(() => {\n\t\tif (enabled && client && conversationId) {\n\t\t\tvoid client\n\t\t\t\t.getConversationSeenData({ conversationId })\n\t\t\t\t.then((response) => {\n\t\t\t\t\tif (response.seenData.length > 0) {\n\t\t\t\t\t\thydrateConversationSeen(conversationId, response.seenData);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tconsole.error(\"Failed to fetch conversation seen data:\", err);\n\t\t\t\t});\n\t\t}\n\t}, [enabled, client, conversationId]);\n\n\t// Auto-mark timeline items as seen\n\tuseEffect(() => {\n\t\tconst canMarkSeen =\n\t\t\tenabled &&\n\t\t\tisWidgetOpen &&\n\t\t\tclient &&\n\t\t\tconversationId &&\n\t\t\tvisitorId &&\n\t\t\tlastTimelineItem &&\n\t\t\tisPageVisible;\n\n\t\tif (!canMarkSeen) {\n\t\t\tif (markSeenTimeoutRef.current) {\n\t\t\t\tclearTimeout(markSeenTimeoutRef.current);\n\t\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (markSeenTimeoutRef.current) {\n\t\t\tclearTimeout(markSeenTimeoutRef.current);\n\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t}\n\n\t\t// Don't mark our own timeline items as seen via API (we already know we saw them)\n\t\tif (lastTimelineItem.visitorId === visitorId) {\n\t\t\tlastSeenItemIdRef.current = lastTimelineItem.id || null;\n\t\t\treturn;\n\t\t}\n\n\t\t// Already marked this item\n\t\tif (lastSeenItemIdRef.current === lastTimelineItem.id) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst pendingItemId = lastTimelineItem.id || null;\n\n\t\tmarkSeenTimeoutRef.current = setTimeout(() => {\n\t\t\tconst attemptMarkSeen = () => {\n\t\t\t\tconst stillCanMark =\n\t\t\t\t\tenabled &&\n\t\t\t\t\tisWidgetOpen &&\n\t\t\t\t\tclient &&\n\t\t\t\t\tconversationId &&\n\t\t\t\t\tvisitorId &&\n\t\t\t\t\tisPageVisible;\n\n\t\t\t\tif (!stillCanMark) {\n\t\t\t\t\tmarkSeenInFlightRef.current = false;\n\t\t\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (markSeenInFlightRef.current) {\n\t\t\t\t\tmarkSeenTimeoutRef.current = setTimeout(attemptMarkSeen, 100);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tmarkSeenInFlightRef.current = true;\n\n\t\t\t\tclient\n\t\t\t\t\t.markConversationSeen({ conversationId })\n\t\t\t\t\t.then((response) => {\n\t\t\t\t\t\tlastSeenItemIdRef.current = pendingItemId;\n\n\t\t\t\t\t\t// Optimistically update local seen store\n\t\t\t\t\t\tupsertConversationSeen({\n\t\t\t\t\t\t\tconversationId,\n\t\t\t\t\t\t\tactorType: \"visitor\",\n\t\t\t\t\t\t\tactorId: visitorId,\n\t\t\t\t\t\t\tlastSeenAt: new Date(response.lastSeenAt),\n\t\t\t\t\t\t});\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err) => {\n\t\t\t\t\t\tconsole.error(\"Failed to mark conversation as seen:\", err);\n\t\t\t\t\t})\n\t\t\t\t\t.finally(() => {\n\t\t\t\t\t\tmarkSeenInFlightRef.current = false;\n\t\t\t\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t\t\t\t});\n\t\t\t};\n\n\t\t\tattemptMarkSeen();\n\t\t}, CONVERSATION_AUTO_SEEN_DELAY_MS);\n\n\t\treturn () => {\n\t\t\tif (markSeenTimeoutRef.current) {\n\t\t\t\tclearTimeout(markSeenTimeoutRef.current);\n\t\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t\t}\n\t\t};\n\t}, [\n\t\tenabled,\n\t\tisWidgetOpen,\n\t\tclient,\n\t\tconversationId,\n\t\tvisitorId,\n\t\tlastTimelineItem,\n\t\tisPageVisible,\n\t]);\n}\n"],"mappings":";;;;;AASA,MAAa,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;AA8D/C,SAAgB,wBACf,SACO;CACP,MAAM,EACL,QACA,gBACA,WACA,kBACA,UAAU,MACV,eAAe,SACZ;CAEJ,MAAM,oBAAoB,OAAsB,KAAK;CACrD,MAAM,sBAAsB,OAAO,MAAM;CACzC,MAAM,qBAAqB,OAA6C,KAAK;CAC7E,MAAM,EAAE,kBAAkB,0BAA0B;AAGpD,iBAAgB;AACf,oBAAkB,UAAU;AAC5B,sBAAoB,UAAU;AAC9B,MAAI,mBAAmB,SAAS;AAC/B,gBAAa,mBAAmB,QAAQ;AACxC,sBAAmB,UAAU;;IAE5B,CAAC,eAAe,CAAC;AAGpB,iBAAgB;AACf,MAAI,CAAC,cAAc;AAClB,OAAI,mBAAmB,SAAS;AAC/B,iBAAa,mBAAmB,QAAQ;AACxC,uBAAmB,UAAU;;AAE9B,uBAAoB,UAAU;AAG9B,qBAAkB,UAAU;;IAE3B,CAAC,aAAa,CAAC;AAGlB,iBAAgB;AACf,MAAI,WAAW,UAAU,eACxB,CAAK,OACH,wBAAwB,EAAE,gBAAgB,CAAC,CAC3C,MAAM,aAAa;AACnB,OAAI,SAAS,SAAS,SAAS,EAC9B,yBAAwB,gBAAgB,SAAS,SAAS;IAE1D,CACD,OAAO,QAAQ;AACf,WAAQ,MAAM,2CAA2C,IAAI;IAC5D;IAEF;EAAC;EAAS;EAAQ;EAAe,CAAC;AAGrC,iBAAgB;AAUf,MAAI,EARH,WACA,gBACA,UACA,kBACA,aACA,oBACA,gBAEiB;AACjB,OAAI,mBAAmB,SAAS;AAC/B,iBAAa,mBAAmB,QAAQ;AACxC,uBAAmB,UAAU;;AAE9B;;AAGD,MAAI,mBAAmB,SAAS;AAC/B,gBAAa,mBAAmB,QAAQ;AACxC,sBAAmB,UAAU;;AAI9B,MAAI,iBAAiB,cAAc,WAAW;AAC7C,qBAAkB,UAAU,iBAAiB,MAAM;AACnD;;AAID,MAAI,kBAAkB,YAAY,iBAAiB,GAClD;EAGD,MAAM,gBAAgB,iBAAiB,MAAM;AAE7C,qBAAmB,UAAU,iBAAiB;GAC7C,MAAM,wBAAwB;AAS7B,QAAI,EAPH,WACA,gBACA,UACA,kBACA,aACA,gBAEkB;AAClB,yBAAoB,UAAU;AAC9B,wBAAmB,UAAU;AAC7B;;AAGD,QAAI,oBAAoB,SAAS;AAChC,wBAAmB,UAAU,WAAW,iBAAiB,IAAI;AAC7D;;AAGD,wBAAoB,UAAU;AAE9B,WACE,qBAAqB,EAAE,gBAAgB,CAAC,CACxC,MAAM,aAAa;AACnB,uBAAkB,UAAU;AAG5B,4BAAuB;MACtB;MACA,WAAW;MACX,SAAS;MACT,YAAY,IAAI,KAAK,SAAS,WAAW;MACzC,CAAC;MACD,CACD,OAAO,QAAQ;AACf,aAAQ,MAAM,wCAAwC,IAAI;MACzD,CACD,cAAc;AACd,yBAAoB,UAAU;AAC9B,wBAAmB,UAAU;MAC5B;;AAGJ,oBAAiB;KACf,gCAAgC;AAEnC,eAAa;AACZ,OAAI,mBAAmB,SAAS;AAC/B,iBAAa,mBAAmB,QAAQ;AACxC,uBAAmB,UAAU;;;IAG7B;EACF;EACA;EACA;EACA;EACA;EACA;EACA;EACA,CAAC"}
1
+ {"version":3,"file":"use-conversation-auto-seen.js","names":[],"sources":["../../src/hooks/use-conversation-auto-seen.ts"],"sourcesContent":["import { CossistantAPIError, type CossistantClient } from \"@cossistant/core\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { useEffect, useRef } from \"react\";\nimport {\n\thydrateConversationSeen,\n\tupsertConversationSeen,\n} from \"../realtime/seen-store\";\nimport { useWindowVisibilityFocus } from \"./use-window-visibility-focus\";\n\nexport const CONVERSATION_AUTO_SEEN_DELAY_MS = 2000;\n\nfunction isNotFoundError(error: unknown): boolean {\n\treturn error instanceof CossistantAPIError && error.code === \"HTTP_404\";\n}\n\nexport type UseConversationAutoSeenOptions = {\n\t/**\n\t * The Cossistant client instance.\n\t */\n\tclient: CossistantClient | null;\n\n\t/**\n\t * The real conversation ID. Pass null if no conversation exists yet.\n\t */\n\tconversationId: string | null;\n\n\t/**\n\t * Current visitor ID.\n\t */\n\tvisitorId?: string;\n\n\t/**\n\t * The last timeline item in the conversation.\n\t * Used to determine if we should mark as seen.\n\t */\n\tlastTimelineItem: TimelineItem | null;\n\n\t/**\n\t * Whether to enable auto-seen tracking.\n\t * Default: true\n\t */\n\tenabled?: boolean;\n\n\t/**\n\t * Whether the support widget is currently open/visible.\n\t * This is required to ensure we only mark conversations as seen when\n\t * the widget is actually visible to the user.\n\t * Default: true\n\t */\n\tisWidgetOpen?: boolean;\n};\n\n/**\n * Automatically marks timeline items as seen when:\n * - A new timeline item arrives from someone else\n * - The page is visible\n * - The support widget is open/visible\n * - The visitor is the current user\n *\n * Also handles:\n * - Fetching and hydrating initial seen data\n * - Preventing duplicate API calls\n * - Page visibility tracking\n * - Widget visibility tracking\n *\n * @example\n * ```tsx\n * useConversationAutoSeen({\n * client,\n * conversationId: realConversationId,\n * visitorId: visitor?.id,\n * lastTimelineItem: items[items.length - 1] ?? null,\n * });\n * ```\n */\nexport function useConversationAutoSeen(\n\toptions: UseConversationAutoSeenOptions\n): void {\n\tconst {\n\t\tclient,\n\t\tconversationId,\n\t\tvisitorId,\n\t\tlastTimelineItem,\n\t\tenabled = true,\n\t\tisWidgetOpen = true,\n\t} = options;\n\n\tconst lastSeenItemIdRef = useRef<string | null>(null);\n\tconst markSeenInFlightRef = useRef(false);\n\tconst markSeenTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst { isPageVisible } = useWindowVisibilityFocus();\n\n\t// Reset seen tracking when conversation changes\n\tuseEffect(() => {\n\t\tlastSeenItemIdRef.current = null;\n\t\tmarkSeenInFlightRef.current = false;\n\t\tif (markSeenTimeoutRef.current) {\n\t\t\tclearTimeout(markSeenTimeoutRef.current);\n\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t}\n\t}, [conversationId]);\n\n\t// Clear timeout immediately when widget closes and reset tracking\n\tuseEffect(() => {\n\t\tif (!isWidgetOpen) {\n\t\t\tif (markSeenTimeoutRef.current) {\n\t\t\t\tclearTimeout(markSeenTimeoutRef.current);\n\t\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t\t}\n\t\t\tmarkSeenInFlightRef.current = false;\n\t\t\t// Reset last seen item ID so we don't skip marking when widget reopens\n\t\t\t// This ensures we check again when the widget is reopened\n\t\t\tlastSeenItemIdRef.current = null;\n\t\t}\n\t}, [isWidgetOpen]);\n\n\t// Fetch and hydrate initial seen data when conversation loads\n\tuseEffect(() => {\n\t\tif (enabled && client && conversationId) {\n\t\t\tvoid client\n\t\t\t\t.getConversationSeenData({ conversationId })\n\t\t\t\t.then((response) => {\n\t\t\t\t\tif (response.seenData.length > 0) {\n\t\t\t\t\t\thydrateConversationSeen(conversationId, response.seenData);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tif (isNotFoundError(err)) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconsole.error(\"Failed to fetch conversation seen data:\", err);\n\t\t\t\t});\n\t\t}\n\t}, [enabled, client, conversationId]);\n\n\t// Auto-mark timeline items as seen\n\tuseEffect(() => {\n\t\tconst canMarkSeen =\n\t\t\tenabled &&\n\t\t\tisWidgetOpen &&\n\t\t\tclient &&\n\t\t\tconversationId &&\n\t\t\tvisitorId &&\n\t\t\tlastTimelineItem &&\n\t\t\tisPageVisible;\n\n\t\tif (!canMarkSeen) {\n\t\t\tif (markSeenTimeoutRef.current) {\n\t\t\t\tclearTimeout(markSeenTimeoutRef.current);\n\t\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (markSeenTimeoutRef.current) {\n\t\t\tclearTimeout(markSeenTimeoutRef.current);\n\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t}\n\n\t\t// Don't mark our own timeline items as seen via API (we already know we saw them)\n\t\tif (lastTimelineItem.visitorId === visitorId) {\n\t\t\tlastSeenItemIdRef.current = lastTimelineItem.id || null;\n\t\t\treturn;\n\t\t}\n\n\t\t// Already marked this item\n\t\tif (lastSeenItemIdRef.current === lastTimelineItem.id) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst pendingItemId = lastTimelineItem.id || null;\n\n\t\tmarkSeenTimeoutRef.current = setTimeout(() => {\n\t\t\tconst attemptMarkSeen = () => {\n\t\t\t\tconst stillCanMark =\n\t\t\t\t\tenabled &&\n\t\t\t\t\tisWidgetOpen &&\n\t\t\t\t\tclient &&\n\t\t\t\t\tconversationId &&\n\t\t\t\t\tvisitorId &&\n\t\t\t\t\tisPageVisible;\n\n\t\t\t\tif (!stillCanMark) {\n\t\t\t\t\tmarkSeenInFlightRef.current = false;\n\t\t\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (markSeenInFlightRef.current) {\n\t\t\t\t\tmarkSeenTimeoutRef.current = setTimeout(attemptMarkSeen, 100);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tmarkSeenInFlightRef.current = true;\n\n\t\t\t\tclient\n\t\t\t\t\t.markConversationSeen({ conversationId })\n\t\t\t\t\t.then((response) => {\n\t\t\t\t\t\tlastSeenItemIdRef.current = pendingItemId;\n\n\t\t\t\t\t\t// Optimistically update local seen store\n\t\t\t\t\t\tupsertConversationSeen({\n\t\t\t\t\t\t\tconversationId,\n\t\t\t\t\t\t\tactorType: \"visitor\",\n\t\t\t\t\t\t\tactorId: visitorId,\n\t\t\t\t\t\t\tlastSeenAt: new Date(response.lastSeenAt),\n\t\t\t\t\t\t});\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err) => {\n\t\t\t\t\t\tif (isNotFoundError(err)) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconsole.error(\"Failed to mark conversation as seen:\", err);\n\t\t\t\t\t})\n\t\t\t\t\t.finally(() => {\n\t\t\t\t\t\tmarkSeenInFlightRef.current = false;\n\t\t\t\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t\t\t\t});\n\t\t\t};\n\n\t\t\tattemptMarkSeen();\n\t\t}, CONVERSATION_AUTO_SEEN_DELAY_MS);\n\n\t\treturn () => {\n\t\t\tif (markSeenTimeoutRef.current) {\n\t\t\t\tclearTimeout(markSeenTimeoutRef.current);\n\t\t\t\tmarkSeenTimeoutRef.current = null;\n\t\t\t}\n\t\t};\n\t}, [\n\t\tenabled,\n\t\tisWidgetOpen,\n\t\tclient,\n\t\tconversationId,\n\t\tvisitorId,\n\t\tlastTimelineItem,\n\t\tisPageVisible,\n\t]);\n}\n"],"mappings":";;;;;;AASA,MAAa,kCAAkC;AAE/C,SAAS,gBAAgB,OAAyB;AACjD,QAAO,iBAAiB,sBAAsB,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;AA+D9D,SAAgB,wBACf,SACO;CACP,MAAM,EACL,QACA,gBACA,WACA,kBACA,UAAU,MACV,eAAe,SACZ;CAEJ,MAAM,oBAAoB,OAAsB,KAAK;CACrD,MAAM,sBAAsB,OAAO,MAAM;CACzC,MAAM,qBAAqB,OAA6C,KAAK;CAC7E,MAAM,EAAE,kBAAkB,0BAA0B;AAGpD,iBAAgB;AACf,oBAAkB,UAAU;AAC5B,sBAAoB,UAAU;AAC9B,MAAI,mBAAmB,SAAS;AAC/B,gBAAa,mBAAmB,QAAQ;AACxC,sBAAmB,UAAU;;IAE5B,CAAC,eAAe,CAAC;AAGpB,iBAAgB;AACf,MAAI,CAAC,cAAc;AAClB,OAAI,mBAAmB,SAAS;AAC/B,iBAAa,mBAAmB,QAAQ;AACxC,uBAAmB,UAAU;;AAE9B,uBAAoB,UAAU;AAG9B,qBAAkB,UAAU;;IAE3B,CAAC,aAAa,CAAC;AAGlB,iBAAgB;AACf,MAAI,WAAW,UAAU,eACxB,CAAK,OACH,wBAAwB,EAAE,gBAAgB,CAAC,CAC3C,MAAM,aAAa;AACnB,OAAI,SAAS,SAAS,SAAS,EAC9B,2BAAwB,gBAAgB,SAAS,SAAS;IAE1D,CACD,OAAO,QAAQ;AACf,OAAI,gBAAgB,IAAI,CACvB;AAGD,WAAQ,MAAM,2CAA2C,IAAI;IAC5D;IAEF;EAAC;EAAS;EAAQ;EAAe,CAAC;AAGrC,iBAAgB;AAUf,MAAI,EARH,WACA,gBACA,UACA,kBACA,aACA,oBACA,gBAEiB;AACjB,OAAI,mBAAmB,SAAS;AAC/B,iBAAa,mBAAmB,QAAQ;AACxC,uBAAmB,UAAU;;AAE9B;;AAGD,MAAI,mBAAmB,SAAS;AAC/B,gBAAa,mBAAmB,QAAQ;AACxC,sBAAmB,UAAU;;AAI9B,MAAI,iBAAiB,cAAc,WAAW;AAC7C,qBAAkB,UAAU,iBAAiB,MAAM;AACnD;;AAID,MAAI,kBAAkB,YAAY,iBAAiB,GAClD;EAGD,MAAM,gBAAgB,iBAAiB,MAAM;AAE7C,qBAAmB,UAAU,iBAAiB;GAC7C,MAAM,wBAAwB;AAS7B,QAAI,EAPH,WACA,gBACA,UACA,kBACA,aACA,gBAEkB;AAClB,yBAAoB,UAAU;AAC9B,wBAAmB,UAAU;AAC7B;;AAGD,QAAI,oBAAoB,SAAS;AAChC,wBAAmB,UAAU,WAAW,iBAAiB,IAAI;AAC7D;;AAGD,wBAAoB,UAAU;AAE9B,WACE,qBAAqB,EAAE,gBAAgB,CAAC,CACxC,MAAM,aAAa;AACnB,uBAAkB,UAAU;AAG5B,8BAAuB;MACtB;MACA,WAAW;MACX,SAAS;MACT,YAAY,IAAI,KAAK,SAAS,WAAW;MACzC,CAAC;MACD,CACD,OAAO,QAAQ;AACf,SAAI,gBAAgB,IAAI,CACvB;AAGD,aAAQ,MAAM,wCAAwC,IAAI;MACzD,CACD,cAAc;AACd,yBAAoB,UAAU;AAC9B,wBAAmB,UAAU;MAC5B;;AAGJ,oBAAiB;KACf,gCAAgC;AAEnC,eAAa;AACZ,OAAI,mBAAmB,SAAS;AAC/B,iBAAa,mBAAmB,QAAQ;AACxC,uBAAmB,UAAU;;;IAG7B;EACF;EACA;EACA;EACA;EACA;EACA;EACA;EACA,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-conversation-page.d.ts","names":[],"sources":["../../src/hooks/use-conversation-page.ts"],"sourcesContent":[],"mappings":";;;KAgBY,0BAAA;;AAAZ;AA+BA;;EAMQ,cAAA,EAAA,MAAA;EAKC;;;EAYsB,cAAA,CAAA,EAAA,MAAA;EAuCf;;;;;;;;UAxEP;;;;;;;;KAUG,yBAAA;;;SAIJ;;SAEA;;;WAKC;;;;;sBAKW;;;;;oBAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuCH,mBAAA,UACN,6BACP"}
1
+ {"version":3,"file":"use-conversation-page.d.ts","names":[],"sources":["../../src/hooks/use-conversation-page.ts"],"sourcesContent":[],"mappings":";;;KAgBY,0BAAA;;AAAZ;AA+BA;;EAMQ,cAAA,EAAA,MAAA;EAKC;;;EAYsB,cAAA,CAAA,EAAA,MAAA;EA2Cf;;;;;;;;UA5EP;;;;;;;;KAUG,yBAAA;;;SAIJ;;SAEA;;;WAKC;;;;;sBAKW;;;;;oBAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2CH,mBAAA,UACN,6BACP"}
@@ -7,9 +7,13 @@ import { useMessageComposer } from "./use-message-composer.js";
7
7
  import { useSupport } from "../provider.js";
8
8
  import { useDefaultMessages } from "./private/use-default-messages.js";
9
9
  import { useEffect, useMemo, useRef } from "react";
10
+ import { CossistantAPIError } from "@cossistant/core";
10
11
  import { ConversationTimelineType, TimelineItemVisibility } from "@cossistant/types/enums";
11
12
 
12
13
  //#region src/hooks/use-conversation-page.ts
14
+ function isNotFoundError(error) {
15
+ return error instanceof CossistantAPIError && error.code === "HTTP_404";
16
+ }
13
17
  /**
14
18
  * Main orchestrator hook for the conversation page.
15
19
  *
@@ -59,7 +63,9 @@ function useConversationPage(options) {
59
63
  });
60
64
  const defaultTimelineItems = useDefaultMessages({ conversationId: lifecycle.conversationId });
61
65
  const effectiveDefaultTimelineItems = hasInitialMessage ? [] : defaultTimelineItems;
62
- const timelineQuery = useConversationTimelineItems(lifecycle.conversationId, { enabled: !lifecycle.isPending });
66
+ const isPendingConversationBootstrap = Boolean(lifecycle.realConversationId && client?.isConversationPending(lifecycle.realConversationId));
67
+ const shouldEnableConversationNetworkSync = Boolean(lifecycle.realConversationId) && !isPendingConversationBootstrap;
68
+ const timelineQuery = useConversationTimelineItems(lifecycle.conversationId, { enabled: shouldEnableConversationNetworkSync });
63
69
  const baseItems = useMemo(() => {
64
70
  if (timelineQuery.items.length > 0) return timelineQuery.items;
65
71
  if (lifecycle.isPending && effectiveDefaultTimelineItems.length > 0) return effectiveDefaultTimelineItems;
@@ -111,6 +117,10 @@ function useConversationPage(options) {
111
117
  visitor?.id
112
118
  ]);
113
119
  const lastTimelineItem = useMemo(() => displayItems.at(-1) ?? null, [displayItems]);
120
+ const timelineError = useMemo(() => {
121
+ if (isPendingConversationBootstrap && isNotFoundError(timelineQuery.error)) return null;
122
+ return timelineQuery.error;
123
+ }, [isPendingConversationBootstrap, timelineQuery.error]);
114
124
  const composer = useMessageComposer({
115
125
  client: client ?? void 0,
116
126
  conversationId: lifecycle.realConversationId,
@@ -160,7 +170,7 @@ function useConversationPage(options) {
160
170
  conversationId: lifecycle.realConversationId,
161
171
  visitorId: visitor?.id,
162
172
  lastTimelineItem,
163
- enabled: autoSeenEnabled,
173
+ enabled: autoSeenEnabled && shouldEnableConversationNetworkSync,
164
174
  isWidgetOpen: autoSeenEnabled
165
175
  });
166
176
  return {
@@ -168,7 +178,7 @@ function useConversationPage(options) {
168
178
  isPending: lifecycle.isPending,
169
179
  items: displayItems,
170
180
  isLoading: timelineQuery.isLoading,
171
- error: timelineQuery.error || composer.error,
181
+ error: timelineError || composer.error,
172
182
  composer: {
173
183
  message: composer.message,
174
184
  files: composer.files,
@@ -1 +1 @@
1
- {"version":3,"file":"use-conversation-page.js","names":["identificationItem: TimelineItem"],"sources":["../../src/hooks/use-conversation-page.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport {\n\tConversationTimelineType,\n\tTimelineItemVisibility,\n} from \"@cossistant/types/enums\";\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { useSupport } from \"../provider\";\nimport { useIdentificationState } from \"../support/context/identification\";\nimport { useWebSocketSafe } from \"../support/context/websocket\";\nimport { useDefaultMessages } from \"./private/use-default-messages\";\nimport { useConversationAutoSeen } from \"./use-conversation-auto-seen\";\nimport { useConversationLifecycle } from \"./use-conversation-lifecycle\";\nimport { useConversationTimelineItems } from \"./use-conversation-timeline-items\";\nimport { useMessageComposer } from \"./use-message-composer\";\n\nexport type UseConversationPageOptions = {\n\t/**\n\t * Initial conversation ID (from URL params, navigation state, etc.)\n\t * Can be PENDING_CONVERSATION_ID or a real ID.\n\t */\n\tconversationId: string;\n\n\t/**\n\t * Optional initial message to send when the conversation opens.\n\t */\n\tinitialMessage?: string;\n\n\t/**\n\t * Callback when conversation ID changes (e.g., after creation).\n\t * Use this to update navigation state or URL.\n\t */\n\tonConversationIdChange?: (conversationId: string) => void;\n\n\t/**\n\t * Optional timeline items to pass through (e.g., optimistic updates).\n\t */\n\titems?: TimelineItem[];\n\n\t/**\n\t * Whether automatic \"seen\" tracking should be enabled.\n\t * Set to false when the conversation isn't visible (e.g. widget closed).\n\t * Default: true\n\t */\n\tautoSeenEnabled?: boolean;\n};\n\nexport type UseConversationPageReturn = {\n\t// Conversation state\n\tconversationId: string;\n\tisPending: boolean;\n\titems: TimelineItem[];\n\tisLoading: boolean;\n\terror: Error | null;\n\n\t// Message composer\n\tcomposer: {\n\t\tmessage: string;\n\t\tfiles: File[];\n\t\tisSubmitting: boolean;\n\t\tisUploading: boolean;\n\t\tcanSubmit: boolean;\n\t\tsetMessage: (message: string) => void;\n\t\taddFiles: (files: File[]) => void;\n\t\tremoveFile: (index: number) => void;\n\t\tsubmit: () => void;\n\t};\n\n\t// UI helpers\n\thasItems: boolean;\n\tlastTimelineItem: TimelineItem | null;\n};\n\n/**\n * Main orchestrator hook for the conversation page.\n *\n * This hook combines all conversation-related logic:\n * - Lifecycle management (pending → real conversation)\n * - Message fetching and display\n * - Message composition and sending\n * - Automatic seen tracking\n * - Default/welcome messages before conversation is created\n *\n * It provides a clean, simple API for building conversation UIs.\n *\n * @example\n * ```tsx\n * export function ConversationPage({ conversationId: initialId }) {\n * const conversation = useConversationPage({\n * conversationId: initialId,\n * onConversationIdChange: (newId) => {\n * // Update URL or navigation state\n * navigate(`/conversation/${newId}`);\n * },\n * });\n *\n * return (\n * <>\n * <MessageList messages={conversation.messages} />\n * <MessageInput\n * value={conversation.composer.message}\n * onChange={conversation.composer.setMessage}\n * onSubmit={conversation.composer.submit}\n * />\n * </>\n * );\n * }\n * ```\n */\nexport function useConversationPage(\n\toptions: UseConversationPageOptions\n): UseConversationPageReturn {\n\tconst {\n\t\tconversationId: initialConversationId,\n\t\tinitialMessage,\n\t\tonConversationIdChange,\n\t\titems: passedItems = [],\n\t\tautoSeenEnabled = true,\n\t} = options;\n\n\tconst { client, visitor, availableAIAgents } = useSupport();\n\tconst websocket = useWebSocketSafe();\n\tconst identificationState = useIdentificationState();\n\n\tconst trimmedInitialMessage = initialMessage?.trim() ?? \"\";\n\tconst hasInitialMessage = trimmedInitialMessage.length > 0;\n\n\t// 1. Manage conversation lifecycle (pending vs real)\n\tconst lifecycle = useConversationLifecycle({\n\t\tinitialConversationId,\n\t\tonConversationCreated: onConversationIdChange,\n\t});\n\n\t// 2. Get default timeline items for pending state\n\tconst defaultTimelineItems = useDefaultMessages({\n\t\tconversationId: lifecycle.conversationId,\n\t});\n\n\tconst effectiveDefaultTimelineItems = hasInitialMessage\n\t\t? []\n\t\t: defaultTimelineItems;\n\n\t// 3. Fetch timeline items from backend if real conversation exists\n\tconst timelineQuery = useConversationTimelineItems(lifecycle.conversationId, {\n\t\tenabled: !lifecycle.isPending,\n\t});\n\n\t// 4. Determine which items to display\n\tconst baseItems = useMemo(() => {\n\t\t// If we have fetched timeline items, use them\n\t\tif (timelineQuery.items.length > 0) {\n\t\t\treturn timelineQuery.items;\n\t\t}\n\n\t\t// If pending, use default timeline items\n\t\tif (lifecycle.isPending && effectiveDefaultTimelineItems.length > 0) {\n\t\t\treturn effectiveDefaultTimelineItems;\n\t\t}\n\n\t\t// Use passed items as fallback\n\t\tif (passedItems.length > 0) {\n\t\t\treturn passedItems;\n\t\t}\n\n\t\treturn [];\n\t}, [\n\t\ttimelineQuery.items,\n\t\tlifecycle.isPending,\n\t\teffectiveDefaultTimelineItems,\n\t\tpassedItems,\n\t]);\n\n\tconst shouldShowIdentificationTool = useMemo(() => {\n\t\tif (lifecycle.isPending) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Hide identification form when an AI agent is available\n\t\tif (availableAIAgents.length > 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Don't show identification form while identification is in progress\n\t\t// This prevents the form from flashing when an authenticated user opens the widget\n\t\tif (identificationState?.isIdentifying) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (visitor?.contact) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn !baseItems.some(\n\t\t\t(item) => item.type === ConversationTimelineType.IDENTIFICATION\n\t\t);\n\t}, [\n\t\tbaseItems,\n\t\tlifecycle.isPending,\n\t\tvisitor?.contact,\n\t\tidentificationState?.isIdentifying,\n\t\tavailableAIAgents.length,\n\t]);\n\n\tconst displayItems = useMemo(() => {\n\t\tif (!shouldShowIdentificationTool) {\n\t\t\treturn baseItems;\n\t\t}\n\n\t\tconst organizationId =\n\t\t\tbaseItems.at(-1)?.organizationId ??\n\t\t\tclient?.getConfiguration().organizationId ??\n\t\t\t\"\";\n\n\t\tconst identificationItem: TimelineItem = {\n\t\t\tid: `identification-${lifecycle.conversationId}`,\n\t\t\tconversationId: lifecycle.conversationId,\n\t\t\torganizationId,\n\t\t\tvisibility: TimelineItemVisibility.PUBLIC,\n\t\t\ttype: ConversationTimelineType.IDENTIFICATION,\n\t\t\ttext: null,\n\t\t\ttool: \"identification\",\n\t\t\tparts: [],\n\t\t\tuserId: null,\n\t\t\tvisitorId: visitor?.id ?? null,\n\t\t\taiAgentId: null,\n\t\t\tcreatedAt: typeof window !== \"undefined\" ? new Date().toISOString() : \"\",\n\t\t\tdeletedAt: null,\n\t\t};\n\n\t\treturn [...baseItems, identificationItem];\n\t}, [\n\t\tbaseItems,\n\t\tclient,\n\t\tlifecycle.conversationId,\n\t\tshouldShowIdentificationTool,\n\t\tvisitor?.id,\n\t]);\n\n\tconst lastTimelineItem = useMemo(\n\t\t() => displayItems.at(-1) ?? null,\n\t\t[displayItems]\n\t);\n\n\t// 5. Set up message composer\n\tconst composer = useMessageComposer({\n\t\tclient: client ?? undefined,\n\t\tconversationId: lifecycle.realConversationId,\n\t\tdefaultTimelineItems: effectiveDefaultTimelineItems,\n\t\tvisitorId: visitor?.id,\n\t\tonConversationInitiated: (newConversationId) => {\n\t\t\t// Immediately switch to new conversation ID for optimistic updates\n\t\t\t// This happens BEFORE the API call, so the UI starts reading from\n\t\t\t// the correct store key right away\n\t\t\tif (lifecycle.isPending) {\n\t\t\t\tlifecycle.setConversationId(newConversationId);\n\t\t\t}\n\t\t},\n\t\tonMessageSent: (newConversationId) => {\n\t\t\t// Also handle this for completeness (API call completed)\n\t\t\tif (lifecycle.isPending) {\n\t\t\t\tlifecycle.setConversationId(newConversationId);\n\t\t\t}\n\t\t},\n\t\t// Pass WebSocket connection for real-time typing events\n\t\trealtimeSend: websocket?.send ?? null,\n\t\tisRealtimeConnected: websocket?.isConnected ?? false,\n\t});\n\n\tconst initialMessageSubmittedRef = useRef(false);\n\tconst lastInitialMessageRef = useRef<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (!hasInitialMessage) {\n\t\t\tinitialMessageSubmittedRef.current = false;\n\t\t\tlastInitialMessageRef.current = null;\n\t\t\treturn;\n\t\t}\n\n\t\tif (lastInitialMessageRef.current !== trimmedInitialMessage) {\n\t\t\tinitialMessageSubmittedRef.current = false;\n\t\t\tlastInitialMessageRef.current = trimmedInitialMessage;\n\t\t}\n\n\t\tif (!lifecycle.isPending) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (composer.message !== trimmedInitialMessage) {\n\t\t\tcomposer.setMessage(trimmedInitialMessage);\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tinitialMessageSubmittedRef.current ||\n\t\t\tcomposer.isSubmitting ||\n\t\t\t!composer.canSubmit\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tinitialMessageSubmittedRef.current = true;\n\t\tcomposer.submit();\n\t}, [\n\t\thasInitialMessage,\n\t\tlifecycle.isPending,\n\t\tcomposer.message,\n\t\tcomposer.setMessage,\n\t\tcomposer.isSubmitting,\n\t\tcomposer.canSubmit,\n\t\tcomposer.submit,\n\t\ttrimmedInitialMessage,\n\t]);\n\n\t// 6. Auto-mark messages as seen\n\tuseConversationAutoSeen({\n\t\tclient,\n\t\tconversationId: lifecycle.realConversationId,\n\t\tvisitorId: visitor?.id,\n\t\tlastTimelineItem,\n\t\tenabled: autoSeenEnabled,\n\t\tisWidgetOpen: autoSeenEnabled,\n\t});\n\n\treturn {\n\t\tconversationId: lifecycle.conversationId,\n\t\tisPending: lifecycle.isPending,\n\t\titems: displayItems,\n\t\tisLoading: timelineQuery.isLoading,\n\t\terror: timelineQuery.error || composer.error,\n\t\tcomposer: {\n\t\t\tmessage: composer.message,\n\t\t\tfiles: composer.files,\n\t\t\tisSubmitting: composer.isSubmitting,\n\t\t\tisUploading: composer.isUploading,\n\t\t\tcanSubmit: composer.canSubmit,\n\t\t\tsetMessage: composer.setMessage,\n\t\t\taddFiles: composer.addFiles,\n\t\t\tremoveFile: composer.removeFile,\n\t\t\tsubmit: composer.submit,\n\t\t},\n\t\thasItems: displayItems.length > 0,\n\t\tlastTimelineItem,\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-conversation-page.js","names":["identificationItem: TimelineItem"],"sources":["../../src/hooks/use-conversation-page.ts"],"sourcesContent":["import { CossistantAPIError, type CossistantClient } from \"@cossistant/core\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport {\n\tConversationTimelineType,\n\tTimelineItemVisibility,\n} from \"@cossistant/types/enums\";\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { useSupport } from \"../provider\";\nimport { useIdentificationState } from \"../support/context/identification\";\nimport { useWebSocketSafe } from \"../support/context/websocket\";\nimport { useDefaultMessages } from \"./private/use-default-messages\";\nimport { useConversationAutoSeen } from \"./use-conversation-auto-seen\";\nimport { useConversationLifecycle } from \"./use-conversation-lifecycle\";\nimport { useConversationTimelineItems } from \"./use-conversation-timeline-items\";\nimport { useMessageComposer } from \"./use-message-composer\";\n\nexport type UseConversationPageOptions = {\n\t/**\n\t * Initial conversation ID (from URL params, navigation state, etc.)\n\t * Can be PENDING_CONVERSATION_ID or a real ID.\n\t */\n\tconversationId: string;\n\n\t/**\n\t * Optional initial message to send when the conversation opens.\n\t */\n\tinitialMessage?: string;\n\n\t/**\n\t * Callback when conversation ID changes (e.g., after creation).\n\t * Use this to update navigation state or URL.\n\t */\n\tonConversationIdChange?: (conversationId: string) => void;\n\n\t/**\n\t * Optional timeline items to pass through (e.g., optimistic updates).\n\t */\n\titems?: TimelineItem[];\n\n\t/**\n\t * Whether automatic \"seen\" tracking should be enabled.\n\t * Set to false when the conversation isn't visible (e.g. widget closed).\n\t * Default: true\n\t */\n\tautoSeenEnabled?: boolean;\n};\n\nexport type UseConversationPageReturn = {\n\t// Conversation state\n\tconversationId: string;\n\tisPending: boolean;\n\titems: TimelineItem[];\n\tisLoading: boolean;\n\terror: Error | null;\n\n\t// Message composer\n\tcomposer: {\n\t\tmessage: string;\n\t\tfiles: File[];\n\t\tisSubmitting: boolean;\n\t\tisUploading: boolean;\n\t\tcanSubmit: boolean;\n\t\tsetMessage: (message: string) => void;\n\t\taddFiles: (files: File[]) => void;\n\t\tremoveFile: (index: number) => void;\n\t\tsubmit: () => void;\n\t};\n\n\t// UI helpers\n\thasItems: boolean;\n\tlastTimelineItem: TimelineItem | null;\n};\n\nfunction isNotFoundError(error: Error | null): boolean {\n\treturn error instanceof CossistantAPIError && error.code === \"HTTP_404\";\n}\n\n/**\n * Main orchestrator hook for the conversation page.\n *\n * This hook combines all conversation-related logic:\n * - Lifecycle management (pending → real conversation)\n * - Message fetching and display\n * - Message composition and sending\n * - Automatic seen tracking\n * - Default/welcome messages before conversation is created\n *\n * It provides a clean, simple API for building conversation UIs.\n *\n * @example\n * ```tsx\n * export function ConversationPage({ conversationId: initialId }) {\n * const conversation = useConversationPage({\n * conversationId: initialId,\n * onConversationIdChange: (newId) => {\n * // Update URL or navigation state\n * navigate(`/conversation/${newId}`);\n * },\n * });\n *\n * return (\n * <>\n * <MessageList messages={conversation.messages} />\n * <MessageInput\n * value={conversation.composer.message}\n * onChange={conversation.composer.setMessage}\n * onSubmit={conversation.composer.submit}\n * />\n * </>\n * );\n * }\n * ```\n */\nexport function useConversationPage(\n\toptions: UseConversationPageOptions\n): UseConversationPageReturn {\n\tconst {\n\t\tconversationId: initialConversationId,\n\t\tinitialMessage,\n\t\tonConversationIdChange,\n\t\titems: passedItems = [],\n\t\tautoSeenEnabled = true,\n\t} = options;\n\n\tconst { client, visitor, availableAIAgents } = useSupport();\n\tconst websocket = useWebSocketSafe();\n\tconst identificationState = useIdentificationState();\n\n\tconst trimmedInitialMessage = initialMessage?.trim() ?? \"\";\n\tconst hasInitialMessage = trimmedInitialMessage.length > 0;\n\n\t// 1. Manage conversation lifecycle (pending vs real)\n\tconst lifecycle = useConversationLifecycle({\n\t\tinitialConversationId,\n\t\tonConversationCreated: onConversationIdChange,\n\t});\n\n\t// 2. Get default timeline items for pending state\n\tconst defaultTimelineItems = useDefaultMessages({\n\t\tconversationId: lifecycle.conversationId,\n\t});\n\n\tconst effectiveDefaultTimelineItems = hasInitialMessage\n\t\t? []\n\t\t: defaultTimelineItems;\n\n\tconst isPendingConversationBootstrap = Boolean(\n\t\tlifecycle.realConversationId &&\n\t\t\tclient?.isConversationPending(lifecycle.realConversationId)\n\t);\n\tconst shouldEnableConversationNetworkSync =\n\t\tBoolean(lifecycle.realConversationId) && !isPendingConversationBootstrap;\n\n\t// 3. Fetch timeline items from backend if real conversation exists\n\tconst timelineQuery = useConversationTimelineItems(lifecycle.conversationId, {\n\t\tenabled: shouldEnableConversationNetworkSync,\n\t});\n\n\t// 4. Determine which items to display\n\tconst baseItems = useMemo(() => {\n\t\t// If we have fetched timeline items, use them\n\t\tif (timelineQuery.items.length > 0) {\n\t\t\treturn timelineQuery.items;\n\t\t}\n\n\t\t// If pending, use default timeline items\n\t\tif (lifecycle.isPending && effectiveDefaultTimelineItems.length > 0) {\n\t\t\treturn effectiveDefaultTimelineItems;\n\t\t}\n\n\t\t// Use passed items as fallback\n\t\tif (passedItems.length > 0) {\n\t\t\treturn passedItems;\n\t\t}\n\n\t\treturn [];\n\t}, [\n\t\ttimelineQuery.items,\n\t\tlifecycle.isPending,\n\t\teffectiveDefaultTimelineItems,\n\t\tpassedItems,\n\t]);\n\n\tconst shouldShowIdentificationTool = useMemo(() => {\n\t\tif (lifecycle.isPending) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Hide identification form when an AI agent is available\n\t\tif (availableAIAgents.length > 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Don't show identification form while identification is in progress\n\t\t// This prevents the form from flashing when an authenticated user opens the widget\n\t\tif (identificationState?.isIdentifying) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (visitor?.contact) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn !baseItems.some(\n\t\t\t(item) => item.type === ConversationTimelineType.IDENTIFICATION\n\t\t);\n\t}, [\n\t\tbaseItems,\n\t\tlifecycle.isPending,\n\t\tvisitor?.contact,\n\t\tidentificationState?.isIdentifying,\n\t\tavailableAIAgents.length,\n\t]);\n\n\tconst displayItems = useMemo(() => {\n\t\tif (!shouldShowIdentificationTool) {\n\t\t\treturn baseItems;\n\t\t}\n\n\t\tconst organizationId =\n\t\t\tbaseItems.at(-1)?.organizationId ??\n\t\t\tclient?.getConfiguration().organizationId ??\n\t\t\t\"\";\n\n\t\tconst identificationItem: TimelineItem = {\n\t\t\tid: `identification-${lifecycle.conversationId}`,\n\t\t\tconversationId: lifecycle.conversationId,\n\t\t\torganizationId,\n\t\t\tvisibility: TimelineItemVisibility.PUBLIC,\n\t\t\ttype: ConversationTimelineType.IDENTIFICATION,\n\t\t\ttext: null,\n\t\t\ttool: \"identification\",\n\t\t\tparts: [],\n\t\t\tuserId: null,\n\t\t\tvisitorId: visitor?.id ?? null,\n\t\t\taiAgentId: null,\n\t\t\tcreatedAt: typeof window !== \"undefined\" ? new Date().toISOString() : \"\",\n\t\t\tdeletedAt: null,\n\t\t};\n\n\t\treturn [...baseItems, identificationItem];\n\t}, [\n\t\tbaseItems,\n\t\tclient,\n\t\tlifecycle.conversationId,\n\t\tshouldShowIdentificationTool,\n\t\tvisitor?.id,\n\t]);\n\n\tconst lastTimelineItem = useMemo(\n\t\t() => displayItems.at(-1) ?? null,\n\t\t[displayItems]\n\t);\n\n\tconst timelineError = useMemo(() => {\n\t\tif (\n\t\t\tisPendingConversationBootstrap &&\n\t\t\tisNotFoundError(timelineQuery.error)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn timelineQuery.error;\n\t}, [isPendingConversationBootstrap, timelineQuery.error]);\n\n\t// 5. Set up message composer\n\tconst composer = useMessageComposer({\n\t\tclient: client ?? undefined,\n\t\tconversationId: lifecycle.realConversationId,\n\t\tdefaultTimelineItems: effectiveDefaultTimelineItems,\n\t\tvisitorId: visitor?.id,\n\t\tonConversationInitiated: (newConversationId) => {\n\t\t\t// Immediately switch to new conversation ID for optimistic updates\n\t\t\t// This happens BEFORE the API call, so the UI starts reading from\n\t\t\t// the correct store key right away\n\t\t\tif (lifecycle.isPending) {\n\t\t\t\tlifecycle.setConversationId(newConversationId);\n\t\t\t}\n\t\t},\n\t\tonMessageSent: (newConversationId) => {\n\t\t\t// Also handle this for completeness (API call completed)\n\t\t\tif (lifecycle.isPending) {\n\t\t\t\tlifecycle.setConversationId(newConversationId);\n\t\t\t}\n\t\t},\n\t\t// Pass WebSocket connection for real-time typing events\n\t\trealtimeSend: websocket?.send ?? null,\n\t\tisRealtimeConnected: websocket?.isConnected ?? false,\n\t});\n\n\tconst initialMessageSubmittedRef = useRef(false);\n\tconst lastInitialMessageRef = useRef<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (!hasInitialMessage) {\n\t\t\tinitialMessageSubmittedRef.current = false;\n\t\t\tlastInitialMessageRef.current = null;\n\t\t\treturn;\n\t\t}\n\n\t\tif (lastInitialMessageRef.current !== trimmedInitialMessage) {\n\t\t\tinitialMessageSubmittedRef.current = false;\n\t\t\tlastInitialMessageRef.current = trimmedInitialMessage;\n\t\t}\n\n\t\tif (!lifecycle.isPending) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (composer.message !== trimmedInitialMessage) {\n\t\t\tcomposer.setMessage(trimmedInitialMessage);\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tinitialMessageSubmittedRef.current ||\n\t\t\tcomposer.isSubmitting ||\n\t\t\t!composer.canSubmit\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tinitialMessageSubmittedRef.current = true;\n\t\tcomposer.submit();\n\t}, [\n\t\thasInitialMessage,\n\t\tlifecycle.isPending,\n\t\tcomposer.message,\n\t\tcomposer.setMessage,\n\t\tcomposer.isSubmitting,\n\t\tcomposer.canSubmit,\n\t\tcomposer.submit,\n\t\ttrimmedInitialMessage,\n\t]);\n\n\t// 6. Auto-mark messages as seen\n\tuseConversationAutoSeen({\n\t\tclient,\n\t\tconversationId: lifecycle.realConversationId,\n\t\tvisitorId: visitor?.id,\n\t\tlastTimelineItem,\n\t\tenabled: autoSeenEnabled && shouldEnableConversationNetworkSync,\n\t\tisWidgetOpen: autoSeenEnabled,\n\t});\n\n\treturn {\n\t\tconversationId: lifecycle.conversationId,\n\t\tisPending: lifecycle.isPending,\n\t\titems: displayItems,\n\t\tisLoading: timelineQuery.isLoading,\n\t\terror: timelineError || composer.error,\n\t\tcomposer: {\n\t\t\tmessage: composer.message,\n\t\t\tfiles: composer.files,\n\t\t\tisSubmitting: composer.isSubmitting,\n\t\t\tisUploading: composer.isUploading,\n\t\t\tcanSubmit: composer.canSubmit,\n\t\t\tsetMessage: composer.setMessage,\n\t\t\taddFiles: composer.addFiles,\n\t\t\tremoveFile: composer.removeFile,\n\t\t\tsubmit: composer.submit,\n\t\t},\n\t\thasItems: displayItems.length > 0,\n\t\tlastTimelineItem,\n\t};\n}\n"],"mappings":"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"}
package/index.d.ts CHANGED
@@ -30,7 +30,7 @@ import { UseVisitorReturn, useVisitor } from "./hooks/use-visitor.js";
30
30
  import { WindowVisibilityFocusState, useWindowVisibilityFocus } from "./hooks/use-window-visibility-focus.js";
31
31
  import "./hooks/index.js";
32
32
  import { IdentifySupportVisitor, IdentifySupportVisitorProps } from "./identify-visitor.js";
33
- import { SupportConfig, SupportConfigProps } from "./support-config.js";
33
+ import { DefaultMessage, DefaultMessageProps, SupportConfig, SupportConfigProps, extractDefaultMessagesFromChildren, resolveSupportConfigMessages } from "./support-config.js";
34
34
  import { index_d_exports } from "./primitives/index.js";
35
35
  import { CossistantContextValue, CossistantProviderProps, SupportContext, SupportProvider, SupportProviderProps, UseSupportValue, useSupport } from "./provider.js";
36
36
  import { RealtimeAuthConfig, RealtimeContextValue, RealtimeProvider, RealtimeProviderProps, useRealtimeConnection } from "./realtime/provider.js";
@@ -49,4 +49,4 @@ import { Header } from "./support/components/header.js";
49
49
  import { WebSocketContextValue, WebSocketProvider, useWebSocket } from "./support/context/websocket.js";
50
50
  import { useSupportConfig, useSupportNavigation, useSupportStore } from "./support/store/support-store.js";
51
51
  import { DefaultRoutes, NavigationState, RouteRegistry, Support, SupportContentProps, SupportPageProps, SupportPageType, SupportProps, SupportRootProps, SupportRouterProps, SupportTriggerProps } from "./support/index.js";
52
- export { Align, CoButton as Button, CONVERSATION_AUTO_SEEN_DELAY_MS, CollisionPadding, ConfigurationError, ContentProps, ConversationEndEvent, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationStartEvent, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CossistantContextValue, CossistantProviderProps, CreateConversationVariables, CustomPage, DaySeparatorItem, DefaultRoutes, ErrorEvent, FileUploadPart, GroupedActivity, GroupedMessage, Header, IdentifySupportVisitor, IdentifySupportVisitorProps, MessageReceivedEvent, MessageSentEvent, NavigationState, PreparedTimelineItems, index_d_exports as Primitives, RealtimeAuthConfig, RealtimeContextValue, RealtimeEventHandler, RealtimeEventHandlerEntry, RealtimeEventHandlersMap, RealtimeEventMeta, RealtimeProvider, RealtimeProviderProps, RootProps, RouteRegistry, SendMessageOptions, SendMessageResult, Side, Support, SupportConfig, SupportConfigProps, SupportContentProps, SupportContext, SupportEvent, SupportEventCallbacks, SupportEventType, SupportHandle, SupportLocale, SupportPageProps, SupportPageType, SupportProps, SupportProvider, SupportProviderProps, SupportRealtimeProvider, SupportRootProps, SupportRouterProps, SupportTextContentOverrides, SupportTriggerProps, TIMELINE_GROUP_WINDOW_MS, Text, TimelineEventItem, TimelineToolItem, TriggerRenderProps, UseClientResult, UseComposerRefocusOptions, UseComposerRefocusReturn, UseConversationAutoSeenOptions, UseConversationHistoryPageOptions, UseConversationHistoryPageReturn, UseConversationLifecycleOptions, UseConversationLifecycleReturn, UseConversationOptions, UseConversationPageOptions, UseConversationPageReturn, UseConversationPreviewOptions, UseConversationPreviewReturn, UseConversationResult, UseConversationTimelineItemsOptions, UseConversationTimelineItemsResult, UseConversationTimelineOptions, UseConversationTimelineReturn, UseConversationsOptions, UseConversationsResult, UseCreateConversationOptions, UseCreateConversationResult, UseFileUploadOptions, UseFileUploadReturn, UseGroupedMessagesOptions, UseGroupedMessagesProps, UseHomePageOptions, UseHomePageReturn, UseMessageComposerOptions, UseMessageComposerReturn, UseMultimodalInputOptions, UseMultimodalInputReturn, UseRealtimeSupportOptions, UseRealtimeSupportResult, UseScrollMaskOptions, UseScrollMaskReturn, UseSendMessageOptions, UseSendMessageResult, UseSoundEffectOptions, UseSoundEffectReturn, UseSupportValue, UseVisitorReturn, WebSocketContextValue, WebSocketProvider, WindowVisibilityFocusState, applyConversationSeenEvent, applyConversationTypingEvent, buildTimelineReadReceiptData, clearTypingFromTimelineItem, clearTypingState, groupTimelineItems, hydrateConversationSeen, prepareTimelineItems, setTypingState, upsertConversationSeen, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useFileUpload, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtime, useRealtimeConnection, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useSupport, useSupportConfig, useSupportEventEmitter, useSupportEvents, useSupportHandle, useSupportNavigation, useSupportStore, useSupportText, useTypingSound, useVisitor, useWebSocket, useWindowVisibilityFocus };
52
+ export { Align, CoButton as Button, CONVERSATION_AUTO_SEEN_DELAY_MS, CollisionPadding, ConfigurationError, ContentProps, ConversationEndEvent, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationStartEvent, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CossistantContextValue, CossistantProviderProps, CreateConversationVariables, CustomPage, DaySeparatorItem, DefaultMessage, DefaultMessageProps, DefaultRoutes, ErrorEvent, FileUploadPart, GroupedActivity, GroupedMessage, Header, IdentifySupportVisitor, IdentifySupportVisitorProps, MessageReceivedEvent, MessageSentEvent, NavigationState, PreparedTimelineItems, index_d_exports as Primitives, RealtimeAuthConfig, RealtimeContextValue, RealtimeEventHandler, RealtimeEventHandlerEntry, RealtimeEventHandlersMap, RealtimeEventMeta, RealtimeProvider, RealtimeProviderProps, RootProps, RouteRegistry, SendMessageOptions, SendMessageResult, Side, Support, SupportConfig, SupportConfigProps, SupportContentProps, SupportContext, SupportEvent, SupportEventCallbacks, SupportEventType, SupportHandle, SupportLocale, SupportPageProps, SupportPageType, SupportProps, SupportProvider, SupportProviderProps, SupportRealtimeProvider, SupportRootProps, SupportRouterProps, SupportTextContentOverrides, SupportTriggerProps, TIMELINE_GROUP_WINDOW_MS, Text, TimelineEventItem, TimelineToolItem, TriggerRenderProps, UseClientResult, UseComposerRefocusOptions, UseComposerRefocusReturn, UseConversationAutoSeenOptions, UseConversationHistoryPageOptions, UseConversationHistoryPageReturn, UseConversationLifecycleOptions, UseConversationLifecycleReturn, UseConversationOptions, UseConversationPageOptions, UseConversationPageReturn, UseConversationPreviewOptions, UseConversationPreviewReturn, UseConversationResult, UseConversationTimelineItemsOptions, UseConversationTimelineItemsResult, UseConversationTimelineOptions, UseConversationTimelineReturn, UseConversationsOptions, UseConversationsResult, UseCreateConversationOptions, UseCreateConversationResult, UseFileUploadOptions, UseFileUploadReturn, UseGroupedMessagesOptions, UseGroupedMessagesProps, UseHomePageOptions, UseHomePageReturn, UseMessageComposerOptions, UseMessageComposerReturn, UseMultimodalInputOptions, UseMultimodalInputReturn, UseRealtimeSupportOptions, UseRealtimeSupportResult, UseScrollMaskOptions, UseScrollMaskReturn, UseSendMessageOptions, UseSendMessageResult, UseSoundEffectOptions, UseSoundEffectReturn, UseSupportValue, UseVisitorReturn, WebSocketContextValue, WebSocketProvider, WindowVisibilityFocusState, applyConversationSeenEvent, applyConversationTypingEvent, buildTimelineReadReceiptData, clearTypingFromTimelineItem, clearTypingState, extractDefaultMessagesFromChildren, groupTimelineItems, hydrateConversationSeen, prepareTimelineItems, resolveSupportConfigMessages, setTypingState, upsertConversationSeen, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useFileUpload, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtime, useRealtimeConnection, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useSupport, useSupportConfig, useSupportEventEmitter, useSupportEvents, useSupportHandle, useSupportNavigation, useSupportStore, useSupportText, useTypingSound, useVisitor, useWebSocket, useWindowVisibilityFocus };
package/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useClientQuery } from "./hooks/private/use-client-query.js";
2
2
  import { useClient } from "./hooks/private/use-rest-client.js";
3
3
  import { applyConversationSeenEvent, hydrateConversationSeen, upsertConversationSeen } from "./realtime/seen-store.js";
4
- import { SupportConfig } from "./support-config.js";
4
+ import { DefaultMessage, SupportConfig, extractDefaultMessagesFromChildren, resolveSupportConfigMessages } from "./support-config.js";
5
5
  import { useScrollMask } from "./hooks/use-scroll-mask.js";
6
6
  import { applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingState, setTypingState } from "./realtime/typing-store.js";
7
7
  import { useSupportConfig, useSupportNavigation, useSupportStore } from "./support/store/support-store.js";
@@ -45,4 +45,4 @@ import { useFileUpload } from "./hooks/use-file-upload.js";
45
45
  import { useRealtimeSupport } from "./hooks/use-realtime-support.js";
46
46
  import { IdentifySupportVisitor } from "./identify-visitor.js";
47
47
 
48
- export { CoButton as Button, CONVERSATION_AUTO_SEEN_DELAY_MS, Header, IdentifySupportVisitor, primitives_exports as Primitives, RealtimeProvider, Support, SupportConfig, SupportContext, SupportProvider, SupportRealtimeProvider, TIMELINE_GROUP_WINDOW_MS, Text, WebSocketProvider, applyConversationSeenEvent, applyConversationTypingEvent, buildTimelineReadReceiptData, clearTypingFromTimelineItem, clearTypingState, groupTimelineItems, hydrateConversationSeen, prepareTimelineItems, setTypingState, upsertConversationSeen, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useFileUpload, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtime, useRealtimeConnection, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useSupport, useSupportConfig, useSupportEventEmitter, useSupportEvents, useSupportHandle, useSupportNavigation, useSupportStore, useSupportText, useTypingSound, useVisitor, useWebSocket, useWindowVisibilityFocus };
48
+ export { CoButton as Button, CONVERSATION_AUTO_SEEN_DELAY_MS, DefaultMessage, Header, IdentifySupportVisitor, primitives_exports as Primitives, RealtimeProvider, Support, SupportConfig, SupportContext, SupportProvider, SupportRealtimeProvider, TIMELINE_GROUP_WINDOW_MS, Text, WebSocketProvider, applyConversationSeenEvent, applyConversationTypingEvent, buildTimelineReadReceiptData, clearTypingFromTimelineItem, clearTypingState, extractDefaultMessagesFromChildren, groupTimelineItems, hydrateConversationSeen, prepareTimelineItems, resolveSupportConfigMessages, setTypingState, upsertConversationSeen, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useFileUpload, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtime, useRealtimeConnection, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useSupport, useSupportConfig, useSupportEventEmitter, useSupportEvents, useSupportHandle, useSupportNavigation, useSupportStore, useSupportText, useTypingSound, useVisitor, useWebSocket, useWindowVisibilityFocus };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cossistant/react",
3
3
  "type": "module",
4
- "version": "0.0.33",
4
+ "version": "0.1.0",
5
5
  "private": false,
6
6
  "author": "Cossistant team",
7
7
  "description": "Headless React SDK for building AI-powered support/chat widgets. Hooks + primitives, WS-driven, TypeScript-first. Next.js-ready, Tailwind optional.",
@@ -50,12 +50,8 @@
50
50
  "types": "./hooks/index.d.ts",
51
51
  "import": "./hooks/index.js"
52
52
  },
53
- "./hooks/*": {
54
- "types": "./hooks/*.d.ts",
55
- "import": "./hooks/*.js"
56
- },
57
53
  "./support.css": "./support.css",
58
- "./tailwind.css": "./tailwind.css",
54
+ "./styles.css": "./styles.css",
59
55
  "./realtime": {
60
56
  "types": "./realtime/index.d.ts",
61
57
  "import": "./realtime/index.js"
@@ -88,13 +84,14 @@
88
84
  "*.css"
89
85
  ],
90
86
  "dependencies": {
91
- "@cossistant/core": "0.0.33",
87
+ "@cossistant/core": "0.1.0",
92
88
  "@cossistant/tiny-markdown": "0.0.1",
93
- "@cossistant/types": "0.0.33",
94
- "facehash": "0.0.7",
89
+ "@cossistant/types": "0.1.0",
90
+ "facehash": "0.1.0",
95
91
  "@floating-ui/react": "^0.27.16",
96
92
  "class-variance-authority": "^0.7.1",
97
93
  "clsx": "^2.1.1",
94
+ "motion": "^12.18.1",
98
95
  "nanoid": "^5.1.5",
99
96
  "react-use-websocket": "^4.13.0",
100
97
  "tailwind-merge": "^3.3.1",
@@ -104,7 +101,6 @@
104
101
  "react": ">=18 <20",
105
102
  "react-dom": ">=18 <20",
106
103
  "@types/react": "",
107
- "motion": "^12.18.1",
108
104
  "tailwindcss": "*"
109
105
  },
110
106
  "peerDependenciesMeta": {
@@ -1,7 +1,7 @@
1
1
  import "react";
2
2
 
3
3
  //#region ../tiny-markdown/src/types.d.ts
4
- type MentionType = "visitor" | "ai-agent" | "human-agent";
4
+ type MentionType = "visitor" | "ai-agent" | "human-agent" | "tool";
5
5
  type Mention = {
6
6
  id: string;
7
7
  name: string;
@@ -67,8 +67,8 @@ declare const createConversationResponseSchema: ZodObject<{
67
67
  input: ZodRecord<ZodString, ZodUnknown>;
68
68
  output: ZodOptional<ZodUnknown>;
69
69
  state: ZodEnum<{
70
- result: "result";
71
70
  error: "error";
71
+ result: "result";
72
72
  partial: "partial";
73
73
  }>;
74
74
  errorText: ZodOptional<ZodString>;
@@ -304,8 +304,8 @@ declare const createConversationResponseSchema: ZodObject<{
304
304
  input: ZodRecord<ZodString, ZodUnknown>;
305
305
  output: ZodOptional<ZodUnknown>;
306
306
  state: ZodEnum<{
307
- result: "result";
308
307
  error: "error";
308
+ result: "result";
309
309
  partial: "partial";
310
310
  }>;
311
311
  errorText: ZodOptional<ZodString>;
@@ -563,8 +563,8 @@ declare const listConversationsResponseSchema: ZodObject<{
563
563
  input: ZodRecord<ZodString, ZodUnknown>;
564
564
  output: ZodOptional<ZodUnknown>;
565
565
  state: ZodEnum<{
566
- result: "result";
567
566
  error: "error";
567
+ result: "result";
568
568
  partial: "partial";
569
569
  }>;
570
570
  errorText: ZodOptional<ZodString>;
@@ -815,8 +815,8 @@ declare const getConversationResponseSchema: ZodObject<{
815
815
  input: ZodRecord<ZodString, ZodUnknown>;
816
816
  output: ZodOptional<ZodUnknown>;
817
817
  state: ZodEnum<{
818
- result: "result";
819
818
  error: "error";
819
+ result: "result";
820
820
  partial: "partial";
821
821
  }>;
822
822
  errorText: ZodOptional<ZodString>;
@@ -66,8 +66,8 @@ declare const timelineItemPartsSchema: ZodArray<ZodUnion<readonly [ZodObject<{
66
66
  input: ZodRecord<ZodString, ZodUnknown>;
67
67
  output: ZodOptional<ZodUnknown>;
68
68
  state: ZodEnum<{
69
- result: "result";
70
69
  error: "error";
70
+ result: "result";
71
71
  partial: "partial";
72
72
  }>;
73
73
  errorText: ZodOptional<ZodString>;
@@ -281,8 +281,8 @@ declare const timelineItemSchema: ZodObject<{
281
281
  input: ZodRecord<ZodString, ZodUnknown>;
282
282
  output: ZodOptional<ZodUnknown>;
283
283
  state: ZodEnum<{
284
- result: "result";
285
284
  error: "error";
285
+ result: "result";
286
286
  partial: "partial";
287
287
  }>;
288
288
  errorText: ZodOptional<ZodString>;
@@ -517,8 +517,8 @@ declare const getConversationTimelineItemsResponseSchema: ZodObject<{
517
517
  input: ZodRecord<ZodString, ZodUnknown>;
518
518
  output: ZodOptional<ZodUnknown>;
519
519
  state: ZodEnum<{
520
- result: "result";
521
520
  error: "error";
521
+ result: "result";
522
522
  partial: "partial";
523
523
  }>;
524
524
  errorText: ZodOptional<ZodString>;