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