@datalayer/agent-runtimes 0.0.3 → 0.0.5

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.
@@ -61,6 +61,8 @@ export declare class VercelAIAdapter extends BaseProtocolAdapter {
61
61
  model?: string;
62
62
  /** Full conversation history to send with the message */
63
63
  messages?: ChatMessage[];
64
+ /** Builtin tools / MCP tools to enable for this request */
65
+ builtinTools?: string[];
64
66
  }): Promise<void>;
65
67
  /**
66
68
  * Parse SSE stream from Vercel AI
@@ -107,6 +107,11 @@ export class VercelAIAdapter extends BaseProtocolAdapter {
107
107
  ...(options?.tools && { tools: options.tools }),
108
108
  // Model override for per-request model selection
109
109
  ...(options?.model && { model: options.model }),
110
+ // Builtin tools / MCP tools to enable
111
+ ...(options?.builtinTools &&
112
+ options.builtinTools.length > 0 && {
113
+ builtinTools: options.builtinTools,
114
+ }),
110
115
  };
111
116
  if (options?.model) {
112
117
  console.log('[VercelAIAdapter] Sending with model:', options.model);
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Agent Runtime Custom Example Component
3
3
  *
4
- * Demonstrates the unified Chat component with Simple transport
4
+ * Demonstrates the unified Chat component with AG-UI transport
5
5
  * and all necessary providers:
6
6
  * - QueryClientProvider for data fetching
7
+ * - Auto-creates agent on the server if it doesn't exist
7
8
  */
8
9
  declare const AgentRuntimeCustomExample: React.FC;
9
10
  export default AgentRuntimeCustomExample;
@@ -3,7 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  * Copyright (c) 2025-2026 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
- import { Text } from '@primer/react';
6
+ import { useCallback, useEffect, useState } from 'react';
7
+ import { Spinner, Text } from '@primer/react';
7
8
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
8
9
  import { Box } from '@datalayer/primer-addons';
9
10
  import { datalayerTheme, DatalayerThemeProvider } from '@datalayer/core';
@@ -19,14 +20,107 @@ const queryClient = new QueryClient({
19
20
  },
20
21
  },
21
22
  });
23
+ const AGENT_NAME = 'datalayer-assistant';
24
+ const BASE_URL = 'http://localhost:8765';
25
+ /**
26
+ * Hook to ensure the agent exists on the server
27
+ * Creates the agent if it doesn't exist
28
+ */
29
+ function useEnsureAgent() {
30
+ const [agentId, setAgentId] = useState(null);
31
+ const [isLoading, setIsLoading] = useState(true);
32
+ const [error, setError] = useState(null);
33
+ const createAgent = useCallback(async () => {
34
+ try {
35
+ const response = await fetch(`${BASE_URL}/api/v1/agents`, {
36
+ method: 'POST',
37
+ headers: {
38
+ 'Content-Type': 'application/json',
39
+ },
40
+ body: JSON.stringify({
41
+ name: AGENT_NAME,
42
+ description: 'Datalayer AI Assistant - Your helpful coding companion',
43
+ agent_library: 'pydantic-ai',
44
+ transport: 'ag-ui',
45
+ model: 'openai:gpt-4o-mini',
46
+ system_prompt: `You are Datalayer Assistant, a helpful AI assistant specialized in data science, Python programming, and Jupyter notebooks.
47
+
48
+ You can help users with:
49
+ - Writing and debugging Python code
50
+ - Data analysis with pandas, numpy, and other libraries
51
+ - Creating visualizations with matplotlib, plotly, etc.
52
+ - Machine learning with scikit-learn, pytorch, etc.
53
+ - General programming questions
54
+
55
+ Be concise, helpful, and provide working code examples when appropriate.`,
56
+ }),
57
+ });
58
+ if (!response.ok) {
59
+ const errorData = await response
60
+ .json()
61
+ .catch(() => ({ detail: 'Unknown error' }));
62
+ throw new Error(errorData.detail || `Failed to create agent: ${response.status}`);
63
+ }
64
+ const data = await response.json();
65
+ console.log('[AgentRuntimeCustomExample] Agent created:', data);
66
+ return data.id;
67
+ }
68
+ catch (err) {
69
+ console.error('[AgentRuntimeCustomExample] Error creating agent:', err);
70
+ throw err;
71
+ }
72
+ }, []);
73
+ const checkOrCreateAgent = useCallback(async () => {
74
+ setIsLoading(true);
75
+ setError(null);
76
+ try {
77
+ // First, try to find existing agent by name
78
+ const listResponse = await fetch(`${BASE_URL}/api/v1/agents`);
79
+ if (listResponse.ok) {
80
+ const data = await listResponse.json();
81
+ // API may return { agents: [...] } or just [...]
82
+ const agents = Array.isArray(data) ? data : data.agents || [];
83
+ const existingAgent = agents.find((a) => a.name === AGENT_NAME);
84
+ if (existingAgent) {
85
+ console.log('[AgentRuntimeCustomExample] Found existing agent:', existingAgent.id);
86
+ setAgentId(existingAgent.id);
87
+ setIsLoading(false);
88
+ return;
89
+ }
90
+ }
91
+ // Agent doesn't exist, create it
92
+ console.log('[AgentRuntimeCustomExample] Creating new agent...');
93
+ const newAgentId = await createAgent();
94
+ if (newAgentId) {
95
+ setAgentId(newAgentId);
96
+ }
97
+ else {
98
+ setError('Failed to create agent');
99
+ }
100
+ }
101
+ catch (err) {
102
+ const errorMessage = err instanceof Error ? err.message : 'Failed to initialize agent';
103
+ setError(errorMessage);
104
+ }
105
+ finally {
106
+ setIsLoading(false);
107
+ }
108
+ }, [createAgent]);
109
+ useEffect(() => {
110
+ checkOrCreateAgent();
111
+ }, [checkOrCreateAgent]);
112
+ return { agentId, isLoading, error, retry: checkOrCreateAgent };
113
+ }
22
114
  /**
23
115
  * Agent Runtime Custom Example Component
24
116
  *
25
- * Demonstrates the unified Chat component with Simple transport
117
+ * Demonstrates the unified Chat component with AG-UI transport
26
118
  * and all necessary providers:
27
119
  * - QueryClientProvider for data fetching
120
+ * - Auto-creates agent on the server if it doesn't exist
28
121
  */
29
122
  const AgentRuntimeCustomExample = () => {
123
+ const { agentId, isLoading, error, retry } = useEnsureAgent();
30
124
  return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(DatalayerThemeProvider, { theme: datalayerTheme, children: _jsxs(Box, { sx: {
31
125
  display: 'flex',
32
126
  flexDirection: 'column',
@@ -41,12 +135,33 @@ const AgentRuntimeCustomExample = () => {
41
135
  fontWeight: 'bold',
42
136
  display: 'block',
43
137
  marginBottom: 1,
44
- }, children: "Agent Runtime Custom Example" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Interactive chat interface with AI assistance" })] }), _jsx(Box, { as: "main", sx: {
138
+ }, children: "Datalayer Assistant" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Interactive chat interface with AI assistance" })] }), _jsx(Box, { as: "main", sx: {
45
139
  flex: 1,
46
140
  overflow: 'hidden',
47
141
  display: 'flex',
48
142
  flexDirection: 'column',
49
- }, children: _jsx(Chat, { transport: "vercel-ai-jupyter", showModelSelector: true, showToolsMenu: true, height: "100%", suggestions: [
143
+ }, children: isLoading ? (_jsxs(Box, { sx: {
144
+ display: 'flex',
145
+ flexDirection: 'column',
146
+ alignItems: 'center',
147
+ justifyContent: 'center',
148
+ height: '100%',
149
+ gap: 3,
150
+ }, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Initializing agent..." })] })) : error ? (_jsxs(Box, { sx: {
151
+ display: 'flex',
152
+ flexDirection: 'column',
153
+ alignItems: 'center',
154
+ justifyContent: 'center',
155
+ height: '100%',
156
+ gap: 3,
157
+ p: 4,
158
+ }, children: [_jsx(Text, { sx: { color: 'danger.fg', fontWeight: 'bold' }, children: "Failed to initialize agent" }), _jsx(Text, { sx: { color: 'fg.muted', textAlign: 'center' }, children: error }), _jsx(Text, { as: "button", onClick: retry, sx: {
159
+ color: 'accent.fg',
160
+ cursor: 'pointer',
161
+ textDecoration: 'underline',
162
+ border: 'none',
163
+ background: 'none',
164
+ }, children: "Retry" })] })) : agentId ? (_jsx(Chat, { transport: "ag-ui", agentId: agentId, showModelSelector: true, showToolsMenu: true, height: "100%", suggestions: [
50
165
  {
51
166
  title: '👋 Say hello',
52
167
  message: 'Hello! What can you help me with today?',
@@ -63,6 +178,6 @@ const AgentRuntimeCustomExample = () => {
63
178
  title: '📊 Data analysis',
64
179
  message: 'How do I analyze data with pandas?',
65
180
  },
66
- ] }) })] }) }) }));
181
+ ] })) : null })] }) }) }));
67
182
  };
68
183
  export default AgentRuntimeCustomExample;
@@ -1,8 +1,8 @@
1
- import type { IAIAgent } from '../models';
1
+ import type { Agent } from '../state';
2
2
  /**
3
3
  * Get the document AI Agent if any.
4
4
  *
5
5
  * It handles checking the AI Agent is alive so it should only be use once per document.
6
6
  */
7
- export declare function useNotebookAIAgent(notebookId: string): IAIAgent | undefined;
7
+ export declare function useNotebookAIAgent(notebookId: string): Agent | undefined;
8
8
  export default useNotebookAIAgent;
@@ -3,7 +3,7 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import { useEffect } from 'react';
6
- import { useAIAgentStore } from '../state';
6
+ import { useAgentStore } from '../state';
7
7
  import { useAIAgents } from './useAgents';
8
8
  /**
9
9
  * Get the document AI Agent if any.
@@ -12,7 +12,10 @@ import { useAIAgents } from './useAgents';
12
12
  */
13
13
  export function useNotebookAIAgent(notebookId) {
14
14
  const { getAIAgent } = useAIAgents();
15
- const { aiAgents, addAIAgent, deleteAIAgent, updateAIAgent } = useAIAgentStore();
15
+ const agents = useAgentStore(state => state.agents);
16
+ const upsertAgent = useAgentStore(state => state.upsertAgent);
17
+ const deleteAgent = useAgentStore(state => state.deleteAgent);
18
+ const getAgentById = useAgentStore(state => state.getAgentById);
16
19
  // Check AI Agent is alive.
17
20
  useEffect(() => {
18
21
  let abortController;
@@ -23,25 +26,39 @@ export function useNotebookAIAgent(notebookId) {
23
26
  signal: abortController.signal,
24
27
  });
25
28
  if (!response.success) {
26
- deleteAIAgent(notebookId);
29
+ deleteAgent(notebookId);
27
30
  return;
28
31
  }
29
- const currentAgent = aiAgents.find(agent => agent.documentId === notebookId);
32
+ const currentAgent = getAgentById(notebookId);
30
33
  const runtimeId = response.agent.runtime?.id;
31
34
  if (currentAgent) {
35
+ // Update existing agent if runtime changed
32
36
  if (currentAgent.runtimeId !== runtimeId) {
33
- updateAIAgent(notebookId, runtimeId);
37
+ upsertAgent({
38
+ id: notebookId,
39
+ baseUrl: currentAgent.baseUrl,
40
+ transport: currentAgent.transport,
41
+ runtimeId,
42
+ status: 'running',
43
+ });
34
44
  }
35
45
  }
36
46
  else {
37
- addAIAgent({
47
+ // Add new agent - assume it's a notebook agent with document tracking
48
+ upsertAgent({
49
+ id: notebookId,
50
+ name: `Notebook ${notebookId}`,
51
+ description: 'AI agent for notebook',
52
+ baseUrl: '', // Will be set by the actual agent implementation
53
+ transport: 'vercel-ai', // Default transport for notebook agents
38
54
  documentId: notebookId,
39
55
  runtimeId,
56
+ status: 'running',
40
57
  });
41
58
  }
42
59
  }
43
60
  catch (r) {
44
- deleteAIAgent(notebookId);
61
+ deleteAgent(notebookId);
45
62
  }
46
63
  };
47
64
  const refreshInterval = setInterval(refreshAIAgent, 60_000);
@@ -49,8 +66,8 @@ export function useNotebookAIAgent(notebookId) {
49
66
  abortController?.abort('Component unmounted');
50
67
  clearInterval(refreshInterval);
51
68
  };
52
- }, [aiAgents, notebookId]);
53
- const aiAgent = aiAgents.find(aiAgent => aiAgent.documentId === notebookId);
69
+ }, [agents, notebookId, getAIAgent, deleteAgent, getAgentById, upsertAgent]);
70
+ const aiAgent = getAgentById(notebookId);
54
71
  return aiAgent;
55
72
  }
56
73
  export default useNotebookAIAgent;
package/lib/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './components';
2
+ export * from './state';
package/lib/index.js CHANGED
@@ -3,3 +3,4 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  export * from './components';
6
+ export * from './state';
@@ -1,11 +1,81 @@
1
- import type { IAIAgent } from '../../models';
2
- export type AIAgentState = {
3
- aiAgents: readonly IAIAgent[];
4
- addAIAgent: (aiAgent: IAIAgent) => void;
5
- deleteAIAgent: (documentId: string) => void;
6
- updateAIAgent: (documentId: string, runtimeId?: string) => void;
1
+ import type { Transport } from '../../components/chat/components/Chat';
2
+ export type AgentStatus = 'running' | 'paused' | 'initializing' | 'error';
3
+ export type { Transport };
4
+ /**
5
+ * Unified Agent model combining runtime tracking and UI state
6
+ */
7
+ export interface Agent {
8
+ /** Unique agent identifier */
9
+ id: string;
10
+ /** Display name */
11
+ name: string;
12
+ /** Agent description */
13
+ description: string;
14
+ /** Base URL for the agent (for Jupyter: baseUrl, for FastAPI: baseUrl) */
15
+ baseUrl: string;
16
+ /** Transport protocol used */
17
+ transport: Transport;
18
+ /** Current status */
19
+ status: AgentStatus;
20
+ /** Last error message if status is 'error' */
21
+ error?: string | null;
22
+ /** Timestamp of last update */
23
+ lastUpdated: number;
24
+ /** Document ID (for document-based agents) */
25
+ documentId?: string;
26
+ /** Runtime ID (for Jupyter kernel-based agents) */
27
+ runtimeId?: string;
28
+ /** Author name (optional, for display) */
29
+ author?: string;
30
+ /** Avatar URL (optional, for display) */
31
+ avatarUrl?: string;
32
+ }
33
+ export type AgentState = {
34
+ /** All registered agents */
35
+ agents: readonly Agent[];
36
+ /** Add or update an agent */
37
+ upsertAgent: (agent: Partial<Agent> & {
38
+ id: string;
39
+ baseUrl: string;
40
+ transport: Transport;
41
+ }) => void;
42
+ /** Get agent by ID */
43
+ getAgentById: (id: string) => Agent | undefined;
44
+ /** Get agent by baseUrl and transport */
45
+ getAgentByUrl: (baseUrl: string, transport: Transport) => Agent | undefined;
46
+ /** Update agent status */
47
+ updateAgentStatus: (id: string, status: AgentStatus, error?: string | null) => void;
48
+ /** Toggle agent status between running/paused */
49
+ toggleAgentStatus: (id: string) => void;
50
+ /** Delete an agent */
51
+ deleteAgent: (id: string) => void;
52
+ /** Clear all agents */
53
+ clearAgents: () => void;
7
54
  };
8
- export declare const aiAgentStore: import("zustand").StoreApi<AIAgentState>;
9
- export declare function useAIAgentStore(): AIAgentState;
10
- export declare function useAIAgentStore<T>(selector: (state: AIAgentState) => T): T;
11
- export default useAIAgentStore;
55
+ export declare const agentStore: Omit<import("zustand").StoreApi<AgentState>, "persist"> & {
56
+ persist: {
57
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<AgentState, unknown>>) => void;
58
+ clearStorage: () => void;
59
+ rehydrate: () => Promise<void> | void;
60
+ hasHydrated: () => boolean;
61
+ onHydrate: (fn: (state: AgentState) => void) => () => void;
62
+ onFinishHydration: (fn: (state: AgentState) => void) => () => void;
63
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<AgentState, unknown>>;
64
+ };
65
+ };
66
+ export declare function useAgentStore(): AgentState;
67
+ export declare function useAgentStore<T>(selector: (state: AgentState) => T): T;
68
+ export type AIAgentState = AgentState;
69
+ export declare const aiAgentStore: Omit<import("zustand").StoreApi<AgentState>, "persist"> & {
70
+ persist: {
71
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<AgentState, unknown>>) => void;
72
+ clearStorage: () => void;
73
+ rehydrate: () => Promise<void> | void;
74
+ hasHydrated: () => boolean;
75
+ onHydrate: (fn: (state: AgentState) => void) => () => void;
76
+ onFinishHydration: (fn: (state: AgentState) => void) => () => void;
77
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<AgentState, unknown>>;
78
+ };
79
+ };
80
+ export declare const useAIAgentStore: typeof useAgentStore;
81
+ export default useAgentStore;
@@ -4,39 +4,105 @@
4
4
  */
5
5
  import { createStore } from 'zustand/vanilla';
6
6
  import { useStore } from 'zustand';
7
- export const aiAgentStore = createStore(set => ({
8
- aiAgents: [],
9
- addAIAgent: (aiAgent) => {
10
- set((state) => {
11
- if (!state.aiAgents.includes(aiAgent)) {
12
- return { aiAgents: state.aiAgents.concat([aiAgent]) };
7
+ import { persist, createJSONStorage } from 'zustand/middleware';
8
+ export const agentStore = createStore()(persist((set, get) => ({
9
+ agents: [],
10
+ upsertAgent: agentData => {
11
+ set(state => {
12
+ const existingIndex = state.agents.findIndex(a => a.id === agentData.id);
13
+ const now = Date.now();
14
+ if (existingIndex >= 0) {
15
+ // Update existing agent
16
+ const updatedAgents = [...state.agents];
17
+ updatedAgents[existingIndex] = {
18
+ ...updatedAgents[existingIndex],
19
+ ...agentData,
20
+ lastUpdated: now,
21
+ };
22
+ return { agents: updatedAgents };
13
23
  }
14
24
  else {
15
- return {};
25
+ // Add new agent
26
+ const newAgent = {
27
+ name: agentData.name || agentData.id,
28
+ description: agentData.description || '',
29
+ status: agentData.status || 'initializing',
30
+ lastUpdated: now,
31
+ ...agentData,
32
+ };
33
+ return { agents: [...state.agents, newAgent] };
16
34
  }
17
35
  });
18
36
  },
19
- deleteAIAgent: (documentId) => {
20
- set((state) => {
21
- return {
22
- aiAgents: state.aiAgents.filter(a => a.documentId === documentId),
23
- };
24
- });
37
+ getAgentById: (id) => {
38
+ const { agents } = get();
39
+ return agents.find(agent => agent.id === id);
40
+ },
41
+ getAgentByUrl: (baseUrl, transport) => {
42
+ const { agents } = get();
43
+ return agents.find(agent => agent.baseUrl === baseUrl && agent.transport === transport);
25
44
  },
26
- updateAIAgent: (documentId, runtimeId) => {
27
- set((state) => {
28
- const index = state.aiAgents.findIndex(aiAgent => aiAgent.documentId === documentId);
45
+ updateAgentStatus: (id, status, error = null) => {
46
+ set(state => {
47
+ const index = state.agents.findIndex(a => a.id === id);
29
48
  if (index >= 0) {
30
- state.aiAgents[index].runtimeId = runtimeId;
31
- return { aiAgents: [...state.aiAgents] };
49
+ const updatedAgents = [...state.agents];
50
+ updatedAgents[index] = {
51
+ ...updatedAgents[index],
52
+ status,
53
+ error,
54
+ lastUpdated: Date.now(),
55
+ };
56
+ return { agents: updatedAgents };
32
57
  }
33
- else {
34
- return {};
58
+ return {};
59
+ });
60
+ },
61
+ toggleAgentStatus: (id) => {
62
+ set(state => {
63
+ const index = state.agents.findIndex(a => a.id === id);
64
+ if (index >= 0) {
65
+ const updatedAgents = [...state.agents];
66
+ const currentStatus = updatedAgents[index].status;
67
+ updatedAgents[index] = {
68
+ ...updatedAgents[index],
69
+ status: currentStatus === 'running' ? 'paused' : 'running',
70
+ lastUpdated: Date.now(),
71
+ };
72
+ return { agents: updatedAgents };
35
73
  }
74
+ return {};
36
75
  });
37
76
  },
77
+ deleteAgent: (id) => {
78
+ set(state => ({
79
+ agents: state.agents.filter(a => a.id !== id),
80
+ }));
81
+ },
82
+ clearAgents: () => {
83
+ set({ agents: [] });
84
+ },
85
+ }), {
86
+ name: 'agent-runtimes-storage',
87
+ storage: createJSONStorage(() => localStorage),
88
+ // Only persist essential fields
89
+ partialize: state => ({
90
+ agents: state.agents.map(agent => ({
91
+ id: agent.id,
92
+ name: agent.name,
93
+ description: agent.description,
94
+ baseUrl: agent.baseUrl,
95
+ transport: agent.transport,
96
+ status: agent.status,
97
+ lastUpdated: agent.lastUpdated,
98
+ documentId: agent.documentId,
99
+ runtimeId: agent.runtimeId,
100
+ })),
101
+ }),
38
102
  }));
39
- export function useAIAgentStore(selector) {
40
- return useStore(aiAgentStore, selector);
103
+ export function useAgentStore(selector) {
104
+ return useStore(agentStore, selector);
41
105
  }
42
- export default useAIAgentStore;
106
+ export const aiAgentStore = agentStore;
107
+ export const useAIAgentStore = useAgentStore;
108
+ export default useAgentStore;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/agent-runtimes",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",
@@ -28,7 +28,9 @@
28
28
  "files": [
29
29
  "lib/**/*.{css,d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
30
30
  "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
31
- "schema/*.json"
31
+ "schema/*.json",
32
+ "patches/",
33
+ "scripts/"
32
34
  ],
33
35
  "main": "lib/index.js",
34
36
  "types": "lib/index.d.ts",
@@ -118,7 +120,7 @@
118
120
  "@ag-ui/encoder": "^0.0.42",
119
121
  "@ag-ui/proto": "^0.0.42",
120
122
  "@agentclientprotocol/sdk": "^0.8.0",
121
- "@ai-sdk/react": "3.0.0-beta.172",
123
+ "@ai-sdk/react": "3.0.30",
122
124
  "@anthropic-ai/sdk": "^0.52.0",
123
125
  "@datalayer/core": "^0.0.24",
124
126
  "@datalayer/icons-react": "^1.0.6",
@@ -177,7 +179,7 @@
177
179
  "@tanstack/react-query": "^5.90.6",
178
180
  "@toon-format/toon": "^1.3.0",
179
181
  "@xyflow/react": "^12.10.0",
180
- "ai": "^6.0.6",
182
+ "ai": "6.0.28",
181
183
  "ansi-to-html": "^0.7.2",
182
184
  "axios": "^1.7.7",
183
185
  "boring-avatars": "^2.0.1",
@@ -281,7 +283,7 @@
281
283
  "vitest": "^3.2.4"
282
284
  },
283
285
  "resolutions": {
284
- "@ai-sdk/react": "3.0.0-beta.172",
286
+ "@ai-sdk/react": "3.0.30",
285
287
  "@microsoft/fast-colors": "5.3.1",
286
288
  "@microsoft/fast-components": "2.30.6",
287
289
  "@microsoft/fast-element": "1.13.0",
@@ -291,14 +293,14 @@
291
293
  "@microsoft/load-themed-styles": "1.10.295",
292
294
  "@types/react": "18.3.20",
293
295
  "@types/react-dom": "18.3.6",
294
- "ai": "^6.0.6",
296
+ "ai": "6.0.28",
295
297
  "pyodide": "0.28.0",
296
298
  "react": "18.3.1",
297
299
  "react-dom": "18.3.1",
298
300
  "typescript": "^5.8.3"
299
301
  },
300
302
  "overrides": {
301
- "@ai-sdk/react": "3.0.0-beta.172",
303
+ "@ai-sdk/react": "3.0.30",
302
304
  "@microsoft/fast-colors": "5.3.1",
303
305
  "@microsoft/fast-components": "2.30.6",
304
306
  "@microsoft/fast-element": "1.13.0",
@@ -308,7 +310,7 @@
308
310
  "@microsoft/load-themed-styles": "1.10.295",
309
311
  "@types/react": "18.3.20",
310
312
  "@types/react-dom": "18.3.6",
311
- "ai": "^6.0.6",
313
+ "ai": "6.0.28",
312
314
  "pyodide": "0.28.0",
313
315
  "react": "18.3.1",
314
316
  "react-dom": "18.3.1",
@@ -0,0 +1 @@
1
+ # Patches directory for patch-package
@@ -0,0 +1,26 @@
1
+ #!/bin/bash
2
+ # Copyright (c) 2025-2026 Datalayer, Inc.
3
+ # Distributed under the terms of the Modified BSD License.
4
+
5
+ # Apply patch-package patches for jupyter packages
6
+ # This is normally run automatically via npm's postinstall hook,
7
+ # but can be run manually if needed.
8
+
9
+ set -e
10
+
11
+ # Colors for output
12
+ GREEN='\033[0;32m'
13
+ BLUE='\033[0;34m'
14
+ NC='\033[0m' # No Color
15
+
16
+ echo -e "${BLUE}📝 Applying patches...${NC}"
17
+
18
+ # Get the script directory and project root
19
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
20
+ CORE_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )"
21
+
22
+ cd "$CORE_ROOT"
23
+
24
+ npx patch-package
25
+
26
+ echo -e "${GREEN}✅ Patches applied successfully${NC}"
@@ -0,0 +1,40 @@
1
+ #!/bin/bash
2
+ # Copyright (c) 2025-2026 Datalayer, Inc.
3
+ # Distributed under the terms of the Modified BSD License.
4
+
5
+ # Create patch-package patches for locally modified jupyter packages
6
+ # This script generates patches that can be committed to the repo and
7
+ # applied automatically during npm install via the postinstall hook.
8
+
9
+ set -e
10
+
11
+ # Colors for output
12
+ GREEN='\033[0;32m'
13
+ BLUE='\033[0;34m'
14
+ YELLOW='\033[1;33m'
15
+ NC='\033[0m' # No Color
16
+
17
+ echo -e "${BLUE}🔧 Creating patches for jupyter packages...${NC}"
18
+
19
+ # Get the script directory and project root
20
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
21
+ CORE_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )"
22
+
23
+ cd "$CORE_ROOT"
24
+
25
+ # First, sync the latest changes from jupyter-ui to ensure patches include all modifications
26
+ echo -e "${BLUE}🔄 Syncing latest changes from jupyter-ui...${NC}"
27
+ bash "$SCRIPT_DIR/sync-jupyter.sh"
28
+
29
+ # Ensure package-lock.json exists (required by patch-package)
30
+ if [ ! -f "package-lock.json" ]; then
31
+ echo -e "${YELLOW}⚠️ No package-lock.json found. Creating one...${NC}"
32
+ npm i --package-lock-only
33
+ fi
34
+
35
+ # Create patches
36
+ echo -e "${BLUE}📝 Generating patches with patch-package...${NC}"
37
+ npx patch-package @datalayer/jupyter-lexical @datalayer/jupyter-react
38
+
39
+ echo -e "${GREEN}✅ Patches created successfully in patches/ directory${NC}"
40
+ echo -e "${BLUE}ℹ️ Patches will be applied automatically on 'npm install' via postinstall hook${NC}"
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright (c) 2025-2026 Datalayer, Inc.
3
+ # Distributed under the terms of the Modified BSD License.
4
+
5
+ """
6
+ Download AI Elements components directly from the registry
7
+ """
8
+
9
+ import json
10
+ import os
11
+ import urllib.request
12
+ import sys
13
+
14
+ BASE_URL = "https://registry.ai-sdk.dev"
15
+ COMPONENTS_DIR = "src/components/ai-elements"
16
+
17
+ # Components to download
18
+ COMPONENTS = [
19
+ "message",
20
+ "conversation",
21
+ "prompt-input",
22
+ "model-selector",
23
+ "artifact",
24
+ "code-block",
25
+ "suggestion",
26
+ "sources",
27
+ "reasoning",
28
+ "tool",
29
+ "loader",
30
+ "shimmer",
31
+ ]
32
+
33
+ def download_component(component_name):
34
+ """Download a component from the registry"""
35
+ url = f"{BASE_URL}/{component_name}.json"
36
+ print(f"Downloading {component_name}...")
37
+
38
+ try:
39
+ with urllib.request.urlopen(url) as response:
40
+ data = json.loads(response.read())
41
+
42
+ # Get the first file (main component file)
43
+ if 'files' in data and len(data['files']) > 0:
44
+ file_data = data['files'][0]
45
+ content = file_data.get('content', '')
46
+ target = file_data.get('target', f"components/ai-elements/{component_name}.tsx")
47
+
48
+ # Adjust target path
49
+ target_path = os.path.join(COMPONENTS_DIR, f"{component_name}.tsx")
50
+
51
+ # Ensure directory exists
52
+ os.makedirs(os.path.dirname(target_path), exist_ok=True)
53
+
54
+ # Write file
55
+ with open(target_path, 'w') as f:
56
+ f.write(content)
57
+
58
+ print(f"✓ Downloaded {component_name} to {target_path}")
59
+ return True
60
+ else:
61
+ print(f"✗ No files found for {component_name}")
62
+ return False
63
+
64
+ except Exception as e:
65
+ print(f"✗ Error downloading {component_name}: {e}")
66
+ return False
67
+
68
+ def main():
69
+ print(f"Downloading AI Elements components to {COMPONENTS_DIR}")
70
+ print(f"Components: {', '.join(COMPONENTS)}\n")
71
+
72
+ # Create components directory
73
+ os.makedirs(COMPONENTS_DIR, exist_ok=True)
74
+
75
+ success_count = 0
76
+ for component in COMPONENTS:
77
+ if download_component(component):
78
+ success_count += 1
79
+
80
+ print(f"\n✓ Successfully downloaded {success_count}/{len(COMPONENTS)} components")
81
+
82
+ if success_count < len(COMPONENTS):
83
+ sys.exit(1)
84
+
85
+ if __name__ == "__main__":
86
+ main()
@@ -0,0 +1,123 @@
1
+ #!/bin/bash
2
+ # Copyright (c) 2025-2026 Datalayer, Inc.
3
+ # Distributed under the terms of the Modified BSD License.
4
+
5
+ # Sync local jupyter-ui packages to @datalayer/agent-runtimes node_modules
6
+ # This script builds the local jupyter packages and copies their lib/ outputs
7
+ # into the core package's node_modules for quick testing during development.
8
+ #
9
+ # Usage:
10
+ # ./sync-jupyter.sh # Run once and exit
11
+ # ./sync-jupyter.sh --watch # Watch for changes and auto-sync
12
+
13
+ set -e
14
+
15
+ # Colors for output
16
+ GREEN='\033[0;32m'
17
+ BLUE='\033[0;34m'
18
+ YELLOW='\033[1;33m'
19
+ NC='\033[0m' # No Color
20
+
21
+ # Get the script directory and project root
22
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
23
+ CORE_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )"
24
+ JUPYTER_UI_ROOT="$( cd "$CORE_ROOT/../jupyter-ui" && pwd )"
25
+
26
+ # Function to perform the sync
27
+ sync_packages() {
28
+ echo -e "${BLUE}🔄 Syncing jupyter-ui packages to @datalayer/agent-runtimes...${NC}"
29
+
30
+ # Build jupyter-react FIRST (lexical depends on it)
31
+ echo -e "${BLUE}📦 Building @datalayer/jupyter-react...${NC}"
32
+ cd "$JUPYTER_UI_ROOT/packages/react"
33
+ echo -e "${YELLOW}[DEBUG] Current directory: $(pwd)${NC}"
34
+ rm -f tsconfig.tsbuildinfo
35
+ rm -rf lib
36
+ echo -e "${YELLOW}[DEBUG] Running gulp resources-to-lib...${NC}"
37
+ npx gulp resources-to-lib
38
+ echo -e "${YELLOW}[DEBUG] Running TypeScript...${NC}"
39
+ npx tsc --noEmitOnError false
40
+ TSC_EXIT=$?
41
+ echo -e "${YELLOW}[DEBUG] TypeScript exit code: $TSC_EXIT${NC}"
42
+ echo -e "${YELLOW}[DEBUG] Checking if lib exists...${NC}"
43
+ ls -la lib 2>&1 | head -5
44
+
45
+ # Verify lib was created
46
+ if [ ! -d "lib" ]; then
47
+ echo -e "${YELLOW}⚠️ lib directory was not created by TypeScript!${NC}"
48
+ exit 1
49
+ fi
50
+ echo -e "${YELLOW}[DEBUG] lib directory verified!${NC}"
51
+
52
+ # Copy react to core's node_modules for patch-package
53
+ echo -e "${BLUE}📋 Copying react to core/node_modules...${NC}"
54
+ cd "$CORE_ROOT"
55
+ # Only replace lib/ directory, preserving LICENSE, README.md, etc.
56
+ rm -rf node_modules/@datalayer/jupyter-react/lib
57
+ mkdir -p node_modules/@datalayer/jupyter-react/lib
58
+ cp -r "$JUPYTER_UI_ROOT/packages/react/lib/." node_modules/@datalayer/jupyter-react/lib/
59
+ cp "$JUPYTER_UI_ROOT/packages/react/package.json" node_modules/@datalayer/jupyter-react/
60
+
61
+ # Now build jupyter-lexical (finds react via workspace hoisting)
62
+ echo -e "${BLUE}📦 Building @datalayer/jupyter-lexical...${NC}"
63
+ cd "$JUPYTER_UI_ROOT/packages/lexical"
64
+ rm -f tsconfig.tsbuildinfo
65
+ rm -rf lib
66
+ echo -e "${YELLOW}[DEBUG] Running gulp resources-to-lib...${NC}"
67
+ npx gulp resources-to-lib
68
+ echo -e "${YELLOW}[DEBUG] Running TypeScript...${NC}"
69
+ npx tsc --noEmitOnError false
70
+
71
+ # Copy lexical to node_modules
72
+ echo -e "${BLUE}📋 Copying lexical to node_modules...${NC}"
73
+ cd "$CORE_ROOT"
74
+ # Only replace lib/ directory, preserving LICENSE, README.md, etc.
75
+ rm -rf node_modules/@datalayer/jupyter-lexical/lib
76
+ mkdir -p node_modules/@datalayer/jupyter-lexical/lib
77
+ cp -r "$JUPYTER_UI_ROOT/packages/lexical/lib/." node_modules/@datalayer/jupyter-lexical/lib/
78
+ cp "$JUPYTER_UI_ROOT/packages/lexical/package.json" node_modules/@datalayer/jupyter-lexical/
79
+
80
+ echo -e "${GREEN}✅ Successfully synced at $(date +"%H:%M:%S")${NC}"
81
+ }
82
+
83
+ # Check if watch mode is requested
84
+ if [[ "$1" == "--watch" ]]; then
85
+ # Check if fswatch is installed
86
+ if ! command -v fswatch &> /dev/null; then
87
+ echo -e "${YELLOW}⚠️ fswatch not found. Installing via Homebrew...${NC}"
88
+ if command -v brew &> /dev/null; then
89
+ brew install fswatch
90
+ else
91
+ echo -e "${YELLOW}⚠️ Homebrew not found. Please install fswatch manually:${NC}"
92
+ echo -e "${YELLOW} brew install fswatch${NC}"
93
+ echo -e "${YELLOW} or visit: https://github.com/emcrisostomo/fswatch${NC}"
94
+ exit 1
95
+ fi
96
+ fi
97
+
98
+ echo -e "${BLUE}👁️ Watch mode enabled. Monitoring jupyter-ui packages for changes...${NC}"
99
+ echo -e "${YELLOW}📁 Watching:${NC}"
100
+ echo -e "${YELLOW} - $JUPYTER_UI_ROOT/packages/lexical/src${NC}"
101
+ echo -e "${YELLOW} - $JUPYTER_UI_ROOT/packages/react/src${NC}"
102
+ echo -e "${YELLOW}Press Ctrl+C to stop${NC}"
103
+ echo ""
104
+
105
+ # Initial sync
106
+ sync_packages
107
+
108
+ # Watch for changes in src directories and trigger sync
109
+ # Using fswatch with:
110
+ # -r: recursive
111
+ # -e: exclude patterns (node_modules, lib, etc.)
112
+ # -l 1: latency 1 second (debounce rapid changes)
113
+ fswatch -r -l 1 \
114
+ -e ".*" -i "\\.tsx?$" -i "\\.jsx?$" -i "\\.css$" \
115
+ "$JUPYTER_UI_ROOT/packages/lexical/src" \
116
+ "$JUPYTER_UI_ROOT/packages/react/src" | while read -r file; do
117
+ echo -e "\n${YELLOW}📝 Change detected in: $(basename "$file")${NC}"
118
+ sync_packages
119
+ done
120
+ else
121
+ # Single run mode
122
+ sync_packages
123
+ fi