@meshagent/meshagent-agents 0.43.3 → 0.44.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 +14 -0
- package/dist/browser/agent-messages.d.ts +6 -1
- package/dist/browser/agent-messages.js +12 -5
- package/dist/browser/chat-client.d.ts +23 -5
- package/dist/browser/chat-client.js +237 -30
- package/dist/browser/thread-storage.d.ts +1 -0
- package/dist/browser/thread-storage.js +18 -2
- package/dist/esm/agent-messages.d.ts +6 -1
- package/dist/esm/agent-messages.js +8 -1
- package/dist/esm/chat-client.d.ts +23 -5
- package/dist/esm/chat-client.js +238 -31
- package/dist/esm/thread-storage.d.ts +1 -0
- package/dist/esm/thread-storage.js +19 -3
- package/dist/node/agent-messages.d.ts +6 -1
- package/dist/node/agent-messages.js +12 -5
- package/dist/node/chat-client.d.ts +23 -5
- package/dist/node/chat-client.js +237 -30
- package/dist/node/thread-storage.d.ts +1 -0
- package/dist/node/thread-storage.js +18 -2
- package/package.json +2 -2
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { RemoteParticipant } from "@meshagent/meshagent";
|
|
2
2
|
import type { RoomClient, Content } from "@meshagent/meshagent";
|
|
3
|
-
import { AgentMessage, AgentModelChanged, AgentThreadStatus, ModelsResponse, AgentConnectionStatus } from "./agent-messages.js";
|
|
3
|
+
import { AgentMessage, AgentModelChanged, AgentThreadStatus, ModelsResponse, ToolChoice, AgentConnectionStatus } from "./agent-messages.js";
|
|
4
4
|
import type { AgentPayload, JsonMap, ClientToolkitDescription } from "./agent-messages.js";
|
|
5
5
|
import { ChangeEmitter } from "./change-emitter.js";
|
|
6
6
|
export declare class AgentMessageEvent {
|
|
7
7
|
readonly message: AgentMessage;
|
|
8
|
+
readonly createdAt: Date;
|
|
8
9
|
readonly attachment?: Uint8Array;
|
|
9
|
-
constructor({ message, attachment }: {
|
|
10
|
+
constructor({ message, createdAt, attachment }: {
|
|
10
11
|
message: AgentMessage;
|
|
12
|
+
createdAt?: Date;
|
|
11
13
|
attachment?: Uint8Array;
|
|
12
14
|
});
|
|
13
15
|
get payload(): AgentPayload;
|
|
@@ -96,7 +98,7 @@ export declare abstract class BaseChatClient extends ChangeEmitter {
|
|
|
96
98
|
drainPendingEvents?: boolean;
|
|
97
99
|
}): ChatThreadSession;
|
|
98
100
|
private openSession;
|
|
99
|
-
startThread({ messageId, message, attachments, name, backend, provider, model, voice, outputModalities, realtimeProtocol, senderName, clientToolkits, omitContent, }: {
|
|
101
|
+
startThread({ messageId, message, attachments, name, backend, provider, model, voice, outputModalities, realtimeProtocol, senderName, clientToolkits, toolChoice, omitContent, }: {
|
|
100
102
|
messageId?: string;
|
|
101
103
|
message: string;
|
|
102
104
|
attachments: string[];
|
|
@@ -109,14 +111,17 @@ export declare abstract class BaseChatClient extends ChangeEmitter {
|
|
|
109
111
|
realtimeProtocol?: string;
|
|
110
112
|
senderName?: string;
|
|
111
113
|
clientToolkits?: ClientToolkitDescription[];
|
|
114
|
+
toolChoice?: ToolChoice;
|
|
112
115
|
omitContent?: boolean;
|
|
113
116
|
}): Promise<ChatThreadStartResult>;
|
|
117
|
+
private resolvedStartThreadMessage;
|
|
114
118
|
abstract sendAgentMessage(message: AgentMessage, options?: {
|
|
115
119
|
attachment?: Uint8Array;
|
|
116
120
|
ignoreOffline?: boolean;
|
|
117
121
|
}): Promise<void>;
|
|
118
122
|
private drainPendingSessionEvents;
|
|
119
|
-
handleAgentMessage(message: AgentMessage, { attachment }?: {
|
|
123
|
+
handleAgentMessage(message: AgentMessage, { createdAt, attachment }?: {
|
|
124
|
+
createdAt?: Date;
|
|
120
125
|
attachment?: Uint8Array;
|
|
121
126
|
}): void;
|
|
122
127
|
emitConnectionStatus({ status, message, reason, retryDelay, }: {
|
|
@@ -132,6 +137,11 @@ export declare class MessagingChatClient extends BaseChatClient {
|
|
|
132
137
|
readonly agentName?: string;
|
|
133
138
|
private readonly pendingParticipantWaits;
|
|
134
139
|
private started;
|
|
140
|
+
private agentParticipantId?;
|
|
141
|
+
private hasConnected;
|
|
142
|
+
private waitingForParticipant;
|
|
143
|
+
private roomAbortController?;
|
|
144
|
+
private messagingListenersAttached;
|
|
135
145
|
constructor({ room, agentName }: {
|
|
136
146
|
room: RoomClient;
|
|
137
147
|
agentName?: string;
|
|
@@ -139,6 +149,8 @@ export declare class MessagingChatClient extends BaseChatClient {
|
|
|
139
149
|
localParticipantName(): string | undefined;
|
|
140
150
|
start(): Promise<void>;
|
|
141
151
|
stop(): Promise<void>;
|
|
152
|
+
private attachMessagingListeners;
|
|
153
|
+
private detachMessagingListeners;
|
|
142
154
|
agentParticipant(): RemoteParticipant | null;
|
|
143
155
|
waitForAgentParticipant({ waitKey }?: {
|
|
144
156
|
waitKey?: string;
|
|
@@ -148,6 +160,10 @@ export declare class MessagingChatClient extends BaseChatClient {
|
|
|
148
160
|
ignoreOffline?: boolean;
|
|
149
161
|
}): Promise<void>;
|
|
150
162
|
private readonly onMessagingChanged;
|
|
163
|
+
private readonly onMessagingParticipantChanged;
|
|
164
|
+
private readonly onMessagingParticipantRemoved;
|
|
165
|
+
private startRoomStatusListener;
|
|
166
|
+
private reopenOpenSessions;
|
|
151
167
|
private readonly onMessagingMessage;
|
|
152
168
|
}
|
|
153
169
|
export declare class ChatThreadSession extends ChangeEmitter {
|
|
@@ -186,6 +202,7 @@ export declare class ChatThreadSession extends ChangeEmitter {
|
|
|
186
202
|
load?: boolean;
|
|
187
203
|
sinceTurn?: string;
|
|
188
204
|
}): Promise<void>;
|
|
205
|
+
private resetReplayState;
|
|
189
206
|
close(): Promise<void>;
|
|
190
207
|
requestModels({ ignoreOffline }?: {
|
|
191
208
|
ignoreOffline?: boolean;
|
|
@@ -202,7 +219,7 @@ export declare class ChatThreadSession extends ChangeEmitter {
|
|
|
202
219
|
requestId: string;
|
|
203
220
|
response: Content;
|
|
204
221
|
}): Promise<void>;
|
|
205
|
-
sendText({ messageId, text, attachments, steer, turnId, backend, provider, model, voice, outputModalities, senderName, clientToolkits, }: {
|
|
222
|
+
sendText({ messageId, text, attachments, steer, turnId, backend, provider, model, voice, outputModalities, senderName, clientToolkits, toolChoice, }: {
|
|
206
223
|
messageId?: string;
|
|
207
224
|
text: string;
|
|
208
225
|
attachments: string[];
|
|
@@ -215,6 +232,7 @@ export declare class ChatThreadSession extends ChangeEmitter {
|
|
|
215
232
|
outputModalities?: string[];
|
|
216
233
|
senderName?: string;
|
|
217
234
|
clientToolkits?: ClientToolkitDescription[];
|
|
235
|
+
toolChoice?: ToolChoice;
|
|
218
236
|
}): Promise<string>;
|
|
219
237
|
sendRealtimeAudioChunk({ chunk, format }: {
|
|
220
238
|
chunk: Uint8Array;
|
package/dist/esm/chat-client.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { Completer, } from "@meshagent/meshagent";
|
|
2
2
|
import { v4 as uuidv4 } from "uuid";
|
|
3
|
-
import { AgentMessage as AgentMessageFactory, AgentAudioGenerationDelta, AgentAudioTranscriptionDelta, AgentRealtimeAudioChunk, AgentRealtimeAudioCommit, AgentFileContentDelta, AgentModelChanged, AgentReasoningContentDelta, AgentTextContentDelta, AgentThreadStatus, AgentToolCallArgumentsDelta, AgentToolCallLogDelta, ChangeModel, CloseThread, DeleteThread, ModelsResponse, ModelsRequest, OpenThread, RenameThread, StartThread, AgentConnectionStatus, TurnInterrupt, TurnStart, TurnSteer, AgentClientToolCallRequested, AgentClientToolCallResponse, agentInputContent, agentAudioGenerationDeltaType, agentAudioTranscriptionDeltaType, agentFileContentDeltaType, agentImageGenerationCompletedType, agentImageGenerationPartialType, agentModelChangedType, agentModelsResponseType, agentReasoningContentDeltaType, agentRoomMessageType, agentTextContentDeltaType, agentThreadClearedType, agentThreadLoadedType, agentConnectionStatusType, agentThreadStatusType, agentThreadStartType, agentThreadStartRejectedType, agentThreadStartedType, agentToolCallArgumentsDeltaType, agentToolCallLogDeltaType, agentTurnEndedType, agentTurnStartAcceptedType, agentTurnStartRejectedType, agentTurnStartType, agentTurnStartedType, agentTurnSteerAcceptedType, agentTurnSteerRejectedType, agentTurnSteerType, agentTurnSteeredType, } from "./agent-messages.js";
|
|
3
|
+
import { AgentMessage as AgentMessageFactory, AgentAudioGenerationDelta, AgentAudioTranscriptionDelta, AgentRealtimeAudioChunk, AgentRealtimeAudioCommit, AgentFileContentDelta, AgentModelChanged, AgentReasoningContentDelta, AgentTextContentDelta, AgentThreadStatus, AgentToolCallArgumentsDelta, AgentToolCallLogDelta, ChangeModel, CloseThread, DeleteThread, ModelsResponse, ModelsRequest, OpenThread, RenameThread, StartThread, AgentConnectionStatus, TurnInterrupt, TurnStart, TurnSteer, AgentClientToolCallRequested, AgentClientToolCallResponse, agentInputContent, agentAudioGenerationDeltaType, agentAudioTranscriptionDeltaType, agentFileContentDeltaType, agentImageGenerationCompletedType, agentImageGenerationPartialType, agentModelChangedType, agentModelsResponseType, agentReasoningContentDeltaType, agentRoomMessageType, agentTextContentDeltaType, agentThreadClearedType, agentThreadCreatedType, agentThreadDeletedType, agentThreadLoadedType, agentThreadListedType, agentThreadUpdatedType, agentConnectionStatusType, agentThreadStatusType, agentThreadStartType, agentThreadStartRejectedType, agentThreadStartedType, agentToolCallArgumentsDeltaType, agentToolCallLogDeltaType, agentToolCallEndedType, agentToolCallInProgressType, agentToolCallPendingType, agentToolCallStartedType, agentSecretRequestedType, agentTurnEndedType, agentTurnInterruptAcceptedType, agentTurnInterruptedType, agentTurnStartAcceptedType, agentTurnStartRejectedType, agentTurnStartType, agentTurnStartedType, agentTurnSteerAcceptedType, agentTurnSteerRejectedType, agentTurnSteerType, agentTurnSteeredType, agentUsageUpdatedType, } from "./agent-messages.js";
|
|
4
4
|
import { ChangeEmitter } from "./change-emitter.js";
|
|
5
5
|
export class AgentMessageEvent {
|
|
6
|
-
constructor({ message, attachment }) {
|
|
6
|
+
constructor({ message, createdAt, attachment }) {
|
|
7
7
|
this.message = message;
|
|
8
|
+
this.createdAt = createdAt ?? dateOrNull(message.createdAt) ?? new Date();
|
|
8
9
|
this.attachment = attachment;
|
|
9
10
|
}
|
|
10
11
|
get payload() {
|
|
11
|
-
return this.message.toJson();
|
|
12
|
+
return { ...this.message.toJson(), created_at: this.createdAt.toISOString() };
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
function agentRoomMessageFromEvent(event) {
|
|
@@ -196,7 +197,7 @@ export class BaseChatClient extends ChangeEmitter {
|
|
|
196
197
|
this.notifyListeners();
|
|
197
198
|
});
|
|
198
199
|
}
|
|
199
|
-
async startThread({ messageId, message, attachments, name, backend, provider, model, voice, outputModalities, realtimeProtocol, senderName, clientToolkits, omitContent = false, }) {
|
|
200
|
+
async startThread({ messageId, message, attachments, name, backend, provider, model, voice, outputModalities, realtimeProtocol, senderName, clientToolkits, toolChoice, omitContent = false, }) {
|
|
200
201
|
const resolvedMessageId = messageId == null || messageId.trim() === "" ? uuidv4() : messageId.trim();
|
|
201
202
|
const resolvedSenderName = this.cleanParticipantName(senderName) ?? this.localParticipantName();
|
|
202
203
|
const payload = new StartThread({
|
|
@@ -211,6 +212,7 @@ export class BaseChatClient extends ChangeEmitter {
|
|
|
211
212
|
realtimeProtocol: nonEmptyTrimmed(realtimeProtocol),
|
|
212
213
|
senderName: resolvedSenderName,
|
|
213
214
|
clientToolkits: clientToolkits != null && clientToolkits.length > 0 ? clientToolkits : undefined,
|
|
215
|
+
toolChoice: toolChoice?.toJson(),
|
|
214
216
|
});
|
|
215
217
|
const completer = new Completer();
|
|
216
218
|
this.pendingStartRequests.set(resolvedMessageId, completer);
|
|
@@ -225,7 +227,10 @@ export class BaseChatClient extends ChangeEmitter {
|
|
|
225
227
|
}
|
|
226
228
|
const session = this.openThread(threadPath, { load: false, drainPendingEvents: false });
|
|
227
229
|
if (!omitContent) {
|
|
228
|
-
session.addAgentMessage(new AgentMessageEvent({
|
|
230
|
+
session.addAgentMessage(new AgentMessageEvent({
|
|
231
|
+
message: this.resolvedStartThreadMessage({ payload, threadPath }),
|
|
232
|
+
createdAt: dateOrNull(payload.createdAt) ?? undefined,
|
|
233
|
+
}));
|
|
229
234
|
}
|
|
230
235
|
this.drainPendingSessionEvents(threadPath, session);
|
|
231
236
|
return new ChatThreadStartResult({
|
|
@@ -240,6 +245,23 @@ export class BaseChatClient extends ChangeEmitter {
|
|
|
240
245
|
this.pendingStartRequests.delete(resolvedMessageId);
|
|
241
246
|
}
|
|
242
247
|
}
|
|
248
|
+
resolvedStartThreadMessage({ payload, threadPath, }) {
|
|
249
|
+
return new TurnStart({
|
|
250
|
+
threadId: threadPath,
|
|
251
|
+
messageId: payload.messageId,
|
|
252
|
+
senderName: payload.senderName,
|
|
253
|
+
content: payload.content,
|
|
254
|
+
backend: payload.backend,
|
|
255
|
+
provider: payload.provider,
|
|
256
|
+
model: payload.model,
|
|
257
|
+
voice: payload.voice,
|
|
258
|
+
outputModalities: payload.outputModalities,
|
|
259
|
+
instructions: payload.instructions,
|
|
260
|
+
mcp: payload.mcp,
|
|
261
|
+
clientToolkits: payload.clientToolkits,
|
|
262
|
+
toolkits: payload.toolkits,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
243
265
|
drainPendingSessionEvents(threadPath, session) {
|
|
244
266
|
const pendingEvents = this.pendingSessionEventsByPath.get(threadPath);
|
|
245
267
|
if (pendingEvents == null) {
|
|
@@ -250,16 +272,11 @@ export class BaseChatClient extends ChangeEmitter {
|
|
|
250
272
|
session.addAgentMessage(event);
|
|
251
273
|
}
|
|
252
274
|
}
|
|
253
|
-
handleAgentMessage(message, { attachment } = {}) {
|
|
275
|
+
handleAgentMessage(message, { createdAt, attachment } = {}) {
|
|
254
276
|
if (message.type === agentConnectionStatusType) {
|
|
255
277
|
this._connectionStatus = message;
|
|
256
|
-
for (const session of this.sessionsByPath.values()) {
|
|
257
|
-
if (session.isOpen) {
|
|
258
|
-
session.addAgentMessage(new AgentMessageEvent({ message, attachment }));
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
278
|
}
|
|
262
|
-
this.eventQueue.add(new AgentMessageEvent({ message, attachment }));
|
|
279
|
+
this.eventQueue.add(new AgentMessageEvent({ message, createdAt, attachment }));
|
|
263
280
|
const sourceMessageId = sourceMessageIdFor(message);
|
|
264
281
|
if ((message.type === agentThreadStartedType || message.type === agentThreadStartRejectedType) &&
|
|
265
282
|
sourceMessageId != null &&
|
|
@@ -274,7 +291,7 @@ export class BaseChatClient extends ChangeEmitter {
|
|
|
274
291
|
this.notifyListeners();
|
|
275
292
|
return;
|
|
276
293
|
}
|
|
277
|
-
const event = new AgentMessageEvent({ message, attachment });
|
|
294
|
+
const event = new AgentMessageEvent({ message, createdAt, attachment });
|
|
278
295
|
const session = this.sessionsByPath.get(threadPath);
|
|
279
296
|
if (session != null) {
|
|
280
297
|
session.addAgentMessage(event);
|
|
@@ -295,11 +312,6 @@ export class BaseChatClient extends ChangeEmitter {
|
|
|
295
312
|
});
|
|
296
313
|
this._connectionStatus = event;
|
|
297
314
|
this.eventQueue.add(new AgentMessageEvent({ message: event }));
|
|
298
|
-
for (const session of this.sessionsByPath.values()) {
|
|
299
|
-
if (session.isOpen) {
|
|
300
|
-
session.addAgentMessage(new AgentMessageEvent({ message: event }));
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
315
|
this.notifyListeners();
|
|
304
316
|
}
|
|
305
317
|
removeThreadSession(threadPath) {
|
|
@@ -317,10 +329,50 @@ export class MessagingChatClient extends BaseChatClient {
|
|
|
317
329
|
super();
|
|
318
330
|
this.pendingParticipantWaits = new Map();
|
|
319
331
|
this.started = false;
|
|
332
|
+
this.hasConnected = false;
|
|
333
|
+
this.waitingForParticipant = false;
|
|
334
|
+
this.messagingListenersAttached = false;
|
|
320
335
|
this.onMessagingChanged = () => {
|
|
321
336
|
const participant = this.agentParticipant();
|
|
337
|
+
const participantId = participant?.id;
|
|
338
|
+
if (participantId !== this.agentParticipantId) {
|
|
339
|
+
if (participantId == null) {
|
|
340
|
+
if (this.agentParticipantId != null) {
|
|
341
|
+
this.waitingForParticipant = true;
|
|
342
|
+
this.emitConnectionStatus({
|
|
343
|
+
status: "disconnected",
|
|
344
|
+
message: "Agent messaging disconnected",
|
|
345
|
+
reason: "participant_disconnected",
|
|
346
|
+
});
|
|
347
|
+
this.emitConnectionStatus({
|
|
348
|
+
status: "reconnecting",
|
|
349
|
+
message: "Waiting for agent messaging",
|
|
350
|
+
reason: "participant_disconnected",
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
else if (this.started && this.waitingForParticipant) {
|
|
354
|
+
this.emitConnectionStatus({
|
|
355
|
+
status: "reconnecting",
|
|
356
|
+
message: "Waiting for agent messaging",
|
|
357
|
+
reason: "participant_offline",
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
const status = this.hasConnected ? "reconnected" : "connected";
|
|
363
|
+
this.hasConnected = true;
|
|
364
|
+
this.waitingForParticipant = false;
|
|
365
|
+
this.emitConnectionStatus({
|
|
366
|
+
status,
|
|
367
|
+
message: status === "reconnected" ? "Agent messaging reconnected" : "Agent messaging connected",
|
|
368
|
+
});
|
|
369
|
+
if (status === "reconnected") {
|
|
370
|
+
void this.reopenOpenSessions();
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
this.agentParticipantId = participantId;
|
|
374
|
+
}
|
|
322
375
|
if (participant != null) {
|
|
323
|
-
this.emitConnectionStatus({ status: "connected", message: "Agent messaging connected" });
|
|
324
376
|
for (const wait of this.pendingParticipantWaits.values()) {
|
|
325
377
|
if (!wait.completed) {
|
|
326
378
|
wait.complete(participant);
|
|
@@ -329,6 +381,12 @@ export class MessagingChatClient extends BaseChatClient {
|
|
|
329
381
|
}
|
|
330
382
|
this.notifyListeners();
|
|
331
383
|
};
|
|
384
|
+
this.onMessagingParticipantChanged = () => {
|
|
385
|
+
this.onMessagingChanged();
|
|
386
|
+
};
|
|
387
|
+
this.onMessagingParticipantRemoved = () => {
|
|
388
|
+
this.onMessagingChanged();
|
|
389
|
+
};
|
|
332
390
|
this.onMessagingMessage = (event) => {
|
|
333
391
|
this.onMessagingChanged();
|
|
334
392
|
const roomMessage = agentRoomMessageFromEvent(event);
|
|
@@ -336,14 +394,19 @@ export class MessagingChatClient extends BaseChatClient {
|
|
|
336
394
|
return;
|
|
337
395
|
}
|
|
338
396
|
const participant = this.agentParticipant();
|
|
339
|
-
|
|
397
|
+
const fromSelectedAgent = participant != null && roomMessage.fromParticipantId === participant.id;
|
|
398
|
+
const fromParticipantlessThreadList = roomMessage.fromParticipantId == null && isThreadListMessage(roomMessage.message);
|
|
399
|
+
if (!fromSelectedAgent && !fromParticipantlessThreadList) {
|
|
340
400
|
return;
|
|
341
401
|
}
|
|
342
|
-
this.handleAgentMessage(AgentMessageFactory.fromJson(roomMessage.message), {
|
|
402
|
+
this.handleAgentMessage(AgentMessageFactory.fromJson(roomMessage.message), {
|
|
403
|
+
createdAt: dateOrNull(roomMessage.message.created_at),
|
|
404
|
+
attachment: roomMessage.attachment,
|
|
405
|
+
});
|
|
343
406
|
};
|
|
344
407
|
this.room = room;
|
|
345
408
|
this.agentName = agentName;
|
|
346
|
-
this.
|
|
409
|
+
this.attachMessagingListeners();
|
|
347
410
|
}
|
|
348
411
|
localParticipantName() {
|
|
349
412
|
const name = this.room.localParticipant?.getAttribute("name");
|
|
@@ -354,12 +417,18 @@ export class MessagingChatClient extends BaseChatClient {
|
|
|
354
417
|
return;
|
|
355
418
|
}
|
|
356
419
|
this.started = true;
|
|
420
|
+
this.attachMessagingListeners();
|
|
357
421
|
this.room.messaging.start();
|
|
358
|
-
this.
|
|
422
|
+
this.startRoomStatusListener();
|
|
423
|
+
await this.room.messaging.enable();
|
|
424
|
+
this.waitingForParticipant = true;
|
|
425
|
+
this.onMessagingChanged();
|
|
359
426
|
}
|
|
360
427
|
async stop() {
|
|
361
428
|
this.started = false;
|
|
362
|
-
this.
|
|
429
|
+
this.roomAbortController?.abort();
|
|
430
|
+
this.roomAbortController = undefined;
|
|
431
|
+
this.detachMessagingListeners();
|
|
363
432
|
for (const wait of this.pendingParticipantWaits.values()) {
|
|
364
433
|
if (!wait.completed) {
|
|
365
434
|
wait.completeError(new Error("Chat client stopped while waiting for an agent participant."));
|
|
@@ -367,12 +436,35 @@ export class MessagingChatClient extends BaseChatClient {
|
|
|
367
436
|
}
|
|
368
437
|
this.pendingParticipantWaits.clear();
|
|
369
438
|
}
|
|
439
|
+
attachMessagingListeners() {
|
|
440
|
+
if (this.messagingListenersAttached) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
this.room.messaging.addListener("message", this.onMessagingMessage);
|
|
444
|
+
this.room.messaging.addListener("participant_added", this.onMessagingParticipantChanged);
|
|
445
|
+
this.room.messaging.addListener("participant_removed", this.onMessagingParticipantRemoved);
|
|
446
|
+
this.room.messaging.addListener("messaging_enabled", this.onMessagingParticipantChanged);
|
|
447
|
+
this.messagingListenersAttached = true;
|
|
448
|
+
}
|
|
449
|
+
detachMessagingListeners() {
|
|
450
|
+
if (!this.messagingListenersAttached) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
this.room.messaging.removeListener("messaging_enabled", this.onMessagingParticipantChanged);
|
|
454
|
+
this.room.messaging.removeListener("participant_removed", this.onMessagingParticipantRemoved);
|
|
455
|
+
this.room.messaging.removeListener("participant_added", this.onMessagingParticipantChanged);
|
|
456
|
+
this.room.messaging.removeListener("message", this.onMessagingMessage);
|
|
457
|
+
this.messagingListenersAttached = false;
|
|
458
|
+
}
|
|
370
459
|
agentParticipant() {
|
|
371
460
|
const normalizedAgentName = this.agentName?.trim();
|
|
372
461
|
for (const participant of this.room.messaging.remoteParticipants) {
|
|
373
462
|
if (normalizedAgentName != null && normalizedAgentName !== "" && participant.getAttribute("name") !== normalizedAgentName) {
|
|
374
463
|
continue;
|
|
375
464
|
}
|
|
465
|
+
if (normalizedAgentName != null && normalizedAgentName !== "") {
|
|
466
|
+
return participant;
|
|
467
|
+
}
|
|
376
468
|
if (participant.getAttribute("supports_agent_messages") === true) {
|
|
377
469
|
return participant;
|
|
378
470
|
}
|
|
@@ -397,7 +489,13 @@ export class MessagingChatClient extends BaseChatClient {
|
|
|
397
489
|
}
|
|
398
490
|
async sendAgentMessage(message, { attachment, ignoreOffline = false } = {}) {
|
|
399
491
|
await this.start();
|
|
400
|
-
|
|
492
|
+
let participant = this.agentParticipant();
|
|
493
|
+
if (participant == null) {
|
|
494
|
+
if (ignoreOffline) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
participant = await this.waitForAgentParticipant({ waitKey: message.messageId });
|
|
498
|
+
}
|
|
401
499
|
await this.room.messaging.sendMessage({
|
|
402
500
|
to: participant,
|
|
403
501
|
type: agentRoomMessageType,
|
|
@@ -406,6 +504,49 @@ export class MessagingChatClient extends BaseChatClient {
|
|
|
406
504
|
ignoreOffline,
|
|
407
505
|
});
|
|
408
506
|
}
|
|
507
|
+
startRoomStatusListener() {
|
|
508
|
+
if (this.roomAbortController != null) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
const controller = new AbortController();
|
|
512
|
+
this.roomAbortController = controller;
|
|
513
|
+
void (async () => {
|
|
514
|
+
for await (const event of this.room.listen({ abortSignal: controller.signal })) {
|
|
515
|
+
if (controller.signal.aborted || event.name !== "room.status") {
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
const status = String(event.status ?? "").trim().toLowerCase();
|
|
519
|
+
const message = String(event.message ?? "").trim();
|
|
520
|
+
if (status === "connected" || status === "ready" || status === "reconnected") {
|
|
521
|
+
this.waitingForParticipant = true;
|
|
522
|
+
this.onMessagingChanged();
|
|
523
|
+
}
|
|
524
|
+
else if (status === "disconnected" || status === "reconnecting" || status === "connecting") {
|
|
525
|
+
this.agentParticipantId = undefined;
|
|
526
|
+
this.waitingForParticipant = true;
|
|
527
|
+
this.emitConnectionStatus({
|
|
528
|
+
status: status === "disconnected" ? "disconnected" : "reconnecting",
|
|
529
|
+
message: message !== "" ? message : status === "disconnected" ? "Agent messaging disconnected" : "Waiting for agent messaging",
|
|
530
|
+
reason: message !== "" ? message : undefined,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
})().catch(() => undefined);
|
|
535
|
+
}
|
|
536
|
+
async reopenOpenSessions() {
|
|
537
|
+
for (const session of this.sessions) {
|
|
538
|
+
if (!session.isOpen) {
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
await session.open({ load: true, sinceTurn: session.lastCompletedTurnId });
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function isThreadListMessage(message) {
|
|
546
|
+
return message.type === agentThreadListedType
|
|
547
|
+
|| message.type === agentThreadCreatedType
|
|
548
|
+
|| message.type === agentThreadUpdatedType
|
|
549
|
+
|| message.type === agentThreadDeletedType;
|
|
409
550
|
}
|
|
410
551
|
export class ChatThreadSession extends ChangeEmitter {
|
|
411
552
|
constructor({ client, threadPath }) {
|
|
@@ -454,6 +595,9 @@ export class ChatThreadSession extends ChangeEmitter {
|
|
|
454
595
|
async open({ load = true, sinceTurn } = {}) {
|
|
455
596
|
if (this.openState) {
|
|
456
597
|
if (load) {
|
|
598
|
+
if (sinceTurn == null) {
|
|
599
|
+
this.resetReplayState();
|
|
600
|
+
}
|
|
457
601
|
const openThread = new OpenThread({ threadId: this.threadPath, load: true, sinceTurn });
|
|
458
602
|
this.setLoadState(this.loadingStateForOpen(openThread, sinceTurn));
|
|
459
603
|
try {
|
|
@@ -484,6 +628,17 @@ export class ChatThreadSession extends ChangeEmitter {
|
|
|
484
628
|
throw error;
|
|
485
629
|
}
|
|
486
630
|
}
|
|
631
|
+
resetReplayState() {
|
|
632
|
+
this._messages.length = 0;
|
|
633
|
+
this.messageIndexes.clear();
|
|
634
|
+
this.localAgentMessageIds.clear();
|
|
635
|
+
this.pendingLocalInputMessageIds.clear();
|
|
636
|
+
this.mergedDeltaMessageIds.clear();
|
|
637
|
+
this.pendingInputsById.clear();
|
|
638
|
+
this.lastCompletedTurnIdValue = undefined;
|
|
639
|
+
this.lastAgentMessageType = undefined;
|
|
640
|
+
this.lastAgentMessageAt = undefined;
|
|
641
|
+
}
|
|
487
642
|
async close() {
|
|
488
643
|
if (!this.openState) {
|
|
489
644
|
return;
|
|
@@ -515,7 +670,7 @@ export class ChatThreadSession extends ChangeEmitter {
|
|
|
515
670
|
response,
|
|
516
671
|
}));
|
|
517
672
|
}
|
|
518
|
-
async sendText({ messageId, text, attachments, steer = false, turnId, backend, provider, model, voice, outputModalities, senderName, clientToolkits, }) {
|
|
673
|
+
async sendText({ messageId, text, attachments, steer = false, turnId, backend, provider, model, voice, outputModalities, senderName, clientToolkits, toolChoice, }) {
|
|
519
674
|
const resolvedMessageId = messageId == null || messageId.trim() === "" ? uuidv4() : messageId.trim();
|
|
520
675
|
const currentModel = this.currentModelValue;
|
|
521
676
|
const resolvedBackend = nonEmptyTrimmed(backend) ?? currentModel?.backend;
|
|
@@ -544,6 +699,7 @@ export class ChatThreadSession extends ChangeEmitter {
|
|
|
544
699
|
voice: resolvedVoice,
|
|
545
700
|
outputModalities: resolvedOutputModalities,
|
|
546
701
|
clientToolkits: clientToolkits != null && clientToolkits.length > 0 ? clientToolkits : undefined,
|
|
702
|
+
toolChoice: toolChoice?.toJson(),
|
|
547
703
|
});
|
|
548
704
|
this.localAgentMessageIds.add(resolvedMessageId);
|
|
549
705
|
this.pendingLocalInputMessageIds.add(resolvedMessageId);
|
|
@@ -593,9 +749,9 @@ export class ChatThreadSession extends ChangeEmitter {
|
|
|
593
749
|
const message = event.message;
|
|
594
750
|
const type = message.type;
|
|
595
751
|
this.lastAgentMessageType = type;
|
|
596
|
-
this.lastAgentMessageAt =
|
|
752
|
+
this.lastAgentMessageAt = event.createdAt;
|
|
597
753
|
this.updateSessionState(message);
|
|
598
|
-
this.updatePendingInputsForMessage(message);
|
|
754
|
+
this.updatePendingInputsForMessage(message, event.createdAt);
|
|
599
755
|
this.appendRenderableMessage(event);
|
|
600
756
|
this.notifyListeners();
|
|
601
757
|
}
|
|
@@ -661,7 +817,7 @@ export class ChatThreadSession extends ChangeEmitter {
|
|
|
661
817
|
}), { notify: false });
|
|
662
818
|
}
|
|
663
819
|
}
|
|
664
|
-
updatePendingInputsForMessage(message) {
|
|
820
|
+
updatePendingInputsForMessage(message, createdAt) {
|
|
665
821
|
const type = message.type;
|
|
666
822
|
const messageId = message.messageId;
|
|
667
823
|
const sourceMessageId = sourceMessageIdFor(message);
|
|
@@ -675,7 +831,7 @@ export class ChatThreadSession extends ChangeEmitter {
|
|
|
675
831
|
messageType: type,
|
|
676
832
|
threadPath: this.threadPath,
|
|
677
833
|
payload: message,
|
|
678
|
-
createdAt
|
|
834
|
+
createdAt,
|
|
679
835
|
awaitingAcceptance: true,
|
|
680
836
|
awaitingApplication: true,
|
|
681
837
|
}));
|
|
@@ -714,6 +870,31 @@ export class ChatThreadSession extends ChangeEmitter {
|
|
|
714
870
|
}
|
|
715
871
|
return;
|
|
716
872
|
}
|
|
873
|
+
if (message.type === agentToolCallEndedType) {
|
|
874
|
+
const itemId = message.error == null ? undefined : normalizedString(message.itemId);
|
|
875
|
+
if (itemId != null) {
|
|
876
|
+
this.upsertItemMessage(`tool_call:${itemId}`, event);
|
|
877
|
+
}
|
|
878
|
+
else if (message.error != null) {
|
|
879
|
+
this.appendMessage(event);
|
|
880
|
+
}
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
if (message.type === agentToolCallPendingType || message.type === agentToolCallInProgressType || message.type === agentToolCallStartedType) {
|
|
884
|
+
const itemId = normalizedString(message.itemId);
|
|
885
|
+
if (itemId != null) {
|
|
886
|
+
this.upsertItemMessage(`tool_call:${itemId}`, event);
|
|
887
|
+
}
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
if (message.type === agentSecretRequestedType) {
|
|
891
|
+
this.appendMessage(event);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
if (message.type === agentModelChangedType) {
|
|
895
|
+
this.appendMessage(event);
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
717
898
|
if (message.type === agentThreadStartType || message.type === agentTurnStartType || message.type === agentTurnSteerType) {
|
|
718
899
|
if (agentInputText(message.content).trim() !== "") {
|
|
719
900
|
this.appendMessage(event);
|
|
@@ -729,6 +910,18 @@ export class ChatThreadSession extends ChangeEmitter {
|
|
|
729
910
|
if (agentInputText(message.content).trim() !== "") {
|
|
730
911
|
this.appendMessage(event, { beforePendingLocalInputs: true });
|
|
731
912
|
}
|
|
913
|
+
else {
|
|
914
|
+
this.appendMessage(event);
|
|
915
|
+
}
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
if (message.type === agentTurnStartedType ||
|
|
919
|
+
message.type === agentTurnSteeredType ||
|
|
920
|
+
message.type === agentTurnStartRejectedType ||
|
|
921
|
+
message.type === agentTurnSteerRejectedType ||
|
|
922
|
+
message.type === agentTurnInterruptAcceptedType ||
|
|
923
|
+
message.type === agentTurnInterruptedType) {
|
|
924
|
+
this.appendMessage(event);
|
|
732
925
|
return;
|
|
733
926
|
}
|
|
734
927
|
if (isMergeableDelta(message)) {
|
|
@@ -742,6 +935,10 @@ export class ChatThreadSession extends ChangeEmitter {
|
|
|
742
935
|
}
|
|
743
936
|
return;
|
|
744
937
|
}
|
|
938
|
+
if (message.type === agentUsageUpdatedType) {
|
|
939
|
+
this.appendMessage(event);
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
745
942
|
if (message instanceof AgentClientToolCallRequested) {
|
|
746
943
|
this.appendMessage(event);
|
|
747
944
|
}
|
|
@@ -794,7 +991,7 @@ export class ChatThreadSession extends ChangeEmitter {
|
|
|
794
991
|
const existing = this._messages[existingIndex].message;
|
|
795
992
|
const merged = mergeDeltaMessage(existing, message);
|
|
796
993
|
if (merged != null) {
|
|
797
|
-
this._messages[existingIndex] = new AgentMessageEvent({ message: merged, attachment: event.attachment });
|
|
994
|
+
this._messages[existingIndex] = new AgentMessageEvent({ message: merged, createdAt: event.createdAt, attachment: event.attachment });
|
|
798
995
|
}
|
|
799
996
|
}
|
|
800
997
|
indexMessagesFrom(start) {
|
|
@@ -958,6 +1155,16 @@ function normalizedString(value) {
|
|
|
958
1155
|
const normalized = value?.trim();
|
|
959
1156
|
return normalized == null || normalized === "" ? undefined : normalized;
|
|
960
1157
|
}
|
|
1158
|
+
function dateOrNull(value) {
|
|
1159
|
+
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
|
1160
|
+
return value;
|
|
1161
|
+
}
|
|
1162
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
1163
|
+
return undefined;
|
|
1164
|
+
}
|
|
1165
|
+
const parsed = new Date(value);
|
|
1166
|
+
return Number.isNaN(parsed.getTime()) ? undefined : parsed;
|
|
1167
|
+
}
|
|
961
1168
|
function nonEmptyTrimmed(value) {
|
|
962
1169
|
const normalized = value?.trim();
|
|
963
1170
|
return normalized == null || normalized === "" ? undefined : normalized;
|
|
@@ -93,6 +93,7 @@ export declare class AgentThreadStorageRepository extends ThreadStorageRepositor
|
|
|
93
93
|
renameThread(threadPath: string, name: string): Promise<void>;
|
|
94
94
|
private requestList;
|
|
95
95
|
private handleEvent;
|
|
96
|
+
private reconnectWatch;
|
|
96
97
|
private handleThreadsListed;
|
|
97
98
|
private upsertThread;
|
|
98
99
|
private entryFromAgentThread;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Field, Schema, Utf8, tableFromArrays, } from "apache-arrow";
|
|
2
2
|
import { ChangeEmitter } from "./change-emitter.js";
|
|
3
|
-
import { DeleteThread, ListThreads, RenameThread, } from "./agent-messages.js";
|
|
3
|
+
import { DeleteThread, ListThreads, RenameThread, UnwatchThreads, WatchThreads, } from "./agent-messages.js";
|
|
4
4
|
export const defaultUntitledThreadName = "New Chat";
|
|
5
5
|
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
6
6
|
export class ThreadListEntry {
|
|
@@ -304,10 +304,12 @@ export class AgentThreadStorageRepository extends ThreadStorageRepository {
|
|
|
304
304
|
this.handleEvent(next.value);
|
|
305
305
|
}
|
|
306
306
|
})();
|
|
307
|
-
|
|
307
|
+
const ready = new Promise((resolve) => {
|
|
308
308
|
this.pendingOpen = { resolve };
|
|
309
|
-
void this.requestList();
|
|
310
309
|
});
|
|
310
|
+
await this.chatClient.sendAgentMessage(new WatchThreads(), { ignoreOffline: true });
|
|
311
|
+
await this.requestList();
|
|
312
|
+
await ready;
|
|
311
313
|
}
|
|
312
314
|
async close() {
|
|
313
315
|
this.closed = true;
|
|
@@ -316,6 +318,7 @@ export class AgentThreadStorageRepository extends ThreadStorageRepository {
|
|
|
316
318
|
this.pendingListMessageId = undefined;
|
|
317
319
|
this.pendingOpen?.resolve();
|
|
318
320
|
this.pendingOpen = undefined;
|
|
321
|
+
await this.chatClient.sendAgentMessage(new UnwatchThreads(), { ignoreOffline: true }).catch(() => undefined);
|
|
319
322
|
}
|
|
320
323
|
entries() {
|
|
321
324
|
return [...this.entriesByPath.values()];
|
|
@@ -355,6 +358,12 @@ export class AgentThreadStorageRepository extends ThreadStorageRepository {
|
|
|
355
358
|
}
|
|
356
359
|
handleEvent(event) {
|
|
357
360
|
const message = event.message;
|
|
361
|
+
if (message.type === "meshagent.agent.connection.status") {
|
|
362
|
+
if (String(message.status ?? "").trim().toLowerCase() === "reconnected") {
|
|
363
|
+
void this.reconnectWatch();
|
|
364
|
+
}
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
358
367
|
if (message.type === "meshagent.agent.thread.listed") {
|
|
359
368
|
this.handleThreadsListed(message);
|
|
360
369
|
return;
|
|
@@ -374,6 +383,13 @@ export class AgentThreadStorageRepository extends ThreadStorageRepository {
|
|
|
374
383
|
}
|
|
375
384
|
}
|
|
376
385
|
}
|
|
386
|
+
async reconnectWatch() {
|
|
387
|
+
if (this.closed) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
await this.chatClient.sendAgentMessage(new WatchThreads(), { ignoreOffline: true });
|
|
391
|
+
await this.requestList();
|
|
392
|
+
}
|
|
377
393
|
handleThreadsListed(message) {
|
|
378
394
|
const nextEntries = new Map();
|
|
379
395
|
for (const thread of message.threads) {
|
|
@@ -19,6 +19,8 @@ export declare const agentParticipantDisconnectType = "meshagent.agent.participa
|
|
|
19
19
|
export declare const agentThreadDeleteType = "meshagent.agent.thread.delete";
|
|
20
20
|
export declare const agentThreadRenameType = "meshagent.agent.thread.rename";
|
|
21
21
|
export declare const agentThreadListType = "meshagent.agent.thread.list";
|
|
22
|
+
export declare const agentThreadWatchType = "meshagent.agent.thread.watch";
|
|
23
|
+
export declare const agentThreadUnwatchType = "meshagent.agent.thread.unwatch";
|
|
22
24
|
export declare const agentThreadListedType = "meshagent.agent.thread.listed";
|
|
23
25
|
export declare const agentThreadCreatedType = "meshagent.agent.thread.created";
|
|
24
26
|
export declare const agentThreadUpdatedType = "meshagent.agent.thread.updated";
|
|
@@ -239,7 +241,8 @@ export declare abstract class AgentMessage {
|
|
|
239
241
|
readonly type: string;
|
|
240
242
|
readonly messageId: string;
|
|
241
243
|
readonly senderName?: string;
|
|
242
|
-
|
|
244
|
+
readonly createdAt: string;
|
|
245
|
+
protected constructor({ type, messageId, message_id, senderName, sender_name, createdAt, created_at }: JsonMap);
|
|
243
246
|
toJson(): JsonMap;
|
|
244
247
|
static fromJson(json: JsonMap): AgentMessage;
|
|
245
248
|
}
|
|
@@ -272,6 +275,8 @@ export declare const ParticipantDisconnect: MessageCtor;
|
|
|
272
275
|
export declare const DeleteThread: MessageCtor;
|
|
273
276
|
export declare const RenameThread: MessageCtor;
|
|
274
277
|
export declare const ListThreads: MessageCtor;
|
|
278
|
+
export declare const WatchThreads: MessageCtor;
|
|
279
|
+
export declare const UnwatchThreads: MessageCtor;
|
|
275
280
|
export declare const ThreadsListed: MessageCtor;
|
|
276
281
|
export declare const ThreadCreated: MessageCtor;
|
|
277
282
|
export declare const ThreadUpdated: MessageCtor;
|