@meshagent/meshagent-agents 0.46.4 → 0.47.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
- ## [0.46.4]
2
- - Stability
1
+ ## [0.47.1]
2
+ - Added duplex messaging stream subscriptions and replay-state alignment in the TypeScript chat clients, bringing the NodeJS SDK in line with the new streaming model.
3
+ - Reworked messaging and room client flow control so dispatch ordering, close behavior, and presence updates stay consistent under streaming loads.
4
+ - Expanded stream and presence test coverage for the new messaging behavior.
5
+
6
+ ## [0.47.0]
7
+ - Improved TypeScript messaging clients so room delivery order and startup/presence behavior stay consistent.
8
+ - Moved managed-agent client routing onto the Rust tunnel path and hardened agent chat-client event handling.
9
+ - Added a `test:rust-process-e2e` entry point for the agents-ts package and expanded live chat/process-agent coverage.
10
+ - Finished aligning CLI pagination behavior across feeds, rooms, mailboxes, scheduled tasks, and shared helpers.
3
11
 
4
12
  ## [0.46.3]
5
13
  - `LivekitProtocolChannel.sendData` now clones outgoing `Uint8Array` payloads before publishing, avoiding shared-buffer mutation issues.
package/README.md CHANGED
@@ -204,3 +204,27 @@ npm test
204
204
  ```
205
205
 
206
206
  The build emits ESM, Node CommonJS, and browser CommonJS outputs under `dist/`.
207
+
208
+ An external E2E harness can validate interoperability with a Rust process agent by
209
+ starting that agent in a room, then supplying its room participant credentials:
210
+
211
+ ```bash
212
+ MESHAGENT_TOKEN=... \
213
+ MESHAGENT_ROOM=... \
214
+ TEST_AGENT_NAME=... \
215
+ MESHAGENT_API_URL=... \
216
+ npm run test:rust-process-e2e
217
+ ```
218
+
219
+ The runner owns only the TypeScript client. The harness remains responsible for
220
+ starting and stopping the Rust process agent named by `TEST_AGENT_NAME`. It must
221
+ also supply either `MESHAGENT_API_URL` (preferred for the Rust harness) or
222
+ `MESHAGENT_ROOM_URL`, so the client does not silently connect to production.
223
+
224
+ From this repository, the preferred full interoperability check starts the
225
+ Rust server and process agent, runs both the Dart and TypeScript suites, and
226
+ stops the fixture automatically:
227
+
228
+ ```bash
229
+ rust/meshagent-server-e2e/run-cross-language-e2e.sh
230
+ ```
@@ -747,26 +747,16 @@ function makeMessageClass({ type, base = "message", fields = [], }) {
747
747
  params.model = stringOrNull(json.model);
748
748
  }
749
749
  for (const field of fields) {
750
- if (field.required) {
751
- if (field.from != null) {
752
- params[field.prop] = field.from(json[field.json], json);
750
+ if (!hasOwn(json, field.json)) {
751
+ if (field.required) {
752
+ if (field.from != null) {
753
+ field.from(undefined, json);
754
+ }
755
+ throw new TypeError(`missing required field '${field.json}'`);
753
756
  }
754
- else if (typeof json[field.json] === "string") {
755
- params[field.prop] = requiredString(json, field.json);
756
- }
757
- else if (typeof json[field.json] === "number") {
758
- params[field.prop] = json[field.json];
759
- }
760
- else {
761
- params[field.prop] = requiredMap(json, field.json);
762
- }
763
- }
764
- else if (hasOwn(json, field.json)) {
765
- params[field.prop] = field.from != null ? field.from(json[field.json], json) : json[field.json];
766
- }
767
- else if (field.default != null) {
768
- params[field.prop] = field.default(json);
757
+ continue;
769
758
  }
759
+ params[field.prop] = json[field.json];
770
760
  }
771
761
  return new this(params);
772
762
  }
@@ -990,10 +980,10 @@ exports.AgentToolCallLogDelta = makeMessageClass({ type: exports.agentToolCallLo
990
980
  exports.AgentToolCallEnded = makeMessageClass({ type: exports.agentToolCallEndedType, base: "llm", fields: [...contentPart, { prop: "namespace", json: "namespace", from: (value) => stringOrNull(value) ?? "meshagent", default: () => "meshagent", always: true }, s("callId"), s("toolkit"), s("tool"), { prop: "result", json: "result", from: contentOrNull, to: contentHeader }, obj("error", "error", AgentError.fromJson)] });
991
981
  exports.AgentToolCallApprovalRequested = makeMessageClass({ type: exports.agentToolCallApprovalRequestedType, base: "llm", fields: toolState.filter((field) => field.prop !== "argumentBytes") });
992
982
  exports.AgentSecretRequested = makeMessageClass({ type: exports.agentSecretRequestedType, base: "llm", fields: [...contentPart, s("requestId", "request_id", true), s("name", "name", true), s("scope")] });
993
- exports.AgentClientToolCallRequested = makeMessageClass({ type: exports.agentClientToolCallRequestedType, base: "llm", fields: [...contentPart, s("requestId", "request_id", true), s("toolkit", "toolkit", true), s("tool", "tool", true), m("arguments", "arguments", true)] });
994
- exports.AgentClientToolCallCancelled = makeMessageClass({ type: exports.agentClientToolCallCancelledType, base: "llm", fields: [...contentPart, s("requestId", "request_id", true), s("reason")] });
983
+ exports.AgentClientToolCallRequested = makeMessageClass({ type: exports.agentClientToolCallRequestedType, base: "llm", fields: [...contentPart, s("requestId", "request_id", true), s("targetParticipantId"), s("toolkit", "toolkit", true), s("tool", "tool", true), m("arguments", "arguments", true)] });
984
+ exports.AgentClientToolCallCancelled = makeMessageClass({ type: exports.agentClientToolCallCancelledType, base: "llm", fields: [...contentPart, s("requestId", "request_id", true), s("targetParticipantId"), s("reason")] });
995
985
  exports.AgentThreadStatus = makeMessageClass({ type: exports.agentThreadStatusType, base: "thread", fields: [s("status"), s("mode"), s("startedAt"), s("turnId"), s("pendingItemId"), i("totalBytes"), i("linesAdded"), i("linesRemoved")] });
996
- exports.AgentConnectionStatus = makeMessageClass({ type: exports.agentConnectionStatusType, fields: [s("status", "status", true), s("message"), s("reason"), n("retryInSeconds")] });
986
+ exports.AgentConnectionStatus = makeMessageClass({ type: exports.agentConnectionStatusType, fields: [s("status", "status", true), s("message"), s("reason"), n("retryInSeconds"), s("participantId")] });
997
987
  exports.AgentThreadEvent = makeMessageClass({ type: exports.agentThreadEventType, base: "llm", fields: [m("event", "event", true)] });
998
988
  exports.AgentImageGenerationStarted = makeMessageClass({ type: exports.agentImageGenerationStartedType, base: "llm", fields: imageGeneration });
999
989
  exports.AgentImageGenerationPartial = makeMessageClass({ type: exports.agentImageGenerationPartialType, base: "llm", fields: [...imageGeneration, obj("image", "image", AgentGeneratedImage.fromJson), i("partialIndex")] });
@@ -1,6 +1,6 @@
1
- import { RemoteParticipant } from "@meshagent/meshagent";
1
+ import { MessagingStream, RemoteParticipant } from "@meshagent/meshagent";
2
2
  import type { RoomClient, Content } from "@meshagent/meshagent";
3
- import { AgentMessage, AgentModelChanged, AgentThreadStatus, ModelsResponse, ToolChoice, AgentConnectionStatus } from "./agent-messages.js";
3
+ import { AgentMessage, AgentModelChanged, AgentThreadStatus, ModelsResponse, ToolChoice, AgentConnectionStatus, AgentClientToolCallRequested } 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 {
@@ -90,6 +90,8 @@ export declare abstract class BaseChatClient extends ChangeEmitter {
90
90
  stop(): Promise<void>;
91
91
  agentParticipant(): RemoteParticipant | null;
92
92
  localParticipantName(): string | undefined;
93
+ localParticipantId(): string | undefined;
94
+ isClientToolCallForLocalParticipant(request: InstanceType<typeof AgentClientToolCallRequested>): boolean;
93
95
  protected cleanParticipantName(value: string | undefined): string | undefined;
94
96
  openThread(threadPath: string, { load, sinceTurn, reloadIfOpen, drainPendingEvents, }?: {
95
97
  load?: boolean;
@@ -131,7 +133,30 @@ export declare abstract class BaseChatClient extends ChangeEmitter {
131
133
  }): void;
132
134
  removeThreadSession(threadPath: string): void;
133
135
  }
134
- export declare class MessagingChatClient extends BaseChatClient {
136
+ export declare const messagingChatThreadSubscribeType = "meshagent.chat.thread.subscribe";
137
+ export declare const messagingChatThreadListSubscribeType = "meshagent.chat.thread_list.subscribe";
138
+ export declare class ThreadSubscribe {
139
+ readonly threadId: string;
140
+ readonly stream: MessagingStream;
141
+ constructor({ threadId, stream }: {
142
+ threadId: string;
143
+ stream: MessagingStream;
144
+ });
145
+ get closed(): boolean;
146
+ send(message: AgentMessage, { attachment }?: {
147
+ attachment?: Uint8Array;
148
+ }): Promise<void>;
149
+ close(): Promise<void>;
150
+ }
151
+ export declare class ThreadListSubscribe {
152
+ readonly stream: MessagingStream;
153
+ constructor({ stream }: {
154
+ stream: MessagingStream;
155
+ });
156
+ get closed(): boolean;
157
+ close(): Promise<void>;
158
+ }
159
+ export declare class BaseMessagingChatClient extends BaseChatClient {
135
160
  readonly room: RoomClient;
136
161
  readonly agentName?: string;
137
162
  private readonly pendingParticipantWaits;
@@ -146,6 +171,7 @@ export declare class MessagingChatClient extends BaseChatClient {
146
171
  agentName?: string;
147
172
  });
148
173
  localParticipantName(): string | undefined;
174
+ localParticipantId(): string | undefined;
149
175
  start(): Promise<void>;
150
176
  stop(): Promise<void>;
151
177
  private attachMessagingListeners;
@@ -164,6 +190,17 @@ export declare class MessagingChatClient extends BaseChatClient {
164
190
  private reopenOpenSessions;
165
191
  private readonly onMessagingMessage;
166
192
  }
193
+ export declare class MessagingChatClient extends BaseMessagingChatClient {
194
+ private readonly threadSubscriptions;
195
+ private threadListSubscription?;
196
+ private readonly streamTasks;
197
+ stop(): Promise<void>;
198
+ private supportsThreadSubscriptions;
199
+ private trackStream;
200
+ sendAgentMessage(message: AgentMessage, { attachment }?: {
201
+ attachment?: Uint8Array;
202
+ }): Promise<void>;
203
+ }
167
204
  export declare class ChatThreadSession extends ChangeEmitter {
168
205
  private readonly client;
169
206
  readonly threadPath: string;
@@ -171,6 +208,8 @@ export declare class ChatThreadSession extends ChangeEmitter {
171
208
  private readonly messageIndexes;
172
209
  private readonly localAgentMessageIds;
173
210
  private readonly pendingLocalInputMessageIds;
211
+ private readonly resolvedInputMessageIds;
212
+ private readonly completedTurnIds;
174
213
  private readonly mergedDeltaMessageIds;
175
214
  private readonly pendingInputsById;
176
215
  private openState;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ChatThreadSession = exports.MessagingChatClient = exports.BaseChatClient = exports.ChatThreadStartResult = exports.ChatThreadSessionLoadState = exports.ChatThreadSessionLoadPhase = exports.PendingAgentInput = exports.AgentMessageEvent = void 0;
3
+ exports.ChatThreadSession = exports.MessagingChatClient = exports.BaseMessagingChatClient = exports.ThreadListSubscribe = exports.ThreadSubscribe = exports.messagingChatThreadListSubscribeType = exports.messagingChatThreadSubscribeType = exports.BaseChatClient = exports.ChatThreadStartResult = exports.ChatThreadSessionLoadState = exports.ChatThreadSessionLoadPhase = exports.PendingAgentInput = exports.AgentMessageEvent = void 0;
4
4
  const meshagent_1 = require("@meshagent/meshagent");
5
5
  const uuid_1 = require("uuid");
6
6
  const agent_messages_js_1 = require("./agent-messages.js");
@@ -175,6 +175,13 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
175
175
  localParticipantName() {
176
176
  return undefined;
177
177
  }
178
+ localParticipantId() {
179
+ return undefined;
180
+ }
181
+ isClientToolCallForLocalParticipant(request) {
182
+ const targetParticipantId = request.targetParticipantId?.trim();
183
+ return targetParticipantId == null || targetParticipantId === "" || targetParticipantId === this.localParticipantId();
184
+ }
178
185
  cleanParticipantName(value) {
179
186
  return nonEmptyTrimmed(value);
180
187
  }
@@ -280,6 +287,10 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
280
287
  }
281
288
  }
282
289
  handleAgentMessage(message, { createdAt, attachment } = {}) {
290
+ if (message instanceof agent_messages_js_1.AgentClientToolCallRequested &&
291
+ !this.isClientToolCallForLocalParticipant(message)) {
292
+ return;
293
+ }
283
294
  if (message.type === agent_messages_js_1.agentConnectionStatusType) {
284
295
  this._connectionStatus = message;
285
296
  }
@@ -332,7 +343,41 @@ class BaseChatClient extends change_emitter_js_1.ChangeEmitter {
332
343
  }
333
344
  }
334
345
  exports.BaseChatClient = BaseChatClient;
335
- class MessagingChatClient extends BaseChatClient {
346
+ exports.messagingChatThreadSubscribeType = "meshagent.chat.thread.subscribe";
347
+ exports.messagingChatThreadListSubscribeType = "meshagent.chat.thread_list.subscribe";
348
+ class ThreadSubscribe {
349
+ constructor({ threadId, stream }) {
350
+ this.threadId = threadId;
351
+ this.stream = stream;
352
+ }
353
+ get closed() {
354
+ return this.stream.closed;
355
+ }
356
+ async send(message, { attachment } = {}) {
357
+ await this.stream.sendMessage({
358
+ type: agent_messages_js_1.agentRoomMessageType,
359
+ message: message.toJson(),
360
+ attachment,
361
+ });
362
+ }
363
+ async close() {
364
+ await this.stream.close();
365
+ }
366
+ }
367
+ exports.ThreadSubscribe = ThreadSubscribe;
368
+ class ThreadListSubscribe {
369
+ constructor({ stream }) {
370
+ this.stream = stream;
371
+ }
372
+ get closed() {
373
+ return this.stream.closed;
374
+ }
375
+ async close() {
376
+ await this.stream.close();
377
+ }
378
+ }
379
+ exports.ThreadListSubscribe = ThreadListSubscribe;
380
+ class BaseMessagingChatClient extends BaseChatClient {
336
381
  constructor({ room, agentName }) {
337
382
  super();
338
383
  this.pendingParticipantWaits = new Map();
@@ -420,6 +465,9 @@ class MessagingChatClient extends BaseChatClient {
420
465
  const name = this.room.localParticipant?.getAttribute("name");
421
466
  return typeof name === "string" ? this.cleanParticipantName(name) : undefined;
422
467
  }
468
+ localParticipantId() {
469
+ return this.room.localParticipant?.id;
470
+ }
423
471
  async start() {
424
472
  if (this.started) {
425
473
  return;
@@ -546,6 +594,123 @@ class MessagingChatClient extends BaseChatClient {
546
594
  }
547
595
  }
548
596
  }
597
+ exports.BaseMessagingChatClient = BaseMessagingChatClient;
598
+ class MessagingChatClient extends BaseMessagingChatClient {
599
+ constructor() {
600
+ super(...arguments);
601
+ this.threadSubscriptions = new Map();
602
+ this.streamTasks = new Set();
603
+ }
604
+ async stop() {
605
+ const subscriptions = [
606
+ ...this.threadSubscriptions.values(),
607
+ ];
608
+ if (this.threadListSubscription != null) {
609
+ subscriptions.push(this.threadListSubscription);
610
+ }
611
+ this.threadSubscriptions.clear();
612
+ this.threadListSubscription = undefined;
613
+ await Promise.all(subscriptions.map(async (subscription) => subscription.close()));
614
+ await Promise.all(this.streamTasks);
615
+ await super.stop();
616
+ }
617
+ supportsThreadSubscriptions(participant) {
618
+ return participant.getAttribute("supports_messaging_streams") === true;
619
+ }
620
+ trackStream(subscription) {
621
+ const task = (async () => {
622
+ try {
623
+ for await (const event of subscription.stream) {
624
+ if (event.kind !== "message" || event.message.type !== agent_messages_js_1.agentRoomMessageType) {
625
+ continue;
626
+ }
627
+ const raw = event.message.message;
628
+ if (typeof raw["type"] !== "string") {
629
+ continue;
630
+ }
631
+ this.handleAgentMessage(agent_messages_js_1.AgentMessage.fromJson(raw), {
632
+ createdAt: dateOrNull(raw["created_at"]),
633
+ attachment: event.message.attachment,
634
+ });
635
+ }
636
+ }
637
+ finally {
638
+ if (subscription instanceof ThreadSubscribe) {
639
+ if (this.threadSubscriptions.get(subscription.threadId) === subscription) {
640
+ this.threadSubscriptions.delete(subscription.threadId);
641
+ }
642
+ }
643
+ else if (this.threadListSubscription === subscription) {
644
+ this.threadListSubscription = undefined;
645
+ }
646
+ }
647
+ })();
648
+ this.streamTasks.add(task);
649
+ void task.finally(() => this.streamTasks.delete(task));
650
+ }
651
+ async sendAgentMessage(message, { attachment } = {}) {
652
+ await this.start();
653
+ const participant = this.agentParticipant();
654
+ if (participant == null) {
655
+ throw new Error("Agent messaging participant is not available.");
656
+ }
657
+ if (!this.supportsThreadSubscriptions(participant)) {
658
+ await super.sendAgentMessage(message, { attachment });
659
+ return;
660
+ }
661
+ if (message.type === agent_messages_js_1.agentThreadOpenType) {
662
+ const previous = this.threadSubscriptions.get(message.threadId);
663
+ if (previous != null) {
664
+ await previous.close();
665
+ }
666
+ const stream = await this.room.messaging.stream({
667
+ to: participant,
668
+ type: exports.messagingChatThreadSubscribeType,
669
+ message: message.toJson(),
670
+ attachment,
671
+ });
672
+ const subscription = new ThreadSubscribe({ threadId: message.threadId, stream });
673
+ this.threadSubscriptions.set(message.threadId, subscription);
674
+ this.trackStream(subscription);
675
+ return;
676
+ }
677
+ if (message.type === agent_messages_js_1.agentThreadCloseType) {
678
+ const subscription = this.threadSubscriptions.get(message.threadId);
679
+ if (subscription != null) {
680
+ this.threadSubscriptions.delete(message.threadId);
681
+ await subscription.close();
682
+ return;
683
+ }
684
+ }
685
+ if (message.type === agent_messages_js_1.agentThreadWatchType) {
686
+ await this.threadListSubscription?.close();
687
+ const stream = await this.room.messaging.stream({
688
+ to: participant,
689
+ type: exports.messagingChatThreadListSubscribeType,
690
+ message: message.toJson(),
691
+ attachment,
692
+ });
693
+ const subscription = new ThreadListSubscribe({ stream });
694
+ this.threadListSubscription = subscription;
695
+ this.trackStream(subscription);
696
+ return;
697
+ }
698
+ if (message.type === agent_messages_js_1.agentThreadUnwatchType && this.threadListSubscription != null) {
699
+ const subscription = this.threadListSubscription;
700
+ this.threadListSubscription = undefined;
701
+ await subscription.close();
702
+ return;
703
+ }
704
+ if ("threadId" in message && typeof message.threadId === "string") {
705
+ const subscription = this.threadSubscriptions.get(message.threadId);
706
+ if (subscription != null) {
707
+ await subscription.send(message, { attachment });
708
+ return;
709
+ }
710
+ }
711
+ await super.sendAgentMessage(message, { attachment });
712
+ }
713
+ }
549
714
  exports.MessagingChatClient = MessagingChatClient;
550
715
  function isThreadListMessage(message) {
551
716
  return message.type === agent_messages_js_1.agentThreadListedType
@@ -560,6 +725,8 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
560
725
  this.messageIndexes = new Map();
561
726
  this.localAgentMessageIds = new Set();
562
727
  this.pendingLocalInputMessageIds = new Set();
728
+ this.resolvedInputMessageIds = new Set();
729
+ this.completedTurnIds = new Set();
563
730
  this.mergedDeltaMessageIds = new Set();
564
731
  this.pendingInputsById = new Map();
565
732
  this.openState = false;
@@ -638,6 +805,8 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
638
805
  this.messageIndexes.clear();
639
806
  this.localAgentMessageIds.clear();
640
807
  this.pendingLocalInputMessageIds.clear();
808
+ this.resolvedInputMessageIds.clear();
809
+ this.completedTurnIds.clear();
641
810
  this.mergedDeltaMessageIds.clear();
642
811
  this.pendingInputsById.clear();
643
812
  this.lastCompletedTurnIdValue = undefined;
@@ -789,15 +958,19 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
789
958
  return;
790
959
  }
791
960
  if (message.type === agent_messages_js_1.agentThreadStatusType && message instanceof agent_messages_js_1.AgentThreadStatus) {
792
- this.threadStatusValue = message;
793
961
  const mode = normalizedString(message.mode);
794
962
  const status = normalizedString(message.status);
795
963
  const turnId = normalizedString(message.turnId);
796
- if (status != null && mode === "steerable" && turnId != null) {
797
- this.activeTurnIdValue = turnId;
964
+ if (status == null) {
965
+ this.threadStatusValue = undefined;
966
+ if (turnId != null && this.activeTurnIdValue === turnId) {
967
+ this.activeTurnIdValue = undefined;
968
+ }
969
+ return;
798
970
  }
799
- else if (status == null && turnId != null && this.activeTurnIdValue === turnId) {
800
- this.activeTurnIdValue = undefined;
971
+ this.threadStatusValue = message;
972
+ if (mode === "steerable" && turnId != null) {
973
+ this.activeTurnIdValue = turnId;
801
974
  }
802
975
  return;
803
976
  }
@@ -825,21 +998,29 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
825
998
  updatePendingInputsForMessage(message, createdAt) {
826
999
  const type = message.type;
827
1000
  const messageId = message.messageId;
828
- const sourceMessageId = sourceMessageIdFor(message);
1001
+ const sourceMessageId = normalizedString(sourceMessageIdFor(message));
829
1002
  if (type === agent_messages_js_1.agentThreadStartType || type === agent_messages_js_1.agentTurnStartType || type === agent_messages_js_1.agentTurnSteerType) {
830
1003
  const normalizedMessageId = messageId.trim();
831
1004
  if (normalizedMessageId !== "") {
1005
+ const wasPending = this.pendingInputsById.has(normalizedMessageId);
1006
+ const turnId = type === agent_messages_js_1.agentTurnStartType || type === agent_messages_js_1.agentTurnSteerType
1007
+ ? normalizedString(threadMessageValue(message, "turnId"))
1008
+ : undefined;
1009
+ const wasAlreadyResolved = !wasPending && (this.resolvedInputMessageIds.has(normalizedMessageId) ||
1010
+ (turnId != null && this.completedTurnIds.has(turnId)));
832
1011
  this.localAgentMessageIds.add(normalizedMessageId);
833
1012
  this.pendingLocalInputMessageIds.add(normalizedMessageId);
834
- this.markPending(new PendingAgentInput({
835
- messageId: normalizedMessageId,
836
- messageType: type,
837
- threadPath: this.threadPath,
838
- payload: message,
839
- createdAt,
840
- awaitingAcceptance: true,
841
- awaitingApplication: true,
842
- }));
1013
+ if (!wasAlreadyResolved) {
1014
+ this.markPending(new PendingAgentInput({
1015
+ messageId: normalizedMessageId,
1016
+ messageType: type,
1017
+ threadPath: this.threadPath,
1018
+ payload: message,
1019
+ createdAt,
1020
+ awaitingAcceptance: true,
1021
+ awaitingApplication: true,
1022
+ }));
1023
+ }
843
1024
  }
844
1025
  }
845
1026
  else if (type === agent_messages_js_1.agentThreadStartRejectedType) {
@@ -852,12 +1033,14 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
852
1033
  }
853
1034
  else if (type === agent_messages_js_1.agentTurnStartedType || type === agent_messages_js_1.agentTurnSteeredType) {
854
1035
  if (sourceMessageId != null) {
1036
+ this.resolvedInputMessageIds.add(sourceMessageId);
855
1037
  this.pendingInputsById.delete(sourceMessageId);
856
1038
  this.pendingLocalInputMessageIds.delete(sourceMessageId);
857
1039
  }
858
1040
  }
859
1041
  else if (type === agent_messages_js_1.agentTurnStartRejectedType || type === agent_messages_js_1.agentTurnSteerRejectedType) {
860
1042
  if (sourceMessageId != null) {
1043
+ this.resolvedInputMessageIds.add(sourceMessageId);
861
1044
  this.pendingInputsById.delete(sourceMessageId);
862
1045
  this.pendingLocalInputMessageIds.delete(sourceMessageId);
863
1046
  }
@@ -865,6 +1048,15 @@ class ChatThreadSession extends change_emitter_js_1.ChangeEmitter {
865
1048
  else if (type === agent_messages_js_1.agentTurnEndedType || type === agent_messages_js_1.agentThreadClearedType) {
866
1049
  this.pendingInputsById.clear();
867
1050
  this.pendingLocalInputMessageIds.clear();
1051
+ if (type === agent_messages_js_1.agentTurnEndedType) {
1052
+ const turnId = normalizedString(threadMessageValue(message, "turnId"));
1053
+ if (turnId != null)
1054
+ this.completedTurnIds.add(turnId);
1055
+ }
1056
+ else if (type === agent_messages_js_1.agentThreadClearedType) {
1057
+ this.resolvedInputMessageIds.clear();
1058
+ this.completedTurnIds.clear();
1059
+ }
868
1060
  }
869
1061
  }
870
1062
  appendRenderableMessage(event) {
@@ -721,26 +721,16 @@ function makeMessageClass({ type, base = "message", fields = [], }) {
721
721
  params.model = stringOrNull(json.model);
722
722
  }
723
723
  for (const field of fields) {
724
- if (field.required) {
725
- if (field.from != null) {
726
- params[field.prop] = field.from(json[field.json], json);
724
+ if (!hasOwn(json, field.json)) {
725
+ if (field.required) {
726
+ if (field.from != null) {
727
+ field.from(undefined, json);
728
+ }
729
+ throw new TypeError(`missing required field '${field.json}'`);
727
730
  }
728
- else if (typeof json[field.json] === "string") {
729
- params[field.prop] = requiredString(json, field.json);
730
- }
731
- else if (typeof json[field.json] === "number") {
732
- params[field.prop] = json[field.json];
733
- }
734
- else {
735
- params[field.prop] = requiredMap(json, field.json);
736
- }
737
- }
738
- else if (hasOwn(json, field.json)) {
739
- params[field.prop] = field.from != null ? field.from(json[field.json], json) : json[field.json];
740
- }
741
- else if (field.default != null) {
742
- params[field.prop] = field.default(json);
731
+ continue;
743
732
  }
733
+ params[field.prop] = json[field.json];
744
734
  }
745
735
  return new this(params);
746
736
  }
@@ -964,10 +954,10 @@ export const AgentToolCallLogDelta = makeMessageClass({ type: agentToolCallLogDe
964
954
  export const AgentToolCallEnded = makeMessageClass({ type: agentToolCallEndedType, base: "llm", fields: [...contentPart, { prop: "namespace", json: "namespace", from: (value) => stringOrNull(value) ?? "meshagent", default: () => "meshagent", always: true }, s("callId"), s("toolkit"), s("tool"), { prop: "result", json: "result", from: contentOrNull, to: contentHeader }, obj("error", "error", AgentError.fromJson)] });
965
955
  export const AgentToolCallApprovalRequested = makeMessageClass({ type: agentToolCallApprovalRequestedType, base: "llm", fields: toolState.filter((field) => field.prop !== "argumentBytes") });
966
956
  export const AgentSecretRequested = makeMessageClass({ type: agentSecretRequestedType, base: "llm", fields: [...contentPart, s("requestId", "request_id", true), s("name", "name", true), s("scope")] });
967
- export const AgentClientToolCallRequested = makeMessageClass({ type: agentClientToolCallRequestedType, base: "llm", fields: [...contentPart, s("requestId", "request_id", true), s("toolkit", "toolkit", true), s("tool", "tool", true), m("arguments", "arguments", true)] });
968
- export const AgentClientToolCallCancelled = makeMessageClass({ type: agentClientToolCallCancelledType, base: "llm", fields: [...contentPart, s("requestId", "request_id", true), s("reason")] });
957
+ export const AgentClientToolCallRequested = makeMessageClass({ type: agentClientToolCallRequestedType, base: "llm", fields: [...contentPart, s("requestId", "request_id", true), s("targetParticipantId"), s("toolkit", "toolkit", true), s("tool", "tool", true), m("arguments", "arguments", true)] });
958
+ export const AgentClientToolCallCancelled = makeMessageClass({ type: agentClientToolCallCancelledType, base: "llm", fields: [...contentPart, s("requestId", "request_id", true), s("targetParticipantId"), s("reason")] });
969
959
  export const AgentThreadStatus = makeMessageClass({ type: agentThreadStatusType, base: "thread", fields: [s("status"), s("mode"), s("startedAt"), s("turnId"), s("pendingItemId"), i("totalBytes"), i("linesAdded"), i("linesRemoved")] });
970
- export const AgentConnectionStatus = makeMessageClass({ type: agentConnectionStatusType, fields: [s("status", "status", true), s("message"), s("reason"), n("retryInSeconds")] });
960
+ export const AgentConnectionStatus = makeMessageClass({ type: agentConnectionStatusType, fields: [s("status", "status", true), s("message"), s("reason"), n("retryInSeconds"), s("participantId")] });
971
961
  export const AgentThreadEvent = makeMessageClass({ type: agentThreadEventType, base: "llm", fields: [m("event", "event", true)] });
972
962
  export const AgentImageGenerationStarted = makeMessageClass({ type: agentImageGenerationStartedType, base: "llm", fields: imageGeneration });
973
963
  export const AgentImageGenerationPartial = makeMessageClass({ type: agentImageGenerationPartialType, base: "llm", fields: [...imageGeneration, obj("image", "image", AgentGeneratedImage.fromJson), i("partialIndex")] });
@@ -1,6 +1,6 @@
1
- import { RemoteParticipant } from "@meshagent/meshagent";
1
+ import { MessagingStream, RemoteParticipant } from "@meshagent/meshagent";
2
2
  import type { RoomClient, Content } from "@meshagent/meshagent";
3
- import { AgentMessage, AgentModelChanged, AgentThreadStatus, ModelsResponse, ToolChoice, AgentConnectionStatus } from "./agent-messages.js";
3
+ import { AgentMessage, AgentModelChanged, AgentThreadStatus, ModelsResponse, ToolChoice, AgentConnectionStatus, AgentClientToolCallRequested } 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 {
@@ -90,6 +90,8 @@ export declare abstract class BaseChatClient extends ChangeEmitter {
90
90
  stop(): Promise<void>;
91
91
  agentParticipant(): RemoteParticipant | null;
92
92
  localParticipantName(): string | undefined;
93
+ localParticipantId(): string | undefined;
94
+ isClientToolCallForLocalParticipant(request: InstanceType<typeof AgentClientToolCallRequested>): boolean;
93
95
  protected cleanParticipantName(value: string | undefined): string | undefined;
94
96
  openThread(threadPath: string, { load, sinceTurn, reloadIfOpen, drainPendingEvents, }?: {
95
97
  load?: boolean;
@@ -131,7 +133,30 @@ export declare abstract class BaseChatClient extends ChangeEmitter {
131
133
  }): void;
132
134
  removeThreadSession(threadPath: string): void;
133
135
  }
134
- export declare class MessagingChatClient extends BaseChatClient {
136
+ export declare const messagingChatThreadSubscribeType = "meshagent.chat.thread.subscribe";
137
+ export declare const messagingChatThreadListSubscribeType = "meshagent.chat.thread_list.subscribe";
138
+ export declare class ThreadSubscribe {
139
+ readonly threadId: string;
140
+ readonly stream: MessagingStream;
141
+ constructor({ threadId, stream }: {
142
+ threadId: string;
143
+ stream: MessagingStream;
144
+ });
145
+ get closed(): boolean;
146
+ send(message: AgentMessage, { attachment }?: {
147
+ attachment?: Uint8Array;
148
+ }): Promise<void>;
149
+ close(): Promise<void>;
150
+ }
151
+ export declare class ThreadListSubscribe {
152
+ readonly stream: MessagingStream;
153
+ constructor({ stream }: {
154
+ stream: MessagingStream;
155
+ });
156
+ get closed(): boolean;
157
+ close(): Promise<void>;
158
+ }
159
+ export declare class BaseMessagingChatClient extends BaseChatClient {
135
160
  readonly room: RoomClient;
136
161
  readonly agentName?: string;
137
162
  private readonly pendingParticipantWaits;
@@ -146,6 +171,7 @@ export declare class MessagingChatClient extends BaseChatClient {
146
171
  agentName?: string;
147
172
  });
148
173
  localParticipantName(): string | undefined;
174
+ localParticipantId(): string | undefined;
149
175
  start(): Promise<void>;
150
176
  stop(): Promise<void>;
151
177
  private attachMessagingListeners;
@@ -164,6 +190,17 @@ export declare class MessagingChatClient extends BaseChatClient {
164
190
  private reopenOpenSessions;
165
191
  private readonly onMessagingMessage;
166
192
  }
193
+ export declare class MessagingChatClient extends BaseMessagingChatClient {
194
+ private readonly threadSubscriptions;
195
+ private threadListSubscription?;
196
+ private readonly streamTasks;
197
+ stop(): Promise<void>;
198
+ private supportsThreadSubscriptions;
199
+ private trackStream;
200
+ sendAgentMessage(message: AgentMessage, { attachment }?: {
201
+ attachment?: Uint8Array;
202
+ }): Promise<void>;
203
+ }
167
204
  export declare class ChatThreadSession extends ChangeEmitter {
168
205
  private readonly client;
169
206
  readonly threadPath: string;
@@ -171,6 +208,8 @@ export declare class ChatThreadSession extends ChangeEmitter {
171
208
  private readonly messageIndexes;
172
209
  private readonly localAgentMessageIds;
173
210
  private readonly pendingLocalInputMessageIds;
211
+ private readonly resolvedInputMessageIds;
212
+ private readonly completedTurnIds;
174
213
  private readonly mergedDeltaMessageIds;
175
214
  private readonly pendingInputsById;
176
215
  private openState;