@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.
@@ -6,12 +6,13 @@ const uuid_1 = require("uuid");
6
6
  const agent_messages_js_1 = require("./agent-messages.js");
7
7
  const change_emitter_js_1 = require("./change-emitter.js");
8
8
  class AgentMessageEvent {
9
- constructor({ message, attachment }) {
9
+ constructor({ message, createdAt, attachment }) {
10
10
  this.message = message;
11
+ this.createdAt = createdAt ?? dateOrNull(message.createdAt) ?? new Date();
11
12
  this.attachment = attachment;
12
13
  }
13
14
  get payload() {
14
- return this.message.toJson();
15
+ return { ...this.message.toJson(), created_at: this.createdAt.toISOString() };
15
16
  }
16
17
  }
17
18
  exports.AgentMessageEvent = AgentMessageEvent;
@@ -203,7 +204,7 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
203
204
  this.notifyListeners();
204
205
  });
205
206
  }
206
- async startThread({ messageId, message, attachments, name, backend, provider, model, voice, outputModalities, realtimeProtocol, senderName, clientToolkits, omitContent = false, }) {
207
+ async startThread({ messageId, message, attachments, name, backend, provider, model, voice, outputModalities, realtimeProtocol, senderName, clientToolkits, toolChoice, omitContent = false, }) {
207
208
  const resolvedMessageId = messageId == null || messageId.trim() === "" ? (0, uuid_1.v4)() : messageId.trim();
208
209
  const resolvedSenderName = this.cleanParticipantName(senderName) ?? this.localParticipantName();
209
210
  const payload = new agent_messages_js_1.StartThread({
@@ -218,6 +219,7 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
218
219
  realtimeProtocol: nonEmptyTrimmed(realtimeProtocol),
219
220
  senderName: resolvedSenderName,
220
221
  clientToolkits: clientToolkits != null && clientToolkits.length > 0 ? clientToolkits : undefined,
222
+ toolChoice: toolChoice?.toJson(),
221
223
  });
222
224
  const completer = new meshagent_1.Completer();
223
225
  this.pendingStartRequests.set(resolvedMessageId, completer);
@@ -232,7 +234,10 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
232
234
  }
233
235
  const session = this.openThread(threadPath, { load: false, drainPendingEvents: false });
234
236
  if (!omitContent) {
235
- session.addAgentMessage(new AgentMessageEvent({ message: payload }));
237
+ session.addAgentMessage(new AgentMessageEvent({
238
+ message: this.resolvedStartThreadMessage({ payload, threadPath }),
239
+ createdAt: dateOrNull(payload.createdAt) ?? undefined,
240
+ }));
236
241
  }
237
242
  this.drainPendingSessionEvents(threadPath, session);
238
243
  return new ChatThreadStartResult({
@@ -247,6 +252,23 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
247
252
  this.pendingStartRequests.delete(resolvedMessageId);
248
253
  }
249
254
  }
255
+ resolvedStartThreadMessage({ payload, threadPath, }) {
256
+ return new agent_messages_js_1.TurnStart({
257
+ threadId: threadPath,
258
+ messageId: payload.messageId,
259
+ senderName: payload.senderName,
260
+ content: payload.content,
261
+ backend: payload.backend,
262
+ provider: payload.provider,
263
+ model: payload.model,
264
+ voice: payload.voice,
265
+ outputModalities: payload.outputModalities,
266
+ instructions: payload.instructions,
267
+ mcp: payload.mcp,
268
+ clientToolkits: payload.clientToolkits,
269
+ toolkits: payload.toolkits,
270
+ });
271
+ }
250
272
  drainPendingSessionEvents(threadPath, session) {
251
273
  const pendingEvents = this.pendingSessionEventsByPath.get(threadPath);
252
274
  if (pendingEvents == null) {
@@ -257,16 +279,11 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
257
279
  session.addAgentMessage(event);
258
280
  }
259
281
  }
260
- handleAgentMessage(message, { attachment } = {}) {
282
+ handleAgentMessage(message, { createdAt, attachment } = {}) {
261
283
  if (message.type === agent_messages_js_1.agentConnectionStatusType) {
262
284
  this._connectionStatus = message;
263
- for (const session of this.sessionsByPath.values()) {
264
- if (session.isOpen) {
265
- session.addAgentMessage(new AgentMessageEvent({ message, attachment }));
266
- }
267
- }
268
285
  }
269
- this.eventQueue.add(new AgentMessageEvent({ message, attachment }));
286
+ this.eventQueue.add(new AgentMessageEvent({ message, createdAt, attachment }));
270
287
  const sourceMessageId = sourceMessageIdFor(message);
271
288
  if ((message.type === agent_messages_js_1.agentThreadStartedType || message.type === agent_messages_js_1.agentThreadStartRejectedType) &&
272
289
  sourceMessageId != null &&
@@ -281,7 +298,7 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
281
298
  this.notifyListeners();
282
299
  return;
283
300
  }
284
- const event = new AgentMessageEvent({ message, attachment });
301
+ const event = new AgentMessageEvent({ message, createdAt, attachment });
285
302
  const session = this.sessionsByPath.get(threadPath);
286
303
  if (session != null) {
287
304
  session.addAgentMessage(event);
@@ -302,11 +319,6 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
302
319
  });
303
320
  this._connectionStatus = event;
304
321
  this.eventQueue.add(new AgentMessageEvent({ message: event }));
305
- for (const session of this.sessionsByPath.values()) {
306
- if (session.isOpen) {
307
- session.addAgentMessage(new AgentMessageEvent({ message: event }));
308
- }
309
- }
310
322
  this.notifyListeners();
311
323
  }
312
324
  removeThreadSession(threadPath) {
@@ -325,10 +337,50 @@ class MessagingChatClient extends BaseChatClient {
325
337
  super();
326
338
  this.pendingParticipantWaits = new Map();
327
339
  this.started = false;
340
+ this.hasConnected = false;
341
+ this.waitingForParticipant = false;
342
+ this.messagingListenersAttached = false;
328
343
  this.onMessagingChanged = () => {
329
344
  const participant = this.agentParticipant();
345
+ const participantId = participant?.id;
346
+ if (participantId !== this.agentParticipantId) {
347
+ if (participantId == null) {
348
+ if (this.agentParticipantId != null) {
349
+ this.waitingForParticipant = true;
350
+ this.emitConnectionStatus({
351
+ status: "disconnected",
352
+ message: "Agent messaging disconnected",
353
+ reason: "participant_disconnected",
354
+ });
355
+ this.emitConnectionStatus({
356
+ status: "reconnecting",
357
+ message: "Waiting for agent messaging",
358
+ reason: "participant_disconnected",
359
+ });
360
+ }
361
+ else if (this.started && this.waitingForParticipant) {
362
+ this.emitConnectionStatus({
363
+ status: "reconnecting",
364
+ message: "Waiting for agent messaging",
365
+ reason: "participant_offline",
366
+ });
367
+ }
368
+ }
369
+ else {
370
+ const status = this.hasConnected ? "reconnected" : "connected";
371
+ this.hasConnected = true;
372
+ this.waitingForParticipant = false;
373
+ this.emitConnectionStatus({
374
+ status,
375
+ message: status === "reconnected" ? "Agent messaging reconnected" : "Agent messaging connected",
376
+ });
377
+ if (status === "reconnected") {
378
+ void this.reopenOpenSessions();
379
+ }
380
+ }
381
+ this.agentParticipantId = participantId;
382
+ }
330
383
  if (participant != null) {
331
- this.emitConnectionStatus({ status: "connected", message: "Agent messaging connected" });
332
384
  for (const wait of this.pendingParticipantWaits.values()) {
333
385
  if (!wait.completed) {
334
386
  wait.complete(participant);
@@ -337,6 +389,12 @@ class MessagingChatClient extends BaseChatClient {
337
389
  }
338
390
  this.notifyListeners();
339
391
  };
392
+ this.onMessagingParticipantChanged = () => {
393
+ this.onMessagingChanged();
394
+ };
395
+ this.onMessagingParticipantRemoved = () => {
396
+ this.onMessagingChanged();
397
+ };
340
398
  this.onMessagingMessage = (event) => {
341
399
  this.onMessagingChanged();
342
400
  const roomMessage = agentRoomMessageFromEvent(event);
@@ -344,14 +402,19 @@ class MessagingChatClient extends BaseChatClient {
344
402
  return;
345
403
  }
346
404
  const participant = this.agentParticipant();
347
- if (participant == null || roomMessage.fromParticipantId !== participant.id) {
405
+ const fromSelectedAgent = participant != null && roomMessage.fromParticipantId === participant.id;
406
+ const fromParticipantlessThreadList = roomMessage.fromParticipantId == null && isThreadListMessage(roomMessage.message);
407
+ if (!fromSelectedAgent && !fromParticipantlessThreadList) {
348
408
  return;
349
409
  }
350
- this.handleAgentMessage(agent_messages_js_1.AgentMessage.fromJson(roomMessage.message), { attachment: roomMessage.attachment });
410
+ this.handleAgentMessage(agent_messages_js_1.AgentMessage.fromJson(roomMessage.message), {
411
+ createdAt: dateOrNull(roomMessage.message.created_at),
412
+ attachment: roomMessage.attachment,
413
+ });
351
414
  };
352
415
  this.room = room;
353
416
  this.agentName = agentName;
354
- this.room.messaging.addListener("message", this.onMessagingMessage);
417
+ this.attachMessagingListeners();
355
418
  }
356
419
  localParticipantName() {
357
420
  const name = this.room.localParticipant?.getAttribute("name");
@@ -362,12 +425,18 @@ class MessagingChatClient extends BaseChatClient {
362
425
  return;
363
426
  }
364
427
  this.started = true;
428
+ this.attachMessagingListeners();
365
429
  this.room.messaging.start();
366
- this.room.messaging.enable();
430
+ this.startRoomStatusListener();
431
+ await this.room.messaging.enable();
432
+ this.waitingForParticipant = true;
433
+ this.onMessagingChanged();
367
434
  }
368
435
  async stop() {
369
436
  this.started = false;
370
- this.room.messaging.removeListener("message", this.onMessagingMessage);
437
+ this.roomAbortController?.abort();
438
+ this.roomAbortController = undefined;
439
+ this.detachMessagingListeners();
371
440
  for (const wait of this.pendingParticipantWaits.values()) {
372
441
  if (!wait.completed) {
373
442
  wait.completeError(new Error("Chat client stopped while waiting for an agent participant."));
@@ -375,12 +444,35 @@ class MessagingChatClient extends BaseChatClient {
375
444
  }
376
445
  this.pendingParticipantWaits.clear();
377
446
  }
447
+ attachMessagingListeners() {
448
+ if (this.messagingListenersAttached) {
449
+ return;
450
+ }
451
+ this.room.messaging.addListener("message", this.onMessagingMessage);
452
+ this.room.messaging.addListener("participant_added", this.onMessagingParticipantChanged);
453
+ this.room.messaging.addListener("participant_removed", this.onMessagingParticipantRemoved);
454
+ this.room.messaging.addListener("messaging_enabled", this.onMessagingParticipantChanged);
455
+ this.messagingListenersAttached = true;
456
+ }
457
+ detachMessagingListeners() {
458
+ if (!this.messagingListenersAttached) {
459
+ return;
460
+ }
461
+ this.room.messaging.removeListener("messaging_enabled", this.onMessagingParticipantChanged);
462
+ this.room.messaging.removeListener("participant_removed", this.onMessagingParticipantRemoved);
463
+ this.room.messaging.removeListener("participant_added", this.onMessagingParticipantChanged);
464
+ this.room.messaging.removeListener("message", this.onMessagingMessage);
465
+ this.messagingListenersAttached = false;
466
+ }
378
467
  agentParticipant() {
379
468
  const normalizedAgentName = this.agentName?.trim();
380
469
  for (const participant of this.room.messaging.remoteParticipants) {
381
470
  if (normalizedAgentName != null && normalizedAgentName !== "" && participant.getAttribute("name") !== normalizedAgentName) {
382
471
  continue;
383
472
  }
473
+ if (normalizedAgentName != null && normalizedAgentName !== "") {
474
+ return participant;
475
+ }
384
476
  if (participant.getAttribute("supports_agent_messages") === true) {
385
477
  return participant;
386
478
  }
@@ -405,7 +497,13 @@ class MessagingChatClient extends BaseChatClient {
405
497
  }
406
498
  async sendAgentMessage(message, { attachment, ignoreOffline = false } = {}) {
407
499
  await this.start();
408
- const participant = this.agentParticipant() ?? await this.waitForAgentParticipant({ waitKey: message.messageId });
500
+ let participant = this.agentParticipant();
501
+ if (participant == null) {
502
+ if (ignoreOffline) {
503
+ return;
504
+ }
505
+ participant = await this.waitForAgentParticipant({ waitKey: message.messageId });
506
+ }
409
507
  await this.room.messaging.sendMessage({
410
508
  to: participant,
411
509
  type: agent_messages_js_1.agentRoomMessageType,
@@ -414,8 +512,51 @@ class MessagingChatClient extends BaseChatClient {
414
512
  ignoreOffline,
415
513
  });
416
514
  }
515
+ startRoomStatusListener() {
516
+ if (this.roomAbortController != null) {
517
+ return;
518
+ }
519
+ const controller = new AbortController();
520
+ this.roomAbortController = controller;
521
+ void (async () => {
522
+ for await (const event of this.room.listen({ abortSignal: controller.signal })) {
523
+ if (controller.signal.aborted || event.name !== "room.status") {
524
+ continue;
525
+ }
526
+ const status = String(event.status ?? "").trim().toLowerCase();
527
+ const message = String(event.message ?? "").trim();
528
+ if (status === "connected" || status === "ready" || status === "reconnected") {
529
+ this.waitingForParticipant = true;
530
+ this.onMessagingChanged();
531
+ }
532
+ else if (status === "disconnected" || status === "reconnecting" || status === "connecting") {
533
+ this.agentParticipantId = undefined;
534
+ this.waitingForParticipant = true;
535
+ this.emitConnectionStatus({
536
+ status: status === "disconnected" ? "disconnected" : "reconnecting",
537
+ message: message !== "" ? message : status === "disconnected" ? "Agent messaging disconnected" : "Waiting for agent messaging",
538
+ reason: message !== "" ? message : undefined,
539
+ });
540
+ }
541
+ }
542
+ })().catch(() => undefined);
543
+ }
544
+ async reopenOpenSessions() {
545
+ for (const session of this.sessions) {
546
+ if (!session.isOpen) {
547
+ continue;
548
+ }
549
+ await session.open({ load: true, sinceTurn: session.lastCompletedTurnId });
550
+ }
551
+ }
417
552
  }
418
553
  exports.MessagingChatClient = MessagingChatClient;
554
+ function isThreadListMessage(message) {
555
+ return message.type === agent_messages_js_1.agentThreadListedType
556
+ || message.type === agent_messages_js_1.agentThreadCreatedType
557
+ || message.type === agent_messages_js_1.agentThreadUpdatedType
558
+ || message.type === agent_messages_js_1.agentThreadDeletedType;
559
+ }
419
560
  class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
420
561
  constructor({ client, threadPath }) {
421
562
  super();
@@ -463,6 +604,9 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
463
604
  async open({ load = true, sinceTurn } = {}) {
464
605
  if (this.openState) {
465
606
  if (load) {
607
+ if (sinceTurn == null) {
608
+ this.resetReplayState();
609
+ }
466
610
  const openThread = new agent_messages_js_1.OpenThread({ threadId: this.threadPath, load: true, sinceTurn });
467
611
  this.setLoadState(this.loadingStateForOpen(openThread, sinceTurn));
468
612
  try {
@@ -493,6 +637,17 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
493
637
  throw error;
494
638
  }
495
639
  }
640
+ resetReplayState() {
641
+ this._messages.length = 0;
642
+ this.messageIndexes.clear();
643
+ this.localAgentMessageIds.clear();
644
+ this.pendingLocalInputMessageIds.clear();
645
+ this.mergedDeltaMessageIds.clear();
646
+ this.pendingInputsById.clear();
647
+ this.lastCompletedTurnIdValue = undefined;
648
+ this.lastAgentMessageType = undefined;
649
+ this.lastAgentMessageAt = undefined;
650
+ }
496
651
  async close() {
497
652
  if (!this.openState) {
498
653
  return;
@@ -524,7 +679,7 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
524
679
  response,
525
680
  }));
526
681
  }
527
- async sendText({ messageId, text, attachments, steer = false, turnId, backend, provider, model, voice, outputModalities, senderName, clientToolkits, }) {
682
+ async sendText({ messageId, text, attachments, steer = false, turnId, backend, provider, model, voice, outputModalities, senderName, clientToolkits, toolChoice, }) {
528
683
  const resolvedMessageId = messageId == null || messageId.trim() === "" ? (0, uuid_1.v4)() : messageId.trim();
529
684
  const currentModel = this.currentModelValue;
530
685
  const resolvedBackend = nonEmptyTrimmed(backend) ?? currentModel?.backend;
@@ -553,6 +708,7 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
553
708
  voice: resolvedVoice,
554
709
  outputModalities: resolvedOutputModalities,
555
710
  clientToolkits: clientToolkits != null && clientToolkits.length > 0 ? clientToolkits : undefined,
711
+ toolChoice: toolChoice?.toJson(),
556
712
  });
557
713
  this.localAgentMessageIds.add(resolvedMessageId);
558
714
  this.pendingLocalInputMessageIds.add(resolvedMessageId);
@@ -602,9 +758,9 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
602
758
  const message = event.message;
603
759
  const type = message.type;
604
760
  this.lastAgentMessageType = type;
605
- this.lastAgentMessageAt = new Date();
761
+ this.lastAgentMessageAt = event.createdAt;
606
762
  this.updateSessionState(message);
607
- this.updatePendingInputsForMessage(message);
763
+ this.updatePendingInputsForMessage(message, event.createdAt);
608
764
  this.appendRenderableMessage(event);
609
765
  this.notifyListeners();
610
766
  }
@@ -670,7 +826,7 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
670
826
  }), { notify: false });
671
827
  }
672
828
  }
673
- updatePendingInputsForMessage(message) {
829
+ updatePendingInputsForMessage(message, createdAt) {
674
830
  const type = message.type;
675
831
  const messageId = message.messageId;
676
832
  const sourceMessageId = sourceMessageIdFor(message);
@@ -684,7 +840,7 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
684
840
  messageType: type,
685
841
  threadPath: this.threadPath,
686
842
  payload: message,
687
- createdAt: new Date(),
843
+ createdAt,
688
844
  awaitingAcceptance: true,
689
845
  awaitingApplication: true,
690
846
  }));
@@ -723,6 +879,31 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
723
879
  }
724
880
  return;
725
881
  }
882
+ if (message.type === agent_messages_js_1.agentToolCallEndedType) {
883
+ const itemId = message.error == null ? undefined : normalizedString(message.itemId);
884
+ if (itemId != null) {
885
+ this.upsertItemMessage(`tool_call:${itemId}`, event);
886
+ }
887
+ else if (message.error != null) {
888
+ this.appendMessage(event);
889
+ }
890
+ return;
891
+ }
892
+ if (message.type === agent_messages_js_1.agentToolCallPendingType || message.type === agent_messages_js_1.agentToolCallInProgressType || message.type === agent_messages_js_1.agentToolCallStartedType) {
893
+ const itemId = normalizedString(message.itemId);
894
+ if (itemId != null) {
895
+ this.upsertItemMessage(`tool_call:${itemId}`, event);
896
+ }
897
+ return;
898
+ }
899
+ if (message.type === agent_messages_js_1.agentSecretRequestedType) {
900
+ this.appendMessage(event);
901
+ return;
902
+ }
903
+ if (message.type === agent_messages_js_1.agentModelChangedType) {
904
+ this.appendMessage(event);
905
+ return;
906
+ }
726
907
  if (message.type === agent_messages_js_1.agentThreadStartType || message.type === agent_messages_js_1.agentTurnStartType || message.type === agent_messages_js_1.agentTurnSteerType) {
727
908
  if (agentInputText(message.content).trim() !== "") {
728
909
  this.appendMessage(event);
@@ -738,6 +919,18 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
738
919
  if (agentInputText(message.content).trim() !== "") {
739
920
  this.appendMessage(event, { beforePendingLocalInputs: true });
740
921
  }
922
+ else {
923
+ this.appendMessage(event);
924
+ }
925
+ return;
926
+ }
927
+ if (message.type === agent_messages_js_1.agentTurnStartedType ||
928
+ message.type === agent_messages_js_1.agentTurnSteeredType ||
929
+ message.type === agent_messages_js_1.agentTurnStartRejectedType ||
930
+ message.type === agent_messages_js_1.agentTurnSteerRejectedType ||
931
+ message.type === agent_messages_js_1.agentTurnInterruptAcceptedType ||
932
+ message.type === agent_messages_js_1.agentTurnInterruptedType) {
933
+ this.appendMessage(event);
741
934
  return;
742
935
  }
743
936
  if (isMergeableDelta(message)) {
@@ -751,6 +944,10 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
751
944
  }
752
945
  return;
753
946
  }
947
+ if (message.type === agent_messages_js_1.agentUsageUpdatedType) {
948
+ this.appendMessage(event);
949
+ return;
950
+ }
754
951
  if (message instanceof agent_messages_js_1.AgentClientToolCallRequested) {
755
952
  this.appendMessage(event);
756
953
  }
@@ -803,7 +1000,7 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
803
1000
  const existing = this._messages[existingIndex].message;
804
1001
  const merged = mergeDeltaMessage(existing, message);
805
1002
  if (merged != null) {
806
- this._messages[existingIndex] = new AgentMessageEvent({ message: merged, attachment: event.attachment });
1003
+ this._messages[existingIndex] = new AgentMessageEvent({ message: merged, createdAt: event.createdAt, attachment: event.attachment });
807
1004
  }
808
1005
  }
809
1006
  indexMessagesFrom(start) {
@@ -968,6 +1165,16 @@ function normalizedString(value) {
968
1165
  const normalized = value?.trim();
969
1166
  return normalized == null || normalized === "" ? undefined : normalized;
970
1167
  }
1168
+ function dateOrNull(value) {
1169
+ if (value instanceof Date && !Number.isNaN(value.getTime())) {
1170
+ return value;
1171
+ }
1172
+ if (typeof value !== "string" || value.trim() === "") {
1173
+ return undefined;
1174
+ }
1175
+ const parsed = new Date(value);
1176
+ return Number.isNaN(parsed.getTime()) ? undefined : parsed;
1177
+ }
971
1178
  function nonEmptyTrimmed(value) {
972
1179
  const normalized = value?.trim();
973
1180
  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;
@@ -313,10 +313,12 @@ class AgentThreadStorageRepository extends ThreadStorageRepository {
313
313
  this.handleEvent(next.value);
314
314
  }
315
315
  })();
316
- await new Promise((resolve) => {
316
+ const ready = new Promise((resolve) => {
317
317
  this.pendingOpen = { resolve };
318
- void this.requestList();
319
318
  });
319
+ await this.chatClient.sendAgentMessage(new agent_messages_js_1.WatchThreads(), { ignoreOffline: true });
320
+ await this.requestList();
321
+ await ready;
320
322
  }
321
323
  async close() {
322
324
  this.closed = true;
@@ -325,6 +327,7 @@ class AgentThreadStorageRepository extends ThreadStorageRepository {
325
327
  this.pendingListMessageId = undefined;
326
328
  this.pendingOpen?.resolve();
327
329
  this.pendingOpen = undefined;
330
+ await this.chatClient.sendAgentMessage(new agent_messages_js_1.UnwatchThreads(), { ignoreOffline: true }).catch(() => undefined);
328
331
  }
329
332
  entries() {
330
333
  return [...this.entriesByPath.values()];
@@ -364,6 +367,12 @@ class AgentThreadStorageRepository extends ThreadStorageRepository {
364
367
  }
365
368
  handleEvent(event) {
366
369
  const message = event.message;
370
+ if (message.type === "meshagent.agent.connection.status") {
371
+ if (String(message.status ?? "").trim().toLowerCase() === "reconnected") {
372
+ void this.reconnectWatch();
373
+ }
374
+ return;
375
+ }
367
376
  if (message.type === "meshagent.agent.thread.listed") {
368
377
  this.handleThreadsListed(message);
369
378
  return;
@@ -383,6 +392,13 @@ class AgentThreadStorageRepository extends ThreadStorageRepository {
383
392
  }
384
393
  }
385
394
  }
395
+ async reconnectWatch() {
396
+ if (this.closed) {
397
+ return;
398
+ }
399
+ await this.chatClient.sendAgentMessage(new agent_messages_js_1.WatchThreads(), { ignoreOffline: true });
400
+ await this.requestList();
401
+ }
386
402
  handleThreadsListed(message) {
387
403
  const nextEntries = new Map();
388
404
  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
- protected constructor({ type, messageId, message_id, senderName, sender_name }: JsonMap);
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;
@@ -18,6 +18,8 @@ export const agentParticipantDisconnectType = "meshagent.agent.participant.disco
18
18
  export const agentThreadDeleteType = "meshagent.agent.thread.delete";
19
19
  export const agentThreadRenameType = "meshagent.agent.thread.rename";
20
20
  export const agentThreadListType = "meshagent.agent.thread.list";
21
+ export const agentThreadWatchType = "meshagent.agent.thread.watch";
22
+ export const agentThreadUnwatchType = "meshagent.agent.thread.unwatch";
21
23
  export const agentThreadListedType = "meshagent.agent.thread.listed";
22
24
  export const agentThreadCreatedType = "meshagent.agent.thread.created";
23
25
  export const agentThreadUpdatedType = "meshagent.agent.thread.updated";
@@ -631,16 +633,18 @@ function omitUndefined(value) {
631
633
  return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
632
634
  }
633
635
  export class AgentMessage {
634
- constructor({ type, messageId, message_id, senderName, sender_name }) {
636
+ constructor({ type, messageId, message_id, senderName, sender_name, createdAt, created_at }) {
635
637
  this.type = String(type);
636
638
  const resolvedMessageId = stringOrNull(messageId ?? message_id)?.trim();
637
639
  this.messageId = resolvedMessageId == null || resolvedMessageId.length === 0 ? uuidv4() : resolvedMessageId;
638
640
  this.senderName = stringOrNull(senderName ?? sender_name);
641
+ this.createdAt = stringOrNull(createdAt ?? created_at) ?? new Date().toISOString();
639
642
  }
640
643
  toJson() {
641
644
  return {
642
645
  type: this.type,
643
646
  message_id: this.messageId,
647
+ created_at: this.createdAt,
644
648
  ...(this.senderName != null ? { sender_name: this.senderName } : {}),
645
649
  };
646
650
  }
@@ -704,6 +708,7 @@ function makeMessageClass({ type, base = "message", fields = [], }) {
704
708
  const params = {
705
709
  messageId: stringOrNull(json.message_id),
706
710
  senderName: stringOrNull(json.sender_name),
711
+ createdAt: stringOrNull(json.created_at),
707
712
  };
708
713
  if (base !== "message") {
709
714
  params.threadId = requiredString(json, "thread_id");
@@ -914,6 +919,8 @@ export const ParticipantDisconnect = makeMessageClass({ type: agentParticipantDi
914
919
  export const DeleteThread = makeMessageClass({ type: agentThreadDeleteType, base: "thread" });
915
920
  export const RenameThread = makeMessageClass({ type: agentThreadRenameType, base: "thread", fields: [s("name", "name", true)] });
916
921
  export const ListThreads = makeMessageClass({ type: agentThreadListType, fields: [{ prop: "limit", json: "limit", from: (value) => intOrNull(value) ?? 200, default: () => 200, always: true }, { prop: "offset", json: "offset", from: (value) => intOrNull(value) ?? 0, default: () => 0, always: true }] });
922
+ export const WatchThreads = makeMessageClass({ type: agentThreadWatchType });
923
+ export const UnwatchThreads = makeMessageClass({ type: agentThreadUnwatchType });
917
924
  export const ThreadsListed = makeMessageClass({ type: agentThreadListedType, fields: [s("sourceMessageId", "source_message_id", true), objList("threads", "threads", AgentThreadListEntry.fromJson, true), i("total"), i("offset"), i("limit")] });
918
925
  export const ThreadCreated = makeMessageClass({ type: agentThreadCreatedType, fields: [obj("thread", "thread", AgentThreadListEntry.fromJson, true)] });
919
926
  export const ThreadUpdated = makeMessageClass({ type: agentThreadUpdatedType, fields: [obj("thread", "thread", AgentThreadListEntry.fromJson, true)] });