@meshagent/meshagent 0.47.0 → 0.48.0

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/browser/agent.js +6 -6
  3. package/dist/browser/containers-client.d.ts +1 -0
  4. package/dist/browser/containers-client.js +34 -0
  5. package/dist/browser/entrypoint.d.ts +1 -0
  6. package/dist/browser/entrypoint.js +3 -3
  7. package/dist/browser/index.d.ts +1 -0
  8. package/dist/browser/index.js +1 -0
  9. package/dist/browser/meshagent-client.d.ts +9 -1
  10. package/dist/browser/meshagent-client.js +1 -0
  11. package/dist/browser/messaging-client.d.ts +72 -1
  12. package/dist/browser/messaging-client.js +302 -1
  13. package/dist/browser/mounts-client.d.ts +21 -0
  14. package/dist/browser/mounts-client.js +82 -0
  15. package/dist/browser/participant-token.d.ts +1 -0
  16. package/dist/browser/participant-token.js +3 -0
  17. package/dist/browser/room-client.d.ts +4 -1
  18. package/dist/browser/room-client.js +27 -12
  19. package/dist/esm/agent.js +6 -6
  20. package/dist/esm/containers-client.d.ts +1 -0
  21. package/dist/esm/containers-client.js +34 -0
  22. package/dist/esm/entrypoint.d.ts +1 -0
  23. package/dist/esm/entrypoint.js +3 -3
  24. package/dist/esm/index.d.ts +1 -0
  25. package/dist/esm/index.js +1 -0
  26. package/dist/esm/meshagent-client.d.ts +9 -1
  27. package/dist/esm/meshagent-client.js +1 -0
  28. package/dist/esm/messaging-client.d.ts +72 -1
  29. package/dist/esm/messaging-client.js +301 -1
  30. package/dist/esm/mounts-client.d.ts +21 -0
  31. package/dist/esm/mounts-client.js +78 -0
  32. package/dist/esm/participant-token.d.ts +1 -0
  33. package/dist/esm/participant-token.js +3 -0
  34. package/dist/esm/room-client.d.ts +4 -1
  35. package/dist/esm/room-client.js +27 -12
  36. package/dist/node/agent.js +6 -6
  37. package/dist/node/containers-client.d.ts +1 -0
  38. package/dist/node/containers-client.js +34 -0
  39. package/dist/node/entrypoint.d.ts +1 -0
  40. package/dist/node/entrypoint.js +3 -3
  41. package/dist/node/index.d.ts +1 -0
  42. package/dist/node/index.js +1 -0
  43. package/dist/node/meshagent-client.d.ts +9 -1
  44. package/dist/node/meshagent-client.js +1 -0
  45. package/dist/node/messaging-client.d.ts +72 -1
  46. package/dist/node/messaging-client.js +302 -1
  47. package/dist/node/mounts-client.d.ts +21 -0
  48. package/dist/node/mounts-client.js +82 -0
  49. package/dist/node/participant-token.d.ts +1 -0
  50. package/dist/node/participant-token.js +3 -0
  51. package/dist/node/room-client.d.ts +4 -1
  52. package/dist/node/room-client.js +27 -12
  53. package/package.json +1 -1
@@ -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();
@@ -0,0 +1,21 @@
1
+ import type { RoomClient } from "./room-client.js";
2
+ export interface MountedVolumeConsumer {
3
+ kind: "room" | "container";
4
+ containerId?: string;
5
+ }
6
+ export interface MountedVolume {
7
+ id: string;
8
+ name: string;
9
+ required: boolean;
10
+ description: string;
11
+ metadata: Record<string, unknown>;
12
+ annotations: Record<string, string>;
13
+ storageClass: "standard" | "juice" | "zerofs";
14
+ maxSizeMb?: number;
15
+ consumers: MountedVolumeConsumer[];
16
+ }
17
+ export declare class MountsClient {
18
+ private readonly room;
19
+ constructor(room: RoomClient);
20
+ list(): Promise<MountedVolume[]>;
21
+ }
@@ -0,0 +1,78 @@
1
+ import { JsonContent } from "./response.js";
2
+ import { RoomServerException } from "./room-server-client.js";
3
+ function isRecord(value) {
4
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5
+ }
6
+ function parseMountedVolume(value) {
7
+ if (!isRecord(value)
8
+ || typeof value["id"] !== "string"
9
+ || typeof value["name"] !== "string"
10
+ || typeof value["required"] !== "boolean") {
11
+ throw new RoomServerException("unexpected return type from mounts.list");
12
+ }
13
+ const metadata = isRecord(value["metadata"]) ? value["metadata"] : {};
14
+ const rawAnnotations = isRecord(value["annotations"]) ? value["annotations"] : {};
15
+ const annotations = {};
16
+ for (const [key, item] of Object.entries(rawAnnotations)) {
17
+ if (typeof item === "string") {
18
+ annotations[key] = item;
19
+ }
20
+ }
21
+ const rawConsumers = value["consumers"] ?? [];
22
+ if (!Array.isArray(rawConsumers)) {
23
+ throw new RoomServerException("unexpected return type from mounts.list");
24
+ }
25
+ const consumers = rawConsumers.map((consumer) => {
26
+ if (!isRecord(consumer)
27
+ || (consumer["kind"] !== "room" && consumer["kind"] !== "container")
28
+ || (consumer["kind"] === "container" && typeof consumer["container_id"] !== "string")
29
+ || (consumer["kind"] === "room" && consumer["container_id"] !== undefined)) {
30
+ throw new RoomServerException("unexpected return type from mounts.list");
31
+ }
32
+ return {
33
+ kind: consumer["kind"],
34
+ containerId: typeof consumer["container_id"] === "string"
35
+ ? consumer["container_id"]
36
+ : undefined,
37
+ };
38
+ });
39
+ const storageClass = value["storage_class"] ?? "standard";
40
+ if (storageClass !== "standard" && storageClass !== "juice" && storageClass !== "zerofs") {
41
+ throw new RoomServerException("unexpected return type from mounts.list");
42
+ }
43
+ const maxSizeMb = value["max_size_mb"];
44
+ if (maxSizeMb !== undefined && (typeof maxSizeMb !== "number" || !Number.isInteger(maxSizeMb) || maxSizeMb <= 0)) {
45
+ throw new RoomServerException("unexpected return type from mounts.list");
46
+ }
47
+ return {
48
+ id: value["id"],
49
+ name: value["name"],
50
+ required: value["required"],
51
+ description: typeof value["description"] === "string" ? value["description"] : "",
52
+ metadata,
53
+ annotations,
54
+ storageClass,
55
+ maxSizeMb,
56
+ consumers,
57
+ };
58
+ }
59
+ export class MountsClient {
60
+ constructor(room) {
61
+ this.room = room;
62
+ }
63
+ async list() {
64
+ const response = await this.room.invokeContent({
65
+ toolkit: "mounts",
66
+ tool: "list",
67
+ input: {},
68
+ });
69
+ if (!(response instanceof JsonContent)) {
70
+ throw new RoomServerException("unexpected return type from mounts.list");
71
+ }
72
+ const volumes = response.json["volumes"];
73
+ if (!Array.isArray(volumes)) {
74
+ throw new RoomServerException("unexpected return type from mounts.list");
75
+ }
76
+ return volumes.map(parseMountedVolume);
77
+ }
78
+ }
@@ -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") {
@@ -4,6 +4,7 @@ import { DeveloperClient } from "./developer-client.js";
4
4
  import { type EventHandler, type EventName } from "./event-emitter.js";
5
5
  import { MessagingClient } from "./messaging-client.js";
6
6
  import { MemoryClient } from "./memory-client.js";
7
+ import { MountsClient } from "./mounts-client.js";
7
8
  import { LocalParticipant } from "./participant.js";
8
9
  import { Protocol, ProtocolCloseKind, type MessageHandler, type ProtocolFactory } from "./protocol.js";
9
10
  import { QueuesClient } from "./queues-client.js";
@@ -59,6 +60,7 @@ export declare class RoomClient {
59
60
  readonly containers: ContainersClient;
60
61
  readonly memory: MemoryClient;
61
62
  readonly services: ServicesClient;
63
+ readonly mounts: MountsClient;
62
64
  _protocolInstance: Protocol;
63
65
  _entered: boolean;
64
66
  _allowDisconnectedRequests: boolean;
@@ -190,6 +192,7 @@ export declare class RoomClient {
190
192
  _sendLocalAttributesNowait(attributes: Record<string, unknown>): void;
191
193
  _resendLocalAttributesNowait(): void;
192
194
  sendRequest(type: string, request: RequestHeader, data?: Uint8Array, afterSend?: () => void): Promise<Content>;
195
+ private _dispatchRequest;
193
196
  call(params: {
194
197
  name: string;
195
198
  url: string;
@@ -249,7 +252,7 @@ export declare class RoomClient {
249
252
  participantId?: string;
250
253
  onBehalfOfId?: string;
251
254
  }): Promise<AsyncIterable<Content>>;
252
- private _sendToolCallRequestChunk;
255
+ private _dispatchToolCallRequestChunk;
253
256
  private _streamInvokeToolRequestChunks;
254
257
  private _decodeToolCallContent;
255
258
  private _handleToolCallResponseChunk;
@@ -5,6 +5,7 @@ import { DeveloperClient } from "./developer-client.js";
5
5
  import { EventEmitter } from "./event-emitter.js";
6
6
  import { MessagingClient } from "./messaging-client.js";
7
7
  import { MemoryClient } from "./memory-client.js";
8
+ import { MountsClient } from "./mounts-client.js";
8
9
  import { LocalParticipant } from "./participant.js";
9
10
  import { ProtocolCloseException, ProtocolCloseKind, ProtocolHandshakeException, ProtocolReconnectUnsupportedException, WebSocketClientProtocol, } from "./protocol.js";
10
11
  import { QueuesClient } from "./queues-client.js";
@@ -257,6 +258,7 @@ export class RoomClient {
257
258
  this.containers = new ContainersClient({ room: this });
258
259
  this.memory = new MemoryClient({ room: this });
259
260
  this.services = new ServicesClient({ room: this });
261
+ this.mounts = new MountsClient(this);
260
262
  }
261
263
  static withIAP({ url = "./.well-known/meshagent/room/connect", reconnectTimeout = null, } = {}) {
262
264
  return new RoomClient({
@@ -1143,6 +1145,11 @@ export class RoomClient {
1143
1145
  this._sendLocalAttributesNowait(attributes);
1144
1146
  }
1145
1147
  async sendRequest(type, request, data, afterSend) {
1148
+ const response = this._dispatchRequest(type, request, data);
1149
+ afterSend?.();
1150
+ return await response;
1151
+ }
1152
+ _dispatchRequest(type, request, data) {
1146
1153
  this._raiseIfTerminal();
1147
1154
  if (this._entered && !this._connected && !this._allowDisconnectedRequests) {
1148
1155
  throw this._disconnectedError({ baseMessage: "room connection is disconnected" });
@@ -1151,9 +1158,8 @@ export class RoomClient {
1151
1158
  const completer = new Completer();
1152
1159
  this._pendingRequests.set(requestId, completer);
1153
1160
  try {
1154
- await this._protocolInstance.send(type, packMessage(request, data), requestId);
1155
- afterSend?.();
1156
- return await completer.fut;
1161
+ this._protocolInstance.sendNowait(type, packMessage(request, data), { id: requestId });
1162
+ return completer.fut;
1157
1163
  }
1158
1164
  catch (error) {
1159
1165
  this._pendingRequests.delete(requestId);
@@ -1259,7 +1265,6 @@ export class RoomClient {
1259
1265
  let requestTask;
1260
1266
  if (params.streamInput === true) {
1261
1267
  request["arguments"] = { type: "control", method: "open" };
1262
- requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
1263
1268
  }
1264
1269
  else {
1265
1270
  const input = params.input;
@@ -1276,9 +1281,14 @@ export class RoomClient {
1276
1281
  if (params.onBehalfOfId != null) {
1277
1282
  request["on_behalf_of_id"] = params.onBehalfOfId;
1278
1283
  }
1284
+ const responseRequest = this._dispatchRequest("room.invoke_tool", request, requestData);
1285
+ params.afterSend?.();
1286
+ if (params.streamInput === true) {
1287
+ requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
1288
+ }
1279
1289
  try {
1280
1290
  const response = await Promise.race([
1281
- this.sendRequest("room.invoke_tool", request, requestData, params.afterSend),
1291
+ responseRequest,
1282
1292
  preOpenError.fut,
1283
1293
  ]);
1284
1294
  this._toolCallPreOpenErrors.delete(toolCallId);
@@ -1333,9 +1343,10 @@ export class RoomClient {
1333
1343
  if (params.onBehalfOfId != null) {
1334
1344
  request["on_behalf_of_id"] = params.onBehalfOfId;
1335
1345
  }
1346
+ const responseRequest = this._dispatchRequest("room.invoke_tool", request);
1336
1347
  const requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
1337
1348
  try {
1338
- const response = await this.sendRequest("room.invoke_tool", request);
1349
+ const response = await responseRequest;
1339
1350
  await requestTask;
1340
1351
  if (response instanceof ControlContent && response.method === "open") {
1341
1352
  throw new RoomServerException(`unexpected return type from ${params.toolkit}.${params.tool}`);
@@ -1366,6 +1377,7 @@ export class RoomClient {
1366
1377
  if (params.onBehalfOfId != null) {
1367
1378
  request["on_behalf_of_id"] = params.onBehalfOfId;
1368
1379
  }
1380
+ const responseRequest = this._dispatchRequest("room.invoke_tool", request);
1369
1381
  const requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
1370
1382
  void requestTask.catch((error) => {
1371
1383
  const stream = this._toolCallStreams.get(toolCallId);
@@ -1379,7 +1391,7 @@ export class RoomClient {
1379
1391
  let response;
1380
1392
  try {
1381
1393
  response = await Promise.race([
1382
- this.sendRequest("room.invoke_tool", request),
1394
+ responseRequest,
1383
1395
  preOpenError.fut,
1384
1396
  ]);
1385
1397
  }
@@ -1401,24 +1413,27 @@ export class RoomClient {
1401
1413
  },
1402
1414
  };
1403
1415
  }
1404
- async _sendToolCallRequestChunk(toolCallId, chunk) {
1416
+ _dispatchToolCallRequestChunk(toolCallId, chunk) {
1417
+ this._raiseIfTerminal();
1418
+ if (this._entered && !this._connected && !this._allowDisconnectedRequests) {
1419
+ throw this._disconnectedError({ baseMessage: "room connection is disconnected" });
1420
+ }
1405
1421
  const packed = chunk.pack();
1406
1422
  const request = {
1407
1423
  tool_call_id: toolCallId,
1408
1424
  chunk: JSON.parse(splitMessageHeader(packed)),
1409
1425
  };
1410
1426
  const payload = splitMessagePayload(packed);
1411
- await this.sendRequest("room.tool_call_request_chunk", request, payload.length > 0 ? payload : undefined);
1427
+ this._protocolInstance.sendNowait("room.tool_call_request_chunk", packMessage(request, payload.length > 0 ? payload : undefined));
1412
1428
  }
1413
1429
  async _streamInvokeToolRequestChunks(toolCallId, input) {
1414
- await Promise.resolve();
1415
1430
  try {
1416
1431
  for await (const item of input) {
1417
- await this._sendToolCallRequestChunk(toolCallId, item);
1432
+ this._dispatchToolCallRequestChunk(toolCallId, item);
1418
1433
  }
1419
1434
  }
1420
1435
  finally {
1421
- await this._sendToolCallRequestChunk(toolCallId, new ControlContent({ method: "close" }));
1436
+ this._dispatchToolCallRequestChunk(toolCallId, new ControlContent({ method: "close" }));
1422
1437
  }
1423
1438
  }
1424
1439
  _decodeToolCallContent(params) {
@@ -530,10 +530,10 @@ class _RemoteToolkitWrapper {
530
530
  return false;
531
531
  }
532
532
  }
533
- async _sendToolCallResponseChunk(messageId, toolCallId, chunk) {
533
+ _sendToolCallResponseChunk(messageId, toolCallId, chunk) {
534
534
  const [header, payload] = (0, utils_js_1.unpackMessage)(chunk.pack());
535
535
  try {
536
- await this.client.protocol.send("room.tool_call_response_chunk", (0, utils_js_1.packMessage)({ tool_call_id: toolCallId, chunk: header }, payload.length > 0 ? payload : undefined), { id: messageId });
536
+ this.client.protocol.sendNowait("room.tool_call_response_chunk", (0, utils_js_1.packMessage)({ tool_call_id: toolCallId, chunk: header }, payload.length > 0 ? payload : undefined), { id: messageId });
537
537
  return true;
538
538
  }
539
539
  catch (error) {
@@ -634,10 +634,10 @@ class _RemoteToolkitWrapper {
634
634
  return;
635
635
  for await (const chunk of output.stream) {
636
636
  this.toolkit.validateOutputContent(tool, chunk);
637
- if (!await this._sendToolCallResponseChunk(messageId, toolCallId, chunk))
637
+ if (!this._sendToolCallResponseChunk(messageId, toolCallId, chunk))
638
638
  return;
639
639
  }
640
- await this._sendToolCallResponseChunk(messageId, toolCallId, new response_js_1.ControlContent({ method: "close" }));
640
+ this._sendToolCallResponseChunk(messageId, toolCallId, new response_js_1.ControlContent({ method: "close" }));
641
641
  return;
642
642
  }
643
643
  throw new Error(`tool ${toolName} returned unsupported output`);
@@ -648,9 +648,9 @@ class _RemoteToolkitWrapper {
648
648
  return;
649
649
  }
650
650
  if (!(error instanceof InvalidToolDataException)) {
651
- await this._sendToolCallResponseChunk(messageId, toolCallId, new response_js_1.ErrorContent({ text: String(error) }));
651
+ this._sendToolCallResponseChunk(messageId, toolCallId, new response_js_1.ErrorContent({ text: String(error) }));
652
652
  }
653
- await this._sendToolCallResponseChunk(messageId, toolCallId, new response_js_1.ControlContent({
653
+ this._sendToolCallResponseChunk(messageId, toolCallId, new response_js_1.ControlContent({
654
654
  method: "close",
655
655
  statusCode: error instanceof InvalidToolDataException ? response_js_1.ControlCloseStatus.INVALID_DATA : undefined,
656
656
  message: error instanceof InvalidToolDataException ? error.message : undefined,
@@ -72,6 +72,7 @@ export interface RoomContainer {
72
72
  state: string;
73
73
  private: boolean;
74
74
  serviceId?: string;
75
+ mounts?: ContainerMountSpec;
75
76
  stats?: RoomContainerStats;
76
77
  exitStatus?: ContainerExitStatus;
77
78
  }