@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,5 +1,5 @@
1
1
  export { HustleProvider, useHustle } from '../providers/index.cjs';
2
- export { A as Attachment, C as ChatMessage, b as ChatOptions, d as ChatResponse, H as HustleConfig, h as HustleContextValue, i as HustleProviderProps, M as Model, c as StreamChunk, g as StreamEndEvent, S as StreamOptions, T as ToolCall, f as ToolEndEvent, a as ToolResult, e as ToolStartEvent } from '../hustle-S48t4lTZ.cjs';
2
+ export { A as Attachment, C as ChatMessage, b as ChatOptions, d as ChatResponse, H as HustleConfig, h as HustleContextValue, i as HustleProviderProps, M as Model, c as StreamChunk, g as StreamEndEvent, S as StreamOptions, T as ToolCall, f as ToolEndEvent, a as ToolResult, e as ToolStartEvent } from '../hustle-C0Ltl5k4.cjs';
3
3
  import { S as StoredPlugin, a as HydratedPlugin, H as HustlePlugin } from '../plugin-COr42J6-.cjs';
4
4
  import 'react/jsx-runtime';
5
5
  import 'hustle-incognito';
@@ -1,5 +1,5 @@
1
1
  export { HustleProvider, useHustle } from '../providers/index.js';
2
- export { A as Attachment, C as ChatMessage, b as ChatOptions, d as ChatResponse, H as HustleConfig, h as HustleContextValue, i as HustleProviderProps, M as Model, c as StreamChunk, g as StreamEndEvent, S as StreamOptions, T as ToolCall, f as ToolEndEvent, a as ToolResult, e as ToolStartEvent } from '../hustle-S48t4lTZ.js';
2
+ export { A as Attachment, C as ChatMessage, b as ChatOptions, d as ChatResponse, H as HustleConfig, h as HustleContextValue, i as HustleProviderProps, M as Model, c as StreamChunk, g as StreamEndEvent, S as StreamOptions, T as ToolCall, f as ToolEndEvent, a as ToolResult, e as ToolStartEvent } from '../hustle-C0Ltl5k4.js';
3
3
  import { S as StoredPlugin, a as HydratedPlugin, H as HustlePlugin } from '../plugin-COr42J6-.js';
4
4
  import 'react/jsx-runtime';
5
5
  import 'hustle-incognito';
@@ -5,12 +5,39 @@ import { HustleIncognitoClient } from 'hustle-incognito';
5
5
  * These types mirror the HustleIncognitoClient types for use in React components
6
6
  */
7
7
 
8
+ /**
9
+ * Tool invocation with state tracking (AI SDK format)
10
+ */
11
+ interface ToolInvocation {
12
+ state: 'call' | 'result';
13
+ step: number;
14
+ toolCallId: string;
15
+ toolName: string;
16
+ args?: Record<string, unknown>;
17
+ result?: unknown;
18
+ }
19
+ /**
20
+ * Message part for AI SDK compatibility
21
+ */
22
+ type MessagePart = {
23
+ type: 'step-start';
24
+ } | {
25
+ type: 'text';
26
+ text: string;
27
+ } | {
28
+ type: 'tool-invocation';
29
+ toolInvocation: ToolInvocation;
30
+ };
8
31
  /**
9
32
  * Chat message structure
10
33
  */
11
34
  interface ChatMessage {
12
35
  role: 'system' | 'user' | 'assistant';
13
36
  content: string;
37
+ /** Tool invocations for this message (AI SDK format) */
38
+ toolInvocations?: ToolInvocation[];
39
+ /** Message parts for AI SDK compatibility */
40
+ parts?: MessagePart[];
14
41
  }
15
42
  /**
16
43
  * Tool call information
@@ -5,12 +5,39 @@ import { HustleIncognitoClient } from 'hustle-incognito';
5
5
  * These types mirror the HustleIncognitoClient types for use in React components
6
6
  */
7
7
 
8
+ /**
9
+ * Tool invocation with state tracking (AI SDK format)
10
+ */
11
+ interface ToolInvocation {
12
+ state: 'call' | 'result';
13
+ step: number;
14
+ toolCallId: string;
15
+ toolName: string;
16
+ args?: Record<string, unknown>;
17
+ result?: unknown;
18
+ }
19
+ /**
20
+ * Message part for AI SDK compatibility
21
+ */
22
+ type MessagePart = {
23
+ type: 'step-start';
24
+ } | {
25
+ type: 'text';
26
+ text: string;
27
+ } | {
28
+ type: 'tool-invocation';
29
+ toolInvocation: ToolInvocation;
30
+ };
8
31
  /**
9
32
  * Chat message structure
10
33
  */
11
34
  interface ChatMessage {
12
35
  role: 'system' | 'user' | 'assistant';
13
36
  content: string;
37
+ /** Tool invocations for this message (AI SDK format) */
38
+ toolInvocations?: ToolInvocation[];
39
+ /** Message parts for AI SDK compatibility */
40
+ parts?: MessagePart[];
14
41
  }
15
42
  /**
16
43
  * Tool call information
package/dist/index.cjs CHANGED
@@ -4049,7 +4049,14 @@ function HustleChat({
4049
4049
  setIsStreaming(true);
4050
4050
  setCurrentToolCalls([]);
4051
4051
  try {
4052
- const chatMessages = messagesRef.current.filter((m) => !m.isStreaming).map((m) => ({ role: m.role, content: m.content }));
4052
+ const chatMessages = messagesRef.current.filter((m) => !m.isStreaming).map((m) => {
4053
+ const msg = { role: m.role, content: m.content };
4054
+ if (m.role === "assistant" && m.toolInvocations && m.toolInvocations.length > 0) {
4055
+ msg.toolInvocations = m.toolInvocations;
4056
+ msg.parts = m.parts;
4057
+ }
4058
+ return msg;
4059
+ });
4053
4060
  chatMessages.push({ role: "user", content: "continue" });
4054
4061
  const stream = chatStream({
4055
4062
  messages: chatMessages,
@@ -4057,6 +4064,9 @@ function HustleChat({
4057
4064
  });
4058
4065
  let fullContent = "";
4059
4066
  const toolCallsAccumulated = [];
4067
+ const toolInvocationsAccumulated = [];
4068
+ const partsAccumulated = [{ type: "step-start" }];
4069
+ let stepCounter = 0;
4060
4070
  for await (const chunk of stream) {
4061
4071
  if (chunk.type === "text") {
4062
4072
  fullContent += chunk.value;
@@ -4068,22 +4078,79 @@ function HustleChat({
4068
4078
  } else if (chunk.type === "tool_call") {
4069
4079
  const toolCall = chunk.value;
4070
4080
  toolCallsAccumulated.push(toolCall);
4081
+ const invocation = {
4082
+ state: "call",
4083
+ step: stepCounter++,
4084
+ toolCallId: toolCall.toolCallId,
4085
+ toolName: toolCall.toolName,
4086
+ args: toolCall.args
4087
+ };
4088
+ toolInvocationsAccumulated.push(invocation);
4089
+ partsAccumulated.push({ type: "tool-invocation", toolInvocation: invocation });
4071
4090
  setCurrentToolCalls([...toolCallsAccumulated]);
4072
4091
  setMessages(
4073
4092
  (prev) => prev.map(
4074
- (m) => m.id === assistantMessage.id ? { ...m, toolCalls: [...toolCallsAccumulated] } : m
4093
+ (m) => m.id === assistantMessage.id ? {
4094
+ ...m,
4095
+ toolCalls: [...toolCallsAccumulated],
4096
+ toolInvocations: [...toolInvocationsAccumulated],
4097
+ parts: [...partsAccumulated]
4098
+ } : m
4075
4099
  )
4076
4100
  );
4077
4101
  onToolCall?.(toolCall);
4102
+ } else if (chunk.type === "tool_result") {
4103
+ const toolResult = chunk.value;
4104
+ const invocationIndex = toolInvocationsAccumulated.findIndex(
4105
+ (inv) => inv.toolCallId === toolResult.toolCallId
4106
+ );
4107
+ if (invocationIndex !== -1) {
4108
+ toolInvocationsAccumulated[invocationIndex] = {
4109
+ ...toolInvocationsAccumulated[invocationIndex],
4110
+ state: "result",
4111
+ result: toolResult.result
4112
+ };
4113
+ const partIndex = partsAccumulated.findIndex(
4114
+ (p) => p.type === "tool-invocation" && p.toolInvocation.toolCallId === toolResult.toolCallId
4115
+ );
4116
+ if (partIndex !== -1) {
4117
+ partsAccumulated[partIndex] = {
4118
+ type: "tool-invocation",
4119
+ toolInvocation: toolInvocationsAccumulated[invocationIndex]
4120
+ };
4121
+ }
4122
+ }
4123
+ setMessages(
4124
+ (prev) => prev.map(
4125
+ (m) => m.id === assistantMessage.id ? {
4126
+ ...m,
4127
+ toolInvocations: [...toolInvocationsAccumulated],
4128
+ parts: [...partsAccumulated]
4129
+ } : m
4130
+ )
4131
+ );
4078
4132
  } else if (chunk.type === "error") {
4079
4133
  console.error("Stream error:", chunk.value);
4080
4134
  }
4081
4135
  }
4082
4136
  const processedResponse = await stream.response;
4083
4137
  const finalContent = processedResponse?.content || fullContent || "(No response)";
4138
+ const finalParts = [{ type: "step-start" }];
4139
+ if (finalContent) {
4140
+ finalParts.push({ type: "text", text: finalContent });
4141
+ }
4142
+ for (const inv of toolInvocationsAccumulated) {
4143
+ finalParts.push({ type: "tool-invocation", toolInvocation: inv });
4144
+ }
4084
4145
  setMessages(
4085
4146
  (prev) => prev.map(
4086
- (m) => m.id === assistantMessage.id ? { ...m, isStreaming: false, content: finalContent } : m
4147
+ (m) => m.id === assistantMessage.id ? {
4148
+ ...m,
4149
+ isStreaming: false,
4150
+ content: finalContent,
4151
+ toolInvocations: toolInvocationsAccumulated.length > 0 ? toolInvocationsAccumulated : void 0,
4152
+ parts: toolInvocationsAccumulated.length > 0 ? finalParts : void 0
4153
+ } : m
4087
4154
  )
4088
4155
  );
4089
4156
  onResponse?.(finalContent);
@@ -4145,7 +4212,14 @@ function HustleChat({
4145
4212
  setIsStreaming(true);
4146
4213
  setCurrentToolCalls([]);
4147
4214
  try {
4148
- const chatMessages = messages.filter((m) => !m.isStreaming).map((m) => ({ role: m.role, content: m.content }));
4215
+ const chatMessages = messages.filter((m) => !m.isStreaming).map((m) => {
4216
+ const msg = { role: m.role, content: m.content };
4217
+ if (m.role === "assistant" && m.toolInvocations && m.toolInvocations.length > 0) {
4218
+ msg.toolInvocations = m.toolInvocations;
4219
+ msg.parts = m.parts;
4220
+ }
4221
+ return msg;
4222
+ });
4149
4223
  chatMessages.push({ role: "user", content });
4150
4224
  const stream = chatStream({
4151
4225
  messages: chatMessages,
@@ -4155,6 +4229,9 @@ function HustleChat({
4155
4229
  setAttachments([]);
4156
4230
  let fullContent = "";
4157
4231
  const toolCallsAccumulated = [];
4232
+ const toolInvocationsAccumulated = [];
4233
+ const partsAccumulated = [{ type: "step-start" }];
4234
+ let stepCounter = 0;
4158
4235
  for await (const chunk of stream) {
4159
4236
  if (chunk.type === "text") {
4160
4237
  fullContent += chunk.value;
@@ -4166,22 +4243,79 @@ function HustleChat({
4166
4243
  } else if (chunk.type === "tool_call") {
4167
4244
  const toolCall = chunk.value;
4168
4245
  toolCallsAccumulated.push(toolCall);
4246
+ const invocation = {
4247
+ state: "call",
4248
+ step: stepCounter++,
4249
+ toolCallId: toolCall.toolCallId,
4250
+ toolName: toolCall.toolName,
4251
+ args: toolCall.args
4252
+ };
4253
+ toolInvocationsAccumulated.push(invocation);
4254
+ partsAccumulated.push({ type: "tool-invocation", toolInvocation: invocation });
4169
4255
  setCurrentToolCalls([...toolCallsAccumulated]);
4170
4256
  setMessages(
4171
4257
  (prev) => prev.map(
4172
- (m) => m.id === assistantMessage.id ? { ...m, toolCalls: [...toolCallsAccumulated] } : m
4258
+ (m) => m.id === assistantMessage.id ? {
4259
+ ...m,
4260
+ toolCalls: [...toolCallsAccumulated],
4261
+ toolInvocations: [...toolInvocationsAccumulated],
4262
+ parts: [...partsAccumulated]
4263
+ } : m
4173
4264
  )
4174
4265
  );
4175
4266
  onToolCall?.(toolCall);
4267
+ } else if (chunk.type === "tool_result") {
4268
+ const toolResult = chunk.value;
4269
+ const invocationIndex = toolInvocationsAccumulated.findIndex(
4270
+ (inv) => inv.toolCallId === toolResult.toolCallId
4271
+ );
4272
+ if (invocationIndex !== -1) {
4273
+ toolInvocationsAccumulated[invocationIndex] = {
4274
+ ...toolInvocationsAccumulated[invocationIndex],
4275
+ state: "result",
4276
+ result: toolResult.result
4277
+ };
4278
+ const partIndex = partsAccumulated.findIndex(
4279
+ (p) => p.type === "tool-invocation" && p.toolInvocation.toolCallId === toolResult.toolCallId
4280
+ );
4281
+ if (partIndex !== -1) {
4282
+ partsAccumulated[partIndex] = {
4283
+ type: "tool-invocation",
4284
+ toolInvocation: toolInvocationsAccumulated[invocationIndex]
4285
+ };
4286
+ }
4287
+ }
4288
+ setMessages(
4289
+ (prev) => prev.map(
4290
+ (m) => m.id === assistantMessage.id ? {
4291
+ ...m,
4292
+ toolInvocations: [...toolInvocationsAccumulated],
4293
+ parts: [...partsAccumulated]
4294
+ } : m
4295
+ )
4296
+ );
4176
4297
  } else if (chunk.type === "error") {
4177
4298
  console.error("Stream error:", chunk.value);
4178
4299
  }
4179
4300
  }
4180
4301
  const processedResponse = await stream.response;
4181
4302
  const finalContent = processedResponse?.content || fullContent || "(No response)";
4303
+ const finalParts = [{ type: "step-start" }];
4304
+ if (finalContent) {
4305
+ finalParts.push({ type: "text", text: finalContent });
4306
+ }
4307
+ for (const inv of toolInvocationsAccumulated) {
4308
+ finalParts.push({ type: "tool-invocation", toolInvocation: inv });
4309
+ }
4182
4310
  setMessages(
4183
4311
  (prev) => prev.map(
4184
- (m) => m.id === assistantMessage.id ? { ...m, isStreaming: false, content: finalContent } : m
4312
+ (m) => m.id === assistantMessage.id ? {
4313
+ ...m,
4314
+ isStreaming: false,
4315
+ content: finalContent,
4316
+ toolInvocations: toolInvocationsAccumulated.length > 0 ? toolInvocationsAccumulated : void 0,
4317
+ parts: toolInvocationsAccumulated.length > 0 ? finalParts : void 0
4318
+ } : m
4185
4319
  )
4186
4320
  );
4187
4321
  onResponse?.(finalContent);