deepagentsdk 0.12.0 → 0.14.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 +478 -288
  2. package/dist/adapters/elements/index.cjs.map +1 -1
  3. package/dist/adapters/elements/index.d.cts +107 -172
  4. package/dist/adapters/elements/index.d.mts +107 -172
  5. package/dist/adapters/elements/index.mjs +471 -284
  6. package/dist/adapters/elements/index.mjs.map +1 -1
  7. package/dist/{types-4g9UvXal.d.mts → agent-D0bKkNI-.d.mts} +352 -3
  8. package/dist/{types-IulnvhFg.d.cts → agent-DwAj5emJ.d.cts} +352 -3
  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,320 +1,507 @@
1
- import { n as createDeepAgent } from "../../agent-CrH-He58.mjs";
2
- import { useCallback, useMemo, useRef, useState } from "react";
1
+ import { convertToModelMessages, createUIMessageStream, createUIMessageStreamResponse } from "ai";
3
2
 
4
- //#region src/adapters/elements/messageAdapter.ts
3
+ //#region src/adapters/elements/createElementsRouteHandler.ts
5
4
  /**
6
- * Converts agent event log to UIMessage format expected by Elements
5
+ * Server-side route handler adapter for AI SDK Elements
7
6
  *
8
- * @param events - Array of agent events from useAgent hook
9
- * @param streamingText - Current streaming text (if any)
10
- * @param uiStatus - Current UI status
11
- * @returns Array of UIMessage objects for Elements Message component
7
+ * Creates a Next.js/Express-compatible route handler that runs DeepAgent
8
+ * and returns UI Message Stream compatible responses with full event visibility.
12
9
  *
13
- * Conversion logic:
14
- * 1. Group events by role (user/assistant)
15
- * 2. Convert each event type to appropriate UIMessagePart
16
- * 3. Handle streaming text as in-progress message
17
- * 4. Preserve event order and tool call/result pairing
10
+ * This handler streams all DeepAgent event types (26+) including:
11
+ * - Text and tool events (standard protocol)
12
+ * - File system operations
13
+ * - Command execution
14
+ * - Web requests and searches
15
+ * - Subagent lifecycle
16
+ * - State changes (todos, checkpoints)
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * // app/api/chat/route.ts (Next.js App Router)
21
+ * import { createDeepAgent } from 'deepagentsdk';
22
+ * import { createElementsRouteHandler } from 'deepagentsdk/adapters/elements';
23
+ * import { anthropic } from '@ai-sdk/anthropic';
24
+ *
25
+ * const agent = createDeepAgent({
26
+ * model: anthropic('claude-sonnet-4-20250514'),
27
+ * });
28
+ *
29
+ * export const POST = createElementsRouteHandler({ agent });
30
+ * ```
31
+ *
32
+ * @see https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol
18
33
  */
19
- function convertEventsToUIMessages(events, streamingText, uiStatus) {
20
- const messages = [];
21
- let currentAssistantParts = [];
22
- let messageIdCounter = 0;
23
- const generateMessageId = () => {
24
- return `msg-${Date.now()}-${++messageIdCounter}`;
25
- };
26
- for (const eventLog of events) {
27
- const event = eventLog.event;
28
- switch (event.type) {
29
- case "user-message":
30
- if (currentAssistantParts.length > 0) {
31
- messages.push({
32
- id: generateMessageId(),
33
- role: "assistant",
34
- parts: currentAssistantParts,
35
- status: "ready"
36
- });
37
- currentAssistantParts = [];
38
- }
39
- messages.push({
40
- id: eventLog.id,
41
- role: "user",
42
- parts: [{
43
- type: "text",
44
- text: event.content
45
- }],
46
- status: "ready"
47
- });
48
- break;
49
- case "text-segment":
50
- currentAssistantParts.push({
51
- type: "text",
52
- text: event.text
53
- });
54
- break;
55
- case "tool-call":
56
- currentAssistantParts.push({
57
- type: "tool-call",
58
- toolCallId: event.toolCallId,
59
- toolName: event.toolName,
60
- args: event.args
61
- });
62
- break;
63
- case "tool-result":
64
- currentAssistantParts.push({
65
- type: "tool-result",
66
- toolCallId: event.toolCallId,
67
- toolName: event.toolName,
68
- result: event.result,
69
- isError: event.isError
70
- });
71
- break;
72
- default: break;
73
- }
74
- }
75
- if (streamingText || currentAssistantParts.length > 0) {
76
- if (streamingText) currentAssistantParts.push({
77
- type: "text",
78
- text: streamingText
79
- });
80
- let messageStatus = "ready";
81
- if (uiStatus === "streaming") messageStatus = "streaming";
82
- else if (uiStatus === "submitted") messageStatus = "submitted";
83
- else if (uiStatus === "error") messageStatus = "error";
84
- messages.push({
85
- id: generateMessageId(),
86
- role: "assistant",
87
- parts: currentAssistantParts,
88
- status: messageStatus
89
- });
90
- }
91
- return messages;
92
- }
93
34
  /**
94
- * Extracts tool parts from the most recent assistant message
35
+ * Creates a route handler that processes chat requests using DeepAgent
36
+ * and streams all 26+ event types in UI Message Stream Protocol format.
37
+ *
38
+ * The returned handler:
39
+ * - Accepts POST requests with { messages: UIMessage[] } body
40
+ * - Runs DeepAgent with the conversation history
41
+ * - Streams responses in UI Message Stream Protocol format
42
+ * - Works with useChat hook from @ai-sdk/react
43
+ * - Provides full visibility into agent behavior (file ops, web requests, subagents, etc.)
95
44
  *
96
- * @param messages - UIMessage array
97
- * @returns Array of tool parts (tool-call and tool-result)
45
+ * @param options - Configuration options
46
+ * @returns A request handler function compatible with Next.js/Express
98
47
  */
99
- function extractToolParts(messages) {
100
- const lastAssistantMessage = [...messages].reverse().find((m) => m.role === "assistant");
101
- if (!lastAssistantMessage) return [];
102
- return lastAssistantMessage.parts.filter((part) => part.type === "tool-call" || part.type === "tool-result").map((part) => {
103
- if (part.type === "tool-call") return {
104
- type: "tool-call",
105
- toolCallId: part.toolCallId,
106
- toolName: part.toolName,
107
- args: part.args
108
- };
109
- else return {
110
- type: "tool-result",
111
- toolCallId: part.toolCallId,
112
- toolName: part.toolName,
113
- result: part.result,
114
- isError: part.isError
115
- };
116
- });
48
+ function createElementsRouteHandler(options) {
49
+ const { agent, onRequest, initialState = {
50
+ todos: [],
51
+ files: {}
52
+ }, threadId, maxSteps, generateId } = options;
53
+ return async (req) => {
54
+ if (onRequest) try {
55
+ await onRequest(req);
56
+ } catch (error) {
57
+ return new Response(JSON.stringify({ error: error instanceof Error ? error.message : "Request rejected" }), {
58
+ status: 401,
59
+ headers: { "Content-Type": "application/json" }
60
+ });
61
+ }
62
+ let requestBody;
63
+ try {
64
+ requestBody = await req.json();
65
+ } catch {
66
+ return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
67
+ status: 400,
68
+ headers: { "Content-Type": "application/json" }
69
+ });
70
+ }
71
+ const { messages } = requestBody;
72
+ if (!messages || !Array.isArray(messages)) return new Response(JSON.stringify({ error: "messages array is required" }), {
73
+ status: 400,
74
+ headers: { "Content-Type": "application/json" }
75
+ });
76
+ const modelMessages = await convertToModelMessages(messages);
77
+ const genId = generateId || (() => crypto.randomUUID());
78
+ let currentTextId = null;
79
+ return createUIMessageStreamResponse({ stream: createUIMessageStream({
80
+ originalMessages: messages,
81
+ generateId: genId,
82
+ execute: async ({ writer }) => {
83
+ try {
84
+ for await (const event of agent.streamWithEvents({
85
+ messages: modelMessages,
86
+ state: initialState,
87
+ threadId,
88
+ maxSteps
89
+ })) {
90
+ const result = mapEventToProtocol(event, writer, genId, currentTextId);
91
+ if (typeof result === "string") currentTextId = result;
92
+ else if (result === null) currentTextId = null;
93
+ }
94
+ if (currentTextId) writer.write({
95
+ type: "text-end",
96
+ id: currentTextId
97
+ });
98
+ } catch (error) {
99
+ if (currentTextId) writer.write({
100
+ type: "text-end",
101
+ id: currentTextId
102
+ });
103
+ throw error;
104
+ }
105
+ },
106
+ onError: (error) => {
107
+ return error instanceof Error ? error.message : "Unknown error";
108
+ }
109
+ }) });
110
+ };
117
111
  }
118
-
119
- //#endregion
120
- //#region src/adapters/elements/statusAdapter.ts
121
112
  /**
122
- * Maps deepagentsdk AgentStatus to Elements UIStatus
113
+ * Maps a DeepAgent event to a UI Message Stream Protocol event.
114
+ *
115
+ * This function handles all 26+ DeepAgent event types, mapping:
116
+ * - Standard protocol events (text, tools, steps, errors)
117
+ * - Custom data events (file operations, web requests, subagents, execution)
123
118
  *
124
- * @param agentStatus - The agent status from useAgent hook
125
- * @returns The corresponding UI status for Elements components
119
+ * @param event - The DeepAgent event to map
120
+ * @param writer - The UI message stream writer
121
+ * @param genId - ID generator function
122
+ * @param currentTextId - The current text part ID (for tracking streaming text)
123
+ * @returns The new currentTextId value (string | null)
126
124
  *
127
- * Mapping rules:
128
- * - idle/done → ready (agent is waiting for input)
129
- * - thinking/tool-call/subagent submitted (agent is processing)
130
- * - streaming streaming (agent is generating text)
131
- * - error error (an error occurred)
125
+ * @example
126
+ * ```typescript
127
+ * // Handles text streaming with proper ID tracking
128
+ * let textId: string | null = null;
129
+ * textId = mapEventToProtocol({ type: 'text', text: 'Hello' }, writer, genId, textId);
130
+ * // textId is now the ID of the active text part
131
+ * ```
132
132
  */
133
- function mapAgentStatusToUIStatus(agentStatus) {
134
- switch (agentStatus) {
135
- case "thinking":
133
+ function mapEventToProtocol(event, writer, genId, currentTextId) {
134
+ switch (event.type) {
135
+ case "step-start":
136
+ writer.write({ type: "start-step" });
137
+ return currentTextId;
138
+ case "step-finish":
139
+ writer.write({ type: "finish-step" });
140
+ return currentTextId;
141
+ case "text":
142
+ if (!currentTextId) {
143
+ const textId = genId();
144
+ writer.write({
145
+ type: "text-start",
146
+ id: textId
147
+ });
148
+ return textId;
149
+ }
150
+ writer.write({
151
+ type: "text-delta",
152
+ id: currentTextId,
153
+ delta: event.text
154
+ });
155
+ return currentTextId;
136
156
  case "tool-call":
137
- case "subagent": return "submitted";
138
- case "streaming": return "streaming";
139
- case "error": return "error";
140
- case "idle":
157
+ if (currentTextId) {
158
+ writer.write({
159
+ type: "text-end",
160
+ id: currentTextId
161
+ });
162
+ currentTextId = null;
163
+ }
164
+ writer.write({
165
+ type: "tool-input-available",
166
+ toolCallId: event.toolCallId,
167
+ toolName: event.toolName,
168
+ input: event.args
169
+ });
170
+ return null;
171
+ case "tool-result":
172
+ if (event.isError) writer.write({
173
+ type: "tool-output-error",
174
+ toolCallId: event.toolCallId,
175
+ errorText: String(event.result)
176
+ });
177
+ else writer.write({
178
+ type: "tool-output-available",
179
+ toolCallId: event.toolCallId,
180
+ output: event.result
181
+ });
182
+ return currentTextId;
183
+ case "todos-changed":
184
+ writer.write({
185
+ type: "data",
186
+ name: "todos-changed",
187
+ data: { todos: event.todos }
188
+ });
189
+ return currentTextId;
190
+ case "file-write-start":
191
+ writer.write({
192
+ type: "data",
193
+ name: "file-write-start",
194
+ data: {
195
+ path: event.path,
196
+ content: event.content
197
+ }
198
+ });
199
+ return currentTextId;
200
+ case "file-written":
201
+ writer.write({
202
+ type: "data",
203
+ name: "file-written",
204
+ data: {
205
+ path: event.path,
206
+ content: event.content
207
+ }
208
+ });
209
+ return currentTextId;
210
+ case "file-edited":
211
+ writer.write({
212
+ type: "data",
213
+ name: "file-edited",
214
+ data: {
215
+ path: event.path,
216
+ occurrences: event.occurrences
217
+ }
218
+ });
219
+ return currentTextId;
220
+ case "file-read":
221
+ writer.write({
222
+ type: "data",
223
+ name: "file-read",
224
+ data: {
225
+ path: event.path,
226
+ lines: event.lines
227
+ }
228
+ });
229
+ return currentTextId;
230
+ case "ls":
231
+ writer.write({
232
+ type: "data",
233
+ name: "ls",
234
+ data: {
235
+ path: event.path,
236
+ count: event.count
237
+ }
238
+ });
239
+ return currentTextId;
240
+ case "glob":
241
+ writer.write({
242
+ type: "data",
243
+ name: "glob",
244
+ data: {
245
+ pattern: event.pattern,
246
+ count: event.count
247
+ }
248
+ });
249
+ return currentTextId;
250
+ case "grep":
251
+ writer.write({
252
+ type: "data",
253
+ name: "grep",
254
+ data: {
255
+ pattern: event.pattern,
256
+ count: event.count
257
+ }
258
+ });
259
+ return currentTextId;
260
+ case "execute-start":
261
+ writer.write({
262
+ type: "data",
263
+ name: "execute-start",
264
+ data: {
265
+ command: event.command,
266
+ sandboxId: event.sandboxId
267
+ }
268
+ });
269
+ return currentTextId;
270
+ case "execute-finish":
271
+ writer.write({
272
+ type: "data",
273
+ name: "execute-finish",
274
+ data: {
275
+ command: event.command,
276
+ exitCode: event.exitCode,
277
+ truncated: event.truncated,
278
+ sandboxId: event.sandboxId
279
+ }
280
+ });
281
+ return currentTextId;
282
+ case "web-search-start":
283
+ writer.write({
284
+ type: "data",
285
+ name: "web-search-start",
286
+ data: { query: event.query }
287
+ });
288
+ return currentTextId;
289
+ case "web-search-finish":
290
+ writer.write({
291
+ type: "data",
292
+ name: "web-search-finish",
293
+ data: {
294
+ query: event.query,
295
+ resultCount: event.resultCount
296
+ }
297
+ });
298
+ return currentTextId;
299
+ case "http-request-start":
300
+ writer.write({
301
+ type: "data",
302
+ name: "http-request-start",
303
+ data: {
304
+ url: event.url,
305
+ method: event.method
306
+ }
307
+ });
308
+ return currentTextId;
309
+ case "http-request-finish":
310
+ writer.write({
311
+ type: "data",
312
+ name: "http-request-finish",
313
+ data: {
314
+ url: event.url,
315
+ statusCode: event.statusCode
316
+ }
317
+ });
318
+ return currentTextId;
319
+ case "fetch-url-start":
320
+ writer.write({
321
+ type: "data",
322
+ name: "fetch-url-start",
323
+ data: { url: event.url }
324
+ });
325
+ return currentTextId;
326
+ case "fetch-url-finish":
327
+ writer.write({
328
+ type: "data",
329
+ name: "fetch-url-finish",
330
+ data: {
331
+ url: event.url,
332
+ success: event.success
333
+ }
334
+ });
335
+ return currentTextId;
336
+ case "subagent-start":
337
+ writer.write({
338
+ type: "data",
339
+ name: "subagent-start",
340
+ data: {
341
+ name: event.name,
342
+ task: event.task
343
+ }
344
+ });
345
+ return currentTextId;
346
+ case "subagent-finish":
347
+ writer.write({
348
+ type: "data",
349
+ name: "subagent-finish",
350
+ data: {
351
+ name: event.name,
352
+ result: event.result
353
+ }
354
+ });
355
+ return currentTextId;
356
+ case "subagent-step":
357
+ writer.write({
358
+ type: "data",
359
+ name: "subagent-step",
360
+ data: {
361
+ stepIndex: event.stepIndex,
362
+ toolCalls: event.toolCalls
363
+ }
364
+ });
365
+ return currentTextId;
366
+ case "checkpoint-saved":
367
+ writer.write({
368
+ type: "data",
369
+ name: "checkpoint-saved",
370
+ data: {
371
+ threadId: event.threadId,
372
+ step: event.step
373
+ }
374
+ });
375
+ return currentTextId;
376
+ case "checkpoint-loaded":
377
+ writer.write({
378
+ type: "data",
379
+ name: "checkpoint-loaded",
380
+ data: {
381
+ threadId: event.threadId,
382
+ step: event.step,
383
+ messagesCount: event.messagesCount
384
+ }
385
+ });
386
+ return currentTextId;
387
+ case "error":
388
+ if (currentTextId) {
389
+ writer.write({
390
+ type: "text-end",
391
+ id: currentTextId
392
+ });
393
+ currentTextId = null;
394
+ }
395
+ writer.write({
396
+ type: "error",
397
+ errorText: event.error.message
398
+ });
399
+ return null;
141
400
  case "done":
142
- default: return "ready";
401
+ if (currentTextId) {
402
+ writer.write({
403
+ type: "text-end",
404
+ id: currentTextId
405
+ });
406
+ currentTextId = null;
407
+ }
408
+ writer.write({
409
+ type: "finish",
410
+ finishReason: "stop"
411
+ });
412
+ return null;
413
+ default: return currentTextId;
143
414
  }
144
415
  }
145
416
 
146
417
  //#endregion
147
- //#region src/adapters/elements/useElementsAdapter.ts
418
+ //#region src/adapters/elements/messageConverters.ts
148
419
  /**
149
- * React hook adapter for AI SDK Elements
420
+ * Message conversion utilities for AI SDK Elements adapter
150
421
  *
151
- * Provides Elements-compatible interface for deepagentsdk
422
+ * Provides utilities for converting between UI message formats and model message formats.
423
+ * The primary conversion is handled by AI SDK's `convertToModelMessages`, but these
424
+ * utilities provide additional helpers for DeepAgent-specific needs.
152
425
  */
153
- let eventCounter = 0;
154
- function createEventId() {
155
- return `event-${++eventCounter}`;
156
- }
157
426
  /**
158
- * Hook that adapts deepagentsdk to work with AI SDK Elements UI components
427
+ * Re-export AI SDK's convertToModelMessages for convenience.
159
428
  *
160
- * @param options - Configuration options
161
- * @returns Elements-compatible interface
429
+ * This function converts UIMessage[] (from useChat) to ModelMessage[]
430
+ * (for agent consumption), handling:
431
+ * - Role mapping (user/assistant)
432
+ * - Tool call/result parts
433
+ * - Text content extraction
162
434
  *
163
435
  * @example
164
- * ```tsx
165
- * import { useElementsAdapter } from 'deepagentsdk/elements';
166
- * import { Conversation, Message, PromptInput } from '@/components/ai-elements';
167
- *
168
- * function Chat() {
169
- * const { uiMessages, sendMessage } = useElementsAdapter({
170
- * model,
171
- * backend
172
- * });
436
+ * ```typescript
437
+ * import { convertUIMessagesToModelMessages } from 'deepagentsdk/adapters/elements';
173
438
  *
174
- * return (
175
- * <Conversation>
176
- * {uiMessages.map(msg => <Message key={msg.id} from={msg.role} />)}
177
- * <PromptInput onSubmit={sendMessage} />
178
- * </Conversation>
179
- * );
180
- * }
439
+ * const modelMessages = await convertUIMessagesToModelMessages(uiMessages);
181
440
  * ```
182
441
  */
183
- function useElementsAdapter(options) {
184
- const { model, backend, tools, maxSteps = 10, systemPrompt } = options;
185
- const [status, setStatus] = useState("idle");
186
- const [streamingText, setStreamingText] = useState("");
187
- const [events, setEvents] = useState([]);
188
- const [state, setState] = useState({
189
- todos: [],
190
- files: {}
191
- });
192
- const abortControllerRef = useRef(null);
193
- const accumulatedTextRef = useRef("");
194
- const agentRef = useRef(createDeepAgent({
195
- model,
196
- maxSteps,
197
- systemPrompt,
198
- backend,
199
- tools
200
- }));
201
- const addEvent = useCallback((event) => {
202
- setEvents((prev) => [...prev, {
203
- id: createEventId(),
204
- type: event.type,
205
- event,
206
- timestamp: /* @__PURE__ */ new Date()
207
- }]);
208
- }, []);
209
- const flushTextSegment = useCallback(() => {
210
- if (accumulatedTextRef.current.trim()) {
211
- addEvent({
212
- type: "text-segment",
213
- text: accumulatedTextRef.current
214
- });
215
- accumulatedTextRef.current = "";
216
- setStreamingText("");
217
- }
218
- }, [addEvent]);
219
- const sendMessage = async (message) => {
220
- if (!message.text.trim()) return;
221
- setStatus("thinking");
222
- setStreamingText("");
223
- accumulatedTextRef.current = "";
224
- addEvent({
225
- type: "user-message",
226
- content: message.text
227
- });
228
- abortControllerRef.current = new AbortController();
229
- try {
230
- for await (const event of agentRef.current.streamWithEvents({
231
- messages: [{
232
- role: "user",
233
- content: message.text
234
- }],
235
- state,
236
- abortSignal: abortControllerRef.current.signal
237
- })) switch (event.type) {
238
- case "text":
239
- setStatus("streaming");
240
- accumulatedTextRef.current += event.text;
241
- setStreamingText(accumulatedTextRef.current);
242
- break;
243
- case "step-start":
244
- if (event.stepNumber > 1) addEvent(event);
245
- break;
246
- case "tool-call":
247
- flushTextSegment();
248
- setStatus("tool-call");
249
- addEvent(event);
250
- break;
251
- case "tool-result":
252
- addEvent(event);
253
- break;
254
- case "todos-changed":
255
- flushTextSegment();
256
- setStatus("tool-call");
257
- setState((prev) => ({
258
- ...prev,
259
- todos: event.todos
260
- }));
261
- addEvent(event);
262
- break;
263
- case "done":
264
- flushTextSegment();
265
- setStatus("done");
266
- setState(event.state);
267
- addEvent(event);
268
- break;
269
- case "error":
270
- flushTextSegment();
271
- setStatus("error");
272
- addEvent(event);
273
- break;
274
- default:
275
- addEvent(event);
276
- break;
277
- }
278
- setStatus("idle");
279
- } catch (err) {
280
- if (err.name === "AbortError") {
281
- flushTextSegment();
282
- setStatus("idle");
283
- } else {
284
- flushTextSegment();
285
- setStatus("error");
286
- }
287
- } finally {
288
- abortControllerRef.current = null;
289
- }
290
- };
291
- const abort = useCallback(() => {
292
- if (abortControllerRef.current) {
293
- abortControllerRef.current.abort();
294
- setStatus("idle");
442
+ async function convertUIMessagesToModelMessages(messages) {
443
+ return await convertToModelMessages(messages);
444
+ }
445
+ /**
446
+ * Extract the last user message text from a UIMessage array.
447
+ * Useful for extracting the prompt from a conversation.
448
+ *
449
+ * @param messages - Array of UI messages
450
+ * @returns The text content of the last user message, or undefined if none
451
+ */
452
+ function extractLastUserMessage(messages) {
453
+ for (let i = messages.length - 1; i >= 0; i--) {
454
+ const msg = messages[i];
455
+ if (msg && msg.role === "user" && msg.parts) {
456
+ const textParts = msg.parts.filter((p) => p.type === "text");
457
+ if (textParts.length > 0) return textParts.map((p) => p.text).join("");
295
458
  }
296
- }, []);
297
- const clear = useCallback(() => {
298
- setEvents([]);
299
- setStreamingText("");
300
- setStatus("idle");
301
- }, []);
302
- const uiStatus = useMemo(() => mapAgentStatusToUIStatus(status), [status]);
303
- const uiMessages = useMemo(() => convertEventsToUIMessages(events, streamingText, uiStatus), [
304
- events,
305
- streamingText,
306
- uiStatus
307
- ]);
459
+ }
460
+ }
461
+ /**
462
+ * Check if the messages contain any tool parts.
463
+ * This is a simplified helper that checks for any tool-related parts.
464
+ *
465
+ * @param messages - Array of UI messages
466
+ * @returns True if there are any tool-related parts in the messages
467
+ */
468
+ function hasToolParts(messages) {
469
+ for (const msg of messages) {
470
+ if (!msg.parts) continue;
471
+ for (const part of msg.parts) if (part.type.startsWith("tool-") || part.type === "dynamic-tool") return true;
472
+ }
473
+ return false;
474
+ }
475
+ /**
476
+ * Count the number of messages by role.
477
+ *
478
+ * @param messages - Array of UI messages
479
+ * @returns Object with counts by role
480
+ */
481
+ function countMessagesByRole(messages) {
482
+ let user = 0;
483
+ let assistant = 0;
484
+ let system = 0;
485
+ for (const msg of messages) if (msg.role === "user") user++;
486
+ else if (msg.role === "assistant") assistant++;
487
+ else if (msg.role === "system") system++;
308
488
  return {
309
- uiMessages,
310
- uiStatus,
311
- toolParts: useMemo(() => extractToolParts(uiMessages), [uiMessages]),
312
- sendMessage,
313
- abort,
314
- clear
489
+ user,
490
+ assistant,
491
+ system
315
492
  };
316
493
  }
494
+ /**
495
+ * Extract all text content from a message.
496
+ *
497
+ * @param message - A UI message
498
+ * @returns Combined text from all text parts
499
+ */
500
+ function extractTextFromMessage(message) {
501
+ if (!message.parts) return "";
502
+ return message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
503
+ }
317
504
 
318
505
  //#endregion
319
- export { convertEventsToUIMessages, extractToolParts, mapAgentStatusToUIStatus, useElementsAdapter };
506
+ export { convertUIMessagesToModelMessages, countMessagesByRole, createElementsRouteHandler, extractLastUserMessage, extractTextFromMessage, hasToolParts, mapEventToProtocol };
320
507
  //# sourceMappingURL=index.mjs.map