@sandagent/sdk 0.2.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/index.d.ts +158 -0
- package/dist/index.js +631 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.d.ts +437 -0
- package/dist/react/index.js +421 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
// src/react/useSandAgentChat.ts
|
|
5
|
+
import { useChat } from "@ai-sdk/react";
|
|
6
|
+
import { DefaultChatTransport } from "ai";
|
|
7
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
8
|
+
function useSandAgentChat({
|
|
9
|
+
apiEndpoint = "/api/ai",
|
|
10
|
+
body = {},
|
|
11
|
+
sessionId: providedSessionId
|
|
12
|
+
} = {}) {
|
|
13
|
+
const [sessionId] = useState(
|
|
14
|
+
() => providedSessionId || `session-${Date.now()}`
|
|
15
|
+
);
|
|
16
|
+
const [selectedArtifact, setSelectedArtifact] = useState(
|
|
17
|
+
null
|
|
18
|
+
);
|
|
19
|
+
const bodyRef = useRef(body);
|
|
20
|
+
const messagesRef = useRef([]);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
bodyRef.current = body;
|
|
23
|
+
}, [body]);
|
|
24
|
+
const {
|
|
25
|
+
messages,
|
|
26
|
+
sendMessage: sendMessageInternal,
|
|
27
|
+
status,
|
|
28
|
+
error,
|
|
29
|
+
stop
|
|
30
|
+
} = useChat({
|
|
31
|
+
transport: new DefaultChatTransport({
|
|
32
|
+
api: apiEndpoint,
|
|
33
|
+
body: () => {
|
|
34
|
+
const lastMessage = messagesRef.current[messagesRef.current.length - 1];
|
|
35
|
+
const metadata = lastMessage?.metadata;
|
|
36
|
+
return {
|
|
37
|
+
sessionId,
|
|
38
|
+
resume: metadata?.sessionId,
|
|
39
|
+
...bodyRef.current
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
messagesRef.current = messages;
|
|
46
|
+
}, [messages]);
|
|
47
|
+
const prevArtifactsRef = useRef([]);
|
|
48
|
+
const artifacts = useMemo(() => {
|
|
49
|
+
const results = [];
|
|
50
|
+
for (const message of messages) {
|
|
51
|
+
for (const part of message.parts) {
|
|
52
|
+
if (part.type === "data-artifact") {
|
|
53
|
+
const data = part.data;
|
|
54
|
+
if (!results.some((a) => a.artifactId === data.artifactId)) {
|
|
55
|
+
results.push(data);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const prev = prevArtifactsRef.current;
|
|
61
|
+
if (prev.length === results.length && prev.every((prevArt, idx) => {
|
|
62
|
+
const currArt = results[idx];
|
|
63
|
+
return prevArt.artifactId === currArt.artifactId && prevArt.content === currArt.content && prevArt.mimeType === currArt.mimeType;
|
|
64
|
+
})) {
|
|
65
|
+
return prev;
|
|
66
|
+
}
|
|
67
|
+
prevArtifactsRef.current = results;
|
|
68
|
+
return results;
|
|
69
|
+
}, [messages]);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (artifacts.length > 0) {
|
|
72
|
+
setSelectedArtifact((prev) => {
|
|
73
|
+
if (!prev) return artifacts[0];
|
|
74
|
+
const currentMatch = artifacts.find(
|
|
75
|
+
(a) => a.artifactId === prev.artifactId
|
|
76
|
+
);
|
|
77
|
+
if (!currentMatch) {
|
|
78
|
+
return artifacts[0];
|
|
79
|
+
}
|
|
80
|
+
if (currentMatch.content === prev.content && currentMatch.mimeType === prev.mimeType) {
|
|
81
|
+
return prev;
|
|
82
|
+
}
|
|
83
|
+
return currentMatch;
|
|
84
|
+
});
|
|
85
|
+
} else {
|
|
86
|
+
setSelectedArtifact((prev) => prev === null ? prev : null);
|
|
87
|
+
}
|
|
88
|
+
}, [artifacts]);
|
|
89
|
+
const isLoading = status === "streaming" || status === "submitted";
|
|
90
|
+
const hasError = status === "error" && !!error;
|
|
91
|
+
const sendMessage = useCallback(
|
|
92
|
+
(text) => {
|
|
93
|
+
if (!isLoading && text.trim()) {
|
|
94
|
+
sendMessageInternal({
|
|
95
|
+
role: "user",
|
|
96
|
+
parts: [{ type: "text", text: text.trim() }]
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
[isLoading, sendMessageInternal]
|
|
101
|
+
);
|
|
102
|
+
const handleSubmit = useCallback(
|
|
103
|
+
(message) => {
|
|
104
|
+
if (!isLoading) {
|
|
105
|
+
if (message.text) {
|
|
106
|
+
sendMessageInternal({
|
|
107
|
+
role: "user",
|
|
108
|
+
parts: [{ type: "text", text: message.text.trim() }]
|
|
109
|
+
});
|
|
110
|
+
} else {
|
|
111
|
+
sendMessageInternal();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
[isLoading, sendMessageInternal]
|
|
116
|
+
);
|
|
117
|
+
return {
|
|
118
|
+
sessionId,
|
|
119
|
+
messages,
|
|
120
|
+
status,
|
|
121
|
+
error,
|
|
122
|
+
isLoading,
|
|
123
|
+
hasError,
|
|
124
|
+
artifacts,
|
|
125
|
+
selectedArtifact,
|
|
126
|
+
setSelectedArtifact,
|
|
127
|
+
sendMessage,
|
|
128
|
+
stop,
|
|
129
|
+
handleSubmit
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/react/useAskUserQuestion.ts
|
|
134
|
+
import { useCallback as useCallback2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
135
|
+
function useAskUserQuestion({
|
|
136
|
+
part,
|
|
137
|
+
onAnswer
|
|
138
|
+
}) {
|
|
139
|
+
const [answers, setAnswers] = useState2({});
|
|
140
|
+
const questions = useMemo2(() => {
|
|
141
|
+
if (!part.input || typeof part.input !== "object") return [];
|
|
142
|
+
const input = part.input;
|
|
143
|
+
if (!input.questions || !Array.isArray(input.questions)) return [];
|
|
144
|
+
return input.questions;
|
|
145
|
+
}, [part.input]);
|
|
146
|
+
const outputAnswers = useMemo2(() => {
|
|
147
|
+
if (part.output && typeof part.output === "object") {
|
|
148
|
+
const output = part.output;
|
|
149
|
+
if (output.answers) {
|
|
150
|
+
const hasRealAnswers = Object.values(output.answers).some(
|
|
151
|
+
(v) => v && v.trim() !== ""
|
|
152
|
+
);
|
|
153
|
+
if (hasRealAnswers) {
|
|
154
|
+
const parsed = {};
|
|
155
|
+
for (const q of questions) {
|
|
156
|
+
const val = output.answers[q.question];
|
|
157
|
+
if (q.multiSelect && val) {
|
|
158
|
+
parsed[q.question] = val.split(", ").filter(Boolean);
|
|
159
|
+
} else {
|
|
160
|
+
parsed[q.question] = val || "";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return parsed;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}, [part.output, questions]);
|
|
169
|
+
const displayAnswers = useMemo2(() => {
|
|
170
|
+
return Object.keys(answers).length > 0 ? answers : outputAnswers || {};
|
|
171
|
+
}, [answers, outputAnswers]);
|
|
172
|
+
const isCompleted = useMemo2(() => {
|
|
173
|
+
return part.state === "output-available" || Object.keys(answers).length === 0 && outputAnswers !== null;
|
|
174
|
+
}, [part.state, answers, outputAnswers]);
|
|
175
|
+
const isWaitingForInput = part.state === "input-available";
|
|
176
|
+
const getAnswersMap = useCallback2(() => {
|
|
177
|
+
const answersMap = {};
|
|
178
|
+
for (const q of questions) {
|
|
179
|
+
const answer = displayAnswers[q.question];
|
|
180
|
+
if (q.multiSelect) {
|
|
181
|
+
answersMap[q.question] = Array.isArray(answer) ? answer.join(", ") : "";
|
|
182
|
+
} else {
|
|
183
|
+
answersMap[q.question] = answer || "";
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return answersMap;
|
|
187
|
+
}, [questions, displayAnswers]);
|
|
188
|
+
const selectAnswer = useCallback2(
|
|
189
|
+
(question, value, multiSelect = false) => {
|
|
190
|
+
const newAnswers = { ...answers };
|
|
191
|
+
if (multiSelect) {
|
|
192
|
+
const current = newAnswers[question] || [];
|
|
193
|
+
newAnswers[question] = current.includes(value) ? current.filter((v) => v !== value) : [...current, value];
|
|
194
|
+
} else {
|
|
195
|
+
newAnswers[question] = value;
|
|
196
|
+
}
|
|
197
|
+
setAnswers(newAnswers);
|
|
198
|
+
const answersMap = {};
|
|
199
|
+
for (const q of questions) {
|
|
200
|
+
const answer = newAnswers[q.question];
|
|
201
|
+
if (q.multiSelect) {
|
|
202
|
+
answersMap[q.question] = Array.isArray(answer) ? answer.join(", ") : "";
|
|
203
|
+
} else {
|
|
204
|
+
answersMap[q.question] = answer || "";
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
onAnswer?.({
|
|
208
|
+
toolCallId: part.toolCallId,
|
|
209
|
+
questions,
|
|
210
|
+
answers: answersMap
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
[answers, questions, part.toolCallId, onAnswer]
|
|
214
|
+
);
|
|
215
|
+
const isSelected = useCallback2(
|
|
216
|
+
(question, optionLabel, multiSelect = false) => {
|
|
217
|
+
const selectedValue = displayAnswers[question];
|
|
218
|
+
if (multiSelect) {
|
|
219
|
+
return selectedValue?.includes(optionLabel) ?? false;
|
|
220
|
+
}
|
|
221
|
+
return selectedValue === optionLabel;
|
|
222
|
+
},
|
|
223
|
+
[displayAnswers]
|
|
224
|
+
);
|
|
225
|
+
return {
|
|
226
|
+
questions,
|
|
227
|
+
answers: displayAnswers,
|
|
228
|
+
isCompleted,
|
|
229
|
+
isWaitingForInput,
|
|
230
|
+
selectAnswer,
|
|
231
|
+
getAnswersMap,
|
|
232
|
+
isSelected
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/react/useWriteTool.ts
|
|
237
|
+
import { useMemo as useMemo3 } from "react";
|
|
238
|
+
function useWriteTool({
|
|
239
|
+
part
|
|
240
|
+
}) {
|
|
241
|
+
const inputData = useMemo3(() => {
|
|
242
|
+
if (!part.input || typeof part.input !== "object") return null;
|
|
243
|
+
const input = part.input;
|
|
244
|
+
if (!input.file_path) return null;
|
|
245
|
+
return input;
|
|
246
|
+
}, [part.input]);
|
|
247
|
+
const outputData = useMemo3(() => {
|
|
248
|
+
if (!part.output || typeof part.output !== "object") return null;
|
|
249
|
+
const output = part.output;
|
|
250
|
+
if (!output.filePath) return null;
|
|
251
|
+
return output;
|
|
252
|
+
}, [part.output]);
|
|
253
|
+
const filePath = outputData?.filePath ?? inputData?.file_path ?? null;
|
|
254
|
+
const fileName = useMemo3(() => {
|
|
255
|
+
if (!filePath) return null;
|
|
256
|
+
return filePath.split("/").pop() || filePath;
|
|
257
|
+
}, [filePath]);
|
|
258
|
+
const fileExtension = useMemo3(() => {
|
|
259
|
+
if (!fileName) return null;
|
|
260
|
+
const parts = fileName.split(".");
|
|
261
|
+
return parts.length > 1 ? parts.pop()?.toLowerCase() || null : null;
|
|
262
|
+
}, [fileName]);
|
|
263
|
+
const content = outputData?.content ?? inputData?.content ?? null;
|
|
264
|
+
const operationType = outputData?.type ?? null;
|
|
265
|
+
const originalContent = outputData?.originalFile ?? null;
|
|
266
|
+
const structuredPatch = outputData?.structuredPatch ?? null;
|
|
267
|
+
const state = useMemo3(() => {
|
|
268
|
+
if (part.state === "input-streaming") return "streaming";
|
|
269
|
+
if (part.state === "output-error") return "error";
|
|
270
|
+
if (part.state === "output-available") return "output-available";
|
|
271
|
+
return "input-available";
|
|
272
|
+
}, [part.state]);
|
|
273
|
+
const isStreaming = state === "streaming";
|
|
274
|
+
const isCompleted = state === "output-available";
|
|
275
|
+
const hasError = state === "error";
|
|
276
|
+
const errorText = part.errorText ?? null;
|
|
277
|
+
const isMarkdown = useMemo3(() => {
|
|
278
|
+
if (!fileExtension) return false;
|
|
279
|
+
return ["md", "markdown", "mdx"].includes(fileExtension);
|
|
280
|
+
}, [fileExtension]);
|
|
281
|
+
return {
|
|
282
|
+
filePath,
|
|
283
|
+
fileName,
|
|
284
|
+
content,
|
|
285
|
+
operationType,
|
|
286
|
+
originalContent,
|
|
287
|
+
structuredPatch,
|
|
288
|
+
state,
|
|
289
|
+
isStreaming,
|
|
290
|
+
isCompleted,
|
|
291
|
+
hasError,
|
|
292
|
+
errorText,
|
|
293
|
+
isMarkdown,
|
|
294
|
+
fileExtension
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/react/useArtifacts.ts
|
|
299
|
+
import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo4, useRef as useRef2, useState as useState3 } from "react";
|
|
300
|
+
function getFileExtensionFromMimeType(mimeType) {
|
|
301
|
+
const mimeToExtension = {
|
|
302
|
+
"text/plain": "txt",
|
|
303
|
+
"text/html": "html",
|
|
304
|
+
"text/css": "css",
|
|
305
|
+
"text/javascript": "js",
|
|
306
|
+
"text/markdown": "md",
|
|
307
|
+
"application/json": "json",
|
|
308
|
+
"application/xml": "xml",
|
|
309
|
+
"application/pdf": "pdf",
|
|
310
|
+
"image/png": "png",
|
|
311
|
+
"image/jpeg": "jpg",
|
|
312
|
+
"image/gif": "gif",
|
|
313
|
+
"image/svg+xml": "svg"
|
|
314
|
+
};
|
|
315
|
+
if (mimeToExtension[mimeType]) {
|
|
316
|
+
return mimeToExtension[mimeType];
|
|
317
|
+
}
|
|
318
|
+
if (mimeType.includes("markdown")) return "md";
|
|
319
|
+
if (mimeType.includes("json")) return "json";
|
|
320
|
+
if (mimeType.includes("javascript")) return "js";
|
|
321
|
+
if (mimeType.includes("typescript")) return "ts";
|
|
322
|
+
if (mimeType.includes("html")) return "html";
|
|
323
|
+
if (mimeType.includes("css")) return "css";
|
|
324
|
+
if (mimeType.includes("xml")) return "xml";
|
|
325
|
+
if (mimeType.includes("python")) return "py";
|
|
326
|
+
return "txt";
|
|
327
|
+
}
|
|
328
|
+
function useArtifacts({
|
|
329
|
+
messages
|
|
330
|
+
}) {
|
|
331
|
+
const [selectedArtifact, setSelectedArtifact] = useState3(
|
|
332
|
+
null
|
|
333
|
+
);
|
|
334
|
+
const prevArtifactsRef = useRef2([]);
|
|
335
|
+
const artifacts = useMemo4(() => {
|
|
336
|
+
const results = [];
|
|
337
|
+
for (const message of messages) {
|
|
338
|
+
for (const part of message.parts) {
|
|
339
|
+
if (part.type === "data-artifact") {
|
|
340
|
+
const data = part.data;
|
|
341
|
+
if (!results.some((a) => a.artifactId === data.artifactId)) {
|
|
342
|
+
results.push(data);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const prev = prevArtifactsRef.current;
|
|
348
|
+
if (prev.length === results.length && prev.every((prevArt, idx) => {
|
|
349
|
+
const currArt = results[idx];
|
|
350
|
+
return prevArt.artifactId === currArt.artifactId && prevArt.content === currArt.content && prevArt.mimeType === currArt.mimeType;
|
|
351
|
+
})) {
|
|
352
|
+
return prev;
|
|
353
|
+
}
|
|
354
|
+
prevArtifactsRef.current = results;
|
|
355
|
+
return results;
|
|
356
|
+
}, [messages]);
|
|
357
|
+
useEffect2(() => {
|
|
358
|
+
if (artifacts.length > 0) {
|
|
359
|
+
setSelectedArtifact((prev) => {
|
|
360
|
+
if (!prev) return artifacts[0];
|
|
361
|
+
const currentMatch = artifacts.find(
|
|
362
|
+
(a) => a.artifactId === prev.artifactId
|
|
363
|
+
);
|
|
364
|
+
if (!currentMatch) {
|
|
365
|
+
return artifacts[0];
|
|
366
|
+
}
|
|
367
|
+
if (currentMatch.content === prev.content && currentMatch.mimeType === prev.mimeType) {
|
|
368
|
+
return prev;
|
|
369
|
+
}
|
|
370
|
+
return currentMatch;
|
|
371
|
+
});
|
|
372
|
+
} else {
|
|
373
|
+
setSelectedArtifact((prev) => prev === null ? prev : null);
|
|
374
|
+
}
|
|
375
|
+
}, [artifacts]);
|
|
376
|
+
const selectArtifactById = useCallback3(
|
|
377
|
+
(artifactId) => {
|
|
378
|
+
const artifact = artifacts.find((a) => a.artifactId === artifactId);
|
|
379
|
+
if (artifact) {
|
|
380
|
+
setSelectedArtifact(artifact);
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
[artifacts]
|
|
384
|
+
);
|
|
385
|
+
const copyContent = useCallback3(async (artifact) => {
|
|
386
|
+
await navigator.clipboard.writeText(artifact.content);
|
|
387
|
+
}, []);
|
|
388
|
+
const downloadArtifact = useCallback3((artifact) => {
|
|
389
|
+
const blob = new Blob([artifact.content], { type: artifact.mimeType });
|
|
390
|
+
const url = URL.createObjectURL(blob);
|
|
391
|
+
const a = document.createElement("a");
|
|
392
|
+
a.href = url;
|
|
393
|
+
const fileName = artifact.artifactId.split("/").pop() || artifact.artifactId;
|
|
394
|
+
const extension = getFileExtensionFromMimeType(artifact.mimeType);
|
|
395
|
+
a.download = fileName.includes(".") ? fileName : `${fileName}.${extension}`;
|
|
396
|
+
document.body.appendChild(a);
|
|
397
|
+
a.click();
|
|
398
|
+
document.body.removeChild(a);
|
|
399
|
+
URL.revokeObjectURL(url);
|
|
400
|
+
}, []);
|
|
401
|
+
const hasArtifacts = artifacts.length > 0;
|
|
402
|
+
const count = artifacts.length;
|
|
403
|
+
return {
|
|
404
|
+
artifacts,
|
|
405
|
+
selectedArtifact,
|
|
406
|
+
setSelectedArtifact,
|
|
407
|
+
selectArtifactById,
|
|
408
|
+
hasArtifacts,
|
|
409
|
+
count,
|
|
410
|
+
copyContent,
|
|
411
|
+
downloadArtifact,
|
|
412
|
+
getFileExtension: getFileExtensionFromMimeType
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
export {
|
|
416
|
+
useArtifacts,
|
|
417
|
+
useAskUserQuestion,
|
|
418
|
+
useSandAgentChat,
|
|
419
|
+
useWriteTool
|
|
420
|
+
};
|
|
421
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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 sessionId: providedSessionId,\n}: UseSandAgentChatOptions = {}): UseSandAgentChatReturn {\n // Session ID management\n const [sessionId] = useState(\n () => providedSessionId || `session-${Date.now()}`,\n );\n\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 // 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 metadata = lastMessage?.metadata as\n | { sessionId?: string }\n | undefined;\n return {\n sessionId,\n resume: metadata?.sessionId,\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 sessionId,\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/approval/submit\", {\n * method: \"POST\",\n * body: JSON.stringify({ sessionId, ...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 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<string, string | string[]> | 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\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) ? answer.join(\", \") : \"\";\n } else {\n answersMap[q.question] = (answer as string) || \"\";\n }\n }\n\n // Trigger callback\n onAnswer?.({\n toolCallId: part.toolCallId,\n questions,\n answers: answersMap,\n });\n },\n [answers, questions, part.toolCallId, 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 (selectedValue as string[] | undefined)?.includes(optionLabel) ?? false;\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 = 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"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sandagent/sdk",
|
|
3
|
+
"version": "0.2.0-beta.5",
|
|
4
|
+
"description": "SandAgent SDK - AI Provider and React hooks for building AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./react": {
|
|
16
|
+
"types": "./dist/react/index.d.ts",
|
|
17
|
+
"import": "./dist/react/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"sandagent",
|
|
26
|
+
"sdk",
|
|
27
|
+
"ai-provider",
|
|
28
|
+
"react",
|
|
29
|
+
"ai",
|
|
30
|
+
"chat",
|
|
31
|
+
"hooks",
|
|
32
|
+
"claude",
|
|
33
|
+
"anthropic"
|
|
34
|
+
],
|
|
35
|
+
"license": "Apache-2.0",
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"ai": ">=4.0.0",
|
|
38
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
39
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependenciesMeta": {
|
|
42
|
+
"react": {
|
|
43
|
+
"optional": true
|
|
44
|
+
},
|
|
45
|
+
"react-dom": {
|
|
46
|
+
"optional": true
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@ai-sdk/provider": "^3.0.0",
|
|
51
|
+
"@ai-sdk/provider-utils": "^2.2.8",
|
|
52
|
+
"@ai-sdk/react": "^3.0.3",
|
|
53
|
+
"@sandagent/manager": "0.2.0-beta.5"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^20.10.0",
|
|
57
|
+
"@types/react": "^19.2.0",
|
|
58
|
+
"@types/react-dom": "^19.2.0",
|
|
59
|
+
"tsup": "^8.5.0",
|
|
60
|
+
"typescript": "^5.9.3"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsup",
|
|
64
|
+
"typecheck": "tsc --noEmit",
|
|
65
|
+
"lint": "biome format . --write && biome check . --write"
|
|
66
|
+
}
|
|
67
|
+
}
|