@sandagent/sdk 0.8.3 → 0.8.4

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.
@@ -1,56 +1,42 @@
1
1
  "use client";
2
2
  "use client";
3
3
 
4
- // src/react/useSandAgentChat.ts
5
- import { useChat } from "@ai-sdk/react";
6
- import { DefaultChatTransport } from "ai";
4
+ // src/react/useArtifacts.ts
7
5
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
8
- function useSandAgentChat({
9
- apiEndpoint = "/api/ai",
10
- body = {}
11
- } = {}) {
6
+ function getFileExtensionFromMimeType(mimeType) {
7
+ const mimeToExtension = {
8
+ "text/plain": "txt",
9
+ "text/html": "html",
10
+ "text/css": "css",
11
+ "text/javascript": "js",
12
+ "text/markdown": "md",
13
+ "application/json": "json",
14
+ "application/xml": "xml",
15
+ "application/pdf": "pdf",
16
+ "image/png": "png",
17
+ "image/jpeg": "jpg",
18
+ "image/gif": "gif",
19
+ "image/svg+xml": "svg"
20
+ };
21
+ if (mimeToExtension[mimeType]) {
22
+ return mimeToExtension[mimeType];
23
+ }
24
+ if (mimeType.includes("markdown")) return "md";
25
+ if (mimeType.includes("json")) return "json";
26
+ if (mimeType.includes("javascript")) return "js";
27
+ if (mimeType.includes("typescript")) return "ts";
28
+ if (mimeType.includes("html")) return "html";
29
+ if (mimeType.includes("css")) return "css";
30
+ if (mimeType.includes("xml")) return "xml";
31
+ if (mimeType.includes("python")) return "py";
32
+ return "txt";
33
+ }
34
+ function useArtifacts({
35
+ messages
36
+ }) {
12
37
  const [selectedArtifact, setSelectedArtifact] = useState(
13
38
  null
14
39
  );
15
- const bodyRef = useRef(body);
16
- const messagesRef = useRef([]);
17
- useEffect(() => {
18
- bodyRef.current = body;
19
- }, [body]);
20
- const getResumeFromMessage = (message) => {
21
- if (!message?.parts) return void 0;
22
- for (const part of message.parts) {
23
- if (part.type === "text") {
24
- const providerMetadata = part.providerMetadata;
25
- if (providerMetadata?.sandagent?.sessionId) {
26
- return providerMetadata.sandagent.sessionId;
27
- }
28
- }
29
- }
30
- return void 0;
31
- };
32
- const {
33
- messages,
34
- sendMessage: sendMessageInternal,
35
- status,
36
- error,
37
- stop
38
- } = useChat({
39
- transport: new DefaultChatTransport({
40
- api: apiEndpoint,
41
- body: () => {
42
- const lastMessage = messagesRef.current[messagesRef.current.length - 1];
43
- const resume = getResumeFromMessage(lastMessage);
44
- return {
45
- resume,
46
- ...bodyRef.current
47
- };
48
- }
49
- })
50
- });
51
- useEffect(() => {
52
- messagesRef.current = messages;
53
- }, [messages]);
54
40
  const prevArtifactsRef = useRef([]);
55
41
  const artifacts = useMemo(() => {
56
42
  const results = [];
@@ -93,46 +79,43 @@ function useSandAgentChat({
93
79
  setSelectedArtifact((prev) => prev === null ? prev : null);
94
80
  }
95
81
  }, [artifacts]);
96
- const isLoading = status === "streaming" || status === "submitted";
97
- const hasError = status === "error" && !!error;
98
- const sendMessage = useCallback(
99
- (text) => {
100
- if (!isLoading && text.trim()) {
101
- sendMessageInternal({
102
- role: "user",
103
- parts: [{ type: "text", text: text.trim() }]
104
- });
105
- }
106
- },
107
- [isLoading, sendMessageInternal]
108
- );
109
- const handleSubmit = useCallback(
110
- (message) => {
111
- if (!isLoading) {
112
- if (message.text) {
113
- sendMessageInternal({
114
- role: "user",
115
- parts: [{ type: "text", text: message.text.trim() }]
116
- });
117
- } else {
118
- sendMessageInternal();
119
- }
82
+ const selectArtifactById = useCallback(
83
+ (artifactId) => {
84
+ const artifact = artifacts.find((a) => a.artifactId === artifactId);
85
+ if (artifact) {
86
+ setSelectedArtifact(artifact);
120
87
  }
121
88
  },
122
- [isLoading, sendMessageInternal]
89
+ [artifacts]
123
90
  );
91
+ const copyContent = useCallback(async (artifact) => {
92
+ await navigator.clipboard.writeText(artifact.content);
93
+ }, []);
94
+ const downloadArtifact = useCallback((artifact) => {
95
+ const blob = new Blob([artifact.content], { type: artifact.mimeType });
96
+ const url = URL.createObjectURL(blob);
97
+ const a = document.createElement("a");
98
+ a.href = url;
99
+ const fileName = artifact.artifactId.split("/").pop() || artifact.artifactId;
100
+ const extension = getFileExtensionFromMimeType(artifact.mimeType);
101
+ a.download = fileName.includes(".") ? fileName : `${fileName}.${extension}`;
102
+ document.body.appendChild(a);
103
+ a.click();
104
+ document.body.removeChild(a);
105
+ URL.revokeObjectURL(url);
106
+ }, []);
107
+ const hasArtifacts = artifacts.length > 0;
108
+ const count = artifacts.length;
124
109
  return {
125
- messages,
126
- status,
127
- error,
128
- isLoading,
129
- hasError,
130
110
  artifacts,
131
111
  selectedArtifact,
132
112
  setSelectedArtifact,
133
- sendMessage,
134
- stop,
135
- handleSubmit
113
+ selectArtifactById,
114
+ hasArtifacts,
115
+ count,
116
+ copyContent,
117
+ downloadArtifact,
118
+ getFileExtension: getFileExtensionFromMimeType
136
119
  };
137
120
  }
138
121
 
@@ -253,29 +236,164 @@ function useAskUserQuestion({
253
236
  };
254
237
  }
255
238
 
239
+ // src/react/useSandAgentChat.ts
240
+ import { useChat } from "@ai-sdk/react";
241
+ import { DefaultChatTransport } from "ai";
242
+ import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef2, useState as useState3 } from "react";
243
+ function useSandAgentChat({
244
+ apiEndpoint = "/api/ai",
245
+ body = {}
246
+ } = {}) {
247
+ const [selectedArtifact, setSelectedArtifact] = useState3(
248
+ null
249
+ );
250
+ const bodyRef = useRef2(body);
251
+ const messagesRef = useRef2([]);
252
+ useEffect2(() => {
253
+ bodyRef.current = body;
254
+ }, [body]);
255
+ const getResumeFromMessage = (message) => {
256
+ if (!message?.parts) return void 0;
257
+ for (const part of message.parts) {
258
+ if (part.type === "text") {
259
+ const providerMetadata = part.providerMetadata;
260
+ if (providerMetadata?.sandagent?.sessionId) {
261
+ return providerMetadata.sandagent.sessionId;
262
+ }
263
+ }
264
+ }
265
+ return void 0;
266
+ };
267
+ const {
268
+ messages,
269
+ sendMessage: sendMessageInternal,
270
+ status,
271
+ error,
272
+ stop
273
+ } = useChat({
274
+ transport: new DefaultChatTransport({
275
+ api: apiEndpoint,
276
+ body: () => {
277
+ const lastMessage = messagesRef.current[messagesRef.current.length - 1];
278
+ const resume = getResumeFromMessage(lastMessage);
279
+ return {
280
+ resume,
281
+ ...bodyRef.current
282
+ };
283
+ }
284
+ })
285
+ });
286
+ useEffect2(() => {
287
+ messagesRef.current = messages;
288
+ }, [messages]);
289
+ const prevArtifactsRef = useRef2([]);
290
+ const artifacts = useMemo3(() => {
291
+ const results = [];
292
+ for (const message of messages) {
293
+ for (const part of message.parts) {
294
+ if (part.type === "data-artifact") {
295
+ const data = part.data;
296
+ if (!results.some((a) => a.artifactId === data.artifactId)) {
297
+ results.push(data);
298
+ }
299
+ }
300
+ }
301
+ }
302
+ const prev = prevArtifactsRef.current;
303
+ if (prev.length === results.length && prev.every((prevArt, idx) => {
304
+ const currArt = results[idx];
305
+ return prevArt.artifactId === currArt.artifactId && prevArt.content === currArt.content && prevArt.mimeType === currArt.mimeType;
306
+ })) {
307
+ return prev;
308
+ }
309
+ prevArtifactsRef.current = results;
310
+ return results;
311
+ }, [messages]);
312
+ useEffect2(() => {
313
+ if (artifacts.length > 0) {
314
+ setSelectedArtifact((prev) => {
315
+ if (!prev) return artifacts[0];
316
+ const currentMatch = artifacts.find(
317
+ (a) => a.artifactId === prev.artifactId
318
+ );
319
+ if (!currentMatch) {
320
+ return artifacts[0];
321
+ }
322
+ if (currentMatch.content === prev.content && currentMatch.mimeType === prev.mimeType) {
323
+ return prev;
324
+ }
325
+ return currentMatch;
326
+ });
327
+ } else {
328
+ setSelectedArtifact((prev) => prev === null ? prev : null);
329
+ }
330
+ }, [artifacts]);
331
+ const isLoading = status === "streaming" || status === "submitted";
332
+ const hasError = status === "error" && !!error;
333
+ const sendMessage = useCallback3(
334
+ (text) => {
335
+ if (!isLoading && text.trim()) {
336
+ sendMessageInternal({
337
+ role: "user",
338
+ parts: [{ type: "text", text: text.trim() }]
339
+ });
340
+ }
341
+ },
342
+ [isLoading, sendMessageInternal]
343
+ );
344
+ const handleSubmit = useCallback3(
345
+ (message) => {
346
+ if (!isLoading) {
347
+ if (message.text) {
348
+ sendMessageInternal({
349
+ role: "user",
350
+ parts: [{ type: "text", text: message.text.trim() }]
351
+ });
352
+ } else {
353
+ sendMessageInternal();
354
+ }
355
+ }
356
+ },
357
+ [isLoading, sendMessageInternal]
358
+ );
359
+ return {
360
+ messages,
361
+ status,
362
+ error,
363
+ isLoading,
364
+ hasError,
365
+ artifacts,
366
+ selectedArtifact,
367
+ setSelectedArtifact,
368
+ sendMessage,
369
+ stop,
370
+ handleSubmit
371
+ };
372
+ }
373
+
256
374
  // src/react/useWriteTool.ts
257
- import { useMemo as useMemo3 } from "react";
375
+ import { useMemo as useMemo4 } from "react";
258
376
  function useWriteTool({
259
377
  part
260
378
  }) {
261
- const inputData = useMemo3(() => {
379
+ const inputData = useMemo4(() => {
262
380
  if (!part.input || typeof part.input !== "object") return null;
263
381
  const input = part.input;
264
382
  if (!input.file_path) return null;
265
383
  return input;
266
384
  }, [part.input]);
267
- const outputData = useMemo3(() => {
385
+ const outputData = useMemo4(() => {
268
386
  if (!part.output || typeof part.output !== "object") return null;
269
387
  const output = part.output;
270
388
  if (!output.filePath) return null;
271
389
  return output;
272
390
  }, [part.output]);
273
391
  const filePath = outputData?.filePath ?? inputData?.file_path ?? null;
274
- const fileName = useMemo3(() => {
392
+ const fileName = useMemo4(() => {
275
393
  if (!filePath) return null;
276
394
  return filePath.split("/").pop() || filePath;
277
395
  }, [filePath]);
278
- const fileExtension = useMemo3(() => {
396
+ const fileExtension = useMemo4(() => {
279
397
  if (!fileName) return null;
280
398
  const parts = fileName.split(".");
281
399
  return parts.length > 1 ? parts.pop()?.toLowerCase() || null : null;
@@ -284,7 +402,7 @@ function useWriteTool({
284
402
  const operationType = outputData?.type ?? null;
285
403
  const originalContent = outputData?.originalFile ?? null;
286
404
  const structuredPatch = outputData?.structuredPatch ?? null;
287
- const state = useMemo3(() => {
405
+ const state = useMemo4(() => {
288
406
  if (part.state === "input-streaming") return "streaming";
289
407
  if (part.state === "output-error") return "error";
290
408
  if (part.state === "output-available") return "output-available";
@@ -294,7 +412,7 @@ function useWriteTool({
294
412
  const isCompleted = state === "output-available";
295
413
  const hasError = state === "error";
296
414
  const errorText = part.errorText ?? null;
297
- const isMarkdown = useMemo3(() => {
415
+ const isMarkdown = useMemo4(() => {
298
416
  if (!fileExtension) return false;
299
417
  return ["md", "markdown", "mdx"].includes(fileExtension);
300
418
  }, [fileExtension]);
@@ -314,124 +432,6 @@ function useWriteTool({
314
432
  fileExtension
315
433
  };
316
434
  }
317
-
318
- // src/react/useArtifacts.ts
319
- import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo4, useRef as useRef2, useState as useState3 } from "react";
320
- function getFileExtensionFromMimeType(mimeType) {
321
- const mimeToExtension = {
322
- "text/plain": "txt",
323
- "text/html": "html",
324
- "text/css": "css",
325
- "text/javascript": "js",
326
- "text/markdown": "md",
327
- "application/json": "json",
328
- "application/xml": "xml",
329
- "application/pdf": "pdf",
330
- "image/png": "png",
331
- "image/jpeg": "jpg",
332
- "image/gif": "gif",
333
- "image/svg+xml": "svg"
334
- };
335
- if (mimeToExtension[mimeType]) {
336
- return mimeToExtension[mimeType];
337
- }
338
- if (mimeType.includes("markdown")) return "md";
339
- if (mimeType.includes("json")) return "json";
340
- if (mimeType.includes("javascript")) return "js";
341
- if (mimeType.includes("typescript")) return "ts";
342
- if (mimeType.includes("html")) return "html";
343
- if (mimeType.includes("css")) return "css";
344
- if (mimeType.includes("xml")) return "xml";
345
- if (mimeType.includes("python")) return "py";
346
- return "txt";
347
- }
348
- function useArtifacts({
349
- messages
350
- }) {
351
- const [selectedArtifact, setSelectedArtifact] = useState3(
352
- null
353
- );
354
- const prevArtifactsRef = useRef2([]);
355
- const artifacts = useMemo4(() => {
356
- const results = [];
357
- for (const message of messages) {
358
- for (const part of message.parts) {
359
- if (part.type === "data-artifact") {
360
- const data = part.data;
361
- if (!results.some((a) => a.artifactId === data.artifactId)) {
362
- results.push(data);
363
- }
364
- }
365
- }
366
- }
367
- const prev = prevArtifactsRef.current;
368
- if (prev.length === results.length && prev.every((prevArt, idx) => {
369
- const currArt = results[idx];
370
- return prevArt.artifactId === currArt.artifactId && prevArt.content === currArt.content && prevArt.mimeType === currArt.mimeType;
371
- })) {
372
- return prev;
373
- }
374
- prevArtifactsRef.current = results;
375
- return results;
376
- }, [messages]);
377
- useEffect2(() => {
378
- if (artifacts.length > 0) {
379
- setSelectedArtifact((prev) => {
380
- if (!prev) return artifacts[0];
381
- const currentMatch = artifacts.find(
382
- (a) => a.artifactId === prev.artifactId
383
- );
384
- if (!currentMatch) {
385
- return artifacts[0];
386
- }
387
- if (currentMatch.content === prev.content && currentMatch.mimeType === prev.mimeType) {
388
- return prev;
389
- }
390
- return currentMatch;
391
- });
392
- } else {
393
- setSelectedArtifact((prev) => prev === null ? prev : null);
394
- }
395
- }, [artifacts]);
396
- const selectArtifactById = useCallback3(
397
- (artifactId) => {
398
- const artifact = artifacts.find((a) => a.artifactId === artifactId);
399
- if (artifact) {
400
- setSelectedArtifact(artifact);
401
- }
402
- },
403
- [artifacts]
404
- );
405
- const copyContent = useCallback3(async (artifact) => {
406
- await navigator.clipboard.writeText(artifact.content);
407
- }, []);
408
- const downloadArtifact = useCallback3((artifact) => {
409
- const blob = new Blob([artifact.content], { type: artifact.mimeType });
410
- const url = URL.createObjectURL(blob);
411
- const a = document.createElement("a");
412
- a.href = url;
413
- const fileName = artifact.artifactId.split("/").pop() || artifact.artifactId;
414
- const extension = getFileExtensionFromMimeType(artifact.mimeType);
415
- a.download = fileName.includes(".") ? fileName : `${fileName}.${extension}`;
416
- document.body.appendChild(a);
417
- a.click();
418
- document.body.removeChild(a);
419
- URL.revokeObjectURL(url);
420
- }, []);
421
- const hasArtifacts = artifacts.length > 0;
422
- const count = artifacts.length;
423
- return {
424
- artifacts,
425
- selectedArtifact,
426
- setSelectedArtifact,
427
- selectArtifactById,
428
- hasArtifacts,
429
- count,
430
- copyContent,
431
- downloadArtifact,
432
- getFileExtension: getFileExtensionFromMimeType
433
- };
434
- }
435
435
  export {
436
436
  useArtifacts,
437
437
  useAskUserQuestion,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/useSandAgentChat.ts","../../src/react/useAskUserQuestion.ts","../../src/react/useWriteTool.ts","../../src/react/useArtifacts.ts"],"sourcesContent":["\"use client\";\n\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport, type UIMessage } from \"ai\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type {\n ArtifactData,\n UseSandAgentChatOptions,\n UseSandAgentChatReturn,\n} from \"./types\";\n\n/**\n * useSandAgentChat - Core hook for SandAgent chat functionality\n *\n * Provides all the logic needed for a chat interface:\n * - Message management\n * - Artifact extraction\n * - Session management\n *\n * @example\n * ```tsx\n * import { useSandAgentChat } from \"@sandagent/sdk/react\";\n *\n * const {\n * messages,\n * sendMessage,\n * status,\n * artifacts,\n * selectedArtifact,\n * setSelectedArtifact,\n * } = useSandAgentChat({\n * apiEndpoint: \"/api/ai\",\n * body: { template: \"default\" },\n * });\n * ```\n */\nexport function useSandAgentChat({\n apiEndpoint = \"/api/ai\",\n body = {},\n}: UseSandAgentChatOptions = {}): UseSandAgentChatReturn {\n // Artifact selection state\n const [selectedArtifact, setSelectedArtifact] = useState<ArtifactData | null>(\n null,\n );\n\n // Refs for accessing latest values in callbacks\n const bodyRef = useRef(body);\n const messagesRef = useRef<UIMessage[]>([]);\n\n useEffect(() => {\n bodyRef.current = body;\n }, [body]);\n\n // Helper to extract resume value (sessionId) from message parts' providerMetadata.\n const getResumeFromMessage = (\n message: UIMessage | undefined,\n ): string | undefined => {\n if (!message?.parts) return undefined;\n for (const part of message.parts) {\n if (part.type === \"text\") {\n const providerMetadata = (\n part as { providerMetadata?: { sandagent?: { sessionId?: string } } }\n ).providerMetadata;\n if (providerMetadata?.sandagent?.sessionId) {\n return providerMetadata.sandagent.sessionId;\n }\n }\n }\n return undefined;\n };\n\n // Core chat hook\n const {\n messages,\n sendMessage: sendMessageInternal,\n status,\n error,\n stop,\n } = useChat({\n transport: new DefaultChatTransport({\n api: apiEndpoint,\n body: () => {\n const lastMessage = messagesRef.current[messagesRef.current.length - 1];\n const resume = getResumeFromMessage(lastMessage);\n return {\n resume,\n ...bodyRef.current,\n };\n },\n }),\n });\n\n // Keep messagesRef in sync\n useEffect(() => {\n messagesRef.current = messages;\n }, [messages]);\n\n // Extract artifacts from messages\n const prevArtifactsRef = useRef<ArtifactData[]>([]);\n const artifacts = useMemo(() => {\n const results: ArtifactData[] = [];\n for (const message of messages) {\n for (const part of message.parts) {\n if (part.type === \"data-artifact\") {\n const data = part.data as ArtifactData;\n if (!results.some((a) => a.artifactId === data.artifactId)) {\n results.push(data);\n }\n }\n }\n }\n\n // Memoization optimization\n const prev = prevArtifactsRef.current;\n if (\n prev.length === results.length &&\n prev.every((prevArt, idx) => {\n const currArt = results[idx];\n return (\n prevArt.artifactId === currArt.artifactId &&\n prevArt.content === currArt.content &&\n prevArt.mimeType === currArt.mimeType\n );\n })\n ) {\n return prev;\n }\n\n prevArtifactsRef.current = results;\n return results;\n }, [messages]);\n\n // Sync selectedArtifact when artifacts change\n useEffect(() => {\n if (artifacts.length > 0) {\n setSelectedArtifact((prev) => {\n if (!prev) return artifacts[0];\n\n const currentMatch = artifacts.find(\n (a) => a.artifactId === prev.artifactId,\n );\n\n if (!currentMatch) {\n return artifacts[0];\n }\n\n if (\n currentMatch.content === prev.content &&\n currentMatch.mimeType === prev.mimeType\n ) {\n return prev;\n }\n\n return currentMatch;\n });\n } else {\n setSelectedArtifact((prev) => (prev === null ? prev : null));\n }\n }, [artifacts]);\n\n const isLoading = status === \"streaming\" || status === \"submitted\";\n const hasError = status === \"error\" && !!error;\n\n // Send message helper\n const sendMessage = useCallback(\n (text: string) => {\n if (!isLoading && text.trim()) {\n sendMessageInternal({\n role: \"user\",\n parts: [{ type: \"text\", text: text.trim() }],\n });\n }\n },\n [isLoading, sendMessageInternal],\n );\n\n // Handle submit for PromptInput compatibility\n const handleSubmit = useCallback(\n (message: { text: string }) => {\n if (!isLoading) {\n if (message.text) {\n sendMessageInternal({\n role: \"user\",\n parts: [{ type: \"text\", text: message.text.trim() }],\n });\n } else {\n sendMessageInternal();\n }\n }\n },\n [isLoading, sendMessageInternal],\n );\n\n return {\n messages,\n status,\n error,\n isLoading,\n hasError,\n artifacts,\n selectedArtifact,\n setSelectedArtifact,\n sendMessage,\n stop,\n handleSubmit,\n };\n}\n","\"use client\";\n\nimport { useCallback, useMemo, useState } from \"react\";\nimport type {\n AskUserQuestionInput,\n AskUserQuestionOutput,\n Question,\n UseAskUserQuestionOptions,\n UseAskUserQuestionReturn,\n} from \"./types\";\n\n/**\n * useAskUserQuestion - Hook for handling AskUserQuestion tool interactions\n *\n * Manages the state for user question/answer interactions in chat.\n * Provides answer selection, completion detection, and formatted output.\n *\n * @example\n * ```tsx\n * import { useAskUserQuestion } from \"@sandagent/sdk/react\";\n *\n * function QuestionUI({ part, sessionId }) {\n * const {\n * questions,\n * answers,\n * isCompleted,\n * selectAnswer,\n * isSelected,\n * } = useAskUserQuestion({\n * part,\n * onAnswer: (data) => {\n * fetch(\"/api/answer\", {\n * method: \"POST\",\n * body: JSON.stringify(data),\n * });\n * },\n * });\n *\n * if (isCompleted) {\n * return <div>Completed</div>;\n * }\n *\n * return (\n * <div>\n * {questions.map((q) => (\n * <div key={q.question}>\n * <p>{q.question}</p>\n * {q.options?.map((opt) => (\n * <button\n * key={opt.label}\n * onClick={() => selectAnswer(q.question, opt.label, q.multiSelect)}\n * style={{ fontWeight: isSelected(q.question, opt.label, q.multiSelect) ? 'bold' : 'normal' }}\n * >\n * {opt.label}\n * </button>\n * ))}\n * </div>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useAskUserQuestion({\n part,\n answerEndpoint = \"/api/answer\",\n extraBody,\n onAnswer,\n}: UseAskUserQuestionOptions): UseAskUserQuestionReturn {\n const [answers, setAnswers] = useState<Record<string, string | string[]>>({});\n\n // Parse questions from part.input\n const questions = useMemo((): Question[] => {\n if (!part.input || typeof part.input !== \"object\") return [];\n const input = part.input as AskUserQuestionInput;\n if (!input.questions || !Array.isArray(input.questions)) return [];\n return input.questions;\n }, [part.input]);\n\n // Parse answers from part.output (for restored sessions)\n const outputAnswers = useMemo((): Record<\n string,\n string | string[]\n > | null => {\n if (part.output && typeof part.output === \"object\") {\n const output = part.output as AskUserQuestionOutput;\n if (output.answers) {\n const hasRealAnswers = Object.values(output.answers).some(\n (v) => v && v.trim() !== \"\",\n );\n if (hasRealAnswers) {\n const parsed: Record<string, string | string[]> = {};\n for (const q of questions) {\n const val = output.answers[q.question];\n if (q.multiSelect && val) {\n parsed[q.question] = val.split(\", \").filter(Boolean);\n } else {\n parsed[q.question] = val || \"\";\n }\n }\n return parsed;\n }\n }\n }\n return null;\n }, [part.output, questions]);\n\n // Determine display answers (user input or restored from output)\n const displayAnswers = useMemo(() => {\n return Object.keys(answers).length > 0 ? answers : outputAnswers || {};\n }, [answers, outputAnswers]);\n\n // Check if completed\n const isCompleted = useMemo(() => {\n return (\n part.state === \"output-available\" ||\n (Object.keys(answers).length === 0 && outputAnswers !== null)\n );\n }, [part.state, answers, outputAnswers]);\n\n // Check if waiting for input (for animation)\n const isWaitingForInput = part.state === \"input-available\";\n\n // Get formatted answers map\n const getAnswersMap = useCallback((): Record<string, string> => {\n const answersMap: Record<string, string> = {};\n for (const q of questions) {\n const answer = displayAnswers[q.question];\n if (q.multiSelect) {\n answersMap[q.question] = Array.isArray(answer) ? answer.join(\", \") : \"\";\n } else {\n answersMap[q.question] = (answer as string) || \"\";\n }\n }\n return answersMap;\n }, [questions, displayAnswers]);\n\n // Select answer handler\n const selectAnswer = useCallback(\n (question: string, value: string, multiSelect = false) => {\n const newAnswers = { ...answers };\n\n if (multiSelect) {\n const current = (newAnswers[question] as string[]) || [];\n newAnswers[question] = current.includes(value)\n ? current.filter((v) => v !== value)\n : [...current, value];\n } else {\n newAnswers[question] = value;\n }\n\n setAnswers(newAnswers);\n\n // Prepare answers map for callback and API\n const answersMap: Record<string, string> = {};\n for (const q of questions) {\n const answer = newAnswers[q.question];\n if (q.multiSelect) {\n answersMap[q.question] = Array.isArray(answer)\n ? answer.join(\", \")\n : \"\";\n } else {\n answersMap[q.question] = (answer as string) || \"\";\n }\n }\n\n // Auto-submit to answer endpoint\n fetch(answerEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n toolCallId: part.toolCallId,\n questions,\n answers: answersMap,\n ...extraBody,\n }),\n }).catch((err) => {\n console.error(\"[useAskUserQuestion] Submit failed:\", err);\n });\n\n // Trigger callback\n onAnswer?.({\n toolCallId: part.toolCallId,\n questions,\n answers: answersMap,\n });\n },\n [answers, questions, part.toolCallId, answerEndpoint, extraBody, onAnswer],\n );\n\n // Check if option is selected\n const isSelected = useCallback(\n (question: string, optionLabel: string, multiSelect = false): boolean => {\n const selectedValue = displayAnswers[question];\n if (multiSelect) {\n return (\n (selectedValue as string[] | undefined)?.includes(optionLabel) ??\n false\n );\n }\n return selectedValue === optionLabel;\n },\n [displayAnswers],\n );\n\n return {\n questions,\n answers: displayAnswers,\n isCompleted,\n isWaitingForInput,\n selectAnswer,\n getAnswersMap,\n isSelected,\n };\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport type { DynamicToolUIPart } from \"./types\";\n\n/**\n * Write tool input structure\n */\nexport interface WriteToolInput {\n file_path: string;\n content: string;\n}\n\n/**\n * Write tool output structure\n */\nexport interface WriteToolOutput {\n type: \"create\" | \"edit\";\n filePath: string;\n content: string;\n structuredPatch?: unknown[];\n originalFile?: string | null;\n}\n\n/**\n * Options for useWriteTool hook\n */\nexport interface UseWriteToolOptions {\n /** The dynamic tool UI part from the message */\n part: DynamicToolUIPart;\n}\n\n/**\n * Return type for useWriteTool hook\n */\nexport interface UseWriteToolReturn {\n /** File path being written to */\n filePath: string | null;\n /** File name (extracted from path) */\n fileName: string | null;\n /** File content */\n content: string | null;\n /** Operation type: 'create' or 'edit' */\n operationType: \"create\" | \"edit\" | null;\n /** Original file content (for edit operations) */\n originalContent: string | null;\n /** Structured patch data (for edit operations) */\n structuredPatch: unknown[] | null;\n /** Tool state */\n state: \"streaming\" | \"input-available\" | \"output-available\" | \"error\";\n /** Whether the tool is currently streaming input */\n isStreaming: boolean;\n /** Whether the write operation completed successfully */\n isCompleted: boolean;\n /** Whether there was an error */\n hasError: boolean;\n /** Error message if any */\n errorText: string | null;\n /** Whether this is a markdown file */\n isMarkdown: boolean;\n /** File extension */\n fileExtension: string | null;\n}\n\n/**\n * useWriteTool - Hook for handling Write tool interactions\n *\n * Parses the Write tool's input and output data, providing\n * easy access to file information and operation state.\n *\n * @example\n * ```tsx\n * import { useWriteTool } from \"@sandagent/sdk/react\";\n *\n * function WriteToolUI({ part }) {\n * const {\n * filePath,\n * fileName,\n * content,\n * operationType,\n * isStreaming,\n * isCompleted,\n * isMarkdown,\n * } = useWriteTool({ part });\n *\n * if (isStreaming) {\n * return <div>Writing {fileName}...</div>;\n * }\n *\n * return (\n * <div>\n * <h3>{fileName} ({operationType})</h3>\n * {isMarkdown ? (\n * <MarkdownRenderer content={content} />\n * ) : (\n * <pre>{content}</pre>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useWriteTool({\n part,\n}: UseWriteToolOptions): UseWriteToolReturn {\n // Parse input data\n const inputData = useMemo((): WriteToolInput | null => {\n if (!part.input || typeof part.input !== \"object\") return null;\n const input = part.input as WriteToolInput;\n if (!input.file_path) return null;\n return input;\n }, [part.input]);\n\n // Parse output data\n const outputData = useMemo((): WriteToolOutput | null => {\n if (!part.output || typeof part.output !== \"object\") return null;\n const output = part.output as WriteToolOutput;\n if (!output.filePath) return null;\n return output;\n }, [part.output]);\n\n // Determine file path (prefer output, fallback to input)\n const filePath = outputData?.filePath ?? inputData?.file_path ?? null;\n\n // Extract file name\n const fileName = useMemo(() => {\n if (!filePath) return null;\n return filePath.split(\"/\").pop() || filePath;\n }, [filePath]);\n\n // Extract file extension\n const fileExtension = useMemo(() => {\n if (!fileName) return null;\n const parts = fileName.split(\".\");\n return parts.length > 1 ? parts.pop()?.toLowerCase() || null : null;\n }, [fileName]);\n\n // Determine content (prefer output, fallback to input)\n const content = outputData?.content ?? inputData?.content ?? null;\n\n // Operation type\n const operationType = outputData?.type ?? null;\n\n // Original content (for edits)\n const originalContent = outputData?.originalFile ?? null;\n\n // Structured patch\n const structuredPatch = outputData?.structuredPatch ?? null;\n\n // State parsing\n const state = useMemo((): UseWriteToolReturn[\"state\"] => {\n if (part.state === \"input-streaming\") return \"streaming\";\n if (part.state === \"output-error\") return \"error\";\n if (part.state === \"output-available\") return \"output-available\";\n return \"input-available\";\n }, [part.state]);\n\n const isStreaming = state === \"streaming\";\n const isCompleted = state === \"output-available\";\n const hasError = state === \"error\";\n const errorText = part.errorText ?? null;\n\n // Check if markdown\n const isMarkdown = useMemo(() => {\n if (!fileExtension) return false;\n return [\"md\", \"markdown\", \"mdx\"].includes(fileExtension);\n }, [fileExtension]);\n\n return {\n filePath,\n fileName,\n content,\n operationType,\n originalContent,\n structuredPatch,\n state,\n isStreaming,\n isCompleted,\n hasError,\n errorText,\n isMarkdown,\n fileExtension,\n };\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ArtifactData, UIMessage } from \"./types\";\n\n/**\n * Options for useArtifacts hook\n */\nexport interface UseArtifactsOptions {\n /** Messages to extract artifacts from */\n messages: UIMessage[];\n}\n\n/**\n * Return type for useArtifacts hook\n */\nexport interface UseArtifactsReturn {\n /** All extracted artifacts */\n artifacts: ArtifactData[];\n /** Currently selected artifact */\n selectedArtifact: ArtifactData | null;\n /** Set the selected artifact */\n setSelectedArtifact: (artifact: ArtifactData | null) => void;\n /** Select artifact by ID */\n selectArtifactById: (artifactId: string) => void;\n /** Whether there are any artifacts */\n hasArtifacts: boolean;\n /** Number of artifacts */\n count: number;\n /** Copy artifact content to clipboard */\n copyContent: (artifact: ArtifactData) => Promise<void>;\n /** Download artifact as file */\n downloadArtifact: (artifact: ArtifactData) => void;\n /** Get file extension from mime type */\n getFileExtension: (mimeType: string) => string;\n}\n\n/**\n * Get file extension from MIME type\n */\nfunction getFileExtensionFromMimeType(mimeType: string): string {\n const mimeToExtension: Record<string, string> = {\n \"text/plain\": \"txt\",\n \"text/html\": \"html\",\n \"text/css\": \"css\",\n \"text/javascript\": \"js\",\n \"text/markdown\": \"md\",\n \"application/json\": \"json\",\n \"application/xml\": \"xml\",\n \"application/pdf\": \"pdf\",\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/gif\": \"gif\",\n \"image/svg+xml\": \"svg\",\n };\n\n // Try exact match\n if (mimeToExtension[mimeType]) {\n return mimeToExtension[mimeType];\n }\n\n // Try partial match (e.g., \"text/x-markdown\" -> \"md\")\n if (mimeType.includes(\"markdown\")) return \"md\";\n if (mimeType.includes(\"json\")) return \"json\";\n if (mimeType.includes(\"javascript\")) return \"js\";\n if (mimeType.includes(\"typescript\")) return \"ts\";\n if (mimeType.includes(\"html\")) return \"html\";\n if (mimeType.includes(\"css\")) return \"css\";\n if (mimeType.includes(\"xml\")) return \"xml\";\n if (mimeType.includes(\"python\")) return \"py\";\n\n // Default\n return \"txt\";\n}\n\n/**\n * useArtifacts - Hook for managing artifacts from chat messages\n *\n * Extracts artifacts from messages and provides selection,\n * copy, and download functionality.\n *\n * @example\n * ```tsx\n * import { useArtifacts } from \"@sandagent/sdk/react\";\n *\n * function ArtifactPanel({ messages }) {\n * const {\n * artifacts,\n * selectedArtifact,\n * setSelectedArtifact,\n * hasArtifacts,\n * copyContent,\n * downloadArtifact,\n * } = useArtifacts({ messages });\n *\n * if (!hasArtifacts) return null;\n *\n * return (\n * <div>\n * {artifacts.map((artifact) => (\n * <button\n * key={artifact.artifactId}\n * onClick={() => setSelectedArtifact(artifact)}\n * >\n * {artifact.artifactId}\n * </button>\n * ))}\n *\n * {selectedArtifact && (\n * <div>\n * <pre>{selectedArtifact.content}</pre>\n * <button onClick={() => copyContent(selectedArtifact)}>Copy</button>\n * <button onClick={() => downloadArtifact(selectedArtifact)}>Download</button>\n * </div>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useArtifacts({\n messages,\n}: UseArtifactsOptions): UseArtifactsReturn {\n const [selectedArtifact, setSelectedArtifact] = useState<ArtifactData | null>(\n null,\n );\n\n // Extract artifacts from messages with memoization optimization\n const prevArtifactsRef = useRef<ArtifactData[]>([]);\n const artifacts = useMemo(() => {\n const results: ArtifactData[] = [];\n for (const message of messages) {\n for (const part of message.parts) {\n if (part.type === \"data-artifact\") {\n const data = part.data as ArtifactData;\n if (!results.some((a) => a.artifactId === data.artifactId)) {\n results.push(data);\n }\n }\n }\n }\n\n // Memoization optimization - return previous reference if content unchanged\n const prev = prevArtifactsRef.current;\n if (\n prev.length === results.length &&\n prev.every((prevArt, idx) => {\n const currArt = results[idx];\n return (\n prevArt.artifactId === currArt.artifactId &&\n prevArt.content === currArt.content &&\n prevArt.mimeType === currArt.mimeType\n );\n })\n ) {\n return prev;\n }\n\n prevArtifactsRef.current = results;\n return results;\n }, [messages]);\n\n // Sync selectedArtifact when artifacts change\n useEffect(() => {\n if (artifacts.length > 0) {\n setSelectedArtifact((prev) => {\n if (!prev) return artifacts[0];\n\n const currentMatch = artifacts.find(\n (a) => a.artifactId === prev.artifactId,\n );\n\n if (!currentMatch) {\n return artifacts[0];\n }\n\n if (\n currentMatch.content === prev.content &&\n currentMatch.mimeType === prev.mimeType\n ) {\n return prev;\n }\n\n return currentMatch;\n });\n } else {\n setSelectedArtifact((prev) => (prev === null ? prev : null));\n }\n }, [artifacts]);\n\n // Select artifact by ID\n const selectArtifactById = useCallback(\n (artifactId: string) => {\n const artifact = artifacts.find((a) => a.artifactId === artifactId);\n if (artifact) {\n setSelectedArtifact(artifact);\n }\n },\n [artifacts],\n );\n\n // Copy content to clipboard\n const copyContent = useCallback(async (artifact: ArtifactData) => {\n await navigator.clipboard.writeText(artifact.content);\n }, []);\n\n // Download artifact as file\n const downloadArtifact = useCallback((artifact: ArtifactData) => {\n const blob = new Blob([artifact.content], { type: artifact.mimeType });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n const fileName =\n artifact.artifactId.split(\"/\").pop() || artifact.artifactId;\n const extension = getFileExtensionFromMimeType(artifact.mimeType);\n a.download = fileName.includes(\".\") ? fileName : `${fileName}.${extension}`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n }, []);\n\n const hasArtifacts = artifacts.length > 0;\n const count = artifacts.length;\n\n return {\n artifacts,\n selectedArtifact,\n setSelectedArtifact,\n selectArtifactById,\n hasArtifacts,\n count,\n copyContent,\n downloadArtifact,\n getFileExtension: getFileExtensionFromMimeType,\n };\n}\n"],"mappings":"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","names":["useCallback","useMemo","useState","useMemo","useCallback","useEffect","useMemo","useRef","useState"]}
1
+ {"version":3,"sources":["../../src/react/useArtifacts.ts","../../src/react/useAskUserQuestion.ts","../../src/react/useSandAgentChat.ts","../../src/react/useWriteTool.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ArtifactData, UIMessage } from \"./types\";\n\n/**\n * Options for useArtifacts hook\n */\nexport interface UseArtifactsOptions {\n /** Messages to extract artifacts from */\n messages: UIMessage[];\n}\n\n/**\n * Return type for useArtifacts hook\n */\nexport interface UseArtifactsReturn {\n /** All extracted artifacts */\n artifacts: ArtifactData[];\n /** Currently selected artifact */\n selectedArtifact: ArtifactData | null;\n /** Set the selected artifact */\n setSelectedArtifact: (artifact: ArtifactData | null) => void;\n /** Select artifact by ID */\n selectArtifactById: (artifactId: string) => void;\n /** Whether there are any artifacts */\n hasArtifacts: boolean;\n /** Number of artifacts */\n count: number;\n /** Copy artifact content to clipboard */\n copyContent: (artifact: ArtifactData) => Promise<void>;\n /** Download artifact as file */\n downloadArtifact: (artifact: ArtifactData) => void;\n /** Get file extension from mime type */\n getFileExtension: (mimeType: string) => string;\n}\n\n/**\n * Get file extension from MIME type\n */\nfunction getFileExtensionFromMimeType(mimeType: string): string {\n const mimeToExtension: Record<string, string> = {\n \"text/plain\": \"txt\",\n \"text/html\": \"html\",\n \"text/css\": \"css\",\n \"text/javascript\": \"js\",\n \"text/markdown\": \"md\",\n \"application/json\": \"json\",\n \"application/xml\": \"xml\",\n \"application/pdf\": \"pdf\",\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/gif\": \"gif\",\n \"image/svg+xml\": \"svg\",\n };\n\n // Try exact match\n if (mimeToExtension[mimeType]) {\n return mimeToExtension[mimeType];\n }\n\n // Try partial match (e.g., \"text/x-markdown\" -> \"md\")\n if (mimeType.includes(\"markdown\")) return \"md\";\n if (mimeType.includes(\"json\")) return \"json\";\n if (mimeType.includes(\"javascript\")) return \"js\";\n if (mimeType.includes(\"typescript\")) return \"ts\";\n if (mimeType.includes(\"html\")) return \"html\";\n if (mimeType.includes(\"css\")) return \"css\";\n if (mimeType.includes(\"xml\")) return \"xml\";\n if (mimeType.includes(\"python\")) return \"py\";\n\n // Default\n return \"txt\";\n}\n\n/**\n * useArtifacts - Hook for managing artifacts from chat messages\n *\n * Extracts artifacts from messages and provides selection,\n * copy, and download functionality.\n *\n * @example\n * ```tsx\n * import { useArtifacts } from \"@sandagent/sdk/react\";\n *\n * function ArtifactPanel({ messages }) {\n * const {\n * artifacts,\n * selectedArtifact,\n * setSelectedArtifact,\n * hasArtifacts,\n * copyContent,\n * downloadArtifact,\n * } = useArtifacts({ messages });\n *\n * if (!hasArtifacts) return null;\n *\n * return (\n * <div>\n * {artifacts.map((artifact) => (\n * <button\n * key={artifact.artifactId}\n * onClick={() => setSelectedArtifact(artifact)}\n * >\n * {artifact.artifactId}\n * </button>\n * ))}\n *\n * {selectedArtifact && (\n * <div>\n * <pre>{selectedArtifact.content}</pre>\n * <button onClick={() => copyContent(selectedArtifact)}>Copy</button>\n * <button onClick={() => downloadArtifact(selectedArtifact)}>Download</button>\n * </div>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useArtifacts({\n messages,\n}: UseArtifactsOptions): UseArtifactsReturn {\n const [selectedArtifact, setSelectedArtifact] = useState<ArtifactData | null>(\n null,\n );\n\n // Extract artifacts from messages with memoization optimization\n const prevArtifactsRef = useRef<ArtifactData[]>([]);\n const artifacts = useMemo(() => {\n const results: ArtifactData[] = [];\n for (const message of messages) {\n for (const part of message.parts) {\n if (part.type === \"data-artifact\") {\n const data = part.data as ArtifactData;\n if (!results.some((a) => a.artifactId === data.artifactId)) {\n results.push(data);\n }\n }\n }\n }\n\n // Memoization optimization - return previous reference if content unchanged\n const prev = prevArtifactsRef.current;\n if (\n prev.length === results.length &&\n prev.every((prevArt, idx) => {\n const currArt = results[idx];\n return (\n prevArt.artifactId === currArt.artifactId &&\n prevArt.content === currArt.content &&\n prevArt.mimeType === currArt.mimeType\n );\n })\n ) {\n return prev;\n }\n\n prevArtifactsRef.current = results;\n return results;\n }, [messages]);\n\n // Sync selectedArtifact when artifacts change\n useEffect(() => {\n if (artifacts.length > 0) {\n setSelectedArtifact((prev) => {\n if (!prev) return artifacts[0];\n\n const currentMatch = artifacts.find(\n (a) => a.artifactId === prev.artifactId,\n );\n\n if (!currentMatch) {\n return artifacts[0];\n }\n\n if (\n currentMatch.content === prev.content &&\n currentMatch.mimeType === prev.mimeType\n ) {\n return prev;\n }\n\n return currentMatch;\n });\n } else {\n setSelectedArtifact((prev) => (prev === null ? prev : null));\n }\n }, [artifacts]);\n\n // Select artifact by ID\n const selectArtifactById = useCallback(\n (artifactId: string) => {\n const artifact = artifacts.find((a) => a.artifactId === artifactId);\n if (artifact) {\n setSelectedArtifact(artifact);\n }\n },\n [artifacts],\n );\n\n // Copy content to clipboard\n const copyContent = useCallback(async (artifact: ArtifactData) => {\n await navigator.clipboard.writeText(artifact.content);\n }, []);\n\n // Download artifact as file\n const downloadArtifact = useCallback((artifact: ArtifactData) => {\n const blob = new Blob([artifact.content], { type: artifact.mimeType });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n const fileName =\n artifact.artifactId.split(\"/\").pop() || artifact.artifactId;\n const extension = getFileExtensionFromMimeType(artifact.mimeType);\n a.download = fileName.includes(\".\") ? fileName : `${fileName}.${extension}`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n }, []);\n\n const hasArtifacts = artifacts.length > 0;\n const count = artifacts.length;\n\n return {\n artifacts,\n selectedArtifact,\n setSelectedArtifact,\n selectArtifactById,\n hasArtifacts,\n count,\n copyContent,\n downloadArtifact,\n getFileExtension: getFileExtensionFromMimeType,\n };\n}\n","\"use client\";\n\nimport { useCallback, useMemo, useState } from \"react\";\nimport type {\n AskUserQuestionInput,\n AskUserQuestionOutput,\n Question,\n UseAskUserQuestionOptions,\n UseAskUserQuestionReturn,\n} from \"./types\";\n\n/**\n * useAskUserQuestion - Hook for handling AskUserQuestion tool interactions\n *\n * Manages the state for user question/answer interactions in chat.\n * Provides answer selection, completion detection, and formatted output.\n *\n * @example\n * ```tsx\n * import { useAskUserQuestion } from \"@sandagent/sdk/react\";\n *\n * function QuestionUI({ part, sessionId }) {\n * const {\n * questions,\n * answers,\n * isCompleted,\n * selectAnswer,\n * isSelected,\n * } = useAskUserQuestion({\n * part,\n * onAnswer: (data) => {\n * fetch(\"/api/answer\", {\n * method: \"POST\",\n * body: JSON.stringify(data),\n * });\n * },\n * });\n *\n * if (isCompleted) {\n * return <div>Completed</div>;\n * }\n *\n * return (\n * <div>\n * {questions.map((q) => (\n * <div key={q.question}>\n * <p>{q.question}</p>\n * {q.options?.map((opt) => (\n * <button\n * key={opt.label}\n * onClick={() => selectAnswer(q.question, opt.label, q.multiSelect)}\n * style={{ fontWeight: isSelected(q.question, opt.label, q.multiSelect) ? 'bold' : 'normal' }}\n * >\n * {opt.label}\n * </button>\n * ))}\n * </div>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useAskUserQuestion({\n part,\n answerEndpoint = \"/api/answer\",\n extraBody,\n onAnswer,\n}: UseAskUserQuestionOptions): UseAskUserQuestionReturn {\n const [answers, setAnswers] = useState<Record<string, string | string[]>>({});\n\n // Parse questions from part.input\n const questions = useMemo((): Question[] => {\n if (!part.input || typeof part.input !== \"object\") return [];\n const input = part.input as AskUserQuestionInput;\n if (!input.questions || !Array.isArray(input.questions)) return [];\n return input.questions;\n }, [part.input]);\n\n // Parse answers from part.output (for restored sessions)\n const outputAnswers = useMemo((): Record<\n string,\n string | string[]\n > | null => {\n if (part.output && typeof part.output === \"object\") {\n const output = part.output as AskUserQuestionOutput;\n if (output.answers) {\n const hasRealAnswers = Object.values(output.answers).some(\n (v) => v && v.trim() !== \"\",\n );\n if (hasRealAnswers) {\n const parsed: Record<string, string | string[]> = {};\n for (const q of questions) {\n const val = output.answers[q.question];\n if (q.multiSelect && val) {\n parsed[q.question] = val.split(\", \").filter(Boolean);\n } else {\n parsed[q.question] = val || \"\";\n }\n }\n return parsed;\n }\n }\n }\n return null;\n }, [part.output, questions]);\n\n // Determine display answers (user input or restored from output)\n const displayAnswers = useMemo(() => {\n return Object.keys(answers).length > 0 ? answers : outputAnswers || {};\n }, [answers, outputAnswers]);\n\n // Check if completed\n const isCompleted = useMemo(() => {\n return (\n part.state === \"output-available\" ||\n (Object.keys(answers).length === 0 && outputAnswers !== null)\n );\n }, [part.state, answers, outputAnswers]);\n\n // Check if waiting for input (for animation)\n const isWaitingForInput = part.state === \"input-available\";\n\n // Get formatted answers map\n const getAnswersMap = useCallback((): Record<string, string> => {\n const answersMap: Record<string, string> = {};\n for (const q of questions) {\n const answer = displayAnswers[q.question];\n if (q.multiSelect) {\n answersMap[q.question] = Array.isArray(answer) ? answer.join(\", \") : \"\";\n } else {\n answersMap[q.question] = (answer as string) || \"\";\n }\n }\n return answersMap;\n }, [questions, displayAnswers]);\n\n // Select answer handler\n const selectAnswer = useCallback(\n (question: string, value: string, multiSelect = false) => {\n const newAnswers = { ...answers };\n\n if (multiSelect) {\n const current = (newAnswers[question] as string[]) || [];\n newAnswers[question] = current.includes(value)\n ? current.filter((v) => v !== value)\n : [...current, value];\n } else {\n newAnswers[question] = value;\n }\n\n setAnswers(newAnswers);\n\n // Prepare answers map for callback and API\n const answersMap: Record<string, string> = {};\n for (const q of questions) {\n const answer = newAnswers[q.question];\n if (q.multiSelect) {\n answersMap[q.question] = Array.isArray(answer)\n ? answer.join(\", \")\n : \"\";\n } else {\n answersMap[q.question] = (answer as string) || \"\";\n }\n }\n\n // Auto-submit to answer endpoint\n fetch(answerEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n toolCallId: part.toolCallId,\n questions,\n answers: answersMap,\n ...extraBody,\n }),\n }).catch((err) => {\n console.error(\"[useAskUserQuestion] Submit failed:\", err);\n });\n\n // Trigger callback\n onAnswer?.({\n toolCallId: part.toolCallId,\n questions,\n answers: answersMap,\n });\n },\n [answers, questions, part.toolCallId, answerEndpoint, extraBody, onAnswer],\n );\n\n // Check if option is selected\n const isSelected = useCallback(\n (question: string, optionLabel: string, multiSelect = false): boolean => {\n const selectedValue = displayAnswers[question];\n if (multiSelect) {\n return (\n (selectedValue as string[] | undefined)?.includes(optionLabel) ??\n false\n );\n }\n return selectedValue === optionLabel;\n },\n [displayAnswers],\n );\n\n return {\n questions,\n answers: displayAnswers,\n isCompleted,\n isWaitingForInput,\n selectAnswer,\n getAnswersMap,\n isSelected,\n };\n}\n","\"use client\";\n\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport, type UIMessage } from \"ai\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type {\n ArtifactData,\n UseSandAgentChatOptions,\n UseSandAgentChatReturn,\n} from \"./types\";\n\n/**\n * useSandAgentChat - Core hook for SandAgent chat functionality\n *\n * Provides all the logic needed for a chat interface:\n * - Message management\n * - Artifact extraction\n * - Session management\n *\n * @example\n * ```tsx\n * import { useSandAgentChat } from \"@sandagent/sdk/react\";\n *\n * const {\n * messages,\n * sendMessage,\n * status,\n * artifacts,\n * selectedArtifact,\n * setSelectedArtifact,\n * } = useSandAgentChat({\n * apiEndpoint: \"/api/ai\",\n * body: { template: \"default\" },\n * });\n * ```\n */\nexport function useSandAgentChat({\n apiEndpoint = \"/api/ai\",\n body = {},\n}: UseSandAgentChatOptions = {}): UseSandAgentChatReturn {\n // Artifact selection state\n const [selectedArtifact, setSelectedArtifact] = useState<ArtifactData | null>(\n null,\n );\n\n // Refs for accessing latest values in callbacks\n const bodyRef = useRef(body);\n const messagesRef = useRef<UIMessage[]>([]);\n\n useEffect(() => {\n bodyRef.current = body;\n }, [body]);\n\n // Helper to extract resume value (sessionId) from message parts' providerMetadata.\n const getResumeFromMessage = (\n message: UIMessage | undefined,\n ): string | undefined => {\n if (!message?.parts) return undefined;\n for (const part of message.parts) {\n if (part.type === \"text\") {\n const providerMetadata = (\n part as { providerMetadata?: { sandagent?: { sessionId?: string } } }\n ).providerMetadata;\n if (providerMetadata?.sandagent?.sessionId) {\n return providerMetadata.sandagent.sessionId;\n }\n }\n }\n return undefined;\n };\n\n // Core chat hook\n const {\n messages,\n sendMessage: sendMessageInternal,\n status,\n error,\n stop,\n } = useChat({\n transport: new DefaultChatTransport({\n api: apiEndpoint,\n body: () => {\n const lastMessage = messagesRef.current[messagesRef.current.length - 1];\n const resume = getResumeFromMessage(lastMessage);\n return {\n resume,\n ...bodyRef.current,\n };\n },\n }),\n });\n\n // Keep messagesRef in sync\n useEffect(() => {\n messagesRef.current = messages;\n }, [messages]);\n\n // Extract artifacts from messages\n const prevArtifactsRef = useRef<ArtifactData[]>([]);\n const artifacts = useMemo(() => {\n const results: ArtifactData[] = [];\n for (const message of messages) {\n for (const part of message.parts) {\n if (part.type === \"data-artifact\") {\n const data = part.data as ArtifactData;\n if (!results.some((a) => a.artifactId === data.artifactId)) {\n results.push(data);\n }\n }\n }\n }\n\n // Memoization optimization\n const prev = prevArtifactsRef.current;\n if (\n prev.length === results.length &&\n prev.every((prevArt, idx) => {\n const currArt = results[idx];\n return (\n prevArt.artifactId === currArt.artifactId &&\n prevArt.content === currArt.content &&\n prevArt.mimeType === currArt.mimeType\n );\n })\n ) {\n return prev;\n }\n\n prevArtifactsRef.current = results;\n return results;\n }, [messages]);\n\n // Sync selectedArtifact when artifacts change\n useEffect(() => {\n if (artifacts.length > 0) {\n setSelectedArtifact((prev) => {\n if (!prev) return artifacts[0];\n\n const currentMatch = artifacts.find(\n (a) => a.artifactId === prev.artifactId,\n );\n\n if (!currentMatch) {\n return artifacts[0];\n }\n\n if (\n currentMatch.content === prev.content &&\n currentMatch.mimeType === prev.mimeType\n ) {\n return prev;\n }\n\n return currentMatch;\n });\n } else {\n setSelectedArtifact((prev) => (prev === null ? prev : null));\n }\n }, [artifacts]);\n\n const isLoading = status === \"streaming\" || status === \"submitted\";\n const hasError = status === \"error\" && !!error;\n\n // Send message helper\n const sendMessage = useCallback(\n (text: string) => {\n if (!isLoading && text.trim()) {\n sendMessageInternal({\n role: \"user\",\n parts: [{ type: \"text\", text: text.trim() }],\n });\n }\n },\n [isLoading, sendMessageInternal],\n );\n\n // Handle submit for PromptInput compatibility\n const handleSubmit = useCallback(\n (message: { text: string }) => {\n if (!isLoading) {\n if (message.text) {\n sendMessageInternal({\n role: \"user\",\n parts: [{ type: \"text\", text: message.text.trim() }],\n });\n } else {\n sendMessageInternal();\n }\n }\n },\n [isLoading, sendMessageInternal],\n );\n\n return {\n messages,\n status,\n error,\n isLoading,\n hasError,\n artifacts,\n selectedArtifact,\n setSelectedArtifact,\n sendMessage,\n stop,\n handleSubmit,\n };\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport type { DynamicToolUIPart } from \"./types\";\n\n/**\n * Write tool input structure\n */\nexport interface WriteToolInput {\n file_path: string;\n content: string;\n}\n\n/**\n * Write tool output structure\n */\nexport interface WriteToolOutput {\n type: \"create\" | \"edit\";\n filePath: string;\n content: string;\n structuredPatch?: unknown[];\n originalFile?: string | null;\n}\n\n/**\n * Options for useWriteTool hook\n */\nexport interface UseWriteToolOptions {\n /** The dynamic tool UI part from the message */\n part: DynamicToolUIPart;\n}\n\n/**\n * Return type for useWriteTool hook\n */\nexport interface UseWriteToolReturn {\n /** File path being written to */\n filePath: string | null;\n /** File name (extracted from path) */\n fileName: string | null;\n /** File content */\n content: string | null;\n /** Operation type: 'create' or 'edit' */\n operationType: \"create\" | \"edit\" | null;\n /** Original file content (for edit operations) */\n originalContent: string | null;\n /** Structured patch data (for edit operations) */\n structuredPatch: unknown[] | null;\n /** Tool state */\n state: \"streaming\" | \"input-available\" | \"output-available\" | \"error\";\n /** Whether the tool is currently streaming input */\n isStreaming: boolean;\n /** Whether the write operation completed successfully */\n isCompleted: boolean;\n /** Whether there was an error */\n hasError: boolean;\n /** Error message if any */\n errorText: string | null;\n /** Whether this is a markdown file */\n isMarkdown: boolean;\n /** File extension */\n fileExtension: string | null;\n}\n\n/**\n * useWriteTool - Hook for handling Write tool interactions\n *\n * Parses the Write tool's input and output data, providing\n * easy access to file information and operation state.\n *\n * @example\n * ```tsx\n * import { useWriteTool } from \"@sandagent/sdk/react\";\n *\n * function WriteToolUI({ part }) {\n * const {\n * filePath,\n * fileName,\n * content,\n * operationType,\n * isStreaming,\n * isCompleted,\n * isMarkdown,\n * } = useWriteTool({ part });\n *\n * if (isStreaming) {\n * return <div>Writing {fileName}...</div>;\n * }\n *\n * return (\n * <div>\n * <h3>{fileName} ({operationType})</h3>\n * {isMarkdown ? (\n * <MarkdownRenderer content={content} />\n * ) : (\n * <pre>{content}</pre>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useWriteTool({\n part,\n}: UseWriteToolOptions): UseWriteToolReturn {\n // Parse input data\n const inputData = useMemo((): WriteToolInput | null => {\n if (!part.input || typeof part.input !== \"object\") return null;\n const input = part.input as WriteToolInput;\n if (!input.file_path) return null;\n return input;\n }, [part.input]);\n\n // Parse output data\n const outputData = useMemo((): WriteToolOutput | null => {\n if (!part.output || typeof part.output !== \"object\") return null;\n const output = part.output as WriteToolOutput;\n if (!output.filePath) return null;\n return output;\n }, [part.output]);\n\n // Determine file path (prefer output, fallback to input)\n const filePath = outputData?.filePath ?? inputData?.file_path ?? null;\n\n // Extract file name\n const fileName = useMemo(() => {\n if (!filePath) return null;\n return filePath.split(\"/\").pop() || filePath;\n }, [filePath]);\n\n // Extract file extension\n const fileExtension = useMemo(() => {\n if (!fileName) return null;\n const parts = fileName.split(\".\");\n return parts.length > 1 ? parts.pop()?.toLowerCase() || null : null;\n }, [fileName]);\n\n // Determine content (prefer output, fallback to input)\n const content = outputData?.content ?? inputData?.content ?? null;\n\n // Operation type\n const operationType = outputData?.type ?? null;\n\n // Original content (for edits)\n const originalContent = outputData?.originalFile ?? null;\n\n // Structured patch\n const structuredPatch = outputData?.structuredPatch ?? null;\n\n // State parsing\n const state = useMemo((): UseWriteToolReturn[\"state\"] => {\n if (part.state === \"input-streaming\") return \"streaming\";\n if (part.state === \"output-error\") return \"error\";\n if (part.state === \"output-available\") return \"output-available\";\n return \"input-available\";\n }, [part.state]);\n\n const isStreaming = state === \"streaming\";\n const isCompleted = state === \"output-available\";\n const hasError = state === \"error\";\n const errorText = part.errorText ?? null;\n\n // Check if markdown\n const isMarkdown = useMemo(() => {\n if (!fileExtension) return false;\n return [\"md\", \"markdown\", \"mdx\"].includes(fileExtension);\n }, [fileExtension]);\n\n return {\n filePath,\n fileName,\n content,\n operationType,\n originalContent,\n structuredPatch,\n state,\n isStreaming,\n isCompleted,\n hasError,\n errorText,\n isMarkdown,\n fileExtension,\n };\n}\n"],"mappings":"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","names":["useCallback","useMemo","useState","useCallback","useEffect","useMemo","useRef","useState","useMemo"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sandagent/sdk",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "SandAgent SDK - AI Provider and React hooks for building AI agents",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -52,7 +52,7 @@
52
52
  "dependencies": {
53
53
  "@ai-sdk/provider": "^3.0.8",
54
54
  "@ai-sdk/react": "^3.0.105",
55
- "@sandagent/manager": "0.8.3"
55
+ "@sandagent/manager": "0.8.4"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/node": "^20.10.0",