@meshagent/meshagent 0.47.0 → 0.47.1

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/esm/agent.js CHANGED
@@ -511,10 +511,10 @@ class _RemoteToolkitWrapper {
511
511
  return false;
512
512
  }
513
513
  }
514
- async _sendToolCallResponseChunk(messageId, toolCallId, chunk) {
514
+ _sendToolCallResponseChunk(messageId, toolCallId, chunk) {
515
515
  const [header, payload] = unpackMessage(chunk.pack());
516
516
  try {
517
- await this.client.protocol.send("room.tool_call_response_chunk", packMessage({ tool_call_id: toolCallId, chunk: header }, payload.length > 0 ? payload : undefined), { id: messageId });
517
+ this.client.protocol.sendNowait("room.tool_call_response_chunk", packMessage({ tool_call_id: toolCallId, chunk: header }, payload.length > 0 ? payload : undefined), { id: messageId });
518
518
  return true;
519
519
  }
520
520
  catch (error) {
@@ -615,10 +615,10 @@ class _RemoteToolkitWrapper {
615
615
  return;
616
616
  for await (const chunk of output.stream) {
617
617
  this.toolkit.validateOutputContent(tool, chunk);
618
- if (!await this._sendToolCallResponseChunk(messageId, toolCallId, chunk))
618
+ if (!this._sendToolCallResponseChunk(messageId, toolCallId, chunk))
619
619
  return;
620
620
  }
621
- await this._sendToolCallResponseChunk(messageId, toolCallId, new ControlContent({ method: "close" }));
621
+ this._sendToolCallResponseChunk(messageId, toolCallId, new ControlContent({ method: "close" }));
622
622
  return;
623
623
  }
624
624
  throw new Error(`tool ${toolName} returned unsupported output`);
@@ -629,9 +629,9 @@ class _RemoteToolkitWrapper {
629
629
  return;
630
630
  }
631
631
  if (!(error instanceof InvalidToolDataException)) {
632
- await this._sendToolCallResponseChunk(messageId, toolCallId, new ErrorContent({ text: String(error) }));
632
+ this._sendToolCallResponseChunk(messageId, toolCallId, new ErrorContent({ text: String(error) }));
633
633
  }
634
- await this._sendToolCallResponseChunk(messageId, toolCallId, new ControlContent({
634
+ this._sendToolCallResponseChunk(messageId, toolCallId, new ControlContent({
635
635
  method: "close",
636
636
  statusCode: error instanceof InvalidToolDataException ? ControlCloseStatus.INVALID_DATA : undefined,
637
637
  message: error instanceof InvalidToolDataException ? error.message : undefined,
@@ -1,9 +1,10 @@
1
1
  import { ApiScope } from "./participant-token.js";
2
2
  export type ProjectRole = "member" | "admin" | "developer" | "room_creator" | "room_inventory" | "room_manager" | "session_inventory" | "agent_creator" | "agent_inventory" | "agent_manager" | "repository_creator" | "repository_inventory" | "repository_manager" | "feed_creator" | "feed_inventory" | "feed_manager" | "oauth_client_creator" | "oauth_client_inventory" | "oauth_client_manager" | "api_key_creator" | "api_key_inventory" | "api_key_manager" | "service_creator" | "service_inventory" | "service_manager" | "service_account_creator" | "service_account_inventory" | "service_account_manager" | "participant_token_creator" | "mailbox_creator" | "mailbox_inventory" | "mailbox_manager" | "route_creator" | "route_inventory" | "route_manager" | "scheduled_task_creator" | "scheduled_task_inventory" | "scheduled_task_manager" | "feed_subscription_creator" | "feed_subscription_inventory" | "feed_subscription_manager" | "llm_logger_creator" | "llm_logger_inventory" | "llm_logger_manager" | "llm_proxy_user" | "usage_reporter" | "billing_manager" | "group_manager";
3
3
  export type ResourceRole = "viewer" | "operator" | "developer" | "admin";
4
+ export type RoomRole = "site_user" | ResourceRole;
4
5
  export type FeedRole = "reader" | "subscriber" | "publisher" | "manager";
5
6
  export type SecretRole = "use_proxy";
6
- export type AccessRole = ProjectRole | ResourceRole | FeedRole | SecretRole | "list";
7
+ export type AccessRole = ProjectRole | RoomRole | FeedRole | SecretRole | "list";
7
8
  export type AccessSubjectType = "user" | "group" | "agent" | "service_account" | "userset";
8
9
  export type AccessResourceType = "project" | "room" | "agent" | "group" | "repository" | "feed" | "secret";
9
10
  export interface AccessSubject {
@@ -654,6 +654,7 @@ export class Meshagent {
654
654
  const parsedSubject = this.parseAccessSubject(data.subject);
655
655
  const parsedDirectRoles = Array.isArray(data.direct_roles)
656
656
  ? data.direct_roles.filter((item) => item === "member" ||
657
+ item === "site_user" ||
657
658
  item === "viewer" ||
658
659
  item === "operator" ||
659
660
  item === "developer" ||
@@ -1,8 +1,72 @@
1
1
  import { EventEmitter } from "./event-emitter.js";
2
2
  import { Participant, RemoteParticipant } from "./participant.js";
3
+ import { Content } from "./response.js";
3
4
  import { RoomClient } from "./room-client.js";
4
- import { RoomMessageEvent } from "./room-event.js";
5
+ import { RoomMessage, RoomMessageEvent } from "./room-event.js";
5
6
  type MessagePayload = Record<string, unknown>;
7
+ export type MessagingStreamEvent = {
8
+ kind: "message";
9
+ message: RoomMessage;
10
+ } | {
11
+ kind: "client_disconnected";
12
+ streamId: string;
13
+ participantId: string;
14
+ } | {
15
+ kind: "closed";
16
+ streamId: string;
17
+ reason: string;
18
+ message?: string;
19
+ };
20
+ declare class MessagingStreamInput {
21
+ private readonly items;
22
+ private signal;
23
+ private active;
24
+ private closed;
25
+ private error;
26
+ enqueue(content: Content, { wait }?: {
27
+ wait?: boolean;
28
+ }): Promise<void>;
29
+ close(error?: unknown): void;
30
+ fail(error: unknown): void;
31
+ stream(): AsyncIterable<Content>;
32
+ }
33
+ export declare class MessagingStream implements AsyncIterable<MessagingStreamEvent> {
34
+ readonly streamId: string;
35
+ readonly remoteParticipantId: string;
36
+ private readonly input;
37
+ private readonly output;
38
+ private readonly inputDone;
39
+ private readonly onClosed;
40
+ private readonly events;
41
+ private eventSignal;
42
+ private state;
43
+ constructor({ streamId, remoteParticipantId, input, output, inputClosed, onClosed, }: {
44
+ streamId: string;
45
+ remoteParticipantId: string;
46
+ input: MessagingStreamInput;
47
+ output: AsyncIterator<Content>;
48
+ inputClosed?: Promise<void>;
49
+ onClosed: () => void;
50
+ });
51
+ get closed(): boolean;
52
+ private push;
53
+ private finish;
54
+ private consumeOutput;
55
+ private content;
56
+ sendMessageNowait(params: {
57
+ type: string;
58
+ message: MessagePayload;
59
+ attachment?: Uint8Array;
60
+ }): void;
61
+ sendMessage(params: {
62
+ type: string;
63
+ message: MessagePayload;
64
+ attachment?: Uint8Array;
65
+ }): Promise<void>;
66
+ close(): Promise<void>;
67
+ clientDisconnected(participantId: string): void;
68
+ [Symbol.asyncIterator](): AsyncIterator<MessagingStreamEvent>;
69
+ }
6
70
  export declare class MessagingClient extends EventEmitter<RoomMessageEvent> {
7
71
  private readonly client;
8
72
  private readonly _messageHandler;
@@ -11,6 +75,7 @@ export declare class MessagingClient extends EventEmitter<RoomMessageEvent> {
11
75
  private _messageQueued;
12
76
  private _sendTask;
13
77
  private readonly _sendOperations;
78
+ private readonly _streams;
14
79
  private _messageQueueClosed;
15
80
  private _desiredEnabled;
16
81
  private _online;
@@ -64,6 +129,12 @@ export declare class MessagingClient extends EventEmitter<RoomMessageEvent> {
64
129
  message: MessagePayload;
65
130
  attachment?: Uint8Array;
66
131
  }): Promise<void>;
132
+ stream({ to, type, message, attachment, }: {
133
+ to: Participant;
134
+ type: string;
135
+ message: MessagePayload;
136
+ attachment?: Uint8Array;
137
+ }): Promise<MessagingStream>;
67
138
  getParticipants(): RemoteParticipant[];
68
139
  getParticipant(id: string): RemoteParticipant | null;
69
140
  getParticipantByName(name: string): RemoteParticipant | null;
@@ -1,7 +1,7 @@
1
1
  import { Completer } from "./completer.js";
2
2
  import { EventEmitter } from "./event-emitter.js";
3
3
  import { RemoteParticipant } from "./participant.js";
4
- import { JsonContent } from "./response.js";
4
+ import { ControlContent, JsonContent } from "./response.js";
5
5
  import { RoomMessage, RoomMessageEvent } from "./room-event.js";
6
6
  import { RoomServerException } from "./room-server-client.js";
7
7
  import { splitMessageHeader, splitMessagePayload } from "./utils.js";
@@ -19,6 +19,255 @@ function bytesToBase64(bytes) {
19
19
  }
20
20
  return globalScope.btoa(binary);
21
21
  }
22
+ function base64ToBytes(value) {
23
+ if (globalScope.Buffer != null) {
24
+ return new Uint8Array(globalScope.Buffer.from(value, "base64"));
25
+ }
26
+ const atob = globalThis.atob;
27
+ if (atob == null) {
28
+ throw new Error("base64 decoding is not available in this runtime");
29
+ }
30
+ const binary = atob(value);
31
+ return Uint8Array.from(binary, (character) => character.charCodeAt(0));
32
+ }
33
+ class MessagingStreamInput {
34
+ constructor() {
35
+ this.items = [];
36
+ this.signal = null;
37
+ this.active = null;
38
+ this.closed = false;
39
+ this.error = null;
40
+ }
41
+ enqueue(content, { wait = false } = {}) {
42
+ if (this.closed) {
43
+ return Promise.reject(this.error ?? new RoomServerException("the messaging stream is closed"));
44
+ }
45
+ const sent = wait ? new Completer() : undefined;
46
+ this.items.push({ content, sent });
47
+ this.signal?.resolve();
48
+ this.signal = null;
49
+ return sent?.fut ?? Promise.resolve();
50
+ }
51
+ close(error = new RoomServerException("the messaging stream is closed")) {
52
+ if (this.closed) {
53
+ return;
54
+ }
55
+ this.closed = true;
56
+ this.error = error;
57
+ this.active?.sent?.reject(error);
58
+ this.active = null;
59
+ for (const item of this.items.splice(0)) {
60
+ item.sent?.reject(error);
61
+ }
62
+ this.signal?.resolve();
63
+ this.signal = null;
64
+ }
65
+ fail(error) {
66
+ this.close(error);
67
+ }
68
+ async *stream() {
69
+ try {
70
+ while (true) {
71
+ while (this.items.length === 0 && !this.closed) {
72
+ this.signal ?? (this.signal = new Completer());
73
+ await this.signal.fut;
74
+ }
75
+ const item = this.items.shift();
76
+ if (item == null) {
77
+ return;
78
+ }
79
+ this.active = item;
80
+ yield item.content;
81
+ item.sent?.resolve();
82
+ this.active = null;
83
+ }
84
+ }
85
+ finally {
86
+ if (this.active != null && this.closed) {
87
+ this.active.sent?.reject(this.error ?? new RoomServerException("the messaging stream is closed"));
88
+ this.active = null;
89
+ }
90
+ }
91
+ }
92
+ }
93
+ export class MessagingStream {
94
+ constructor({ streamId, remoteParticipantId, input, output, inputClosed, onClosed, }) {
95
+ this.events = [];
96
+ this.eventSignal = null;
97
+ this.state = "accepted";
98
+ this.streamId = streamId;
99
+ this.remoteParticipantId = remoteParticipantId;
100
+ this.input = input;
101
+ this.output = output;
102
+ this.inputDone = Promise.resolve(inputClosed).catch((error) => {
103
+ this.input.fail(error);
104
+ this.push({
105
+ kind: "closed",
106
+ streamId: this.streamId,
107
+ reason: "error",
108
+ message: error instanceof Error ? error.message : String(error),
109
+ });
110
+ void this.output.return?.();
111
+ this.finish();
112
+ });
113
+ this.onClosed = onClosed;
114
+ void this.consumeOutput();
115
+ }
116
+ get closed() {
117
+ return this.state === "terminal";
118
+ }
119
+ push(event) {
120
+ if (this.state !== "accepted") {
121
+ return;
122
+ }
123
+ this.events.push(event);
124
+ this.eventSignal?.resolve();
125
+ this.eventSignal = null;
126
+ }
127
+ finish() {
128
+ if (this.state === "terminal") {
129
+ return;
130
+ }
131
+ this.state = "terminal";
132
+ this.input.close();
133
+ this.eventSignal?.resolve();
134
+ this.eventSignal = null;
135
+ void this.inputDone.finally(this.onClosed);
136
+ }
137
+ async consumeOutput() {
138
+ try {
139
+ while (true) {
140
+ const next = await this.output.next();
141
+ if (next.done) {
142
+ return;
143
+ }
144
+ if (next.value instanceof ControlContent && next.value.method === "close") {
145
+ this.push({
146
+ kind: "client_disconnected",
147
+ streamId: this.streamId,
148
+ participantId: this.remoteParticipantId,
149
+ });
150
+ return;
151
+ }
152
+ if (!(next.value instanceof JsonContent)) {
153
+ throw new RoomServerException("unexpected chunk from messaging.stream");
154
+ }
155
+ const value = next.value.json;
156
+ const kind = value["kind"];
157
+ if (kind === "message") {
158
+ const type = value["type"];
159
+ const message = value["message"];
160
+ if (typeof type !== "string" || typeof message !== "object" || message == null || Array.isArray(message)) {
161
+ throw new RoomServerException("invalid message chunk from messaging.stream");
162
+ }
163
+ const encodedAttachment = value["attachment_base64"];
164
+ this.push({
165
+ kind: "message",
166
+ message: new RoomMessage({
167
+ fromParticipantId: this.remoteParticipantId,
168
+ type,
169
+ message: message,
170
+ attachment: typeof encodedAttachment === "string" && encodedAttachment !== ""
171
+ ? base64ToBytes(encodedAttachment)
172
+ : undefined,
173
+ }),
174
+ });
175
+ }
176
+ else if (kind === "client_disconnected" && typeof value["participant_id"] === "string") {
177
+ this.push({
178
+ kind,
179
+ streamId: this.streamId,
180
+ participantId: value["participant_id"],
181
+ });
182
+ return;
183
+ }
184
+ else if (kind === "closed") {
185
+ this.push({
186
+ kind,
187
+ streamId: this.streamId,
188
+ reason: typeof value["reason"] === "string" ? value["reason"] : "closed",
189
+ message: typeof value["message"] === "string" ? value["message"] : undefined,
190
+ });
191
+ return;
192
+ }
193
+ else {
194
+ throw new RoomServerException("unexpected chunk from messaging.stream");
195
+ }
196
+ }
197
+ }
198
+ catch (error) {
199
+ this.input.fail(error);
200
+ this.push({
201
+ kind: "closed",
202
+ streamId: this.streamId,
203
+ reason: "error",
204
+ message: error instanceof Error ? error.message : String(error),
205
+ });
206
+ }
207
+ finally {
208
+ this.finish();
209
+ }
210
+ }
211
+ content({ type, message, attachment, }) {
212
+ return new JsonContent({
213
+ json: {
214
+ type,
215
+ message_json: JSON.stringify(message),
216
+ ...(attachment == null ? {} : { attachment_base64: bytesToBase64(attachment) }),
217
+ },
218
+ });
219
+ }
220
+ sendMessageNowait(params) {
221
+ if (this.state !== "accepted") {
222
+ throw new RoomServerException("the messaging stream is closed");
223
+ }
224
+ void this.input.enqueue(this.content(params));
225
+ }
226
+ async sendMessage(params) {
227
+ if (this.state !== "accepted") {
228
+ throw new RoomServerException("the messaging stream is closed");
229
+ }
230
+ await this.input.enqueue(this.content(params), { wait: true });
231
+ }
232
+ async close() {
233
+ if (this.state === "terminal") {
234
+ await this.inputDone;
235
+ return;
236
+ }
237
+ this.input.close();
238
+ try {
239
+ await this.inputDone;
240
+ }
241
+ finally {
242
+ await this.output.return?.();
243
+ this.finish();
244
+ }
245
+ }
246
+ clientDisconnected(participantId) {
247
+ if (this.state === "terminal") {
248
+ return;
249
+ }
250
+ this.push({
251
+ kind: "client_disconnected",
252
+ streamId: this.streamId,
253
+ participantId,
254
+ });
255
+ this.finish();
256
+ }
257
+ async *[Symbol.asyncIterator]() {
258
+ while (true) {
259
+ while (this.events.length === 0 && this.state !== "terminal") {
260
+ this.eventSignal ?? (this.eventSignal = new Completer());
261
+ await this.eventSignal.fut;
262
+ }
263
+ const event = this.events.shift();
264
+ if (event == null) {
265
+ return;
266
+ }
267
+ yield event;
268
+ }
269
+ }
270
+ }
22
271
  export class MessagingClient extends EventEmitter {
23
272
  constructor({ room }) {
24
273
  super();
@@ -28,6 +277,7 @@ export class MessagingClient extends EventEmitter {
28
277
  this._messageQueued = null;
29
278
  this._sendTask = null;
30
279
  this._sendOperations = new Set();
280
+ this._streams = new Set();
31
281
  this._messageQueueClosed = false;
32
282
  this._desiredEnabled = false;
33
283
  this._online = false;
@@ -93,6 +343,7 @@ export class MessagingClient extends EventEmitter {
93
343
  await sendTask;
94
344
  }
95
345
  await Promise.all(this._sendOperations);
346
+ await Promise.all([...this._streams].map(async (stream) => stream.close()));
96
347
  this._desiredEnabled = false;
97
348
  this._clearCurrentConnectionState();
98
349
  }
@@ -156,6 +407,9 @@ export class MessagingClient extends EventEmitter {
156
407
  }
157
408
  }
158
409
  _onRoomDisconnect({ reason: _reason }) {
410
+ for (const stream of [...this._streams]) {
411
+ stream.clientDisconnected(this.client.localParticipant?.id ?? stream.remoteParticipantId);
412
+ }
159
413
  this._clearCurrentConnectionState();
160
414
  }
161
415
  _onRoomReconnect() {
@@ -348,6 +602,49 @@ export class MessagingClient extends EventEmitter {
348
602
  throw error;
349
603
  }
350
604
  }
605
+ async stream({ to, type, message, attachment, }) {
606
+ const input = new MessagingStreamInput();
607
+ void input.enqueue(new JsonContent({
608
+ json: {
609
+ to_participant_id: to.id,
610
+ type,
611
+ message_json: JSON.stringify(message),
612
+ ...(attachment == null ? {} : { attachment_base64: bytesToBase64(attachment) }),
613
+ },
614
+ }));
615
+ const outputResult = await this.client.invokeToolCall({
616
+ toolkit: "messaging",
617
+ tool: "stream",
618
+ input: input.stream(),
619
+ streamInput: true,
620
+ });
621
+ if (outputResult.kind !== "stream") {
622
+ input.close();
623
+ throw new RoomServerException("unexpected return type from messaging.stream");
624
+ }
625
+ const outputIterable = outputResult.stream;
626
+ const output = outputIterable[Symbol.asyncIterator]();
627
+ const accepted = await output.next();
628
+ if (accepted.done
629
+ || !(accepted.value instanceof JsonContent)
630
+ || accepted.value.json["kind"] !== "accepted"
631
+ || typeof accepted.value.json["stream_id"] !== "string") {
632
+ input.close();
633
+ await output.return?.();
634
+ throw new RoomServerException("messaging.stream did not acknowledge acceptance");
635
+ }
636
+ let stream;
637
+ stream = new MessagingStream({
638
+ streamId: accepted.value.json["stream_id"],
639
+ remoteParticipantId: to.id,
640
+ input,
641
+ output,
642
+ inputClosed: outputResult.inputClosed,
643
+ onClosed: () => this._streams.delete(stream),
644
+ });
645
+ this._streams.add(stream);
646
+ return stream;
647
+ }
351
648
  getParticipants() {
352
649
  return this.remoteParticipants;
353
650
  }
@@ -441,6 +738,9 @@ export class MessagingClient extends EventEmitter {
441
738
  this._wakeMessageQueue();
442
739
  this._drainQueuedMessages({ error });
443
740
  this._desiredEnabled = false;
741
+ for (const stream of [...this._streams]) {
742
+ stream.clientDisconnected(this.client.localParticipant?.id ?? stream.remoteParticipantId);
743
+ }
444
744
  this._clearCurrentConnectionState();
445
745
  this.client.protocol.removeHandler("messaging.send", this._messageHandler);
446
746
  super.dispose();
@@ -404,6 +404,7 @@ export declare class ParticipantToken {
404
404
  addTunnelGrant(ports: number[]): void;
405
405
  addRoleGrant(role: string): void;
406
406
  addRoomGrant(roomName: string): void;
407
+ addSiteGrant(roomName: string): void;
407
408
  addApiGrant(grant: ApiScope): void;
408
409
  grantScope(name: string): string | ApiScope | undefined;
409
410
  getApiGrant(): ApiScope | undefined;
@@ -1294,6 +1294,9 @@ export class ParticipantToken {
1294
1294
  addRoomGrant(roomName) {
1295
1295
  this.grants.push(new ParticipantGrant({ name: "room", scope: roomName }));
1296
1296
  }
1297
+ addSiteGrant(roomName) {
1298
+ this.grants.push(new ParticipantGrant({ name: "site", scope: roomName }));
1299
+ }
1297
1300
  addApiGrant(grant) {
1298
1301
  for (const existingGrant of this.grants) {
1299
1302
  if (existingGrant.name === "api") {
@@ -190,6 +190,7 @@ export declare class RoomClient {
190
190
  _sendLocalAttributesNowait(attributes: Record<string, unknown>): void;
191
191
  _resendLocalAttributesNowait(): void;
192
192
  sendRequest(type: string, request: RequestHeader, data?: Uint8Array, afterSend?: () => void): Promise<Content>;
193
+ private _dispatchRequest;
193
194
  call(params: {
194
195
  name: string;
195
196
  url: string;
@@ -249,7 +250,7 @@ export declare class RoomClient {
249
250
  participantId?: string;
250
251
  onBehalfOfId?: string;
251
252
  }): Promise<AsyncIterable<Content>>;
252
- private _sendToolCallRequestChunk;
253
+ private _dispatchToolCallRequestChunk;
253
254
  private _streamInvokeToolRequestChunks;
254
255
  private _decodeToolCallContent;
255
256
  private _handleToolCallResponseChunk;
@@ -1143,6 +1143,11 @@ export class RoomClient {
1143
1143
  this._sendLocalAttributesNowait(attributes);
1144
1144
  }
1145
1145
  async sendRequest(type, request, data, afterSend) {
1146
+ const response = this._dispatchRequest(type, request, data);
1147
+ afterSend?.();
1148
+ return await response;
1149
+ }
1150
+ _dispatchRequest(type, request, data) {
1146
1151
  this._raiseIfTerminal();
1147
1152
  if (this._entered && !this._connected && !this._allowDisconnectedRequests) {
1148
1153
  throw this._disconnectedError({ baseMessage: "room connection is disconnected" });
@@ -1151,9 +1156,8 @@ export class RoomClient {
1151
1156
  const completer = new Completer();
1152
1157
  this._pendingRequests.set(requestId, completer);
1153
1158
  try {
1154
- await this._protocolInstance.send(type, packMessage(request, data), requestId);
1155
- afterSend?.();
1156
- return await completer.fut;
1159
+ this._protocolInstance.sendNowait(type, packMessage(request, data), { id: requestId });
1160
+ return completer.fut;
1157
1161
  }
1158
1162
  catch (error) {
1159
1163
  this._pendingRequests.delete(requestId);
@@ -1259,7 +1263,6 @@ export class RoomClient {
1259
1263
  let requestTask;
1260
1264
  if (params.streamInput === true) {
1261
1265
  request["arguments"] = { type: "control", method: "open" };
1262
- requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
1263
1266
  }
1264
1267
  else {
1265
1268
  const input = params.input;
@@ -1276,9 +1279,14 @@ export class RoomClient {
1276
1279
  if (params.onBehalfOfId != null) {
1277
1280
  request["on_behalf_of_id"] = params.onBehalfOfId;
1278
1281
  }
1282
+ const responseRequest = this._dispatchRequest("room.invoke_tool", request, requestData);
1283
+ params.afterSend?.();
1284
+ if (params.streamInput === true) {
1285
+ requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
1286
+ }
1279
1287
  try {
1280
1288
  const response = await Promise.race([
1281
- this.sendRequest("room.invoke_tool", request, requestData, params.afterSend),
1289
+ responseRequest,
1282
1290
  preOpenError.fut,
1283
1291
  ]);
1284
1292
  this._toolCallPreOpenErrors.delete(toolCallId);
@@ -1333,9 +1341,10 @@ export class RoomClient {
1333
1341
  if (params.onBehalfOfId != null) {
1334
1342
  request["on_behalf_of_id"] = params.onBehalfOfId;
1335
1343
  }
1344
+ const responseRequest = this._dispatchRequest("room.invoke_tool", request);
1336
1345
  const requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
1337
1346
  try {
1338
- const response = await this.sendRequest("room.invoke_tool", request);
1347
+ const response = await responseRequest;
1339
1348
  await requestTask;
1340
1349
  if (response instanceof ControlContent && response.method === "open") {
1341
1350
  throw new RoomServerException(`unexpected return type from ${params.toolkit}.${params.tool}`);
@@ -1366,6 +1375,7 @@ export class RoomClient {
1366
1375
  if (params.onBehalfOfId != null) {
1367
1376
  request["on_behalf_of_id"] = params.onBehalfOfId;
1368
1377
  }
1378
+ const responseRequest = this._dispatchRequest("room.invoke_tool", request);
1369
1379
  const requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
1370
1380
  void requestTask.catch((error) => {
1371
1381
  const stream = this._toolCallStreams.get(toolCallId);
@@ -1379,7 +1389,7 @@ export class RoomClient {
1379
1389
  let response;
1380
1390
  try {
1381
1391
  response = await Promise.race([
1382
- this.sendRequest("room.invoke_tool", request),
1392
+ responseRequest,
1383
1393
  preOpenError.fut,
1384
1394
  ]);
1385
1395
  }
@@ -1401,24 +1411,27 @@ export class RoomClient {
1401
1411
  },
1402
1412
  };
1403
1413
  }
1404
- async _sendToolCallRequestChunk(toolCallId, chunk) {
1414
+ _dispatchToolCallRequestChunk(toolCallId, chunk) {
1415
+ this._raiseIfTerminal();
1416
+ if (this._entered && !this._connected && !this._allowDisconnectedRequests) {
1417
+ throw this._disconnectedError({ baseMessage: "room connection is disconnected" });
1418
+ }
1405
1419
  const packed = chunk.pack();
1406
1420
  const request = {
1407
1421
  tool_call_id: toolCallId,
1408
1422
  chunk: JSON.parse(splitMessageHeader(packed)),
1409
1423
  };
1410
1424
  const payload = splitMessagePayload(packed);
1411
- await this.sendRequest("room.tool_call_request_chunk", request, payload.length > 0 ? payload : undefined);
1425
+ this._protocolInstance.sendNowait("room.tool_call_request_chunk", packMessage(request, payload.length > 0 ? payload : undefined));
1412
1426
  }
1413
1427
  async _streamInvokeToolRequestChunks(toolCallId, input) {
1414
- await Promise.resolve();
1415
1428
  try {
1416
1429
  for await (const item of input) {
1417
- await this._sendToolCallRequestChunk(toolCallId, item);
1430
+ this._dispatchToolCallRequestChunk(toolCallId, item);
1418
1431
  }
1419
1432
  }
1420
1433
  finally {
1421
- await this._sendToolCallRequestChunk(toolCallId, new ControlContent({ method: "close" }));
1434
+ this._dispatchToolCallRequestChunk(toolCallId, new ControlContent({ method: "close" }));
1422
1435
  }
1423
1436
  }
1424
1437
  _decodeToolCallContent(params) {