@mastra/client-js 1.32.1-alpha.2 → 1.33.0-alpha.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/CHANGELOG.md +53 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +171 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +171 -30
- package/dist/index.js.map +1 -1
- package/dist/resources/agent-controller.d.ts +55 -51
- package/dist/resources/agent-controller.d.ts.map +1 -1
- package/dist/route-types.generated.d.ts +29 -3
- package/dist/route-types.generated.d.ts.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# @mastra/client-js
|
|
2
2
|
|
|
3
|
+
## 1.33.0-alpha.3
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added a `requestContext` option to agent controller session methods in @mastra/client-js. You can now pass custom request context to `sendMessage`, `steer`, `followUp`, `approveTool`, and `respondToToolSuspension`, matching what `agent.generate()` already supports. The context is merged into the run's request context on the server, so it reaches dynamic instructions and tools. ([#19531](https://github.com/mastra-ai/mastra/pull/19531))
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const session = client.getAgentController('code').session('user-1');
|
|
11
|
+
await session.sendMessage('hello', { requestContext: { userId: 'user-1', tier: 'pro' } });
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
- Added reconnect support to `AgentControllerSession.subscribe()` so SSE subscriptions recover after proxy timeouts or transport errors. Fixes #19202. ([#19560](https://github.com/mastra-ai/mastra/pull/19560))
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
await session.subscribe({
|
|
18
|
+
onEvent: event => {
|
|
19
|
+
/* handle event */
|
|
20
|
+
},
|
|
21
|
+
reconnect: true,
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- Fixed AgentController live events and thread history returning serialized message timestamps instead of Date values. ([#18783](https://github.com/mastra-ai/mastra/pull/18783))
|
|
28
|
+
|
|
29
|
+
- Fixed AgentControllerSession.subscribe() so it resolves only after the stream is established and rejects when it cannot connect (leaving no background retry loop). Reconnect now applies only after an established stream drops, backs off exponentially, and fires a new onReconnect callback on each re-established stream so consumers can re-sync missed events: ([#19580](https://github.com/mastra-ai/mastra/pull/19580))
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
const subscription = await session.subscribe({
|
|
33
|
+
onEvent: event => applyEvent(event),
|
|
34
|
+
onError: error => showDisconnected(error),
|
|
35
|
+
reconnect: { maxRetries: 5, delayMs: 1000, maxDelayMs: 30_000 },
|
|
36
|
+
onReconnect: async () => {
|
|
37
|
+
// Events emitted while disconnected are not replayed — re-sync state.
|
|
38
|
+
applyState(await session.state());
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- Added stable metrics and logs capability reporting for observability storage. The system packages response now includes `observabilityStorageCapabilities` with `metrics` and `logs` flags, enabling capability-based detection that is resilient to bundler-generated constructor name changes. ([#19305](https://github.com/mastra-ai/mastra/pull/19305))
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
const packages = await client.getSystemPackages();
|
|
47
|
+
console.log(packages.observabilityStorageCapabilities?.metrics); // true
|
|
48
|
+
console.log(packages.observabilityStorageCapabilities?.logs); // true
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Studio now uses the capability response instead of relying on constructor names, with a fallback for older servers.
|
|
52
|
+
|
|
53
|
+
- Updated dependencies [[`1426af2`](https://github.com/mastra-ai/mastra/commit/1426af24975879c000d13ac75673f630fcc970c1), [`975295d`](https://github.com/mastra-ai/mastra/commit/975295d418552f0d46a59edfef4c3ee555f9930a), [`85e4fb5`](https://github.com/mastra-ai/mastra/commit/85e4fb50087a81c74df3a762f53b56373db0b912), [`ef03c0c`](https://github.com/mastra-ai/mastra/commit/ef03c0cfc62367a458e4cc56462e2148b35681c5), [`4fb4d88`](https://github.com/mastra-ai/mastra/commit/4fb4d881bc107acee13890ad4d78661016c510ed), [`4eba27a`](https://github.com/mastra-ai/mastra/commit/4eba27adcf60f991df0e62f94b3e75b4e67f3b4b), [`c701be3`](https://github.com/mastra-ai/mastra/commit/c701be32d7d9aa94a66da8c6cc38dcac6856f464)]:
|
|
54
|
+
- @mastra/core@1.52.0-alpha.3
|
|
55
|
+
|
|
3
56
|
## 1.32.1-alpha.2
|
|
4
57
|
|
|
5
58
|
### Patch Changes
|
package/dist/docs/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: mastra-client-js
|
|
|
3
3
|
description: Documentation for @mastra/client-js. Use when working with @mastra/client-js APIs, configuration, or implementation.
|
|
4
4
|
metadata:
|
|
5
5
|
package: "@mastra/client-js"
|
|
6
|
-
version: "1.
|
|
6
|
+
version: "1.33.0-alpha.3"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## When to use
|
package/dist/index.cjs
CHANGED
|
@@ -6144,6 +6144,19 @@ var Channels = class extends BaseResource {
|
|
|
6144
6144
|
};
|
|
6145
6145
|
|
|
6146
6146
|
// src/resources/agent-controller.ts
|
|
6147
|
+
function hydrateMessage(message) {
|
|
6148
|
+
return {
|
|
6149
|
+
...message,
|
|
6150
|
+
createdAt: message.createdAt instanceof Date ? message.createdAt : new Date(message.createdAt)
|
|
6151
|
+
};
|
|
6152
|
+
}
|
|
6153
|
+
function hydrateEventMessage(event) {
|
|
6154
|
+
if (event.type !== "message_start" && event.type !== "message_update" && event.type !== "message_end") return event;
|
|
6155
|
+
return {
|
|
6156
|
+
...event,
|
|
6157
|
+
message: hydrateMessage(event.message)
|
|
6158
|
+
};
|
|
6159
|
+
}
|
|
6147
6160
|
var AgentControllerSession = class extends BaseResource {
|
|
6148
6161
|
constructor(options, controllerId, resourceId, scope) {
|
|
6149
6162
|
super(options);
|
|
@@ -6179,46 +6192,158 @@ var AgentControllerSession = class extends BaseResource {
|
|
|
6179
6192
|
/**
|
|
6180
6193
|
* Subscribe to this session's event stream (SSE). The assistant's reply to a
|
|
6181
6194
|
* message arrives here as `message_*` events, not on the sendMessage call.
|
|
6195
|
+
*
|
|
6196
|
+
* The returned promise resolves once the stream is actually established and
|
|
6197
|
+
* rejects when it cannot be. A rejected subscribe leaves nothing running —
|
|
6198
|
+
* `reconnect` governs only re-establishment after an established stream
|
|
6199
|
+
* drops, so callers that want connect-time retry can loop around
|
|
6200
|
+
* `subscribe()` themselves and keep cancellation control.
|
|
6182
6201
|
*/
|
|
6183
6202
|
async subscribe(options) {
|
|
6184
|
-
const
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6203
|
+
const finiteOr = (value, fallback) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
6204
|
+
const retriesOr = (value, fallback) => value === Infinity || typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
6205
|
+
const reconnectOptions = options.reconnect === true ? { maxRetries: Infinity, delayMs: 1e3, maxDelayMs: 3e4 } : options.reconnect ? {
|
|
6206
|
+
maxRetries: retriesOr(options.reconnect.maxRetries, Infinity),
|
|
6207
|
+
delayMs: finiteOr(options.reconnect.delayMs, 1e3),
|
|
6208
|
+
maxDelayMs: finiteOr(options.reconnect.maxDelayMs, 3e4)
|
|
6209
|
+
} : null;
|
|
6210
|
+
const backoffDelay = (attempt) => reconnectOptions ? Math.min(reconnectOptions.delayMs * 2 ** (attempt - 1), reconnectOptions.maxDelayMs) : 0;
|
|
6190
6211
|
let cancelled = false;
|
|
6191
|
-
let
|
|
6192
|
-
|
|
6212
|
+
let currentReader = null;
|
|
6213
|
+
let reconnectTimer;
|
|
6214
|
+
let delayResolve;
|
|
6215
|
+
const settleDelay = () => {
|
|
6216
|
+
if (reconnectTimer) {
|
|
6217
|
+
clearTimeout(reconnectTimer);
|
|
6218
|
+
reconnectTimer = void 0;
|
|
6219
|
+
}
|
|
6220
|
+
const resolve = delayResolve;
|
|
6221
|
+
delayResolve = void 0;
|
|
6222
|
+
resolve?.();
|
|
6223
|
+
};
|
|
6224
|
+
const delay = (ms) => new Promise((resolve) => {
|
|
6225
|
+
delayResolve = resolve;
|
|
6226
|
+
reconnectTimer = setTimeout(settleDelay, ms);
|
|
6227
|
+
});
|
|
6228
|
+
const requestStream = async () => {
|
|
6229
|
+
const response = await this.request(this.url(`${this.base()}/stream`), { stream: true });
|
|
6230
|
+
if (!response.body) {
|
|
6231
|
+
throw new Error("No response body for agent controller session stream");
|
|
6232
|
+
}
|
|
6233
|
+
return response;
|
|
6234
|
+
};
|
|
6235
|
+
const streamEndedError = () => new Error("Agent controller session stream ended unexpectedly");
|
|
6236
|
+
const findFrameSeparator = (text) => {
|
|
6237
|
+
const candidates = [
|
|
6238
|
+
{ index: text.indexOf("\r\n\r\n"), length: 4 },
|
|
6239
|
+
{ index: text.indexOf("\n\n"), length: 2 },
|
|
6240
|
+
{ index: text.indexOf("\r\r"), length: 2 }
|
|
6241
|
+
].filter((candidate) => candidate.index !== -1);
|
|
6242
|
+
if (candidates.length === 0) return null;
|
|
6243
|
+
return candidates.reduce((earliest, candidate) => candidate.index < earliest.index ? candidate : earliest);
|
|
6244
|
+
};
|
|
6245
|
+
const pump = async (response) => {
|
|
6246
|
+
const reader = response.body.getReader();
|
|
6247
|
+
currentReader = reader;
|
|
6248
|
+
const decoder = new TextDecoder();
|
|
6249
|
+
let buffer = "";
|
|
6193
6250
|
try {
|
|
6194
6251
|
while (!cancelled) {
|
|
6195
6252
|
const { done, value } = await reader.read();
|
|
6196
|
-
if (done)
|
|
6253
|
+
if (done) return cancelled ? { kind: "cancelled" } : { kind: "done" };
|
|
6197
6254
|
buffer += decoder.decode(value, { stream: true });
|
|
6198
|
-
let
|
|
6199
|
-
while ((
|
|
6200
|
-
const frame = buffer.slice(0,
|
|
6201
|
-
buffer = buffer.slice(
|
|
6202
|
-
for (const line of frame.split(
|
|
6255
|
+
let separator;
|
|
6256
|
+
while ((separator = findFrameSeparator(buffer)) !== null) {
|
|
6257
|
+
const frame = buffer.slice(0, separator.index);
|
|
6258
|
+
buffer = buffer.slice(separator.index + separator.length);
|
|
6259
|
+
for (const line of frame.split(/\r\n|\n|\r/)) {
|
|
6203
6260
|
if (!line.startsWith("data:")) continue;
|
|
6204
6261
|
const data = line.slice(5).trim();
|
|
6205
6262
|
if (!data) continue;
|
|
6263
|
+
let event;
|
|
6206
6264
|
try {
|
|
6207
|
-
|
|
6265
|
+
event = hydrateEventMessage(JSON.parse(data));
|
|
6208
6266
|
} catch {
|
|
6267
|
+
continue;
|
|
6268
|
+
}
|
|
6269
|
+
try {
|
|
6270
|
+
options.onEvent(event);
|
|
6271
|
+
} catch (cause) {
|
|
6272
|
+
if (!cancelled) safeOnError(cause);
|
|
6273
|
+
return { kind: "consumer_error" };
|
|
6209
6274
|
}
|
|
6210
6275
|
}
|
|
6211
6276
|
}
|
|
6212
6277
|
}
|
|
6278
|
+
return { kind: "cancelled" };
|
|
6279
|
+
} catch (error) {
|
|
6280
|
+
return cancelled ? { kind: "cancelled" } : { kind: "transport_error", error };
|
|
6281
|
+
} finally {
|
|
6282
|
+
if (currentReader === reader) currentReader = null;
|
|
6283
|
+
void reader.cancel().catch(() => {
|
|
6284
|
+
});
|
|
6285
|
+
}
|
|
6286
|
+
};
|
|
6287
|
+
const safeOnError = (error) => {
|
|
6288
|
+
try {
|
|
6289
|
+
options.onError?.(error);
|
|
6290
|
+
} catch {
|
|
6291
|
+
}
|
|
6292
|
+
};
|
|
6293
|
+
const reportTerminalError = (result) => {
|
|
6294
|
+
safeOnError(result.kind === "transport_error" ? result.error : streamEndedError());
|
|
6295
|
+
};
|
|
6296
|
+
const pumpSafe = async (response) => {
|
|
6297
|
+
try {
|
|
6298
|
+
return await pump(response);
|
|
6213
6299
|
} catch (error) {
|
|
6214
|
-
|
|
6300
|
+
return cancelled ? { kind: "cancelled" } : { kind: "transport_error", error };
|
|
6301
|
+
}
|
|
6302
|
+
};
|
|
6303
|
+
const firstResponse = await requestStream();
|
|
6304
|
+
const run = async (initial) => {
|
|
6305
|
+
let response = initial;
|
|
6306
|
+
while (!cancelled) {
|
|
6307
|
+
let result = await pumpSafe(response);
|
|
6308
|
+
if (cancelled || result.kind === "cancelled" || result.kind === "consumer_error") return;
|
|
6309
|
+
if (!reconnectOptions) {
|
|
6310
|
+
reportTerminalError(result);
|
|
6311
|
+
return;
|
|
6312
|
+
}
|
|
6313
|
+
let attempts = 0;
|
|
6314
|
+
let reconnectedResponse;
|
|
6315
|
+
while (!reconnectedResponse) {
|
|
6316
|
+
if (attempts >= reconnectOptions.maxRetries) {
|
|
6317
|
+
reportTerminalError(result);
|
|
6318
|
+
return;
|
|
6319
|
+
}
|
|
6320
|
+
attempts++;
|
|
6321
|
+
await delay(backoffDelay(attempts));
|
|
6322
|
+
if (cancelled) return;
|
|
6323
|
+
try {
|
|
6324
|
+
reconnectedResponse = await requestStream();
|
|
6325
|
+
} catch (error) {
|
|
6326
|
+
result = { kind: "transport_error", error };
|
|
6327
|
+
}
|
|
6328
|
+
}
|
|
6329
|
+
if (cancelled) {
|
|
6330
|
+
void reconnectedResponse.body?.cancel().catch(() => {
|
|
6331
|
+
});
|
|
6332
|
+
return;
|
|
6333
|
+
}
|
|
6334
|
+
response = reconnectedResponse;
|
|
6335
|
+
try {
|
|
6336
|
+
options.onReconnect?.();
|
|
6337
|
+
} catch {
|
|
6338
|
+
}
|
|
6215
6339
|
}
|
|
6216
6340
|
};
|
|
6217
|
-
void
|
|
6341
|
+
void run(firstResponse);
|
|
6218
6342
|
return {
|
|
6219
6343
|
unsubscribe: () => {
|
|
6220
6344
|
cancelled = true;
|
|
6221
|
-
|
|
6345
|
+
settleDelay();
|
|
6346
|
+
void currentReader?.cancel().catch(() => {
|
|
6222
6347
|
});
|
|
6223
6348
|
}
|
|
6224
6349
|
};
|
|
@@ -6227,12 +6352,18 @@ var AgentControllerSession = class extends BaseResource {
|
|
|
6227
6352
|
* Send a user message. The reply streams over `subscribe()`.
|
|
6228
6353
|
* Pass a structured message to attach files (e.g. pasted images) as base64-encoded data:
|
|
6229
6354
|
* `sendMessage({ content: 'What is in this image?', files })`.
|
|
6355
|
+
* Pass `options.requestContext` to merge custom context into the run's request context.
|
|
6230
6356
|
*/
|
|
6231
|
-
async sendMessage(message) {
|
|
6357
|
+
async sendMessage(message, options) {
|
|
6232
6358
|
const { content, files } = typeof message === "string" ? { content: message, files: void 0 } : message;
|
|
6359
|
+
const requestContext = parseClientRequestContext(options?.requestContext);
|
|
6233
6360
|
await this.request(this.url(`${this.base()}/messages`), {
|
|
6234
6361
|
method: "POST",
|
|
6235
|
-
body: {
|
|
6362
|
+
body: {
|
|
6363
|
+
message: content,
|
|
6364
|
+
...files?.length ? { files } : {},
|
|
6365
|
+
...requestContext ? { requestContext } : {}
|
|
6366
|
+
}
|
|
6236
6367
|
});
|
|
6237
6368
|
}
|
|
6238
6369
|
/** Abort the in-flight run for this session. */
|
|
@@ -6240,10 +6371,11 @@ var AgentControllerSession = class extends BaseResource {
|
|
|
6240
6371
|
await this.request(this.url(`${this.base()}/abort`), { method: "POST" });
|
|
6241
6372
|
}
|
|
6242
6373
|
/** Approve or decline a pending tool call (`tool_approval_required`). */
|
|
6243
|
-
async approveTool(toolCallId, approved) {
|
|
6374
|
+
async approveTool(toolCallId, approved, options) {
|
|
6375
|
+
const requestContext = parseClientRequestContext(options?.requestContext);
|
|
6244
6376
|
await this.request(this.url(`${this.base()}/tool-approval`), {
|
|
6245
6377
|
method: "POST",
|
|
6246
|
-
body: { toolCallId, approved }
|
|
6378
|
+
body: { toolCallId, approved, ...requestContext ? { requestContext } : {} }
|
|
6247
6379
|
});
|
|
6248
6380
|
}
|
|
6249
6381
|
/**
|
|
@@ -6251,15 +6383,20 @@ var AgentControllerSession = class extends BaseResource {
|
|
|
6251
6383
|
* depends on the tool: a string (or string[]) for `ask_user`, "Yes"/"No" for
|
|
6252
6384
|
* `request_access`, and a {@link PlanResume} for `submit_plan`.
|
|
6253
6385
|
*/
|
|
6254
|
-
async respondToToolSuspension(toolCallId, resumeData) {
|
|
6386
|
+
async respondToToolSuspension(toolCallId, resumeData, options) {
|
|
6387
|
+
const requestContext = parseClientRequestContext(options?.requestContext);
|
|
6255
6388
|
await this.request(this.url(`${this.base()}/tool-suspension`), {
|
|
6256
6389
|
method: "POST",
|
|
6257
|
-
body: { toolCallId, resumeData }
|
|
6390
|
+
body: { toolCallId, resumeData, ...requestContext ? { requestContext } : {} }
|
|
6258
6391
|
});
|
|
6259
6392
|
}
|
|
6260
6393
|
/** Inject a message into the in-flight run without starting a new turn. */
|
|
6261
|
-
async steer(message) {
|
|
6262
|
-
|
|
6394
|
+
async steer(message, options) {
|
|
6395
|
+
const requestContext = parseClientRequestContext(options?.requestContext);
|
|
6396
|
+
await this.request(this.url(`${this.base()}/steer`), {
|
|
6397
|
+
method: "POST",
|
|
6398
|
+
body: { message, ...requestContext ? { requestContext } : {} }
|
|
6399
|
+
});
|
|
6263
6400
|
}
|
|
6264
6401
|
/** Get the current mode, model, and thread (for initial UI hydration). */
|
|
6265
6402
|
state() {
|
|
@@ -6335,14 +6472,18 @@ var AgentControllerSession = class extends BaseResource {
|
|
|
6335
6472
|
const body = await this.request(
|
|
6336
6473
|
this.url(`${this.base()}/threads/${encodeURIComponent(threadId)}/messages${params}`)
|
|
6337
6474
|
);
|
|
6338
|
-
return body.messages;
|
|
6475
|
+
return body.messages.map(hydrateMessage);
|
|
6339
6476
|
}
|
|
6340
6477
|
/**
|
|
6341
6478
|
* Queue a follow-up message. If the session is idle it sends immediately;
|
|
6342
6479
|
* if a run is active it queues for after completion.
|
|
6343
6480
|
*/
|
|
6344
|
-
async followUp(message) {
|
|
6345
|
-
|
|
6481
|
+
async followUp(message, options) {
|
|
6482
|
+
const requestContext = parseClientRequestContext(options?.requestContext);
|
|
6483
|
+
await this.request(this.url(`${this.base()}/follow-up`), {
|
|
6484
|
+
method: "POST",
|
|
6485
|
+
body: { message, ...requestContext ? { requestContext } : {} }
|
|
6486
|
+
});
|
|
6346
6487
|
}
|
|
6347
6488
|
/** Get the observational memory record for this session's thread. */
|
|
6348
6489
|
async getOMRecord() {
|
|
@@ -6452,7 +6593,7 @@ var AgentController = class extends BaseResource {
|
|
|
6452
6593
|
}
|
|
6453
6594
|
};
|
|
6454
6595
|
function agentControllerMessageText(message) {
|
|
6455
|
-
return message.content.filter((
|
|
6596
|
+
return message.content.parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
|
|
6456
6597
|
}
|
|
6457
6598
|
|
|
6458
6599
|
// src/client.ts
|