@meshagent/meshagent-agents 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.
- package/CHANGELOG.md +15 -0
- package/README.md +28 -5
- package/dist/browser/agent-messages.d.ts +1448 -107
- package/dist/browser/agent-messages.js +345 -193
- package/dist/browser/chat-client.d.ts +129 -35
- package/dist/browser/chat-client.js +631 -203
- package/dist/browser/images-dataset.d.ts +1 -1
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +8 -0
- package/dist/browser/thread-storage.d.ts +1 -1
- package/dist/esm/agent-messages.d.ts +1448 -107
- package/dist/esm/agent-messages.js +340 -189
- package/dist/esm/chat-client.d.ts +129 -35
- package/dist/esm/chat-client.js +623 -203
- package/dist/esm/images-dataset.d.ts +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/thread-storage.d.ts +1 -1
- package/dist/node/agent-messages.d.ts +1448 -107
- package/dist/node/agent-messages.js +345 -193
- package/dist/node/chat-client.d.ts +129 -35
- package/dist/node/chat-client.js +631 -203
- package/dist/node/images-dataset.d.ts +1 -1
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.js +8 -0
- package/dist/node/thread-storage.d.ts +1 -1
- package/package.json +7 -3
package/dist/node/chat-client.js
CHANGED
|
@@ -1,19 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ChatThreadSession = exports.MessagingChatClient = exports.BaseChatClient = exports.ChatThreadStartResult = exports.ChatThreadSessionLoadState = exports.ChatThreadSessionLoadPhase = exports.PendingAgentInput = exports.AgentMessageEvent = void 0;
|
|
6
|
+
exports.ChatThreadSession = exports.MessagingChatClient = exports.WebSocketChatClient = exports.BaseMessagingChatClient = exports.ThreadListSubscribe = exports.ThreadSubscribe = exports.messagingChatThreadListSubscribeType = exports.messagingChatThreadSubscribeType = exports.BaseChatClient = exports.PendingThreadStartCandidate = exports.ChatThreadStartResult = exports.ChatThreadSessionLoadState = exports.ChatThreadSessionLoadPhase = exports.PendingAgentInput = exports.AgentMessageEvent = void 0;
|
|
4
7
|
const meshagent_1 = require("@meshagent/meshagent");
|
|
8
|
+
const msgpack_1 = require("@msgpack/msgpack");
|
|
9
|
+
const isomorphic_ws_1 = __importDefault(require("isomorphic-ws"));
|
|
5
10
|
const uuid_1 = require("uuid");
|
|
6
11
|
const agent_messages_js_1 = require("./agent-messages.js");
|
|
7
12
|
const change_emitter_js_1 = require("./change-emitter.js");
|
|
8
13
|
class AgentMessageEvent {
|
|
9
14
|
constructor({ message, createdAt, attachment }) {
|
|
10
|
-
this.
|
|
11
|
-
this.
|
|
15
|
+
this.listeners = new Set();
|
|
16
|
+
this.messageValue = message;
|
|
17
|
+
this.createdAt = createdAt ?? message.createdAtUtc ?? new Date();
|
|
12
18
|
this.attachment = attachment;
|
|
13
19
|
}
|
|
20
|
+
get message() {
|
|
21
|
+
return this.messageValue;
|
|
22
|
+
}
|
|
14
23
|
get payload() {
|
|
15
24
|
return { ...this.message.toJson(), created_at: this.createdAt.toISOString() };
|
|
16
25
|
}
|
|
26
|
+
addEventListener(listener) {
|
|
27
|
+
this.listeners.add(listener);
|
|
28
|
+
}
|
|
29
|
+
removeEventListener(listener) {
|
|
30
|
+
this.listeners.delete(listener);
|
|
31
|
+
}
|
|
32
|
+
replaceMessage(message) {
|
|
33
|
+
this.messageValue = message;
|
|
34
|
+
for (const listener of [...this.listeners]) {
|
|
35
|
+
listener();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
17
38
|
}
|
|
18
39
|
exports.AgentMessageEvent = AgentMessageEvent;
|
|
19
40
|
function agentRoomMessageFromEvent(event) {
|
|
@@ -99,6 +120,13 @@ class ChatThreadStartResult {
|
|
|
99
120
|
}
|
|
100
121
|
}
|
|
101
122
|
exports.ChatThreadStartResult = ChatThreadStartResult;
|
|
123
|
+
class PendingThreadStartCandidate {
|
|
124
|
+
constructor({ messageId, senderName }) {
|
|
125
|
+
this.messageId = messageId;
|
|
126
|
+
this.senderName = senderName;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
exports.PendingThreadStartCandidate = PendingThreadStartCandidate;
|
|
102
130
|
class EventQueue {
|
|
103
131
|
constructor() {
|
|
104
132
|
this.subscribers = new Set();
|
|
@@ -151,12 +179,15 @@ class EventQueue {
|
|
|
151
179
|
}
|
|
152
180
|
}
|
|
153
181
|
class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
|
|
154
|
-
constructor() {
|
|
155
|
-
super(
|
|
182
|
+
constructor({ threadCreatedPendingStartMatcher, deduplicateClientToolRequests = false, } = {}) {
|
|
183
|
+
super();
|
|
156
184
|
this.sessionsByPath = new Map();
|
|
157
185
|
this.pendingStartRequests = new Map();
|
|
158
186
|
this.pendingSessionEventsByPath = new Map();
|
|
159
187
|
this.eventQueue = new EventQueue();
|
|
188
|
+
this.claimedClientToolRequests = new Set();
|
|
189
|
+
this.threadCreatedPendingStartMatcher = threadCreatedPendingStartMatcher;
|
|
190
|
+
this.deduplicateClientToolRequests = deduplicateClientToolRequests;
|
|
160
191
|
}
|
|
161
192
|
get events() {
|
|
162
193
|
return this.eventQueue.stream;
|
|
@@ -178,14 +209,41 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
|
|
|
178
209
|
localParticipantId() {
|
|
179
210
|
return undefined;
|
|
180
211
|
}
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
212
|
+
clientToolRequestKey(threadPath, requestId) {
|
|
213
|
+
const normalizedThreadPath = threadPath.trim();
|
|
214
|
+
const normalizedRequestId = requestId.trim();
|
|
215
|
+
if (normalizedThreadPath === "" || normalizedRequestId === "") {
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
return `${normalizedThreadPath}\n${normalizedRequestId}`;
|
|
219
|
+
}
|
|
220
|
+
claimClientToolRequest(threadPath, requestId) {
|
|
221
|
+
if (!this.deduplicateClientToolRequests) {
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
const key = this.clientToolRequestKey(threadPath, requestId);
|
|
225
|
+
if (key == null) {
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
if (this.claimedClientToolRequests.has(key)) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
this.claimedClientToolRequests.add(key);
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
finishClientToolRequest(threadPath, requestId, { responseSent }) {
|
|
235
|
+
if (!this.deduplicateClientToolRequests || responseSent) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const key = this.clientToolRequestKey(threadPath, requestId);
|
|
239
|
+
if (key != null) {
|
|
240
|
+
this.claimedClientToolRequests.delete(key);
|
|
241
|
+
}
|
|
184
242
|
}
|
|
185
243
|
cleanParticipantName(value) {
|
|
186
244
|
return nonEmptyTrimmed(value);
|
|
187
245
|
}
|
|
188
|
-
openThread(threadPath, { load = true, sinceTurn, reloadIfOpen = false,
|
|
246
|
+
openThread(threadPath, { load = true, sinceTurn, reloadIfOpen = false, } = {}) {
|
|
189
247
|
const normalized = threadPath.trim();
|
|
190
248
|
if (normalized === "") {
|
|
191
249
|
throw new TypeError("thread path cannot be empty");
|
|
@@ -197,11 +255,8 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
|
|
|
197
255
|
}
|
|
198
256
|
return existing;
|
|
199
257
|
}
|
|
200
|
-
const created =
|
|
258
|
+
const created = ChatThreadSession.create({ client: this, threadPath: normalized });
|
|
201
259
|
this.sessionsByPath.set(normalized, created);
|
|
202
|
-
if (drainPendingEvents) {
|
|
203
|
-
this.drainPendingSessionEvents(normalized, created);
|
|
204
|
-
}
|
|
205
260
|
this.openSession(created, { load, sinceTurn });
|
|
206
261
|
this.notifyListeners();
|
|
207
262
|
return created;
|
|
@@ -211,7 +266,7 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
|
|
|
211
266
|
this.notifyListeners();
|
|
212
267
|
});
|
|
213
268
|
}
|
|
214
|
-
async startThread({ messageId, message, attachments, name, backend, provider, model, voice, outputModalities, realtimeProtocol, senderName, clientToolkits,
|
|
269
|
+
async startThread({ messageId, message, attachments, name, backend, provider, model, voice, outputModalities, realtimeProtocol, senderName, clientToolkits, omitContent = false, }) {
|
|
215
270
|
const resolvedMessageId = messageId == null || messageId.trim() === "" ? (0, uuid_1.v4)() : messageId.trim();
|
|
216
271
|
const resolvedSenderName = this.cleanParticipantName(senderName) ?? this.localParticipantName();
|
|
217
272
|
const payload = new agent_messages_js_1.StartThread({
|
|
@@ -226,10 +281,9 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
|
|
|
226
281
|
realtimeProtocol: nonEmptyTrimmed(realtimeProtocol),
|
|
227
282
|
senderName: resolvedSenderName,
|
|
228
283
|
clientToolkits: clientToolkits != null && clientToolkits.length > 0 ? clientToolkits : undefined,
|
|
229
|
-
toolChoice: toolChoice?.toJson(),
|
|
230
284
|
});
|
|
231
285
|
const completer = new meshagent_1.Completer();
|
|
232
|
-
this.pendingStartRequests.set(resolvedMessageId, completer);
|
|
286
|
+
this.pendingStartRequests.set(resolvedMessageId, { request: payload, completer });
|
|
233
287
|
try {
|
|
234
288
|
await this.sendAgentMessage(payload);
|
|
235
289
|
const response = await completer.fut;
|
|
@@ -239,18 +293,18 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
|
|
|
239
293
|
if (threadPath === "") {
|
|
240
294
|
throw new Error("Agent did not return a thread_id for the new thread.");
|
|
241
295
|
}
|
|
242
|
-
const session = this.openThread(threadPath, { load: false
|
|
296
|
+
const session = this.openThread(threadPath, { load: false });
|
|
243
297
|
if (!omitContent) {
|
|
244
298
|
session.addAgentMessage(new AgentMessageEvent({
|
|
245
299
|
message: this.resolvedStartThreadMessage({ payload, threadPath }),
|
|
246
|
-
createdAt:
|
|
300
|
+
createdAt: payload.createdAtUtc,
|
|
247
301
|
}));
|
|
248
302
|
}
|
|
249
303
|
this.drainPendingSessionEvents(threadPath, session);
|
|
250
304
|
return new ChatThreadStartResult({
|
|
251
305
|
session,
|
|
252
306
|
threadPath,
|
|
253
|
-
realtimeConnection: response
|
|
307
|
+
realtimeConnection: response instanceof agent_messages_js_1.ThreadStarted && response.realtimeConnection != null
|
|
254
308
|
? response.realtimeConnection.toJson()
|
|
255
309
|
: undefined,
|
|
256
310
|
});
|
|
@@ -274,6 +328,7 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
|
|
|
274
328
|
mcp: payload.mcp,
|
|
275
329
|
clientToolkits: payload.clientToolkits,
|
|
276
330
|
toolkits: payload.toolkits,
|
|
331
|
+
toolChoice: payload.toolChoice,
|
|
277
332
|
});
|
|
278
333
|
}
|
|
279
334
|
drainPendingSessionEvents(threadPath, session) {
|
|
@@ -287,9 +342,13 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
|
|
|
287
342
|
}
|
|
288
343
|
}
|
|
289
344
|
handleAgentMessage(message, { createdAt, attachment } = {}) {
|
|
290
|
-
if (message instanceof agent_messages_js_1.AgentClientToolCallRequested
|
|
291
|
-
|
|
292
|
-
|
|
345
|
+
if (message instanceof agent_messages_js_1.AgentClientToolCallRequested) {
|
|
346
|
+
const targetParticipantId = message.targetParticipantId?.trim();
|
|
347
|
+
if (targetParticipantId != null &&
|
|
348
|
+
targetParticipantId !== "" &&
|
|
349
|
+
targetParticipantId !== this.localParticipantId()) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
293
352
|
}
|
|
294
353
|
if (message.type === agent_messages_js_1.agentConnectionStatusType) {
|
|
295
354
|
this._connectionStatus = message;
|
|
@@ -300,10 +359,13 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
|
|
|
300
359
|
sourceMessageId != null &&
|
|
301
360
|
sourceMessageId.trim() !== "") {
|
|
302
361
|
const pending = this.pendingStartRequests.get(sourceMessageId.trim());
|
|
303
|
-
if (pending != null && !pending.completed) {
|
|
304
|
-
pending.complete(message);
|
|
362
|
+
if (pending != null && !pending.completer.completed) {
|
|
363
|
+
pending.completer.complete(message);
|
|
305
364
|
}
|
|
306
365
|
}
|
|
366
|
+
if (message instanceof agent_messages_js_1.ThreadCreated) {
|
|
367
|
+
this.acknowledgePendingStartFromThreadCreated(message);
|
|
368
|
+
}
|
|
307
369
|
const threadPath = "threadId" in message ? String(message.threadId).trim() : "";
|
|
308
370
|
if (threadPath === "") {
|
|
309
371
|
this.notifyListeners();
|
|
@@ -321,6 +383,40 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
|
|
|
321
383
|
}
|
|
322
384
|
this.notifyListeners();
|
|
323
385
|
}
|
|
386
|
+
acknowledgePendingStartFromThreadCreated(message) {
|
|
387
|
+
const matcher = this.threadCreatedPendingStartMatcher;
|
|
388
|
+
const threadPath = String(message.thread?.path ?? "").trim();
|
|
389
|
+
if (matcher == null || threadPath === "") {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
const candidates = [...this.pendingStartRequests.entries()]
|
|
393
|
+
.filter(([, pending]) => !pending.completer.completed)
|
|
394
|
+
.map(([messageId, pending]) => new PendingThreadStartCandidate({
|
|
395
|
+
messageId,
|
|
396
|
+
senderName: pending.request.senderName,
|
|
397
|
+
}));
|
|
398
|
+
if (candidates.length === 0) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
let matchedMessageId;
|
|
402
|
+
try {
|
|
403
|
+
matchedMessageId = matcher(message, candidates)?.trim();
|
|
404
|
+
}
|
|
405
|
+
catch {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (matchedMessageId == null || matchedMessageId === "") {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
const pending = this.pendingStartRequests.get(matchedMessageId);
|
|
412
|
+
if (pending == null || pending.completer.completed) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
pending.completer.complete(new agent_messages_js_1.ThreadStarted({
|
|
416
|
+
sourceMessageId: matchedMessageId,
|
|
417
|
+
threadId: threadPath,
|
|
418
|
+
}));
|
|
419
|
+
}
|
|
324
420
|
emitConnectionStatus({ status, message, reason, retryDelay, }) {
|
|
325
421
|
const event = new agent_messages_js_1.AgentConnectionStatus({
|
|
326
422
|
status,
|
|
@@ -343,9 +439,51 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
|
|
|
343
439
|
}
|
|
344
440
|
}
|
|
345
441
|
exports.BaseChatClient = BaseChatClient;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
442
|
+
exports.messagingChatThreadSubscribeType = "meshagent.chat.thread.subscribe";
|
|
443
|
+
exports.messagingChatThreadListSubscribeType = "meshagent.chat.thread_list.subscribe";
|
|
444
|
+
class ThreadSubscribe {
|
|
445
|
+
constructor({ threadId, stream }) {
|
|
446
|
+
this.threadId = threadId;
|
|
447
|
+
this.stream = stream;
|
|
448
|
+
}
|
|
449
|
+
get closed() {
|
|
450
|
+
return this.stream.closed;
|
|
451
|
+
}
|
|
452
|
+
async send(message, { attachment } = {}) {
|
|
453
|
+
await this.stream.sendMessage({
|
|
454
|
+
type: agent_messages_js_1.agentRoomMessageType,
|
|
455
|
+
message: message.toJson(),
|
|
456
|
+
attachment,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
async close() {
|
|
460
|
+
await this.stream.close();
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
exports.ThreadSubscribe = ThreadSubscribe;
|
|
464
|
+
class ThreadListSubscribe {
|
|
465
|
+
constructor({ stream }) {
|
|
466
|
+
this.stream = stream;
|
|
467
|
+
}
|
|
468
|
+
get closed() {
|
|
469
|
+
return this.stream.closed;
|
|
470
|
+
}
|
|
471
|
+
async close() {
|
|
472
|
+
await this.stream.close();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
exports.ThreadListSubscribe = ThreadListSubscribe;
|
|
476
|
+
function participantWaitAbortedError(reason) {
|
|
477
|
+
if (reason instanceof Error) {
|
|
478
|
+
return reason;
|
|
479
|
+
}
|
|
480
|
+
const error = new Error("Agent participant wait was cancelled.");
|
|
481
|
+
error.name = "AbortError";
|
|
482
|
+
return error;
|
|
483
|
+
}
|
|
484
|
+
class BaseMessagingChatClient extends BaseChatClient {
|
|
485
|
+
constructor({ room, agentName, threadCreatedPendingStartMatcher, deduplicateClientToolRequests = true, }) {
|
|
486
|
+
super({ threadCreatedPendingStartMatcher, deduplicateClientToolRequests });
|
|
349
487
|
this.pendingParticipantWaits = new Map();
|
|
350
488
|
this.started = false;
|
|
351
489
|
this.hasConnected = false;
|
|
@@ -442,7 +580,7 @@ class MessagingChatClient extends BaseChatClient {
|
|
|
442
580
|
this.attachMessagingListeners();
|
|
443
581
|
this.room.messaging.start();
|
|
444
582
|
this.startRoomStatusListener();
|
|
445
|
-
|
|
583
|
+
this.room.messaging.enable();
|
|
446
584
|
this.waitingForParticipant = true;
|
|
447
585
|
this.onMessagingChanged();
|
|
448
586
|
}
|
|
@@ -493,7 +631,13 @@ class MessagingChatClient extends BaseChatClient {
|
|
|
493
631
|
}
|
|
494
632
|
return null;
|
|
495
633
|
}
|
|
496
|
-
async waitForAgentParticipant({ waitKey } = {}) {
|
|
634
|
+
async waitForAgentParticipant({ waitKey, signal, timeoutMs, } = {}) {
|
|
635
|
+
if (signal?.aborted) {
|
|
636
|
+
throw participantWaitAbortedError(signal.reason);
|
|
637
|
+
}
|
|
638
|
+
if (timeoutMs != null && (!Number.isFinite(timeoutMs) || timeoutMs < 0)) {
|
|
639
|
+
throw new RangeError("Agent participant wait timeout must be a non-negative finite number.");
|
|
640
|
+
}
|
|
497
641
|
const existing = this.agentParticipant();
|
|
498
642
|
if (existing != null) {
|
|
499
643
|
return existing;
|
|
@@ -501,12 +645,34 @@ class MessagingChatClient extends BaseChatClient {
|
|
|
501
645
|
const key = waitKey == null || waitKey.trim() === "" ? (0, uuid_1.v4)() : waitKey.trim();
|
|
502
646
|
const completer = new meshagent_1.Completer();
|
|
503
647
|
this.pendingParticipantWaits.set(key, completer);
|
|
648
|
+
const handleAbort = () => {
|
|
649
|
+
if (!completer.completed) {
|
|
650
|
+
completer.completeError(participantWaitAbortedError(signal?.reason));
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
signal?.addEventListener("abort", handleAbort, { once: true });
|
|
654
|
+
if (signal?.aborted) {
|
|
655
|
+
handleAbort();
|
|
656
|
+
}
|
|
657
|
+
const timeout = timeoutMs == null
|
|
658
|
+
? undefined
|
|
659
|
+
: setTimeout(() => {
|
|
660
|
+
if (!completer.completed) {
|
|
661
|
+
completer.completeError(new Error(`Agent messaging participant was not available within ${timeoutMs}ms.`));
|
|
662
|
+
}
|
|
663
|
+
}, timeoutMs);
|
|
504
664
|
this.onMessagingChanged();
|
|
505
665
|
try {
|
|
506
666
|
return await completer.fut;
|
|
507
667
|
}
|
|
508
668
|
finally {
|
|
509
|
-
|
|
669
|
+
signal?.removeEventListener("abort", handleAbort);
|
|
670
|
+
if (timeout != null) {
|
|
671
|
+
clearTimeout(timeout);
|
|
672
|
+
}
|
|
673
|
+
if (this.pendingParticipantWaits.get(key) === completer) {
|
|
674
|
+
this.pendingParticipantWaits.delete(key);
|
|
675
|
+
}
|
|
510
676
|
}
|
|
511
677
|
}
|
|
512
678
|
async sendAgentMessage(message, { attachment } = {}) {
|
|
@@ -560,6 +726,372 @@ class MessagingChatClient extends BaseChatClient {
|
|
|
560
726
|
}
|
|
561
727
|
}
|
|
562
728
|
}
|
|
729
|
+
exports.BaseMessagingChatClient = BaseMessagingChatClient;
|
|
730
|
+
class WebSocketChatClient extends BaseChatClient {
|
|
731
|
+
constructor({ url, token, participantName, protocols = ["meshagent-msgpack"], reconnect = true, reconnectInitialDelay = 1000, reconnectMaxDelay = 10000, }) {
|
|
732
|
+
super({ deduplicateClientToolRequests: true });
|
|
733
|
+
this.started = false;
|
|
734
|
+
this.stopping = false;
|
|
735
|
+
this.connecting = false;
|
|
736
|
+
this.reconnectAttempts = 0;
|
|
737
|
+
this.onMessage = (event) => {
|
|
738
|
+
try {
|
|
739
|
+
const bytes = webSocketMessageBytes(event.data);
|
|
740
|
+
if (bytes == null) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
const decoded = (0, msgpack_1.decode)(bytes);
|
|
744
|
+
if (!isJsonMap(decoded)) {
|
|
745
|
+
throw new Error("chat websocket received a non-object message");
|
|
746
|
+
}
|
|
747
|
+
const message = agent_messages_js_1.AgentMessage.fromJson(decoded);
|
|
748
|
+
if (message instanceof agent_messages_js_1.AgentConnectionStatus) {
|
|
749
|
+
const participantId = message.participantId?.trim();
|
|
750
|
+
if (participantId != null && participantId !== "") {
|
|
751
|
+
this.assignedParticipantId = participantId;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
this.handleAgentMessage(message);
|
|
755
|
+
}
|
|
756
|
+
catch (error) {
|
|
757
|
+
this.receiveError = error;
|
|
758
|
+
this.emitConnectionStatus({
|
|
759
|
+
status: "disconnected",
|
|
760
|
+
message: "Chat websocket message could not be decoded",
|
|
761
|
+
reason: describeError(error),
|
|
762
|
+
});
|
|
763
|
+
this.webSocket?.close(1003);
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
this.onSocketError = (event) => {
|
|
767
|
+
this.receiveError = event.error ?? new Error("chat websocket error");
|
|
768
|
+
};
|
|
769
|
+
this.onSocketClose = (event) => {
|
|
770
|
+
const webSocket = this.webSocket;
|
|
771
|
+
if (webSocket == null) {
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
this.closeCode = event.code;
|
|
775
|
+
this.closeReason = event.reason;
|
|
776
|
+
this.detachSocketListeners(webSocket);
|
|
777
|
+
this.webSocket = undefined;
|
|
778
|
+
this.assignedParticipantId = undefined;
|
|
779
|
+
if (this.stopping || !this.started) {
|
|
780
|
+
this.notifyListeners();
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
const reason = event.reason !== "" ? `code=${event.code}: ${event.reason}` : `code=${event.code}`;
|
|
784
|
+
if (this.reconnect) {
|
|
785
|
+
this.scheduleReconnect({ reason });
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
this.emitConnectionStatus({
|
|
789
|
+
status: "disconnected",
|
|
790
|
+
message: "Chat websocket disconnected",
|
|
791
|
+
reason,
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
this.notifyListeners();
|
|
795
|
+
};
|
|
796
|
+
this.url = url;
|
|
797
|
+
this.token = token;
|
|
798
|
+
this.participantName = participantName;
|
|
799
|
+
this.protocols = protocols;
|
|
800
|
+
this.reconnect = reconnect;
|
|
801
|
+
this.reconnectInitialDelay = reconnectInitialDelay;
|
|
802
|
+
this.reconnectMaxDelay = reconnectMaxDelay;
|
|
803
|
+
}
|
|
804
|
+
get isConnected() {
|
|
805
|
+
return this.webSocket != null && this.webSocket.readyState === isomorphic_ws_1.default.OPEN;
|
|
806
|
+
}
|
|
807
|
+
localParticipantName() {
|
|
808
|
+
return this.cleanParticipantName(this.participantName);
|
|
809
|
+
}
|
|
810
|
+
localParticipantId() {
|
|
811
|
+
return this.assignedParticipantId;
|
|
812
|
+
}
|
|
813
|
+
async start() {
|
|
814
|
+
this.started = true;
|
|
815
|
+
this.stopping = false;
|
|
816
|
+
if (this.webSocket != null || this.connecting) {
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
await this.connect({ isReconnect: false });
|
|
820
|
+
}
|
|
821
|
+
async connect({ isReconnect }) {
|
|
822
|
+
if (this.webSocket != null || this.connecting || !this.started || this.stopping) {
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
this.connecting = true;
|
|
826
|
+
const webSocket = new isomorphic_ws_1.default(this.url.toString(), this.resolvedProtocols());
|
|
827
|
+
this.webSocket = webSocket;
|
|
828
|
+
this.assignedParticipantId = undefined;
|
|
829
|
+
webSocket.binaryType = "arraybuffer";
|
|
830
|
+
try {
|
|
831
|
+
await new Promise((resolve, reject) => {
|
|
832
|
+
const onOpen = () => {
|
|
833
|
+
cleanup();
|
|
834
|
+
resolve();
|
|
835
|
+
};
|
|
836
|
+
const onError = (event) => {
|
|
837
|
+
cleanup();
|
|
838
|
+
reject(event.error ?? new Error("chat websocket connection failed"));
|
|
839
|
+
};
|
|
840
|
+
const cleanup = () => {
|
|
841
|
+
webSocket.removeEventListener("open", onOpen);
|
|
842
|
+
webSocket.removeEventListener("error", onError);
|
|
843
|
+
};
|
|
844
|
+
webSocket.addEventListener("open", onOpen);
|
|
845
|
+
webSocket.addEventListener("error", onError);
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
catch (error) {
|
|
849
|
+
if (this.webSocket === webSocket) {
|
|
850
|
+
this.webSocket = undefined;
|
|
851
|
+
}
|
|
852
|
+
this.receiveError = error;
|
|
853
|
+
this.connecting = false;
|
|
854
|
+
if (isReconnect && this.reconnect && !this.stopping) {
|
|
855
|
+
this.scheduleReconnect({ reason: describeError(error) });
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
this.emitConnectionStatus({
|
|
859
|
+
status: "disconnected",
|
|
860
|
+
message: "Chat websocket connection failed",
|
|
861
|
+
reason: describeError(error),
|
|
862
|
+
});
|
|
863
|
+
throw error;
|
|
864
|
+
}
|
|
865
|
+
webSocket.addEventListener("message", this.onMessage);
|
|
866
|
+
webSocket.addEventListener("error", this.onSocketError);
|
|
867
|
+
webSocket.addEventListener("close", this.onSocketClose);
|
|
868
|
+
this.connecting = false;
|
|
869
|
+
this.receiveError = undefined;
|
|
870
|
+
this.closeCode = undefined;
|
|
871
|
+
this.closeReason = undefined;
|
|
872
|
+
const status = isReconnect ? "reconnected" : "connected";
|
|
873
|
+
this.emitConnectionStatus({
|
|
874
|
+
status,
|
|
875
|
+
message: isReconnect ? "Chat websocket reconnected" : "Chat websocket connected",
|
|
876
|
+
});
|
|
877
|
+
if (isReconnect) {
|
|
878
|
+
await this.reopenOpenSessions();
|
|
879
|
+
}
|
|
880
|
+
this.reconnectAttempts = 0;
|
|
881
|
+
this.notifyListeners();
|
|
882
|
+
}
|
|
883
|
+
async stop() {
|
|
884
|
+
this.started = false;
|
|
885
|
+
this.stopping = true;
|
|
886
|
+
if (this.reconnectTimer != null) {
|
|
887
|
+
clearTimeout(this.reconnectTimer);
|
|
888
|
+
this.reconnectTimer = undefined;
|
|
889
|
+
}
|
|
890
|
+
const webSocket = this.webSocket;
|
|
891
|
+
this.webSocket = undefined;
|
|
892
|
+
this.assignedParticipantId = undefined;
|
|
893
|
+
if (webSocket != null) {
|
|
894
|
+
this.detachSocketListeners(webSocket);
|
|
895
|
+
webSocket.close(1000);
|
|
896
|
+
}
|
|
897
|
+
this.emitConnectionStatus({
|
|
898
|
+
status: "disconnected",
|
|
899
|
+
message: "Chat websocket disconnected",
|
|
900
|
+
reason: "client_stopped",
|
|
901
|
+
});
|
|
902
|
+
this.stopping = false;
|
|
903
|
+
this.notifyListeners();
|
|
904
|
+
}
|
|
905
|
+
async sendAgentMessage(message, { attachment } = {}) {
|
|
906
|
+
if (attachment != null) {
|
|
907
|
+
throw new Error("WebSocketChatClient does not support binary attachments yet.");
|
|
908
|
+
}
|
|
909
|
+
const webSocket = this.webSocket;
|
|
910
|
+
if (webSocket == null || webSocket.readyState !== isomorphic_ws_1.default.OPEN) {
|
|
911
|
+
throw new Error(this.closedMessage());
|
|
912
|
+
}
|
|
913
|
+
webSocket.send((0, msgpack_1.encode)(message.toJson()));
|
|
914
|
+
}
|
|
915
|
+
detachSocketListeners(webSocket) {
|
|
916
|
+
webSocket.removeEventListener("message", this.onMessage);
|
|
917
|
+
webSocket.removeEventListener("error", this.onSocketError);
|
|
918
|
+
webSocket.removeEventListener("close", this.onSocketClose);
|
|
919
|
+
}
|
|
920
|
+
scheduleReconnect({ reason }) {
|
|
921
|
+
if (this.reconnectTimer != null || this.stopping || !this.started) {
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
const retryDelay = this.nextReconnectDelay();
|
|
925
|
+
this.emitConnectionStatus({
|
|
926
|
+
status: "reconnecting",
|
|
927
|
+
message: "Chat websocket disconnected; reconnecting",
|
|
928
|
+
reason,
|
|
929
|
+
retryDelay,
|
|
930
|
+
});
|
|
931
|
+
this.reconnectTimer = setTimeout(() => {
|
|
932
|
+
this.reconnectTimer = undefined;
|
|
933
|
+
void this.connect({ isReconnect: true });
|
|
934
|
+
}, retryDelay);
|
|
935
|
+
}
|
|
936
|
+
nextReconnectDelay() {
|
|
937
|
+
const initialDelay = Math.max(1, this.reconnectInitialDelay);
|
|
938
|
+
const maxDelay = Math.max(initialDelay, this.reconnectMaxDelay);
|
|
939
|
+
const factor = 2 ** Math.min(this.reconnectAttempts, 6);
|
|
940
|
+
this.reconnectAttempts += 1;
|
|
941
|
+
return Math.min(initialDelay * factor, maxDelay);
|
|
942
|
+
}
|
|
943
|
+
async reopenOpenSessions() {
|
|
944
|
+
for (const session of this.sessions) {
|
|
945
|
+
if (!session.isOpen) {
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
await session.open({ load: true, sinceTurn: session.lastCompletedTurnId });
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
resolvedProtocols() {
|
|
952
|
+
const resolved = [];
|
|
953
|
+
for (const protocol of this.protocols) {
|
|
954
|
+
const normalized = protocol.trim();
|
|
955
|
+
if (normalized !== "" && !resolved.includes(normalized)) {
|
|
956
|
+
resolved.push(normalized);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
const normalizedToken = this.token.trim();
|
|
960
|
+
if (normalizedToken !== "") {
|
|
961
|
+
resolved.push(`meshagent-agent.${normalizedToken}`);
|
|
962
|
+
}
|
|
963
|
+
return resolved;
|
|
964
|
+
}
|
|
965
|
+
closedMessage() {
|
|
966
|
+
if (this.receiveError != null) {
|
|
967
|
+
return `chat websocket is closed: ${describeError(this.receiveError)}`;
|
|
968
|
+
}
|
|
969
|
+
if (this.closeCode != null) {
|
|
970
|
+
return `chat websocket is closed (code=${this.closeCode})`;
|
|
971
|
+
}
|
|
972
|
+
if (this.closeReason != null && this.closeReason.trim() !== "") {
|
|
973
|
+
return `chat websocket is closed: ${this.closeReason}`;
|
|
974
|
+
}
|
|
975
|
+
return "chat websocket is closed";
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
exports.WebSocketChatClient = WebSocketChatClient;
|
|
979
|
+
class MessagingChatClient extends BaseMessagingChatClient {
|
|
980
|
+
constructor() {
|
|
981
|
+
super(...arguments);
|
|
982
|
+
this.threadSubscriptions = new Map();
|
|
983
|
+
this.streamTasks = new Set();
|
|
984
|
+
}
|
|
985
|
+
async stop() {
|
|
986
|
+
const subscriptions = [
|
|
987
|
+
...this.threadSubscriptions.values(),
|
|
988
|
+
];
|
|
989
|
+
if (this.threadListSubscription != null) {
|
|
990
|
+
subscriptions.push(this.threadListSubscription);
|
|
991
|
+
}
|
|
992
|
+
this.threadSubscriptions.clear();
|
|
993
|
+
this.threadListSubscription = undefined;
|
|
994
|
+
await Promise.all(subscriptions.map(async (subscription) => subscription.close()));
|
|
995
|
+
await Promise.all(this.streamTasks);
|
|
996
|
+
await super.stop();
|
|
997
|
+
}
|
|
998
|
+
supportsThreadSubscriptions(participant) {
|
|
999
|
+
return participant.getAttribute("supports_messaging_streams") === true;
|
|
1000
|
+
}
|
|
1001
|
+
trackStream(subscription) {
|
|
1002
|
+
const task = (async () => {
|
|
1003
|
+
try {
|
|
1004
|
+
for await (const event of subscription.stream) {
|
|
1005
|
+
if (event.kind !== "message" || event.message.type !== agent_messages_js_1.agentRoomMessageType) {
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
const raw = event.message.message;
|
|
1009
|
+
if (typeof raw["type"] !== "string") {
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
this.handleAgentMessage(agent_messages_js_1.AgentMessage.fromJson(raw), {
|
|
1013
|
+
createdAt: dateOrNull(raw["created_at"]),
|
|
1014
|
+
attachment: event.message.attachment,
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
finally {
|
|
1019
|
+
if (subscription instanceof ThreadSubscribe) {
|
|
1020
|
+
if (this.threadSubscriptions.get(subscription.threadId) === subscription) {
|
|
1021
|
+
this.threadSubscriptions.delete(subscription.threadId);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
else if (this.threadListSubscription === subscription) {
|
|
1025
|
+
this.threadListSubscription = undefined;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
})();
|
|
1029
|
+
this.streamTasks.add(task);
|
|
1030
|
+
void task.finally(() => this.streamTasks.delete(task));
|
|
1031
|
+
}
|
|
1032
|
+
async sendAgentMessage(message, { attachment } = {}) {
|
|
1033
|
+
await this.start();
|
|
1034
|
+
const participant = this.agentParticipant();
|
|
1035
|
+
if (participant == null) {
|
|
1036
|
+
throw new Error("Agent messaging participant is not available.");
|
|
1037
|
+
}
|
|
1038
|
+
if (!this.supportsThreadSubscriptions(participant)) {
|
|
1039
|
+
await super.sendAgentMessage(message, { attachment });
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
if (message instanceof agent_messages_js_1.OpenThread) {
|
|
1043
|
+
const previous = this.threadSubscriptions.get(message.threadId);
|
|
1044
|
+
if (previous != null) {
|
|
1045
|
+
await previous.close();
|
|
1046
|
+
}
|
|
1047
|
+
const stream = await this.room.messaging.stream({
|
|
1048
|
+
to: participant,
|
|
1049
|
+
type: exports.messagingChatThreadSubscribeType,
|
|
1050
|
+
message: message.toJson(),
|
|
1051
|
+
attachment,
|
|
1052
|
+
});
|
|
1053
|
+
const subscription = new ThreadSubscribe({ threadId: message.threadId, stream });
|
|
1054
|
+
this.threadSubscriptions.set(message.threadId, subscription);
|
|
1055
|
+
this.trackStream(subscription);
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
if (message instanceof agent_messages_js_1.CloseThread) {
|
|
1059
|
+
const subscription = this.threadSubscriptions.get(message.threadId);
|
|
1060
|
+
if (subscription != null) {
|
|
1061
|
+
this.threadSubscriptions.delete(message.threadId);
|
|
1062
|
+
await subscription.close();
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
if (message instanceof agent_messages_js_1.WatchThreads) {
|
|
1067
|
+
await this.threadListSubscription?.close();
|
|
1068
|
+
const stream = await this.room.messaging.stream({
|
|
1069
|
+
to: participant,
|
|
1070
|
+
type: exports.messagingChatThreadListSubscribeType,
|
|
1071
|
+
message: message.toJson(),
|
|
1072
|
+
attachment,
|
|
1073
|
+
});
|
|
1074
|
+
const subscription = new ThreadListSubscribe({ stream });
|
|
1075
|
+
this.threadListSubscription = subscription;
|
|
1076
|
+
this.trackStream(subscription);
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
if (message instanceof agent_messages_js_1.UnwatchThreads && this.threadListSubscription != null) {
|
|
1080
|
+
const subscription = this.threadListSubscription;
|
|
1081
|
+
this.threadListSubscription = undefined;
|
|
1082
|
+
await subscription.close();
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
if ("threadId" in message && typeof message.threadId === "string") {
|
|
1086
|
+
const subscription = this.threadSubscriptions.get(message.threadId);
|
|
1087
|
+
if (subscription != null) {
|
|
1088
|
+
await subscription.send(message, { attachment });
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
await super.sendAgentMessage(message, { attachment });
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
563
1095
|
exports.MessagingChatClient = MessagingChatClient;
|
|
564
1096
|
function isThreadListMessage(message) {
|
|
565
1097
|
return message.type === agent_messages_js_1.agentThreadListedType
|
|
@@ -583,6 +1115,9 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
583
1115
|
this.client = client;
|
|
584
1116
|
this.threadPath = threadPath;
|
|
585
1117
|
}
|
|
1118
|
+
static create({ client, threadPath }) {
|
|
1119
|
+
return new ChatThreadSession({ client, threadPath });
|
|
1120
|
+
}
|
|
586
1121
|
get messages() {
|
|
587
1122
|
return [...this._messages];
|
|
588
1123
|
}
|
|
@@ -601,18 +1136,6 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
601
1136
|
get lastCompletedTurnId() {
|
|
602
1137
|
return this.lastCompletedTurnIdValue;
|
|
603
1138
|
}
|
|
604
|
-
get activeTurnId() {
|
|
605
|
-
return this.activeTurnIdValue;
|
|
606
|
-
}
|
|
607
|
-
get threadStatus() {
|
|
608
|
-
return this.threadStatusValue;
|
|
609
|
-
}
|
|
610
|
-
get currentModel() {
|
|
611
|
-
return this.currentModelValue;
|
|
612
|
-
}
|
|
613
|
-
get modelsResponse() {
|
|
614
|
-
return this.modelsResponseValue;
|
|
615
|
-
}
|
|
616
1139
|
async open({ load = true, sinceTurn } = {}) {
|
|
617
1140
|
if (this.openState) {
|
|
618
1141
|
if (load) {
|
|
@@ -627,7 +1150,6 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
627
1150
|
}
|
|
628
1151
|
catch (error) {
|
|
629
1152
|
this.setLoadState(this.failedLoadState(error));
|
|
630
|
-
throw error;
|
|
631
1153
|
}
|
|
632
1154
|
}
|
|
633
1155
|
return;
|
|
@@ -646,7 +1168,6 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
646
1168
|
}
|
|
647
1169
|
catch (error) {
|
|
648
1170
|
this.setLoadState(this.failedLoadState(error));
|
|
649
|
-
throw error;
|
|
650
1171
|
}
|
|
651
1172
|
}
|
|
652
1173
|
resetReplayState() {
|
|
@@ -693,16 +1214,20 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
693
1214
|
response,
|
|
694
1215
|
}));
|
|
695
1216
|
}
|
|
696
|
-
|
|
1217
|
+
claimClientToolCall(requestId) {
|
|
1218
|
+
return this.client.claimClientToolRequest(this.threadPath, requestId);
|
|
1219
|
+
}
|
|
1220
|
+
finishClientToolCall(requestId, { responseSent }) {
|
|
1221
|
+
this.client.finishClientToolRequest(this.threadPath, requestId, { responseSent });
|
|
1222
|
+
}
|
|
1223
|
+
injectMessages(messages) {
|
|
1224
|
+
return this.client.sendAgentMessage(new agent_messages_js_1.InjectMessages({
|
|
1225
|
+
threadId: this.threadPath,
|
|
1226
|
+
messages: [...messages],
|
|
1227
|
+
}));
|
|
1228
|
+
}
|
|
1229
|
+
async sendText({ messageId, text, attachments, steer = false, turnId, backend, provider, model, voice, outputModalities, senderName, clientToolkits, }) {
|
|
697
1230
|
const resolvedMessageId = messageId == null || messageId.trim() === "" ? (0, uuid_1.v4)() : messageId.trim();
|
|
698
|
-
const currentModel = this.currentModelValue;
|
|
699
|
-
const resolvedBackend = nonEmptyTrimmed(backend) ?? currentModel?.backend;
|
|
700
|
-
const resolvedProvider = nonEmptyTrimmed(provider) ?? currentModel?.provider;
|
|
701
|
-
const resolvedModel = nonEmptyTrimmed(model) ?? currentModel?.model;
|
|
702
|
-
const resolvedVoice = nonEmptyTrimmed(voice) ?? currentModel?.voice;
|
|
703
|
-
const resolvedOutputModalities = outputModalities != null && outputModalities.length > 0
|
|
704
|
-
? outputModalities
|
|
705
|
-
: currentModel?.outputModalities;
|
|
706
1231
|
const payload = steer
|
|
707
1232
|
? new agent_messages_js_1.TurnSteer({
|
|
708
1233
|
threadId: this.threadPath,
|
|
@@ -716,13 +1241,12 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
716
1241
|
messageId: resolvedMessageId,
|
|
717
1242
|
senderName: nonEmptyTrimmed(senderName),
|
|
718
1243
|
content: (0, agent_messages_js_1.agentInputContent)({ text, attachments }),
|
|
719
|
-
backend:
|
|
720
|
-
provider:
|
|
721
|
-
model:
|
|
722
|
-
voice:
|
|
723
|
-
outputModalities:
|
|
1244
|
+
backend: nonEmptyTrimmed(backend),
|
|
1245
|
+
provider: nonEmptyTrimmed(provider),
|
|
1246
|
+
model: nonEmptyTrimmed(model),
|
|
1247
|
+
voice: nonEmptyTrimmed(voice),
|
|
1248
|
+
outputModalities: outputModalities != null && outputModalities.length > 0 ? outputModalities : undefined,
|
|
724
1249
|
clientToolkits: clientToolkits != null && clientToolkits.length > 0 ? clientToolkits : undefined,
|
|
725
|
-
toolChoice: toolChoice?.toJson(),
|
|
726
1250
|
});
|
|
727
1251
|
this.localAgentMessageIds.add(resolvedMessageId);
|
|
728
1252
|
this.pendingLocalInputMessageIds.add(resolvedMessageId);
|
|
@@ -741,10 +1265,10 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
741
1265
|
await this.client.sendAgentMessage(new agent_messages_js_1.AgentRealtimeAudioChunk({
|
|
742
1266
|
threadId: this.threadPath,
|
|
743
1267
|
messageId: (0, uuid_1.v4)(),
|
|
744
|
-
format,
|
|
1268
|
+
format: new agent_messages_js_1.AgentAudioFormat(format),
|
|
745
1269
|
}), { attachment: chunk });
|
|
746
1270
|
}
|
|
747
|
-
async commitRealtimeAudio({ turnId, provider, model, voice, outputModalities, }) {
|
|
1271
|
+
async commitRealtimeAudio({ turnId, backend, provider, model, voice, outputModalities, }) {
|
|
748
1272
|
const messageId = (0, uuid_1.v4)();
|
|
749
1273
|
await this.client.sendAgentMessage(new agent_messages_js_1.AgentRealtimeAudioCommit({
|
|
750
1274
|
threadId: this.threadPath,
|
|
@@ -755,6 +1279,7 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
755
1279
|
threadId: this.threadPath,
|
|
756
1280
|
messageId: (0, uuid_1.v4)(),
|
|
757
1281
|
turnId,
|
|
1282
|
+
backend: nonEmptyTrimmed(backend),
|
|
758
1283
|
provider: nonEmptyTrimmed(provider),
|
|
759
1284
|
model: nonEmptyTrimmed(model),
|
|
760
1285
|
voice: nonEmptyTrimmed(voice),
|
|
@@ -783,56 +1308,9 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
783
1308
|
const turnId = normalizedString(threadMessageValue(message, "turnId"));
|
|
784
1309
|
if (turnId != null) {
|
|
785
1310
|
this.lastCompletedTurnIdValue = turnId;
|
|
786
|
-
if (this.activeTurnIdValue === turnId) {
|
|
787
|
-
this.activeTurnIdValue = undefined;
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
const statusTurnId = normalizedString(this.threadStatusValue?.turnId);
|
|
791
|
-
if (statusTurnId == null || statusTurnId === turnId) {
|
|
792
|
-
this.threadStatusValue = undefined;
|
|
793
1311
|
}
|
|
794
|
-
return;
|
|
795
1312
|
}
|
|
796
|
-
if (message.type === agent_messages_js_1.
|
|
797
|
-
const turnId = normalizedString(threadMessageValue(message, "turnId"));
|
|
798
|
-
if (turnId != null) {
|
|
799
|
-
this.activeTurnIdValue = turnId;
|
|
800
|
-
}
|
|
801
|
-
if (message.type === agent_messages_js_1.agentTurnStartedType) {
|
|
802
|
-
const statusTurnId = normalizedString(this.threadStatusValue?.turnId);
|
|
803
|
-
if (statusTurnId == null || statusTurnId === turnId) {
|
|
804
|
-
this.threadStatusValue = undefined;
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
return;
|
|
808
|
-
}
|
|
809
|
-
if (message.type === agent_messages_js_1.agentThreadStatusType && message instanceof agent_messages_js_1.AgentThreadStatus) {
|
|
810
|
-
const mode = normalizedString(message.mode);
|
|
811
|
-
const status = normalizedString(message.status);
|
|
812
|
-
const turnId = normalizedString(message.turnId);
|
|
813
|
-
if (status == null) {
|
|
814
|
-
this.threadStatusValue = undefined;
|
|
815
|
-
if (turnId != null && this.activeTurnIdValue === turnId) {
|
|
816
|
-
this.activeTurnIdValue = undefined;
|
|
817
|
-
}
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
this.threadStatusValue = message;
|
|
821
|
-
if (mode === "steerable" && turnId != null) {
|
|
822
|
-
this.activeTurnIdValue = turnId;
|
|
823
|
-
}
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
|
-
if (message.type === agent_messages_js_1.agentModelChangedType && message instanceof agent_messages_js_1.AgentModelChanged) {
|
|
827
|
-
this.currentModelValue = message;
|
|
828
|
-
return;
|
|
829
|
-
}
|
|
830
|
-
if (message.type === agent_messages_js_1.agentModelsResponseType && message instanceof agent_messages_js_1.ModelsResponse) {
|
|
831
|
-
this.modelsResponseValue = message;
|
|
832
|
-
this.applyActiveModel(message);
|
|
833
|
-
return;
|
|
834
|
-
}
|
|
835
|
-
if (message.type === agent_messages_js_1.agentThreadLoadedType) {
|
|
1313
|
+
else if (message.type === agent_messages_js_1.agentThreadLoadedType) {
|
|
836
1314
|
this.setLoadState(new ChatThreadSessionLoadState({
|
|
837
1315
|
phase: ChatThreadSessionLoadPhase.Loaded,
|
|
838
1316
|
startedAt: this.loadStateValue.startedAt,
|
|
@@ -910,45 +1388,21 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
910
1388
|
}
|
|
911
1389
|
appendRenderableMessage(event) {
|
|
912
1390
|
const message = event.message;
|
|
913
|
-
if (message
|
|
914
|
-
if (message.error != null) {
|
|
915
|
-
this.appendMessage(event);
|
|
916
|
-
}
|
|
917
|
-
return;
|
|
918
|
-
}
|
|
919
|
-
if (message.type === agent_messages_js_1.agentToolCallEndedType) {
|
|
920
|
-
const itemId = message.error == null ? undefined : normalizedString(message.itemId);
|
|
921
|
-
if (itemId != null) {
|
|
922
|
-
this.upsertItemMessage(`tool_call:${itemId}`, event);
|
|
923
|
-
}
|
|
924
|
-
else if (message.error != null) {
|
|
925
|
-
this.appendMessage(event);
|
|
926
|
-
}
|
|
927
|
-
return;
|
|
928
|
-
}
|
|
929
|
-
if (message.type === agent_messages_js_1.agentToolCallPendingType || message.type === agent_messages_js_1.agentToolCallInProgressType || message.type === agent_messages_js_1.agentToolCallStartedType) {
|
|
930
|
-
const itemId = normalizedString(message.itemId);
|
|
931
|
-
if (itemId != null) {
|
|
932
|
-
this.upsertItemMessage(`tool_call:${itemId}`, event);
|
|
933
|
-
}
|
|
934
|
-
return;
|
|
935
|
-
}
|
|
936
|
-
if (message.type === agent_messages_js_1.agentSecretRequestedType) {
|
|
937
|
-
this.appendMessage(event);
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
if (message.type === agent_messages_js_1.agentModelChangedType) {
|
|
1391
|
+
if (message instanceof agent_messages_js_1.TurnEnded) {
|
|
941
1392
|
this.appendMessage(event);
|
|
942
1393
|
return;
|
|
943
1394
|
}
|
|
944
|
-
if (message
|
|
945
|
-
if (agentInputText(message
|
|
1395
|
+
if (message instanceof agent_messages_js_1.StartThread || message instanceof agent_messages_js_1.TurnStart || message instanceof agent_messages_js_1.TurnSteer) {
|
|
1396
|
+
if (agentInputText(messageValue(message, "content")).trim() !== "") {
|
|
946
1397
|
this.appendMessage(event);
|
|
1398
|
+
const messageId = normalizedString(message.messageId);
|
|
1399
|
+
if (messageId != null)
|
|
1400
|
+
this.pendingLocalInputMessageIds.add(messageId);
|
|
947
1401
|
}
|
|
948
1402
|
return;
|
|
949
1403
|
}
|
|
950
|
-
if (message
|
|
951
|
-
const sourceMessageId = normalizedString(
|
|
1404
|
+
if (message instanceof agent_messages_js_1.TurnStartAccepted) {
|
|
1405
|
+
const sourceMessageId = normalizedString(message.sourceMessageId);
|
|
952
1406
|
if (sourceMessageId != null && this.localAgentMessageIds.has(sourceMessageId)) {
|
|
953
1407
|
this.pendingLocalInputMessageIds.delete(sourceMessageId);
|
|
954
1408
|
return;
|
|
@@ -956,36 +1410,21 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
956
1410
|
if (agentInputText(message.content).trim() !== "") {
|
|
957
1411
|
this.appendMessage(event, { beforePendingLocalInputs: true });
|
|
958
1412
|
}
|
|
959
|
-
else {
|
|
960
|
-
this.appendMessage(event);
|
|
961
|
-
}
|
|
962
|
-
return;
|
|
963
|
-
}
|
|
964
|
-
if (message.type === agent_messages_js_1.agentTurnStartedType ||
|
|
965
|
-
message.type === agent_messages_js_1.agentTurnSteeredType ||
|
|
966
|
-
message.type === agent_messages_js_1.agentTurnStartRejectedType ||
|
|
967
|
-
message.type === agent_messages_js_1.agentTurnSteerRejectedType ||
|
|
968
|
-
message.type === agent_messages_js_1.agentTurnInterruptAcceptedType ||
|
|
969
|
-
message.type === agent_messages_js_1.agentTurnInterruptedType) {
|
|
970
|
-
this.appendMessage(event);
|
|
971
1413
|
return;
|
|
972
1414
|
}
|
|
973
1415
|
if (isMergeableDelta(message)) {
|
|
974
1416
|
this.appendOrMergeDelta(event);
|
|
975
1417
|
return;
|
|
976
1418
|
}
|
|
977
|
-
if (message
|
|
978
|
-
|
|
979
|
-
if (itemId != null) {
|
|
980
|
-
this.upsertItemMessage(`image_generation:${itemId}`, event);
|
|
981
|
-
}
|
|
1419
|
+
if (message instanceof agent_messages_js_1.AgentImageGenerationPartial || message instanceof agent_messages_js_1.AgentImageGenerationCompleted) {
|
|
1420
|
+
this.appendMessage(event);
|
|
982
1421
|
return;
|
|
983
1422
|
}
|
|
984
|
-
if (message
|
|
1423
|
+
if (message instanceof agent_messages_js_1.AgentClientToolCallRequested) {
|
|
985
1424
|
this.appendMessage(event);
|
|
986
1425
|
return;
|
|
987
1426
|
}
|
|
988
|
-
if (message instanceof agent_messages_js_1.
|
|
1427
|
+
if (message instanceof agent_messages_js_1.AgentConnectionStatus || message instanceof agent_messages_js_1.AgentThreadMessage) {
|
|
989
1428
|
this.appendMessage(event);
|
|
990
1429
|
}
|
|
991
1430
|
}
|
|
@@ -1005,15 +1444,6 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
1005
1444
|
this.messageIndexes.set(normalizedMessageId, this._messages.length);
|
|
1006
1445
|
this._messages.push(event);
|
|
1007
1446
|
}
|
|
1008
|
-
upsertItemMessage(key, event) {
|
|
1009
|
-
const existingIndex = this.messageIndexes.get(key);
|
|
1010
|
-
if (existingIndex == null) {
|
|
1011
|
-
this.messageIndexes.set(key, this._messages.length);
|
|
1012
|
-
this._messages.push(event);
|
|
1013
|
-
return;
|
|
1014
|
-
}
|
|
1015
|
-
this._messages[existingIndex] = event;
|
|
1016
|
-
}
|
|
1017
1447
|
appendOrMergeDelta(event) {
|
|
1018
1448
|
const message = event.message;
|
|
1019
1449
|
const normalizedMessageId = normalizedString(message.messageId);
|
|
@@ -1023,7 +1453,7 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
1023
1453
|
}
|
|
1024
1454
|
this.mergedDeltaMessageIds.add(normalizedMessageId);
|
|
1025
1455
|
}
|
|
1026
|
-
const itemId = normalizedString(message
|
|
1456
|
+
const itemId = normalizedString(messageValue(message, "itemId"));
|
|
1027
1457
|
if (itemId == null) {
|
|
1028
1458
|
return;
|
|
1029
1459
|
}
|
|
@@ -1037,7 +1467,7 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
1037
1467
|
const existing = this._messages[existingIndex].message;
|
|
1038
1468
|
const merged = mergeDeltaMessage(existing, message);
|
|
1039
1469
|
if (merged != null) {
|
|
1040
|
-
this._messages[existingIndex]
|
|
1470
|
+
this._messages[existingIndex].replaceMessage(merged);
|
|
1041
1471
|
}
|
|
1042
1472
|
}
|
|
1043
1473
|
indexMessagesFrom(start) {
|
|
@@ -1048,29 +1478,6 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
1048
1478
|
}
|
|
1049
1479
|
}
|
|
1050
1480
|
}
|
|
1051
|
-
applyActiveModel(response) {
|
|
1052
|
-
for (const provider of response.providers) {
|
|
1053
|
-
for (const model of provider.models) {
|
|
1054
|
-
if (!model.active) {
|
|
1055
|
-
continue;
|
|
1056
|
-
}
|
|
1057
|
-
this.currentModelValue = new agent_messages_js_1.AgentModelChanged({
|
|
1058
|
-
threadId: this.threadPath,
|
|
1059
|
-
sourceMessageId: response.sourceMessageId,
|
|
1060
|
-
backend: provider.backend,
|
|
1061
|
-
provider: provider.name,
|
|
1062
|
-
model: model.name,
|
|
1063
|
-
voice: model.defaultOutputVoice,
|
|
1064
|
-
inputFormat: model.inputFormat,
|
|
1065
|
-
outputFormat: model.outputFormat,
|
|
1066
|
-
turnDetection: model.turnDetection,
|
|
1067
|
-
realtimeProtocols: model.realtimeProtocols,
|
|
1068
|
-
outputModalities: model.modalities.length > 0 ? [model.modalities[0]] : ["text"],
|
|
1069
|
-
});
|
|
1070
|
-
return;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
1481
|
markClosed({ notify = true } = {}) {
|
|
1075
1482
|
this.openState = false;
|
|
1076
1483
|
this.loadStateValue = new ChatThreadSessionLoadState({
|
|
@@ -1130,12 +1537,33 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
|
|
|
1130
1537
|
}
|
|
1131
1538
|
}
|
|
1132
1539
|
exports.ChatThreadSession = ChatThreadSession;
|
|
1540
|
+
function webSocketMessageBytes(value) {
|
|
1541
|
+
if (value instanceof Uint8Array) {
|
|
1542
|
+
return value;
|
|
1543
|
+
}
|
|
1544
|
+
if (value instanceof ArrayBuffer) {
|
|
1545
|
+
return new Uint8Array(value);
|
|
1546
|
+
}
|
|
1547
|
+
if (ArrayBuffer.isView(value)) {
|
|
1548
|
+
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
1549
|
+
}
|
|
1550
|
+
return undefined;
|
|
1551
|
+
}
|
|
1552
|
+
function isJsonMap(value) {
|
|
1553
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1554
|
+
}
|
|
1555
|
+
function describeError(error) {
|
|
1556
|
+
return error instanceof Error ? error.message : String(error);
|
|
1557
|
+
}
|
|
1133
1558
|
function sourceMessageIdFor(message) {
|
|
1134
1559
|
const value = "sourceMessageId" in message ? message.sourceMessageId : undefined;
|
|
1135
1560
|
return typeof value === "string" ? value : undefined;
|
|
1136
1561
|
}
|
|
1562
|
+
function messageValue(message, key) {
|
|
1563
|
+
return message[key];
|
|
1564
|
+
}
|
|
1137
1565
|
function threadMessageValue(message, key) {
|
|
1138
|
-
const value = message
|
|
1566
|
+
const value = messageValue(message, key);
|
|
1139
1567
|
return typeof value === "string" ? value : undefined;
|
|
1140
1568
|
}
|
|
1141
1569
|
function agentInputText(content) {
|
|
@@ -1163,7 +1591,7 @@ function isMergeableDelta(message) {
|
|
|
1163
1591
|
}
|
|
1164
1592
|
function messageIndexKey(message) {
|
|
1165
1593
|
if (isMergeableDelta(message)) {
|
|
1166
|
-
const itemId = normalizedString(message
|
|
1594
|
+
const itemId = normalizedString(messageValue(message, "itemId"));
|
|
1167
1595
|
return itemId == null ? undefined : `${message.type}:${itemId}`;
|
|
1168
1596
|
}
|
|
1169
1597
|
return normalizedString(message.messageId);
|
|
@@ -1176,30 +1604,30 @@ function mergeDeltaMessage(existing, incoming) {
|
|
|
1176
1604
|
const data = new Uint8Array(existing.data.length + incoming.data.length);
|
|
1177
1605
|
data.set(existing.data, 0);
|
|
1178
1606
|
data.set(incoming.data, existing.data.length);
|
|
1179
|
-
return
|
|
1607
|
+
return agent_messages_js_1.AgentAudioGenerationDelta.fromJson({ ...existing.toJson(), data });
|
|
1180
1608
|
}
|
|
1181
1609
|
if (existing.type === agent_messages_js_1.agentAudioTranscriptionDeltaType && existing instanceof agent_messages_js_1.AgentAudioTranscriptionDelta && incoming instanceof agent_messages_js_1.AgentAudioTranscriptionDelta) {
|
|
1182
|
-
return
|
|
1610
|
+
return agent_messages_js_1.AgentAudioTranscriptionDelta.fromJson({ ...existing.toJson(), text: `${existing.text}${incoming.text}` });
|
|
1183
1611
|
}
|
|
1184
1612
|
if (existing.type === agent_messages_js_1.agentFileContentDeltaType && incoming instanceof agent_messages_js_1.AgentFileContentDelta) {
|
|
1185
1613
|
return incoming;
|
|
1186
1614
|
}
|
|
1187
1615
|
if (existing.type === agent_messages_js_1.agentReasoningContentDeltaType && existing instanceof agent_messages_js_1.AgentReasoningContentDelta && incoming instanceof agent_messages_js_1.AgentReasoningContentDelta) {
|
|
1188
|
-
return
|
|
1616
|
+
return agent_messages_js_1.AgentReasoningContentDelta.fromJson({ ...existing.toJson(), text: `${existing.text}${incoming.text}` });
|
|
1189
1617
|
}
|
|
1190
1618
|
if (existing.type === agent_messages_js_1.agentTextContentDeltaType && existing instanceof agent_messages_js_1.AgentTextContentDelta && incoming instanceof agent_messages_js_1.AgentTextContentDelta) {
|
|
1191
|
-
return
|
|
1619
|
+
return agent_messages_js_1.AgentTextContentDelta.fromJson({ ...existing.toJson(), text: `${existing.text}${incoming.text}` });
|
|
1192
1620
|
}
|
|
1193
1621
|
if (existing.type === agent_messages_js_1.agentToolCallArgumentsDeltaType && existing instanceof agent_messages_js_1.AgentToolCallArgumentsDelta && incoming instanceof agent_messages_js_1.AgentToolCallArgumentsDelta) {
|
|
1194
|
-
return
|
|
1622
|
+
return agent_messages_js_1.AgentToolCallArgumentsDelta.fromJson({ ...existing.toJson(), delta: `${existing.delta}${incoming.delta}` });
|
|
1195
1623
|
}
|
|
1196
1624
|
if (existing.type === agent_messages_js_1.agentToolCallLogDeltaType && existing instanceof agent_messages_js_1.AgentToolCallLogDelta && incoming instanceof agent_messages_js_1.AgentToolCallLogDelta) {
|
|
1197
|
-
return
|
|
1625
|
+
return agent_messages_js_1.AgentToolCallLogDelta.fromJson({ ...existing.toJson(), lines: [...existing.lines, ...incoming.lines] });
|
|
1198
1626
|
}
|
|
1199
1627
|
return undefined;
|
|
1200
1628
|
}
|
|
1201
1629
|
function normalizedString(value) {
|
|
1202
|
-
const normalized = value
|
|
1630
|
+
const normalized = typeof value === "string" ? value.trim() : undefined;
|
|
1203
1631
|
return normalized == null || normalized === "" ? undefined : normalized;
|
|
1204
1632
|
}
|
|
1205
1633
|
function dateOrNull(value) {
|