@dudousxd/nestjs-agent-react 0.3.3 → 0.4.1

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.cjs CHANGED
@@ -42,6 +42,7 @@ __export(src_exports, {
42
42
  formatRelativeTime: () => formatRelativeTime,
43
43
  loadStoredSpeechLang: () => loadStoredSpeechLang,
44
44
  persistSpeechLang: () => persistSpeechLang,
45
+ storedMessageToUiMessage: () => storedMessageToUiMessage,
45
46
  useAgentChat: () => useAgentChat,
46
47
  useSpeechRecognition: () => useSpeechRecognition
47
48
  });
@@ -223,6 +224,14 @@ var AgentChatTransport = class {
223
224
  break;
224
225
  case "step-finish":
225
226
  closeStep();
227
+ if (event.costUsd !== void 0) {
228
+ controller.enqueue({
229
+ type: "message-metadata",
230
+ messageMetadata: {
231
+ costUsd: event.costUsd
232
+ }
233
+ });
234
+ }
226
235
  break;
227
236
  case "text":
228
237
  ensureStep();
@@ -259,7 +268,15 @@ var AgentChatTransport = class {
259
268
  controller.enqueue({
260
269
  type: "tool-input-start",
261
270
  toolCallId: event.id,
262
- toolName: event.name
271
+ toolName: event.name,
272
+ // `toolKind` is absent on older backends — omitted (not `undefined`-valued) so
273
+ // `toolMetadata` itself is only present when there's something to say, letting a
274
+ // UI gate approval affordances on `kind === 'action'` without hardcoding tool names.
275
+ ...event.toolKind !== void 0 ? {
276
+ toolMetadata: {
277
+ toolKind: event.toolKind
278
+ }
279
+ } : {}
263
280
  });
264
281
  break;
265
282
  case "tool-input-delta":
@@ -276,7 +293,12 @@ var AgentChatTransport = class {
276
293
  type: "tool-input-available",
277
294
  toolCallId: event.id,
278
295
  toolName: event.name,
279
- input: event.input
296
+ input: event.input,
297
+ ...event.toolKind !== void 0 ? {
298
+ toolMetadata: {
299
+ toolKind: event.toolKind
300
+ }
301
+ } : {}
280
302
  });
281
303
  break;
282
304
  case "tool-output":
@@ -458,10 +480,39 @@ var AgentClient = class {
458
480
  return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
459
481
  }
460
482
  renameThread(id, title) {
461
- return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, {
483
+ return this.updateThread(id, {
462
484
  title
463
485
  });
464
486
  }
487
+ /** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
488
+ updateThread(id, patch) {
489
+ return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, patch);
490
+ }
491
+ /**
492
+ * Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
493
+ * mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
494
+ * what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
495
+ */
496
+ async uploadAttachment(file) {
497
+ const fetchImpl = this.options.fetch ?? globalThis.fetch;
498
+ const baseUrl = (this.options.baseUrl ?? "").replace(/\/$/, "");
499
+ const dynamic = await this.options.getHeaders?.() ?? {};
500
+ const formData = new FormData();
501
+ formData.append("file", file);
502
+ const response = await fetchImpl(`${baseUrl}/agent/attachments`, {
503
+ method: "POST",
504
+ headers: {
505
+ accept: "application/json",
506
+ ...this.options.headers,
507
+ ...dynamic
508
+ },
509
+ body: formData,
510
+ ...this.options.credentials !== void 0 ? {
511
+ credentials: this.options.credentials
512
+ } : {}
513
+ });
514
+ return this.handleResponse(response, "POST", "/agent/attachments");
515
+ }
465
516
  promoteThread(id) {
466
517
  return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
467
518
  }
@@ -501,6 +552,9 @@ var AgentClient = class {
501
552
  credentials: this.options.credentials
502
553
  } : {}
503
554
  });
555
+ return this.handleResponse(response, method, path);
556
+ }
557
+ async handleResponse(response, method, path) {
504
558
  if (!response.ok) {
505
559
  throw new AgentHttpError(response.status, method, path, response.statusText);
506
560
  }
@@ -511,6 +565,52 @@ var AgentClient = class {
511
565
  }
512
566
  };
513
567
 
568
+ // src/stored-message-to-ui-message.ts
569
+ function storedMessageToUiMessage(message) {
570
+ const parts = [];
571
+ if (message.content) {
572
+ parts.push({
573
+ type: "text",
574
+ text: message.content
575
+ });
576
+ }
577
+ for (const attachment of message.attachments ?? []) {
578
+ parts.push({
579
+ type: "file",
580
+ mediaType: attachment.contentType,
581
+ filename: attachment.name,
582
+ url: attachment.url
583
+ });
584
+ }
585
+ for (const call of message.toolCalls ?? []) {
586
+ const result = message.toolResults?.find((candidate) => candidate.id === call.id);
587
+ const toolKind = call.kind;
588
+ parts.push({
589
+ type: `tool-${call.name}`,
590
+ toolCallId: call.id,
591
+ ...toolKind !== void 0 ? {
592
+ toolMetadata: {
593
+ toolKind
594
+ }
595
+ } : {},
596
+ ...result !== void 0 ? {
597
+ state: "output-available",
598
+ input: call.input,
599
+ output: result.output
600
+ } : {
601
+ state: "input-available",
602
+ input: call.input
603
+ }
604
+ });
605
+ }
606
+ return {
607
+ id: message.id,
608
+ role: message.role,
609
+ parts
610
+ };
611
+ }
612
+ __name(storedMessageToUiMessage, "storedMessageToUiMessage");
613
+
514
614
  // src/use-agent-chat.ts
515
615
  var import_react = require("@ai-sdk/react");
516
616
  var import_react2 = require("react");
@@ -520,6 +620,9 @@ function useAgentChat(options) {
520
620
  const [runId, setRunId] = (0, import_react2.useState)(options.resumeRunId);
521
621
  const runIdRef = (0, import_react2.useRef)(runId);
522
622
  runIdRef.current = runId;
623
+ const autoResumeRunIdRef = (0, import_react2.useRef)(void 0);
624
+ const [autoResume, setAutoResume] = (0, import_react2.useState)(false);
625
+ const [activeRunId, setActiveRunId] = (0, import_react2.useState)(null);
523
626
  const regenerateNext = (0, import_react2.useRef)(false);
524
627
  const createdThreadId = (0, import_react2.useRef)(void 0);
525
628
  const client = (0, import_react2.useMemo)(() => {
@@ -537,6 +640,26 @@ function useAgentChat(options) {
537
640
  getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
538
641
  });
539
642
  }, []);
643
+ (0, import_react2.useEffect)(() => {
644
+ if (!options.resume || options.threadId === void 0) return;
645
+ let cancelled = false;
646
+ const threadId = options.threadId;
647
+ void client.getThread(threadId).then((thread) => {
648
+ if (cancelled) return;
649
+ setActiveRunId(thread.activeRunId ?? null);
650
+ if (thread.activeRunId != null) {
651
+ autoResumeRunIdRef.current = thread.activeRunId;
652
+ setAutoResume(true);
653
+ }
654
+ });
655
+ return () => {
656
+ cancelled = true;
657
+ };
658
+ }, [
659
+ client,
660
+ options.resume,
661
+ options.threadId
662
+ ]);
540
663
  const transport = (0, import_react2.useMemo)(() => {
541
664
  function onMeta(meta) {
542
665
  setRunId(meta.runId);
@@ -578,13 +701,13 @@ function useAgentChat(options) {
578
701
  } : {}
579
702
  };
580
703
  }, "getBody"),
581
- getResumeRunId: /* @__PURE__ */ __name(() => latest.current.resumeRunId, "getResumeRunId"),
704
+ getResumeRunId: /* @__PURE__ */ __name(() => latest.current.resumeRunId ?? autoResumeRunIdRef.current, "getResumeRunId"),
582
705
  onMeta
583
706
  });
584
707
  }, []);
585
708
  const chat = (0, import_react.useChat)({
586
709
  transport,
587
- resume: options.resumeRunId !== void 0,
710
+ resume: options.resumeRunId !== void 0 || autoResume,
588
711
  ...options.threadId !== void 0 ? {
589
712
  id: options.threadId
590
713
  } : {},
@@ -651,10 +774,10 @@ function useAgentChat(options) {
651
774
  ]);
652
775
  const cancel = (0, import_react2.useCallback)(async () => {
653
776
  chatRef.current.stop();
654
- const activeRunId = runIdRef.current;
655
- if (activeRunId) {
777
+ const activeRunId2 = runIdRef.current;
778
+ if (activeRunId2) {
656
779
  try {
657
- await client.cancelStream(activeRunId);
780
+ await client.cancelStream(activeRunId2);
658
781
  } catch {
659
782
  }
660
783
  }
@@ -686,6 +809,8 @@ function useAgentChat(options) {
686
809
  ...chat,
687
810
  addToolResult,
688
811
  runId,
812
+ /** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
813
+ activeRunId,
689
814
  client,
690
815
  threads,
691
816
  loadThreads,
@@ -1295,6 +1420,7 @@ __name(persistSpeechLang, "persistSpeechLang");
1295
1420
  formatRelativeTime,
1296
1421
  loadStoredSpeechLang,
1297
1422
  persistSpeechLang,
1423
+ storedMessageToUiMessage,
1298
1424
  useAgentChat,
1299
1425
  useSpeechRecognition
1300
1426
  });