@runtypelabs/persona 3.19.0 → 3.21.0

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.
@@ -3790,6 +3790,28 @@ var SequenceReorderBuffer = class {
3790
3790
  // src/client.ts
3791
3791
  var DEFAULT_ENDPOINT = "https://api.runtype.com/v1/dispatch";
3792
3792
  var DEFAULT_CLIENT_API_BASE = "https://api.runtype.com";
3793
+ function filenameFromMediaType(mediaType) {
3794
+ var _a, _b;
3795
+ const lower = mediaType.toLowerCase();
3796
+ const knownExtensions = {
3797
+ "application/pdf": "pdf",
3798
+ "application/json": "json",
3799
+ "application/zip": "zip",
3800
+ "text/plain": "txt",
3801
+ "text/csv": "csv",
3802
+ "text/markdown": "md"
3803
+ };
3804
+ const ext = knownExtensions[lower];
3805
+ if (ext) return `attachment.${ext}`;
3806
+ const slash = lower.indexOf("/");
3807
+ if (slash > 0) {
3808
+ const subtype = (_b = (_a = lower.slice(slash + 1).split(";")[0]) == null ? void 0 : _a.trim()) != null ? _b : "";
3809
+ if (subtype && subtype !== "octet-stream" && /^[a-z0-9.+-]+$/i.test(subtype)) {
3810
+ return `attachment.${subtype}`;
3811
+ }
3812
+ }
3813
+ return "attachment";
3814
+ }
3793
3815
  var hasValidContent = (message) => {
3794
3816
  if (message.contentParts && message.contentParts.length > 0) {
3795
3817
  return true;
@@ -5742,6 +5764,89 @@ var AgentWidgetClient = class {
5742
5764
  }
5743
5765
  }
5744
5766
  }
5767
+ } else if (payloadType === "agent_media") {
5768
+ const rawMedia = Array.isArray(payload.media) ? payload.media : [];
5769
+ const mediaContentParts = [];
5770
+ for (const part of rawMedia) {
5771
+ if (!part || typeof part !== "object") continue;
5772
+ const rec = part;
5773
+ const partType = typeof rec.type === "string" ? rec.type : void 0;
5774
+ const rawMediaType = typeof rec.mediaType === "string" ? rec.mediaType.toLowerCase() : "";
5775
+ let src = null;
5776
+ let mediaType = "";
5777
+ if (partType === "media") {
5778
+ const data = typeof rec.data === "string" ? rec.data : void 0;
5779
+ if (!data) continue;
5780
+ mediaType = rawMediaType.length > 0 ? rawMediaType : "application/octet-stream";
5781
+ src = `data:${mediaType};base64,${data}`;
5782
+ } else if (partType === "image-url") {
5783
+ const url = typeof rec.url === "string" ? rec.url : void 0;
5784
+ if (!url) continue;
5785
+ mediaType = rawMediaType;
5786
+ src = url;
5787
+ } else if (partType === "file-url") {
5788
+ const url = typeof rec.url === "string" ? rec.url : void 0;
5789
+ if (!url) continue;
5790
+ mediaType = rawMediaType;
5791
+ src = url;
5792
+ } else {
5793
+ continue;
5794
+ }
5795
+ if (!src) continue;
5796
+ if (partType === "image-url" || mediaType.startsWith("image/")) {
5797
+ mediaContentParts.push({
5798
+ type: "image",
5799
+ image: src,
5800
+ ...mediaType ? { mimeType: mediaType } : {}
5801
+ });
5802
+ } else if (mediaType.startsWith("audio/")) {
5803
+ mediaContentParts.push({
5804
+ type: "audio",
5805
+ audio: src,
5806
+ mimeType: mediaType
5807
+ });
5808
+ } else if (mediaType.startsWith("video/")) {
5809
+ mediaContentParts.push({
5810
+ type: "video",
5811
+ video: src,
5812
+ mimeType: mediaType
5813
+ });
5814
+ } else {
5815
+ const resolvedMediaType = mediaType || "application/octet-stream";
5816
+ mediaContentParts.push({
5817
+ type: "file",
5818
+ data: src,
5819
+ mimeType: resolvedMediaType,
5820
+ filename: filenameFromMediaType(resolvedMediaType)
5821
+ });
5822
+ }
5823
+ }
5824
+ if (mediaContentParts.length > 0) {
5825
+ const seq = nextSequence();
5826
+ const toolCallIdRaw = payload.toolCallId;
5827
+ const mediaIdSuffix = typeof toolCallIdRaw === "string" && toolCallIdRaw.length > 0 ? `${toolCallIdRaw}-${seq}` : String(seq);
5828
+ const mediaMessage = {
5829
+ id: `agent-media-${mediaIdSuffix}`,
5830
+ role: "assistant",
5831
+ content: "",
5832
+ contentParts: mediaContentParts,
5833
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
5834
+ streaming: false,
5835
+ sequence: seq,
5836
+ agentMetadata: {
5837
+ executionId: payload.executionId,
5838
+ iteration: payload.iteration
5839
+ }
5840
+ };
5841
+ emitMessage(mediaMessage);
5842
+ const prevAssistant = assistantMessage;
5843
+ if (prevAssistant) {
5844
+ prevAssistant.streaming = false;
5845
+ emitMessage(prevAssistant);
5846
+ }
5847
+ assistantMessage = null;
5848
+ assistantMessageRef.current = null;
5849
+ }
5745
5850
  } else if (payloadType === "agent_iteration_complete") {
5746
5851
  } else if (payloadType === "agent_reflection" || payloadType === "agent_reflect") {
5747
5852
  const reflectionId = `agent-reflection-${payload.executionId}-${payload.iteration}`;
@@ -7505,7 +7610,8 @@ var AgentWidgetSession = class _AgentWidgetSession {
7505
7610
  createdAt,
7506
7611
  sequence,
7507
7612
  streaming = false,
7508
- voiceProcessing
7613
+ voiceProcessing,
7614
+ rawContent
7509
7615
  } = options;
7510
7616
  const messageId = id != null ? id : role === "user" ? generateUserMessageId() : role === "assistant" ? generateAssistantMessageId() : `system-${Date.now()}-${Math.random().toString(16).slice(2)}`;
7511
7617
  const message = {
@@ -7518,7 +7624,8 @@ var AgentWidgetSession = class _AgentWidgetSession {
7518
7624
  // Only include optional fields if provided
7519
7625
  ...llmContent !== void 0 && { llmContent },
7520
7626
  ...contentParts !== void 0 && { contentParts },
7521
- ...voiceProcessing !== void 0 && { voiceProcessing }
7627
+ ...voiceProcessing !== void 0 && { voiceProcessing },
7628
+ ...rawContent !== void 0 && { rawContent }
7522
7629
  };
7523
7630
  this.upsertMessage(message);
7524
7631
  return message;
@@ -7583,7 +7690,9 @@ var AgentWidgetSession = class _AgentWidgetSession {
7583
7690
  id,
7584
7691
  createdAt,
7585
7692
  sequence,
7586
- streaming = false
7693
+ streaming = false,
7694
+ voiceProcessing,
7695
+ rawContent
7587
7696
  } = options;
7588
7697
  const messageId = id != null ? id : role === "user" ? generateUserMessageId() : role === "assistant" ? generateAssistantMessageId() : `system-${Date.now()}-${Math.random().toString(16).slice(2)}`;
7589
7698
  const message = {
@@ -7594,7 +7703,9 @@ var AgentWidgetSession = class _AgentWidgetSession {
7594
7703
  sequence: sequence != null ? sequence : this.nextSequence(),
7595
7704
  streaming,
7596
7705
  ...llmContent !== void 0 && { llmContent },
7597
- ...contentParts !== void 0 && { contentParts }
7706
+ ...contentParts !== void 0 && { contentParts },
7707
+ ...voiceProcessing !== void 0 && { voiceProcessing },
7708
+ ...rawContent !== void 0 && { rawContent }
7598
7709
  };
7599
7710
  results.push(message);
7600
7711
  }
@@ -7602,6 +7713,48 @@ var AgentWidgetSession = class _AgentWidgetSession {
7602
7713
  this.callbacks.onMessagesChanged([...this.messages]);
7603
7714
  return results;
7604
7715
  }
7716
+ /**
7717
+ * Convenience method for injecting a registered component directive as
7718
+ * an assistant message — the same shape Persona produces from a streamed
7719
+ * `{ "text": "...", "component": "...", "props": {...} }` payload.
7720
+ *
7721
+ * Sets `content` to `text`, `rawContent` to the JSON directive (so
7722
+ * `extractComponentDirectiveFromMessage` can find it), and forwards
7723
+ * `llmContent` / `id` / `createdAt` / `sequence`.
7724
+ *
7725
+ * @example
7726
+ * session.injectComponentDirective({
7727
+ * component: "DynamicForm",
7728
+ * props: { title: "Book a demo", fields: [...] },
7729
+ * text: "Share your details to book a demo.",
7730
+ * llmContent: "[Showed booking form]"
7731
+ * });
7732
+ */
7733
+ injectComponentDirective(options) {
7734
+ const {
7735
+ component,
7736
+ props = {},
7737
+ text = "",
7738
+ llmContent,
7739
+ id,
7740
+ createdAt,
7741
+ sequence
7742
+ } = options;
7743
+ const directive = {
7744
+ text,
7745
+ component,
7746
+ props
7747
+ };
7748
+ return this.injectMessage({
7749
+ role: "assistant",
7750
+ content: text,
7751
+ rawContent: JSON.stringify(directive),
7752
+ ...llmContent !== void 0 && { llmContent },
7753
+ ...id !== void 0 && { id },
7754
+ ...createdAt !== void 0 && { createdAt },
7755
+ ...sequence !== void 0 && { sequence }
7756
+ });
7757
+ }
7605
7758
  async sendMessage(rawInput, options) {
7606
7759
  var _a, _b, _c, _d, _e;
7607
7760
  const input = rawInput.trim();
@@ -11074,6 +11227,19 @@ var isSafeImageSrc = (src) => {
11074
11227
  if (!src.includes(":")) return true;
11075
11228
  return false;
11076
11229
  };
11230
+ var isSafeMediaSrc = (src) => {
11231
+ const lower = src.toLowerCase();
11232
+ if (lower.startsWith("javascript:")) return false;
11233
+ if (lower.startsWith("data:text/html")) return false;
11234
+ if (lower.startsWith("data:text/javascript")) return false;
11235
+ if (lower.startsWith("data:text/xml")) return false;
11236
+ if (lower.startsWith("data:application/xhtml")) return false;
11237
+ if (lower.startsWith("data:image/svg+xml")) return false;
11238
+ if (/^(?:https?|blob):/i.test(src)) return true;
11239
+ if (lower.startsWith("data:")) return true;
11240
+ if (!src.includes(":")) return true;
11241
+ return false;
11242
+ };
11077
11243
  var MESSAGE_IMAGE_PREVIEW_MAX_WIDTH_PX = 320;
11078
11244
  var MESSAGE_IMAGE_PREVIEW_MAX_HEIGHT_PX = 320;
11079
11245
  var getMessageImageParts = (message) => {
@@ -11084,6 +11250,24 @@ var getMessageImageParts = (message) => {
11084
11250
  (part) => part.type === "image" && typeof part.image === "string" && part.image.trim().length > 0
11085
11251
  );
11086
11252
  };
11253
+ var getMessageAudioParts = (message) => {
11254
+ if (!message.contentParts || message.contentParts.length === 0) return [];
11255
+ return message.contentParts.filter(
11256
+ (part) => part.type === "audio" && typeof part.audio === "string" && part.audio.trim().length > 0
11257
+ );
11258
+ };
11259
+ var getMessageVideoParts = (message) => {
11260
+ if (!message.contentParts || message.contentParts.length === 0) return [];
11261
+ return message.contentParts.filter(
11262
+ (part) => part.type === "video" && typeof part.video === "string" && part.video.trim().length > 0
11263
+ );
11264
+ };
11265
+ var getMessageFileParts = (message) => {
11266
+ if (!message.contentParts || message.contentParts.length === 0) return [];
11267
+ return message.contentParts.filter(
11268
+ (part) => part.type === "file" && typeof part.data === "string" && part.data.trim().length > 0
11269
+ );
11270
+ };
11087
11271
  var createMessageImagePreviews = (imageParts, hasVisibleText, onPreviewFailed) => {
11088
11272
  if (imageParts.length === 0) return null;
11089
11273
  try {
@@ -11152,6 +11336,109 @@ var createMessageImagePreviews = (imageParts, hasVisibleText, onPreviewFailed) =
11152
11336
  return null;
11153
11337
  }
11154
11338
  };
11339
+ var createMessageAudioPreviews = (audioParts) => {
11340
+ if (audioParts.length === 0) return null;
11341
+ try {
11342
+ const container = createElement(
11343
+ "div",
11344
+ "persona-flex persona-flex-col persona-gap-2"
11345
+ );
11346
+ container.setAttribute("data-message-attachments", "audio");
11347
+ let visible = 0;
11348
+ audioParts.forEach((part) => {
11349
+ if (!isSafeMediaSrc(part.audio)) return;
11350
+ const audioElement = createElement("audio");
11351
+ audioElement.controls = true;
11352
+ audioElement.preload = "metadata";
11353
+ audioElement.src = part.audio;
11354
+ audioElement.style.display = "block";
11355
+ audioElement.style.width = "100%";
11356
+ audioElement.style.maxWidth = `${MESSAGE_IMAGE_PREVIEW_MAX_WIDTH_PX}px`;
11357
+ container.appendChild(audioElement);
11358
+ visible += 1;
11359
+ });
11360
+ if (visible === 0) {
11361
+ container.remove();
11362
+ return null;
11363
+ }
11364
+ return container;
11365
+ } catch {
11366
+ return null;
11367
+ }
11368
+ };
11369
+ var createMessageVideoPreviews = (videoParts) => {
11370
+ if (videoParts.length === 0) return null;
11371
+ try {
11372
+ const container = createElement(
11373
+ "div",
11374
+ "persona-flex persona-flex-col persona-gap-2"
11375
+ );
11376
+ container.setAttribute("data-message-attachments", "video");
11377
+ let visible = 0;
11378
+ videoParts.forEach((part) => {
11379
+ if (!isSafeMediaSrc(part.video)) return;
11380
+ const videoElement = createElement("video");
11381
+ videoElement.controls = true;
11382
+ videoElement.preload = "metadata";
11383
+ videoElement.src = part.video;
11384
+ videoElement.style.display = "block";
11385
+ videoElement.style.width = "100%";
11386
+ videoElement.style.maxWidth = `${MESSAGE_IMAGE_PREVIEW_MAX_WIDTH_PX}px`;
11387
+ videoElement.style.maxHeight = `${MESSAGE_IMAGE_PREVIEW_MAX_HEIGHT_PX}px`;
11388
+ videoElement.style.borderRadius = "10px";
11389
+ videoElement.style.backgroundColor = "var(--persona-attachment-image-bg, var(--persona-container, #f3f4f6))";
11390
+ container.appendChild(videoElement);
11391
+ visible += 1;
11392
+ });
11393
+ if (visible === 0) {
11394
+ container.remove();
11395
+ return null;
11396
+ }
11397
+ return container;
11398
+ } catch {
11399
+ return null;
11400
+ }
11401
+ };
11402
+ var createMessageFilePreviews = (fileParts) => {
11403
+ if (fileParts.length === 0) return null;
11404
+ try {
11405
+ const container = createElement(
11406
+ "div",
11407
+ "persona-flex persona-flex-col persona-gap-2"
11408
+ );
11409
+ container.setAttribute("data-message-attachments", "files");
11410
+ let visible = 0;
11411
+ fileParts.forEach((part) => {
11412
+ if (!isSafeMediaSrc(part.data)) return;
11413
+ const link = createElement("a");
11414
+ link.href = part.data;
11415
+ link.download = part.filename;
11416
+ link.target = "_blank";
11417
+ link.rel = "noopener noreferrer";
11418
+ link.textContent = part.filename;
11419
+ link.className = "persona-message-file-attachment";
11420
+ link.style.display = "inline-flex";
11421
+ link.style.alignItems = "center";
11422
+ link.style.gap = "6px";
11423
+ link.style.padding = "6px 10px";
11424
+ link.style.borderRadius = "8px";
11425
+ link.style.fontSize = "0.875rem";
11426
+ link.style.textDecoration = "underline";
11427
+ link.style.backgroundColor = "var(--persona-attachment-file-bg, var(--persona-container, #f3f4f6))";
11428
+ link.style.border = "1px solid var(--persona-attachment-file-border, var(--persona-border, #e5e7eb))";
11429
+ link.style.color = "inherit";
11430
+ container.appendChild(link);
11431
+ visible += 1;
11432
+ });
11433
+ if (visible === 0) {
11434
+ container.remove();
11435
+ return null;
11436
+ }
11437
+ return container;
11438
+ } catch {
11439
+ return null;
11440
+ }
11441
+ };
11155
11442
  var createTypingIndicator = () => {
11156
11443
  const container = document.createElement("div");
11157
11444
  container.className = "persona-flex persona-items-center persona-space-x-1 persona-h-5 persona-mt-2";
@@ -11482,6 +11769,27 @@ var createStandardBubble = (message, transform, layoutConfig, actionsConfig, act
11482
11769
  textContentDiv.style.display = "";
11483
11770
  }
11484
11771
  }
11772
+ const audioParts = getMessageAudioParts(message);
11773
+ if (audioParts.length > 0) {
11774
+ const audioPreviews = createMessageAudioPreviews(audioParts);
11775
+ if (audioPreviews) {
11776
+ bubble.appendChild(audioPreviews);
11777
+ }
11778
+ }
11779
+ const videoParts = getMessageVideoParts(message);
11780
+ if (videoParts.length > 0) {
11781
+ const videoPreviews = createMessageVideoPreviews(videoParts);
11782
+ if (videoPreviews) {
11783
+ bubble.appendChild(videoPreviews);
11784
+ }
11785
+ }
11786
+ const fileParts = getMessageFileParts(message);
11787
+ if (fileParts.length > 0) {
11788
+ const filePreviews = createMessageFilePreviews(fileParts);
11789
+ if (filePreviews) {
11790
+ bubble.appendChild(filePreviews);
11791
+ }
11792
+ }
11485
11793
  bubble.appendChild(contentDiv);
11486
11794
  if (showTimestamp && timestampPosition === "below" && message.createdAt) {
11487
11795
  const timestamp = createTimestamp(message, timestampConfig);
@@ -15326,24 +15634,39 @@ function renderComponentDirective(directive, options) {
15326
15634
  return null;
15327
15635
  }
15328
15636
  }
15637
+ function selectDirectiveSource(message) {
15638
+ if (typeof message.rawContent === "string" && message.rawContent.length > 0) {
15639
+ return message.rawContent;
15640
+ }
15641
+ if (typeof message.content === "string") {
15642
+ const trimmed = message.content.trim();
15643
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
15644
+ return message.content;
15645
+ }
15646
+ }
15647
+ return null;
15648
+ }
15329
15649
  function hasComponentDirective(message) {
15330
- if (!message.rawContent) return false;
15650
+ const source = selectDirectiveSource(message);
15651
+ if (!source) return false;
15331
15652
  try {
15332
- const parsed = JSON.parse(message.rawContent);
15653
+ const parsed = JSON.parse(source);
15333
15654
  return typeof parsed === "object" && parsed !== null && "component" in parsed && typeof parsed.component === "string";
15334
15655
  } catch {
15335
15656
  return false;
15336
15657
  }
15337
15658
  }
15338
15659
  function extractComponentDirectiveFromMessage(message) {
15339
- if (!message.rawContent) return null;
15660
+ const source = selectDirectiveSource(message);
15661
+ if (!source) return null;
15340
15662
  try {
15341
- const parsed = JSON.parse(message.rawContent);
15663
+ const parsed = JSON.parse(source);
15342
15664
  if (typeof parsed === "object" && parsed !== null && "component" in parsed && typeof parsed.component === "string") {
15665
+ const directive = parsed;
15343
15666
  return {
15344
- component: parsed.component,
15345
- props: parsed.props && typeof parsed.props === "object" && parsed.props !== null ? parsed.props : {},
15346
- raw: message.rawContent
15667
+ component: directive.component,
15668
+ props: directive.props && typeof directive.props === "object" && directive.props !== null ? directive.props : {},
15669
+ raw: source
15347
15670
  };
15348
15671
  }
15349
15672
  } catch {
@@ -20743,6 +21066,12 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
20743
21066
  }
20744
21067
  return session.injectMessageBatch(optionsList);
20745
21068
  },
21069
+ injectComponentDirective(options) {
21070
+ if (!open && isPanelToggleable()) {
21071
+ setOpenState(true, "system");
21072
+ }
21073
+ return session.injectComponentDirective(options);
21074
+ },
20746
21075
  /** @deprecated Use injectMessage() instead */
20747
21076
  injectTestMessage(event) {
20748
21077
  if (!open && isPanelToggleable()) {
@@ -728,10 +728,28 @@ type FileContentPart = {
728
728
  mimeType: string;
729
729
  filename: string;
730
730
  };
731
+ /**
732
+ * Audio content part for multi-modal messages
733
+ * Supports base64 data URIs or URLs
734
+ */
735
+ type AudioContentPart = {
736
+ type: 'audio';
737
+ audio: string;
738
+ mimeType?: string;
739
+ };
740
+ /**
741
+ * Video content part for multi-modal messages
742
+ * Supports base64 data URIs or URLs
743
+ */
744
+ type VideoContentPart = {
745
+ type: 'video';
746
+ video: string;
747
+ mimeType?: string;
748
+ };
731
749
  /**
732
750
  * Union type for all content part types
733
751
  */
734
- type ContentPart = TextContentPart | ImageContentPart | FileContentPart;
752
+ type ContentPart = TextContentPart | ImageContentPart | FileContentPart | AudioContentPart | VideoContentPart;
735
753
  /**
736
754
  * Message content can be a simple string or an array of content parts
737
755
  */
@@ -4097,6 +4115,22 @@ type InjectMessageOptions = {
4097
4115
  * Consumers can detect this in `messageTransform` to render custom UI.
4098
4116
  */
4099
4117
  voiceProcessing?: boolean;
4118
+ /**
4119
+ * Raw structured payload (typically a JSON string) representing the
4120
+ * full directive that produced this message — e.g. `{ "text": "...",
4121
+ * "component": "Foo", "props": {...} }`.
4122
+ *
4123
+ * Mirrors the field populated by stream parsers during normal LLM
4124
+ * responses. Set this when injecting a message that should render as a
4125
+ * component directive (`hasComponentDirective` /
4126
+ * `extractComponentDirectiveFromMessage` look at `rawContent` first).
4127
+ *
4128
+ * Priority for the API payload remains:
4129
+ * `contentParts > llmContent > rawContent > content`. Pass `llmContent`
4130
+ * alongside `rawContent` if the LLM should see something other than the
4131
+ * raw directive.
4132
+ */
4133
+ rawContent?: string;
4100
4134
  };
4101
4135
  /**
4102
4136
  * Options for injecting assistant messages (most common case).
@@ -4113,6 +4147,57 @@ type InjectUserMessageOptions = Omit<InjectMessageOptions, "role">;
4113
4147
  * Role defaults to 'system'.
4114
4148
  */
4115
4149
  type InjectSystemMessageOptions = Omit<InjectMessageOptions, "role">;
4150
+ /**
4151
+ * Options for injecting an assistant message that renders as a component
4152
+ * directive — sugar over `injectAssistantMessage` for the common case of
4153
+ * "render this registered component, same as if the LLM had emitted it".
4154
+ *
4155
+ * Equivalent to calling `injectAssistantMessage({ content: text, rawContent:
4156
+ * JSON.stringify({ text, component, props }), llmContent })`.
4157
+ *
4158
+ * @example
4159
+ * widget.injectComponentDirective({
4160
+ * component: "DynamicForm",
4161
+ * props: { title: "Book a demo", fields: [...] },
4162
+ * text: "Share your details to book a demo.",
4163
+ * llmContent: "[Showed booking form]"
4164
+ * });
4165
+ */
4166
+ type InjectComponentDirectiveOptions = {
4167
+ /**
4168
+ * Name of a renderer registered via `componentRegistry.register(...)`.
4169
+ */
4170
+ component: string;
4171
+ /**
4172
+ * Props passed to the component renderer.
4173
+ */
4174
+ props?: Record<string, unknown>;
4175
+ /**
4176
+ * Bubble copy displayed above (or with) the rendered component.
4177
+ * Mirrors the `text` field in a streamed JSON directive.
4178
+ * @default ""
4179
+ */
4180
+ text?: string;
4181
+ /**
4182
+ * Content sent to the LLM in API requests. When omitted, the raw
4183
+ * directive JSON is what the LLM would see (per the standard
4184
+ * priority chain). Provide a redacted/short version to avoid sending
4185
+ * the full directive in subsequent turns.
4186
+ */
4187
+ llmContent?: string;
4188
+ /**
4189
+ * Optional message ID. If omitted, an assistant id is auto-generated.
4190
+ */
4191
+ id?: string;
4192
+ /**
4193
+ * Optional creation timestamp (ISO string). If omitted, uses current time.
4194
+ */
4195
+ createdAt?: string;
4196
+ /**
4197
+ * Optional sequence number for ordering.
4198
+ */
4199
+ sequence?: number;
4200
+ };
4116
4201
  type PersonaArtifactRecord = {
4117
4202
  id: string;
4118
4203
  artifactType: PersonaArtifactKind;
@@ -4480,6 +4565,12 @@ type Controller = {
4480
4565
  * Inject multiple messages in a single batch with one sort and one render pass.
4481
4566
  */
4482
4567
  injectMessageBatch: (optionsList: InjectMessageOptions[]) => AgentWidgetMessage[];
4568
+ /**
4569
+ * Convenience method for injecting an assistant message that renders as a
4570
+ * registered component — same shape Persona produces from a streamed
4571
+ * `{ "text": "...", "component": "...", "props": {...} }` payload.
4572
+ */
4573
+ injectComponentDirective: (options: InjectComponentDirectiveOptions) => AgentWidgetMessage;
4483
4574
  /**
4484
4575
  * @deprecated Use injectMessage() instead.
4485
4576
  */
@@ -728,10 +728,28 @@ type FileContentPart = {
728
728
  mimeType: string;
729
729
  filename: string;
730
730
  };
731
+ /**
732
+ * Audio content part for multi-modal messages
733
+ * Supports base64 data URIs or URLs
734
+ */
735
+ type AudioContentPart = {
736
+ type: 'audio';
737
+ audio: string;
738
+ mimeType?: string;
739
+ };
740
+ /**
741
+ * Video content part for multi-modal messages
742
+ * Supports base64 data URIs or URLs
743
+ */
744
+ type VideoContentPart = {
745
+ type: 'video';
746
+ video: string;
747
+ mimeType?: string;
748
+ };
731
749
  /**
732
750
  * Union type for all content part types
733
751
  */
734
- type ContentPart = TextContentPart | ImageContentPart | FileContentPart;
752
+ type ContentPart = TextContentPart | ImageContentPart | FileContentPart | AudioContentPart | VideoContentPart;
735
753
  /**
736
754
  * Message content can be a simple string or an array of content parts
737
755
  */
@@ -4097,6 +4115,22 @@ type InjectMessageOptions = {
4097
4115
  * Consumers can detect this in `messageTransform` to render custom UI.
4098
4116
  */
4099
4117
  voiceProcessing?: boolean;
4118
+ /**
4119
+ * Raw structured payload (typically a JSON string) representing the
4120
+ * full directive that produced this message — e.g. `{ "text": "...",
4121
+ * "component": "Foo", "props": {...} }`.
4122
+ *
4123
+ * Mirrors the field populated by stream parsers during normal LLM
4124
+ * responses. Set this when injecting a message that should render as a
4125
+ * component directive (`hasComponentDirective` /
4126
+ * `extractComponentDirectiveFromMessage` look at `rawContent` first).
4127
+ *
4128
+ * Priority for the API payload remains:
4129
+ * `contentParts > llmContent > rawContent > content`. Pass `llmContent`
4130
+ * alongside `rawContent` if the LLM should see something other than the
4131
+ * raw directive.
4132
+ */
4133
+ rawContent?: string;
4100
4134
  };
4101
4135
  /**
4102
4136
  * Options for injecting assistant messages (most common case).
@@ -4113,6 +4147,57 @@ type InjectUserMessageOptions = Omit<InjectMessageOptions, "role">;
4113
4147
  * Role defaults to 'system'.
4114
4148
  */
4115
4149
  type InjectSystemMessageOptions = Omit<InjectMessageOptions, "role">;
4150
+ /**
4151
+ * Options for injecting an assistant message that renders as a component
4152
+ * directive — sugar over `injectAssistantMessage` for the common case of
4153
+ * "render this registered component, same as if the LLM had emitted it".
4154
+ *
4155
+ * Equivalent to calling `injectAssistantMessage({ content: text, rawContent:
4156
+ * JSON.stringify({ text, component, props }), llmContent })`.
4157
+ *
4158
+ * @example
4159
+ * widget.injectComponentDirective({
4160
+ * component: "DynamicForm",
4161
+ * props: { title: "Book a demo", fields: [...] },
4162
+ * text: "Share your details to book a demo.",
4163
+ * llmContent: "[Showed booking form]"
4164
+ * });
4165
+ */
4166
+ type InjectComponentDirectiveOptions = {
4167
+ /**
4168
+ * Name of a renderer registered via `componentRegistry.register(...)`.
4169
+ */
4170
+ component: string;
4171
+ /**
4172
+ * Props passed to the component renderer.
4173
+ */
4174
+ props?: Record<string, unknown>;
4175
+ /**
4176
+ * Bubble copy displayed above (or with) the rendered component.
4177
+ * Mirrors the `text` field in a streamed JSON directive.
4178
+ * @default ""
4179
+ */
4180
+ text?: string;
4181
+ /**
4182
+ * Content sent to the LLM in API requests. When omitted, the raw
4183
+ * directive JSON is what the LLM would see (per the standard
4184
+ * priority chain). Provide a redacted/short version to avoid sending
4185
+ * the full directive in subsequent turns.
4186
+ */
4187
+ llmContent?: string;
4188
+ /**
4189
+ * Optional message ID. If omitted, an assistant id is auto-generated.
4190
+ */
4191
+ id?: string;
4192
+ /**
4193
+ * Optional creation timestamp (ISO string). If omitted, uses current time.
4194
+ */
4195
+ createdAt?: string;
4196
+ /**
4197
+ * Optional sequence number for ordering.
4198
+ */
4199
+ sequence?: number;
4200
+ };
4116
4201
  type PersonaArtifactRecord = {
4117
4202
  id: string;
4118
4203
  artifactType: PersonaArtifactKind;
@@ -4480,6 +4565,12 @@ type Controller = {
4480
4565
  * Inject multiple messages in a single batch with one sort and one render pass.
4481
4566
  */
4482
4567
  injectMessageBatch: (optionsList: InjectMessageOptions[]) => AgentWidgetMessage[];
4568
+ /**
4569
+ * Convenience method for injecting an assistant message that renders as a
4570
+ * registered component — same shape Persona produces from a streamed
4571
+ * `{ "text": "...", "component": "...", "props": {...} }` payload.
4572
+ */
4573
+ injectComponentDirective: (options: InjectComponentDirectiveOptions) => AgentWidgetMessage;
4483
4574
  /**
4484
4575
  * @deprecated Use injectMessage() instead.
4485
4576
  */