deepagentsdk 0.12.0 → 0.13.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 (37) hide show
  1. package/dist/adapters/elements/index.cjs +244 -294
  2. package/dist/adapters/elements/index.cjs.map +1 -1
  3. package/dist/adapters/elements/index.d.cts +84 -174
  4. package/dist/adapters/elements/index.d.mts +84 -174
  5. package/dist/adapters/elements/index.mjs +238 -290
  6. package/dist/adapters/elements/index.mjs.map +1 -1
  7. package/dist/{types-4g9UvXal.d.mts → agent-BDM-PIu8.d.mts} +374 -25
  8. package/dist/{types-IulnvhFg.d.cts → agent-DToEVxs-.d.cts} +374 -25
  9. package/dist/{chunk-CbDLau6x.cjs → chunk-C5azi7Hr.cjs} +33 -0
  10. package/dist/cli/index.cjs +12 -12
  11. package/dist/cli/index.cjs.map +1 -1
  12. package/dist/cli/index.mjs +2 -2
  13. package/dist/cli/index.mjs.map +1 -1
  14. package/dist/{agent-Cuks-Idh.cjs → file-saver-BYPKakT4.cjs} +799 -205
  15. package/dist/file-saver-BYPKakT4.cjs.map +1 -0
  16. package/dist/{agent-CrH-He58.mjs → file-saver-Hj5so3dV.mjs} +793 -199
  17. package/dist/file-saver-Hj5so3dV.mjs.map +1 -0
  18. package/dist/index.cjs +83 -73
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +5 -353
  21. package/dist/index.d.mts +5 -353
  22. package/dist/index.mjs +13 -3
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/{load-B6CA5js_.mjs → load-BBYEnMwz.mjs} +1 -1
  25. package/dist/{load-B6CA5js_.mjs.map → load-BBYEnMwz.mjs.map} +1 -1
  26. package/dist/{load-94gjHorc.mjs → load-BDxe6Cet.mjs} +1 -1
  27. package/dist/{load-79a2H4m0.cjs → load-BrRAKlO6.cjs} +2 -2
  28. package/dist/{load-79a2H4m0.cjs.map → load-BrRAKlO6.cjs.map} +1 -1
  29. package/dist/load-DqllBbDc.cjs +4 -0
  30. package/package.json +1 -1
  31. package/dist/agent-CrH-He58.mjs.map +0 -1
  32. package/dist/agent-Cuks-Idh.cjs.map +0 -1
  33. package/dist/file-saver-BJCqMIb5.mjs +0 -655
  34. package/dist/file-saver-BJCqMIb5.mjs.map +0 -1
  35. package/dist/file-saver-C6O2LAvg.cjs +0 -679
  36. package/dist/file-saver-C6O2LAvg.cjs.map +0 -1
  37. package/dist/load-C2qVmZMp.cjs +0 -3
@@ -1,324 +1,274 @@
1
- const require_chunk = require('../../chunk-CbDLau6x.cjs');
2
- const require_agent = require('../../agent-Cuks-Idh.cjs');
3
- let react = require("react");
1
+ const require_chunk = require('../../chunk-C5azi7Hr.cjs');
2
+ let ai = require("ai");
4
3
 
5
- //#region src/adapters/elements/messageAdapter.ts
4
+ //#region src/adapters/elements/createElementsRouteHandler.ts
6
5
  /**
7
- * Converts agent event log to UIMessage format expected by Elements
6
+ * Server-side route handler adapter for AI SDK Elements
8
7
  *
9
- * @param events - Array of agent events from useAgent hook
10
- * @param streamingText - Current streaming text (if any)
11
- * @param uiStatus - Current UI status
12
- * @returns Array of UIMessage objects for Elements Message component
8
+ * Creates a Next.js/Express-compatible route handler that runs DeepAgent
9
+ * and returns UI Message Stream compatible responses for use with useChat.
13
10
  *
14
- * Conversion logic:
15
- * 1. Group events by role (user/assistant)
16
- * 2. Convert each event type to appropriate UIMessagePart
17
- * 3. Handle streaming text as in-progress message
18
- * 4. Preserve event order and tool call/result pairing
19
- */
20
- function convertEventsToUIMessages(events, streamingText, uiStatus) {
21
- const messages = [];
22
- let currentAssistantParts = [];
23
- let messageIdCounter = 0;
24
- const generateMessageId = () => {
25
- return `msg-${Date.now()}-${++messageIdCounter}`;
26
- };
27
- for (const eventLog of events) {
28
- const event = eventLog.event;
29
- switch (event.type) {
30
- case "user-message":
31
- if (currentAssistantParts.length > 0) {
32
- messages.push({
33
- id: generateMessageId(),
34
- role: "assistant",
35
- parts: currentAssistantParts,
36
- status: "ready"
37
- });
38
- currentAssistantParts = [];
39
- }
40
- messages.push({
41
- id: eventLog.id,
42
- role: "user",
43
- parts: [{
44
- type: "text",
45
- text: event.content
46
- }],
47
- status: "ready"
48
- });
49
- break;
50
- case "text-segment":
51
- currentAssistantParts.push({
52
- type: "text",
53
- text: event.text
54
- });
55
- break;
56
- case "tool-call":
57
- currentAssistantParts.push({
58
- type: "tool-call",
59
- toolCallId: event.toolCallId,
60
- toolName: event.toolName,
61
- args: event.args
62
- });
63
- break;
64
- case "tool-result":
65
- currentAssistantParts.push({
66
- type: "tool-result",
67
- toolCallId: event.toolCallId,
68
- toolName: event.toolName,
69
- result: event.result,
70
- isError: event.isError
71
- });
72
- break;
73
- default: break;
74
- }
75
- }
76
- if (streamingText || currentAssistantParts.length > 0) {
77
- if (streamingText) currentAssistantParts.push({
78
- type: "text",
79
- text: streamingText
80
- });
81
- let messageStatus = "ready";
82
- if (uiStatus === "streaming") messageStatus = "streaming";
83
- else if (uiStatus === "submitted") messageStatus = "submitted";
84
- else if (uiStatus === "error") messageStatus = "error";
85
- messages.push({
86
- id: generateMessageId(),
87
- role: "assistant",
88
- parts: currentAssistantParts,
89
- status: messageStatus
90
- });
91
- }
92
- return messages;
93
- }
94
- /**
95
- * Extracts tool parts from the most recent assistant message
11
+ * @example
12
+ * ```typescript
13
+ * // app/api/chat/route.ts (Next.js App Router)
14
+ * import { createDeepAgent } from 'deepagentsdk';
15
+ * import { createElementsRouteHandler } from 'deepagentsdk/adapters/elements';
16
+ * import { anthropic } from '@ai-sdk/anthropic';
96
17
  *
97
- * @param messages - UIMessage array
98
- * @returns Array of tool parts (tool-call and tool-result)
18
+ * const agent = createDeepAgent({
19
+ * model: anthropic('claude-sonnet-4-20250514'),
20
+ * });
21
+ *
22
+ * export const POST = createElementsRouteHandler({ agent });
23
+ * ```
24
+ *
25
+ * @see https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol
99
26
  */
100
- function extractToolParts(messages) {
101
- const lastAssistantMessage = [...messages].reverse().find((m) => m.role === "assistant");
102
- if (!lastAssistantMessage) return [];
103
- return lastAssistantMessage.parts.filter((part) => part.type === "tool-call" || part.type === "tool-result").map((part) => {
104
- if (part.type === "tool-call") return {
105
- type: "tool-call",
106
- toolCallId: part.toolCallId,
107
- toolName: part.toolName,
108
- args: part.args
109
- };
110
- else return {
111
- type: "tool-result",
112
- toolCallId: part.toolCallId,
113
- toolName: part.toolName,
114
- result: part.result,
115
- isError: part.isError
116
- };
117
- });
118
- }
119
-
120
- //#endregion
121
- //#region src/adapters/elements/statusAdapter.ts
122
27
  /**
123
- * Maps deepagentsdk AgentStatus to Elements UIStatus
28
+ * Creates a route handler that processes chat requests using DeepAgent
29
+ * and returns UI Message Stream compatible responses.
124
30
  *
125
- * @param agentStatus - The agent status from useAgent hook
126
- * @returns The corresponding UI status for Elements components
31
+ * The returned handler:
32
+ * - Accepts POST requests with { messages: UIMessage[] } body
33
+ * - Runs DeepAgent with the conversation history
34
+ * - Streams responses in UI Message Stream Protocol format
35
+ * - Works with useChat hook from @ai-sdk/react
127
36
  *
128
- * Mapping rules:
129
- * - idle/done ready (agent is waiting for input)
130
- * - thinking/tool-call/subagent → submitted (agent is processing)
131
- * - streaming → streaming (agent is generating text)
132
- * - error → error (an error occurred)
37
+ * @param options - Configuration options
38
+ * @returns A request handler function compatible with Next.js/Express
133
39
  */
134
- function mapAgentStatusToUIStatus(agentStatus) {
135
- switch (agentStatus) {
136
- case "thinking":
137
- case "tool-call":
138
- case "subagent": return "submitted";
139
- case "streaming": return "streaming";
140
- case "error": return "error";
141
- case "idle":
142
- case "done":
143
- default: return "ready";
144
- }
40
+ function createElementsRouteHandler(options) {
41
+ const { agent, onRequest, initialState = {
42
+ todos: [],
43
+ files: {}
44
+ }, threadId, maxSteps, generateId } = options;
45
+ return async (req) => {
46
+ if (onRequest) try {
47
+ await onRequest(req);
48
+ } catch (error) {
49
+ return new Response(JSON.stringify({ error: error instanceof Error ? error.message : "Request rejected" }), {
50
+ status: 401,
51
+ headers: { "Content-Type": "application/json" }
52
+ });
53
+ }
54
+ let requestBody;
55
+ try {
56
+ requestBody = await req.json();
57
+ } catch {
58
+ return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
59
+ status: 400,
60
+ headers: { "Content-Type": "application/json" }
61
+ });
62
+ }
63
+ const { messages } = requestBody;
64
+ if (!messages || !Array.isArray(messages)) return new Response(JSON.stringify({ error: "messages array is required" }), {
65
+ status: 400,
66
+ headers: { "Content-Type": "application/json" }
67
+ });
68
+ const modelMessages = await (0, ai.convertToModelMessages)(messages);
69
+ let currentTextId = null;
70
+ const genId = generateId || (() => crypto.randomUUID());
71
+ return (0, ai.createUIMessageStreamResponse)({ stream: (0, ai.createUIMessageStream)({
72
+ originalMessages: messages,
73
+ generateId: genId,
74
+ execute: async ({ writer }) => {
75
+ try {
76
+ for await (const event of agent.streamWithEvents({
77
+ messages: modelMessages,
78
+ state: initialState,
79
+ threadId,
80
+ maxSteps
81
+ })) switch (event.type) {
82
+ case "step-start":
83
+ writer.write({ type: "start-step" });
84
+ break;
85
+ case "step-finish":
86
+ writer.write({ type: "finish-step" });
87
+ break;
88
+ case "text":
89
+ if (!currentTextId) {
90
+ currentTextId = genId();
91
+ writer.write({
92
+ type: "text-start",
93
+ id: currentTextId
94
+ });
95
+ }
96
+ writer.write({
97
+ type: "text-delta",
98
+ id: currentTextId,
99
+ delta: event.text
100
+ });
101
+ break;
102
+ case "tool-call":
103
+ if (currentTextId) {
104
+ writer.write({
105
+ type: "text-end",
106
+ id: currentTextId
107
+ });
108
+ currentTextId = null;
109
+ }
110
+ writer.write({
111
+ type: "tool-input-available",
112
+ toolCallId: event.toolCallId,
113
+ toolName: event.toolName,
114
+ input: event.args
115
+ });
116
+ break;
117
+ case "tool-result":
118
+ if (event.isError) writer.write({
119
+ type: "tool-output-error",
120
+ toolCallId: event.toolCallId,
121
+ errorText: String(event.result)
122
+ });
123
+ else writer.write({
124
+ type: "tool-output-available",
125
+ toolCallId: event.toolCallId,
126
+ output: event.result
127
+ });
128
+ break;
129
+ case "todos-changed":
130
+ writer.write({
131
+ type: "data",
132
+ name: "todos-changed",
133
+ data: { todos: event.todos }
134
+ });
135
+ break;
136
+ case "error":
137
+ if (currentTextId) {
138
+ writer.write({
139
+ type: "text-end",
140
+ id: currentTextId
141
+ });
142
+ currentTextId = null;
143
+ }
144
+ writer.write({
145
+ type: "error",
146
+ errorText: event.error.message
147
+ });
148
+ break;
149
+ case "done":
150
+ if (currentTextId) {
151
+ writer.write({
152
+ type: "text-end",
153
+ id: currentTextId
154
+ });
155
+ currentTextId = null;
156
+ }
157
+ break;
158
+ default: break;
159
+ }
160
+ if (currentTextId) writer.write({
161
+ type: "text-end",
162
+ id: currentTextId
163
+ });
164
+ } catch (error) {
165
+ if (currentTextId) writer.write({
166
+ type: "text-end",
167
+ id: currentTextId
168
+ });
169
+ throw error;
170
+ }
171
+ },
172
+ onError: (error) => {
173
+ return error instanceof Error ? error.message : "Unknown error";
174
+ }
175
+ }) });
176
+ };
145
177
  }
146
178
 
147
179
  //#endregion
148
- //#region src/adapters/elements/useElementsAdapter.ts
180
+ //#region src/adapters/elements/messageConverters.ts
149
181
  /**
150
- * React hook adapter for AI SDK Elements
182
+ * Message conversion utilities for AI SDK Elements adapter
151
183
  *
152
- * Provides Elements-compatible interface for deepagentsdk
184
+ * Provides utilities for converting between UI message formats and model message formats.
185
+ * The primary conversion is handled by AI SDK's `convertToModelMessages`, but these
186
+ * utilities provide additional helpers for DeepAgent-specific needs.
153
187
  */
154
- let eventCounter = 0;
155
- function createEventId() {
156
- return `event-${++eventCounter}`;
157
- }
158
188
  /**
159
- * Hook that adapts deepagentsdk to work with AI SDK Elements UI components
189
+ * Re-export AI SDK's convertToModelMessages for convenience.
160
190
  *
161
- * @param options - Configuration options
162
- * @returns Elements-compatible interface
191
+ * This function converts UIMessage[] (from useChat) to ModelMessage[]
192
+ * (for agent consumption), handling:
193
+ * - Role mapping (user/assistant)
194
+ * - Tool call/result parts
195
+ * - Text content extraction
163
196
  *
164
197
  * @example
165
- * ```tsx
166
- * import { useElementsAdapter } from 'deepagentsdk/elements';
167
- * import { Conversation, Message, PromptInput } from '@/components/ai-elements';
168
- *
169
- * function Chat() {
170
- * const { uiMessages, sendMessage } = useElementsAdapter({
171
- * model,
172
- * backend
173
- * });
198
+ * ```typescript
199
+ * import { convertUIMessagesToModelMessages } from 'deepagentsdk/adapters/elements';
174
200
  *
175
- * return (
176
- * <Conversation>
177
- * {uiMessages.map(msg => <Message key={msg.id} from={msg.role} />)}
178
- * <PromptInput onSubmit={sendMessage} />
179
- * </Conversation>
180
- * );
181
- * }
201
+ * const modelMessages = await convertUIMessagesToModelMessages(uiMessages);
182
202
  * ```
183
203
  */
184
- function useElementsAdapter(options) {
185
- const { model, backend, tools, maxSteps = 10, systemPrompt } = options;
186
- const [status, setStatus] = (0, react.useState)("idle");
187
- const [streamingText, setStreamingText] = (0, react.useState)("");
188
- const [events, setEvents] = (0, react.useState)([]);
189
- const [state, setState] = (0, react.useState)({
190
- todos: [],
191
- files: {}
192
- });
193
- const abortControllerRef = (0, react.useRef)(null);
194
- const accumulatedTextRef = (0, react.useRef)("");
195
- const agentRef = (0, react.useRef)(require_agent.createDeepAgent({
196
- model,
197
- maxSteps,
198
- systemPrompt,
199
- backend,
200
- tools
201
- }));
202
- const addEvent = (0, react.useCallback)((event) => {
203
- setEvents((prev) => [...prev, {
204
- id: createEventId(),
205
- type: event.type,
206
- event,
207
- timestamp: /* @__PURE__ */ new Date()
208
- }]);
209
- }, []);
210
- const flushTextSegment = (0, react.useCallback)(() => {
211
- if (accumulatedTextRef.current.trim()) {
212
- addEvent({
213
- type: "text-segment",
214
- text: accumulatedTextRef.current
215
- });
216
- accumulatedTextRef.current = "";
217
- setStreamingText("");
218
- }
219
- }, [addEvent]);
220
- const sendMessage = async (message) => {
221
- if (!message.text.trim()) return;
222
- setStatus("thinking");
223
- setStreamingText("");
224
- accumulatedTextRef.current = "";
225
- addEvent({
226
- type: "user-message",
227
- content: message.text
228
- });
229
- abortControllerRef.current = new AbortController();
230
- try {
231
- for await (const event of agentRef.current.streamWithEvents({
232
- messages: [{
233
- role: "user",
234
- content: message.text
235
- }],
236
- state,
237
- abortSignal: abortControllerRef.current.signal
238
- })) switch (event.type) {
239
- case "text":
240
- setStatus("streaming");
241
- accumulatedTextRef.current += event.text;
242
- setStreamingText(accumulatedTextRef.current);
243
- break;
244
- case "step-start":
245
- if (event.stepNumber > 1) addEvent(event);
246
- break;
247
- case "tool-call":
248
- flushTextSegment();
249
- setStatus("tool-call");
250
- addEvent(event);
251
- break;
252
- case "tool-result":
253
- addEvent(event);
254
- break;
255
- case "todos-changed":
256
- flushTextSegment();
257
- setStatus("tool-call");
258
- setState((prev) => ({
259
- ...prev,
260
- todos: event.todos
261
- }));
262
- addEvent(event);
263
- break;
264
- case "done":
265
- flushTextSegment();
266
- setStatus("done");
267
- setState(event.state);
268
- addEvent(event);
269
- break;
270
- case "error":
271
- flushTextSegment();
272
- setStatus("error");
273
- addEvent(event);
274
- break;
275
- default:
276
- addEvent(event);
277
- break;
278
- }
279
- setStatus("idle");
280
- } catch (err) {
281
- if (err.name === "AbortError") {
282
- flushTextSegment();
283
- setStatus("idle");
284
- } else {
285
- flushTextSegment();
286
- setStatus("error");
287
- }
288
- } finally {
289
- abortControllerRef.current = null;
290
- }
291
- };
292
- const abort = (0, react.useCallback)(() => {
293
- if (abortControllerRef.current) {
294
- abortControllerRef.current.abort();
295
- setStatus("idle");
204
+ async function convertUIMessagesToModelMessages(messages) {
205
+ return await (0, ai.convertToModelMessages)(messages);
206
+ }
207
+ /**
208
+ * Extract the last user message text from a UIMessage array.
209
+ * Useful for extracting the prompt from a conversation.
210
+ *
211
+ * @param messages - Array of UI messages
212
+ * @returns The text content of the last user message, or undefined if none
213
+ */
214
+ function extractLastUserMessage(messages) {
215
+ for (let i = messages.length - 1; i >= 0; i--) {
216
+ const msg = messages[i];
217
+ if (msg && msg.role === "user" && msg.parts) {
218
+ const textParts = msg.parts.filter((p) => p.type === "text");
219
+ if (textParts.length > 0) return textParts.map((p) => p.text).join("");
296
220
  }
297
- }, []);
298
- const clear = (0, react.useCallback)(() => {
299
- setEvents([]);
300
- setStreamingText("");
301
- setStatus("idle");
302
- }, []);
303
- const uiStatus = (0, react.useMemo)(() => mapAgentStatusToUIStatus(status), [status]);
304
- const uiMessages = (0, react.useMemo)(() => convertEventsToUIMessages(events, streamingText, uiStatus), [
305
- events,
306
- streamingText,
307
- uiStatus
308
- ]);
221
+ }
222
+ }
223
+ /**
224
+ * Check if the messages contain any tool parts.
225
+ * This is a simplified helper that checks for any tool-related parts.
226
+ *
227
+ * @param messages - Array of UI messages
228
+ * @returns True if there are any tool-related parts in the messages
229
+ */
230
+ function hasToolParts(messages) {
231
+ for (const msg of messages) {
232
+ if (!msg.parts) continue;
233
+ for (const part of msg.parts) if (part.type.startsWith("tool-") || part.type === "dynamic-tool") return true;
234
+ }
235
+ return false;
236
+ }
237
+ /**
238
+ * Count the number of messages by role.
239
+ *
240
+ * @param messages - Array of UI messages
241
+ * @returns Object with counts by role
242
+ */
243
+ function countMessagesByRole(messages) {
244
+ let user = 0;
245
+ let assistant = 0;
246
+ let system = 0;
247
+ for (const msg of messages) if (msg.role === "user") user++;
248
+ else if (msg.role === "assistant") assistant++;
249
+ else if (msg.role === "system") system++;
309
250
  return {
310
- uiMessages,
311
- uiStatus,
312
- toolParts: (0, react.useMemo)(() => extractToolParts(uiMessages), [uiMessages]),
313
- sendMessage,
314
- abort,
315
- clear
251
+ user,
252
+ assistant,
253
+ system
316
254
  };
317
255
  }
256
+ /**
257
+ * Extract all text content from a message.
258
+ *
259
+ * @param message - A UI message
260
+ * @returns Combined text from all text parts
261
+ */
262
+ function extractTextFromMessage(message) {
263
+ if (!message.parts) return "";
264
+ return message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
265
+ }
318
266
 
319
267
  //#endregion
320
- exports.convertEventsToUIMessages = convertEventsToUIMessages;
321
- exports.extractToolParts = extractToolParts;
322
- exports.mapAgentStatusToUIStatus = mapAgentStatusToUIStatus;
323
- exports.useElementsAdapter = useElementsAdapter;
268
+ exports.convertUIMessagesToModelMessages = convertUIMessagesToModelMessages;
269
+ exports.countMessagesByRole = countMessagesByRole;
270
+ exports.createElementsRouteHandler = createElementsRouteHandler;
271
+ exports.extractLastUserMessage = extractLastUserMessage;
272
+ exports.extractTextFromMessage = extractTextFromMessage;
273
+ exports.hasToolParts = hasToolParts;
324
274
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["createDeepAgent"],"sources":["../../../src/adapters/elements/messageAdapter.ts","../../../src/adapters/elements/statusAdapter.ts","../../../src/adapters/elements/useElementsAdapter.ts"],"sourcesContent":["/**\n * Message transformation adapter for AI SDK Elements\n *\n * Converts deepagentsdk events to Elements UIMessage format\n */\n\nimport type { AgentEventLog } from \"../../cli/hooks/useAgent\";\nimport type { UIMessage, UIMessagePart, UIStatus } from \"./types\";\n\n/**\n * Converts agent event log to UIMessage format expected by Elements\n *\n * @param events - Array of agent events from useAgent hook\n * @param streamingText - Current streaming text (if any)\n * @param uiStatus - Current UI status\n * @returns Array of UIMessage objects for Elements Message component\n *\n * Conversion logic:\n * 1. Group events by role (user/assistant)\n * 2. Convert each event type to appropriate UIMessagePart\n * 3. Handle streaming text as in-progress message\n * 4. Preserve event order and tool call/result pairing\n */\nexport function convertEventsToUIMessages(\n events: AgentEventLog[],\n streamingText: string,\n uiStatus: UIStatus\n): UIMessage[] {\n const messages: UIMessage[] = [];\n let currentAssistantParts: UIMessagePart[] = [];\n let messageIdCounter = 0;\n\n const generateMessageId = (): string => {\n return `msg-${Date.now()}-${++messageIdCounter}`;\n };\n\n for (const eventLog of events) {\n const event = eventLog.event;\n\n switch (event.type) {\n case \"user-message\":\n // Flush any pending assistant parts before user message\n if (currentAssistantParts.length > 0) {\n messages.push({\n id: generateMessageId(),\n role: \"assistant\",\n parts: currentAssistantParts,\n status: \"ready\",\n });\n currentAssistantParts = [];\n }\n\n // Add user message\n messages.push({\n id: eventLog.id,\n role: \"user\",\n parts: [{ type: \"text\", text: event.content }],\n status: \"ready\",\n });\n break;\n\n case \"text-segment\":\n // Add text segment as separate text part\n currentAssistantParts.push({\n type: \"text\",\n text: event.text,\n });\n break;\n\n case \"tool-call\":\n // Add tool call part\n currentAssistantParts.push({\n type: \"tool-call\",\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n args: event.args,\n });\n break;\n\n case \"tool-result\":\n // Add tool result part\n currentAssistantParts.push({\n type: \"tool-result\",\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n result: event.result,\n isError: event.isError,\n });\n break;\n\n // Ignore other event types for message rendering\n // (they're handled separately by Elements components like Task, etc.)\n default:\n break;\n }\n }\n\n // Add streaming text as in-progress assistant message\n if (streamingText || currentAssistantParts.length > 0) {\n if (streamingText) {\n currentAssistantParts.push({ type: \"text\", text: streamingText });\n }\n\n // Determine status for current message\n let messageStatus: UIStatus = \"ready\";\n if (uiStatus === \"streaming\") {\n messageStatus = \"streaming\";\n } else if (uiStatus === \"submitted\") {\n messageStatus = \"submitted\";\n } else if (uiStatus === \"error\") {\n messageStatus = \"error\";\n }\n\n messages.push({\n id: generateMessageId(),\n role: \"assistant\",\n parts: currentAssistantParts,\n status: messageStatus,\n });\n }\n\n return messages;\n}\n\n/**\n * Extracts tool parts from the most recent assistant message\n *\n * @param messages - UIMessage array\n * @returns Array of tool parts (tool-call and tool-result)\n */\nexport function extractToolParts(messages: UIMessage[]) {\n // Get last assistant message\n const lastAssistantMessage = [...messages]\n .reverse()\n .find((m) => m.role === \"assistant\");\n\n if (!lastAssistantMessage) {\n return [];\n }\n\n // Extract only tool-related parts\n return lastAssistantMessage.parts\n .filter(\n (part): part is Extract<UIMessagePart, { type: \"tool-call\" | \"tool-result\" }> =>\n part.type === \"tool-call\" || part.type === \"tool-result\"\n )\n .map((part) => {\n if (part.type === \"tool-call\") {\n return {\n type: \"tool-call\" as const,\n toolCallId: part.toolCallId,\n toolName: part.toolName,\n args: part.args,\n };\n } else {\n return {\n type: \"tool-result\" as const,\n toolCallId: part.toolCallId,\n toolName: part.toolName,\n result: part.result,\n isError: part.isError,\n };\n }\n });\n}\n","/**\n * Status mapping adapter for AI SDK Elements\n *\n * Maps deepagentsdk AgentStatus to Elements UIStatus\n */\n\nimport type { AgentStatus } from \"../../cli/hooks/useAgent\";\nimport type { UIStatus } from \"./types\";\n\n/**\n * Maps deepagentsdk AgentStatus to Elements UIStatus\n *\n * @param agentStatus - The agent status from useAgent hook\n * @returns The corresponding UI status for Elements components\n *\n * Mapping rules:\n * - idle/done → ready (agent is waiting for input)\n * - thinking/tool-call/subagent → submitted (agent is processing)\n * - streaming → streaming (agent is generating text)\n * - error → error (an error occurred)\n */\nexport function mapAgentStatusToUIStatus(\n agentStatus: AgentStatus\n): UIStatus {\n switch (agentStatus) {\n case \"thinking\":\n case \"tool-call\":\n case \"subagent\":\n return \"submitted\";\n case \"streaming\":\n return \"streaming\";\n case \"error\":\n return \"error\";\n case \"idle\":\n case \"done\":\n default:\n return \"ready\";\n }\n}\n","/**\n * React hook adapter for AI SDK Elements\n *\n * Provides Elements-compatible interface for deepagentsdk\n */\n\nimport { useState, useCallback, useRef, useMemo } from \"react\";\nimport { createDeepAgent } from \"../../agent\";\nimport type { LanguageModel, ToolSet } from \"ai\";\nimport type {\n BackendProtocol,\n DeepAgentState,\n DeepAgentEvent,\n} from \"../../types\";\nimport {\n convertEventsToUIMessages,\n extractToolParts,\n} from \"./messageAdapter\";\nimport { mapAgentStatusToUIStatus } from \"./statusAdapter\";\nimport type { UseElementsAdapterReturn, PromptInputMessage } from \"./types\";\nimport type { AgentStatus, AgentEventLog } from \"../../cli/hooks/useAgent\";\n\n/**\n * Options for useElementsAdapter hook\n */\nexport interface UseElementsAdapterOptions {\n /**\n * Language model instance from AI SDK provider\n */\n model: LanguageModel;\n\n /**\n * Backend for state management\n */\n backend: BackendProtocol;\n\n /**\n * Optional tools to provide to the agent\n */\n tools?: ToolSet;\n\n /**\n * Maximum number of tool loop iterations\n * @default 10\n */\n maxSteps?: number;\n\n /**\n * System prompt for the agent\n */\n systemPrompt?: string;\n}\n\nlet eventCounter = 0;\n\nfunction createEventId(): string {\n return `event-${++eventCounter}`;\n}\n\n/**\n * Hook that adapts deepagentsdk to work with AI SDK Elements UI components\n *\n * @param options - Configuration options\n * @returns Elements-compatible interface\n *\n * @example\n * ```tsx\n * import { useElementsAdapter } from 'deepagentsdk/elements';\n * import { Conversation, Message, PromptInput } from '@/components/ai-elements';\n *\n * function Chat() {\n * const { uiMessages, sendMessage } = useElementsAdapter({\n * model,\n * backend\n * });\n *\n * return (\n * <Conversation>\n * {uiMessages.map(msg => <Message key={msg.id} from={msg.role} />)}\n * <PromptInput onSubmit={sendMessage} />\n * </Conversation>\n * );\n * }\n * ```\n */\nexport function useElementsAdapter(\n options: UseElementsAdapterOptions\n): UseElementsAdapterReturn {\n const { model, backend, tools, maxSteps = 10, systemPrompt } = options;\n\n const [status, setStatus] = useState<AgentStatus>(\"idle\");\n const [streamingText, setStreamingText] = useState(\"\");\n const [events, setEvents] = useState<AgentEventLog[]>([]);\n const [state, setState] = useState<DeepAgentState>({\n todos: [],\n files: {},\n });\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const accumulatedTextRef = useRef(\"\");\n\n // Create agent instance\n const agentRef = useRef(\n createDeepAgent({\n model,\n maxSteps,\n systemPrompt,\n backend,\n tools,\n })\n );\n\n const addEvent = useCallback(\n (event: DeepAgentEvent | { type: \"text-segment\"; text: string }) => {\n setEvents((prev) => [\n ...prev,\n {\n id: createEventId(),\n type: event.type,\n event,\n timestamp: new Date(),\n },\n ]);\n },\n []\n );\n\n // Flush accumulated text as a text-segment event\n const flushTextSegment = useCallback(() => {\n if (accumulatedTextRef.current.trim()) {\n addEvent({\n type: \"text-segment\",\n text: accumulatedTextRef.current,\n });\n accumulatedTextRef.current = \"\";\n setStreamingText(\"\");\n }\n }, [addEvent]);\n\n const sendMessage = async (message: PromptInputMessage): Promise<void> => {\n if (!message.text.trim()) {\n return; // Ignore empty messages\n }\n\n // Reset for new generation\n setStatus(\"thinking\");\n setStreamingText(\"\");\n accumulatedTextRef.current = \"\";\n\n // Add user message to events\n addEvent({ type: \"user-message\", content: message.text });\n\n // Create new abort controller\n abortControllerRef.current = new AbortController();\n\n try {\n for await (const event of agentRef.current.streamWithEvents({\n messages: [{ role: \"user\", content: message.text }],\n state,\n abortSignal: abortControllerRef.current.signal,\n })) {\n switch (event.type) {\n case \"text\":\n setStatus(\"streaming\");\n accumulatedTextRef.current += event.text;\n setStreamingText(accumulatedTextRef.current);\n break;\n\n case \"step-start\":\n if (event.stepNumber > 1) {\n addEvent(event);\n }\n break;\n\n case \"tool-call\":\n flushTextSegment();\n setStatus(\"tool-call\");\n addEvent(event);\n break;\n\n case \"tool-result\":\n addEvent(event);\n break;\n\n case \"todos-changed\":\n flushTextSegment();\n setStatus(\"tool-call\");\n setState((prev) => ({ ...prev, todos: event.todos }));\n addEvent(event);\n break;\n\n case \"done\":\n flushTextSegment();\n setStatus(\"done\");\n setState(event.state);\n addEvent(event);\n break;\n\n case \"error\":\n flushTextSegment();\n setStatus(\"error\");\n addEvent(event);\n break;\n\n default:\n addEvent(event);\n break;\n }\n }\n\n setStatus(\"idle\");\n } catch (err) {\n if ((err as Error).name === \"AbortError\") {\n flushTextSegment();\n setStatus(\"idle\");\n } else {\n flushTextSegment();\n setStatus(\"error\");\n }\n } finally {\n abortControllerRef.current = null;\n }\n };\n\n const abort = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n setStatus(\"idle\");\n }\n }, []);\n\n const clear = useCallback(() => {\n setEvents([]);\n setStreamingText(\"\");\n setStatus(\"idle\");\n }, []);\n\n // Convert agent status to UI status\n const uiStatus = useMemo(\n () => mapAgentStatusToUIStatus(status),\n [status]\n );\n\n // Convert events to UI messages\n const uiMessages = useMemo(\n () => convertEventsToUIMessages(events, streamingText, uiStatus),\n [events, streamingText, uiStatus]\n );\n\n // Extract tool parts from current message\n const toolParts = useMemo(() => extractToolParts(uiMessages), [uiMessages]);\n\n return {\n uiMessages,\n uiStatus,\n toolParts,\n sendMessage,\n abort,\n clear,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/adapters/elements/createElementsRouteHandler.ts","../../../src/adapters/elements/messageConverters.ts"],"sourcesContent":["/**\n * Server-side route handler adapter for AI SDK Elements\n *\n * Creates a Next.js/Express-compatible route handler that runs DeepAgent\n * and returns UI Message Stream compatible responses for use with useChat.\n *\n * @example\n * ```typescript\n * // app/api/chat/route.ts (Next.js App Router)\n * import { createDeepAgent } from 'deepagentsdk';\n * import { createElementsRouteHandler } from 'deepagentsdk/adapters/elements';\n * import { anthropic } from '@ai-sdk/anthropic';\n *\n * const agent = createDeepAgent({\n * model: anthropic('claude-sonnet-4-20250514'),\n * });\n *\n * export const POST = createElementsRouteHandler({ agent });\n * ```\n *\n * @see https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol\n */\n\nimport {\n createUIMessageStream,\n createUIMessageStreamResponse,\n convertToModelMessages,\n type UIMessage,\n} from \"ai\";\nimport type { DeepAgent } from \"../../agent\";\nimport type { DeepAgentState, ModelMessage } from \"../../types\";\n\n/**\n * Options for creating an Elements route handler\n */\nexport interface CreateElementsRouteHandlerOptions {\n /**\n * The DeepAgent instance to use for handling requests\n */\n agent: DeepAgent;\n\n /**\n * Optional callback before processing a request.\n * Use for authentication, logging, rate limiting, etc.\n *\n * @example\n * ```typescript\n * onRequest: async (req) => {\n * const token = req.headers.get('Authorization');\n * if (!validateToken(token)) {\n * throw new Error('Unauthorized');\n * }\n * }\n * ```\n */\n onRequest?: (req: Request) => Promise<void> | void;\n\n /**\n * Optional initial state to provide to the agent.\n * If not provided, uses empty state { todos: [], files: {} }\n */\n initialState?: DeepAgentState;\n\n /**\n * Optional thread ID for checkpointing.\n * If provided, enables conversation persistence.\n */\n threadId?: string;\n\n /**\n * Optional maximum number of steps for the agent loop.\n */\n maxSteps?: number;\n\n /**\n * Custom ID generator for message IDs.\n * Defaults to crypto.randomUUID if available.\n */\n generateId?: () => string;\n}\n\n/**\n * Creates a route handler that processes chat requests using DeepAgent\n * and returns UI Message Stream compatible responses.\n *\n * The returned handler:\n * - Accepts POST requests with { messages: UIMessage[] } body\n * - Runs DeepAgent with the conversation history\n * - Streams responses in UI Message Stream Protocol format\n * - Works with useChat hook from @ai-sdk/react\n *\n * @param options - Configuration options\n * @returns A request handler function compatible with Next.js/Express\n */\nexport function createElementsRouteHandler(\n options: CreateElementsRouteHandlerOptions\n): (req: Request) => Promise<Response> {\n const {\n agent,\n onRequest,\n initialState = { todos: [], files: {} },\n threadId,\n maxSteps,\n generateId,\n } = options;\n\n return async (req: Request): Promise<Response> => {\n // Run optional request hook (auth, logging, etc.)\n if (onRequest) {\n try {\n await onRequest(req);\n } catch (error) {\n return new Response(\n JSON.stringify({\n error: error instanceof Error ? error.message : \"Request rejected\",\n }),\n { status: 401, headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n }\n\n // Parse request body\n let requestBody: { messages: UIMessage[] };\n try {\n requestBody = await req.json();\n } catch {\n return new Response(\n JSON.stringify({ error: \"Invalid JSON body\" }),\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n\n const { messages } = requestBody;\n\n if (!messages || !Array.isArray(messages)) {\n return new Response(\n JSON.stringify({ error: \"messages array is required\" }),\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\n );\n }\n\n // Convert UI messages to model messages using AI SDK's converter\n const modelMessages = await convertToModelMessages(messages) as ModelMessage[];\n\n // Track current text part ID for proper text streaming\n let currentTextId: string | null = null;\n const genId = generateId || (() => crypto.randomUUID());\n\n // Create the UI message stream\n const stream = createUIMessageStream({\n originalMessages: messages,\n generateId: genId,\n execute: async ({ writer }) => {\n try {\n for await (const event of agent.streamWithEvents({\n messages: modelMessages,\n state: initialState,\n threadId,\n maxSteps,\n })) {\n switch (event.type) {\n case \"step-start\":\n // Emit step start for UI progress tracking\n writer.write({ type: \"start-step\" });\n break;\n\n case \"step-finish\":\n // Emit step finish\n writer.write({ type: \"finish-step\" });\n break;\n\n case \"text\":\n // Handle text streaming\n if (!currentTextId) {\n // Start a new text part\n currentTextId = genId();\n writer.write({\n type: \"text-start\",\n id: currentTextId,\n });\n }\n // Stream text delta\n writer.write({\n type: \"text-delta\",\n id: currentTextId,\n delta: event.text,\n });\n break;\n\n case \"tool-call\":\n // End any in-progress text before tool call\n if (currentTextId) {\n writer.write({ type: \"text-end\", id: currentTextId });\n currentTextId = null;\n }\n // Emit tool input available\n writer.write({\n type: \"tool-input-available\",\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n input: event.args,\n });\n break;\n\n case \"tool-result\":\n // Emit tool output\n if (event.isError) {\n writer.write({\n type: \"tool-output-error\",\n toolCallId: event.toolCallId,\n errorText: String(event.result),\n });\n } else {\n writer.write({\n type: \"tool-output-available\",\n toolCallId: event.toolCallId,\n output: event.result,\n });\n }\n break;\n\n case \"todos-changed\":\n // Emit as custom data part for UI to handle\n // Note: This requires UI to handle custom data types\n writer.write({\n type: \"data\" as any,\n name: \"todos-changed\",\n data: { todos: event.todos },\n } as any);\n break;\n\n case \"error\":\n // End any in-progress text\n if (currentTextId) {\n writer.write({ type: \"text-end\", id: currentTextId });\n currentTextId = null;\n }\n // Emit error\n writer.write({\n type: \"error\",\n errorText: event.error.message,\n });\n break;\n\n case \"done\":\n // End any in-progress text\n if (currentTextId) {\n writer.write({ type: \"text-end\", id: currentTextId });\n currentTextId = null;\n }\n // Done event is handled automatically by the stream\n break;\n\n // Other events (file operations, subagents, etc.) can be\n // emitted as custom data parts if needed by the UI\n default:\n // Optionally emit other events as data parts\n // writer.write({\n // type: \"data\" as any,\n // name: event.type,\n // data: event,\n // } as any);\n break;\n }\n }\n\n // Ensure text is ended if stream completes\n if (currentTextId) {\n writer.write({ type: \"text-end\", id: currentTextId });\n }\n } catch (error) {\n // End any in-progress text on error\n if (currentTextId) {\n writer.write({ type: \"text-end\", id: currentTextId });\n }\n // Re-throw to let the stream handle the error\n throw error;\n }\n },\n onError: (error) => {\n // Return error message for the stream\n return error instanceof Error ? error.message : \"Unknown error\";\n },\n });\n\n // Return the stream response\n return createUIMessageStreamResponse({ stream });\n };\n}\n\n/**\n * Type for the request handler returned by createElementsRouteHandler\n */\nexport type ElementsRouteHandler = (req: Request) => Promise<Response>;\n","/**\n * Message conversion utilities for AI SDK Elements adapter\n *\n * Provides utilities for converting between UI message formats and model message formats.\n * The primary conversion is handled by AI SDK's `convertToModelMessages`, but these\n * utilities provide additional helpers for DeepAgent-specific needs.\n */\n\nimport {\n convertToModelMessages,\n type UIMessage,\n} from \"ai\";\nimport type { ModelMessage } from \"../../types\";\n\n/**\n * Re-export AI SDK's convertToModelMessages for convenience.\n *\n * This function converts UIMessage[] (from useChat) to ModelMessage[]\n * (for agent consumption), handling:\n * - Role mapping (user/assistant)\n * - Tool call/result parts\n * - Text content extraction\n *\n * @example\n * ```typescript\n * import { convertUIMessagesToModelMessages } from 'deepagentsdk/adapters/elements';\n *\n * const modelMessages = await convertUIMessagesToModelMessages(uiMessages);\n * ```\n */\nexport async function convertUIMessagesToModelMessages(\n messages: UIMessage[]\n): Promise<ModelMessage[]> {\n return await convertToModelMessages(messages) as ModelMessage[];\n}\n\n/**\n * Extract the last user message text from a UIMessage array.\n * Useful for extracting the prompt from a conversation.\n *\n * @param messages - Array of UI messages\n * @returns The text content of the last user message, or undefined if none\n */\nexport function extractLastUserMessage(messages: UIMessage[]): string | undefined {\n // Find the last user message\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg && msg.role === \"user\" && msg.parts) {\n // Extract text parts\n const textParts = msg.parts.filter(\n (p): p is { type: \"text\"; text: string } => p.type === \"text\"\n );\n if (textParts.length > 0) {\n return textParts.map(p => p.text).join(\"\");\n }\n }\n }\n return undefined;\n}\n\n/**\n * Check if the messages contain any tool parts.\n * This is a simplified helper that checks for any tool-related parts.\n *\n * @param messages - Array of UI messages\n * @returns True if there are any tool-related parts in the messages\n */\nexport function hasToolParts(messages: UIMessage[]): boolean {\n for (const msg of messages) {\n if (!msg.parts) continue;\n for (const part of msg.parts) {\n // Tool parts have type starting with \"tool-\" or \"dynamic-tool\"\n if (part.type.startsWith(\"tool-\") || part.type === \"dynamic-tool\") {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Count the number of messages by role.\n *\n * @param messages - Array of UI messages\n * @returns Object with counts by role\n */\nexport function countMessagesByRole(\n messages: UIMessage[]\n): { user: number; assistant: number; system: number } {\n let user = 0;\n let assistant = 0;\n let system = 0;\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n user++;\n } else if (msg.role === \"assistant\") {\n assistant++;\n } else if (msg.role === \"system\") {\n system++;\n }\n }\n\n return { user, assistant, system };\n}\n\n/**\n * Extract all text content from a message.\n *\n * @param message - A UI message\n * @returns Combined text from all text parts\n */\nexport function extractTextFromMessage(message: UIMessage): string {\n if (!message.parts) return \"\";\n\n return message.parts\n .filter((p): p is { type: \"text\"; text: string } => p.type === \"text\")\n .map(p => p.text)\n .join(\"\");\n}\n"],"mappings":"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"}