@runtypelabs/sdk 4.19.2 → 4.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.
package/dist/index.mjs CHANGED
@@ -1,3 +1,503 @@
1
+ // src/unified-stream-adapter.ts
2
+ var UNIFIED_EVENTS_QUERY = "events=unified";
3
+ function withUnifiedEvents(path) {
4
+ return path.includes("?") ? `${path}&${UNIFIED_EVENTS_QUERY}` : `${path}?${UNIFIED_EVENTS_QUERY}`;
5
+ }
6
+ var UNIFIED_ONLY_EVENT_TYPES = /* @__PURE__ */ new Set([
7
+ "execution_start",
8
+ "execution_complete",
9
+ "execution_error",
10
+ "turn_start",
11
+ "turn_complete",
12
+ "text_start",
13
+ "text_delta",
14
+ "text_complete",
15
+ "reasoning_start",
16
+ "reasoning_delta",
17
+ "reasoning_complete",
18
+ "media_start",
19
+ "media_delta",
20
+ "media_complete",
21
+ "tool_output_delta",
22
+ "await"
23
+ ]);
24
+ function isUnifiedEventType(type) {
25
+ return typeof type === "string" && UNIFIED_ONLY_EVENT_TYPES.has(type);
26
+ }
27
+ function str(value) {
28
+ return typeof value === "string" ? value : void 0;
29
+ }
30
+ function num(value) {
31
+ return typeof value === "number" ? value : void 0;
32
+ }
33
+ function compact(event) {
34
+ const out = { type: event.type };
35
+ for (const [k, v] of Object.entries(event)) {
36
+ if (k !== "type" && v !== void 0) out[k] = v;
37
+ }
38
+ return out;
39
+ }
40
+ var LEGACY_FLOW_PASSTHROUGH = /* @__PURE__ */ new Set([
41
+ "flow_start",
42
+ "flow_complete",
43
+ "flow_error",
44
+ "flow_await",
45
+ "step_await",
46
+ "step_delta"
47
+ ]);
48
+ function createFlowEventTranslator() {
49
+ let currentStepId;
50
+ let currentStepName;
51
+ return (raw) => {
52
+ if (!raw || typeof raw !== "object") return [];
53
+ const data = raw;
54
+ const type = str(data.type);
55
+ if (!type) return [];
56
+ const executionId = str(data.executionId);
57
+ const seq = num(data.seq);
58
+ if (LEGACY_FLOW_PASSTHROUGH.has(type)) return [data];
59
+ switch (type) {
60
+ case "execution_start":
61
+ if (data.kind !== "flow") return [];
62
+ return [
63
+ compact({
64
+ type: "flow_start",
65
+ executionId,
66
+ seq,
67
+ flowId: data.flowId,
68
+ flowName: data.flowName,
69
+ totalSteps: data.totalSteps,
70
+ startedAt: data.startedAt,
71
+ source: data.source
72
+ })
73
+ ];
74
+ case "step_start": {
75
+ const id = str(data.id) ?? str(data.stepId);
76
+ const name = str(data.name) ?? str(data.stepName);
77
+ currentStepId = id;
78
+ currentStepName = name;
79
+ return [
80
+ compact({
81
+ type: "step_start",
82
+ executionId,
83
+ seq,
84
+ stepId: id,
85
+ name,
86
+ stepName: name,
87
+ stepType: data.stepType,
88
+ index: data.index,
89
+ totalSteps: data.totalSteps,
90
+ startedAt: data.startedAt,
91
+ outputVariable: data.outputVariable
92
+ })
93
+ ];
94
+ }
95
+ case "text_delta":
96
+ return [
97
+ compact({
98
+ type: "step_delta",
99
+ executionId,
100
+ seq,
101
+ delta: str(data.delta) ?? "",
102
+ text: str(data.delta) ?? "",
103
+ stepId: currentStepId,
104
+ name: currentStepName,
105
+ stepName: currentStepName
106
+ })
107
+ ];
108
+ case "step_complete": {
109
+ const id = str(data.id) ?? str(data.stepId);
110
+ const name = str(data.name) ?? str(data.stepName);
111
+ const out = compact({
112
+ type: "step_complete",
113
+ executionId,
114
+ seq,
115
+ stepId: id,
116
+ name,
117
+ stepName: name,
118
+ stepType: data.stepType,
119
+ result: data.result,
120
+ success: data.success,
121
+ executionTime: num(data.durationMs) ?? num(data.executionTime),
122
+ stopReason: data.stopReason,
123
+ completedAt: data.completedAt,
124
+ unresolvedVariables: data.unresolvedVariables,
125
+ error: data.error
126
+ });
127
+ currentStepId = void 0;
128
+ currentStepName = void 0;
129
+ return [out];
130
+ }
131
+ case "execution_complete":
132
+ if (data.kind !== "flow") return [];
133
+ return [
134
+ compact({
135
+ type: "flow_complete",
136
+ executionId,
137
+ seq,
138
+ success: data.success,
139
+ totalSteps: data.totalSteps,
140
+ successfulSteps: data.successfulSteps,
141
+ failedSteps: data.failedSteps,
142
+ executionTime: data.durationMs,
143
+ finalOutput: data.finalOutput,
144
+ completedAt: data.completedAt
145
+ })
146
+ ];
147
+ case "execution_error":
148
+ return [
149
+ compact({
150
+ type: "flow_error",
151
+ executionId,
152
+ seq,
153
+ error: data.error,
154
+ code: data.code,
155
+ upgradeUrl: data.upgradeUrl
156
+ })
157
+ ];
158
+ case "await":
159
+ return [
160
+ compact({
161
+ type: "flow_await",
162
+ executionId,
163
+ seq,
164
+ toolId: data.toolId ?? data.toolCallId,
165
+ toolName: data.toolName,
166
+ parameters: data.parameters,
167
+ awaitedAt: data.awaitedAt,
168
+ origin: data.origin,
169
+ pageOrigin: data.pageOrigin
170
+ })
171
+ ];
172
+ // Channels/markers/tool-family/approval/skip/source/custom/ping the flow
173
+ // consumer never read → no legacy event. (A non-terminal `error` is only
174
+ // produced on agent streams; dropping it here avoids a false flow_error.)
175
+ default:
176
+ return [];
177
+ }
178
+ };
179
+ }
180
+ function createAgentEventTranslator() {
181
+ let agentId;
182
+ let iteration;
183
+ let turnId;
184
+ let reasoningScope;
185
+ let media = null;
186
+ return (raw) => {
187
+ if (!raw || typeof raw !== "object") return [];
188
+ const data = raw;
189
+ const type = str(data.type);
190
+ if (!type) return [];
191
+ const executionId = str(data.executionId);
192
+ const seq = num(data.seq);
193
+ const iter = num(data.iteration) ?? iteration;
194
+ if (type.startsWith("agent_")) return [data];
195
+ switch (type) {
196
+ case "execution_start":
197
+ if (data.kind !== "agent") return [];
198
+ agentId = str(data.agentId);
199
+ return [
200
+ compact({
201
+ type: "agent_start",
202
+ executionId,
203
+ seq,
204
+ agentId: data.agentId,
205
+ agentName: data.agentName,
206
+ maxTurns: data.maxTurns,
207
+ startedAt: data.startedAt,
208
+ config: data.config
209
+ })
210
+ ];
211
+ case "turn_start":
212
+ turnId = str(data.id);
213
+ iteration = num(data.iteration) ?? iteration;
214
+ return [
215
+ compact({
216
+ type: "agent_turn_start",
217
+ executionId,
218
+ seq,
219
+ iteration: data.iteration ?? iteration,
220
+ turnId: data.id,
221
+ turnIndex: data.turnIndex,
222
+ role: data.role
223
+ })
224
+ ];
225
+ case "text_delta":
226
+ return [
227
+ compact({
228
+ type: "agent_turn_delta",
229
+ executionId,
230
+ seq,
231
+ iteration: iter,
232
+ turnId,
233
+ delta: str(data.delta) ?? "",
234
+ contentType: "text"
235
+ })
236
+ ];
237
+ case "reasoning_start":
238
+ reasoningScope = data.scope === "loop" ? "loop" : "turn";
239
+ return [];
240
+ case "reasoning_delta":
241
+ return [
242
+ compact({
243
+ type: "agent_turn_delta",
244
+ executionId,
245
+ seq,
246
+ iteration: iter,
247
+ turnId,
248
+ delta: str(data.delta) ?? "",
249
+ contentType: "thinking"
250
+ })
251
+ ];
252
+ case "reasoning_complete": {
253
+ const scope = data.scope === "loop" ? "loop" : reasoningScope;
254
+ reasoningScope = void 0;
255
+ if (scope === "loop") {
256
+ return [
257
+ compact({
258
+ type: "agent_reflection",
259
+ executionId,
260
+ seq,
261
+ iteration: iter,
262
+ reflection: data.text
263
+ })
264
+ ];
265
+ }
266
+ return [];
267
+ }
268
+ case "turn_complete":
269
+ return [
270
+ compact({
271
+ type: "agent_turn_complete",
272
+ executionId,
273
+ seq,
274
+ iteration: data.iteration ?? iter,
275
+ turnId: data.id,
276
+ role: data.role,
277
+ completedAt: data.completedAt,
278
+ content: data.content,
279
+ tokens: data.tokens,
280
+ cost: data.cost,
281
+ stopReason: data.stopReason
282
+ })
283
+ ];
284
+ case "tool_start":
285
+ return [
286
+ compact({
287
+ type: "agent_tool_start",
288
+ executionId,
289
+ seq,
290
+ iteration: iter,
291
+ toolCallId: data.toolCallId,
292
+ toolName: data.toolName,
293
+ toolType: data.toolType,
294
+ parameters: data.parameters,
295
+ origin: data.origin,
296
+ pageOrigin: data.pageOrigin
297
+ })
298
+ ];
299
+ case "tool_output_delta":
300
+ return [
301
+ compact({
302
+ type: "agent_tool_delta",
303
+ executionId,
304
+ seq,
305
+ iteration: iter,
306
+ toolCallId: data.toolCallId,
307
+ delta: str(data.delta) ?? ""
308
+ })
309
+ ];
310
+ case "tool_input_delta":
311
+ return [
312
+ compact({
313
+ type: "agent_tool_input_delta",
314
+ executionId,
315
+ seq,
316
+ iteration: iter,
317
+ toolCallId: data.toolCallId,
318
+ delta: str(data.delta) ?? ""
319
+ })
320
+ ];
321
+ case "tool_input_complete":
322
+ return [
323
+ compact({
324
+ type: "agent_tool_input_complete",
325
+ executionId,
326
+ seq,
327
+ iteration: iter,
328
+ toolCallId: data.toolCallId,
329
+ toolName: data.toolName,
330
+ parameters: data.parameters ?? {},
331
+ hiddenParameterNames: data.hiddenParameterNames
332
+ })
333
+ ];
334
+ case "tool_complete":
335
+ return [
336
+ compact({
337
+ type: "agent_tool_complete",
338
+ executionId,
339
+ seq,
340
+ iteration: num(data.iteration) ?? iter,
341
+ toolCallId: data.toolCallId,
342
+ toolName: data.toolName,
343
+ success: data.success,
344
+ result: data.result,
345
+ executionTime: data.executionTime
346
+ })
347
+ ];
348
+ case "media_start":
349
+ media = {
350
+ mediaType: str(data.mediaType),
351
+ role: str(data.role),
352
+ toolCallId: str(data.toolCallId),
353
+ chunks: []
354
+ };
355
+ return [];
356
+ case "media_delta": {
357
+ const delta = str(data.delta);
358
+ if (media && delta) media.chunks.push(delta);
359
+ return [];
360
+ }
361
+ case "media_complete": {
362
+ const acc = media;
363
+ media = null;
364
+ const mediaType = str(data.mediaType) ?? acc?.mediaType;
365
+ const toolCallId = str(data.toolCallId) ?? acc?.toolCallId;
366
+ const url = str(data.url);
367
+ const inlineData = str(data.data) ?? (acc && acc.chunks.length ? acc.chunks.join("") : void 0);
368
+ let item;
369
+ if (url) {
370
+ const kind = mediaType && mediaType.startsWith("image/") ? "image-url" : "file-url";
371
+ item = compact({ type: kind, url, mediaType });
372
+ } else {
373
+ item = compact({ type: "media", data: inlineData ?? "", mediaType });
374
+ }
375
+ return [
376
+ compact({
377
+ type: "agent_media",
378
+ executionId,
379
+ seq,
380
+ iteration: iter,
381
+ toolCallId,
382
+ media: [item]
383
+ })
384
+ ];
385
+ }
386
+ case "approval_start":
387
+ return [
388
+ compact({
389
+ type: "agent_approval_start",
390
+ executionId,
391
+ seq,
392
+ iteration: iter,
393
+ approvalId: data.approvalId,
394
+ toolCallId: data.toolCallId,
395
+ toolName: data.toolName,
396
+ toolType: data.toolType,
397
+ description: data.description,
398
+ reason: data.reason,
399
+ parameters: data.parameters,
400
+ timeout: data.timeout,
401
+ startedAt: data.startedAt
402
+ })
403
+ ];
404
+ case "approval_complete":
405
+ return [
406
+ compact({
407
+ type: "agent_approval_complete",
408
+ executionId,
409
+ seq,
410
+ approvalId: data.approvalId,
411
+ decision: data.decision,
412
+ completedAt: data.completedAt,
413
+ resolvedBy: data.resolvedBy
414
+ })
415
+ ];
416
+ case "await":
417
+ return [
418
+ compact({
419
+ type: "agent_await",
420
+ executionId,
421
+ seq,
422
+ toolId: data.toolId ?? data.toolCallId,
423
+ toolName: data.toolName,
424
+ parameters: data.parameters,
425
+ awaitedAt: data.awaitedAt,
426
+ origin: data.origin,
427
+ pageOrigin: data.pageOrigin
428
+ })
429
+ ];
430
+ case "execution_complete":
431
+ if (data.kind !== "agent") return [];
432
+ return [
433
+ compact({
434
+ type: "agent_complete",
435
+ executionId,
436
+ seq,
437
+ agentId: data.agentId ?? agentId,
438
+ success: data.success,
439
+ iterations: data.iterations,
440
+ stopReason: data.stopReason,
441
+ completedAt: data.completedAt,
442
+ totalCost: data.totalCost,
443
+ totalTokens: data.totalTokens,
444
+ finalOutput: data.finalOutput,
445
+ duration: data.durationMs
446
+ })
447
+ ];
448
+ case "execution_error":
449
+ return [
450
+ compact({
451
+ type: "agent_complete",
452
+ executionId,
453
+ seq,
454
+ agentId,
455
+ success: false,
456
+ stopReason: "error",
457
+ error: errorMessage(data.error),
458
+ completedAt: data.completedAt
459
+ })
460
+ ];
461
+ case "error":
462
+ return [
463
+ compact({
464
+ type: "agent_error",
465
+ executionId,
466
+ seq,
467
+ iteration: iter,
468
+ error: errorObject(data.error),
469
+ recoverable: true
470
+ })
471
+ ];
472
+ case "ping":
473
+ return [compact({ type: "agent_ping", executionId, seq, timestamp: data.timestamp })];
474
+ // turn/text/reasoning open+close markers, step_*, artifact_*, source,
475
+ // custom (fallback beat / routing), and the skill-fold result envelope
476
+ // the SDK never consumed → no legacy event.
477
+ default:
478
+ return [];
479
+ }
480
+ };
481
+ }
482
+ function errorMessage(error) {
483
+ if (typeof error === "string") return error;
484
+ if (error && typeof error === "object" && "message" in error) {
485
+ const message = error.message;
486
+ if (typeof message === "string") return message;
487
+ }
488
+ return error === void 0 ? "Agent execution failed" : JSON.stringify(error);
489
+ }
490
+ function errorObject(error) {
491
+ if (typeof error === "string") return { code: "error", message: error };
492
+ if (error && typeof error === "object") {
493
+ const e = error;
494
+ const obj = { code: e.code ?? "error", message: e.message ?? "" };
495
+ if (e.details !== void 0) obj.details = e.details;
496
+ return obj;
497
+ }
498
+ return { code: "error", message: String(error) };
499
+ }
500
+
1
501
  // src/stream-utils.ts
2
502
  function parseSSEChunk(chunk, buffer) {
3
503
  buffer += chunk;
@@ -35,7 +535,7 @@ function parseFinalBuffer(buffer) {
35
535
  }
36
536
  return null;
37
537
  }
38
- async function processStream(response, callbacks = {}) {
538
+ async function processStream(response, callbacks = {}, options = {}) {
39
539
  if (!response.ok) {
40
540
  const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
41
541
  callbacks.onError?.(error);
@@ -59,6 +559,20 @@ async function processStream(response, callbacks = {}) {
59
559
  executionTime: 0,
60
560
  success: true
61
561
  };
562
+ let translate = options.unified === true ? createFlowEventTranslator() : null;
563
+ const autoDetect = options.unified === void 0;
564
+ const dispatch = (parsed) => {
565
+ if (autoDetect && !translate && isUnifiedEventType(parsed?.type)) {
566
+ translate = createFlowEventTranslator();
567
+ }
568
+ if (translate) {
569
+ for (const ev of translate(parsed)) {
570
+ handleEvent(ev, callbacks, results, flowSummary);
571
+ }
572
+ } else {
573
+ handleEvent(parsed, callbacks, results, flowSummary);
574
+ }
575
+ };
62
576
  const contentType = response.headers.get("content-type");
63
577
  if (contentType?.includes("application/json")) {
64
578
  try {
@@ -115,8 +629,7 @@ async function processStream(response, callbacks = {}) {
115
629
  buffer = remainingBuffer;
116
630
  for (const eventStr of events) {
117
631
  try {
118
- const event = JSON.parse(eventStr);
119
- handleEvent(event, callbacks, results, flowSummary);
632
+ dispatch(JSON.parse(eventStr));
120
633
  } catch {
121
634
  console.warn("Failed to parse SSE event:", eventStr);
122
635
  }
@@ -125,8 +638,7 @@ async function processStream(response, callbacks = {}) {
125
638
  const finalEvent = parseFinalBuffer(buffer);
126
639
  if (finalEvent) {
127
640
  try {
128
- const event = JSON.parse(finalEvent);
129
- handleEvent(event, callbacks, results, flowSummary);
641
+ dispatch(JSON.parse(finalEvent));
130
642
  } catch {
131
643
  }
132
644
  }
@@ -196,10 +708,17 @@ function handleEvent(event, callbacks, results, summary) {
196
708
  break;
197
709
  }
198
710
  }
199
- async function* streamEvents(response) {
711
+ async function* streamEvents(response, options = {}) {
200
712
  if (!response.ok) {
201
713
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
202
714
  }
715
+ let translate = options.unified === true ? createFlowEventTranslator() : null;
716
+ const autoDetect = options.unified === void 0;
717
+ const maybeDetect = (parsed) => {
718
+ if (autoDetect && !translate && isUnifiedEventType(parsed?.type)) {
719
+ translate = createFlowEventTranslator();
720
+ }
721
+ };
203
722
  const contentType = response.headers.get("content-type");
204
723
  if (contentType?.includes("application/json")) {
205
724
  try {
@@ -229,19 +748,35 @@ async function* streamEvents(response) {
229
748
  const { events, remainingBuffer } = parseSSEChunk(chunk, buffer);
230
749
  buffer = remainingBuffer;
231
750
  for (const eventStr of events) {
751
+ let parsed;
232
752
  try {
233
- const event = JSON.parse(eventStr);
234
- yield event;
753
+ parsed = JSON.parse(eventStr);
235
754
  } catch {
755
+ continue;
756
+ }
757
+ maybeDetect(parsed);
758
+ if (translate) {
759
+ for (const ev of translate(parsed)) yield ev;
760
+ } else {
761
+ yield parsed;
236
762
  }
237
763
  }
238
764
  }
239
765
  const finalEvent = parseFinalBuffer(buffer);
240
766
  if (finalEvent) {
767
+ let parsed;
241
768
  try {
242
- const event = JSON.parse(finalEvent);
243
- yield event;
769
+ parsed = JSON.parse(finalEvent);
244
770
  } catch {
771
+ parsed = void 0;
772
+ }
773
+ if (parsed !== void 0) {
774
+ maybeDetect(parsed);
775
+ if (translate) {
776
+ for (const ev of translate(parsed)) yield ev;
777
+ } else {
778
+ yield parsed;
779
+ }
245
780
  }
246
781
  }
247
782
  } finally {
@@ -251,10 +786,16 @@ async function* streamEvents(response) {
251
786
 
252
787
  // src/flow-result.ts
253
788
  var FlowResult = class {
254
- constructor(response, summary) {
789
+ /**
790
+ * @param options.unified - The response is a unified-vocabulary SSE stream
791
+ * (`/dispatch` since the unified-SSE cutover). Set by dispatch call sites;
792
+ * left unset by still-legacy producers (`/prompts/{id}/run`, eval stream).
793
+ */
794
+ constructor(response, summary, options = {}) {
255
795
  this.consumed = false;
256
796
  this.cachedSummary = null;
257
797
  this.response = response;
798
+ this.streamOptions = options;
258
799
  if (summary) {
259
800
  this.cachedSummary = summary;
260
801
  this.consumed = true;
@@ -307,7 +848,7 @@ var FlowResult = class {
307
848
  }
308
849
  this.ensureNotConsumed();
309
850
  this.consumed = true;
310
- this.cachedSummary = await processStream(this.response, callbacks);
851
+ this.cachedSummary = await processStream(this.response, callbacks, this.streamOptions);
311
852
  return this.cachedSummary;
312
853
  }
313
854
  /**
@@ -1153,9 +1694,9 @@ var FlowBuilder = class {
1153
1694
  }
1154
1695
  const response = await client.dispatch(config);
1155
1696
  if (callbacks) {
1156
- return processStream(response, callbacks);
1697
+ return processStream(response, callbacks, { unified: true });
1157
1698
  }
1158
- return new FlowResult(response);
1699
+ return new FlowResult(response, void 0, { unified: true });
1159
1700
  }
1160
1701
  /**
1161
1702
  * Set a run condition (when predicate) on the last added step.
@@ -1287,9 +1828,9 @@ var ClientFlowBuilder = class extends FlowBuilder {
1287
1828
  }
1288
1829
  const response = await dispatchClient.dispatch(config);
1289
1830
  if (runCallbacks) {
1290
- return processStream(response, runCallbacks);
1831
+ return processStream(response, runCallbacks, { unified: true });
1291
1832
  }
1292
- return new FlowResult(response);
1833
+ return new FlowResult(response, void 0, { unified: true });
1293
1834
  }
1294
1835
  };
1295
1836
  function isStreamCallbacks(obj) {
@@ -2307,7 +2848,7 @@ var RuntypeFlowBuilder = class {
2307
2848
  config.options = { ...config.options, streamResponse: true };
2308
2849
  const client = this.getClient();
2309
2850
  const response = await this.dispatchWithPersistedFlow(client, config);
2310
- const result = new FlowResult(response);
2851
+ const result = new FlowResult(response, void 0, { unified: true });
2311
2852
  if (callbacks) {
2312
2853
  await result.stream(callbacks);
2313
2854
  }
@@ -2336,7 +2877,7 @@ var RuntypeFlowBuilder = class {
2336
2877
  config.options = { ...config.options, streamResponse: true };
2337
2878
  const client = this.getClient();
2338
2879
  const response = await this.dispatchWithPersistedFlow(client, config);
2339
- const result = new FlowResult(response);
2880
+ const result = new FlowResult(response, void 0, { unified: true });
2340
2881
  await result.getSummary();
2341
2882
  return result;
2342
2883
  }
@@ -2371,7 +2912,7 @@ var RuntypeFlowBuilder = class {
2371
2912
  onError: (error) => callbacks?.onError?.(error)
2372
2913
  };
2373
2914
  try {
2374
- for await (const event of streamEvents(response)) {
2915
+ for await (const event of streamEvents(response, { unified: true })) {
2375
2916
  collectLocalToolAwait(pausedTools, event);
2376
2917
  switch (event.type) {
2377
2918
  case "flow_start":
@@ -2479,7 +3020,7 @@ var RuntypeFlowBuilder = class {
2479
3020
  streamResponse: isStreaming
2480
3021
  };
2481
3022
  if (isStreaming) {
2482
- currentResponse = await client.requestStream("/dispatch/resume", {
3023
+ currentResponse = await client.requestStream(withUnifiedEvents("/dispatch/resume"), {
2483
3024
  method: "POST",
2484
3025
  body: JSON.stringify(resumeData)
2485
3026
  });
@@ -4270,13 +4811,17 @@ function defineFpo(fpo) {
4270
4811
  return fpo;
4271
4812
  }
4272
4813
  async function ensureFpo(client, fpo, options = {}) {
4273
- const { dryRun, onConflict } = options;
4814
+ const { dryRun, onConflict, prune } = options;
4274
4815
  return client.post("/products/ensure-fpo", {
4275
4816
  fpo,
4276
4817
  ...dryRun ? { dryRun: true } : {},
4277
- ...onConflict ? { onConflict } : {}
4818
+ ...onConflict ? { onConflict } : {},
4819
+ ...prune ? { prune: true } : {}
4278
4820
  });
4279
4821
  }
4822
+ async function pullFpo(client, name) {
4823
+ return client.get("/products/pull-fpo", { name });
4824
+ }
4280
4825
 
4281
4826
  // src/products-namespace.ts
4282
4827
  var ProductsNamespace = class {
@@ -4334,6 +4879,24 @@ var ProductsNamespace = class {
4334
4879
  async ensureFpo(fpo, options = {}) {
4335
4880
  return ensureFpo(this.getClient(), fpo, options);
4336
4881
  }
4882
+ /**
4883
+ * Reconstruct a self-contained Full Product Object from the live product graph
4884
+ * (resolved by name) — the absorb-drift direction of `ensureFpo`. Feeding the
4885
+ * returned `fpo` back into `ensureFpo` converges to `unchanged`.
4886
+ *
4887
+ * records/schedules/secrets are not reconstructed and `tools` is emitted empty
4888
+ * (tool refs are portable `tool:<name>`); inspect `result.warnings` for
4889
+ * per-pull caveats.
4890
+ *
4891
+ * @example
4892
+ * ```typescript
4893
+ * const { fpo } = await Runtype.products.pullFpo('Support Copilot')
4894
+ * await Runtype.products.ensureFpo(fpo, { dryRun: true }) // → result: 'unchanged'
4895
+ * ```
4896
+ */
4897
+ async pullFpo(name) {
4898
+ return pullFpo(this.getClient(), name);
4899
+ }
4337
4900
  };
4338
4901
 
4339
4902
  // src/surfaces-ensure.ts
@@ -4701,7 +5264,7 @@ var RuntypeClient = class {
4701
5264
  * Dispatch flow execution (streaming)
4702
5265
  */
4703
5266
  async dispatch(config) {
4704
- return this.requestStream("/dispatch", {
5267
+ return this.requestStream(withUnifiedEvents("/dispatch"), {
4705
5268
  method: "POST",
4706
5269
  body: JSON.stringify(transformRequest(config))
4707
5270
  });
@@ -5072,7 +5635,7 @@ var Runtype = class {
5072
5635
 
5073
5636
  // src/version.ts
5074
5637
  var FALLBACK_VERSION = "0.0.0";
5075
- var SDK_VERSION = "4.19.2".length > 0 ? "4.19.2" : FALLBACK_VERSION;
5638
+ var SDK_VERSION = "4.21.0".length > 0 ? "4.21.0" : FALLBACK_VERSION;
5076
5639
  var RUNTYPE_CLIENT_KIND = "sdk";
5077
5640
  var SDK_USER_AGENT = `runtype-sdk/${SDK_VERSION} (typescript)`;
5078
5641
 
@@ -7566,7 +8129,7 @@ var DispatchEndpoint = class {
7566
8129
  * Dispatch with streaming response
7567
8130
  */
7568
8131
  async executeStream(data) {
7569
- return this.client.requestStream("/dispatch", {
8132
+ return this.client.requestStream(withUnifiedEvents("/dispatch"), {
7570
8133
  method: "POST",
7571
8134
  body: JSON.stringify(data)
7572
8135
  });
@@ -7582,7 +8145,7 @@ var DispatchEndpoint = class {
7582
8145
  */
7583
8146
  async resume(data) {
7584
8147
  if (data.streamResponse) {
7585
- return this.client.requestStream("/dispatch/resume", {
8148
+ return this.client.requestStream(withUnifiedEvents("/dispatch/resume"), {
7586
8149
  method: "POST",
7587
8150
  body: JSON.stringify(data)
7588
8151
  });
@@ -8097,6 +8660,19 @@ async function processAgentStream(body, callbacks) {
8097
8660
  const reader = body.getReader();
8098
8661
  const decoder = new TextDecoder();
8099
8662
  let buffer = "";
8663
+ let translate = null;
8664
+ const dispatch = (event) => {
8665
+ if (!translate && isUnifiedEventType(event.data?.type ?? event.eventType)) {
8666
+ translate = createAgentEventTranslator();
8667
+ }
8668
+ if (translate) {
8669
+ for (const legacy of translate(event.data)) {
8670
+ dispatchAgentEvent({ eventType: legacy.type, data: legacy }, callbacks);
8671
+ }
8672
+ } else {
8673
+ dispatchAgentEvent(event, callbacks);
8674
+ }
8675
+ };
8100
8676
  try {
8101
8677
  while (true) {
8102
8678
  const { done, value } = await reader.read();
@@ -8104,7 +8680,7 @@ async function processAgentStream(body, callbacks) {
8104
8680
  if (buffer.trim()) {
8105
8681
  const { events: events2 } = parseSSEChunkWithEventType(buffer + "\n\n", "");
8106
8682
  for (const event of events2) {
8107
- dispatchAgentEvent(event, callbacks);
8683
+ dispatch(event);
8108
8684
  }
8109
8685
  }
8110
8686
  break;
@@ -8113,7 +8689,7 @@ async function processAgentStream(body, callbacks) {
8113
8689
  const { events, remainingBuffer } = parseSSEChunkWithEventType(chunk, buffer);
8114
8690
  buffer = remainingBuffer;
8115
8691
  for (const event of events) {
8116
- dispatchAgentEvent(event, callbacks);
8692
+ dispatch(event);
8117
8693
  }
8118
8694
  }
8119
8695
  } finally {
@@ -8301,7 +8877,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8301
8877
  * ```
8302
8878
  */
8303
8879
  async executeStream(id, data, init) {
8304
- return this.client.requestStream(`/agents/${id}/execute`, {
8880
+ return this.client.requestStream(withUnifiedEvents(`/agents/${id}/execute`), {
8305
8881
  method: "POST",
8306
8882
  body: JSON.stringify({
8307
8883
  ...data,
@@ -8653,16 +9229,19 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8653
9229
  }
8654
9230
  let resumeResponse;
8655
9231
  try {
8656
- resumeResponse = await this.client.requestStream(`/agents/${id}/resume`, {
8657
- method: "POST",
8658
- body: JSON.stringify({
8659
- executionId,
8660
- toolOutputs: { [toolName]: toolResult },
8661
- streamResponse: true,
8662
- debugMode: data.debugMode
8663
- }),
8664
- ...abortSignal ? { signal: abortSignal } : {}
8665
- });
9232
+ resumeResponse = await this.client.requestStream(
9233
+ withUnifiedEvents(`/agents/${id}/resume`),
9234
+ {
9235
+ method: "POST",
9236
+ body: JSON.stringify({
9237
+ executionId,
9238
+ toolOutputs: { [toolName]: toolResult },
9239
+ streamResponse: true,
9240
+ debugMode: data.debugMode
9241
+ }),
9242
+ ...abortSignal ? { signal: abortSignal } : {}
9243
+ }
9244
+ );
8666
9245
  } catch (error) {
8667
9246
  if (abortSignal?.aborted) return finishAborted(executionId);
8668
9247
  throw error;
@@ -10127,9 +10706,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
10127
10706
  }
10128
10707
  /** Returns true if a server-side session error message indicates a transient
10129
10708
  * network failure that is safe to retry. */
10130
- static isRetryableSessionError(errorMessage) {
10131
- if (!errorMessage) return false;
10132
- const lower = errorMessage.toLowerCase();
10709
+ static isRetryableSessionError(errorMessage2) {
10710
+ if (!errorMessage2) return false;
10711
+ const lower = errorMessage2.toLowerCase();
10133
10712
  return _AgentsEndpoint.RETRYABLE_SESSION_ERROR_PATTERNS.some(
10134
10713
  (pattern) => lower.includes(pattern)
10135
10714
  );
@@ -11581,7 +12160,7 @@ var RuntypeClient2 = class {
11581
12160
  success: true
11582
12161
  };
11583
12162
  try {
11584
- for await (const event of streamEvents(response)) {
12163
+ for await (const event of streamEvents(response, { unified: true })) {
11585
12164
  collectLocalToolAwait(pausedTools, event);
11586
12165
  switch (event.type) {
11587
12166
  case "flow_start":
@@ -12764,6 +13343,7 @@ export {
12764
13343
  ToolEnsureConflictError,
12765
13344
  ToolsEndpoint,
12766
13345
  ToolsNamespace,
13346
+ UNIFIED_EVENTS_QUERY,
12767
13347
  UsersEndpoint,
12768
13348
  applyGeneratedRuntimeToolProposalToDispatchRequest,
12769
13349
  attachRuntimeToolsToDispatchRequest,
@@ -12780,8 +13360,10 @@ export {
12780
13360
  computeSkillContentHash,
12781
13361
  computeSurfaceContentHash,
12782
13362
  computeToolContentHash,
13363
+ createAgentEventTranslator,
12783
13364
  createClient,
12784
13365
  createExternalTool,
13366
+ createFlowEventTranslator,
12785
13367
  defaultWorkflow,
12786
13368
  defaultWorkflowConfig,
12787
13369
  defineAgent,
@@ -12804,6 +13386,7 @@ export {
12804
13386
  isDiscoveryToolName,
12805
13387
  isMarathonArtifactPath,
12806
13388
  isPreservationSensitiveTask,
13389
+ isUnifiedEventType,
12807
13390
  isWorkflowHookRef,
12808
13391
  listWorkflowHooks,
12809
13392
  normalizeAgentDefinition,
@@ -12818,6 +13401,7 @@ export {
12818
13401
  parseOffloadedOutputId,
12819
13402
  parseSSEChunk,
12820
13403
  processStream,
13404
+ pullFpo,
12821
13405
  registerWorkflowHook,
12822
13406
  resolveStallStopAfter,
12823
13407
  resolveWorkflowHook,
@@ -12825,5 +13409,6 @@ export {
12825
13409
  shouldInjectEmptySessionNudge,
12826
13410
  shouldRequestModelEscalation,
12827
13411
  streamEvents,
12828
- unregisterWorkflowHook
13412
+ unregisterWorkflowHook,
13413
+ withUnifiedEvents
12829
13414
  };