@mastra/client-js 0.10.17-alpha.2 → 0.10.17-alpha.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @mastra/client-js@0.10.17-alpha.2 build /home/runner/work/mastra/mastra/client-sdks/client-js
2
+ > @mastra/client-js@0.10.17-alpha.4 build /home/runner/work/mastra/mastra/client-sdks/client-js
3
3
  > tsup src/index.ts --format esm,cjs --dts --clean --treeshake=smallest --splitting
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -9,11 +9,11 @@
9
9
  CLI Cleaning output folder
10
10
  ESM Build start
11
11
  CJS Build start
12
- ESM dist/index.js 74.97 KB
13
- ESM ⚡️ Build success in 1712ms
14
- CJS dist/index.cjs 75.29 KB
15
- CJS ⚡️ Build success in 1713ms
12
+ CJS dist/index.cjs 75.52 KB
13
+ CJS ⚡️ Build success in 2034ms
14
+ ESM dist/index.js 75.21 KB
15
+ ESM ⚡️ Build success in 2036ms
16
16
  DTS Build start
17
- DTS ⚡️ Build success in 16680ms
18
- DTS dist/index.d.ts 47.13 KB
19
- DTS dist/index.d.cts 47.13 KB
17
+ DTS ⚡️ Build success in 18390ms
18
+ DTS dist/index.d.ts 47.19 KB
19
+ DTS dist/index.d.cts 47.19 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @mastra/client-js
2
2
 
3
+ ## 0.10.17-alpha.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [ad0a58b]
8
+ - @mastra/core@0.12.0-alpha.4
9
+
10
+ ## 0.10.17-alpha.3
11
+
12
+ ### Patch Changes
13
+
14
+ - 9802f42: Added types and tests to ensure client-js and hono endpoints can save memory messages where the input is either a v1 or v2 mastra message
15
+ - 1ac8f6b: deduplicate message
16
+ - @mastra/core@0.12.0-alpha.3
17
+
3
18
  ## 0.10.17-alpha.2
4
19
 
5
20
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -741,7 +741,12 @@ var Agent = class extends BaseResource {
741
741
  this.processChatResponse({
742
742
  stream: streamForProcessing,
743
743
  update: ({ message }) => {
744
- messages.push(message);
744
+ const existingIndex = messages.findIndex((m) => m.id === message.id);
745
+ if (existingIndex !== -1) {
746
+ messages[existingIndex] = message;
747
+ } else {
748
+ messages.push(message);
749
+ }
745
750
  },
746
751
  onFinish: async ({ finishReason, message }) => {
747
752
  if (finishReason === "tool-calls") {
@@ -801,7 +806,7 @@ var Agent = class extends BaseResource {
801
806
  this.processStreamResponse(
802
807
  {
803
808
  ...processedParams,
804
- messages: [...messageArray, ...messages, lastMessage]
809
+ messages: [...messageArray, ...messages.filter((m) => m.id !== lastMessage.id), lastMessage]
805
810
  },
806
811
  writable
807
812
  ).catch((error) => {
package/dist/index.d.cts CHANGED
@@ -164,14 +164,14 @@ interface GetVectorIndexResponse {
164
164
  count: number;
165
165
  }
166
166
  interface SaveMessageToMemoryParams {
167
- messages: MastraMessageV1[];
167
+ messages: (MastraMessageV1 | MastraMessageV2)[];
168
168
  agentId: string;
169
169
  }
170
170
  interface SaveNetworkMessageToMemoryParams {
171
- messages: MastraMessageV1[];
171
+ messages: (MastraMessageV1 | MastraMessageV2)[];
172
172
  networkId: string;
173
173
  }
174
- type SaveMessageToMemoryResponse = MastraMessageV1[];
174
+ type SaveMessageToMemoryResponse = (MastraMessageV1 | MastraMessageV2)[];
175
175
  interface CreateMemoryThreadParams {
176
176
  title?: string;
177
177
  metadata?: Record<string, any>;
package/dist/index.d.ts CHANGED
@@ -164,14 +164,14 @@ interface GetVectorIndexResponse {
164
164
  count: number;
165
165
  }
166
166
  interface SaveMessageToMemoryParams {
167
- messages: MastraMessageV1[];
167
+ messages: (MastraMessageV1 | MastraMessageV2)[];
168
168
  agentId: string;
169
169
  }
170
170
  interface SaveNetworkMessageToMemoryParams {
171
- messages: MastraMessageV1[];
171
+ messages: (MastraMessageV1 | MastraMessageV2)[];
172
172
  networkId: string;
173
173
  }
174
- type SaveMessageToMemoryResponse = MastraMessageV1[];
174
+ type SaveMessageToMemoryResponse = (MastraMessageV1 | MastraMessageV2)[];
175
175
  interface CreateMemoryThreadParams {
176
176
  title?: string;
177
177
  metadata?: Record<string, any>;
package/dist/index.js CHANGED
@@ -735,7 +735,12 @@ var Agent = class extends BaseResource {
735
735
  this.processChatResponse({
736
736
  stream: streamForProcessing,
737
737
  update: ({ message }) => {
738
- messages.push(message);
738
+ const existingIndex = messages.findIndex((m) => m.id === message.id);
739
+ if (existingIndex !== -1) {
740
+ messages[existingIndex] = message;
741
+ } else {
742
+ messages.push(message);
743
+ }
739
744
  },
740
745
  onFinish: async ({ finishReason, message }) => {
741
746
  if (finishReason === "tool-calls") {
@@ -795,7 +800,7 @@ var Agent = class extends BaseResource {
795
800
  this.processStreamResponse(
796
801
  {
797
802
  ...processedParams,
798
- messages: [...messageArray, ...messages, lastMessage]
803
+ messages: [...messageArray, ...messages.filter((m) => m.id !== lastMessage.id), lastMessage]
799
804
  },
800
805
  writable
801
806
  ).catch((error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/client-js",
3
- "version": "0.10.17-alpha.2",
3
+ "version": "0.10.17-alpha.4",
4
4
  "description": "The official TypeScript library for the Mastra Client API",
5
5
  "author": "",
6
6
  "type": "module",
@@ -34,7 +34,7 @@
34
34
  "rxjs": "7.8.1",
35
35
  "zod": "^3.25.67",
36
36
  "zod-to-json-schema": "^3.24.5",
37
- "@mastra/core": "0.12.0-alpha.2"
37
+ "@mastra/core": "0.12.0-alpha.4"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "zod": "^3.0.0"
package/src/index.test.ts CHANGED
@@ -310,6 +310,114 @@ describe('MastraClient Resources', () => {
310
310
  }
311
311
  });
312
312
 
313
+ it('should stream responses with tool calls', async () => {
314
+ const firstMockChunk = `0:"test "
315
+ 0:"response"
316
+ 9:{"toolCallId":"tool1","toolName":"testTool","args":{"arg1":"value1"}}
317
+ e:{"finishReason":"tool-calls","usage":{"promptTokens":1,"completionTokens":1},"isContinued":false}
318
+ d:{"finishReason":"tool-calls","usage":{"promptTokens":2,"completionTokens":2}}
319
+ `;
320
+
321
+ const secondMockChunk = `0:"final response"
322
+ e:{"finishReason":"stop","usage":{"promptTokens":2,"completionTokens":2},"isContinued":false}
323
+ d:{"finishReason":"stop","usage":{"promptTokens":2,"completionTokens":2}}
324
+ `;
325
+
326
+ const firstResponseBody = new ReadableStream({
327
+ start(controller) {
328
+ controller.enqueue(new TextEncoder().encode(firstMockChunk));
329
+ controller.close();
330
+ },
331
+ });
332
+
333
+ const secondResponseBody = new ReadableStream({
334
+ start(controller) {
335
+ controller.enqueue(new TextEncoder().encode(secondMockChunk));
336
+ controller.close();
337
+ },
338
+ });
339
+
340
+ (global.fetch as any)
341
+ .mockResolvedValueOnce(
342
+ new Response(firstResponseBody, {
343
+ status: 200,
344
+ headers: new Headers({ 'Content-Type': 'text/event-stream' }),
345
+ }),
346
+ )
347
+ .mockResolvedValueOnce(
348
+ new Response(secondResponseBody, {
349
+ status: 200,
350
+ headers: new Headers({ 'Content-Type': 'text/event-stream' }),
351
+ }),
352
+ );
353
+
354
+ const response = await agent.stream({
355
+ messages: [
356
+ {
357
+ role: 'user',
358
+ content: 'test',
359
+ },
360
+ ],
361
+ clientTools: {
362
+ testTool: {
363
+ id: 'testTool',
364
+ description: 'Test Tool',
365
+ inputSchema: {
366
+ type: 'object',
367
+ properties: {
368
+ arg1: { type: 'string' },
369
+ },
370
+ },
371
+ execute: async () => {
372
+ return 'test result';
373
+ },
374
+ },
375
+ },
376
+ });
377
+
378
+ expect(response.body).toBeInstanceOf(ReadableStream);
379
+ const reader = response?.body?.getReader();
380
+ expect(reader).toBeDefined();
381
+
382
+ let output = '';
383
+ if (reader) {
384
+ while (true) {
385
+ const { value, done } = await reader.read();
386
+ if (done) break;
387
+ output += new TextDecoder().decode(value);
388
+ }
389
+ }
390
+
391
+ expect(global.fetch).toHaveBeenCalledTimes(2);
392
+
393
+ const [secondUrl, secondConfig] = (global.fetch as any).mock.calls[1];
394
+ expect(secondUrl).toBe(`${clientOptions.baseUrl}/api/agents/test-agent/stream`);
395
+
396
+ const secondRequestBody = JSON.parse(secondConfig.body);
397
+ expect(secondRequestBody.messages).toHaveLength(2);
398
+ expect(secondRequestBody.messages[0].content).toBe('test');
399
+ expect(secondRequestBody.messages[1].content).toBe('test response');
400
+ expect(secondRequestBody.messages[1].parts).toEqual([
401
+ {
402
+ type: 'text',
403
+ text: 'test response',
404
+ },
405
+ {
406
+ type: 'tool-invocation',
407
+ toolInvocation: {
408
+ state: 'result',
409
+ step: 0,
410
+ toolCallId: 'tool1',
411
+ toolName: 'testTool',
412
+ args: {
413
+ arg1: 'value1',
414
+ },
415
+ result: 'test result',
416
+ },
417
+ },
418
+ ]);
419
+ });
420
+
313
421
  it('should get agent tool', async () => {
314
422
  const mockResponse = {
315
423
  id: 'tool1',
@@ -632,7 +632,13 @@ export class Agent extends BaseResource {
632
632
  this.processChatResponse({
633
633
  stream: streamForProcessing,
634
634
  update: ({ message }) => {
635
- messages.push(message);
635
+ const existingIndex = messages.findIndex(m => m.id === message.id);
636
+
637
+ if (existingIndex !== -1) {
638
+ messages[existingIndex] = message;
639
+ } else {
640
+ messages.push(message);
641
+ }
636
642
  },
637
643
  onFinish: async ({ finishReason, message }) => {
638
644
  if (finishReason === 'tool-calls') {
@@ -711,7 +717,7 @@ export class Agent extends BaseResource {
711
717
  this.processStreamResponse(
712
718
  {
713
719
  ...processedParams,
714
- messages: [...messageArray, ...messages, lastMessage],
720
+ messages: [...messageArray, ...messages.filter(m => m.id !== lastMessage.id), lastMessage],
715
721
  },
716
722
  writable,
717
723
  ).catch(error => {
package/src/types.ts CHANGED
@@ -198,16 +198,16 @@ export interface GetVectorIndexResponse {
198
198
  }
199
199
 
200
200
  export interface SaveMessageToMemoryParams {
201
- messages: MastraMessageV1[];
201
+ messages: (MastraMessageV1 | MastraMessageV2)[];
202
202
  agentId: string;
203
203
  }
204
204
 
205
205
  export interface SaveNetworkMessageToMemoryParams {
206
- messages: MastraMessageV1[];
206
+ messages: (MastraMessageV1 | MastraMessageV2)[];
207
207
  networkId: string;
208
208
  }
209
209
 
210
- export type SaveMessageToMemoryResponse = MastraMessageV1[];
210
+ export type SaveMessageToMemoryResponse = (MastraMessageV1 | MastraMessageV2)[];
211
211
 
212
212
  export interface CreateMemoryThreadParams {
213
213
  title?: string;
@@ -0,0 +1,180 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import type { MastraMessageV1, MastraMessageV2 } from '@mastra/core';
3
+ import { MastraClient } from './client';
4
+
5
+ describe('V2 Message Format Support', () => {
6
+ let client: MastraClient;
7
+ const agentId = 'test-agent';
8
+
9
+ beforeEach(() => {
10
+ global.fetch = vi.fn();
11
+ client = new MastraClient({
12
+ baseUrl: 'http://localhost:3000',
13
+ });
14
+ });
15
+
16
+ it('should send v1 messages successfully', async () => {
17
+ const v1Messages: MastraMessageV1[] = [
18
+ {
19
+ id: 'msg-v1-1',
20
+ role: 'user',
21
+ content: 'Hello from v1!',
22
+ type: 'text',
23
+ createdAt: new Date(),
24
+ threadId: 'thread-123',
25
+ resourceId: 'resource-123',
26
+ },
27
+ ];
28
+
29
+ (global.fetch as any).mockResolvedValueOnce({
30
+ ok: true,
31
+ json: async () => v1Messages,
32
+ });
33
+
34
+ const result = await client.saveMessageToMemory({
35
+ agentId,
36
+ messages: v1Messages,
37
+ });
38
+
39
+ expect(result).toEqual(v1Messages);
40
+ expect(global.fetch).toHaveBeenCalledWith(
41
+ expect.stringContaining('/api/memory/save-messages'),
42
+ expect.objectContaining({
43
+ method: 'POST',
44
+ body: JSON.stringify({ agentId, messages: v1Messages }),
45
+ }),
46
+ );
47
+ });
48
+
49
+ it('should send v2 messages successfully', async () => {
50
+ const v2Messages: MastraMessageV2[] = [
51
+ {
52
+ id: 'msg-v2-1',
53
+ role: 'assistant',
54
+ createdAt: new Date(),
55
+ threadId: 'thread-123',
56
+ resourceId: 'resource-123',
57
+ content: {
58
+ format: 2,
59
+ parts: [{ type: 'text', text: 'Hello from v2!' }],
60
+ content: 'Hello from v2!',
61
+ },
62
+ },
63
+ ];
64
+
65
+ (global.fetch as any).mockResolvedValueOnce({
66
+ ok: true,
67
+ json: async () => v2Messages,
68
+ });
69
+
70
+ const result = await client.saveMessageToMemory({
71
+ agentId,
72
+ messages: v2Messages,
73
+ });
74
+
75
+ expect(result).toEqual(v2Messages);
76
+ expect(global.fetch).toHaveBeenCalledWith(
77
+ expect.stringContaining('/api/memory/save-messages'),
78
+ expect.objectContaining({
79
+ method: 'POST',
80
+ body: JSON.stringify({ agentId, messages: v2Messages }),
81
+ }),
82
+ );
83
+ });
84
+
85
+ it('should send mixed v1 and v2 messages successfully', async () => {
86
+ const mixedMessages: (MastraMessageV1 | MastraMessageV2)[] = [
87
+ {
88
+ id: 'msg-v1-1',
89
+ role: 'user',
90
+ content: 'Question in v1 format',
91
+ type: 'text',
92
+ createdAt: new Date(),
93
+ threadId: 'thread-123',
94
+ resourceId: 'resource-123',
95
+ },
96
+ {
97
+ id: 'msg-v2-1',
98
+ role: 'assistant',
99
+ createdAt: new Date(),
100
+ threadId: 'thread-123',
101
+ resourceId: 'resource-123',
102
+ content: {
103
+ format: 2,
104
+ parts: [
105
+ { type: 'text', text: 'Answer in v2 format' },
106
+ {
107
+ type: 'tool-invocation',
108
+ toolInvocation: {
109
+ state: 'result' as const,
110
+ toolCallId: 'call-123',
111
+ toolName: 'calculator',
112
+ args: { a: 1, b: 2 },
113
+ result: '3',
114
+ },
115
+ },
116
+ ],
117
+ toolInvocations: [
118
+ {
119
+ state: 'result' as const,
120
+ toolCallId: 'call-123',
121
+ toolName: 'calculator',
122
+ args: { a: 1, b: 2 },
123
+ result: '3',
124
+ },
125
+ ],
126
+ },
127
+ },
128
+ ];
129
+
130
+ (global.fetch as any).mockResolvedValueOnce({
131
+ ok: true,
132
+ json: async () => mixedMessages,
133
+ });
134
+
135
+ const result = await client.saveMessageToMemory({
136
+ agentId,
137
+ messages: mixedMessages,
138
+ });
139
+
140
+ expect(result).toEqual(mixedMessages);
141
+ expect(global.fetch).toHaveBeenCalledWith(
142
+ expect.stringContaining('/api/memory/save-messages'),
143
+ expect.objectContaining({
144
+ method: 'POST',
145
+ body: JSON.stringify({ agentId, messages: mixedMessages }),
146
+ }),
147
+ );
148
+ });
149
+
150
+ it('should handle v2 messages with attachments', async () => {
151
+ const v2MessageWithAttachments: MastraMessageV2 = {
152
+ id: 'msg-v2-att',
153
+ role: 'user',
154
+ createdAt: new Date(),
155
+ threadId: 'thread-123',
156
+ resourceId: 'resource-123',
157
+ content: {
158
+ format: 2,
159
+ parts: [
160
+ { type: 'text', text: 'Check out this image:' },
161
+ { type: 'file', data: 'data:image/png;base64,iVBORw0...', mimeType: 'image/png' },
162
+ ],
163
+ experimental_attachments: [{ url: 'data:image/png;base64,iVBORw0...', contentType: 'image/png' }],
164
+ },
165
+ };
166
+
167
+ (global.fetch as any).mockResolvedValueOnce({
168
+ ok: true,
169
+ json: async () => [v2MessageWithAttachments],
170
+ });
171
+
172
+ const result = await client.saveMessageToMemory({
173
+ agentId,
174
+ messages: [v2MessageWithAttachments],
175
+ });
176
+
177
+ expect(result).toHaveLength(1);
178
+ expect(result[0]).toEqual(v2MessageWithAttachments);
179
+ });
180
+ });