@nextclaw/ncp-react 0.3.2 → 0.3.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.
package/dist/index.d.ts CHANGED
@@ -32,4 +32,39 @@ declare function useHydratedNcpAgent({ sessionId, client, loadSeed, autoResumeRu
32
32
 
33
33
  declare function useNcpAgent(sessionId: string, client: NcpAgentClientEndpoint): UseNcpAgentResult;
34
34
 
35
- export { type NcpConversationSeed, type NcpConversationSeedLoader, type UseHydratedNcpAgentOptions, type UseHydratedNcpAgentResult, type UseNcpAgentResult, useHydratedNcpAgent, useNcpAgent };
35
+ declare const DEFAULT_NCP_IMAGE_ATTACHMENT_ACCEPT = "image/png,image/jpeg,image/webp,image/gif";
36
+ declare const DEFAULT_NCP_IMAGE_ATTACHMENT_MIME_TYPES: readonly ["image/png", "image/jpeg", "image/webp", "image/gif"];
37
+ declare const DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES: number;
38
+ type NcpDraftAttachment = {
39
+ id: string;
40
+ name: string;
41
+ mimeType: string;
42
+ contentBase64: string;
43
+ sizeBytes: number;
44
+ };
45
+ type NcpRejectedAttachment = {
46
+ fileName: string;
47
+ mimeType: string;
48
+ sizeBytes: number;
49
+ reason: "unsupported-type" | "too-large" | "read-failed";
50
+ };
51
+ type ReadNcpDraftAttachmentsOptions = {
52
+ acceptedMimeTypes?: readonly string[];
53
+ maxBytes?: number;
54
+ };
55
+ type ReadNcpDraftAttachmentsResult = {
56
+ attachments: NcpDraftAttachment[];
57
+ rejected: NcpRejectedAttachment[];
58
+ };
59
+ declare function buildNcpImageAttachmentDataUrl(attachment: NcpDraftAttachment): string;
60
+ declare function buildNcpRequestEnvelope(params: {
61
+ sessionId: string;
62
+ text?: string;
63
+ attachments?: readonly NcpDraftAttachment[];
64
+ metadata?: Record<string, unknown>;
65
+ messageId?: string;
66
+ timestamp?: string;
67
+ }): NcpRequestEnvelope | null;
68
+ declare function readFilesAsNcpDraftAttachments(files: Iterable<File>, options?: ReadNcpDraftAttachmentsOptions): Promise<ReadNcpDraftAttachmentsResult>;
69
+
70
+ export { DEFAULT_NCP_IMAGE_ATTACHMENT_ACCEPT, DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES, DEFAULT_NCP_IMAGE_ATTACHMENT_MIME_TYPES, type NcpConversationSeed, type NcpConversationSeedLoader, type NcpDraftAttachment, type NcpRejectedAttachment, type ReadNcpDraftAttachmentsOptions, type ReadNcpDraftAttachmentsResult, type UseHydratedNcpAgentOptions, type UseHydratedNcpAgentResult, type UseNcpAgentResult, buildNcpImageAttachmentDataUrl, buildNcpRequestEnvelope, readFilesAsNcpDraftAttachments, useHydratedNcpAgent, useNcpAgent };
package/dist/index.js CHANGED
@@ -55,13 +55,6 @@ function normalizeSendEnvelope(input, sessionId) {
55
55
  }
56
56
  };
57
57
  }
58
- function toHydrationPayload(sessionId, snapshot) {
59
- return {
60
- sessionId,
61
- messages: snapshot.messages,
62
- activeRun: snapshot.activeRun
63
- };
64
- }
65
58
  function useScopedAgentManager(sessionId) {
66
59
  const managerRef = useRef();
67
60
  if (!managerRef.current || managerRef.current.sessionId !== sessionId) {
@@ -110,7 +103,6 @@ function useNcpAgentRuntime({
110
103
  return;
111
104
  }
112
105
  setIsSending(true);
113
- const previousSnapshot = manager.getSnapshot();
114
106
  await manager.dispatch({
115
107
  type: NcpEventType.MessageSent,
116
108
  payload: {
@@ -121,9 +113,6 @@ function useNcpAgentRuntime({
121
113
  });
122
114
  try {
123
115
  await client.send(envelope);
124
- } catch (error) {
125
- manager.hydrate(toHydrationPayload(sessionId, previousSnapshot));
126
- throw error;
127
116
  } finally {
128
117
  setIsSending(false);
129
118
  }
@@ -235,7 +224,126 @@ function useNcpAgent(sessionId, client) {
235
224
  const manager = useScopedAgentManager(sessionId);
236
225
  return useNcpAgentRuntime({ sessionId, client, manager });
237
226
  }
227
+
228
+ // src/attachments/ncp-attachments.ts
229
+ var DEFAULT_NCP_IMAGE_ATTACHMENT_ACCEPT = "image/png,image/jpeg,image/webp,image/gif";
230
+ var DEFAULT_NCP_IMAGE_ATTACHMENT_MIME_TYPES = [
231
+ "image/png",
232
+ "image/jpeg",
233
+ "image/webp",
234
+ "image/gif"
235
+ ];
236
+ var DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES = 10 * 1024 * 1024;
237
+ function createAttachmentId() {
238
+ return `ncp-file-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
239
+ }
240
+ function readFileAsDataUrl(file) {
241
+ return new Promise((resolve, reject) => {
242
+ const reader = new FileReader();
243
+ reader.onerror = () => reject(reader.error ?? new Error(`Failed to read ${file.name}`));
244
+ reader.onload = () => {
245
+ if (typeof reader.result !== "string") {
246
+ reject(new Error(`Unexpected FileReader result for ${file.name}`));
247
+ return;
248
+ }
249
+ resolve(reader.result);
250
+ };
251
+ reader.readAsDataURL(file);
252
+ });
253
+ }
254
+ function toBase64Content(dataUrl) {
255
+ const commaIndex = dataUrl.indexOf(",");
256
+ return commaIndex >= 0 ? dataUrl.slice(commaIndex + 1) : dataUrl;
257
+ }
258
+ function buildNcpImageAttachmentDataUrl(attachment) {
259
+ return `data:${attachment.mimeType};base64,${attachment.contentBase64}`;
260
+ }
261
+ function buildNcpRequestEnvelope(params) {
262
+ const trimmedText = params.text?.trim() ?? "";
263
+ const attachments = params.attachments ?? [];
264
+ const parts = [
265
+ ...trimmedText ? [{ type: "text", text: trimmedText }] : [],
266
+ ...attachments.map((attachment) => ({
267
+ type: "file",
268
+ name: attachment.name,
269
+ mimeType: attachment.mimeType,
270
+ contentBase64: attachment.contentBase64,
271
+ sizeBytes: attachment.sizeBytes
272
+ }))
273
+ ];
274
+ if (parts.length === 0) {
275
+ return null;
276
+ }
277
+ const timestamp = params.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
278
+ const messageId = params.messageId ?? `user-${Date.now().toString(36)}`;
279
+ return {
280
+ sessionId: params.sessionId,
281
+ message: {
282
+ id: messageId,
283
+ sessionId: params.sessionId,
284
+ role: "user",
285
+ status: "final",
286
+ parts,
287
+ timestamp,
288
+ ...params.metadata ? { metadata: params.metadata } : {}
289
+ },
290
+ ...params.metadata ? { metadata: params.metadata } : {}
291
+ };
292
+ }
293
+ async function readFilesAsNcpDraftAttachments(files, options = {}) {
294
+ const acceptedMimeTypes = new Set(
295
+ options.acceptedMimeTypes ?? DEFAULT_NCP_IMAGE_ATTACHMENT_MIME_TYPES
296
+ );
297
+ const maxBytes = options.maxBytes ?? DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES;
298
+ const attachments = [];
299
+ const rejected = [];
300
+ for (const file of files) {
301
+ const mimeType = file.type.trim().toLowerCase();
302
+ if (!acceptedMimeTypes.has(mimeType)) {
303
+ rejected.push({
304
+ fileName: file.name,
305
+ mimeType,
306
+ sizeBytes: file.size,
307
+ reason: "unsupported-type"
308
+ });
309
+ continue;
310
+ }
311
+ if (file.size > maxBytes) {
312
+ rejected.push({
313
+ fileName: file.name,
314
+ mimeType,
315
+ sizeBytes: file.size,
316
+ reason: "too-large"
317
+ });
318
+ continue;
319
+ }
320
+ try {
321
+ const dataUrl = await readFileAsDataUrl(file);
322
+ attachments.push({
323
+ id: createAttachmentId(),
324
+ name: file.name,
325
+ mimeType,
326
+ contentBase64: toBase64Content(dataUrl),
327
+ sizeBytes: file.size
328
+ });
329
+ } catch {
330
+ rejected.push({
331
+ fileName: file.name,
332
+ mimeType,
333
+ sizeBytes: file.size,
334
+ reason: "read-failed"
335
+ });
336
+ }
337
+ }
338
+ return { attachments, rejected };
339
+ }
238
340
  export {
341
+ DEFAULT_NCP_IMAGE_ATTACHMENT_ACCEPT,
342
+ DEFAULT_NCP_IMAGE_ATTACHMENT_MAX_BYTES,
343
+ DEFAULT_NCP_IMAGE_ATTACHMENT_MIME_TYPES,
344
+ buildNcpImageAttachmentDataUrl,
345
+ buildNcpRequestEnvelope,
346
+ readFilesAsNcpDraftAttachments,
239
347
  useHydratedNcpAgent,
240
348
  useNcpAgent
241
349
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ncp-react",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "private": false,
5
5
  "description": "React bindings for building NCP-based agent applications.",
6
6
  "type": "module",
@@ -15,8 +15,8 @@
15
15
  "dist"
16
16
  ],
17
17
  "dependencies": {
18
- "@nextclaw/ncp": "0.3.1",
19
- "@nextclaw/ncp-toolkit": "0.4.1"
18
+ "@nextclaw/ncp": "0.3.2",
19
+ "@nextclaw/ncp-toolkit": "0.4.2"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "react": "^18.0.0 || ^19.0.0"