@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
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.48.0]
2
+ - Reworked the TS/JS agent chat stack to preserve ordered threaded messages and support injected-message threads across chat clients and process handling.
3
+ - Added chat feed widgets and virtual scrolling to the React/Tailwind UI, improving large-thread performance.
4
+ - Aligned `meshagent-agents-ts` with the Dart agents implementation and refreshed the shared chat/message contracts.
5
+ - Removed the LiveKit dependency from the React package and updated shared package dependencies for the new chat stack.
6
+
7
+ ## [0.47.1]
8
+ - Changed room mount discovery to return typed room/container consumers without exposing internal source paths, and added typed container mount specs to listed containers.
9
+ - Added duplex messaging stream subscriptions and replay-state alignment in the TypeScript chat clients, bringing the NodeJS SDK in line with the new streaming model.
10
+ - Reworked messaging and room client flow control so dispatch ordering, close behavior, and presence updates stay consistent under streaming loads.
11
+ - Expanded stream and presence test coverage for the new messaging behavior.
12
+
1
13
  ## [0.47.0]
2
14
  - Improved TypeScript messaging clients so room delivery order and startup/presence behavior stay consistent.
3
15
  - Moved managed-agent client routing onto the Rust tunnel path and hardened agent chat-client event handling.
@@ -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
  }
@@ -9,6 +9,38 @@ const stream_controller_js_1 = require("./stream-controller.js");
9
9
  function isRecord(value) {
10
10
  return typeof value === "object" && value !== null && !Array.isArray(value);
11
11
  }
12
+ function parseContainerMountSpec(value) {
13
+ if (value === undefined || value === null) {
14
+ return undefined;
15
+ }
16
+ if (!isRecord(value)) {
17
+ throw new room_server_client_js_1.RoomServerException("unexpected return type from containers.list");
18
+ }
19
+ const readMounts = (field, validate) => {
20
+ const raw = value[field];
21
+ if (raw === undefined || raw === null) {
22
+ return undefined;
23
+ }
24
+ if (!Array.isArray(raw) || !raw.every((mount) => isRecord(mount) && validate(mount))) {
25
+ throw new room_server_client_js_1.RoomServerException("unexpected return type from containers.list");
26
+ }
27
+ return raw;
28
+ };
29
+ const optionalMountFieldsAreValid = (mount) => (mount["subpath"] === undefined || mount["subpath"] === null || typeof mount["subpath"] === "string")
30
+ && (mount["read_only"] === undefined || typeof mount["read_only"] === "boolean");
31
+ return {
32
+ room: readMounts("room", (mount) => typeof mount["path"] === "string" && optionalMountFieldsAreValid(mount)),
33
+ volumes: readMounts("volumes", (mount) => typeof mount["name"] === "string"
34
+ && typeof mount["path"] === "string"
35
+ && optionalMountFieldsAreValid(mount)),
36
+ empty_dirs: readMounts("empty_dirs", (mount) => typeof mount["path"] === "string"
37
+ && (mount["read_only"] === undefined || typeof mount["read_only"] === "boolean")),
38
+ configs: readMounts("configs", (mount) => mount["path"] === undefined || mount["path"] === null || typeof mount["path"] === "string"),
39
+ files: readMounts("files", (mount) => typeof mount["path"] === "string"
40
+ && (mount["read_only"] === undefined || typeof mount["read_only"] === "boolean")
41
+ && (mount["text"] === undefined || mount["text"] === null || typeof mount["text"] === "string")),
42
+ };
43
+ }
12
44
  function toStringMapList(values) {
13
45
  return Object.entries(values).map(([key, value]) => ({ key, value }));
14
46
  }
@@ -904,6 +936,7 @@ class ContainersClient {
904
936
  const imageIdRaw = entry["image_id"];
905
937
  const portsRaw = entry["ports"];
906
938
  const serviceIdRaw = entry["service_id"];
939
+ const mountsRaw = entry["mounts"];
907
940
  const statsRaw = entry["stats"];
908
941
  const exitStatusRaw = entry["exit_status"];
909
942
  if (!Array.isArray(portsRaw) ||
@@ -964,6 +997,7 @@ class ContainersClient {
964
997
  state,
965
998
  private: privateFlag,
966
999
  serviceId: typeof serviceIdRaw === "string" ? serviceIdRaw : undefined,
1000
+ mounts: parseContainerMountSpec(mountsRaw),
967
1001
  stats,
968
1002
  exitStatus,
969
1003
  });
@@ -16,6 +16,7 @@ export var ServerXmlDocument: {
16
16
  ignoreRemoteMapChanges: UndoManagerOptions;
17
17
  captureTimeout: UndoManagerOptions;
18
18
  afterTransactionHandler: (transaction: any) => void;
19
+ destroy(): void;
19
20
  addToScope(ytypes: Array<any | {
20
21
  gc: DocOpts;
21
22
  gcFilter: DocOpts;
@@ -3994,10 +3994,9 @@ var UndoManager = class extends ObservableV2 {
3994
3994
  this.emit("stack-item-updated", changeEvent);
3995
3995
  }
3996
3996
  };
3997
+ this.destroy = this.destroy.bind(this);
3997
3998
  this.doc.on("afterTransaction", this.afterTransactionHandler);
3998
- this.doc.on("destroy", () => {
3999
- this.destroy();
4000
- });
3999
+ this.doc.on("destroy", this.destroy);
4001
4000
  }
4002
4001
  /**
4003
4002
  * Extend the scope.
@@ -4114,6 +4113,7 @@ var UndoManager = class extends ObservableV2 {
4114
4113
  destroy() {
4115
4114
  this.trackedOrigins.delete(this);
4116
4115
  this.doc.off("afterTransaction", this.afterTransactionHandler);
4116
+ this.doc.off("destroy", this.destroy);
4117
4117
  super.destroy();
4118
4118
  }
4119
4119
  };
@@ -10,6 +10,7 @@ export * from './document.js';
10
10
  export * from './meshagent-client.js';
11
11
  export * from './messaging-client.js';
12
12
  export * from './memory-client.js';
13
+ export * from './mounts-client.js';
13
14
  export * from './oauth-scopes.js';
14
15
  export * from './participant-token.js';
15
16
  export * from './participant.js';
@@ -26,6 +26,7 @@ __exportStar(require("./document.js"), exports);
26
26
  __exportStar(require("./meshagent-client.js"), exports);
27
27
  __exportStar(require("./messaging-client.js"), exports);
28
28
  __exportStar(require("./memory-client.js"), exports);
29
+ __exportStar(require("./mounts-client.js"), exports);
29
30
  __exportStar(require("./oauth-scopes.js"), exports);
30
31
  __exportStar(require("./participant-token.js"), exports);
31
32
  __exportStar(require("./participant.js"), exports);
@@ -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 {
@@ -234,6 +235,12 @@ export interface RoomStorageMountSpec {
234
235
  subpath?: string | null;
235
236
  read_only?: boolean;
236
237
  }
238
+ export interface VolumeStorageMountSpec {
239
+ name: string;
240
+ path: string;
241
+ subpath?: string | null;
242
+ read_only?: boolean;
243
+ }
237
244
  export interface EmptyDirMountSpec {
238
245
  path: string;
239
246
  read_only?: boolean;
@@ -248,6 +255,7 @@ export interface FileMountSpec {
248
255
  }
249
256
  export interface ContainerMountSpec {
250
257
  room?: RoomStorageMountSpec[];
258
+ volumes?: VolumeStorageMountSpec[];
251
259
  empty_dirs?: EmptyDirMountSpec[];
252
260
  configs?: ConfigMountSpec[];
253
261
  files?: FileMountSpec[];
@@ -659,6 +659,7 @@ class Meshagent {
659
659
  const parsedSubject = this.parseAccessSubject(data.subject);
660
660
  const parsedDirectRoles = Array.isArray(data.direct_roles)
661
661
  ? data.direct_roles.filter((item) => item === "member" ||
662
+ item === "site_user" ||
662
663
  item === "viewer" ||
663
664
  item === "operator" ||
664
665
  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,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MessagingClient = void 0;
3
+ exports.MessagingClient = exports.MessagingStream = void 0;
4
4
  const completer_js_1 = require("./completer.js");
5
5
  const event_emitter_js_1 = require("./event-emitter.js");
6
6
  const participant_js_1 = require("./participant.js");
@@ -22,6 +22,256 @@ function bytesToBase64(bytes) {
22
22
  }
23
23
  return globalScope.btoa(binary);
24
24
  }
25
+ function base64ToBytes(value) {
26
+ if (globalScope.Buffer != null) {
27
+ return new Uint8Array(globalScope.Buffer.from(value, "base64"));
28
+ }
29
+ const atob = globalThis.atob;
30
+ if (atob == null) {
31
+ throw new Error("base64 decoding is not available in this runtime");
32
+ }
33
+ const binary = atob(value);
34
+ return Uint8Array.from(binary, (character) => character.charCodeAt(0));
35
+ }
36
+ class MessagingStreamInput {
37
+ constructor() {
38
+ this.items = [];
39
+ this.signal = null;
40
+ this.active = null;
41
+ this.closed = false;
42
+ this.error = null;
43
+ }
44
+ enqueue(content, { wait = false } = {}) {
45
+ if (this.closed) {
46
+ return Promise.reject(this.error ?? new room_server_client_js_1.RoomServerException("the messaging stream is closed"));
47
+ }
48
+ const sent = wait ? new completer_js_1.Completer() : undefined;
49
+ this.items.push({ content, sent });
50
+ this.signal?.resolve();
51
+ this.signal = null;
52
+ return sent?.fut ?? Promise.resolve();
53
+ }
54
+ close(error = new room_server_client_js_1.RoomServerException("the messaging stream is closed")) {
55
+ if (this.closed) {
56
+ return;
57
+ }
58
+ this.closed = true;
59
+ this.error = error;
60
+ this.active?.sent?.reject(error);
61
+ this.active = null;
62
+ for (const item of this.items.splice(0)) {
63
+ item.sent?.reject(error);
64
+ }
65
+ this.signal?.resolve();
66
+ this.signal = null;
67
+ }
68
+ fail(error) {
69
+ this.close(error);
70
+ }
71
+ async *stream() {
72
+ try {
73
+ while (true) {
74
+ while (this.items.length === 0 && !this.closed) {
75
+ this.signal ?? (this.signal = new completer_js_1.Completer());
76
+ await this.signal.fut;
77
+ }
78
+ const item = this.items.shift();
79
+ if (item == null) {
80
+ return;
81
+ }
82
+ this.active = item;
83
+ yield item.content;
84
+ item.sent?.resolve();
85
+ this.active = null;
86
+ }
87
+ }
88
+ finally {
89
+ if (this.active != null && this.closed) {
90
+ this.active.sent?.reject(this.error ?? new room_server_client_js_1.RoomServerException("the messaging stream is closed"));
91
+ this.active = null;
92
+ }
93
+ }
94
+ }
95
+ }
96
+ class MessagingStream {
97
+ constructor({ streamId, remoteParticipantId, input, output, inputClosed, onClosed, }) {
98
+ this.events = [];
99
+ this.eventSignal = null;
100
+ this.state = "accepted";
101
+ this.streamId = streamId;
102
+ this.remoteParticipantId = remoteParticipantId;
103
+ this.input = input;
104
+ this.output = output;
105
+ this.inputDone = Promise.resolve(inputClosed).catch((error) => {
106
+ this.input.fail(error);
107
+ this.push({
108
+ kind: "closed",
109
+ streamId: this.streamId,
110
+ reason: "error",
111
+ message: error instanceof Error ? error.message : String(error),
112
+ });
113
+ void this.output.return?.();
114
+ this.finish();
115
+ });
116
+ this.onClosed = onClosed;
117
+ void this.consumeOutput();
118
+ }
119
+ get closed() {
120
+ return this.state === "terminal";
121
+ }
122
+ push(event) {
123
+ if (this.state !== "accepted") {
124
+ return;
125
+ }
126
+ this.events.push(event);
127
+ this.eventSignal?.resolve();
128
+ this.eventSignal = null;
129
+ }
130
+ finish() {
131
+ if (this.state === "terminal") {
132
+ return;
133
+ }
134
+ this.state = "terminal";
135
+ this.input.close();
136
+ this.eventSignal?.resolve();
137
+ this.eventSignal = null;
138
+ void this.inputDone.finally(this.onClosed);
139
+ }
140
+ async consumeOutput() {
141
+ try {
142
+ while (true) {
143
+ const next = await this.output.next();
144
+ if (next.done) {
145
+ return;
146
+ }
147
+ if (next.value instanceof response_js_1.ControlContent && next.value.method === "close") {
148
+ this.push({
149
+ kind: "client_disconnected",
150
+ streamId: this.streamId,
151
+ participantId: this.remoteParticipantId,
152
+ });
153
+ return;
154
+ }
155
+ if (!(next.value instanceof response_js_1.JsonContent)) {
156
+ throw new room_server_client_js_1.RoomServerException("unexpected chunk from messaging.stream");
157
+ }
158
+ const value = next.value.json;
159
+ const kind = value["kind"];
160
+ if (kind === "message") {
161
+ const type = value["type"];
162
+ const message = value["message"];
163
+ if (typeof type !== "string" || typeof message !== "object" || message == null || Array.isArray(message)) {
164
+ throw new room_server_client_js_1.RoomServerException("invalid message chunk from messaging.stream");
165
+ }
166
+ const encodedAttachment = value["attachment_base64"];
167
+ this.push({
168
+ kind: "message",
169
+ message: new room_event_js_1.RoomMessage({
170
+ fromParticipantId: this.remoteParticipantId,
171
+ type,
172
+ message: message,
173
+ attachment: typeof encodedAttachment === "string" && encodedAttachment !== ""
174
+ ? base64ToBytes(encodedAttachment)
175
+ : undefined,
176
+ }),
177
+ });
178
+ }
179
+ else if (kind === "client_disconnected" && typeof value["participant_id"] === "string") {
180
+ this.push({
181
+ kind,
182
+ streamId: this.streamId,
183
+ participantId: value["participant_id"],
184
+ });
185
+ return;
186
+ }
187
+ else if (kind === "closed") {
188
+ this.push({
189
+ kind,
190
+ streamId: this.streamId,
191
+ reason: typeof value["reason"] === "string" ? value["reason"] : "closed",
192
+ message: typeof value["message"] === "string" ? value["message"] : undefined,
193
+ });
194
+ return;
195
+ }
196
+ else {
197
+ throw new room_server_client_js_1.RoomServerException("unexpected chunk from messaging.stream");
198
+ }
199
+ }
200
+ }
201
+ catch (error) {
202
+ this.input.fail(error);
203
+ this.push({
204
+ kind: "closed",
205
+ streamId: this.streamId,
206
+ reason: "error",
207
+ message: error instanceof Error ? error.message : String(error),
208
+ });
209
+ }
210
+ finally {
211
+ this.finish();
212
+ }
213
+ }
214
+ content({ type, message, attachment, }) {
215
+ return new response_js_1.JsonContent({
216
+ json: {
217
+ type,
218
+ message_json: JSON.stringify(message),
219
+ ...(attachment == null ? {} : { attachment_base64: bytesToBase64(attachment) }),
220
+ },
221
+ });
222
+ }
223
+ sendMessageNowait(params) {
224
+ if (this.state !== "accepted") {
225
+ throw new room_server_client_js_1.RoomServerException("the messaging stream is closed");
226
+ }
227
+ void this.input.enqueue(this.content(params));
228
+ }
229
+ async sendMessage(params) {
230
+ if (this.state !== "accepted") {
231
+ throw new room_server_client_js_1.RoomServerException("the messaging stream is closed");
232
+ }
233
+ await this.input.enqueue(this.content(params), { wait: true });
234
+ }
235
+ async close() {
236
+ if (this.state === "terminal") {
237
+ await this.inputDone;
238
+ return;
239
+ }
240
+ this.input.close();
241
+ try {
242
+ await this.inputDone;
243
+ }
244
+ finally {
245
+ await this.output.return?.();
246
+ this.finish();
247
+ }
248
+ }
249
+ clientDisconnected(participantId) {
250
+ if (this.state === "terminal") {
251
+ return;
252
+ }
253
+ this.push({
254
+ kind: "client_disconnected",
255
+ streamId: this.streamId,
256
+ participantId,
257
+ });
258
+ this.finish();
259
+ }
260
+ async *[Symbol.asyncIterator]() {
261
+ while (true) {
262
+ while (this.events.length === 0 && this.state !== "terminal") {
263
+ this.eventSignal ?? (this.eventSignal = new completer_js_1.Completer());
264
+ await this.eventSignal.fut;
265
+ }
266
+ const event = this.events.shift();
267
+ if (event == null) {
268
+ return;
269
+ }
270
+ yield event;
271
+ }
272
+ }
273
+ }
274
+ exports.MessagingStream = MessagingStream;
25
275
  class MessagingClient extends event_emitter_js_1.EventEmitter {
26
276
  constructor({ room }) {
27
277
  super();
@@ -31,6 +281,7 @@ class MessagingClient extends event_emitter_js_1.EventEmitter {
31
281
  this._messageQueued = null;
32
282
  this._sendTask = null;
33
283
  this._sendOperations = new Set();
284
+ this._streams = new Set();
34
285
  this._messageQueueClosed = false;
35
286
  this._desiredEnabled = false;
36
287
  this._online = false;
@@ -96,6 +347,7 @@ class MessagingClient extends event_emitter_js_1.EventEmitter {
96
347
  await sendTask;
97
348
  }
98
349
  await Promise.all(this._sendOperations);
350
+ await Promise.all([...this._streams].map(async (stream) => stream.close()));
99
351
  this._desiredEnabled = false;
100
352
  this._clearCurrentConnectionState();
101
353
  }
@@ -159,6 +411,9 @@ class MessagingClient extends event_emitter_js_1.EventEmitter {
159
411
  }
160
412
  }
161
413
  _onRoomDisconnect({ reason: _reason }) {
414
+ for (const stream of [...this._streams]) {
415
+ stream.clientDisconnected(this.client.localParticipant?.id ?? stream.remoteParticipantId);
416
+ }
162
417
  this._clearCurrentConnectionState();
163
418
  }
164
419
  _onRoomReconnect() {
@@ -351,6 +606,49 @@ class MessagingClient extends event_emitter_js_1.EventEmitter {
351
606
  throw error;
352
607
  }
353
608
  }
609
+ async stream({ to, type, message, attachment, }) {
610
+ const input = new MessagingStreamInput();
611
+ void input.enqueue(new response_js_1.JsonContent({
612
+ json: {
613
+ to_participant_id: to.id,
614
+ type,
615
+ message_json: JSON.stringify(message),
616
+ ...(attachment == null ? {} : { attachment_base64: bytesToBase64(attachment) }),
617
+ },
618
+ }));
619
+ const outputResult = await this.client.invokeToolCall({
620
+ toolkit: "messaging",
621
+ tool: "stream",
622
+ input: input.stream(),
623
+ streamInput: true,
624
+ });
625
+ if (outputResult.kind !== "stream") {
626
+ input.close();
627
+ throw new room_server_client_js_1.RoomServerException("unexpected return type from messaging.stream");
628
+ }
629
+ const outputIterable = outputResult.stream;
630
+ const output = outputIterable[Symbol.asyncIterator]();
631
+ const accepted = await output.next();
632
+ if (accepted.done
633
+ || !(accepted.value instanceof response_js_1.JsonContent)
634
+ || accepted.value.json["kind"] !== "accepted"
635
+ || typeof accepted.value.json["stream_id"] !== "string") {
636
+ input.close();
637
+ await output.return?.();
638
+ throw new room_server_client_js_1.RoomServerException("messaging.stream did not acknowledge acceptance");
639
+ }
640
+ let stream;
641
+ stream = new MessagingStream({
642
+ streamId: accepted.value.json["stream_id"],
643
+ remoteParticipantId: to.id,
644
+ input,
645
+ output,
646
+ inputClosed: outputResult.inputClosed,
647
+ onClosed: () => this._streams.delete(stream),
648
+ });
649
+ this._streams.add(stream);
650
+ return stream;
651
+ }
354
652
  getParticipants() {
355
653
  return this.remoteParticipants;
356
654
  }
@@ -444,6 +742,9 @@ class MessagingClient extends event_emitter_js_1.EventEmitter {
444
742
  this._wakeMessageQueue();
445
743
  this._drainQueuedMessages({ error });
446
744
  this._desiredEnabled = false;
745
+ for (const stream of [...this._streams]) {
746
+ stream.clientDisconnected(this.client.localParticipant?.id ?? stream.remoteParticipantId);
747
+ }
447
748
  this._clearCurrentConnectionState();
448
749
  this.client.protocol.removeHandler("messaging.send", this._messageHandler);
449
750
  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
+ }