@runtypelabs/persona 3.5.0 → 3.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runtypelabs/persona",
3
- "version": "3.5.0",
3
+ "version": "3.5.1",
4
4
  "description": "Themeable, pluggable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
package/src/client.ts CHANGED
@@ -881,7 +881,8 @@ export class AgentWidgetClient {
881
881
  onEvent: SSEHandler,
882
882
  assistantMessageRef: { current: AgentWidgetMessage | null },
883
883
  emitMessage: (msg: AgentWidgetMessage) => void,
884
- nextSequence: () => number
884
+ nextSequence: () => number,
885
+ partIdState: { current: string | null }
885
886
  ): Promise<boolean> {
886
887
  if (!this.parseSSEEvent) return false;
887
888
 
@@ -889,8 +890,7 @@ export class AgentWidgetClient {
889
890
  const result = await this.parseSSEEvent(payload);
890
891
  if (result === null) return false; // Event should be ignored
891
892
 
892
- const ensureAssistant = () => {
893
- if (assistantMessageRef.current) return assistantMessageRef.current;
893
+ const createNewAssistant = (partId?: string): AgentWidgetMessage => {
894
894
  const msg: AgentWidgetMessage = {
895
895
  id: `assistant-${Date.now()}-${Math.random().toString(16).slice(2)}`,
896
896
  role: "assistant",
@@ -898,15 +898,42 @@ export class AgentWidgetClient {
898
898
  createdAt: new Date().toISOString(),
899
899
  streaming: true,
900
900
  variant: "assistant",
901
- sequence: nextSequence()
901
+ sequence: nextSequence(),
902
+ ...(partId !== undefined && { partId })
902
903
  };
903
904
  assistantMessageRef.current = msg;
904
905
  emitMessage(msg);
905
906
  return msg;
906
907
  };
907
908
 
909
+ const ensureAssistant = (partId?: string) => {
910
+ if (assistantMessageRef.current) return assistantMessageRef.current;
911
+ return createNewAssistant(partId);
912
+ };
913
+
908
914
  if (result.text !== undefined) {
909
- const assistant = ensureAssistant();
915
+ // partId-based message segmentation: when partId changes, seal current
916
+ // message and start a new one for chronological tool/text interleaving
917
+ if (result.partId !== undefined && partIdState.current !== null && result.partId !== partIdState.current) {
918
+ // Seal the current assistant message
919
+ if (assistantMessageRef.current) {
920
+ assistantMessageRef.current.streaming = false;
921
+ emitMessage(assistantMessageRef.current);
922
+ }
923
+ // Create a new assistant message for the new text segment
924
+ createNewAssistant(result.partId);
925
+ }
926
+
927
+ // Update partId tracking (only when partId is provided — backward compatible)
928
+ if (result.partId !== undefined) {
929
+ partIdState.current = result.partId;
930
+ }
931
+
932
+ const assistant = ensureAssistant(result.partId);
933
+ // Tag the message with partId if present and not already set
934
+ if (result.partId !== undefined && !assistant.partId) {
935
+ assistant.partId = result.partId;
936
+ }
910
937
  assistant.content += result.text;
911
938
  emitMessage(assistant);
912
939
  }
@@ -916,10 +943,12 @@ export class AgentWidgetClient {
916
943
  assistantMessageRef.current.streaming = false;
917
944
  emitMessage(assistantMessageRef.current);
918
945
  }
946
+ partIdState.current = null;
919
947
  onEvent({ type: "status", status: "idle" });
920
948
  }
921
949
 
922
950
  if (result.error) {
951
+ partIdState.current = null;
923
952
  onEvent({
924
953
  type: "error",
925
954
  error: new Error(result.error)
@@ -987,6 +1016,8 @@ export class AgentWidgetClient {
987
1016
  let assistantMessage: AgentWidgetMessage | null = null;
988
1017
  // Reference to track assistant message for custom event handler
989
1018
  const assistantMessageRef = { current: null as AgentWidgetMessage | null };
1019
+ // Track current partId for message segmentation at tool boundaries
1020
+ const partIdState = { current: null as string | null };
990
1021
  const reasoningMessages = new Map<string, AgentWidgetMessage>();
991
1022
  const toolMessages = new Map<string, AgentWidgetMessage>();
992
1023
  const reasoningContext = {
@@ -1276,10 +1307,11 @@ export class AgentWidgetClient {
1276
1307
  onEvent,
1277
1308
  assistantMessageRef,
1278
1309
  emitMessage,
1279
- nextSequence
1310
+ nextSequence,
1311
+ partIdState
1280
1312
  );
1281
- // Update assistantMessage from ref (in case it was created)
1282
- if (assistantMessageRef.current && !assistantMessage) {
1313
+ // Update assistantMessage from ref (in case it was created or replaced by partId segmentation)
1314
+ if (assistantMessageRef.current && assistantMessageRef.current !== assistantMessage) {
1283
1315
  assistantMessage = assistantMessageRef.current;
1284
1316
  }
1285
1317
  if (handled) continue; // Skip default handling if custom handler processed it
package/src/types.ts CHANGED
@@ -1247,6 +1247,8 @@ export type AgentWidgetSSEEventResult = {
1247
1247
  done?: boolean;
1248
1248
  /** Error message if an error occurred */
1249
1249
  error?: string;
1250
+ /** Text segment identity — when this changes, a new assistant message bubble is created */
1251
+ partId?: string;
1250
1252
  } | null;
1251
1253
 
1252
1254
  /**
@@ -2843,6 +2845,12 @@ export type AgentWidgetMessage = {
2843
2845
  * }
2844
2846
  */
2845
2847
  llmContent?: string;
2848
+ /**
2849
+ * Text segment identity for chronological ordering.
2850
+ * When present, identifies which text segment this message represents
2851
+ * (e.g., "text_0", "text_1") for messages split at tool boundaries.
2852
+ */
2853
+ partId?: string;
2846
2854
  /**
2847
2855
  * Metadata for messages created during agent loop execution.
2848
2856
  * Contains execution context like iteration number and turn ID.