@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/CHANGELOG.md +5 -0
- package/dist/browser/agent.js +6 -6
- package/dist/browser/meshagent-client.d.ts +2 -1
- package/dist/browser/meshagent-client.js +1 -0
- package/dist/browser/messaging-client.d.ts +72 -1
- package/dist/browser/messaging-client.js +302 -1
- package/dist/browser/participant-token.d.ts +1 -0
- package/dist/browser/participant-token.js +3 -0
- package/dist/browser/room-client.d.ts +2 -1
- package/dist/browser/room-client.js +25 -12
- package/dist/esm/agent.js +6 -6
- package/dist/esm/meshagent-client.d.ts +2 -1
- package/dist/esm/meshagent-client.js +1 -0
- package/dist/esm/messaging-client.d.ts +72 -1
- package/dist/esm/messaging-client.js +301 -1
- package/dist/esm/participant-token.d.ts +1 -0
- package/dist/esm/participant-token.js +3 -0
- package/dist/esm/room-client.d.ts +2 -1
- package/dist/esm/room-client.js +25 -12
- package/dist/node/agent.js +6 -6
- package/dist/node/meshagent-client.d.ts +2 -1
- package/dist/node/meshagent-client.js +1 -0
- package/dist/node/messaging-client.d.ts +72 -1
- package/dist/node/messaging-client.js +302 -1
- package/dist/node/participant-token.d.ts +1 -0
- package/dist/node/participant-token.js +3 -0
- package/dist/node/room-client.d.ts +2 -1
- package/dist/node/room-client.js +25 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
## [0.47.1]
|
|
2
|
+
- 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.
|
|
3
|
+
- Reworked messaging and room client flow control so dispatch ordering, close behavior, and presence updates stay consistent under streaming loads.
|
|
4
|
+
- Expanded stream and presence test coverage for the new messaging behavior.
|
|
5
|
+
|
|
1
6
|
## [0.47.0]
|
|
2
7
|
- Improved TypeScript messaging clients so room delivery order and startup/presence behavior stay consistent.
|
|
3
8
|
- Moved managed-agent client routing onto the Rust tunnel path and hardened agent chat-client event handling.
|
package/dist/browser/agent.js
CHANGED
|
@@ -530,10 +530,10 @@ class _RemoteToolkitWrapper {
|
|
|
530
530
|
return false;
|
|
531
531
|
}
|
|
532
532
|
}
|
|
533
|
-
|
|
533
|
+
_sendToolCallResponseChunk(messageId, toolCallId, chunk) {
|
|
534
534
|
const [header, payload] = (0, utils_js_1.unpackMessage)(chunk.pack());
|
|
535
535
|
try {
|
|
536
|
-
|
|
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 (!
|
|
637
|
+
if (!this._sendToolCallResponseChunk(messageId, toolCallId, chunk))
|
|
638
638
|
return;
|
|
639
639
|
}
|
|
640
|
-
|
|
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
|
-
|
|
651
|
+
this._sendToolCallResponseChunk(messageId, toolCallId, new response_js_1.ErrorContent({ text: String(error) }));
|
|
652
652
|
}
|
|
653
|
-
|
|
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,
|
|
@@ -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 |
|
|
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 {
|
|
@@ -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();
|
|
@@ -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;
|
|
@@ -1323,6 +1323,9 @@ class ParticipantToken {
|
|
|
1323
1323
|
addRoomGrant(roomName) {
|
|
1324
1324
|
this.grants.push(new ParticipantGrant({ name: "room", scope: roomName }));
|
|
1325
1325
|
}
|
|
1326
|
+
addSiteGrant(roomName) {
|
|
1327
|
+
this.grants.push(new ParticipantGrant({ name: "site", scope: roomName }));
|
|
1328
|
+
}
|
|
1326
1329
|
addApiGrant(grant) {
|
|
1327
1330
|
for (const existingGrant of this.grants) {
|
|
1328
1331
|
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
|
|
253
|
+
private _dispatchToolCallRequestChunk;
|
|
253
254
|
private _streamInvokeToolRequestChunks;
|
|
254
255
|
private _decodeToolCallContent;
|
|
255
256
|
private _handleToolCallResponseChunk;
|
|
@@ -1147,6 +1147,11 @@ class RoomClient {
|
|
|
1147
1147
|
this._sendLocalAttributesNowait(attributes);
|
|
1148
1148
|
}
|
|
1149
1149
|
async sendRequest(type, request, data, afterSend) {
|
|
1150
|
+
const response = this._dispatchRequest(type, request, data);
|
|
1151
|
+
afterSend?.();
|
|
1152
|
+
return await response;
|
|
1153
|
+
}
|
|
1154
|
+
_dispatchRequest(type, request, data) {
|
|
1150
1155
|
this._raiseIfTerminal();
|
|
1151
1156
|
if (this._entered && !this._connected && !this._allowDisconnectedRequests) {
|
|
1152
1157
|
throw this._disconnectedError({ baseMessage: "room connection is disconnected" });
|
|
@@ -1155,9 +1160,8 @@ class RoomClient {
|
|
|
1155
1160
|
const completer = new completer_js_1.Completer();
|
|
1156
1161
|
this._pendingRequests.set(requestId, completer);
|
|
1157
1162
|
try {
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
return await completer.fut;
|
|
1163
|
+
this._protocolInstance.sendNowait(type, (0, utils_js_1.packMessage)(request, data), { id: requestId });
|
|
1164
|
+
return completer.fut;
|
|
1161
1165
|
}
|
|
1162
1166
|
catch (error) {
|
|
1163
1167
|
this._pendingRequests.delete(requestId);
|
|
@@ -1263,7 +1267,6 @@ class RoomClient {
|
|
|
1263
1267
|
let requestTask;
|
|
1264
1268
|
if (params.streamInput === true) {
|
|
1265
1269
|
request["arguments"] = { type: "control", method: "open" };
|
|
1266
|
-
requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
|
|
1267
1270
|
}
|
|
1268
1271
|
else {
|
|
1269
1272
|
const input = params.input;
|
|
@@ -1280,9 +1283,14 @@ class RoomClient {
|
|
|
1280
1283
|
if (params.onBehalfOfId != null) {
|
|
1281
1284
|
request["on_behalf_of_id"] = params.onBehalfOfId;
|
|
1282
1285
|
}
|
|
1286
|
+
const responseRequest = this._dispatchRequest("room.invoke_tool", request, requestData);
|
|
1287
|
+
params.afterSend?.();
|
|
1288
|
+
if (params.streamInput === true) {
|
|
1289
|
+
requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
|
|
1290
|
+
}
|
|
1283
1291
|
try {
|
|
1284
1292
|
const response = await Promise.race([
|
|
1285
|
-
|
|
1293
|
+
responseRequest,
|
|
1286
1294
|
preOpenError.fut,
|
|
1287
1295
|
]);
|
|
1288
1296
|
this._toolCallPreOpenErrors.delete(toolCallId);
|
|
@@ -1337,9 +1345,10 @@ class RoomClient {
|
|
|
1337
1345
|
if (params.onBehalfOfId != null) {
|
|
1338
1346
|
request["on_behalf_of_id"] = params.onBehalfOfId;
|
|
1339
1347
|
}
|
|
1348
|
+
const responseRequest = this._dispatchRequest("room.invoke_tool", request);
|
|
1340
1349
|
const requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
|
|
1341
1350
|
try {
|
|
1342
|
-
const response = await
|
|
1351
|
+
const response = await responseRequest;
|
|
1343
1352
|
await requestTask;
|
|
1344
1353
|
if (response instanceof response_js_1.ControlContent && response.method === "open") {
|
|
1345
1354
|
throw new room_server_client_js_1.RoomServerException(`unexpected return type from ${params.toolkit}.${params.tool}`);
|
|
@@ -1370,6 +1379,7 @@ class RoomClient {
|
|
|
1370
1379
|
if (params.onBehalfOfId != null) {
|
|
1371
1380
|
request["on_behalf_of_id"] = params.onBehalfOfId;
|
|
1372
1381
|
}
|
|
1382
|
+
const responseRequest = this._dispatchRequest("room.invoke_tool", request);
|
|
1373
1383
|
const requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
|
|
1374
1384
|
void requestTask.catch((error) => {
|
|
1375
1385
|
const stream = this._toolCallStreams.get(toolCallId);
|
|
@@ -1383,7 +1393,7 @@ class RoomClient {
|
|
|
1383
1393
|
let response;
|
|
1384
1394
|
try {
|
|
1385
1395
|
response = await Promise.race([
|
|
1386
|
-
|
|
1396
|
+
responseRequest,
|
|
1387
1397
|
preOpenError.fut,
|
|
1388
1398
|
]);
|
|
1389
1399
|
}
|
|
@@ -1405,24 +1415,27 @@ class RoomClient {
|
|
|
1405
1415
|
},
|
|
1406
1416
|
};
|
|
1407
1417
|
}
|
|
1408
|
-
|
|
1418
|
+
_dispatchToolCallRequestChunk(toolCallId, chunk) {
|
|
1419
|
+
this._raiseIfTerminal();
|
|
1420
|
+
if (this._entered && !this._connected && !this._allowDisconnectedRequests) {
|
|
1421
|
+
throw this._disconnectedError({ baseMessage: "room connection is disconnected" });
|
|
1422
|
+
}
|
|
1409
1423
|
const packed = chunk.pack();
|
|
1410
1424
|
const request = {
|
|
1411
1425
|
tool_call_id: toolCallId,
|
|
1412
1426
|
chunk: JSON.parse((0, utils_js_1.splitMessageHeader)(packed)),
|
|
1413
1427
|
};
|
|
1414
1428
|
const payload = (0, utils_js_1.splitMessagePayload)(packed);
|
|
1415
|
-
|
|
1429
|
+
this._protocolInstance.sendNowait("room.tool_call_request_chunk", (0, utils_js_1.packMessage)(request, payload.length > 0 ? payload : undefined));
|
|
1416
1430
|
}
|
|
1417
1431
|
async _streamInvokeToolRequestChunks(toolCallId, input) {
|
|
1418
|
-
await Promise.resolve();
|
|
1419
1432
|
try {
|
|
1420
1433
|
for await (const item of input) {
|
|
1421
|
-
|
|
1434
|
+
this._dispatchToolCallRequestChunk(toolCallId, item);
|
|
1422
1435
|
}
|
|
1423
1436
|
}
|
|
1424
1437
|
finally {
|
|
1425
|
-
|
|
1438
|
+
this._dispatchToolCallRequestChunk(toolCallId, new response_js_1.ControlContent({ method: "close" }));
|
|
1426
1439
|
}
|
|
1427
1440
|
}
|
|
1428
1441
|
_decodeToolCallContent(params) {
|