@emblemvault/hustle-react 1.4.1 → 1.4.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.
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  import { createContext, useState, useEffect, useCallback, useRef, useMemo, useContext } from 'react';
3
2
  import { HustleIncognitoClient } from 'hustle-incognito';
4
3
  import { useEmblemAuthOptional } from '@emblemvault/emblem-auth-react';
@@ -15179,7 +15178,14 @@ function HustleChat({
15179
15178
  setIsStreaming(true);
15180
15179
  setCurrentToolCalls([]);
15181
15180
  try {
15182
- const chatMessages = messagesRef.current.filter((m2) => !m2.isStreaming).map((m2) => ({ role: m2.role, content: m2.content }));
15181
+ const chatMessages = messagesRef.current.filter((m2) => !m2.isStreaming).map((m2) => {
15182
+ const msg = { role: m2.role, content: m2.content };
15183
+ if (m2.role === "assistant" && m2.toolInvocations && m2.toolInvocations.length > 0) {
15184
+ msg.toolInvocations = m2.toolInvocations;
15185
+ msg.parts = m2.parts;
15186
+ }
15187
+ return msg;
15188
+ });
15183
15189
  chatMessages.push({ role: "user", content: "continue" });
15184
15190
  const stream = chatStream({
15185
15191
  messages: chatMessages,
@@ -15187,6 +15193,9 @@ function HustleChat({
15187
15193
  });
15188
15194
  let fullContent = "";
15189
15195
  const toolCallsAccumulated = [];
15196
+ const toolInvocationsAccumulated = [];
15197
+ const partsAccumulated = [{ type: "step-start" }];
15198
+ let stepCounter = 0;
15190
15199
  for await (const chunk of stream) {
15191
15200
  if (chunk.type === "text") {
15192
15201
  fullContent += chunk.value;
@@ -15198,22 +15207,79 @@ function HustleChat({
15198
15207
  } else if (chunk.type === "tool_call") {
15199
15208
  const toolCall = chunk.value;
15200
15209
  toolCallsAccumulated.push(toolCall);
15210
+ const invocation = {
15211
+ state: "call",
15212
+ step: stepCounter++,
15213
+ toolCallId: toolCall.toolCallId,
15214
+ toolName: toolCall.toolName,
15215
+ args: toolCall.args
15216
+ };
15217
+ toolInvocationsAccumulated.push(invocation);
15218
+ partsAccumulated.push({ type: "tool-invocation", toolInvocation: invocation });
15201
15219
  setCurrentToolCalls([...toolCallsAccumulated]);
15202
15220
  setMessages(
15203
15221
  (prev) => prev.map(
15204
- (m2) => m2.id === assistantMessage.id ? { ...m2, toolCalls: [...toolCallsAccumulated] } : m2
15222
+ (m2) => m2.id === assistantMessage.id ? {
15223
+ ...m2,
15224
+ toolCalls: [...toolCallsAccumulated],
15225
+ toolInvocations: [...toolInvocationsAccumulated],
15226
+ parts: [...partsAccumulated]
15227
+ } : m2
15205
15228
  )
15206
15229
  );
15207
15230
  onToolCall?.(toolCall);
15231
+ } else if (chunk.type === "tool_result") {
15232
+ const toolResult = chunk.value;
15233
+ const invocationIndex = toolInvocationsAccumulated.findIndex(
15234
+ (inv) => inv.toolCallId === toolResult.toolCallId
15235
+ );
15236
+ if (invocationIndex !== -1) {
15237
+ toolInvocationsAccumulated[invocationIndex] = {
15238
+ ...toolInvocationsAccumulated[invocationIndex],
15239
+ state: "result",
15240
+ result: toolResult.result
15241
+ };
15242
+ const partIndex = partsAccumulated.findIndex(
15243
+ (p) => p.type === "tool-invocation" && p.toolInvocation.toolCallId === toolResult.toolCallId
15244
+ );
15245
+ if (partIndex !== -1) {
15246
+ partsAccumulated[partIndex] = {
15247
+ type: "tool-invocation",
15248
+ toolInvocation: toolInvocationsAccumulated[invocationIndex]
15249
+ };
15250
+ }
15251
+ }
15252
+ setMessages(
15253
+ (prev) => prev.map(
15254
+ (m2) => m2.id === assistantMessage.id ? {
15255
+ ...m2,
15256
+ toolInvocations: [...toolInvocationsAccumulated],
15257
+ parts: [...partsAccumulated]
15258
+ } : m2
15259
+ )
15260
+ );
15208
15261
  } else if (chunk.type === "error") {
15209
15262
  console.error("Stream error:", chunk.value);
15210
15263
  }
15211
15264
  }
15212
15265
  const processedResponse = await stream.response;
15213
15266
  const finalContent = processedResponse?.content || fullContent || "(No response)";
15267
+ const finalParts = [{ type: "step-start" }];
15268
+ if (finalContent) {
15269
+ finalParts.push({ type: "text", text: finalContent });
15270
+ }
15271
+ for (const inv of toolInvocationsAccumulated) {
15272
+ finalParts.push({ type: "tool-invocation", toolInvocation: inv });
15273
+ }
15214
15274
  setMessages(
15215
15275
  (prev) => prev.map(
15216
- (m2) => m2.id === assistantMessage.id ? { ...m2, isStreaming: false, content: finalContent } : m2
15276
+ (m2) => m2.id === assistantMessage.id ? {
15277
+ ...m2,
15278
+ isStreaming: false,
15279
+ content: finalContent,
15280
+ toolInvocations: toolInvocationsAccumulated.length > 0 ? toolInvocationsAccumulated : void 0,
15281
+ parts: toolInvocationsAccumulated.length > 0 ? finalParts : void 0
15282
+ } : m2
15217
15283
  )
15218
15284
  );
15219
15285
  onResponse?.(finalContent);
@@ -15275,7 +15341,14 @@ function HustleChat({
15275
15341
  setIsStreaming(true);
15276
15342
  setCurrentToolCalls([]);
15277
15343
  try {
15278
- const chatMessages = messages.filter((m2) => !m2.isStreaming).map((m2) => ({ role: m2.role, content: m2.content }));
15344
+ const chatMessages = messages.filter((m2) => !m2.isStreaming).map((m2) => {
15345
+ const msg = { role: m2.role, content: m2.content };
15346
+ if (m2.role === "assistant" && m2.toolInvocations && m2.toolInvocations.length > 0) {
15347
+ msg.toolInvocations = m2.toolInvocations;
15348
+ msg.parts = m2.parts;
15349
+ }
15350
+ return msg;
15351
+ });
15279
15352
  chatMessages.push({ role: "user", content });
15280
15353
  const stream = chatStream({
15281
15354
  messages: chatMessages,
@@ -15285,6 +15358,9 @@ function HustleChat({
15285
15358
  setAttachments([]);
15286
15359
  let fullContent = "";
15287
15360
  const toolCallsAccumulated = [];
15361
+ const toolInvocationsAccumulated = [];
15362
+ const partsAccumulated = [{ type: "step-start" }];
15363
+ let stepCounter = 0;
15288
15364
  for await (const chunk of stream) {
15289
15365
  if (chunk.type === "text") {
15290
15366
  fullContent += chunk.value;
@@ -15296,22 +15372,79 @@ function HustleChat({
15296
15372
  } else if (chunk.type === "tool_call") {
15297
15373
  const toolCall = chunk.value;
15298
15374
  toolCallsAccumulated.push(toolCall);
15375
+ const invocation = {
15376
+ state: "call",
15377
+ step: stepCounter++,
15378
+ toolCallId: toolCall.toolCallId,
15379
+ toolName: toolCall.toolName,
15380
+ args: toolCall.args
15381
+ };
15382
+ toolInvocationsAccumulated.push(invocation);
15383
+ partsAccumulated.push({ type: "tool-invocation", toolInvocation: invocation });
15299
15384
  setCurrentToolCalls([...toolCallsAccumulated]);
15300
15385
  setMessages(
15301
15386
  (prev) => prev.map(
15302
- (m2) => m2.id === assistantMessage.id ? { ...m2, toolCalls: [...toolCallsAccumulated] } : m2
15387
+ (m2) => m2.id === assistantMessage.id ? {
15388
+ ...m2,
15389
+ toolCalls: [...toolCallsAccumulated],
15390
+ toolInvocations: [...toolInvocationsAccumulated],
15391
+ parts: [...partsAccumulated]
15392
+ } : m2
15303
15393
  )
15304
15394
  );
15305
15395
  onToolCall?.(toolCall);
15396
+ } else if (chunk.type === "tool_result") {
15397
+ const toolResult = chunk.value;
15398
+ const invocationIndex = toolInvocationsAccumulated.findIndex(
15399
+ (inv) => inv.toolCallId === toolResult.toolCallId
15400
+ );
15401
+ if (invocationIndex !== -1) {
15402
+ toolInvocationsAccumulated[invocationIndex] = {
15403
+ ...toolInvocationsAccumulated[invocationIndex],
15404
+ state: "result",
15405
+ result: toolResult.result
15406
+ };
15407
+ const partIndex = partsAccumulated.findIndex(
15408
+ (p) => p.type === "tool-invocation" && p.toolInvocation.toolCallId === toolResult.toolCallId
15409
+ );
15410
+ if (partIndex !== -1) {
15411
+ partsAccumulated[partIndex] = {
15412
+ type: "tool-invocation",
15413
+ toolInvocation: toolInvocationsAccumulated[invocationIndex]
15414
+ };
15415
+ }
15416
+ }
15417
+ setMessages(
15418
+ (prev) => prev.map(
15419
+ (m2) => m2.id === assistantMessage.id ? {
15420
+ ...m2,
15421
+ toolInvocations: [...toolInvocationsAccumulated],
15422
+ parts: [...partsAccumulated]
15423
+ } : m2
15424
+ )
15425
+ );
15306
15426
  } else if (chunk.type === "error") {
15307
15427
  console.error("Stream error:", chunk.value);
15308
15428
  }
15309
15429
  }
15310
15430
  const processedResponse = await stream.response;
15311
15431
  const finalContent = processedResponse?.content || fullContent || "(No response)";
15432
+ const finalParts = [{ type: "step-start" }];
15433
+ if (finalContent) {
15434
+ finalParts.push({ type: "text", text: finalContent });
15435
+ }
15436
+ for (const inv of toolInvocationsAccumulated) {
15437
+ finalParts.push({ type: "tool-invocation", toolInvocation: inv });
15438
+ }
15312
15439
  setMessages(
15313
15440
  (prev) => prev.map(
15314
- (m2) => m2.id === assistantMessage.id ? { ...m2, isStreaming: false, content: finalContent } : m2
15441
+ (m2) => m2.id === assistantMessage.id ? {
15442
+ ...m2,
15443
+ isStreaming: false,
15444
+ content: finalContent,
15445
+ toolInvocations: toolInvocationsAccumulated.length > 0 ? toolInvocationsAccumulated : void 0,
15446
+ parts: toolInvocationsAccumulated.length > 0 ? finalParts : void 0
15447
+ } : m2
15315
15448
  )
15316
15449
  );
15317
15450
  onResponse?.(finalContent);