@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/session.ts
CHANGED
|
@@ -10,7 +10,9 @@ import {
|
|
|
10
10
|
InjectMessageOptions,
|
|
11
11
|
InjectAssistantMessageOptions,
|
|
12
12
|
InjectUserMessageOptions,
|
|
13
|
-
InjectSystemMessageOptions
|
|
13
|
+
InjectSystemMessageOptions,
|
|
14
|
+
PersonaArtifactRecord,
|
|
15
|
+
PersonaArtifactManualUpsert
|
|
14
16
|
} from "./types";
|
|
15
17
|
import {
|
|
16
18
|
generateUserMessageId,
|
|
@@ -19,14 +21,12 @@ import {
|
|
|
19
21
|
import { IMAGE_ONLY_MESSAGE_FALLBACK_TEXT } from "./utils/content";
|
|
20
22
|
import type {
|
|
21
23
|
VoiceProvider,
|
|
22
|
-
VoiceResult,
|
|
23
24
|
VoiceStatus,
|
|
24
25
|
VoiceConfig,
|
|
25
26
|
TextToSpeechConfig
|
|
26
27
|
} from "./types";
|
|
27
28
|
import {
|
|
28
29
|
createVoiceProvider,
|
|
29
|
-
createBestAvailableVoiceProvider,
|
|
30
30
|
isVoiceSupported
|
|
31
31
|
} from "./voice";
|
|
32
32
|
|
|
@@ -42,6 +42,10 @@ type SessionCallbacks = {
|
|
|
42
42
|
onStreamingChanged: (streaming: boolean) => void;
|
|
43
43
|
onError?: (error: Error) => void;
|
|
44
44
|
onVoiceStatusChanged?: (status: VoiceStatus) => void;
|
|
45
|
+
onArtifactsState?: (state: {
|
|
46
|
+
artifacts: PersonaArtifactRecord[];
|
|
47
|
+
selectedId: string | null;
|
|
48
|
+
}) => void;
|
|
45
49
|
};
|
|
46
50
|
|
|
47
51
|
export class AgentWidgetSession {
|
|
@@ -58,6 +62,9 @@ export class AgentWidgetSession {
|
|
|
58
62
|
// Agent execution state
|
|
59
63
|
private agentExecution: AgentExecutionState | null = null;
|
|
60
64
|
|
|
65
|
+
private artifacts = new Map<string, PersonaArtifactRecord>();
|
|
66
|
+
private selectedArtifactId: string | null = null;
|
|
67
|
+
|
|
61
68
|
// Voice support
|
|
62
69
|
private voiceProvider: VoiceProvider | null = null;
|
|
63
70
|
private voiceActive = false;
|
|
@@ -136,6 +143,38 @@ export class AgentWidgetSession {
|
|
|
136
143
|
return this.voiceStatus;
|
|
137
144
|
}
|
|
138
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Get the voice interruption mode from the provider (none/cancel/barge-in)
|
|
148
|
+
*/
|
|
149
|
+
public getVoiceInterruptionMode(): "none" | "cancel" | "barge-in" {
|
|
150
|
+
if (this.voiceProvider?.getInterruptionMode) {
|
|
151
|
+
return this.voiceProvider.getInterruptionMode();
|
|
152
|
+
}
|
|
153
|
+
return "none";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Stop voice playback / cancel in-flight request without starting recording.
|
|
158
|
+
* Returns to idle state.
|
|
159
|
+
*/
|
|
160
|
+
public stopVoicePlayback(): void {
|
|
161
|
+
if (this.voiceProvider?.stopPlayback) {
|
|
162
|
+
this.voiceProvider.stopPlayback();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Returns true if the barge-in mic stream is alive (hot mic between turns) */
|
|
167
|
+
public isBargeInActive(): boolean {
|
|
168
|
+
return this.voiceProvider?.isBargeInActive?.() ?? false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Tear down the barge-in mic pipeline — "hang up" the always-on mic */
|
|
172
|
+
public async deactivateBargeIn(): Promise<void> {
|
|
173
|
+
if (this.voiceProvider?.deactivateBargeIn) {
|
|
174
|
+
await this.voiceProvider.deactivateBargeIn();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
139
178
|
// Pending placeholder IDs for Runtype two-phase voice flow
|
|
140
179
|
private pendingVoiceUserMessageId: string | null = null;
|
|
141
180
|
private pendingVoiceAssistantMessageId: string | null = null;
|
|
@@ -227,9 +266,12 @@ export class AgentWidgetSession {
|
|
|
227
266
|
this.injectAssistantMessage({ content: result.text.trim() });
|
|
228
267
|
}
|
|
229
268
|
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
|
|
269
|
+
// Mark assistant message as already spoken so browser TTS doesn't
|
|
270
|
+
// double-speak. This covers both paths:
|
|
271
|
+
// - Batch: audio.base64 is present in the voice_response
|
|
272
|
+
// - Streaming: audio arrives as binary PCM chunks (no base64 here)
|
|
273
|
+
// In either case, the Runtype provider handles TTS — browser TTS must skip.
|
|
274
|
+
{
|
|
233
275
|
const spokenId = this.pendingVoiceAssistantMessageId
|
|
234
276
|
?? [...this.messages].reverse().find(m => m.role === 'assistant')?.id;
|
|
235
277
|
if (spokenId) this.ttsSpokenMessageIds.add(spokenId);
|
|
@@ -939,11 +981,128 @@ export class AgentWidgetSession {
|
|
|
939
981
|
this.abortController = null;
|
|
940
982
|
this.messages = [];
|
|
941
983
|
this.agentExecution = null;
|
|
984
|
+
this.clearArtifactState();
|
|
942
985
|
this.setStreaming(false);
|
|
943
986
|
this.setStatus("idle");
|
|
944
987
|
this.callbacks.onMessagesChanged([...this.messages]);
|
|
945
988
|
}
|
|
946
989
|
|
|
990
|
+
public getArtifacts(): PersonaArtifactRecord[] {
|
|
991
|
+
return [...this.artifacts.values()];
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
public getArtifactById(id: string): PersonaArtifactRecord | undefined {
|
|
995
|
+
return this.artifacts.get(id);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
public getSelectedArtifactId(): string | null {
|
|
999
|
+
return this.selectedArtifactId;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
public selectArtifact(id: string | null): void {
|
|
1003
|
+
this.selectedArtifactId = id;
|
|
1004
|
+
this.emitArtifactsState();
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
public clearArtifacts(): void {
|
|
1008
|
+
this.clearArtifactState();
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
public upsertArtifact(manual: PersonaArtifactManualUpsert): PersonaArtifactRecord {
|
|
1012
|
+
const id =
|
|
1013
|
+
manual.id ||
|
|
1014
|
+
`art_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
|
|
1015
|
+
if (manual.artifactType === "markdown") {
|
|
1016
|
+
const rec: PersonaArtifactRecord = {
|
|
1017
|
+
id,
|
|
1018
|
+
artifactType: "markdown",
|
|
1019
|
+
title: manual.title,
|
|
1020
|
+
status: "complete",
|
|
1021
|
+
markdown: manual.content
|
|
1022
|
+
};
|
|
1023
|
+
this.artifacts.set(id, rec);
|
|
1024
|
+
this.selectedArtifactId = id;
|
|
1025
|
+
this.emitArtifactsState();
|
|
1026
|
+
return rec;
|
|
1027
|
+
}
|
|
1028
|
+
const rec: PersonaArtifactRecord = {
|
|
1029
|
+
id,
|
|
1030
|
+
artifactType: "component",
|
|
1031
|
+
title: manual.title,
|
|
1032
|
+
status: "complete",
|
|
1033
|
+
component: manual.component,
|
|
1034
|
+
props: manual.props ?? {}
|
|
1035
|
+
};
|
|
1036
|
+
this.artifacts.set(id, rec);
|
|
1037
|
+
this.selectedArtifactId = id;
|
|
1038
|
+
this.emitArtifactsState();
|
|
1039
|
+
return rec;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
private clearArtifactState(): void {
|
|
1043
|
+
if (this.artifacts.size === 0 && this.selectedArtifactId === null) return;
|
|
1044
|
+
this.artifacts.clear();
|
|
1045
|
+
this.selectedArtifactId = null;
|
|
1046
|
+
this.emitArtifactsState();
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
private emitArtifactsState(): void {
|
|
1050
|
+
this.callbacks.onArtifactsState?.({
|
|
1051
|
+
artifacts: [...this.artifacts.values()],
|
|
1052
|
+
selectedId: this.selectedArtifactId
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
private applyArtifactStreamEvent(ev: AgentWidgetEvent): void {
|
|
1057
|
+
switch (ev.type) {
|
|
1058
|
+
case "artifact_start": {
|
|
1059
|
+
if (ev.artifactType === "markdown") {
|
|
1060
|
+
this.artifacts.set(ev.id, {
|
|
1061
|
+
id: ev.id,
|
|
1062
|
+
artifactType: "markdown",
|
|
1063
|
+
title: ev.title,
|
|
1064
|
+
status: "streaming",
|
|
1065
|
+
markdown: ""
|
|
1066
|
+
});
|
|
1067
|
+
} else {
|
|
1068
|
+
this.artifacts.set(ev.id, {
|
|
1069
|
+
id: ev.id,
|
|
1070
|
+
artifactType: "component",
|
|
1071
|
+
title: ev.title,
|
|
1072
|
+
status: "streaming",
|
|
1073
|
+
component: ev.component ?? "",
|
|
1074
|
+
props: {}
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
this.selectedArtifactId = ev.id;
|
|
1078
|
+
break;
|
|
1079
|
+
}
|
|
1080
|
+
case "artifact_delta": {
|
|
1081
|
+
const row = this.artifacts.get(ev.id);
|
|
1082
|
+
if (row?.artifactType === "markdown") {
|
|
1083
|
+
row.markdown = (row.markdown ?? "") + ev.artDelta;
|
|
1084
|
+
}
|
|
1085
|
+
break;
|
|
1086
|
+
}
|
|
1087
|
+
case "artifact_update": {
|
|
1088
|
+
const row = this.artifacts.get(ev.id);
|
|
1089
|
+
if (row?.artifactType === "component") {
|
|
1090
|
+
row.props = { ...row.props, ...ev.props };
|
|
1091
|
+
if (ev.component) row.component = ev.component;
|
|
1092
|
+
}
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
case "artifact_complete": {
|
|
1096
|
+
const row = this.artifacts.get(ev.id);
|
|
1097
|
+
if (row) row.status = "complete";
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
default:
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
this.emitArtifactsState();
|
|
1104
|
+
}
|
|
1105
|
+
|
|
947
1106
|
public hydrateMessages(messages: AgentWidgetMessage[]) {
|
|
948
1107
|
this.abortController?.abort();
|
|
949
1108
|
this.abortController = null;
|
|
@@ -972,7 +1131,7 @@ export class AgentWidgetSession {
|
|
|
972
1131
|
agentName: event.message.agentMetadata.agentName ?? '',
|
|
973
1132
|
status: 'running',
|
|
974
1133
|
currentIteration: event.message.agentMetadata.iteration ?? 0,
|
|
975
|
-
|
|
1134
|
+
maxTurns: 0
|
|
976
1135
|
};
|
|
977
1136
|
} else if (event.message.agentMetadata.iteration !== undefined) {
|
|
978
1137
|
this.agentExecution.currentIteration = event.message.agentMetadata.iteration;
|
|
@@ -998,6 +1157,13 @@ export class AgentWidgetSession {
|
|
|
998
1157
|
this.agentExecution.status = 'error';
|
|
999
1158
|
}
|
|
1000
1159
|
this.callbacks.onError?.(event.error);
|
|
1160
|
+
} else if (
|
|
1161
|
+
event.type === "artifact_start" ||
|
|
1162
|
+
event.type === "artifact_delta" ||
|
|
1163
|
+
event.type === "artifact_update" ||
|
|
1164
|
+
event.type === "artifact_complete"
|
|
1165
|
+
) {
|
|
1166
|
+
this.applyArtifactStreamEvent(event);
|
|
1001
1167
|
}
|
|
1002
1168
|
};
|
|
1003
1169
|
|