@runtypelabs/persona 1.47.0 → 2.0.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.
- package/README.md +140 -8
- package/dist/index.cjs +90 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1093 -25
- package/dist/index.d.ts +1093 -25
- package/dist/index.global.js +111 -60
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +90 -39
- package/dist/index.js.map +1 -1
- package/dist/install.global.js +1 -1
- package/dist/install.global.js.map +1 -1
- package/dist/widget.css +852 -505
- package/package.json +1 -1
- package/src/artifacts-session.test.ts +80 -0
- package/src/client.test.ts +20 -21
- package/src/client.ts +153 -4
- package/src/components/approval-bubble.ts +45 -42
- package/src/components/artifact-card.ts +91 -0
- package/src/components/artifact-pane.ts +501 -0
- package/src/components/composer-builder.ts +32 -27
- package/src/components/event-stream-view.ts +40 -40
- package/src/components/feedback.ts +36 -36
- package/src/components/forms.ts +11 -11
- package/src/components/header-builder.test.ts +32 -0
- package/src/components/header-builder.ts +55 -36
- package/src/components/header-layouts.ts +58 -125
- package/src/components/launcher.ts +36 -21
- package/src/components/message-bubble.ts +92 -65
- package/src/components/messages.ts +2 -2
- package/src/components/panel.ts +42 -11
- package/src/components/reasoning-bubble.ts +23 -23
- package/src/components/registry.ts +4 -0
- package/src/components/suggestions.ts +1 -1
- package/src/components/tool-bubble.ts +32 -32
- package/src/defaults.ts +30 -4
- package/src/index.ts +80 -2
- package/src/install.ts +22 -0
- package/src/plugins/types.ts +23 -0
- package/src/postprocessors.ts +2 -2
- package/src/runtime/host-layout.ts +174 -0
- package/src/runtime/init.test.ts +236 -0
- package/src/runtime/init.ts +114 -55
- package/src/session.ts +173 -7
- package/src/styles/tailwind.css +1 -1
- package/src/styles/widget.css +852 -505
- package/src/types/theme.ts +354 -0
- package/src/types.ts +348 -16
- package/src/ui.docked.test.ts +104 -0
- package/src/ui.ts +1093 -244
- package/src/utils/artifact-gate.test.ts +255 -0
- package/src/utils/artifact-gate.ts +142 -0
- package/src/utils/artifact-resize.test.ts +64 -0
- package/src/utils/artifact-resize.ts +67 -0
- package/src/utils/attachment-manager.ts +10 -10
- package/src/utils/code-generators.test.ts +52 -0
- package/src/utils/code-generators.ts +40 -36
- package/src/utils/dock.ts +17 -0
- package/src/utils/dom-context.test.ts +504 -0
- package/src/utils/dom-context.ts +896 -0
- package/src/utils/dom.ts +12 -1
- package/src/utils/message-fingerprint.test.ts +187 -0
- package/src/utils/message-fingerprint.ts +105 -0
- package/src/utils/migration.ts +179 -0
- package/src/utils/morph.ts +1 -1
- package/src/utils/plugins.ts +175 -0
- package/src/utils/positioning.ts +4 -4
- package/src/utils/theme.test.ts +125 -0
- package/src/utils/theme.ts +216 -60
- package/src/utils/tokens.ts +682 -0
- package/src/voice/audio-playback-manager.ts +187 -0
- package/src/voice/runtype-voice-provider.ts +305 -69
- package/src/voice/voice-activity-detector.ts +90 -0
- package/src/voice/voice.test.ts +6 -5
package/src/types.ts
CHANGED
|
@@ -71,6 +71,8 @@ export type AgentWidgetRequestPayload = {
|
|
|
71
71
|
flowId?: string;
|
|
72
72
|
context?: Record<string, unknown>;
|
|
73
73
|
metadata?: Record<string, unknown>;
|
|
74
|
+
/** Per-turn template variables for /v1/client/chat (merged as root-level {{var}} in Runtype). */
|
|
75
|
+
inputs?: Record<string, unknown>;
|
|
74
76
|
};
|
|
75
77
|
|
|
76
78
|
// ============================================================================
|
|
@@ -81,30 +83,65 @@ export type AgentWidgetRequestPayload = {
|
|
|
81
83
|
* Configuration for agent loop behavior.
|
|
82
84
|
*/
|
|
83
85
|
export type AgentLoopConfig = {
|
|
84
|
-
/** Maximum number of
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
|
|
86
|
+
/** Maximum number of agent turns (1-100). The loop continues while the model calls tools. */
|
|
87
|
+
maxTurns: number;
|
|
88
|
+
/** Maximum cost budget in USD. Agent stops when exceeded. */
|
|
89
|
+
maxCost?: number;
|
|
88
90
|
/** Enable periodic reflection during execution */
|
|
89
91
|
enableReflection?: boolean;
|
|
90
|
-
/** Number of iterations between reflections */
|
|
92
|
+
/** Number of iterations between reflections (1-50) */
|
|
91
93
|
reflectionInterval?: number;
|
|
92
94
|
};
|
|
93
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Configuration for agent tools (search, code execution, MCP servers, etc.)
|
|
98
|
+
*/
|
|
99
|
+
export type AgentToolsConfig = {
|
|
100
|
+
/** Tool IDs to enable (e.g., "builtin:exa", "builtin:dalle", "builtin:openai_web_search") */
|
|
101
|
+
toolIds?: string[];
|
|
102
|
+
/** Per-tool configuration overrides keyed by tool ID */
|
|
103
|
+
toolConfigs?: Record<string, Record<string, unknown>>;
|
|
104
|
+
/** Inline tool definitions for runtime-defined tools */
|
|
105
|
+
runtimeTools?: Array<Record<string, unknown>>;
|
|
106
|
+
/** Custom MCP server connections */
|
|
107
|
+
mcpServers?: Array<Record<string, unknown>>;
|
|
108
|
+
/** Maximum number of tool invocations per execution */
|
|
109
|
+
maxToolCalls?: number;
|
|
110
|
+
/** Tool approval configuration for human-in-the-loop workflows */
|
|
111
|
+
approval?: {
|
|
112
|
+
/** Tool names/patterns to require approval for, or true for all tools */
|
|
113
|
+
require: string[] | boolean;
|
|
114
|
+
/** Approval timeout in milliseconds (default: 300000 / 5 minutes) */
|
|
115
|
+
timeout?: number;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/** Artifact kinds for the Persona sidebar and dispatch payload */
|
|
120
|
+
export type PersonaArtifactKind = "markdown" | "component";
|
|
121
|
+
|
|
94
122
|
/**
|
|
95
123
|
* Agent configuration for agent execution mode.
|
|
96
124
|
* When provided in the widget config, enables agent loop execution instead of flow dispatch.
|
|
97
125
|
*/
|
|
126
|
+
export type ArtifactConfigPayload = {
|
|
127
|
+
enabled: true;
|
|
128
|
+
types: PersonaArtifactKind[];
|
|
129
|
+
};
|
|
130
|
+
|
|
98
131
|
export type AgentConfig = {
|
|
99
132
|
/** Agent display name */
|
|
100
133
|
name: string;
|
|
101
|
-
/** Model identifier (e.g., 'openai:gpt-4o-mini') */
|
|
134
|
+
/** Model identifier (e.g., 'openai:gpt-4o-mini', 'qwen/qwen3-8b') */
|
|
102
135
|
model: string;
|
|
103
136
|
/** System prompt for the agent */
|
|
104
137
|
systemPrompt: string;
|
|
105
138
|
/** Temperature for model responses */
|
|
106
139
|
temperature?: number;
|
|
107
|
-
/**
|
|
140
|
+
/** Tool configuration for the agent */
|
|
141
|
+
tools?: AgentToolsConfig;
|
|
142
|
+
/** Persona artifacts — sibling of tools (virtual agent / API parity) */
|
|
143
|
+
artifacts?: ArtifactConfigPayload;
|
|
144
|
+
/** Loop configuration for multi-turn execution */
|
|
108
145
|
loopConfig?: AgentLoopConfig;
|
|
109
146
|
};
|
|
110
147
|
|
|
@@ -142,10 +179,10 @@ export type AgentExecutionState = {
|
|
|
142
179
|
agentName: string;
|
|
143
180
|
status: 'running' | 'complete' | 'error';
|
|
144
181
|
currentIteration: number;
|
|
145
|
-
|
|
182
|
+
maxTurns: number;
|
|
146
183
|
startedAt?: number;
|
|
147
184
|
completedAt?: number;
|
|
148
|
-
stopReason?: '
|
|
185
|
+
stopReason?: 'complete' | 'end_turn' | 'max_turns' | 'max_cost' | 'timeout' | 'error';
|
|
149
186
|
};
|
|
150
187
|
|
|
151
188
|
/**
|
|
@@ -361,12 +398,143 @@ export type AgentWidgetControllerEventMap = {
|
|
|
361
398
|
"approval:resolved": { approval: AgentWidgetApproval; decision: string };
|
|
362
399
|
};
|
|
363
400
|
|
|
401
|
+
/**
|
|
402
|
+
* Layout for the artifact split / drawer (CSS lengths unless noted).
|
|
403
|
+
*
|
|
404
|
+
* **Close behavior:** In desktop split mode, the artifact chrome `Close` control uses the same
|
|
405
|
+
* dismiss path as the mobile drawer (`onDismiss` on the artifact pane): the pane is hidden until
|
|
406
|
+
* new artifact content arrives or the host calls `showArtifacts()` on the widget handle.
|
|
407
|
+
*/
|
|
408
|
+
export type AgentWidgetArtifactsLayoutConfig = {
|
|
409
|
+
/** Flex gap between chat column and artifact pane. @default 0.5rem */
|
|
410
|
+
splitGap?: string;
|
|
411
|
+
/** Artifact column width in split mode. @default 40% */
|
|
412
|
+
paneWidth?: string;
|
|
413
|
+
/** Max width of artifact column. @default 28rem */
|
|
414
|
+
paneMaxWidth?: string;
|
|
415
|
+
/** Min width of artifact column (optional). */
|
|
416
|
+
paneMinWidth?: string;
|
|
417
|
+
/**
|
|
418
|
+
* When the floating panel is at most this wide (px), use in-panel drawer for artifacts
|
|
419
|
+
* instead of a side-by-side split (viewport can still be wide).
|
|
420
|
+
* @default 520
|
|
421
|
+
*/
|
|
422
|
+
narrowHostMaxWidth?: number;
|
|
423
|
+
/**
|
|
424
|
+
* When true (default), widen the launcher panel while artifacts are visible and not user-dismissed.
|
|
425
|
+
* No-op for inline embed (`launcher.enabled === false`).
|
|
426
|
+
*/
|
|
427
|
+
expandLauncherPanelWhenOpen?: boolean;
|
|
428
|
+
/** Panel width when expanded (launcher + artifacts visible). @default min(720px, calc(100vw - 24px)) */
|
|
429
|
+
expandedPanelWidth?: string;
|
|
430
|
+
/**
|
|
431
|
+
* When true, shows a drag handle between chat and artifact columns in desktop split mode only
|
|
432
|
+
* (hidden in narrow-host drawer and viewport ≤640px). Width is not persisted across reloads.
|
|
433
|
+
*/
|
|
434
|
+
resizable?: boolean;
|
|
435
|
+
/** Min artifact column width while resizing. Only `px` strings are supported. @default 200px */
|
|
436
|
+
resizableMinWidth?: string;
|
|
437
|
+
/** Optional max artifact width cap while resizing (`px` only). Layout still bounds by chat min width. */
|
|
438
|
+
resizableMaxWidth?: string;
|
|
439
|
+
/**
|
|
440
|
+
* Visual treatment for the artifact column in split mode.
|
|
441
|
+
* - `'panel'` — bordered sidebar with left border, gap, and shadow (default).
|
|
442
|
+
* - `'seamless'` — flush with chat: no border or shadow, container background, zero gap.
|
|
443
|
+
* @default 'panel'
|
|
444
|
+
*/
|
|
445
|
+
paneAppearance?: "panel" | "seamless";
|
|
446
|
+
/** Border radius on the artifact pane (CSS length). Works with any `paneAppearance`. */
|
|
447
|
+
paneBorderRadius?: string;
|
|
448
|
+
/** CSS `box-shadow` on the artifact pane. Set `"none"` to suppress the default shadow. */
|
|
449
|
+
paneShadow?: string;
|
|
450
|
+
/**
|
|
451
|
+
* Full `border` shorthand for the artifact `<aside>` (all sides). Overrides default pane borders.
|
|
452
|
+
* Example: `"1px solid #cccccc"`.
|
|
453
|
+
*/
|
|
454
|
+
paneBorder?: string;
|
|
455
|
+
/**
|
|
456
|
+
* `border-left` shorthand only — typical for split view next to chat (with or without resizer).
|
|
457
|
+
* Ignored if `paneBorder` is set. Example: `"1px solid #cccccc"`.
|
|
458
|
+
*/
|
|
459
|
+
paneBorderLeft?: string;
|
|
460
|
+
/**
|
|
461
|
+
* Desktop split only (not narrow-host drawer / not ≤640px): square the **main chat card’s**
|
|
462
|
+
* top-right and bottom-right radii, and round the **artifact pane’s** top-right and bottom-right
|
|
463
|
+
* to match `persona-rounded-2xl` (`--persona-radius-lg`) so the two columns read as one shell.
|
|
464
|
+
*/
|
|
465
|
+
unifiedSplitChrome?: boolean;
|
|
466
|
+
/**
|
|
467
|
+
* When `unifiedSplitChrome` is true, outer-right corner radius on the artifact column (CSS length).
|
|
468
|
+
* @default matches theme large radius (`--persona-radius-lg`)
|
|
469
|
+
*/
|
|
470
|
+
unifiedSplitOuterRadius?: string;
|
|
471
|
+
/**
|
|
472
|
+
* Background color for the artifact column (CSS color). Sets `--persona-artifact-pane-bg` on the widget root.
|
|
473
|
+
*/
|
|
474
|
+
paneBackground?: string;
|
|
475
|
+
/**
|
|
476
|
+
* Horizontal padding for artifact toolbar and content (CSS length), e.g. `24px`.
|
|
477
|
+
*/
|
|
478
|
+
panePadding?: string;
|
|
479
|
+
/**
|
|
480
|
+
* Toolbar layout preset.
|
|
481
|
+
* - `default` — "Artifacts" title, horizontal tabs, text close.
|
|
482
|
+
* - `document` — view/source toggle, document title, copy / refresh / close; tab strip hidden when only one artifact.
|
|
483
|
+
* @default 'default'
|
|
484
|
+
*/
|
|
485
|
+
toolbarPreset?: "default" | "document";
|
|
486
|
+
/**
|
|
487
|
+
* When `toolbarPreset` is `document`, show a visible "Copy" label next to the copy icon.
|
|
488
|
+
*/
|
|
489
|
+
documentToolbarShowCopyLabel?: boolean;
|
|
490
|
+
/**
|
|
491
|
+
* When `toolbarPreset` is `document`, show a small chevron after the copy control (e.g. menu affordance).
|
|
492
|
+
*/
|
|
493
|
+
documentToolbarShowCopyChevron?: boolean;
|
|
494
|
+
/** Document toolbar icon buttons (view, code, copy, refresh, close) — CSS color. Sets `--persona-artifact-doc-toolbar-icon-color` on the widget root. */
|
|
495
|
+
documentToolbarIconColor?: string;
|
|
496
|
+
/** Active view/source toggle background. Sets `--persona-artifact-doc-toggle-active-bg`. */
|
|
497
|
+
documentToolbarToggleActiveBackground?: string;
|
|
498
|
+
/** Active view/source toggle border color. Sets `--persona-artifact-doc-toggle-active-border`. */
|
|
499
|
+
documentToolbarToggleActiveBorderColor?: string;
|
|
500
|
+
/**
|
|
501
|
+
* Invoked when the document toolbar Refresh control is used (before the pane re-renders).
|
|
502
|
+
* Use to replay `connectStream`, refetch, etc.
|
|
503
|
+
*/
|
|
504
|
+
onDocumentToolbarRefresh?: () => void | Promise<void>;
|
|
505
|
+
/**
|
|
506
|
+
* Optional copy dropdown entries (shown when `documentToolbarShowCopyChevron` is true and this array is non-empty).
|
|
507
|
+
* The main Copy control still performs default copy unless `onDocumentToolbarCopyMenuSelect` handles everything.
|
|
508
|
+
*/
|
|
509
|
+
documentToolbarCopyMenuItems?: Array<{ id: string; label: string }>;
|
|
510
|
+
/**
|
|
511
|
+
* When set, invoked for the chevron menu (and can override default copy per `actionId`).
|
|
512
|
+
*/
|
|
513
|
+
onDocumentToolbarCopyMenuSelect?: (payload: {
|
|
514
|
+
actionId: string;
|
|
515
|
+
artifactId: string | null;
|
|
516
|
+
markdown: string;
|
|
517
|
+
jsonPayload: string;
|
|
518
|
+
}) => void | Promise<void>;
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
export type AgentWidgetArtifactsFeature = {
|
|
522
|
+
/** When true, Persona shows the artifact pane and handles artifact_* SSE events */
|
|
523
|
+
enabled?: boolean;
|
|
524
|
+
/** If set, artifact events for other types are ignored */
|
|
525
|
+
allowedTypes?: PersonaArtifactKind[];
|
|
526
|
+
/** Split / drawer dimensions and launcher widen behavior */
|
|
527
|
+
layout?: AgentWidgetArtifactsLayoutConfig;
|
|
528
|
+
};
|
|
529
|
+
|
|
364
530
|
export type AgentWidgetFeatureFlags = {
|
|
365
531
|
showReasoning?: boolean;
|
|
366
532
|
showToolCalls?: boolean;
|
|
367
533
|
showEventStreamToggle?: boolean;
|
|
368
534
|
/** Configuration for the Event Stream inspector view */
|
|
369
535
|
eventStream?: EventStreamConfig;
|
|
536
|
+
/** Optional artifact sidebar (split pane / mobile drawer) */
|
|
537
|
+
artifacts?: AgentWidgetArtifactsFeature;
|
|
370
538
|
};
|
|
371
539
|
|
|
372
540
|
export type SSEEventRecord = {
|
|
@@ -531,7 +699,7 @@ export type AgentWidgetTheme = {
|
|
|
531
699
|
/**
|
|
532
700
|
* Border style for the chat panel container.
|
|
533
701
|
* @example "1px solid #e5e7eb" | "none"
|
|
534
|
-
* @default "1px solid var(--
|
|
702
|
+
* @default "1px solid var(--persona-border)"
|
|
535
703
|
*/
|
|
536
704
|
panelBorder?: string;
|
|
537
705
|
/**
|
|
@@ -548,6 +716,24 @@ export type AgentWidgetTheme = {
|
|
|
548
716
|
panelBorderRadius?: string;
|
|
549
717
|
};
|
|
550
718
|
|
|
719
|
+
export type AgentWidgetDockConfig = {
|
|
720
|
+
/**
|
|
721
|
+
* Side of the wrapped container where the docked panel should render.
|
|
722
|
+
* @default "right"
|
|
723
|
+
*/
|
|
724
|
+
side?: "left" | "right";
|
|
725
|
+
/**
|
|
726
|
+
* Expanded width of the docked panel.
|
|
727
|
+
* @default "420px"
|
|
728
|
+
*/
|
|
729
|
+
width?: string;
|
|
730
|
+
/**
|
|
731
|
+
* Width of the collapsed launcher rail when the docked panel is closed.
|
|
732
|
+
* @default "72px"
|
|
733
|
+
*/
|
|
734
|
+
collapsedWidth?: string;
|
|
735
|
+
};
|
|
736
|
+
|
|
551
737
|
export type AgentWidgetLauncherConfig = {
|
|
552
738
|
enabled?: boolean;
|
|
553
739
|
title?: string;
|
|
@@ -558,6 +744,18 @@ export type AgentWidgetLauncherConfig = {
|
|
|
558
744
|
agentIconName?: string;
|
|
559
745
|
agentIconHidden?: boolean;
|
|
560
746
|
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
747
|
+
/**
|
|
748
|
+
* Controls how the launcher panel is mounted relative to the host page.
|
|
749
|
+
* - "floating": default floating launcher / panel behavior
|
|
750
|
+
* - "docked": wraps the target container and renders as a sibling dock
|
|
751
|
+
*
|
|
752
|
+
* @default "floating"
|
|
753
|
+
*/
|
|
754
|
+
mountMode?: "floating" | "docked";
|
|
755
|
+
/**
|
|
756
|
+
* Layout configuration for docked mode.
|
|
757
|
+
*/
|
|
758
|
+
dock?: AgentWidgetDockConfig;
|
|
561
759
|
autoExpand?: boolean;
|
|
562
760
|
width?: string;
|
|
563
761
|
/**
|
|
@@ -597,6 +795,21 @@ export type AgentWidgetLauncherConfig = {
|
|
|
597
795
|
* @default 0
|
|
598
796
|
*/
|
|
599
797
|
heightOffset?: number;
|
|
798
|
+
/**
|
|
799
|
+
* When true, the widget panel expands to fill the full viewport on mobile devices.
|
|
800
|
+
* Removes border-radius, margins, and shadows for a native app-like experience.
|
|
801
|
+
* Applies when viewport width is at or below `mobileBreakpoint`.
|
|
802
|
+
*
|
|
803
|
+
* @default true
|
|
804
|
+
*/
|
|
805
|
+
mobileFullscreen?: boolean;
|
|
806
|
+
/**
|
|
807
|
+
* Viewport width (in pixels) at or below which the widget enters mobile fullscreen mode.
|
|
808
|
+
* Only applies when `mobileFullscreen` is true.
|
|
809
|
+
*
|
|
810
|
+
* @default 640
|
|
811
|
+
*/
|
|
812
|
+
mobileBreakpoint?: number;
|
|
600
813
|
callToActionIconText?: string;
|
|
601
814
|
callToActionIconName?: string;
|
|
602
815
|
callToActionIconColor?: string;
|
|
@@ -651,6 +864,13 @@ export type AgentWidgetSendButtonConfig = {
|
|
|
651
864
|
size?: string;
|
|
652
865
|
};
|
|
653
866
|
|
|
867
|
+
/** Optional composer UI state for custom `renderComposer` implementations. */
|
|
868
|
+
export type AgentWidgetComposerConfig = {
|
|
869
|
+
models?: Array<{ id: string; label: string }>;
|
|
870
|
+
/** Current selection; host or plugin may update this at runtime. */
|
|
871
|
+
selectedModelId?: string;
|
|
872
|
+
};
|
|
873
|
+
|
|
654
874
|
export type AgentWidgetClearChatConfig = {
|
|
655
875
|
enabled?: boolean;
|
|
656
876
|
placement?: "inline" | "top-right";
|
|
@@ -696,6 +916,26 @@ export type AgentWidgetVoiceRecognitionConfig = {
|
|
|
696
916
|
recordingBackgroundColor?: string;
|
|
697
917
|
recordingBorderColor?: string;
|
|
698
918
|
showRecordingIndicator?: boolean;
|
|
919
|
+
|
|
920
|
+
// Processing state (after recording stops, waiting for agent response)
|
|
921
|
+
/** Icon name shown while processing voice input. Default: "loader" */
|
|
922
|
+
processingIconName?: string;
|
|
923
|
+
/** Icon color during processing. Inherits idle iconColor if not set */
|
|
924
|
+
processingIconColor?: string;
|
|
925
|
+
/** Button background color during processing. Inherits idle backgroundColor if not set */
|
|
926
|
+
processingBackgroundColor?: string;
|
|
927
|
+
/** Button border color during processing. Inherits idle borderColor if not set */
|
|
928
|
+
processingBorderColor?: string;
|
|
929
|
+
|
|
930
|
+
// Speaking state (agent TTS audio is playing)
|
|
931
|
+
/** Icon name shown while agent is speaking. Default: "volume-2" (or "square" in cancel mode) */
|
|
932
|
+
speakingIconName?: string;
|
|
933
|
+
/** Icon color while speaking. Inherits idle iconColor if not set */
|
|
934
|
+
speakingIconColor?: string;
|
|
935
|
+
/** Button background color while speaking. Inherits idle backgroundColor if not set */
|
|
936
|
+
speakingBackgroundColor?: string;
|
|
937
|
+
/** Button border color while speaking. Inherits idle borderColor if not set */
|
|
938
|
+
speakingBorderColor?: string;
|
|
699
939
|
autoResume?: boolean | "assistant";
|
|
700
940
|
|
|
701
941
|
// Voice provider configuration
|
|
@@ -794,11 +1034,12 @@ export type VoiceResult = {
|
|
|
794
1034
|
/**
|
|
795
1035
|
* Voice provider status states
|
|
796
1036
|
*/
|
|
797
|
-
export type VoiceStatus =
|
|
1037
|
+
export type VoiceStatus =
|
|
798
1038
|
| 'disconnected'
|
|
799
1039
|
| 'connected'
|
|
800
1040
|
| 'listening'
|
|
801
1041
|
| 'processing'
|
|
1042
|
+
| 'speaking'
|
|
802
1043
|
| 'error'
|
|
803
1044
|
| 'idle';
|
|
804
1045
|
|
|
@@ -843,6 +1084,18 @@ export interface VoiceProvider {
|
|
|
843
1084
|
|
|
844
1085
|
/** Register a callback fired when recording stops and audio is about to be sent */
|
|
845
1086
|
onProcessingStart?(callback: () => void): void;
|
|
1087
|
+
|
|
1088
|
+
/** Returns the current interruption mode (only meaningful for Runtype provider) */
|
|
1089
|
+
getInterruptionMode?(): "none" | "cancel" | "barge-in";
|
|
1090
|
+
|
|
1091
|
+
/** Returns true if the barge-in mic stream is alive (hot mic between turns) */
|
|
1092
|
+
isBargeInActive?(): boolean;
|
|
1093
|
+
|
|
1094
|
+
/** Tear down the barge-in mic pipeline — "hang up" the always-on mic */
|
|
1095
|
+
deactivateBargeIn?(): Promise<void>;
|
|
1096
|
+
|
|
1097
|
+
/** Stop playback / cancel in-flight request without starting recording */
|
|
1098
|
+
stopPlayback?(): void;
|
|
846
1099
|
}
|
|
847
1100
|
|
|
848
1101
|
/**
|
|
@@ -1076,6 +1329,8 @@ export type ClientChatRequest = {
|
|
|
1076
1329
|
/** ID for the expected assistant response message */
|
|
1077
1330
|
assistantMessageId?: string;
|
|
1078
1331
|
metadata?: Record<string, unknown>;
|
|
1332
|
+
/** Per-turn inputs for Runtype prompt templates (e.g. {{page_url}}). */
|
|
1333
|
+
inputs?: Record<string, unknown>;
|
|
1079
1334
|
context?: Record<string, unknown>;
|
|
1080
1335
|
};
|
|
1081
1336
|
|
|
@@ -1102,6 +1357,15 @@ export type ClientFeedbackRequest = {
|
|
|
1102
1357
|
// Layout Configuration Types
|
|
1103
1358
|
// ============================================================================
|
|
1104
1359
|
|
|
1360
|
+
/** Icon button in the header title row (minimal layout). */
|
|
1361
|
+
export type AgentWidgetHeaderTrailingAction = {
|
|
1362
|
+
id: string;
|
|
1363
|
+
/** Lucide icon name, e.g. `chevron-down` */
|
|
1364
|
+
icon?: string;
|
|
1365
|
+
label?: string;
|
|
1366
|
+
ariaLabel?: string;
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1105
1369
|
/**
|
|
1106
1370
|
* Context provided to header render functions
|
|
1107
1371
|
*/
|
|
@@ -1109,6 +1373,10 @@ export type HeaderRenderContext = {
|
|
|
1109
1373
|
config: AgentWidgetConfig;
|
|
1110
1374
|
onClose?: () => void;
|
|
1111
1375
|
onClearChat?: () => void;
|
|
1376
|
+
/** Built from `layout.header.trailingActions` for custom `render` implementations. */
|
|
1377
|
+
trailingActions?: AgentWidgetHeaderTrailingAction[];
|
|
1378
|
+
/** Fired when a built-in trailing action is activated (same as `layout.header.onAction`). */
|
|
1379
|
+
onAction?: (actionId: string) => void;
|
|
1112
1380
|
};
|
|
1113
1381
|
|
|
1114
1382
|
/**
|
|
@@ -1134,12 +1402,11 @@ export type SlotRenderContext = {
|
|
|
1134
1402
|
*/
|
|
1135
1403
|
export type AgentWidgetHeaderLayoutConfig = {
|
|
1136
1404
|
/**
|
|
1137
|
-
* Layout preset: "default" | "minimal"
|
|
1405
|
+
* Layout preset: "default" | "minimal"
|
|
1138
1406
|
* - default: Standard layout with icon, title, subtitle, and buttons
|
|
1139
1407
|
* - minimal: Simplified layout with just title and close button
|
|
1140
|
-
* - expanded: Full branding area with additional content space
|
|
1141
1408
|
*/
|
|
1142
|
-
layout?: "default" | "minimal"
|
|
1409
|
+
layout?: "default" | "minimal";
|
|
1143
1410
|
/** Show/hide the header icon */
|
|
1144
1411
|
showIcon?: boolean;
|
|
1145
1412
|
/** Show/hide the title */
|
|
@@ -1155,6 +1422,12 @@ export type AgentWidgetHeaderLayoutConfig = {
|
|
|
1155
1422
|
* When provided, replaces the entire header with custom content
|
|
1156
1423
|
*/
|
|
1157
1424
|
render?: (context: HeaderRenderContext) => HTMLElement;
|
|
1425
|
+
/**
|
|
1426
|
+
* Shown after the title in `minimal` header layout (e.g. chevron menu affordance).
|
|
1427
|
+
*/
|
|
1428
|
+
trailingActions?: AgentWidgetHeaderTrailingAction[];
|
|
1429
|
+
/** Called when a `trailingActions` button is clicked. */
|
|
1430
|
+
onAction?: (actionId: string) => void;
|
|
1158
1431
|
};
|
|
1159
1432
|
|
|
1160
1433
|
/**
|
|
@@ -1277,6 +1550,12 @@ export type AgentWidgetLayoutConfig = {
|
|
|
1277
1550
|
* @default true
|
|
1278
1551
|
*/
|
|
1279
1552
|
showFooter?: boolean;
|
|
1553
|
+
/**
|
|
1554
|
+
* Max width for the content area (messages + composer).
|
|
1555
|
+
* Applied with `margin: 0 auto` for centering.
|
|
1556
|
+
* Accepts any CSS width value (e.g. "90ch", "720px", "80%").
|
|
1557
|
+
*/
|
|
1558
|
+
contentMaxWidth?: string;
|
|
1280
1559
|
};
|
|
1281
1560
|
|
|
1282
1561
|
// ============================================================================
|
|
@@ -1817,7 +2096,7 @@ export type AgentWidgetConfig = {
|
|
|
1817
2096
|
* name: 'Assistant',
|
|
1818
2097
|
* model: 'openai:gpt-4o-mini',
|
|
1819
2098
|
* systemPrompt: 'You are a helpful assistant.',
|
|
1820
|
-
|
|
2099
|
+
* loopConfig: { maxTurns: 5 }
|
|
1821
2100
|
* }
|
|
1822
2101
|
* }
|
|
1823
2102
|
* ```
|
|
@@ -1933,6 +2212,11 @@ export type AgentWidgetConfig = {
|
|
|
1933
2212
|
welcomeSubtitle?: string;
|
|
1934
2213
|
inputPlaceholder?: string;
|
|
1935
2214
|
sendButtonLabel?: string;
|
|
2215
|
+
/**
|
|
2216
|
+
* When false, the welcome / intro card is not shown above the message list.
|
|
2217
|
+
* @default true
|
|
2218
|
+
*/
|
|
2219
|
+
showWelcomeCard?: boolean;
|
|
1936
2220
|
};
|
|
1937
2221
|
theme?: AgentWidgetTheme;
|
|
1938
2222
|
/**
|
|
@@ -2084,6 +2368,12 @@ export type AgentWidgetConfig = {
|
|
|
2084
2368
|
* @default true
|
|
2085
2369
|
*/
|
|
2086
2370
|
enableComponentStreaming?: boolean;
|
|
2371
|
+
/**
|
|
2372
|
+
* When false, JSON component directives render without the default bubble chrome
|
|
2373
|
+
* (surface background, border, extra padding). Use for wide custom cards in the transcript.
|
|
2374
|
+
* @default true
|
|
2375
|
+
*/
|
|
2376
|
+
wrapComponentDirectiveInBubble?: boolean;
|
|
2087
2377
|
/**
|
|
2088
2378
|
* Custom stream parser for extracting text from streaming structured responses.
|
|
2089
2379
|
* Handles incremental parsing of JSON, XML, or other formats.
|
|
@@ -2286,6 +2576,12 @@ export type AgentWidgetConfig = {
|
|
|
2286
2576
|
*/
|
|
2287
2577
|
attachments?: AgentWidgetAttachmentsConfig;
|
|
2288
2578
|
|
|
2579
|
+
/**
|
|
2580
|
+
* Composer extras for custom `renderComposer` plugins (model picker, etc.).
|
|
2581
|
+
* `selectedModelId` may be updated at runtime by the host.
|
|
2582
|
+
*/
|
|
2583
|
+
composer?: AgentWidgetComposerConfig;
|
|
2584
|
+
|
|
2289
2585
|
/**
|
|
2290
2586
|
* Persist widget state (open/closed, voice mode) across page navigations.
|
|
2291
2587
|
* When `true`, uses default settings with sessionStorage.
|
|
@@ -2563,10 +2859,46 @@ export type InjectUserMessageOptions = Omit<InjectMessageOptions, "role">;
|
|
|
2563
2859
|
*/
|
|
2564
2860
|
export type InjectSystemMessageOptions = Omit<InjectMessageOptions, "role">;
|
|
2565
2861
|
|
|
2862
|
+
export type PersonaArtifactRecord = {
|
|
2863
|
+
id: string;
|
|
2864
|
+
artifactType: PersonaArtifactKind;
|
|
2865
|
+
title?: string;
|
|
2866
|
+
status: "streaming" | "complete";
|
|
2867
|
+
markdown?: string;
|
|
2868
|
+
component?: string;
|
|
2869
|
+
props?: Record<string, unknown>;
|
|
2870
|
+
};
|
|
2871
|
+
|
|
2872
|
+
/** Programmatic artifact upsert (controller / window API) */
|
|
2873
|
+
export type PersonaArtifactManualUpsert =
|
|
2874
|
+
| { id?: string; artifactType: "markdown"; title?: string; content: string }
|
|
2875
|
+
| {
|
|
2876
|
+
id?: string;
|
|
2877
|
+
artifactType: "component";
|
|
2878
|
+
title?: string;
|
|
2879
|
+
component: string;
|
|
2880
|
+
props?: Record<string, unknown>;
|
|
2881
|
+
};
|
|
2882
|
+
|
|
2566
2883
|
export type AgentWidgetEvent =
|
|
2567
2884
|
| { type: "message"; message: AgentWidgetMessage }
|
|
2568
2885
|
| { type: "status"; status: "connecting" | "connected" | "error" | "idle" }
|
|
2569
|
-
| { type: "error"; error: Error }
|
|
2886
|
+
| { type: "error"; error: Error }
|
|
2887
|
+
| {
|
|
2888
|
+
type: "artifact_start";
|
|
2889
|
+
id: string;
|
|
2890
|
+
artifactType: PersonaArtifactKind;
|
|
2891
|
+
title?: string;
|
|
2892
|
+
component?: string;
|
|
2893
|
+
}
|
|
2894
|
+
| { type: "artifact_delta"; id: string; artDelta: string }
|
|
2895
|
+
| {
|
|
2896
|
+
type: "artifact_update";
|
|
2897
|
+
id: string;
|
|
2898
|
+
props: Record<string, unknown>;
|
|
2899
|
+
component?: string;
|
|
2900
|
+
}
|
|
2901
|
+
| { type: "artifact_complete"; id: string };
|
|
2570
2902
|
|
|
2571
2903
|
export type AgentWidgetInitOptions = {
|
|
2572
2904
|
target: string | HTMLElement;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
import { createWidgetHostLayout } from "./runtime/host-layout";
|
|
6
|
+
import { createAgentExperience } from "./ui";
|
|
7
|
+
|
|
8
|
+
describe("createAgentExperience docked mode", () => {
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
document.body.innerHTML = "";
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("collapses the docked panel wrapper and keeps the rail trigger visible when closed", () => {
|
|
14
|
+
const mount = document.createElement("div");
|
|
15
|
+
document.body.appendChild(mount);
|
|
16
|
+
|
|
17
|
+
const controller = createAgentExperience(mount, {
|
|
18
|
+
apiUrl: "https://api.example.com/chat",
|
|
19
|
+
launcher: {
|
|
20
|
+
mountMode: "docked",
|
|
21
|
+
autoExpand: true,
|
|
22
|
+
dock: {
|
|
23
|
+
side: "right",
|
|
24
|
+
width: "420px",
|
|
25
|
+
collapsedWidth: "72px",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const wrapper = mount.firstElementChild as HTMLElement | null;
|
|
31
|
+
const launcherButton = mount.lastElementChild as HTMLButtonElement | null;
|
|
32
|
+
|
|
33
|
+
expect(wrapper).not.toBeNull();
|
|
34
|
+
expect(wrapper?.style.display).toBe("flex");
|
|
35
|
+
expect(launcherButton).not.toBeNull();
|
|
36
|
+
expect(launcherButton?.tagName).toBe("BUTTON");
|
|
37
|
+
expect(launcherButton?.style.display).toBe("none");
|
|
38
|
+
expect(launcherButton?.className).toContain("persona-mt-4");
|
|
39
|
+
|
|
40
|
+
controller.close();
|
|
41
|
+
|
|
42
|
+
expect(wrapper?.style.display).toBe("none");
|
|
43
|
+
expect(launcherButton?.style.display).toBe("");
|
|
44
|
+
|
|
45
|
+
controller.open();
|
|
46
|
+
|
|
47
|
+
expect(wrapper?.style.display).toBe("flex");
|
|
48
|
+
expect(launcherButton?.style.display).toBe("none");
|
|
49
|
+
|
|
50
|
+
controller.destroy();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("collapses the dock width when the header close button is clicked", () => {
|
|
54
|
+
const target = document.createElement("div");
|
|
55
|
+
document.body.appendChild(target);
|
|
56
|
+
|
|
57
|
+
const hostLayout = createWidgetHostLayout(target, {
|
|
58
|
+
launcher: {
|
|
59
|
+
mountMode: "docked",
|
|
60
|
+
autoExpand: true,
|
|
61
|
+
dock: {
|
|
62
|
+
side: "right",
|
|
63
|
+
width: "420px",
|
|
64
|
+
collapsedWidth: "72px",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
const mount = document.createElement("div");
|
|
69
|
+
hostLayout.host.appendChild(mount);
|
|
70
|
+
|
|
71
|
+
const controller = createAgentExperience(mount, {
|
|
72
|
+
apiUrl: "https://api.example.com/chat",
|
|
73
|
+
launcher: {
|
|
74
|
+
mountMode: "docked",
|
|
75
|
+
autoExpand: true,
|
|
76
|
+
dock: {
|
|
77
|
+
side: "right",
|
|
78
|
+
width: "420px",
|
|
79
|
+
collapsedWidth: "72px",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const syncDockState = () => hostLayout.syncWidgetState(controller.getState());
|
|
85
|
+
const openUnsub = controller.on("widget:opened", syncDockState);
|
|
86
|
+
const closeUnsub = controller.on("widget:closed", syncDockState);
|
|
87
|
+
syncDockState();
|
|
88
|
+
|
|
89
|
+
const dockSlot = hostLayout.shell?.querySelector<HTMLElement>('[data-persona-dock-role="panel"]');
|
|
90
|
+
const closeButton = mount.querySelector<HTMLButtonElement>('[aria-label="Close chat"]');
|
|
91
|
+
|
|
92
|
+
expect(dockSlot?.style.width).toBe("420px");
|
|
93
|
+
expect(closeButton).not.toBeNull();
|
|
94
|
+
|
|
95
|
+
closeButton!.click();
|
|
96
|
+
|
|
97
|
+
expect(dockSlot?.style.width).toBe("72px");
|
|
98
|
+
|
|
99
|
+
openUnsub();
|
|
100
|
+
closeUnsub();
|
|
101
|
+
controller.destroy();
|
|
102
|
+
hostLayout.destroy();
|
|
103
|
+
});
|
|
104
|
+
});
|