@runtypelabs/persona 3.35.0 → 3.37.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/dist/codegen.cjs +1 -1
- package/dist/codegen.js +1 -1
- package/dist/index.cjs +46 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.global.js +33 -33
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +47 -47
- package/dist/index.js.map +1 -1
- package/dist/install.global.js +1 -1
- package/dist/install.global.js.map +1 -1
- package/dist/smart-dom-reader.d.cts +10 -0
- package/dist/smart-dom-reader.d.ts +10 -0
- package/dist/theme-editor-preview.cjs +48 -48
- package/dist/theme-editor-preview.d.cts +10 -0
- package/dist/theme-editor-preview.d.ts +10 -0
- package/dist/theme-editor-preview.js +49 -49
- package/dist/theme-editor.d.cts +10 -0
- package/dist/theme-editor.d.ts +10 -0
- package/package.json +1 -1
- package/src/client.test.ts +142 -0
- package/src/client.ts +63 -8
- package/src/generated/runtype-openapi-contract.ts +1 -0
- package/src/install.ts +4 -0
- package/src/types.ts +10 -0
- package/src/utils/__fixtures__/unified-translator.oracle.ts +669 -0
- package/src/utils/unified-event-bridge.test.ts +263 -0
- package/src/utils/unified-event-bridge.ts +431 -0
package/dist/theme-editor.d.cts
CHANGED
|
@@ -4891,6 +4891,16 @@ type AgentWidgetConfig = {
|
|
|
4891
4891
|
* ```
|
|
4892
4892
|
*/
|
|
4893
4893
|
parseSSEEvent?: AgentWidgetSSEEventParser;
|
|
4894
|
+
/**
|
|
4895
|
+
* Wire vocabulary requested from the dispatch endpoint.
|
|
4896
|
+
* - `'legacy'` (default): the current `agent_*` / `flow_*` SSE events.
|
|
4897
|
+
* - `'unified'`: opt into the API's neutral unified event vocabulary by sending
|
|
4898
|
+
* `?events=unified`; incoming frames are transparently bridged back to the same
|
|
4899
|
+
* handlers, so rendering is unchanged. Requires an upstream that honors the
|
|
4900
|
+
* param — the widget detects the actual wire mode from the first stream frame
|
|
4901
|
+
* and falls back to legacy automatically if the param was ignored.
|
|
4902
|
+
*/
|
|
4903
|
+
events?: "legacy" | "unified";
|
|
4894
4904
|
/**
|
|
4895
4905
|
* Called for every parsed SSE frame (after JSON parse), before native handling.
|
|
4896
4906
|
* Use for lightweight side effects (e.g. telemetry). Does not replace native
|
package/dist/theme-editor.d.ts
CHANGED
|
@@ -4891,6 +4891,16 @@ type AgentWidgetConfig = {
|
|
|
4891
4891
|
* ```
|
|
4892
4892
|
*/
|
|
4893
4893
|
parseSSEEvent?: AgentWidgetSSEEventParser;
|
|
4894
|
+
/**
|
|
4895
|
+
* Wire vocabulary requested from the dispatch endpoint.
|
|
4896
|
+
* - `'legacy'` (default): the current `agent_*` / `flow_*` SSE events.
|
|
4897
|
+
* - `'unified'`: opt into the API's neutral unified event vocabulary by sending
|
|
4898
|
+
* `?events=unified`; incoming frames are transparently bridged back to the same
|
|
4899
|
+
* handlers, so rendering is unchanged. Requires an upstream that honors the
|
|
4900
|
+
* param — the widget detects the actual wire mode from the first stream frame
|
|
4901
|
+
* and falls back to legacy automatically if the param was ignored.
|
|
4902
|
+
*/
|
|
4903
|
+
events?: "legacy" | "unified";
|
|
4894
4904
|
/**
|
|
4895
4905
|
* Called for every parsed SSE frame (after JSON parse), before native handling.
|
|
4896
4906
|
* Use for lightweight side effects (e.g. telemetry). Does not replace native
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/persona",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.37.0",
|
|
4
4
|
"description": "Themeable, pluggable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
package/src/client.test.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
|
2
2
|
import { AgentWidgetClient, preferFinalStructuredContent } from './client';
|
|
3
3
|
import { AgentWidgetEvent, AgentWidgetMessage } from './types';
|
|
4
4
|
import { createJsonStreamParser } from './utils/formatting';
|
|
5
|
+
import { VERSION } from './version';
|
|
5
6
|
|
|
6
7
|
describe('AgentWidgetClient - Empty Message Filtering', () => {
|
|
7
8
|
let client: AgentWidgetClient;
|
|
@@ -4176,3 +4177,144 @@ describe('AgentWidgetClient - Feedback request builder', () => {
|
|
|
4176
4177
|
expect(feedbackBodies[0].rating).toBe(5);
|
|
4177
4178
|
});
|
|
4178
4179
|
});
|
|
4180
|
+
|
|
4181
|
+
describe('AgentWidgetClient - version header', () => {
|
|
4182
|
+
function captureHeaders() {
|
|
4183
|
+
const headers: Array<Record<string, string>> = [];
|
|
4184
|
+
global.fetch = vi.fn().mockImplementation(async (_url: string, options: { headers: Record<string, string> }) => {
|
|
4185
|
+
headers.push(options.headers);
|
|
4186
|
+
const encoder = new TextEncoder();
|
|
4187
|
+
const stream = new ReadableStream({
|
|
4188
|
+
start(c) {
|
|
4189
|
+
c.enqueue(encoder.encode('data: {"type":"flow_complete","success":true}\n\n'));
|
|
4190
|
+
c.close();
|
|
4191
|
+
},
|
|
4192
|
+
});
|
|
4193
|
+
return { ok: true, body: stream };
|
|
4194
|
+
});
|
|
4195
|
+
return headers;
|
|
4196
|
+
}
|
|
4197
|
+
|
|
4198
|
+
const msg = () => ({
|
|
4199
|
+
messages: [{ id: 'u1', role: 'user' as const, content: 'hi', createdAt: '2025-01-01T00:00:00.000Z' }],
|
|
4200
|
+
});
|
|
4201
|
+
|
|
4202
|
+
it('broadcasts X-Persona-Version on the dispatch request', async () => {
|
|
4203
|
+
const headers = captureHeaders();
|
|
4204
|
+
const client = new AgentWidgetClient({ apiUrl: 'http://localhost:8000' });
|
|
4205
|
+
|
|
4206
|
+
await client.dispatch(msg(), () => undefined);
|
|
4207
|
+
|
|
4208
|
+
expect(headers[0]['X-Persona-Version']).toBe(VERSION);
|
|
4209
|
+
});
|
|
4210
|
+
|
|
4211
|
+
it('lets an explicit config header override the version', async () => {
|
|
4212
|
+
const headers = captureHeaders();
|
|
4213
|
+
const client = new AgentWidgetClient({
|
|
4214
|
+
apiUrl: 'http://localhost:8000',
|
|
4215
|
+
headers: { 'X-Persona-Version': 'override' },
|
|
4216
|
+
});
|
|
4217
|
+
|
|
4218
|
+
await client.dispatch(msg(), () => undefined);
|
|
4219
|
+
|
|
4220
|
+
expect(headers[0]['X-Persona-Version']).toBe('override');
|
|
4221
|
+
});
|
|
4222
|
+
});
|
|
4223
|
+
|
|
4224
|
+
describe('AgentWidgetClient - Unified event vocabulary (events: "unified")', () => {
|
|
4225
|
+
const unifiedTextStream = (execId: string) => [
|
|
4226
|
+
sseEvent('execution_start', { kind: 'agent', executionId: execId, agentId: 'virtual', agentName: 'Test', maxTurns: 1, startedAt: new Date().toISOString(), seq: 1 }),
|
|
4227
|
+
sseEvent('turn_start', { executionId: execId, id: 'turn_1', iteration: 1, role: 'assistant', seq: 2 }),
|
|
4228
|
+
sseEvent('text_start', { executionId: execId, id: 'text_1', role: 'assistant', seq: 3 }),
|
|
4229
|
+
sseEvent('text_delta', { executionId: execId, id: 'text_1', delta: 'Hello', seq: 4 }),
|
|
4230
|
+
sseEvent('text_delta', { executionId: execId, id: 'text_1', delta: ' World', seq: 5 }),
|
|
4231
|
+
sseEvent('text_complete', { executionId: execId, id: 'text_1', seq: 6 }),
|
|
4232
|
+
sseEvent('turn_complete', { executionId: execId, id: 'turn_1', iteration: 1, role: 'assistant', completedAt: new Date().toISOString(), seq: 7 }),
|
|
4233
|
+
sseEvent('execution_complete', { kind: 'agent', executionId: execId, success: true, completedAt: new Date().toISOString(), seq: 8 }),
|
|
4234
|
+
];
|
|
4235
|
+
|
|
4236
|
+
it('appends ?events=unified to the dispatch URL when opted in', async () => {
|
|
4237
|
+
let capturedUrl = '';
|
|
4238
|
+
global.fetch = vi.fn().mockImplementation(async (url: string) => {
|
|
4239
|
+
capturedUrl = url;
|
|
4240
|
+
const encoder = new TextEncoder();
|
|
4241
|
+
const stream = new ReadableStream({
|
|
4242
|
+
start(c) {
|
|
4243
|
+
for (const e of unifiedTextStream('exec_u')) c.enqueue(encoder.encode(e));
|
|
4244
|
+
c.close();
|
|
4245
|
+
},
|
|
4246
|
+
});
|
|
4247
|
+
return { ok: true, body: stream };
|
|
4248
|
+
});
|
|
4249
|
+
|
|
4250
|
+
const client = new AgentWidgetClient({
|
|
4251
|
+
apiUrl: 'http://localhost:8000',
|
|
4252
|
+
events: 'unified',
|
|
4253
|
+
agent: { name: 'Test', model: 'openai:gpt-4o-mini', systemPrompt: 'test' },
|
|
4254
|
+
});
|
|
4255
|
+
await client.dispatch(
|
|
4256
|
+
{ messages: [{ id: 'u1', role: 'user', content: 'Hi', createdAt: new Date().toISOString() }] },
|
|
4257
|
+
() => {}
|
|
4258
|
+
);
|
|
4259
|
+
expect(capturedUrl).toContain('events=unified');
|
|
4260
|
+
});
|
|
4261
|
+
|
|
4262
|
+
it('renders a unified stream by bridging it back to the legacy handlers', async () => {
|
|
4263
|
+
const events: AgentWidgetEvent[] = [];
|
|
4264
|
+
const execId = 'exec_u1';
|
|
4265
|
+
global.fetch = createAgentStreamFetch(unifiedTextStream(execId));
|
|
4266
|
+
|
|
4267
|
+
const client = new AgentWidgetClient({
|
|
4268
|
+
apiUrl: 'http://localhost:8000',
|
|
4269
|
+
events: 'unified',
|
|
4270
|
+
agent: { name: 'Test', model: 'openai:gpt-4o-mini', systemPrompt: 'test' },
|
|
4271
|
+
});
|
|
4272
|
+
await client.dispatch(
|
|
4273
|
+
{ messages: [{ id: 'u1', role: 'user', content: 'Hi', createdAt: new Date().toISOString() }] },
|
|
4274
|
+
(e) => events.push(e)
|
|
4275
|
+
);
|
|
4276
|
+
|
|
4277
|
+
const messageEvents = events.filter((e) => e.type === 'message');
|
|
4278
|
+
expect(messageEvents.length).toBeGreaterThan(0);
|
|
4279
|
+
const last = messageEvents[messageEvents.length - 1];
|
|
4280
|
+
expect(last.type).toBe('message');
|
|
4281
|
+
if (last.type === 'message') {
|
|
4282
|
+
expect(last.message.content).toBe('Hello World');
|
|
4283
|
+
expect(last.message.streaming).toBe(false);
|
|
4284
|
+
expect(last.message.role).toBe('assistant');
|
|
4285
|
+
expect(last.message.agentMetadata?.executionId).toBe(execId);
|
|
4286
|
+
}
|
|
4287
|
+
});
|
|
4288
|
+
|
|
4289
|
+
it('auto-falls-back to legacy when "unified" was requested but the API streamed legacy frames', async () => {
|
|
4290
|
+
const events: AgentWidgetEvent[] = [];
|
|
4291
|
+
const execId = 'exec_legacy_fallback';
|
|
4292
|
+
// Same content, LEGACY vocabulary — the API ignored ?events=unified.
|
|
4293
|
+
global.fetch = createAgentStreamFetch([
|
|
4294
|
+
sseEvent('agent_start', { executionId: execId, agentId: 'virtual', agentName: 'Test', maxTurns: 1, startedAt: new Date().toISOString(), seq: 1 }),
|
|
4295
|
+
sseEvent('agent_turn_start', { executionId: execId, iteration: 1, turnId: 'turn_1', role: 'assistant', seq: 2 }),
|
|
4296
|
+
sseEvent('agent_turn_delta', { executionId: execId, iteration: 1, delta: 'Hello', contentType: 'text', turnId: 'turn_1', seq: 3 }),
|
|
4297
|
+
sseEvent('agent_turn_delta', { executionId: execId, iteration: 1, delta: ' World', contentType: 'text', turnId: 'turn_1', seq: 4 }),
|
|
4298
|
+
sseEvent('agent_turn_complete', { executionId: execId, iteration: 1, turnId: 'turn_1', completedAt: new Date().toISOString(), seq: 5 }),
|
|
4299
|
+
sseEvent('agent_complete', { executionId: execId, success: true, stopReason: 'max_iterations', completedAt: new Date().toISOString(), seq: 6 }),
|
|
4300
|
+
]);
|
|
4301
|
+
|
|
4302
|
+
const client = new AgentWidgetClient({
|
|
4303
|
+
apiUrl: 'http://localhost:8000',
|
|
4304
|
+
events: 'unified',
|
|
4305
|
+
agent: { name: 'Test', model: 'openai:gpt-4o-mini', systemPrompt: 'test' },
|
|
4306
|
+
});
|
|
4307
|
+
await client.dispatch(
|
|
4308
|
+
{ messages: [{ id: 'u1', role: 'user', content: 'Hi', createdAt: new Date().toISOString() }] },
|
|
4309
|
+
(e) => events.push(e)
|
|
4310
|
+
);
|
|
4311
|
+
|
|
4312
|
+
const messageEvents = events.filter((e) => e.type === 'message');
|
|
4313
|
+
const last = messageEvents[messageEvents.length - 1];
|
|
4314
|
+
expect(last.type).toBe('message');
|
|
4315
|
+
if (last.type === 'message') {
|
|
4316
|
+
expect(last.message.content).toBe('Hello World');
|
|
4317
|
+
expect(last.message.streaming).toBe(false);
|
|
4318
|
+
}
|
|
4319
|
+
});
|
|
4320
|
+
});
|
package/src/client.ts
CHANGED
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
createXmlParser
|
|
34
34
|
} from "./utils/formatting";
|
|
35
35
|
import { SequenceReorderBuffer } from "./utils/sequence-buffer";
|
|
36
|
+
import { UnifiedToLegacyBridge, isUnifiedLifecycleStart } from "./utils/unified-event-bridge";
|
|
37
|
+
import { VERSION } from "./version";
|
|
36
38
|
// artifactsSidebarEnabled is used in ui.ts to gate the sidebar pane rendering;
|
|
37
39
|
// artifact events are always processed here regardless of config.
|
|
38
40
|
|
|
@@ -188,6 +190,7 @@ export class AgentWidgetClient {
|
|
|
188
190
|
this.apiUrl = config.apiUrl ?? DEFAULT_ENDPOINT;
|
|
189
191
|
this.headers = {
|
|
190
192
|
"Content-Type": "application/json",
|
|
193
|
+
"X-Persona-Version": VERSION,
|
|
191
194
|
...config.headers
|
|
192
195
|
};
|
|
193
196
|
this.debug = Boolean(config.debug);
|
|
@@ -290,6 +293,17 @@ export class AgentWidgetClient {
|
|
|
290
293
|
return !!this.config.agent;
|
|
291
294
|
}
|
|
292
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Append `?events=unified` when the widget opted into the unified vocabulary,
|
|
298
|
+
* preserving any existing query string. No-op for the default legacy mode.
|
|
299
|
+
* The server only switches vocabularies when it sees the param; the widget
|
|
300
|
+
* still auto-detects the actual wire mode from the first stream frame.
|
|
301
|
+
*/
|
|
302
|
+
private withEventsParam(url: string): string {
|
|
303
|
+
if (this.config.events !== "unified") return url;
|
|
304
|
+
return url + (url.includes("?") ? "&" : "?") + "events=unified";
|
|
305
|
+
}
|
|
306
|
+
|
|
293
307
|
/**
|
|
294
308
|
* Get the appropriate API URL based on mode
|
|
295
309
|
*/
|
|
@@ -355,6 +369,7 @@ export class AgentWidgetClient {
|
|
|
355
369
|
method: 'POST',
|
|
356
370
|
headers: {
|
|
357
371
|
'Content-Type': 'application/json',
|
|
372
|
+
'X-Persona-Version': VERSION,
|
|
358
373
|
},
|
|
359
374
|
body: JSON.stringify(requestBody),
|
|
360
375
|
});
|
|
@@ -495,6 +510,7 @@ export class AgentWidgetClient {
|
|
|
495
510
|
method: 'POST',
|
|
496
511
|
headers: {
|
|
497
512
|
'Content-Type': 'application/json',
|
|
513
|
+
'X-Persona-Version': VERSION,
|
|
498
514
|
},
|
|
499
515
|
body: JSON.stringify(requestBody),
|
|
500
516
|
});
|
|
@@ -678,10 +694,11 @@ export class AgentWidgetClient {
|
|
|
678
694
|
console.debug("[AgentWidgetClient] client token dispatch", chatRequest);
|
|
679
695
|
}
|
|
680
696
|
|
|
681
|
-
response = await fetch(this.getClientApiUrl('chat'), {
|
|
697
|
+
response = await fetch(this.withEventsParam(this.getClientApiUrl('chat')), {
|
|
682
698
|
method: 'POST',
|
|
683
699
|
headers: {
|
|
684
700
|
'Content-Type': 'application/json',
|
|
701
|
+
'X-Persona-Version': VERSION,
|
|
685
702
|
},
|
|
686
703
|
body: JSON.stringify(chatRequest),
|
|
687
704
|
signal: controller.signal,
|
|
@@ -797,7 +814,7 @@ export class AgentWidgetClient {
|
|
|
797
814
|
if (this.customFetch) {
|
|
798
815
|
try {
|
|
799
816
|
response = await this.customFetch(
|
|
800
|
-
this.apiUrl,
|
|
817
|
+
this.withEventsParam(this.apiUrl),
|
|
801
818
|
{
|
|
802
819
|
method: "POST",
|
|
803
820
|
headers,
|
|
@@ -812,7 +829,7 @@ export class AgentWidgetClient {
|
|
|
812
829
|
throw err;
|
|
813
830
|
}
|
|
814
831
|
} else {
|
|
815
|
-
response = await fetch(this.apiUrl, {
|
|
832
|
+
response = await fetch(this.withEventsParam(this.apiUrl), {
|
|
816
833
|
method: "POST",
|
|
817
834
|
headers,
|
|
818
835
|
body: JSON.stringify(payload),
|
|
@@ -873,7 +890,7 @@ export class AgentWidgetClient {
|
|
|
873
890
|
if (this.customFetch) {
|
|
874
891
|
try {
|
|
875
892
|
response = await this.customFetch(
|
|
876
|
-
this.apiUrl,
|
|
893
|
+
this.withEventsParam(this.apiUrl),
|
|
877
894
|
{
|
|
878
895
|
method: "POST",
|
|
879
896
|
headers,
|
|
@@ -888,7 +905,7 @@ export class AgentWidgetClient {
|
|
|
888
905
|
throw err;
|
|
889
906
|
}
|
|
890
907
|
} else {
|
|
891
|
-
response = await fetch(this.apiUrl, {
|
|
908
|
+
response = await fetch(this.withEventsParam(this.apiUrl), {
|
|
892
909
|
method: "POST",
|
|
893
910
|
headers,
|
|
894
911
|
body: JSON.stringify(payload),
|
|
@@ -991,9 +1008,11 @@ export class AgentWidgetClient {
|
|
|
991
1008
|
options?: { streamResponse?: boolean; signal?: AbortSignal }
|
|
992
1009
|
): Promise<Response> {
|
|
993
1010
|
const isClientToken = this.isClientTokenMode();
|
|
994
|
-
const url =
|
|
995
|
-
|
|
996
|
-
|
|
1011
|
+
const url = this.withEventsParam(
|
|
1012
|
+
isClientToken
|
|
1013
|
+
? this.getClientApiUrl('resume')
|
|
1014
|
+
: `${this.config.apiUrl?.replace(/\/+$/, '') || DEFAULT_CLIENT_API_BASE}/resume`
|
|
1015
|
+
);
|
|
997
1016
|
|
|
998
1017
|
// The client-token resume route authenticates the session, not a Bearer
|
|
999
1018
|
// key. A WebMCP approval can sit awaiting user input for a long time, so by
|
|
@@ -1828,6 +1847,17 @@ export class AgentWidgetClient {
|
|
|
1828
1847
|
seqReadyQueue.push({ payloadType, payload });
|
|
1829
1848
|
scheduleReadyQueueDrain();
|
|
1830
1849
|
});
|
|
1850
|
+
// Unified-event consumer (opt-in; see utils/unified-event-bridge.ts). When the
|
|
1851
|
+
// API streams the neutral unified vocabulary, each frame is transduced back to
|
|
1852
|
+
// the legacy events the dispatch chain below already renders. Seed the mode from
|
|
1853
|
+
// the requested vocabulary so /resume (whose stream continues mid-run with no
|
|
1854
|
+
// `execution_start`) bridges correctly; a leading lifecycle frame is then
|
|
1855
|
+
// authoritative and can flip it (e.g. an upstream that ignored `?events=unified`
|
|
1856
|
+
// streams `agent_start`). The unified stream is single-connection and in order,
|
|
1857
|
+
// so bridged events bypass the reorder buffer.
|
|
1858
|
+
const unifiedBridge = new UnifiedToLegacyBridge();
|
|
1859
|
+
let unifiedMode = this.config.events === "unified";
|
|
1860
|
+
let wireModeResolved = false;
|
|
1831
1861
|
// Agent execution state tracking
|
|
1832
1862
|
let agentExecution: AgentExecutionState | null = null;
|
|
1833
1863
|
// Track assistant messages per agent iteration for 'separate' mode
|
|
@@ -3368,6 +3398,31 @@ export class AgentWidgetClient {
|
|
|
3368
3398
|
if (handled) continue; // Skip default handling if custom handler processed it
|
|
3369
3399
|
}
|
|
3370
3400
|
|
|
3401
|
+
// Unified vocabulary (opt-in): resolve the wire mode from the first
|
|
3402
|
+
// lifecycle frame (the flag only requests the param; the frame is
|
|
3403
|
+
// authoritative), then transduce unified → legacy, bypassing the reorder
|
|
3404
|
+
// buffer (the unified stream is single-connection and already in order).
|
|
3405
|
+
if (!wireModeResolved) {
|
|
3406
|
+
if (isUnifiedLifecycleStart(payloadType)) {
|
|
3407
|
+
unifiedMode = true;
|
|
3408
|
+
wireModeResolved = true;
|
|
3409
|
+
} else if (
|
|
3410
|
+
payloadType === "agent_start" ||
|
|
3411
|
+
payloadType === "flow_start" ||
|
|
3412
|
+
payloadType === "step_start"
|
|
3413
|
+
) {
|
|
3414
|
+
unifiedMode = false;
|
|
3415
|
+
wireModeResolved = true;
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
if (unifiedMode) {
|
|
3419
|
+
for (const legacyEvent of unifiedBridge.push(payloadType, payload)) {
|
|
3420
|
+
seqReadyQueue.push(legacyEvent);
|
|
3421
|
+
}
|
|
3422
|
+
drainReadyQueue();
|
|
3423
|
+
continue;
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3371
3426
|
// Push through the sequence reorder buffer
|
|
3372
3427
|
seqBuffer.push(payloadType, payload);
|
|
3373
3428
|
drainReadyQueue();
|
package/src/install.ts
CHANGED
|
@@ -18,6 +18,9 @@ interface SiteAgentInstallConfig {
|
|
|
18
18
|
clientToken?: string;
|
|
19
19
|
flowId?: string;
|
|
20
20
|
apiUrl?: string;
|
|
21
|
+
/** Wire vocabulary requested from the dispatch endpoint; 'unified' opts into the
|
|
22
|
+
* API's neutral event schema (transparently bridged back to the same rendering). */
|
|
23
|
+
events?: "legacy" | "unified";
|
|
21
24
|
// Optional query param key that gates widget installation in preview mode
|
|
22
25
|
previewQueryParam?: string;
|
|
23
26
|
// Shadow DOM option (defaults to false for better CSS compatibility)
|
|
@@ -377,6 +380,7 @@ declare global {
|
|
|
377
380
|
if (config.apiUrl && !widgetConfig.apiUrl) widgetConfig.apiUrl = config.apiUrl;
|
|
378
381
|
if (config.clientToken && !widgetConfig.clientToken) widgetConfig.clientToken = config.clientToken;
|
|
379
382
|
if (config.flowId && !widgetConfig.flowId) widgetConfig.flowId = config.flowId;
|
|
383
|
+
if (config.events && !widgetConfig.events) widgetConfig.events = config.events;
|
|
380
384
|
|
|
381
385
|
const hasApiConfig = !!(widgetConfig.apiUrl || widgetConfig.clientToken);
|
|
382
386
|
return { target, widgetConfig, hasApiConfig };
|
package/src/types.ts
CHANGED
|
@@ -4056,6 +4056,16 @@ export type AgentWidgetConfig = {
|
|
|
4056
4056
|
* ```
|
|
4057
4057
|
*/
|
|
4058
4058
|
parseSSEEvent?: AgentWidgetSSEEventParser;
|
|
4059
|
+
/**
|
|
4060
|
+
* Wire vocabulary requested from the dispatch endpoint.
|
|
4061
|
+
* - `'legacy'` (default): the current `agent_*` / `flow_*` SSE events.
|
|
4062
|
+
* - `'unified'`: opt into the API's neutral unified event vocabulary by sending
|
|
4063
|
+
* `?events=unified`; incoming frames are transparently bridged back to the same
|
|
4064
|
+
* handlers, so rendering is unchanged. Requires an upstream that honors the
|
|
4065
|
+
* param — the widget detects the actual wire mode from the first stream frame
|
|
4066
|
+
* and falls back to legacy automatically if the param was ignored.
|
|
4067
|
+
*/
|
|
4068
|
+
events?: "legacy" | "unified";
|
|
4059
4069
|
/**
|
|
4060
4070
|
* Called for every parsed SSE frame (after JSON parse), before native handling.
|
|
4061
4071
|
* Use for lightweight side effects (e.g. telemetry). Does not replace native
|