@runtypelabs/persona 3.20.0 → 3.21.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.
@@ -3679,6 +3679,28 @@ var SequenceReorderBuffer = class {
3679
3679
  // src/client.ts
3680
3680
  var DEFAULT_ENDPOINT = "https://api.runtype.com/v1/dispatch";
3681
3681
  var DEFAULT_CLIENT_API_BASE = "https://api.runtype.com";
3682
+ function filenameFromMediaType(mediaType) {
3683
+ var _a, _b;
3684
+ const lower = mediaType.toLowerCase();
3685
+ const knownExtensions = {
3686
+ "application/pdf": "pdf",
3687
+ "application/json": "json",
3688
+ "application/zip": "zip",
3689
+ "text/plain": "txt",
3690
+ "text/csv": "csv",
3691
+ "text/markdown": "md"
3692
+ };
3693
+ const ext = knownExtensions[lower];
3694
+ if (ext) return `attachment.${ext}`;
3695
+ const slash = lower.indexOf("/");
3696
+ if (slash > 0) {
3697
+ const subtype = (_b = (_a = lower.slice(slash + 1).split(";")[0]) == null ? void 0 : _a.trim()) != null ? _b : "";
3698
+ if (subtype && subtype !== "octet-stream" && /^[a-z0-9.+-]+$/i.test(subtype)) {
3699
+ return `attachment.${subtype}`;
3700
+ }
3701
+ }
3702
+ return "attachment";
3703
+ }
3682
3704
  var hasValidContent = (message) => {
3683
3705
  if (message.contentParts && message.contentParts.length > 0) {
3684
3706
  return true;
@@ -5631,6 +5653,89 @@ var AgentWidgetClient = class {
5631
5653
  }
5632
5654
  }
5633
5655
  }
5656
+ } else if (payloadType === "agent_media") {
5657
+ const rawMedia = Array.isArray(payload.media) ? payload.media : [];
5658
+ const mediaContentParts = [];
5659
+ for (const part of rawMedia) {
5660
+ if (!part || typeof part !== "object") continue;
5661
+ const rec = part;
5662
+ const partType = typeof rec.type === "string" ? rec.type : void 0;
5663
+ const rawMediaType = typeof rec.mediaType === "string" ? rec.mediaType.toLowerCase() : "";
5664
+ let src = null;
5665
+ let mediaType = "";
5666
+ if (partType === "media") {
5667
+ const data = typeof rec.data === "string" ? rec.data : void 0;
5668
+ if (!data) continue;
5669
+ mediaType = rawMediaType.length > 0 ? rawMediaType : "application/octet-stream";
5670
+ src = `data:${mediaType};base64,${data}`;
5671
+ } else if (partType === "image-url") {
5672
+ const url = typeof rec.url === "string" ? rec.url : void 0;
5673
+ if (!url) continue;
5674
+ mediaType = rawMediaType;
5675
+ src = url;
5676
+ } else if (partType === "file-url") {
5677
+ const url = typeof rec.url === "string" ? rec.url : void 0;
5678
+ if (!url) continue;
5679
+ mediaType = rawMediaType;
5680
+ src = url;
5681
+ } else {
5682
+ continue;
5683
+ }
5684
+ if (!src) continue;
5685
+ if (partType === "image-url" || mediaType.startsWith("image/")) {
5686
+ mediaContentParts.push({
5687
+ type: "image",
5688
+ image: src,
5689
+ ...mediaType ? { mimeType: mediaType } : {}
5690
+ });
5691
+ } else if (mediaType.startsWith("audio/")) {
5692
+ mediaContentParts.push({
5693
+ type: "audio",
5694
+ audio: src,
5695
+ mimeType: mediaType
5696
+ });
5697
+ } else if (mediaType.startsWith("video/")) {
5698
+ mediaContentParts.push({
5699
+ type: "video",
5700
+ video: src,
5701
+ mimeType: mediaType
5702
+ });
5703
+ } else {
5704
+ const resolvedMediaType = mediaType || "application/octet-stream";
5705
+ mediaContentParts.push({
5706
+ type: "file",
5707
+ data: src,
5708
+ mimeType: resolvedMediaType,
5709
+ filename: filenameFromMediaType(resolvedMediaType)
5710
+ });
5711
+ }
5712
+ }
5713
+ if (mediaContentParts.length > 0) {
5714
+ const seq = nextSequence();
5715
+ const toolCallIdRaw = payload.toolCallId;
5716
+ const mediaIdSuffix = typeof toolCallIdRaw === "string" && toolCallIdRaw.length > 0 ? `${toolCallIdRaw}-${seq}` : String(seq);
5717
+ const mediaMessage = {
5718
+ id: `agent-media-${mediaIdSuffix}`,
5719
+ role: "assistant",
5720
+ content: "",
5721
+ contentParts: mediaContentParts,
5722
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
5723
+ streaming: false,
5724
+ sequence: seq,
5725
+ agentMetadata: {
5726
+ executionId: payload.executionId,
5727
+ iteration: payload.iteration
5728
+ }
5729
+ };
5730
+ emitMessage(mediaMessage);
5731
+ const prevAssistant = assistantMessage;
5732
+ if (prevAssistant) {
5733
+ prevAssistant.streaming = false;
5734
+ emitMessage(prevAssistant);
5735
+ }
5736
+ assistantMessage = null;
5737
+ assistantMessageRef.current = null;
5738
+ }
5634
5739
  } else if (payloadType === "agent_iteration_complete") {
5635
5740
  } else if (payloadType === "agent_reflection" || payloadType === "agent_reflect") {
5636
5741
  const reflectionId = `agent-reflection-${payload.executionId}-${payload.iteration}`;
@@ -7655,8 +7760,10 @@ var AgentWidgetSession = class _AgentWidgetSession {
7655
7760
  */
7656
7761
  async connectStream(stream, options) {
7657
7762
  var _a, _b, _c;
7658
- if (this.streaming) return;
7659
- (_a = this.abortController) == null ? void 0 : _a.abort();
7763
+ if (this.streaming && !(options == null ? void 0 : options.allowReentry)) return;
7764
+ if (!(options == null ? void 0 : options.allowReentry)) {
7765
+ (_a = this.abortController) == null ? void 0 : _a.abort();
7766
+ }
7660
7767
  let hasStale = false;
7661
7768
  for (const msg of this.messages) {
7662
7769
  if (msg.streaming) {
@@ -7690,7 +7797,7 @@ var AgentWidgetSession = class _AgentWidgetSession {
7690
7797
  * and pipes the response stream through connectStream().
7691
7798
  */
7692
7799
  async resolveApproval(approval, decision) {
7693
- var _a, _b, _c;
7800
+ var _a, _b, _c, _d;
7694
7801
  const approvalMessageId = `approval-${approval.id}`;
7695
7802
  const updatedApproval = {
7696
7803
  ...approval,
@@ -7707,6 +7814,9 @@ var AgentWidgetSession = class _AgentWidgetSession {
7707
7814
  approval: updatedApproval
7708
7815
  };
7709
7816
  this.upsertMessage(updatedMessage);
7817
+ (_a = this.abortController) == null ? void 0 : _a.abort();
7818
+ this.abortController = new AbortController();
7819
+ this.setStreaming(true);
7710
7820
  const approvalConfig = this.config.approval;
7711
7821
  const onDecision = approvalConfig && typeof approvalConfig === "object" ? approvalConfig.onDecision : void 0;
7712
7822
  try {
@@ -7737,7 +7847,7 @@ var AgentWidgetSession = class _AgentWidgetSession {
7737
7847
  if (!response.ok) {
7738
7848
  const errorData = await response.json().catch(() => null);
7739
7849
  throw new Error(
7740
- (_a = errorData == null ? void 0 : errorData.error) != null ? _a : `Approval request failed: ${response.status}`
7850
+ (_b = errorData == null ? void 0 : errorData.error) != null ? _b : `Approval request failed: ${response.status}`
7741
7851
  );
7742
7852
  }
7743
7853
  stream = response.body;
@@ -7745,23 +7855,35 @@ var AgentWidgetSession = class _AgentWidgetSession {
7745
7855
  stream = response;
7746
7856
  }
7747
7857
  if (stream) {
7748
- await this.connectStream(stream);
7749
- } else if (decision === "denied") {
7750
- this.appendMessage({
7751
- id: `denial-${approval.id}`,
7752
- role: "assistant",
7753
- content: "Tool execution was denied by user.",
7754
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
7755
- streaming: false,
7756
- sequence: this.nextSequence()
7757
- });
7858
+ await this.connectStream(stream, { allowReentry: true });
7859
+ } else {
7860
+ if (decision === "denied") {
7861
+ this.appendMessage({
7862
+ id: `denial-${approval.id}`,
7863
+ role: "assistant",
7864
+ content: "Tool execution was denied by user.",
7865
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
7866
+ streaming: false,
7867
+ sequence: this.nextSequence()
7868
+ });
7869
+ }
7870
+ this.setStreaming(false);
7871
+ this.abortController = null;
7758
7872
  }
7873
+ } else {
7874
+ this.setStreaming(false);
7875
+ this.abortController = null;
7759
7876
  }
7760
7877
  } catch (error) {
7761
- (_c = (_b = this.callbacks).onError) == null ? void 0 : _c.call(
7762
- _b,
7763
- error instanceof Error ? error : new Error(String(error))
7764
- );
7878
+ const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("abort"));
7879
+ this.setStreaming(false);
7880
+ this.abortController = null;
7881
+ if (!isAbortError) {
7882
+ (_d = (_c = this.callbacks).onError) == null ? void 0 : _d.call(
7883
+ _c,
7884
+ error instanceof Error ? error : new Error(String(error))
7885
+ );
7886
+ }
7765
7887
  }
7766
7888
  }
7767
7889
  /**
@@ -7815,7 +7937,7 @@ var AgentWidgetSession = class _AgentWidgetSession {
7815
7937
  });
7816
7938
  }
7817
7939
  async resolveAskUserQuestion(toolMessage, answer) {
7818
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
7940
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
7819
7941
  const live = this.messages.find((m) => m.id === toolMessage.id);
7820
7942
  if (((_a = live == null ? void 0 : live.agentMetadata) == null ? void 0 : _a.askUserQuestionAnswered) === true) return;
7821
7943
  const executionId = (_b = toolMessage.agentMetadata) == null ? void 0 : _b.executionId;
@@ -7839,8 +7961,11 @@ var AgentWidgetSession = class _AgentWidgetSession {
7839
7961
  }
7840
7962
  }
7841
7963
  this.markAskUserQuestionResolved(toolMessage, structuredAnswers);
7964
+ (_h = this.abortController) == null ? void 0 : _h.abort();
7965
+ this.abortController = new AbortController();
7966
+ this.setStreaming(true);
7842
7967
  const toolCallId = toolMessage.toolCall.id;
7843
- const args = (_h = toolMessage.toolCall) == null ? void 0 : _h.args;
7968
+ const args = (_i = toolMessage.toolCall) == null ? void 0 : _i.args;
7844
7969
  const questions = Array.isArray(args == null ? void 0 : args.questions) ? args.questions : [];
7845
7970
  if (questions.length === 0) {
7846
7971
  const fallback = typeof answer === "string" ? answer : Object.entries(answer).map(
@@ -7886,17 +8011,25 @@ var AgentWidgetSession = class _AgentWidgetSession {
7886
8011
  if (!response.ok) {
7887
8012
  const errorData = await response.json().catch(() => null);
7888
8013
  throw new Error(
7889
- (_i = errorData == null ? void 0 : errorData.error) != null ? _i : `Resume failed: ${response.status}`
8014
+ (_j = errorData == null ? void 0 : errorData.error) != null ? _j : `Resume failed: ${response.status}`
7890
8015
  );
7891
8016
  }
7892
8017
  if (response.body) {
7893
- await this.connectStream(response.body);
8018
+ await this.connectStream(response.body, { allowReentry: true });
8019
+ } else {
8020
+ this.setStreaming(false);
8021
+ this.abortController = null;
7894
8022
  }
7895
8023
  } catch (error) {
7896
- (_k = (_j = this.callbacks).onError) == null ? void 0 : _k.call(
7897
- _j,
7898
- error instanceof Error ? error : new Error(String(error))
7899
- );
8024
+ const isAbortError = error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted") || error.message.includes("abort"));
8025
+ this.setStreaming(false);
8026
+ this.abortController = null;
8027
+ if (!isAbortError) {
8028
+ (_l = (_k = this.callbacks).onError) == null ? void 0 : _l.call(
8029
+ _k,
8030
+ error instanceof Error ? error : new Error(String(error))
8031
+ );
8032
+ }
7900
8033
  }
7901
8034
  }
7902
8035
  cancel() {
@@ -11125,6 +11258,19 @@ var isSafeImageSrc = (src) => {
11125
11258
  if (!src.includes(":")) return true;
11126
11259
  return false;
11127
11260
  };
11261
+ var isSafeMediaSrc = (src) => {
11262
+ const lower = src.toLowerCase();
11263
+ if (lower.startsWith("javascript:")) return false;
11264
+ if (lower.startsWith("data:text/html")) return false;
11265
+ if (lower.startsWith("data:text/javascript")) return false;
11266
+ if (lower.startsWith("data:text/xml")) return false;
11267
+ if (lower.startsWith("data:application/xhtml")) return false;
11268
+ if (lower.startsWith("data:image/svg+xml")) return false;
11269
+ if (/^(?:https?|blob):/i.test(src)) return true;
11270
+ if (lower.startsWith("data:")) return true;
11271
+ if (!src.includes(":")) return true;
11272
+ return false;
11273
+ };
11128
11274
  var MESSAGE_IMAGE_PREVIEW_MAX_WIDTH_PX = 320;
11129
11275
  var MESSAGE_IMAGE_PREVIEW_MAX_HEIGHT_PX = 320;
11130
11276
  var getMessageImageParts = (message) => {
@@ -11135,6 +11281,24 @@ var getMessageImageParts = (message) => {
11135
11281
  (part) => part.type === "image" && typeof part.image === "string" && part.image.trim().length > 0
11136
11282
  );
11137
11283
  };
11284
+ var getMessageAudioParts = (message) => {
11285
+ if (!message.contentParts || message.contentParts.length === 0) return [];
11286
+ return message.contentParts.filter(
11287
+ (part) => part.type === "audio" && typeof part.audio === "string" && part.audio.trim().length > 0
11288
+ );
11289
+ };
11290
+ var getMessageVideoParts = (message) => {
11291
+ if (!message.contentParts || message.contentParts.length === 0) return [];
11292
+ return message.contentParts.filter(
11293
+ (part) => part.type === "video" && typeof part.video === "string" && part.video.trim().length > 0
11294
+ );
11295
+ };
11296
+ var getMessageFileParts = (message) => {
11297
+ if (!message.contentParts || message.contentParts.length === 0) return [];
11298
+ return message.contentParts.filter(
11299
+ (part) => part.type === "file" && typeof part.data === "string" && part.data.trim().length > 0
11300
+ );
11301
+ };
11138
11302
  var createMessageImagePreviews = (imageParts, hasVisibleText, onPreviewFailed) => {
11139
11303
  if (imageParts.length === 0) return null;
11140
11304
  try {
@@ -11203,6 +11367,109 @@ var createMessageImagePreviews = (imageParts, hasVisibleText, onPreviewFailed) =
11203
11367
  return null;
11204
11368
  }
11205
11369
  };
11370
+ var createMessageAudioPreviews = (audioParts) => {
11371
+ if (audioParts.length === 0) return null;
11372
+ try {
11373
+ const container = createElement(
11374
+ "div",
11375
+ "persona-flex persona-flex-col persona-gap-2"
11376
+ );
11377
+ container.setAttribute("data-message-attachments", "audio");
11378
+ let visible = 0;
11379
+ audioParts.forEach((part) => {
11380
+ if (!isSafeMediaSrc(part.audio)) return;
11381
+ const audioElement = createElement("audio");
11382
+ audioElement.controls = true;
11383
+ audioElement.preload = "metadata";
11384
+ audioElement.src = part.audio;
11385
+ audioElement.style.display = "block";
11386
+ audioElement.style.width = "100%";
11387
+ audioElement.style.maxWidth = `${MESSAGE_IMAGE_PREVIEW_MAX_WIDTH_PX}px`;
11388
+ container.appendChild(audioElement);
11389
+ visible += 1;
11390
+ });
11391
+ if (visible === 0) {
11392
+ container.remove();
11393
+ return null;
11394
+ }
11395
+ return container;
11396
+ } catch {
11397
+ return null;
11398
+ }
11399
+ };
11400
+ var createMessageVideoPreviews = (videoParts) => {
11401
+ if (videoParts.length === 0) return null;
11402
+ try {
11403
+ const container = createElement(
11404
+ "div",
11405
+ "persona-flex persona-flex-col persona-gap-2"
11406
+ );
11407
+ container.setAttribute("data-message-attachments", "video");
11408
+ let visible = 0;
11409
+ videoParts.forEach((part) => {
11410
+ if (!isSafeMediaSrc(part.video)) return;
11411
+ const videoElement = createElement("video");
11412
+ videoElement.controls = true;
11413
+ videoElement.preload = "metadata";
11414
+ videoElement.src = part.video;
11415
+ videoElement.style.display = "block";
11416
+ videoElement.style.width = "100%";
11417
+ videoElement.style.maxWidth = `${MESSAGE_IMAGE_PREVIEW_MAX_WIDTH_PX}px`;
11418
+ videoElement.style.maxHeight = `${MESSAGE_IMAGE_PREVIEW_MAX_HEIGHT_PX}px`;
11419
+ videoElement.style.borderRadius = "10px";
11420
+ videoElement.style.backgroundColor = "var(--persona-attachment-image-bg, var(--persona-container, #f3f4f6))";
11421
+ container.appendChild(videoElement);
11422
+ visible += 1;
11423
+ });
11424
+ if (visible === 0) {
11425
+ container.remove();
11426
+ return null;
11427
+ }
11428
+ return container;
11429
+ } catch {
11430
+ return null;
11431
+ }
11432
+ };
11433
+ var createMessageFilePreviews = (fileParts) => {
11434
+ if (fileParts.length === 0) return null;
11435
+ try {
11436
+ const container = createElement(
11437
+ "div",
11438
+ "persona-flex persona-flex-col persona-gap-2"
11439
+ );
11440
+ container.setAttribute("data-message-attachments", "files");
11441
+ let visible = 0;
11442
+ fileParts.forEach((part) => {
11443
+ if (!isSafeMediaSrc(part.data)) return;
11444
+ const link = createElement("a");
11445
+ link.href = part.data;
11446
+ link.download = part.filename;
11447
+ link.target = "_blank";
11448
+ link.rel = "noopener noreferrer";
11449
+ link.textContent = part.filename;
11450
+ link.className = "persona-message-file-attachment";
11451
+ link.style.display = "inline-flex";
11452
+ link.style.alignItems = "center";
11453
+ link.style.gap = "6px";
11454
+ link.style.padding = "6px 10px";
11455
+ link.style.borderRadius = "8px";
11456
+ link.style.fontSize = "0.875rem";
11457
+ link.style.textDecoration = "underline";
11458
+ link.style.backgroundColor = "var(--persona-attachment-file-bg, var(--persona-container, #f3f4f6))";
11459
+ link.style.border = "1px solid var(--persona-attachment-file-border, var(--persona-border, #e5e7eb))";
11460
+ link.style.color = "inherit";
11461
+ container.appendChild(link);
11462
+ visible += 1;
11463
+ });
11464
+ if (visible === 0) {
11465
+ container.remove();
11466
+ return null;
11467
+ }
11468
+ return container;
11469
+ } catch {
11470
+ return null;
11471
+ }
11472
+ };
11206
11473
  var createTypingIndicator = () => {
11207
11474
  const container = document.createElement("div");
11208
11475
  container.className = "persona-flex persona-items-center persona-space-x-1 persona-h-5 persona-mt-2";
@@ -11533,6 +11800,27 @@ var createStandardBubble = (message, transform, layoutConfig, actionsConfig, act
11533
11800
  textContentDiv.style.display = "";
11534
11801
  }
11535
11802
  }
11803
+ const audioParts = getMessageAudioParts(message);
11804
+ if (audioParts.length > 0) {
11805
+ const audioPreviews = createMessageAudioPreviews(audioParts);
11806
+ if (audioPreviews) {
11807
+ bubble.appendChild(audioPreviews);
11808
+ }
11809
+ }
11810
+ const videoParts = getMessageVideoParts(message);
11811
+ if (videoParts.length > 0) {
11812
+ const videoPreviews = createMessageVideoPreviews(videoParts);
11813
+ if (videoPreviews) {
11814
+ bubble.appendChild(videoPreviews);
11815
+ }
11816
+ }
11817
+ const fileParts = getMessageFileParts(message);
11818
+ if (fileParts.length > 0) {
11819
+ const filePreviews = createMessageFilePreviews(fileParts);
11820
+ if (filePreviews) {
11821
+ bubble.appendChild(filePreviews);
11822
+ }
11823
+ }
11536
11824
  bubble.appendChild(contentDiv);
11537
11825
  if (showTimestamp && timestampPosition === "below" && message.createdAt) {
11538
11826
  const timestamp = createTimestamp(message, timestampConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runtypelabs/persona",
3
- "version": "3.20.0",
3
+ "version": "3.21.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",