@meshagent/meshagent-agents 0.43.1 → 0.43.3

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.
@@ -15,6 +15,30 @@ class AgentMessageEvent {
15
15
  }
16
16
  }
17
17
  exports.AgentMessageEvent = AgentMessageEvent;
18
+ function agentRoomMessageFromEvent(event) {
19
+ const candidate = event.message;
20
+ if (candidate == null || typeof candidate !== "object" || Array.isArray(candidate)) {
21
+ return undefined;
22
+ }
23
+ const roomMessage = candidate;
24
+ if (roomMessage.type !== agent_messages_js_1.agentRoomMessageType) {
25
+ return undefined;
26
+ }
27
+ if (roomMessage.message == null || typeof roomMessage.message !== "object" || Array.isArray(roomMessage.message)) {
28
+ return undefined;
29
+ }
30
+ const rawMessage = roomMessage.message;
31
+ const payload = typeof rawMessage.type === "string" ? rawMessage : rawMessage.payload;
32
+ if (payload == null || typeof payload !== "object" || Array.isArray(payload)) {
33
+ return undefined;
34
+ }
35
+ return {
36
+ fromParticipantId: typeof roomMessage.fromParticipantId === "string" ? roomMessage.fromParticipantId : undefined,
37
+ message: payload,
38
+ local: roomMessage.local === true,
39
+ attachment: roomMessage.attachment instanceof Uint8Array ? roomMessage.attachment : undefined,
40
+ };
41
+ }
18
42
  class PendingAgentInput {
19
43
  constructor({ messageId, messageType, threadPath, payload, createdAt, awaitingAcceptance = false, awaitingApplication = false, awaitingOnline = false, }) {
20
44
  this.messageId = messageId;
@@ -76,28 +100,52 @@ class ChatThreadStartResult {
76
100
  exports.ChatThreadStartResult = ChatThreadStartResult;
77
101
  class EventQueue {
78
102
  constructor() {
79
- this.items = [];
80
- this.waiters = [];
103
+ this.subscribers = new Set();
81
104
  }
82
105
  add(item) {
83
- const waiter = this.waiters.shift();
84
- if (waiter != null) {
85
- waiter({ value: item, done: false });
86
- return;
106
+ for (const subscriber of this.subscribers) {
107
+ if (subscriber.closed) {
108
+ continue;
109
+ }
110
+ const waiter = subscriber.waiters.shift();
111
+ if (waiter != null) {
112
+ waiter({ value: item, done: false });
113
+ }
114
+ else {
115
+ subscriber.items.push(item);
116
+ }
87
117
  }
88
- this.items.push(item);
89
118
  }
90
119
  get stream() {
91
120
  return {
92
- [Symbol.asyncIterator]: () => ({
93
- next: async () => {
94
- const item = this.items.shift();
95
- if (item !== undefined) {
96
- return { value: item, done: false };
97
- }
98
- return new Promise((resolve) => this.waiters.push(resolve));
99
- },
100
- }),
121
+ [Symbol.asyncIterator]: () => {
122
+ const subscriber = {
123
+ items: [],
124
+ waiters: [],
125
+ closed: false,
126
+ };
127
+ this.subscribers.add(subscriber);
128
+ return {
129
+ next: async () => {
130
+ if (subscriber.closed) {
131
+ return { done: true, value: undefined };
132
+ }
133
+ const item = subscriber.items.shift();
134
+ if (item !== undefined) {
135
+ return { value: item, done: false };
136
+ }
137
+ return new Promise((resolve) => subscriber.waiters.push(resolve));
138
+ },
139
+ return: async () => {
140
+ subscriber.closed = true;
141
+ this.subscribers.delete(subscriber);
142
+ for (const waiter of subscriber.waiters.splice(0)) {
143
+ waiter({ done: true, value: undefined });
144
+ }
145
+ return { done: true, value: undefined };
146
+ },
147
+ };
148
+ },
101
149
  };
102
150
  }
103
151
  }
@@ -106,6 +154,7 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
106
154
  super(...arguments);
107
155
  this.sessionsByPath = new Map();
108
156
  this.pendingStartRequests = new Map();
157
+ this.pendingSessionEventsByPath = new Map();
109
158
  this.eventQueue = new EventQueue();
110
159
  }
111
160
  get events() {
@@ -128,7 +177,7 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
128
177
  cleanParticipantName(value) {
129
178
  return nonEmptyTrimmed(value);
130
179
  }
131
- openThread(threadPath, { load = true, sinceTurn, reloadIfOpen = false } = {}) {
180
+ openThread(threadPath, { load = true, sinceTurn, reloadIfOpen = false, drainPendingEvents = true, } = {}) {
132
181
  const normalized = threadPath.trim();
133
182
  if (normalized === "") {
134
183
  throw new TypeError("thread path cannot be empty");
@@ -136,23 +185,32 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
136
185
  const existing = this.sessionsByPath.get(normalized);
137
186
  if (existing != null) {
138
187
  if (!existing.isOpen || reloadIfOpen) {
139
- void existing.open({ load, sinceTurn });
188
+ this.openSession(existing, { load, sinceTurn });
140
189
  }
141
190
  return existing;
142
191
  }
143
192
  const created = new ChatThreadSession({ client: this, threadPath: normalized });
144
193
  this.sessionsByPath.set(normalized, created);
145
- void created.open({ load, sinceTurn });
194
+ if (drainPendingEvents) {
195
+ this.drainPendingSessionEvents(normalized, created);
196
+ }
197
+ this.openSession(created, { load, sinceTurn });
146
198
  this.notifyListeners();
147
199
  return created;
148
200
  }
149
- async startThread({ messageId, message, attachments, name, provider, model, voice, outputModalities, realtimeProtocol, senderName, clientToolkits, omitContent = false, }) {
201
+ openSession(session, options) {
202
+ void session.open(options).catch(() => {
203
+ this.notifyListeners();
204
+ });
205
+ }
206
+ async startThread({ messageId, message, attachments, name, backend, provider, model, voice, outputModalities, realtimeProtocol, senderName, clientToolkits, omitContent = false, }) {
150
207
  const resolvedMessageId = messageId == null || messageId.trim() === "" ? (0, uuid_1.v4)() : messageId.trim();
151
208
  const resolvedSenderName = this.cleanParticipantName(senderName) ?? this.localParticipantName();
152
209
  const payload = new agent_messages_js_1.StartThread({
153
210
  messageId: resolvedMessageId,
154
211
  content: omitContent ? undefined : (0, agent_messages_js_1.agentInputContent)({ text: message, attachments }),
155
212
  name: nonEmptyTrimmed(name),
213
+ backend: nonEmptyTrimmed(backend),
156
214
  provider: nonEmptyTrimmed(provider),
157
215
  model: nonEmptyTrimmed(model),
158
216
  voice: nonEmptyTrimmed(voice),
@@ -172,10 +230,11 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
172
230
  if (threadPath === "") {
173
231
  throw new Error("Agent did not return a thread_id for the new thread.");
174
232
  }
175
- const session = this.openThread(threadPath, { load: false });
233
+ const session = this.openThread(threadPath, { load: false, drainPendingEvents: false });
176
234
  if (!omitContent) {
177
235
  session.addAgentMessage(new AgentMessageEvent({ message: payload }));
178
236
  }
237
+ this.drainPendingSessionEvents(threadPath, session);
179
238
  return new ChatThreadStartResult({
180
239
  session,
181
240
  threadPath,
@@ -188,6 +247,16 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
188
247
  this.pendingStartRequests.delete(resolvedMessageId);
189
248
  }
190
249
  }
250
+ drainPendingSessionEvents(threadPath, session) {
251
+ const pendingEvents = this.pendingSessionEventsByPath.get(threadPath);
252
+ if (pendingEvents == null) {
253
+ return;
254
+ }
255
+ this.pendingSessionEventsByPath.delete(threadPath);
256
+ for (const event of pendingEvents) {
257
+ session.addAgentMessage(event);
258
+ }
259
+ }
191
260
  handleAgentMessage(message, { attachment } = {}) {
192
261
  if (message.type === agent_messages_js_1.agentConnectionStatusType) {
193
262
  this._connectionStatus = message;
@@ -212,7 +281,16 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
212
281
  this.notifyListeners();
213
282
  return;
214
283
  }
215
- this.sessionsByPath.get(threadPath)?.addAgentMessage(new AgentMessageEvent({ message, attachment }));
284
+ const event = new AgentMessageEvent({ message, attachment });
285
+ const session = this.sessionsByPath.get(threadPath);
286
+ if (session != null) {
287
+ session.addAgentMessage(event);
288
+ }
289
+ else {
290
+ const pending = this.pendingSessionEventsByPath.get(threadPath) ?? [];
291
+ pending.push(event);
292
+ this.pendingSessionEventsByPath.set(threadPath, pending);
293
+ }
216
294
  this.notifyListeners();
217
295
  }
218
296
  emitConnectionStatus({ status, message, reason, retryDelay, }) {
@@ -246,7 +324,6 @@ class MessagingChatClient extends BaseChatClient {
246
324
  constructor({ room, agentName }) {
247
325
  super();
248
326
  this.pendingParticipantWaits = new Map();
249
- this.roomAbortController = new AbortController();
250
327
  this.started = false;
251
328
  this.onMessagingChanged = () => {
252
329
  const participant = this.agentParticipant();
@@ -260,10 +337,21 @@ class MessagingChatClient extends BaseChatClient {
260
337
  }
261
338
  this.notifyListeners();
262
339
  };
340
+ this.onMessagingMessage = (event) => {
341
+ this.onMessagingChanged();
342
+ const roomMessage = agentRoomMessageFromEvent(event);
343
+ if (roomMessage == null || roomMessage.local) {
344
+ return;
345
+ }
346
+ const participant = this.agentParticipant();
347
+ if (participant == null || roomMessage.fromParticipantId !== participant.id) {
348
+ return;
349
+ }
350
+ this.handleAgentMessage(agent_messages_js_1.AgentMessage.fromJson(roomMessage.message), { attachment: roomMessage.attachment });
351
+ };
263
352
  this.room = room;
264
353
  this.agentName = agentName;
265
- this.room.messaging.addListener("message", this.onMessagingChanged);
266
- this.roomPump = this.consumeRoomEvents();
354
+ this.room.messaging.addListener("message", this.onMessagingMessage);
267
355
  }
268
356
  localParticipantName() {
269
357
  const name = this.room.localParticipant?.getAttribute("name");
@@ -279,15 +367,13 @@ class MessagingChatClient extends BaseChatClient {
279
367
  }
280
368
  async stop() {
281
369
  this.started = false;
282
- this.roomAbortController.abort();
283
- this.room.messaging.removeListener("message", this.onMessagingChanged);
370
+ this.room.messaging.removeListener("message", this.onMessagingMessage);
284
371
  for (const wait of this.pendingParticipantWaits.values()) {
285
372
  if (!wait.completed) {
286
373
  wait.completeError(new Error("Chat client stopped while waiting for an agent participant."));
287
374
  }
288
375
  }
289
376
  this.pendingParticipantWaits.clear();
290
- await this.roomPump?.catch(() => undefined);
291
377
  }
292
378
  agentParticipant() {
293
379
  const normalizedAgentName = this.agentName?.trim();
@@ -328,38 +414,16 @@ class MessagingChatClient extends BaseChatClient {
328
414
  ignoreOffline,
329
415
  });
330
416
  }
331
- async consumeRoomEvents() {
332
- try {
333
- for await (const event of this.room.listen({ abortSignal: this.roomAbortController.signal })) {
334
- this.onRoomEvent(event);
335
- }
336
- }
337
- catch {
338
- if (!this.roomAbortController.signal.aborted) {
339
- throw new Error("chat client room event listener stopped unexpectedly");
340
- }
341
- }
342
- }
343
- onRoomEvent(event) {
344
- if (!(event instanceof meshagent_1.RoomMessageEvent) || event.message.type !== agent_messages_js_1.agentRoomMessageType) {
345
- return;
346
- }
347
- const participant = this.agentParticipant();
348
- if (participant == null || event.message.fromParticipantId !== participant.id) {
349
- return;
350
- }
351
- const message = event.message.message;
352
- const rawPayload = typeof message.type === "string" ? message : message.payload;
353
- if (rawPayload != null && typeof rawPayload === "object" && !Array.isArray(rawPayload)) {
354
- this.handleAgentMessage(agent_messages_js_1.AgentMessage.fromJson(rawPayload), { attachment: event.message.attachment });
355
- }
356
- }
357
417
  }
358
418
  exports.MessagingChatClient = MessagingChatClient;
359
419
  class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
360
420
  constructor({ client, threadPath }) {
361
421
  super();
362
422
  this._messages = [];
423
+ this.messageIndexes = new Map();
424
+ this.localAgentMessageIds = new Set();
425
+ this.pendingLocalInputMessageIds = new Set();
426
+ this.mergedDeltaMessageIds = new Set();
363
427
  this.pendingInputsById = new Map();
364
428
  this.openState = false;
365
429
  this.loadStateValue = ChatThreadSessionLoadState.idle();
@@ -384,6 +448,18 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
384
448
  get lastCompletedTurnId() {
385
449
  return this.lastCompletedTurnIdValue;
386
450
  }
451
+ get activeTurnId() {
452
+ return this.activeTurnIdValue;
453
+ }
454
+ get threadStatus() {
455
+ return this.threadStatusValue;
456
+ }
457
+ get currentModel() {
458
+ return this.currentModelValue;
459
+ }
460
+ get modelsResponse() {
461
+ return this.modelsResponseValue;
462
+ }
387
463
  async open({ load = true, sinceTurn } = {}) {
388
464
  if (this.openState) {
389
465
  if (load) {
@@ -427,10 +503,11 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
427
503
  requestModels({ ignoreOffline = false } = {}) {
428
504
  return this.client.sendAgentMessage(new agent_messages_js_1.ModelsRequest({ messageId: (0, uuid_1.v4)() }), { ignoreOffline });
429
505
  }
430
- changeModel({ provider, model, voice }) {
506
+ changeModel({ backend, provider, model, voice }) {
431
507
  return this.client.sendAgentMessage(new agent_messages_js_1.ChangeModel({
432
508
  threadId: this.threadPath,
433
509
  messageId: (0, uuid_1.v4)(),
510
+ backend: nonEmptyTrimmed(backend),
434
511
  provider,
435
512
  model,
436
513
  voice: nonEmptyTrimmed(voice),
@@ -447,8 +524,16 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
447
524
  response,
448
525
  }));
449
526
  }
450
- async sendText({ messageId, text, attachments, steer = false, turnId, provider, model, voice, outputModalities, senderName, clientToolkits, }) {
527
+ async sendText({ messageId, text, attachments, steer = false, turnId, backend, provider, model, voice, outputModalities, senderName, clientToolkits, }) {
451
528
  const resolvedMessageId = messageId == null || messageId.trim() === "" ? (0, uuid_1.v4)() : messageId.trim();
529
+ const currentModel = this.currentModelValue;
530
+ const resolvedBackend = nonEmptyTrimmed(backend) ?? currentModel?.backend;
531
+ const resolvedProvider = nonEmptyTrimmed(provider) ?? currentModel?.provider;
532
+ const resolvedModel = nonEmptyTrimmed(model) ?? currentModel?.model;
533
+ const resolvedVoice = nonEmptyTrimmed(voice) ?? currentModel?.voice;
534
+ const resolvedOutputModalities = outputModalities != null && outputModalities.length > 0
535
+ ? outputModalities
536
+ : currentModel?.outputModalities;
452
537
  const payload = steer
453
538
  ? new agent_messages_js_1.TurnSteer({
454
539
  threadId: this.threadPath,
@@ -462,21 +547,15 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
462
547
  messageId: resolvedMessageId,
463
548
  senderName: nonEmptyTrimmed(senderName),
464
549
  content: (0, agent_messages_js_1.agentInputContent)({ text, attachments }),
465
- provider: nonEmptyTrimmed(provider),
466
- model: nonEmptyTrimmed(model),
467
- voice: nonEmptyTrimmed(voice),
468
- outputModalities: outputModalities != null && outputModalities.length > 0 ? outputModalities : undefined,
550
+ backend: resolvedBackend,
551
+ provider: resolvedProvider,
552
+ model: resolvedModel,
553
+ voice: resolvedVoice,
554
+ outputModalities: resolvedOutputModalities,
469
555
  clientToolkits: clientToolkits != null && clientToolkits.length > 0 ? clientToolkits : undefined,
470
556
  });
471
- this.markPending(new PendingAgentInput({
472
- messageId: resolvedMessageId,
473
- messageType: payload.type,
474
- threadPath: this.threadPath,
475
- payload,
476
- createdAt: new Date(),
477
- awaitingAcceptance: true,
478
- awaitingApplication: true,
479
- }));
557
+ this.localAgentMessageIds.add(resolvedMessageId);
558
+ this.pendingLocalInputMessageIds.add(resolvedMessageId);
480
559
  this.addAgentMessage(new AgentMessageEvent({ message: payload }));
481
560
  try {
482
561
  await this.client.sendAgentMessage(payload);
@@ -520,16 +599,86 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
520
599
  return this.client.sendAgentMessage(new agent_messages_js_1.RenameThread({ threadId: threadPath, name }));
521
600
  }
522
601
  addAgentMessage(event) {
523
- this._messages.push(event);
524
602
  const message = event.message;
525
603
  const type = message.type;
526
604
  this.lastAgentMessageType = type;
527
605
  this.lastAgentMessageAt = new Date();
606
+ this.updateSessionState(message);
607
+ this.updatePendingInputsForMessage(message);
608
+ this.appendRenderableMessage(event);
609
+ this.notifyListeners();
610
+ }
611
+ updateSessionState(message) {
612
+ if (message.type === agent_messages_js_1.agentTurnEndedType) {
613
+ const turnId = normalizedString(threadMessageValue(message, "turnId"));
614
+ if (turnId != null) {
615
+ this.lastCompletedTurnIdValue = turnId;
616
+ if (this.activeTurnIdValue === turnId) {
617
+ this.activeTurnIdValue = undefined;
618
+ }
619
+ }
620
+ const statusTurnId = normalizedString(this.threadStatusValue?.turnId);
621
+ if (statusTurnId == null || statusTurnId === turnId) {
622
+ this.threadStatusValue = undefined;
623
+ }
624
+ return;
625
+ }
626
+ if (message.type === agent_messages_js_1.agentTurnStartAcceptedType || message.type === agent_messages_js_1.agentTurnStartedType) {
627
+ const turnId = normalizedString(threadMessageValue(message, "turnId"));
628
+ if (turnId != null) {
629
+ this.activeTurnIdValue = turnId;
630
+ }
631
+ if (message.type === agent_messages_js_1.agentTurnStartedType) {
632
+ const statusTurnId = normalizedString(this.threadStatusValue?.turnId);
633
+ if (statusTurnId == null || statusTurnId === turnId) {
634
+ this.threadStatusValue = undefined;
635
+ }
636
+ }
637
+ return;
638
+ }
639
+ if (message.type === agent_messages_js_1.agentThreadStatusType && message instanceof agent_messages_js_1.AgentThreadStatus) {
640
+ this.threadStatusValue = message;
641
+ const mode = normalizedString(message.mode);
642
+ const status = normalizedString(message.status);
643
+ const turnId = normalizedString(message.turnId);
644
+ if (status != null && mode === "steerable" && turnId != null) {
645
+ this.activeTurnIdValue = turnId;
646
+ }
647
+ else if (status == null && turnId != null && this.activeTurnIdValue === turnId) {
648
+ this.activeTurnIdValue = undefined;
649
+ }
650
+ return;
651
+ }
652
+ if (message.type === agent_messages_js_1.agentModelChangedType && message instanceof agent_messages_js_1.AgentModelChanged) {
653
+ this.currentModelValue = message;
654
+ return;
655
+ }
656
+ if (message.type === agent_messages_js_1.agentModelsResponseType && message instanceof agent_messages_js_1.ModelsResponse) {
657
+ this.modelsResponseValue = message;
658
+ this.applyActiveModel(message);
659
+ return;
660
+ }
661
+ if (message.type === agent_messages_js_1.agentThreadLoadedType) {
662
+ this.setLoadState(new ChatThreadSessionLoadState({
663
+ phase: ChatThreadSessionLoadPhase.Loaded,
664
+ startedAt: this.loadStateValue.startedAt,
665
+ completedAt: this.lastAgentMessageAt,
666
+ requestMessageId: this.loadStateValue.requestMessageId,
667
+ sinceTurn: this.loadStateValue.sinceTurn,
668
+ lastMessageType: message.type,
669
+ lastMessageAt: this.lastAgentMessageAt,
670
+ }), { notify: false });
671
+ }
672
+ }
673
+ updatePendingInputsForMessage(message) {
674
+ const type = message.type;
528
675
  const messageId = message.messageId;
529
676
  const sourceMessageId = sourceMessageIdFor(message);
530
- if (type === agent_messages_js_1.agentTurnStartType || type === agent_messages_js_1.agentTurnSteerType) {
677
+ if (type === agent_messages_js_1.agentThreadStartType || type === agent_messages_js_1.agentTurnStartType || type === agent_messages_js_1.agentTurnSteerType) {
531
678
  const normalizedMessageId = messageId.trim();
532
679
  if (normalizedMessageId !== "") {
680
+ this.localAgentMessageIds.add(normalizedMessageId);
681
+ this.pendingLocalInputMessageIds.add(normalizedMessageId);
533
682
  this.markPending(new PendingAgentInput({
534
683
  messageId: normalizedMessageId,
535
684
  messageType: type,
@@ -541,37 +690,152 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
541
690
  }));
542
691
  }
543
692
  }
693
+ else if (type === agent_messages_js_1.agentThreadStartRejectedType) {
694
+ if (sourceMessageId != null) {
695
+ this.pendingInputsById.delete(sourceMessageId);
696
+ }
697
+ }
544
698
  else if (type === agent_messages_js_1.agentTurnStartAcceptedType || type === agent_messages_js_1.agentTurnSteerAcceptedType) {
545
699
  this.updatePending(sourceMessageId, { awaitingAcceptance: false, awaitingApplication: true });
546
700
  }
547
701
  else if (type === agent_messages_js_1.agentTurnStartedType || type === agent_messages_js_1.agentTurnSteeredType) {
548
702
  if (sourceMessageId != null) {
549
703
  this.pendingInputsById.delete(sourceMessageId);
704
+ this.pendingLocalInputMessageIds.delete(sourceMessageId);
550
705
  }
551
706
  }
552
707
  else if (type === agent_messages_js_1.agentTurnStartRejectedType || type === agent_messages_js_1.agentTurnSteerRejectedType) {
553
708
  if (sourceMessageId != null) {
554
709
  this.pendingInputsById.delete(sourceMessageId);
710
+ this.pendingLocalInputMessageIds.delete(sourceMessageId);
555
711
  }
556
712
  }
557
713
  else if (type === agent_messages_js_1.agentTurnEndedType || type === agent_messages_js_1.agentThreadClearedType) {
558
714
  this.pendingInputsById.clear();
559
- if (message.type === agent_messages_js_1.agentTurnEndedType && "turnId" in message && typeof message.turnId === "string" && message.turnId.trim() !== "") {
560
- this.lastCompletedTurnIdValue = message.turnId.trim();
715
+ this.pendingLocalInputMessageIds.clear();
716
+ }
717
+ }
718
+ appendRenderableMessage(event) {
719
+ const message = event.message;
720
+ if (message.type === agent_messages_js_1.agentTurnEndedType) {
721
+ if (message.error != null) {
722
+ this.appendMessage(event);
561
723
  }
724
+ return;
562
725
  }
563
- else if (type === agent_messages_js_1.agentThreadLoadedType) {
564
- this.setLoadState(new ChatThreadSessionLoadState({
565
- phase: ChatThreadSessionLoadPhase.Loaded,
566
- startedAt: this.loadStateValue.startedAt,
567
- completedAt: this.lastAgentMessageAt,
568
- requestMessageId: this.loadStateValue.requestMessageId,
569
- sinceTurn: this.loadStateValue.sinceTurn,
570
- lastMessageType: type,
571
- lastMessageAt: this.lastAgentMessageAt,
572
- }), { notify: false });
726
+ if (message.type === agent_messages_js_1.agentThreadStartType || message.type === agent_messages_js_1.agentTurnStartType || message.type === agent_messages_js_1.agentTurnSteerType) {
727
+ if (agentInputText(message.content).trim() !== "") {
728
+ this.appendMessage(event);
729
+ }
730
+ return;
731
+ }
732
+ if (message.type === agent_messages_js_1.agentTurnStartAcceptedType || message.type === agent_messages_js_1.agentTurnSteerAcceptedType) {
733
+ const sourceMessageId = normalizedString(sourceMessageIdFor(message));
734
+ if (sourceMessageId != null && this.localAgentMessageIds.has(sourceMessageId)) {
735
+ this.pendingLocalInputMessageIds.delete(sourceMessageId);
736
+ return;
737
+ }
738
+ if (agentInputText(message.content).trim() !== "") {
739
+ this.appendMessage(event, { beforePendingLocalInputs: true });
740
+ }
741
+ return;
742
+ }
743
+ if (isMergeableDelta(message)) {
744
+ this.appendOrMergeDelta(event);
745
+ return;
746
+ }
747
+ if (message.type === agent_messages_js_1.agentImageGenerationPartialType || message.type === agent_messages_js_1.agentImageGenerationCompletedType) {
748
+ const itemId = normalizedString(message.itemId);
749
+ if (itemId != null) {
750
+ this.upsertItemMessage(`image_generation:${itemId}`, event);
751
+ }
752
+ return;
753
+ }
754
+ if (message instanceof agent_messages_js_1.AgentClientToolCallRequested) {
755
+ this.appendMessage(event);
756
+ }
757
+ }
758
+ appendMessage(event, { beforePendingLocalInputs = false } = {}) {
759
+ const normalizedMessageId = normalizedString(event.message.messageId);
760
+ if (normalizedMessageId == null || this.messageIndexes.has(normalizedMessageId)) {
761
+ return;
762
+ }
763
+ if (beforePendingLocalInputs) {
764
+ const insertIndex = this._messages.findIndex((existing) => this.pendingLocalInputMessageIds.has(existing.message.messageId));
765
+ if (insertIndex >= 0) {
766
+ this._messages.splice(insertIndex, 0, event);
767
+ this.indexMessagesFrom(insertIndex);
768
+ return;
769
+ }
770
+ }
771
+ this.messageIndexes.set(normalizedMessageId, this._messages.length);
772
+ this._messages.push(event);
773
+ }
774
+ upsertItemMessage(key, event) {
775
+ const existingIndex = this.messageIndexes.get(key);
776
+ if (existingIndex == null) {
777
+ this.messageIndexes.set(key, this._messages.length);
778
+ this._messages.push(event);
779
+ return;
780
+ }
781
+ this._messages[existingIndex] = event;
782
+ }
783
+ appendOrMergeDelta(event) {
784
+ const message = event.message;
785
+ const normalizedMessageId = normalizedString(message.messageId);
786
+ if (normalizedMessageId != null) {
787
+ if (this.mergedDeltaMessageIds.has(normalizedMessageId)) {
788
+ return;
789
+ }
790
+ this.mergedDeltaMessageIds.add(normalizedMessageId);
791
+ }
792
+ const itemId = normalizedString(message.itemId);
793
+ if (itemId == null) {
794
+ return;
795
+ }
796
+ const key = `${message.type}:${itemId}`;
797
+ const existingIndex = this.messageIndexes.get(key);
798
+ if (existingIndex == null) {
799
+ this.messageIndexes.set(key, this._messages.length);
800
+ this._messages.push(event);
801
+ return;
802
+ }
803
+ const existing = this._messages[existingIndex].message;
804
+ const merged = mergeDeltaMessage(existing, message);
805
+ if (merged != null) {
806
+ this._messages[existingIndex] = new AgentMessageEvent({ message: merged, attachment: event.attachment });
807
+ }
808
+ }
809
+ indexMessagesFrom(start) {
810
+ for (let index = Math.max(0, start); index < this._messages.length; index += 1) {
811
+ const key = messageIndexKey(this._messages[index].message);
812
+ if (key != null) {
813
+ this.messageIndexes.set(key, index);
814
+ }
815
+ }
816
+ }
817
+ applyActiveModel(response) {
818
+ for (const provider of response.providers) {
819
+ for (const model of provider.models) {
820
+ if (!model.active) {
821
+ continue;
822
+ }
823
+ this.currentModelValue = new agent_messages_js_1.AgentModelChanged({
824
+ threadId: this.threadPath,
825
+ sourceMessageId: response.sourceMessageId,
826
+ backend: provider.backend,
827
+ provider: provider.name,
828
+ model: model.name,
829
+ voice: model.defaultOutputVoice,
830
+ inputFormat: model.inputFormat,
831
+ outputFormat: model.outputFormat,
832
+ turnDetection: model.turnDetection,
833
+ realtimeProtocols: model.realtimeProtocols,
834
+ outputModalities: model.modalities.length > 0 ? [model.modalities[0]] : ["text"],
835
+ });
836
+ return;
837
+ }
573
838
  }
574
- this.notifyListeners();
575
839
  }
576
840
  markClosed({ notify = true } = {}) {
577
841
  this.openState = false;
@@ -636,6 +900,74 @@ function sourceMessageIdFor(message) {
636
900
  const value = "sourceMessageId" in message ? message.sourceMessageId : undefined;
637
901
  return typeof value === "string" ? value : undefined;
638
902
  }
903
+ function threadMessageValue(message, key) {
904
+ const value = message[key];
905
+ return typeof value === "string" ? value : undefined;
906
+ }
907
+ function agentInputText(content) {
908
+ if (!Array.isArray(content)) {
909
+ return "";
910
+ }
911
+ return content
912
+ .filter((entry) => (typeof entry === "object" &&
913
+ entry !== null &&
914
+ "type" in entry &&
915
+ entry.type === "text" &&
916
+ "text" in entry &&
917
+ typeof entry.text === "string"))
918
+ .map((entry) => entry.text)
919
+ .join("");
920
+ }
921
+ function isMergeableDelta(message) {
922
+ return message.type === agent_messages_js_1.agentAudioGenerationDeltaType ||
923
+ message.type === agent_messages_js_1.agentAudioTranscriptionDeltaType ||
924
+ message.type === agent_messages_js_1.agentFileContentDeltaType ||
925
+ message.type === agent_messages_js_1.agentReasoningContentDeltaType ||
926
+ message.type === agent_messages_js_1.agentTextContentDeltaType ||
927
+ message.type === agent_messages_js_1.agentToolCallArgumentsDeltaType ||
928
+ message.type === agent_messages_js_1.agentToolCallLogDeltaType;
929
+ }
930
+ function messageIndexKey(message) {
931
+ if (isMergeableDelta(message)) {
932
+ const itemId = normalizedString(message.itemId);
933
+ return itemId == null ? undefined : `${message.type}:${itemId}`;
934
+ }
935
+ return normalizedString(message.messageId);
936
+ }
937
+ function mergeDeltaMessage(existing, incoming) {
938
+ if (existing.type !== incoming.type) {
939
+ return undefined;
940
+ }
941
+ if (existing.type === agent_messages_js_1.agentAudioGenerationDeltaType && existing instanceof agent_messages_js_1.AgentAudioGenerationDelta && incoming instanceof agent_messages_js_1.AgentAudioGenerationDelta) {
942
+ const data = new Uint8Array(existing.data.length + incoming.data.length);
943
+ data.set(existing.data, 0);
944
+ data.set(incoming.data, existing.data.length);
945
+ return new agent_messages_js_1.AgentAudioGenerationDelta({ ...existing.toJson(), data });
946
+ }
947
+ if (existing.type === agent_messages_js_1.agentAudioTranscriptionDeltaType && existing instanceof agent_messages_js_1.AgentAudioTranscriptionDelta && incoming instanceof agent_messages_js_1.AgentAudioTranscriptionDelta) {
948
+ return new agent_messages_js_1.AgentAudioTranscriptionDelta({ ...existing.toJson(), text: `${existing.text}${incoming.text}` });
949
+ }
950
+ if (existing.type === agent_messages_js_1.agentFileContentDeltaType && incoming instanceof agent_messages_js_1.AgentFileContentDelta) {
951
+ return incoming;
952
+ }
953
+ if (existing.type === agent_messages_js_1.agentReasoningContentDeltaType && existing instanceof agent_messages_js_1.AgentReasoningContentDelta && incoming instanceof agent_messages_js_1.AgentReasoningContentDelta) {
954
+ return new agent_messages_js_1.AgentReasoningContentDelta({ ...existing.toJson(), text: `${existing.text}${incoming.text}` });
955
+ }
956
+ if (existing.type === agent_messages_js_1.agentTextContentDeltaType && existing instanceof agent_messages_js_1.AgentTextContentDelta && incoming instanceof agent_messages_js_1.AgentTextContentDelta) {
957
+ return new agent_messages_js_1.AgentTextContentDelta({ ...existing.toJson(), text: `${existing.text}${incoming.text}` });
958
+ }
959
+ if (existing.type === agent_messages_js_1.agentToolCallArgumentsDeltaType && existing instanceof agent_messages_js_1.AgentToolCallArgumentsDelta && incoming instanceof agent_messages_js_1.AgentToolCallArgumentsDelta) {
960
+ return new agent_messages_js_1.AgentToolCallArgumentsDelta({ ...existing.toJson(), delta: `${existing.delta}${incoming.delta}` });
961
+ }
962
+ if (existing.type === agent_messages_js_1.agentToolCallLogDeltaType && existing instanceof agent_messages_js_1.AgentToolCallLogDelta && incoming instanceof agent_messages_js_1.AgentToolCallLogDelta) {
963
+ return new agent_messages_js_1.AgentToolCallLogDelta({ ...existing.toJson(), lines: [...existing.lines, ...incoming.lines] });
964
+ }
965
+ return undefined;
966
+ }
967
+ function normalizedString(value) {
968
+ const normalized = value?.trim();
969
+ return normalized == null || normalized === "" ? undefined : normalized;
970
+ }
639
971
  function nonEmptyTrimmed(value) {
640
972
  const normalized = value?.trim();
641
973
  return normalized == null || normalized === "" ? undefined : normalized;