@dudousxd/nestjs-agent-react 0.3.2 → 0.3.3

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.js CHANGED
@@ -101,18 +101,26 @@ var AgentChatTransport = class {
101
101
  this.options.onMeta?.(meta);
102
102
  }
103
103
  /**
104
- * Parse the backend's SSE byte stream and synthesize a valid v7
105
- * UI-message chunk stream. Recognized backend frames:
106
- * - `event: meta` `data: {"runId","threadId"}` → records identity
107
- * - `data: {"delta":"..."}` → `text-delta`
108
- * - `event: done` `data: {}` → terminates
104
+ * Parse the backend's SSE byte stream and re-emit it as a valid v7 UI-message chunk stream.
105
+ * Recognized frames:
106
+ * - `event: meta` `data: {"runId","threadId"}` → records identity
107
+ * - `data: <AgentStreamEvent JSON>` → mapped to UI chunks (text / reasoning / tool)
108
+ * - `event: done` `data: {}` → terminates
109
+ * - `event: error` `data: {code,message}` → error chunk
110
+ *
111
+ * A run is ONE UI message with N steps. Each `step-start`/`step-finish` pair brackets a model
112
+ * call plus its tool execution; text and reasoning open lazily and close at the step boundary, so
113
+ * tool-call cards (input streaming → output) render live between the prose.
109
114
  */
110
115
  toChunkStream(source) {
111
116
  const reader = source.getReader();
112
117
  const decoder = new TextDecoder();
113
- const textId = `txt-${Math.random().toString(36).slice(2)}`;
114
118
  let buffer = "";
115
119
  let started = false;
120
+ let stepOpen = false;
121
+ let stepIndex = 0;
122
+ let textId = null;
123
+ let reasoningId = null;
116
124
  const record = /* @__PURE__ */ __name((meta) => this.recordMeta(meta), "record");
117
125
  return new ReadableStream({
118
126
  async pull(controller) {
@@ -122,24 +130,133 @@ var AgentChatTransport = class {
122
130
  controller.enqueue({
123
131
  type: "start"
124
132
  });
125
- controller.enqueue({
126
- type: "start-step"
127
- });
128
- controller.enqueue({
129
- type: "text-start",
130
- id: textId
131
- });
132
133
  }
133
134
  __name(ensureStarted, "ensureStarted");
134
- function finish() {
135
+ function openStep() {
135
136
  ensureStarted();
137
+ if (stepOpen) closeStep();
138
+ stepOpen = true;
139
+ stepIndex += 1;
140
+ textId = null;
141
+ reasoningId = null;
136
142
  controller.enqueue({
137
- type: "text-end",
138
- id: textId
143
+ type: "start-step"
139
144
  });
145
+ }
146
+ __name(openStep, "openStep");
147
+ function ensureStep() {
148
+ if (!stepOpen) openStep();
149
+ }
150
+ __name(ensureStep, "ensureStep");
151
+ function closeStep() {
152
+ if (!stepOpen) return;
153
+ if (textId !== null) {
154
+ controller.enqueue({
155
+ type: "text-end",
156
+ id: textId
157
+ });
158
+ textId = null;
159
+ }
160
+ if (reasoningId !== null) {
161
+ controller.enqueue({
162
+ type: "reasoning-end",
163
+ id: reasoningId
164
+ });
165
+ reasoningId = null;
166
+ }
140
167
  controller.enqueue({
141
168
  type: "finish-step"
142
169
  });
170
+ stepOpen = false;
171
+ }
172
+ __name(closeStep, "closeStep");
173
+ function emit(event) {
174
+ switch (event.kind) {
175
+ case "step-start":
176
+ openStep();
177
+ break;
178
+ case "step-finish":
179
+ closeStep();
180
+ break;
181
+ case "text":
182
+ ensureStep();
183
+ if (textId === null) {
184
+ textId = `txt-${stepIndex}`;
185
+ controller.enqueue({
186
+ type: "text-start",
187
+ id: textId
188
+ });
189
+ }
190
+ controller.enqueue({
191
+ type: "text-delta",
192
+ id: textId,
193
+ delta: event.text
194
+ });
195
+ break;
196
+ case "reasoning":
197
+ ensureStep();
198
+ if (reasoningId === null) {
199
+ reasoningId = `rsn-${stepIndex}`;
200
+ controller.enqueue({
201
+ type: "reasoning-start",
202
+ id: reasoningId
203
+ });
204
+ }
205
+ controller.enqueue({
206
+ type: "reasoning-delta",
207
+ id: reasoningId,
208
+ delta: event.text
209
+ });
210
+ break;
211
+ case "tool-input-start":
212
+ ensureStep();
213
+ controller.enqueue({
214
+ type: "tool-input-start",
215
+ toolCallId: event.id,
216
+ toolName: event.name
217
+ });
218
+ break;
219
+ case "tool-input-delta":
220
+ ensureStep();
221
+ controller.enqueue({
222
+ type: "tool-input-delta",
223
+ toolCallId: event.id,
224
+ inputTextDelta: event.delta
225
+ });
226
+ break;
227
+ case "tool-input-available":
228
+ ensureStep();
229
+ controller.enqueue({
230
+ type: "tool-input-available",
231
+ toolCallId: event.id,
232
+ toolName: event.name,
233
+ input: event.input
234
+ });
235
+ break;
236
+ case "tool-output":
237
+ ensureStep();
238
+ controller.enqueue({
239
+ type: "tool-output-available",
240
+ toolCallId: event.id,
241
+ output: event.output
242
+ });
243
+ break;
244
+ case "tool-output-error":
245
+ ensureStep();
246
+ controller.enqueue({
247
+ type: "tool-output-error",
248
+ toolCallId: event.id,
249
+ errorText: event.error
250
+ });
251
+ break;
252
+ default:
253
+ break;
254
+ }
255
+ }
256
+ __name(emit, "emit");
257
+ function finish() {
258
+ ensureStarted();
259
+ closeStep();
143
260
  controller.enqueue({
144
261
  type: "finish"
145
262
  });
@@ -178,15 +295,8 @@ var AgentChatTransport = class {
178
295
  const meta = parseMeta(frame.data);
179
296
  if (meta) record(meta);
180
297
  } else if (frame.data) {
181
- const delta = parseDelta(frame.data);
182
- if (delta) {
183
- ensureStarted();
184
- controller.enqueue({
185
- type: "text-delta",
186
- id: textId,
187
- delta
188
- });
189
- }
298
+ const event = parseEvent(frame.data);
299
+ if (event) emit(event);
190
300
  }
191
301
  separator = buffer.indexOf("\n\n");
192
302
  }
@@ -248,17 +358,17 @@ function parseErrorText(data) {
248
358
  return "Agent run failed";
249
359
  }
250
360
  __name(parseErrorText, "parseErrorText");
251
- function parseDelta(data) {
361
+ function parseEvent(data) {
252
362
  try {
253
363
  const parsed = JSON.parse(data);
254
- if (parsed !== null && typeof parsed === "object" && "delta" in parsed && typeof parsed.delta === "string") {
255
- return parsed.delta;
364
+ if (parsed !== null && typeof parsed === "object" && "kind" in parsed) {
365
+ return parsed;
256
366
  }
257
367
  } catch {
258
368
  }
259
369
  return null;
260
370
  }
261
- __name(parseDelta, "parseDelta");
371
+ __name(parseEvent, "parseEvent");
262
372
  function extractText(message) {
263
373
  let text = "";
264
374
  for (const part of message.parts ?? []) {
@@ -365,6 +475,7 @@ function useAgentChat(options) {
365
475
  const runIdRef = useRef(runId);
366
476
  runIdRef.current = runId;
367
477
  const regenerateNext = useRef(false);
478
+ const createdThreadId = useRef(void 0);
368
479
  const client = useMemo(() => {
369
480
  if (options.client) return options.client;
370
481
  return new AgentClient({
@@ -383,6 +494,10 @@ function useAgentChat(options) {
383
494
  const transport = useMemo(() => {
384
495
  function onMeta(meta) {
385
496
  setRunId(meta.runId);
497
+ if (latest.current.threadId === void 0 && meta.threadId && createdThreadId.current !== meta.threadId) {
498
+ createdThreadId.current = meta.threadId;
499
+ latest.current.onThreadCreated?.(meta.threadId);
500
+ }
386
501
  }
387
502
  __name(onMeta, "onMeta");
388
503
  return new AgentChatTransport({
@@ -404,9 +519,10 @@ function useAgentChat(options) {
404
519
  const pageContext = current.getPageContext?.() ?? null;
405
520
  const regenerate2 = regenerateNext.current;
406
521
  regenerateNext.current = false;
522
+ const threadId = current.threadId ?? createdThreadId.current;
407
523
  return {
408
- ...current.threadId !== void 0 ? {
409
- threadId: current.threadId
524
+ ...threadId !== void 0 ? {
525
+ threadId
410
526
  } : {},
411
527
  ...pageContext ? {
412
528
  pageContext