@datalayer/core 0.0.17 → 0.0.19

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 (107) hide show
  1. package/README.md +9 -13
  2. package/lib/client/auth/AuthenticationManager.d.ts +95 -0
  3. package/lib/client/auth/AuthenticationManager.js +214 -0
  4. package/lib/client/auth/index.d.ts +8 -0
  5. package/lib/client/auth/index.js +17 -0
  6. package/lib/client/auth/storage.d.ts +154 -0
  7. package/lib/client/auth/storage.js +447 -0
  8. package/lib/client/auth/strategies.d.ts +54 -0
  9. package/lib/client/auth/strategies.js +238 -0
  10. package/lib/client/auth/types.d.ts +151 -0
  11. package/lib/{examples/index.js → client/auth/types.js} +4 -2
  12. package/lib/client/base.d.ts +3 -0
  13. package/lib/client/base.js +9 -0
  14. package/lib/client/index.d.ts +1 -0
  15. package/lib/client/index.js +2 -0
  16. package/lib/components/auth/Login.d.ts +40 -0
  17. package/lib/components/auth/Login.js +173 -0
  18. package/lib/components/auth/Login.stories.d.ts +54 -0
  19. package/lib/components/auth/Login.stories.js +104 -0
  20. package/lib/components/auth/LoginToken.d.ts +16 -0
  21. package/lib/components/auth/LoginToken.js +63 -0
  22. package/lib/components/auth/index.d.ts +5 -0
  23. package/lib/components/auth/index.js +16 -0
  24. package/lib/components/avatars/BoringAvatar.d.ts +6 -15
  25. package/lib/components/avatars/BoringAvatar.js +30 -34
  26. package/lib/components/avatars/BoringAvatar.stories.d.ts +7 -16
  27. package/lib/components/avatars/UserProfileAvatar.d.ts +1 -6
  28. package/lib/components/avatars/UserProfileAvatar.js +3 -8
  29. package/lib/components/buttons/DownloadCSVButton.d.ts +2 -7
  30. package/lib/components/buttons/DownloadCSVButton.js +1 -5
  31. package/lib/components/buttons/DownloadJsonButton.d.ts +3 -10
  32. package/lib/components/buttons/DownloadJsonButton.js +1 -7
  33. package/lib/components/buttons/UploadButton.d.ts +1 -4
  34. package/lib/components/buttons/UploadButton.js +3 -7
  35. package/lib/components/chat/ChatComponent.js +4 -0
  36. package/lib/components/chat/display/ReasoningPart.js +4 -0
  37. package/lib/components/chat/display/ToolPart.js +4 -0
  38. package/lib/components/chat/display/index.js +4 -0
  39. package/lib/components/chat/handler.js +4 -0
  40. package/lib/components/chat/index.js +4 -0
  41. package/lib/components/display/CenteredSpinner.d.ts +1 -4
  42. package/lib/components/display/CenteredSpinner.js +1 -5
  43. package/lib/components/display/HorizontalCenter.d.ts +1 -4
  44. package/lib/components/display/HorizontalCenter.js +1 -5
  45. package/lib/components/flashes/FlashClosable.d.ts +1 -4
  46. package/lib/components/flashes/FlashClosable.js +1 -5
  47. package/lib/components/flashes/FlashDisclaimer.js +1 -1
  48. package/lib/components/index.d.ts +1 -0
  49. package/lib/components/index.js +1 -0
  50. package/lib/components/notebooks/JupyterNotebook.d.ts +1 -6
  51. package/lib/components/notebooks/JupyterNotebook.js +1 -5
  52. package/lib/components/snapshots/RuntimeSnapshotMenu.d.ts +1 -4
  53. package/lib/components/snapshots/RuntimeSnapshotMenu.js +1 -5
  54. package/lib/config/Configuration.js +1 -1
  55. package/lib/examples/CellExample.js +11 -47
  56. package/lib/examples/lexical-theme.css +436 -0
  57. package/lib/examples/notebooks/Matplotlib.ipynb.json +1 -1
  58. package/lib/examples/notebooks/NotebookExample1.ipynb.json +1 -1
  59. package/lib/hooks/useAIJupyterChat.js +4 -0
  60. package/lib/hooks/useBackdrop.d.ts +4 -4
  61. package/lib/hooks/useBackdrop.js +5 -9
  62. package/lib/hooks/useCache.d.ts +5 -1
  63. package/lib/hooks/useCache.js +27 -14
  64. package/lib/hooks/useMobile.js +4 -0
  65. package/lib/hooks/useScreenshot.d.ts +3 -5
  66. package/lib/hooks/useScreenshot.js +1 -8
  67. package/lib/models/Outbound.d.ts +2 -0
  68. package/lib/models/Outbound.js +3 -1
  69. package/lib/state/substates/CoreState.js +1 -1
  70. package/lib/state/substates/IAMState.js +15 -6
  71. package/lib/tools/adapters/agui/AgUIToolAdapter.d.ts +75 -0
  72. package/lib/tools/adapters/agui/AgUIToolAdapter.js +244 -0
  73. package/lib/tools/adapters/agui/index.d.ts +10 -0
  74. package/lib/tools/adapters/agui/index.js +19 -0
  75. package/lib/tools/adapters/agui/lexicalHooks.d.ts +27 -0
  76. package/lib/tools/adapters/agui/lexicalHooks.js +64 -0
  77. package/lib/tools/adapters/agui/notebookHooks.d.ts +27 -0
  78. package/lib/tools/adapters/agui/notebookHooks.js +61 -0
  79. package/lib/tools/index.d.ts +6 -0
  80. package/lib/tools/index.js +18 -0
  81. package/lib/types.js +2 -3
  82. package/lib/utils/cli/index.d.ts +4 -0
  83. package/lib/utils/cli/index.js +13 -0
  84. package/lib/utils/cli/query.d.ts +6 -0
  85. package/lib/utils/cli/query.js +26 -0
  86. package/lib/utils/index.d.ts +1 -0
  87. package/lib/utils/index.js +1 -0
  88. package/package.json +49 -7
  89. package/lib/examples/ChatExample.d.ts +0 -8
  90. package/lib/examples/ChatExample.js +0 -51
  91. package/lib/examples/DatalayerNotebookExample.d.ts +0 -16
  92. package/lib/examples/DatalayerNotebookExample.js +0 -75
  93. package/lib/examples/NativeNavigationExample.d.ts +0 -8
  94. package/lib/examples/NativeNavigationExample.js +0 -97
  95. package/lib/examples/NotebookMutationsKernel.d.ts +0 -2
  96. package/lib/examples/NotebookMutationsKernel.js +0 -115
  97. package/lib/examples/NotebookMutationsServiceManager.d.ts +0 -2
  98. package/lib/examples/NotebookMutationsServiceManager.js +0 -107
  99. package/lib/examples/ReactRouterExample.d.ts +0 -6
  100. package/lib/examples/ReactRouterExample.js +0 -175
  101. package/lib/examples/example-selector.d.ts +0 -22
  102. package/lib/examples/example-selector.js +0 -46
  103. package/lib/examples/index.d.ts +0 -2
  104. package/lib/examples/main.d.ts +0 -1
  105. package/lib/examples/main.js +0 -153
  106. package/lib/examples/notebooks/OutputIPyWidgetsExample.d.ts +0 -145
  107. package/lib/examples/notebooks/OutputIPyWidgetsExample.js +0 -153
@@ -21,7 +21,7 @@
21
21
  "execution_count": 1,
22
22
  "metadata": {
23
23
  "datalayer": {
24
- "_about": "AI Platform for Data Analysis"
24
+ "_about": "AI Agents for Data Analysis"
25
25
  }
26
26
  },
27
27
  "outputs": [
@@ -1,3 +1,7 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
1
5
  /*
2
6
  * Copyright (c) 2024-2025 Datalayer, Inc.
3
7
  *
@@ -1,3 +1,4 @@
1
+ import { ReactNode } from 'react';
1
2
  import PropTypes from 'prop-types';
2
3
  type BackdropContextType = {
3
4
  closeBackdrop: () => void;
@@ -10,19 +11,18 @@ export declare function useBackdrop(): BackdropContextType;
10
11
  */
11
12
  export declare const BackdropContextProvider: import("react").Provider<BackdropContextType>;
12
13
  type IBackdropProviderProps = {
13
- children?: JSX.Element | JSX.Element[];
14
+ children?: ReactNode;
14
15
  zIndex?: number;
15
16
  disableDarken?: boolean;
16
17
  backdropSurface?: any;
17
18
  };
18
- export declare function BackdropProvider(props: IBackdropProviderProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare function BackdropProvider({ children, zIndex, disableDarken, backdropSurface, }: IBackdropProviderProps): import("react/jsx-runtime").JSX.Element;
19
20
  export declare namespace BackdropProvider {
20
21
  var propTypes: {
21
- children: PropTypes.Requireable<PropTypes.ReactElementLike>;
22
+ children: PropTypes.Requireable<NonNullable<PropTypes.ReactNodeLike>>;
22
23
  zIndex: PropTypes.Requireable<number>;
23
24
  disableDarken: PropTypes.Requireable<boolean>;
24
25
  backdropSurface: PropTypes.Requireable<(...args: any[]) => any>;
25
26
  };
26
- var defaultProps: IBackdropProviderProps;
27
27
  }
28
28
  export {};
@@ -48,8 +48,7 @@ const App = () => (
48
48
  )
49
49
  export default App
50
50
  */
51
- export function BackdropProvider(props) {
52
- const { children, zIndex, disableDarken, backdropSurface } = props;
51
+ export function BackdropProvider({ children = null, zIndex = 9999, disableDarken = false, backdropSurface = null, }) {
53
52
  const defaultBackdropSurface = {
54
53
  position: 'fixed',
55
54
  top: 0,
@@ -82,7 +81,10 @@ export function BackdropProvider(props) {
82
81
  (backdropSurface ? (backdropSurface(backdrop.render(closeBackdrop))) : (_jsx("div", { style: defaultBackdropSurface, children: backdrop.render(closeBackdrop) })))] }));
83
82
  }
84
83
  BackdropProvider.propTypes = {
85
- children: PropTypes.element,
84
+ children: PropTypes.oneOfType([
85
+ PropTypes.node,
86
+ PropTypes.arrayOf(PropTypes.node),
87
+ ]),
86
88
  // zIndex of the backdrop surface. Unused if backdropSurface is overridden.
87
89
  zIndex: PropTypes.number,
88
90
  // If true, backdrop background is transparent.
@@ -92,9 +94,3 @@ BackdropProvider.propTypes = {
92
94
  // See /example/src/ExampleApp.jsx for proper use.
93
95
  backdropSurface: PropTypes.func,
94
96
  };
95
- BackdropProvider.defaultProps = {
96
- children: undefined,
97
- disableBackdrop: false,
98
- zIndex: 9999,
99
- backdropSurface: null,
100
- };
@@ -173,6 +173,7 @@ export declare const queryKeys: {
173
173
  };
174
174
  readonly contacts: {
175
175
  readonly all: () => readonly ["contacts"];
176
+ readonly lists: () => readonly ["contacts", "list"];
176
177
  readonly details: () => readonly ["contacts", "detail"];
177
178
  readonly detail: (id: string) => readonly ["contacts", "detail", string];
178
179
  readonly byHandle: (handle: string) => readonly ["contacts", "handle", string];
@@ -683,7 +684,9 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
683
684
  useRefreshInvite: (options?: UseMutationOptions<unknown, Error, string>) => import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;
684
685
  useRefreshInvites: (options?: UseMutationOptions<unknown, Error, string>) => import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;
685
686
  useClearCachedInvites: (options?: UseMutationOptions<void, Error, void>) => import("@tanstack/react-query").UseMutationResult<void, Error, void, unknown>;
686
- useContact: (contactId: string) => import("@tanstack/react-query").UseQueryResult<IContact | undefined, Error>;
687
+ useContact: (contactId: string, options?: {
688
+ enabled?: boolean;
689
+ }) => import("@tanstack/react-query").UseQueryResult<IContact | undefined, Error>;
687
690
  useContactByHandle: (handle: string) => import("@tanstack/react-query").UseQueryResult<IContact | null | undefined, Error>;
688
691
  useSearchContacts: (query: string) => import("@tanstack/react-query").UseQueryResult<any, Error>;
689
692
  useCreateContact: () => import("@tanstack/react-query").UseMutationResult<any, Error, IContact, unknown>;
@@ -893,6 +896,7 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
893
896
  };
894
897
  readonly contacts: {
895
898
  readonly all: () => readonly ["contacts"];
899
+ readonly lists: () => readonly ["contacts", "list"];
896
900
  readonly details: () => readonly ["contacts", "detail"];
897
901
  readonly detail: (id: string) => readonly ["contacts", "detail", string];
898
902
  readonly byHandle: (handle: string) => readonly ["contacts", "handle", string];
@@ -227,6 +227,7 @@ export const queryKeys = {
227
227
  // Contacts
228
228
  contacts: {
229
229
  all: () => ['contacts'],
230
+ lists: () => [...queryKeys.contacts.all(), 'list'],
230
231
  details: () => [...queryKeys.contacts.all(), 'detail'],
231
232
  detail: (id) => [...queryKeys.contacts.details(), id],
232
233
  byHandle: (handle) => [...queryKeys.contacts.all(), 'handle', handle],
@@ -1903,7 +1904,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1903
1904
  const useCreateToken = () => {
1904
1905
  return useMutation({
1905
1906
  mutationFn: async (token) => {
1906
- return requestDatalayer({
1907
+ const resp = await requestDatalayer({
1907
1908
  url: `${configuration.iamRunUrl}/api/iam/v1/tokens`,
1908
1909
  method: 'POST',
1909
1910
  body: {
@@ -1911,13 +1912,15 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1911
1912
  expirationDate: token.expirationDate.getTime(),
1912
1913
  },
1913
1914
  });
1915
+ // Transform the token in the response
1916
+ if (resp.success && resp.token) {
1917
+ resp.token = toToken(resp.token);
1918
+ }
1919
+ return resp;
1914
1920
  },
1915
1921
  onSuccess: resp => {
1916
1922
  if (resp.success && resp.token) {
1917
- const tok = toToken(resp.token);
1918
- if (tok) {
1919
- queryClient.setQueryData(queryKeys.tokens.detail(tok.id), tok);
1920
- }
1923
+ queryClient.setQueryData(queryKeys.tokens.detail(resp.token.id), resp.token);
1921
1924
  }
1922
1925
  queryClient.invalidateQueries({ queryKey: queryKeys.tokens.all() });
1923
1926
  },
@@ -1929,7 +1932,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1929
1932
  /**
1930
1933
  * Get contact by ID
1931
1934
  */
1932
- const useContact = (contactId) => {
1935
+ const useContact = (contactId, options) => {
1933
1936
  return useQuery({
1934
1937
  queryKey: queryKeys.contacts.detail(contactId),
1935
1938
  queryFn: async () => {
@@ -1947,7 +1950,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1947
1950
  throw new Error(resp.message || 'Failed to fetch contact');
1948
1951
  },
1949
1952
  ...DEFAULT_QUERY_OPTIONS,
1950
- enabled: !!contactId,
1953
+ enabled: options?.enabled ?? !!contactId,
1951
1954
  });
1952
1955
  };
1953
1956
  /**
@@ -2025,9 +2028,9 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
2025
2028
  queryClient.invalidateQueries({
2026
2029
  queryKey: queryKeys.contacts.detail(variables.contactId),
2027
2030
  });
2028
- // Invalidate all contact queries
2031
+ // Invalidate contact list queries (avoid re-fetching deleted detail)
2029
2032
  queryClient.invalidateQueries({
2030
- queryKey: queryKeys.contacts.all(),
2033
+ queryKey: queryKeys.contacts.lists(),
2031
2034
  });
2032
2035
  },
2033
2036
  });
@@ -4027,7 +4030,10 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
4027
4030
  });
4028
4031
  },
4029
4032
  onSuccess: () => {
4030
- queryClient.invalidateQueries({ queryKey: ['outbounds'] });
4033
+ queryClient.invalidateQueries({
4034
+ queryKey: ['outbounds'],
4035
+ refetchType: 'inactive',
4036
+ });
4031
4037
  },
4032
4038
  });
4033
4039
  };
@@ -4050,6 +4056,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
4050
4056
  onSuccess: (_, outboundId) => {
4051
4057
  queryClient.invalidateQueries({
4052
4058
  queryKey: ['outbounds', outboundId],
4059
+ refetchType: 'inactive',
4053
4060
  });
4054
4061
  },
4055
4062
  });
@@ -4068,10 +4075,13 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
4068
4075
  });
4069
4076
  },
4070
4077
  onSuccess: (_, outboundId) => {
4071
- queryClient.invalidateQueries({
4078
+ queryClient.removeQueries({
4072
4079
  queryKey: ['outbounds', outboundId],
4073
4080
  });
4074
- queryClient.invalidateQueries({ queryKey: ['outbounds'] });
4081
+ queryClient.invalidateQueries({
4082
+ queryKey: ['outbounds'],
4083
+ refetchType: 'inactive',
4084
+ });
4075
4085
  },
4076
4086
  });
4077
4087
  };
@@ -4102,10 +4112,13 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
4102
4112
  });
4103
4113
  },
4104
4114
  onSuccess: (_, outboundId) => {
4105
- queryClient.invalidateQueries({
4115
+ queryClient.removeQueries({
4106
4116
  queryKey: ['outbounds', outboundId],
4107
4117
  });
4108
- queryClient.invalidateQueries({ queryKey: ['outbounds'] });
4118
+ queryClient.invalidateQueries({
4119
+ queryKey: ['outbounds'],
4120
+ refetchType: 'inactive',
4121
+ });
4109
4122
  },
4110
4123
  });
4111
4124
  };
@@ -1,3 +1,7 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
1
5
  /*
2
6
  * Copyright (c) 2024-2025 Datalayer, Inc.
3
7
  *
@@ -1,3 +1,4 @@
1
+ import { ReactNode } from 'react';
1
2
  type ScreenshotContextType = {
2
3
  closeScreenshot: () => void;
3
4
  displayScreenshot: (nextScreenshot: any) => void;
@@ -9,13 +10,10 @@ export declare function useScreenshot(): ScreenshotContextType;
9
10
  */
10
11
  export declare const ScreenshotContextProvider: import("react").Provider<ScreenshotContextType>;
11
12
  type IScreenshotProviderProps = {
12
- children?: JSX.Element | JSX.Element[];
13
+ children?: ReactNode;
13
14
  zIndex?: number;
14
15
  disableDarken?: boolean;
15
16
  screenshotSurface?: (qfds: any) => JSX.Element;
16
17
  };
17
- export declare function ScreenshotProvider(props: IScreenshotProviderProps): import("react/jsx-runtime").JSX.Element;
18
- export declare namespace ScreenshotProvider {
19
- var defaultProps: IScreenshotProviderProps;
20
- }
18
+ export declare function ScreenshotProvider({ children, zIndex, disableDarken, screenshotSurface, }: IScreenshotProviderProps): import("react/jsx-runtime").JSX.Element;
21
19
  export {};
@@ -19,8 +19,7 @@ export function useScreenshot() {
19
19
  * The type for the Screenshot context provider.
20
20
  */
21
21
  export const ScreenshotContextProvider = ScreenshotContext.Provider;
22
- export function ScreenshotProvider(props) {
23
- const { children, zIndex, disableDarken, screenshotSurface } = props;
22
+ export function ScreenshotProvider({ children = null, zIndex = 9999, disableDarken = false, screenshotSurface = undefined, }) {
24
23
  const defaultScreenshotSurface = {
25
24
  position: 'fixed',
26
25
  top: 0,
@@ -52,9 +51,3 @@ export function ScreenshotProvider(props) {
52
51
  return (_jsxs(ScreenshotContextProvider, { value: { closeScreenshot, displayScreenshot }, children: [_jsx(LayoutScreenshot, {}), children, screenshot.open &&
53
52
  (screenshotSurface ? (screenshotSurface(screenshot.render(closeScreenshot))) : (_jsx("div", { style: defaultScreenshotSurface, children: screenshot.render(closeScreenshot) })))] }));
54
53
  }
55
- ScreenshotProvider.defaultProps = {
56
- children: undefined,
57
- disableScreenshot: false,
58
- zIndex: 9999,
59
- screenshotSurface: undefined,
60
- };
@@ -1,5 +1,6 @@
1
1
  export declare class Outbound implements IOutbound {
2
2
  id: string;
3
+ uid: string;
3
4
  subType: string;
4
5
  name: string;
5
6
  description: string;
@@ -24,6 +25,7 @@ export declare class Outbound implements IOutbound {
24
25
  export declare function asOutbound(u: any): IOutbound;
25
26
  export type IOutbound = {
26
27
  id: string;
28
+ uid: string;
27
29
  subType: string;
28
30
  name: string;
29
31
  description: string;
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export class Outbound {
6
6
  id;
7
+ uid;
7
8
  subType;
8
9
  name;
9
10
  description;
@@ -18,7 +19,8 @@ export class Outbound {
18
19
  lastUpdateDate;
19
20
  launchedDate;
20
21
  constructor(u) {
21
- this.id = u.uid;
22
+ this.id = u.id;
23
+ this.uid = u.uid;
22
24
  this.subType = u.subType;
23
25
  this.name = u.name;
24
26
  this.description = u.description;
@@ -35,7 +35,7 @@ let initialConfiguration = {
35
35
  },
36
36
  brand: {
37
37
  name: 'Datalayer',
38
- about: 'AI Platform for Data Analysis',
38
+ about: 'AI Agents for Data Analysis',
39
39
  logoUrl: 'https://assets.datalayer.tech/datalayer-25.svg',
40
40
  logoSquareUrl: 'https://assets.datalayer.tech/datalayer-square.png',
41
41
  copyright: '© 2025 Datalayer, Inc',
@@ -268,15 +268,24 @@ iamStore
268
268
  }
269
269
  // Start the credits poll in any case after trying to validate the user token.
270
270
  creditsPoll.start();
271
- // Force a refresh when the user comeback to the application tab
271
+ // Force a refresh when the user comes back to the application tab.
272
272
  // Useful for checkout platform redirecting to another tab to add credits.
273
- if (typeof document !== 'undefined') {
274
- document.addEventListener('visibilitychange', () => {
275
- if (!document.hidden && iamStore.getState().user?.id) {
273
+ const maybeSetupVisibilityListener = () => {
274
+ if (typeof window === 'undefined') {
275
+ return;
276
+ }
277
+ const doc = window.document;
278
+ if (!doc || typeof doc.addEventListener !== 'function') {
279
+ return;
280
+ }
281
+ const onVisibilityChange = () => {
282
+ if (!doc.hidden && iamStore.getState().user?.id) {
276
283
  creditsPoll.refresh();
277
284
  }
278
- });
279
- }
285
+ };
286
+ doc.addEventListener('visibilitychange', onVisibilityChange);
287
+ };
288
+ maybeSetupVisibilityListener();
280
289
  });
281
290
  // Connect the core store with the iam store.
282
291
  coreStore.subscribe((state, prevState) => {
@@ -0,0 +1,75 @@
1
+ /**
2
+ * ag-ui/CopilotKit Tool Adapter
3
+ *
4
+ * Converts unified tool definitions to CopilotKit's useCopilotAction format.
5
+ *
6
+ * @see https://docs.copilotkit.ai/langgraph/frontend-actions
7
+ * @module tools/adapters/agui/AgUIToolAdapter
8
+ */
9
+ import type { ToolDefinition, ToolOperation, ToolExecutionContext } from '@datalayer/jupyter-react';
10
+ /**
11
+ * Type signature for CopilotKit's useFrontendTool hook
12
+ * This is shared across both notebook and lexical adapters
13
+ */
14
+ export type UseFrontendToolFn = (tool: {
15
+ name: string;
16
+ description: string;
17
+ parameters: any;
18
+ handler: (params: any) => Promise<string>;
19
+ render?: (props: any) => any;
20
+ }, dependencies?: any[]) => void;
21
+ /**
22
+ * Component to register a single action with CopilotKit
23
+ * Must be used inside CopilotKit context
24
+ */
25
+ export declare function ActionRegistrar({ action, useFrontendTool, }: {
26
+ action: any;
27
+ useFrontendTool: UseFrontendToolFn;
28
+ }): null;
29
+ /**
30
+ * CopilotKit parameter definition (array format, not JSON Schema)
31
+ */
32
+ export interface CopilotKitParameter {
33
+ name: string;
34
+ type?: 'string' | 'number' | 'boolean' | 'object' | 'object[]';
35
+ description?: string;
36
+ required?: boolean;
37
+ attributes?: CopilotKitParameter[];
38
+ }
39
+ /**
40
+ * CopilotKit action definition (matches useCopilotAction interface)
41
+ */
42
+ export interface CopilotKitAction {
43
+ /** Action name */
44
+ name: string;
45
+ /** Description for AI model */
46
+ description: string;
47
+ /** Parameters array (NOT JSON Schema) */
48
+ parameters: CopilotKitParameter[];
49
+ /** Handler function */
50
+ handler: (params: unknown) => Promise<string>;
51
+ /** Optional custom UI renderer */
52
+ render?: (props: {
53
+ status: string;
54
+ args: unknown;
55
+ result: unknown;
56
+ }) => React.ReactNode;
57
+ }
58
+ /**
59
+ * Converts unified tool definition to CopilotKit action format
60
+ *
61
+ * @param definition - Tool definition
62
+ * @param operation - Core operation
63
+ * @param context - Execution context (documentId + executor)
64
+ * @returns CopilotKit action
65
+ */
66
+ export declare function createCopilotKitAction(definition: ToolDefinition, operation: ToolOperation<unknown, unknown>, context: ToolExecutionContext): CopilotKitAction;
67
+ /**
68
+ * Creates CopilotKit actions from all tool definitions
69
+ *
70
+ * @param definitions - Tool definitions
71
+ * @param operations - Core operations registry
72
+ * @param context - Execution context (documentId + executor)
73
+ * @returns CopilotKit actions
74
+ */
75
+ export declare function createAllCopilotKitActions(definitions: ToolDefinition[], operations: Record<string, ToolOperation<unknown, unknown>>, context: ToolExecutionContext): CopilotKitAction[];
@@ -0,0 +1,244 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { OperationRunner } from '@datalayer/jupyter-react';
6
+ /**
7
+ * Component to register a single action with CopilotKit
8
+ * Must be used inside CopilotKit context
9
+ */
10
+ export function ActionRegistrar({ action, useFrontendTool, }) {
11
+ useFrontendTool(action, [action]);
12
+ return null;
13
+ }
14
+ /**
15
+ * Deduplication cache to prevent executing the same operation multiple times
16
+ * Maps operation signature (hash of tool + params) to timestamp
17
+ */
18
+ const executionCache = new Map();
19
+ const CACHE_TTL_MS = 2000; // 2 seconds - operations within this window are considered duplicates
20
+ /**
21
+ * Generate a unique signature for an operation call
22
+ */
23
+ function getOperationSignature(toolName, params) {
24
+ return `${toolName}:${JSON.stringify(params)}`;
25
+ }
26
+ /**
27
+ * Check if this operation was recently executed
28
+ */
29
+ function isRecentDuplicate(toolName, params) {
30
+ const signature = getOperationSignature(toolName, params);
31
+ const lastExecution = executionCache.get(signature);
32
+ if (lastExecution) {
33
+ const timeSinceExecution = Date.now() - lastExecution;
34
+ if (timeSinceExecution < CACHE_TTL_MS) {
35
+ console.log(`[ag-ui] 🚫 DUPLICATE DETECTED - Skipping execution (${timeSinceExecution}ms since last call)`);
36
+ return true;
37
+ }
38
+ }
39
+ // Clean up old entries while we're here
40
+ for (const [key, timestamp] of executionCache.entries()) {
41
+ if (Date.now() - timestamp > CACHE_TTL_MS * 2) {
42
+ executionCache.delete(key);
43
+ }
44
+ }
45
+ // Mark this execution
46
+ executionCache.set(signature, Date.now());
47
+ return false;
48
+ }
49
+ /**
50
+ * Parses array parameters from CopilotKit's string format to actual arrays.
51
+ *
52
+ * CopilotKit doesn't have native support for primitive arrays, so it sends them
53
+ * as JSON strings (e.g., "[1, 2, 3]"). This function parses those strings back
54
+ * into actual arrays based on the schema definition.
55
+ *
56
+ * @param params - Raw parameters from CopilotKit
57
+ * @param jsonSchema - JSON Schema definition to identify array parameters
58
+ * @returns Parsed parameters with arrays converted from strings
59
+ */
60
+ function parseArrayParameters(params, jsonSchema) {
61
+ if (typeof params !== 'object' || params === null) {
62
+ return params;
63
+ }
64
+ const parsedParams = { ...params };
65
+ // Iterate through schema properties to find array types
66
+ for (const [name, schema] of Object.entries(jsonSchema.properties || {})) {
67
+ const propSchema = schema;
68
+ // If this property is an array type and the value is a string, parse it
69
+ if (propSchema.type === 'array' && typeof parsedParams[name] === 'string') {
70
+ try {
71
+ const parsed = JSON.parse(parsedParams[name]);
72
+ // Validate it's actually an array after parsing
73
+ if (Array.isArray(parsed)) {
74
+ parsedParams[name] = parsed;
75
+ }
76
+ }
77
+ catch (error) {
78
+ // If parsing fails, log warning but keep original value
79
+ console.warn(`[ag-ui] Failed to parse array parameter "${name}":`, error);
80
+ }
81
+ }
82
+ }
83
+ return parsedParams;
84
+ }
85
+ /**
86
+ * Converts JSON Schema to CopilotKit parameter array format
87
+ *
88
+ * @param jsonSchema - JSON Schema parameters object
89
+ * @returns CopilotKit parameters array
90
+ */
91
+ function jsonSchemaToParameters(jsonSchema) {
92
+ const parameters = [];
93
+ const required = jsonSchema.required || [];
94
+ for (const [name, schema] of Object.entries(jsonSchema.properties || {})) {
95
+ const propSchema = schema;
96
+ // Map JSON Schema type to CopilotKit type
97
+ let copilotType = 'string';
98
+ if (propSchema.type === 'array') {
99
+ // Handle array types based on items type
100
+ const itemsType = propSchema.items?.type;
101
+ if (itemsType === 'object') {
102
+ copilotType = 'object[]';
103
+ }
104
+ else {
105
+ // For primitive arrays (number[], string[], etc.)
106
+ // CopilotKit doesn't have explicit support, but we can use the description
107
+ // to clarify and handle parsing in the handler
108
+ copilotType = 'string'; // Will be parsed as JSON array string
109
+ // Enhance description to clarify it's an array
110
+ propSchema.description =
111
+ `${propSchema.description || ''} (JSON array of ${itemsType || 'values'})`.trim();
112
+ }
113
+ }
114
+ else {
115
+ // Original logic for non-array types
116
+ copilotType =
117
+ propSchema.type || 'string';
118
+ }
119
+ const param = {
120
+ name,
121
+ type: copilotType,
122
+ description: propSchema.description,
123
+ required: required.includes(name),
124
+ };
125
+ // Handle nested object properties (for object types)
126
+ if (propSchema.type === 'object' && propSchema.properties) {
127
+ // Cast to proper type for recursive call
128
+ param.attributes = jsonSchemaToParameters({
129
+ type: 'object',
130
+ properties: propSchema.properties, // eslint-disable-line @typescript-eslint/no-explicit-any
131
+ required: propSchema.required || [],
132
+ });
133
+ }
134
+ parameters.push(param);
135
+ }
136
+ console.log('[ag-ui] Converted parameters:', JSON.stringify(parameters, null, 2));
137
+ return parameters;
138
+ }
139
+ /**
140
+ * Converts unified tool definition to CopilotKit action format
141
+ *
142
+ * @param definition - Tool definition
143
+ * @param operation - Core operation
144
+ * @param context - Execution context (documentId + executor)
145
+ * @returns CopilotKit action
146
+ */
147
+ export function createCopilotKitAction(definition, operation, context) {
148
+ // Create runner instance for this action
149
+ const runner = new OperationRunner();
150
+ const action = {
151
+ name: definition.toolReferenceName || definition.name,
152
+ description: definition.description,
153
+ parameters: jsonSchemaToParameters(definition.parameters),
154
+ // CRITICAL FIX: Set to true to enable CopilotKit's deduplication mechanism
155
+ // See https://github.com/CopilotKit/CopilotKit/issues/2310
156
+ // Without this, actions get re-executed cumulatively (A → A,B → A,B,C)
157
+ _isRenderAndWait: true,
158
+ handler: async (params) => {
159
+ console.log(`[ag-ui] ========== HANDLER CALLED ==========`);
160
+ console.log(`[ag-ui] Tool: ${definition.name}`);
161
+ console.log(`[ag-ui] Operation: ${definition.operation}`);
162
+ console.log(`[ag-ui] Params:`, params);
163
+ console.log(`[ag-ui] Context:`, context);
164
+ // Check for duplicate execution
165
+ const signature = `${definition.name}:${JSON.stringify(params)}`;
166
+ console.log(`[ag-ui] 🔍 Signature:`, signature.substring(0, 150));
167
+ if (isRecentDuplicate(definition.name, params)) {
168
+ console.log(`[ag-ui] 🚫 DUPLICATE DETECTED - Returning early`);
169
+ return 'Operation already executed recently (duplicate detected and skipped)';
170
+ }
171
+ console.log(`[ag-ui] ✅ NOT a duplicate - Proceeding with execution`);
172
+ try {
173
+ // Parse array parameters that CopilotKit sends as JSON strings
174
+ // CopilotKit doesn't have native support for primitive arrays, so they're sent as strings
175
+ const parsedParams = parseArrayParameters(params, definition.parameters);
176
+ console.log(`[ag-ui] Calling runner.execute with TOON format...`);
177
+ console.log(`[ag-ui] Operation name:`, operation);
178
+ console.log(`[ag-ui] Parsed params being passed:`, JSON.stringify(parsedParams, null, 2));
179
+ console.log(`[ag-ui] Context being passed:`, JSON.stringify(context, null, 2));
180
+ // Use OperationRunner to execute operation with TOON format
181
+ // TOON format returns human/LLM-readable string (default)
182
+ const result = await runner.execute(operation, parsedParams, {
183
+ ...context,
184
+ format: 'toon', // CopilotKit expects string responses
185
+ });
186
+ console.log(`[ag-ui] Operation result:`, result);
187
+ // Result is already a string (TOON format)
188
+ if (typeof result === 'string') {
189
+ return result;
190
+ }
191
+ // Fallback: if somehow we got an object (shouldn't happen with format='toon')
192
+ if (typeof result === 'object' && result !== null) {
193
+ const resultObj = result;
194
+ // Return success message if available
195
+ if (resultObj.message) {
196
+ return resultObj.message;
197
+ }
198
+ // Return error if failed
199
+ if (resultObj.success === false && resultObj.error) {
200
+ return `❌ Error: ${resultObj.error}`;
201
+ }
202
+ // Otherwise format as JSON
203
+ return JSON.stringify(result, null, 2);
204
+ }
205
+ return String(result);
206
+ }
207
+ catch (error) {
208
+ console.error(`[ag-ui] ❌ ERROR in handler:`, error);
209
+ console.error(`[ag-ui] Error stack:`, error instanceof Error ? error.stack : 'N/A');
210
+ const errorMessage = error instanceof Error ? error.message : String(error);
211
+ return `❌ Error: ${errorMessage}`;
212
+ }
213
+ },
214
+ // Optional: Custom render function for ag-ui
215
+ // Note: config supports platform-specific extensions, cast to any for flexibility
216
+ render: definition.config?.agui?.renderingHints?.customRender
217
+ ? definition.config.agui.renderingHints
218
+ .customRender
219
+ : undefined,
220
+ };
221
+ return action;
222
+ }
223
+ /**
224
+ * Creates CopilotKit actions from all tool definitions
225
+ *
226
+ * @param definitions - Tool definitions
227
+ * @param operations - Core operations registry
228
+ * @param context - Execution context (documentId + executor)
229
+ * @returns CopilotKit actions
230
+ */
231
+ export function createAllCopilotKitActions(definitions, operations, context) {
232
+ const actions = [];
233
+ for (const definition of definitions) {
234
+ const operation = operations[definition.operation];
235
+ if (!operation) {
236
+ console.warn(`[ag-ui Tools] No operation found for ${definition.name} (operation: ${definition.operation})`);
237
+ continue;
238
+ }
239
+ const action = createCopilotKitAction(definition, operation, context);
240
+ actions.push(action);
241
+ }
242
+ console.log(`[ag-ui Tools] Created ${actions.length} CopilotKit actions`);
243
+ return actions;
244
+ }