@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
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified → legacy SSE event bridge (widget consumer side).
|
|
3
|
+
*
|
|
4
|
+
* The mirror image of the runtype-core `createUnifiedEventWrite` translator: it
|
|
5
|
+
* takes frames in the neutral 33-event UNIFIED vocabulary (emitted by the API
|
|
6
|
+
* when a caller requests `?events=unified`) and maps each back onto the LEGACY
|
|
7
|
+
* `agent_*` / `flow_*` / `step_*` / `artifact_*` events the widget's existing
|
|
8
|
+
* dispatch chain (`client.ts`) already renders. The dispatch chain is untouched.
|
|
9
|
+
*
|
|
10
|
+
* Opt-in: only engaged when `events: 'unified'` is set (or auto-detected from a
|
|
11
|
+
* leading `execution_start` frame). Default `'legacy'` bypasses this module.
|
|
12
|
+
*
|
|
13
|
+
* Stateful: the unified vocabulary collapses agent/flow distinctions and
|
|
14
|
+
* decouples text/reasoning block ids from turns, so the bridge tracks `kind`,
|
|
15
|
+
* the open turn/step, and buffers the media triad — exactly inverting the api
|
|
16
|
+
* translator's state. See `docs/specs/widget-unified-event-bridge.md` for the
|
|
17
|
+
* full mapping table (every field rename is pinned to the handler that reads it).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Canonical WebMCP wire prefix — mirrors `webmcp-bridge.ts` (WEBMCP_TOOL_PREFIX
|
|
21
|
+
* / isWebMcpToolName). Inlined to keep this pure mapper dependency-free. */
|
|
22
|
+
const WEBMCP_PREFIX = "webmcp:";
|
|
23
|
+
|
|
24
|
+
export type LegacyEvent = { payloadType: string; payload: Record<string, unknown> };
|
|
25
|
+
|
|
26
|
+
/** True when a frame's `type` identifies the unified vocabulary unambiguously.
|
|
27
|
+
* `execution_start` is unified-exclusive; `agent_start` / `flow_start` are
|
|
28
|
+
* legacy-exclusive. Used by `client.ts` to auto-detect the wire mode from the
|
|
29
|
+
* first lifecycle frame (so the `events` flag is a request, not a commitment). */
|
|
30
|
+
export function isUnifiedLifecycleStart(type: string): boolean {
|
|
31
|
+
return type === "execution_start";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class UnifiedToLegacyBridge {
|
|
35
|
+
private kind: "agent" | "flow" = "agent";
|
|
36
|
+
private executionId: string;
|
|
37
|
+
private iteration = 1;
|
|
38
|
+
/** current agent turn id (from `turn_start`) — the `turnId` agent deltas need */
|
|
39
|
+
private openTurnId: string | null = null;
|
|
40
|
+
/** current flow step id (from `step_start`) — the `id` flow text attaches to */
|
|
41
|
+
private openStepId: string | null = null;
|
|
42
|
+
private openTextBlockId: string | null = null;
|
|
43
|
+
private openReasoningId: string | null = null;
|
|
44
|
+
private readonly mediaBuffers = new Map<
|
|
45
|
+
string,
|
|
46
|
+
{ mediaType?: string; role?: string; toolCallId?: unknown; parts: string[] }
|
|
47
|
+
>();
|
|
48
|
+
|
|
49
|
+
constructor(opts?: { executionId?: string }) {
|
|
50
|
+
this.executionId = opts?.executionId ?? "";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Translate ONE decoded unified frame into 0..N legacy events, in order. */
|
|
54
|
+
push(type: string, payload: Record<string, unknown>): LegacyEvent[] {
|
|
55
|
+
// Every unified frame carries the envelope executionId/seq; iteration rides
|
|
56
|
+
// on turn/tool frames. Capture them so legacy `agent_*` payloads can be stamped.
|
|
57
|
+
if (typeof payload.executionId === "string" && payload.executionId) {
|
|
58
|
+
this.executionId = payload.executionId;
|
|
59
|
+
}
|
|
60
|
+
if (typeof payload.iteration === "number") this.iteration = payload.iteration;
|
|
61
|
+
|
|
62
|
+
switch (type) {
|
|
63
|
+
// ===== lifecycle =====
|
|
64
|
+
case "execution_start": {
|
|
65
|
+
this.kind = payload.kind === "flow" ? "flow" : "agent";
|
|
66
|
+
if (this.kind !== "agent") return []; // no flow_start handler; state only
|
|
67
|
+
return [
|
|
68
|
+
this.out("agent_start", {
|
|
69
|
+
executionId: this.executionId,
|
|
70
|
+
agentId: payload.agentId,
|
|
71
|
+
agentName: payload.agentName,
|
|
72
|
+
maxTurns: payload.maxTurns,
|
|
73
|
+
startedAt: payload.startedAt,
|
|
74
|
+
}),
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
case "turn_start": {
|
|
78
|
+
this.openTurnId = typeof payload.id === "string" ? payload.id : null;
|
|
79
|
+
if (this.kind !== "agent") return [];
|
|
80
|
+
return [this.out("agent_turn_start", { turnId: payload.id, iteration: payload.iteration })];
|
|
81
|
+
}
|
|
82
|
+
case "turn_complete": {
|
|
83
|
+
const out =
|
|
84
|
+
this.kind === "agent"
|
|
85
|
+
? [
|
|
86
|
+
this.out("agent_turn_complete", {
|
|
87
|
+
turnId: payload.id,
|
|
88
|
+
iteration: payload.iteration,
|
|
89
|
+
stopReason: payload.stopReason,
|
|
90
|
+
completedAt: payload.completedAt,
|
|
91
|
+
}),
|
|
92
|
+
]
|
|
93
|
+
: [];
|
|
94
|
+
if (this.openTurnId === payload.id) this.openTurnId = null;
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
case "execution_complete": {
|
|
98
|
+
if ((payload.kind ?? this.kind) === "agent") {
|
|
99
|
+
return [
|
|
100
|
+
this.out("agent_complete", {
|
|
101
|
+
executionId: this.executionId,
|
|
102
|
+
success: payload.success,
|
|
103
|
+
completedAt: payload.completedAt,
|
|
104
|
+
stopReason: payload.stopReason,
|
|
105
|
+
}),
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
return [
|
|
109
|
+
this.out("flow_complete", {
|
|
110
|
+
success: payload.success,
|
|
111
|
+
completedAt: payload.completedAt,
|
|
112
|
+
duration: payload.durationMs,
|
|
113
|
+
finalOutput: payload.finalOutput,
|
|
114
|
+
totalSteps: payload.totalSteps,
|
|
115
|
+
successfulSteps: payload.successfulSteps,
|
|
116
|
+
failedSteps: payload.failedSteps,
|
|
117
|
+
}),
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
case "execution_error": {
|
|
121
|
+
if ((payload.kind ?? this.kind) === "agent") {
|
|
122
|
+
// API emits no execution_complete on agent failure; surface as a
|
|
123
|
+
// terminal agent_error (recoverable:false → onEvent error in client.ts).
|
|
124
|
+
return [this.out("agent_error", { recoverable: false, error: payload.error })];
|
|
125
|
+
}
|
|
126
|
+
return [
|
|
127
|
+
this.out("flow_error", {
|
|
128
|
+
error: payload.error,
|
|
129
|
+
code: payload.code,
|
|
130
|
+
upgradeUrl: payload.upgradeUrl,
|
|
131
|
+
}),
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
case "error":
|
|
135
|
+
// Unified `error` is the NON-terminal one. Route to agent_error
|
|
136
|
+
// (recoverable → warn only), NOT legacy `error` (that branch is terminal).
|
|
137
|
+
return [this.out("agent_error", { recoverable: true, error: payload.error })];
|
|
138
|
+
case "ping":
|
|
139
|
+
return [this.out("agent_ping", { timestamp: payload.timestamp })];
|
|
140
|
+
|
|
141
|
+
// ===== text channel =====
|
|
142
|
+
case "text_start":
|
|
143
|
+
this.openTextBlockId = typeof payload.id === "string" ? payload.id : null;
|
|
144
|
+
return []; // agent bubble created lazily by the first delta
|
|
145
|
+
case "text_delta": {
|
|
146
|
+
const delta = String(payload.delta ?? "");
|
|
147
|
+
if (this.kind === "agent") {
|
|
148
|
+
return [
|
|
149
|
+
this.out("agent_turn_delta", {
|
|
150
|
+
contentType: "text",
|
|
151
|
+
delta,
|
|
152
|
+
turnId: this.openTurnId ?? payload.id,
|
|
153
|
+
iteration: this.iteration,
|
|
154
|
+
executionId: this.executionId,
|
|
155
|
+
}),
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
return [this.out("step_delta", { id: this.openStepId ?? payload.id, text: delta, stepType: "prompt" })];
|
|
159
|
+
}
|
|
160
|
+
case "text_complete":
|
|
161
|
+
if (this.openTextBlockId === payload.id) this.openTextBlockId = null;
|
|
162
|
+
return [];
|
|
163
|
+
|
|
164
|
+
// ===== reasoning channel =====
|
|
165
|
+
case "reasoning_start":
|
|
166
|
+
this.openReasoningId = typeof payload.id === "string" ? payload.id : null;
|
|
167
|
+
if (this.kind === "flow") return [this.out("reason_start", { id: payload.id })];
|
|
168
|
+
return [];
|
|
169
|
+
case "reasoning_delta": {
|
|
170
|
+
const delta = String(payload.delta ?? "");
|
|
171
|
+
if (this.kind === "agent") {
|
|
172
|
+
return [
|
|
173
|
+
this.out("agent_turn_delta", {
|
|
174
|
+
contentType: "thinking",
|
|
175
|
+
delta,
|
|
176
|
+
turnId: this.openTurnId ?? payload.id,
|
|
177
|
+
iteration: this.iteration,
|
|
178
|
+
executionId: this.executionId,
|
|
179
|
+
}),
|
|
180
|
+
];
|
|
181
|
+
}
|
|
182
|
+
return [this.out("reason_delta", { id: payload.id, delta })];
|
|
183
|
+
}
|
|
184
|
+
case "reasoning_complete": {
|
|
185
|
+
const hasText = typeof payload.text === "string" && payload.text.length > 0;
|
|
186
|
+
if (this.kind === "agent") {
|
|
187
|
+
// E3: scope:'loop' (or a text-carrying close) is a reflection fold.
|
|
188
|
+
if (payload.scope === "loop" || hasText) {
|
|
189
|
+
return [
|
|
190
|
+
this.out("agent_reflection", {
|
|
191
|
+
reflection: payload.text ?? "",
|
|
192
|
+
executionId: this.executionId,
|
|
193
|
+
iteration: this.iteration,
|
|
194
|
+
}),
|
|
195
|
+
];
|
|
196
|
+
}
|
|
197
|
+
return []; // turn-scoped thinking: closed by turn_complete
|
|
198
|
+
}
|
|
199
|
+
const out: LegacyEvent[] = [];
|
|
200
|
+
if (hasText) out.push(this.out("reason_delta", { id: payload.id, delta: payload.text }));
|
|
201
|
+
out.push(this.out("reason_complete", { id: payload.id }));
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ===== tool channel =====
|
|
206
|
+
case "tool_start": {
|
|
207
|
+
if (this.kind === "agent") {
|
|
208
|
+
return [
|
|
209
|
+
this.out("agent_tool_start", {
|
|
210
|
+
toolCallId: payload.toolCallId,
|
|
211
|
+
toolName: payload.toolName,
|
|
212
|
+
parameters: payload.parameters,
|
|
213
|
+
executionId: this.executionId,
|
|
214
|
+
iteration: payload.iteration ?? this.iteration,
|
|
215
|
+
startedAt: payload.startedAt,
|
|
216
|
+
}),
|
|
217
|
+
];
|
|
218
|
+
}
|
|
219
|
+
return [
|
|
220
|
+
this.out("tool_start", {
|
|
221
|
+
toolId: payload.toolCallId,
|
|
222
|
+
toolName: payload.toolName,
|
|
223
|
+
parameters: payload.parameters,
|
|
224
|
+
executionId: this.executionId,
|
|
225
|
+
iteration: payload.iteration ?? this.iteration,
|
|
226
|
+
startedAt: payload.startedAt,
|
|
227
|
+
}),
|
|
228
|
+
];
|
|
229
|
+
}
|
|
230
|
+
case "tool_input_delta": {
|
|
231
|
+
if (this.kind === "agent") {
|
|
232
|
+
return [
|
|
233
|
+
this.out("agent_turn_delta", {
|
|
234
|
+
contentType: "tool_input",
|
|
235
|
+
delta: String(payload.delta ?? ""),
|
|
236
|
+
toolCallId: payload.toolCallId,
|
|
237
|
+
}),
|
|
238
|
+
];
|
|
239
|
+
}
|
|
240
|
+
return []; // flow tool-input isn't surfaced to the UI
|
|
241
|
+
}
|
|
242
|
+
case "tool_input_complete":
|
|
243
|
+
return []; // args already set at tool_start; no handler
|
|
244
|
+
case "tool_output_delta": {
|
|
245
|
+
const delta = String(payload.delta ?? "");
|
|
246
|
+
if (this.kind === "agent") {
|
|
247
|
+
return [this.out("agent_tool_delta", { toolCallId: payload.toolCallId, delta })];
|
|
248
|
+
}
|
|
249
|
+
return [this.out("tool_delta", { toolId: payload.toolCallId, delta })];
|
|
250
|
+
}
|
|
251
|
+
case "tool_complete": {
|
|
252
|
+
if (this.kind === "agent") {
|
|
253
|
+
return [
|
|
254
|
+
this.out("agent_tool_complete", {
|
|
255
|
+
toolCallId: payload.toolCallId,
|
|
256
|
+
result: payload.result,
|
|
257
|
+
executionTime: payload.executionTime,
|
|
258
|
+
completedAt: payload.completedAt,
|
|
259
|
+
}),
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
return [
|
|
263
|
+
this.out("tool_complete", {
|
|
264
|
+
toolId: payload.toolCallId,
|
|
265
|
+
result: payload.result,
|
|
266
|
+
duration: payload.executionTime,
|
|
267
|
+
completedAt: payload.completedAt,
|
|
268
|
+
}),
|
|
269
|
+
];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ===== media channel (triad → one agent_media) =====
|
|
273
|
+
case "media_start": {
|
|
274
|
+
const id = String(payload.id);
|
|
275
|
+
this.mediaBuffers.set(id, {
|
|
276
|
+
mediaType: typeof payload.mediaType === "string" ? payload.mediaType : undefined,
|
|
277
|
+
role: typeof payload.role === "string" ? payload.role : undefined,
|
|
278
|
+
toolCallId: payload.toolCallId,
|
|
279
|
+
parts: [],
|
|
280
|
+
});
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
case "media_delta": {
|
|
284
|
+
const buf = this.mediaBuffers.get(String(payload.id));
|
|
285
|
+
if (buf && typeof payload.delta === "string") buf.parts.push(payload.delta);
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
case "media_complete": {
|
|
289
|
+
const id = String(payload.id);
|
|
290
|
+
const buf = this.mediaBuffers.get(id);
|
|
291
|
+
this.mediaBuffers.delete(id);
|
|
292
|
+
const mediaType =
|
|
293
|
+
(typeof payload.mediaType === "string" ? payload.mediaType : undefined) ??
|
|
294
|
+
buf?.mediaType ??
|
|
295
|
+
"application/octet-stream";
|
|
296
|
+
const data = typeof payload.data === "string" ? payload.data : undefined;
|
|
297
|
+
const url =
|
|
298
|
+
typeof payload.url === "string"
|
|
299
|
+
? payload.url
|
|
300
|
+
: buf && buf.parts.length > 0
|
|
301
|
+
? buf.parts.join("")
|
|
302
|
+
: undefined;
|
|
303
|
+
let part: Record<string, unknown> | null = null;
|
|
304
|
+
if (data) {
|
|
305
|
+
part = { type: "media", data, mediaType };
|
|
306
|
+
} else if (url) {
|
|
307
|
+
part = { type: mediaType.startsWith("image/") ? "image-url" : "file-url", url, mediaType };
|
|
308
|
+
}
|
|
309
|
+
if (!part) return [];
|
|
310
|
+
return [
|
|
311
|
+
this.out("agent_media", {
|
|
312
|
+
media: [part],
|
|
313
|
+
executionId: this.executionId,
|
|
314
|
+
iteration: this.iteration,
|
|
315
|
+
toolCallId: payload.toolCallId ?? buf?.toolCallId,
|
|
316
|
+
}),
|
|
317
|
+
];
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// ===== approvals =====
|
|
321
|
+
case "approval_start":
|
|
322
|
+
return [
|
|
323
|
+
this.out("agent_approval_start", {
|
|
324
|
+
approvalId: payload.approvalId,
|
|
325
|
+
toolName: payload.toolName,
|
|
326
|
+
toolType: payload.toolType,
|
|
327
|
+
description: payload.description,
|
|
328
|
+
reason: payload.reason,
|
|
329
|
+
parameters: payload.parameters,
|
|
330
|
+
executionId: this.executionId,
|
|
331
|
+
}),
|
|
332
|
+
];
|
|
333
|
+
case "approval_complete":
|
|
334
|
+
return [
|
|
335
|
+
this.out("agent_approval_complete", {
|
|
336
|
+
approvalId: payload.approvalId,
|
|
337
|
+
decision: payload.decision,
|
|
338
|
+
executionId: this.executionId,
|
|
339
|
+
toolName: payload.toolName,
|
|
340
|
+
description: payload.description,
|
|
341
|
+
}),
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
// ===== await (local-tool / WebMCP pause) — onto the 3.35.0 step_await path =====
|
|
345
|
+
case "await": {
|
|
346
|
+
const raw = typeof payload.toolName === "string" ? payload.toolName : "";
|
|
347
|
+
const toolName =
|
|
348
|
+
payload.origin === "webmcp" && !raw.startsWith(WEBMCP_PREFIX) ? `${WEBMCP_PREFIX}${raw}` : raw;
|
|
349
|
+
return [
|
|
350
|
+
this.out("step_await", {
|
|
351
|
+
awaitReason: "local_tool_required",
|
|
352
|
+
toolName,
|
|
353
|
+
parameters: payload.parameters,
|
|
354
|
+
toolCallId: payload.toolCallId,
|
|
355
|
+
toolId: payload.toolId,
|
|
356
|
+
executionId: this.executionId,
|
|
357
|
+
awaitedAt: payload.awaitedAt,
|
|
358
|
+
}),
|
|
359
|
+
];
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ===== steps (flow) =====
|
|
363
|
+
case "step_start":
|
|
364
|
+
this.openStepId = typeof payload.id === "string" ? payload.id : null;
|
|
365
|
+
return [
|
|
366
|
+
this.out("step_start", {
|
|
367
|
+
id: payload.id,
|
|
368
|
+
name: payload.name,
|
|
369
|
+
stepType: payload.stepType,
|
|
370
|
+
index: payload.index,
|
|
371
|
+
totalSteps: payload.totalSteps,
|
|
372
|
+
startedAt: payload.startedAt,
|
|
373
|
+
outputVariable: payload.outputVariable,
|
|
374
|
+
}),
|
|
375
|
+
];
|
|
376
|
+
case "step_complete": {
|
|
377
|
+
const out = this.out("step_complete", {
|
|
378
|
+
id: payload.id,
|
|
379
|
+
name: payload.name,
|
|
380
|
+
stepType: payload.stepType,
|
|
381
|
+
success: payload.success,
|
|
382
|
+
duration: payload.durationMs,
|
|
383
|
+
result: payload.result,
|
|
384
|
+
stopReason: payload.stopReason,
|
|
385
|
+
completedAt: payload.completedAt,
|
|
386
|
+
unresolvedVariables: payload.unresolvedVariables,
|
|
387
|
+
});
|
|
388
|
+
if (this.openStepId === payload.id) this.openStepId = null;
|
|
389
|
+
return [out];
|
|
390
|
+
}
|
|
391
|
+
case "step_skip":
|
|
392
|
+
return []; // no step_skip handler
|
|
393
|
+
|
|
394
|
+
// ===== artifacts (1:1) =====
|
|
395
|
+
case "artifact_start":
|
|
396
|
+
return [
|
|
397
|
+
this.out("artifact_start", {
|
|
398
|
+
id: payload.id,
|
|
399
|
+
artifactType: payload.artifactType,
|
|
400
|
+
title: payload.title,
|
|
401
|
+
component: payload.component,
|
|
402
|
+
}),
|
|
403
|
+
];
|
|
404
|
+
case "artifact_delta":
|
|
405
|
+
return [this.out("artifact_delta", { id: payload.id, delta: payload.delta })];
|
|
406
|
+
case "artifact_update":
|
|
407
|
+
return [
|
|
408
|
+
this.out("artifact_update", { id: payload.id, component: payload.component, props: payload.props }),
|
|
409
|
+
];
|
|
410
|
+
case "artifact_complete":
|
|
411
|
+
return [this.out("artifact_complete", { id: payload.id })];
|
|
412
|
+
|
|
413
|
+
// ===== dropped (no legacy renderer) =====
|
|
414
|
+
case "source":
|
|
415
|
+
case "custom":
|
|
416
|
+
return [];
|
|
417
|
+
|
|
418
|
+
default:
|
|
419
|
+
return [];
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/** Build a legacy event, dropping undefined fields so the payload is clean. */
|
|
424
|
+
private out(payloadType: string, payload: Record<string, unknown>): LegacyEvent {
|
|
425
|
+
const pruned: Record<string, unknown> = {};
|
|
426
|
+
for (const [k, v] of Object.entries(payload)) {
|
|
427
|
+
if (v !== undefined) pruned[k] = v;
|
|
428
|
+
}
|
|
429
|
+
return { payloadType, payload: pruned };
|
|
430
|
+
}
|
|
431
|
+
}
|