@tanstack/ai-react 0.18.0 → 0.19.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.
Files changed (49) hide show
  1. package/README.md +15 -1
  2. package/dist/esm/index.d.ts +1 -1
  3. package/dist/esm/index.js +2 -23
  4. package/dist/esm/mcp-app-resource.js +45 -45
  5. package/dist/esm/mcp-app-resource.js.map +1 -1
  6. package/dist/esm/mcp-apps.js +1 -4
  7. package/dist/esm/types.d.ts +29 -3
  8. package/dist/esm/use-audio-recorder.js +41 -46
  9. package/dist/esm/use-audio-recorder.js.map +1 -1
  10. package/dist/esm/use-chat.js +330 -275
  11. package/dist/esm/use-chat.js.map +1 -1
  12. package/dist/esm/use-generate-audio.d.ts +50 -4
  13. package/dist/esm/use-generate-audio.js +47 -23
  14. package/dist/esm/use-generate-audio.js.map +1 -1
  15. package/dist/esm/use-generate-image.d.ts +50 -4
  16. package/dist/esm/use-generate-image.js +49 -23
  17. package/dist/esm/use-generate-image.js.map +1 -1
  18. package/dist/esm/use-generate-speech.d.ts +50 -4
  19. package/dist/esm/use-generate-speech.js +43 -23
  20. package/dist/esm/use-generate-speech.js.map +1 -1
  21. package/dist/esm/use-generate-video.d.ts +50 -4
  22. package/dist/esm/use-generate-video.js +141 -103
  23. package/dist/esm/use-generate-video.js.map +1 -1
  24. package/dist/esm/use-generation.d.ts +59 -6
  25. package/dist/esm/use-generation.js +113 -90
  26. package/dist/esm/use-generation.js.map +1 -1
  27. package/dist/esm/use-mcp-app-bridge.js +46 -23
  28. package/dist/esm/use-mcp-app-bridge.js.map +1 -1
  29. package/dist/esm/use-realtime-chat.js +185 -185
  30. package/dist/esm/use-realtime-chat.js.map +1 -1
  31. package/dist/esm/use-summarize.d.ts +50 -4
  32. package/dist/esm/use-summarize.js +46 -23
  33. package/dist/esm/use-summarize.js.map +1 -1
  34. package/dist/esm/use-transcription.d.ts +50 -4
  35. package/dist/esm/use-transcription.js +51 -20
  36. package/dist/esm/use-transcription.js.map +1 -1
  37. package/package.json +7 -7
  38. package/src/index.ts +10 -0
  39. package/src/types.ts +44 -0
  40. package/src/use-chat.ts +221 -35
  41. package/src/use-generate-audio.ts +64 -17
  42. package/src/use-generate-image.ts +64 -17
  43. package/src/use-generate-speech.ts +64 -17
  44. package/src/use-generate-video.ts +99 -18
  45. package/src/use-generation.ts +112 -18
  46. package/src/use-summarize.ts +64 -17
  47. package/src/use-transcription.ts +60 -18
  48. package/dist/esm/index.js.map +0 -1
  49. package/dist/esm/mcp-apps.js.map +0 -1
@@ -1,10 +1,12 @@
1
1
  import { useGeneration } from './use-generation'
2
+ import { reconstructImageResult } from '@tanstack/ai-client'
2
3
  import type { ImageGenerationResult, StreamChunk } from '@tanstack/ai'
3
4
  import type {
4
5
  AIDevtoolsDisplayOptions,
5
6
  ConnectConnectionAdapter,
6
7
  GenerationClientState,
7
8
  GenerationFetcher,
9
+ GenerationPersistenceOptions,
8
10
  ImageGenerateInput,
9
11
  InferGenerationOutputFromReturn,
10
12
  } from '@tanstack/ai-client'
@@ -19,12 +21,51 @@ export interface UseGenerateImageOptions<TOutput = ImageGenerationResult> {
19
21
  connection?: ConnectConnectionAdapter
20
22
  /** Direct async function for image generation */
21
23
  fetcher?: GenerationFetcher<ImageGenerateInput, ImageGenerationResult>
22
- /** Unique identifier for this generation instance */
24
+ /**
25
+ * @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
26
+ */
23
27
  id?: string
24
28
  /** Additional body parameters to send with connect-based adapter requests */
25
29
  body?: Record<string, any>
26
30
  /** Display options for TanStack AI Devtools. */
27
31
  devtools?: AIDevtoolsDisplayOptions
32
+ /**
33
+ * How this generation persists across reloads.
34
+ * - Omit / `false`: ephemeral, in-memory only.
35
+ * - `true`: server-driven — on mount the client hydrates the last generation
36
+ * for its `threadId` from the server (needs a connection with a
37
+ * `hydrateGeneration` handler) and repaints it; it never auto-starts a run.
38
+ */
39
+ persistence?: boolean
40
+ /**
41
+ * The **scope** this generation belongs to: a stable, app-chosen name for the
42
+ * slot successive runs fill — not a link to a chat conversation.
43
+ *
44
+ * The hook starts empty and produces many runs over its life; each gets its
45
+ * own `runId`, but all belong to one scope. Persistence keys on this, so
46
+ * derive it from your own domain and keep it identical across reloads (e.g.
47
+ * `` `video-${videoId}-start-frame` ``). It is also sent as the AG-UI thread
48
+ * id on the wire, which the protocol requires.
49
+ *
50
+ * **Required whenever `persistence` is set** — an app that cannot name the
51
+ * scope has nothing to restore to. Optional for ephemeral generations, where
52
+ * it falls back to `id` purely to satisfy the wire.
53
+ */
54
+ threadId?: string
55
+ /**
56
+ * Server-driven hydration handler for `persistence: true` when the
57
+ * connection doesn't carry one (e.g. alongside `fetcher`, or a `stream()` /
58
+ * `rpcStream()` adapter built without handlers) — typically a one-line
59
+ * server-function call. The connection's own handler takes precedence.
60
+ */
61
+ hydrateGeneration?: ConnectConnectionAdapter['hydrateGeneration']
62
+ /**
63
+ * Re-attach handler that replays a run still generating to completion on
64
+ * mount, when the connection doesn't carry one. Without it, a restored
65
+ * `running` snapshot surfaces as an (interrupted) error. The connection's
66
+ * own handler takes precedence.
67
+ */
68
+ joinRun?: ConnectConnectionAdapter['joinRun']
28
69
  /**
29
70
  * Callback when images are generated. Can optionally return a transformed value.
30
71
  *
@@ -61,6 +102,13 @@ export interface UseGenerateImageReturn<TOutput = ImageGenerationResult> {
61
102
  stop: () => void
62
103
  /** Clear result, error, and return to idle */
63
104
  reset: () => void
105
+ /**
106
+ * The id of the generation job currently running, or `null` when nothing is in
107
+ * flight. Each call to `generate` is one job with its own id. Pass it to your
108
+ * own endpoint to cancel or poll the provider job — `stop()` only aborts the
109
+ * local stream, it does not stop work already running on the provider.
110
+ */
111
+ runId: string | null
64
112
  }
65
113
 
66
114
  /**
@@ -96,9 +144,12 @@ export interface UseGenerateImageReturn<TOutput = ImageGenerationResult> {
96
144
  * ```
97
145
  */
98
146
  export function useGenerateImage<TTransformed = void>(
99
- options: Omit<UseGenerateImageOptions, 'onResult'> & {
147
+ options: Omit<
148
+ UseGenerateImageOptions,
149
+ 'onResult' | 'persistence' | 'threadId' | 'id'
150
+ > & {
100
151
  onResult?: (result: ImageGenerationResult) => TTransformed
101
- },
152
+ } & GenerationPersistenceOptions,
102
153
  ): UseGenerateImageReturn<
103
154
  InferGenerationOutputFromReturn<ImageGenerationResult, TTransformed>
104
155
  > {
@@ -108,19 +159,15 @@ export function useGenerateImage<TTransformed = void>(
108
159
  hookName: 'useGenerateImage',
109
160
  outputKind: 'image' as const,
110
161
  }
111
- const { generate, result, isLoading, error, status, stop, reset } =
112
- useGeneration<ImageGenerateInput, ImageGenerationResult, TTransformed>({
113
- ...options,
114
- devtools,
115
- })
162
+ const generation = useGeneration<
163
+ ImageGenerateInput,
164
+ ImageGenerationResult,
165
+ TTransformed
166
+ >({
167
+ ...options,
168
+ devtools,
169
+ reconstructResult: reconstructImageResult,
170
+ })
116
171
 
117
- return {
118
- generate: generate as (input: ImageGenerateInput) => Promise<void>,
119
- result,
120
- isLoading,
121
- error,
122
- status,
123
- stop,
124
- reset,
125
- }
172
+ return generation
126
173
  }
@@ -1,10 +1,12 @@
1
1
  import { useGeneration } from './use-generation'
2
+ import { reconstructSpeechResult } from '@tanstack/ai-client'
2
3
  import type { StreamChunk, TTSResult } from '@tanstack/ai'
3
4
  import type {
4
5
  AIDevtoolsDisplayOptions,
5
6
  ConnectConnectionAdapter,
6
7
  GenerationClientState,
7
8
  GenerationFetcher,
9
+ GenerationPersistenceOptions,
8
10
  InferGenerationOutputFromReturn,
9
11
  SpeechGenerateInput,
10
12
  } from '@tanstack/ai-client'
@@ -19,12 +21,51 @@ export interface UseGenerateSpeechOptions<TOutput = TTSResult> {
19
21
  connection?: ConnectConnectionAdapter
20
22
  /** Direct async function for speech generation */
21
23
  fetcher?: GenerationFetcher<SpeechGenerateInput, TTSResult>
22
- /** Unique identifier for this generation instance */
24
+ /**
25
+ * @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
26
+ */
23
27
  id?: string
24
28
  /** Additional body parameters to send with connect-based adapter requests */
25
29
  body?: Record<string, any>
26
30
  /** Display options for TanStack AI Devtools. */
27
31
  devtools?: AIDevtoolsDisplayOptions
32
+ /**
33
+ * How this generation persists across reloads.
34
+ * - Omit / `false`: ephemeral, in-memory only.
35
+ * - `true`: server-driven — on mount the client hydrates the last generation
36
+ * for its `threadId` from the server (needs a connection with a
37
+ * `hydrateGeneration` handler) and repaints it; it never auto-starts a run.
38
+ */
39
+ persistence?: boolean
40
+ /**
41
+ * The **scope** this generation belongs to: a stable, app-chosen name for the
42
+ * slot successive runs fill — not a link to a chat conversation.
43
+ *
44
+ * The hook starts empty and produces many runs over its life; each gets its
45
+ * own `runId`, but all belong to one scope. Persistence keys on this, so
46
+ * derive it from your own domain and keep it identical across reloads (e.g.
47
+ * `` `video-${videoId}-start-frame` ``). It is also sent as the AG-UI thread
48
+ * id on the wire, which the protocol requires.
49
+ *
50
+ * **Required whenever `persistence` is set** — an app that cannot name the
51
+ * scope has nothing to restore to. Optional for ephemeral generations, where
52
+ * it falls back to `id` purely to satisfy the wire.
53
+ */
54
+ threadId?: string
55
+ /**
56
+ * Server-driven hydration handler for `persistence: true` when the
57
+ * connection doesn't carry one (e.g. alongside `fetcher`, or a `stream()` /
58
+ * `rpcStream()` adapter built without handlers) — typically a one-line
59
+ * server-function call. The connection's own handler takes precedence.
60
+ */
61
+ hydrateGeneration?: ConnectConnectionAdapter['hydrateGeneration']
62
+ /**
63
+ * Re-attach handler that replays a run still generating to completion on
64
+ * mount, when the connection doesn't carry one. Without it, a restored
65
+ * `running` snapshot surfaces as an (interrupted) error. The connection's
66
+ * own handler takes precedence.
67
+ */
68
+ joinRun?: ConnectConnectionAdapter['joinRun']
28
69
  /**
29
70
  * Callback when speech is generated. Can optionally return a transformed value.
30
71
  *
@@ -61,6 +102,13 @@ export interface UseGenerateSpeechReturn<TOutput = TTSResult> {
61
102
  stop: () => void
62
103
  /** Clear result, error, and return to idle */
63
104
  reset: () => void
105
+ /**
106
+ * The id of the generation job currently running, or `null` when nothing is in
107
+ * flight. Each call to `generate` is one job with its own id. Pass it to your
108
+ * own endpoint to cancel or poll the provider job — `stop()` only aborts the
109
+ * local stream, it does not stop work already running on the provider.
110
+ */
111
+ runId: string | null
64
112
  }
65
113
 
66
114
  /**
@@ -90,9 +138,12 @@ export interface UseGenerateSpeechReturn<TOutput = TTSResult> {
90
138
  * ```
91
139
  */
92
140
  export function useGenerateSpeech<TTransformed = void>(
93
- options: Omit<UseGenerateSpeechOptions, 'onResult'> & {
141
+ options: Omit<
142
+ UseGenerateSpeechOptions,
143
+ 'onResult' | 'persistence' | 'threadId' | 'id'
144
+ > & {
94
145
  onResult?: (result: TTSResult) => TTransformed
95
- },
146
+ } & GenerationPersistenceOptions,
96
147
  ): UseGenerateSpeechReturn<
97
148
  InferGenerationOutputFromReturn<TTSResult, TTransformed>
98
149
  > {
@@ -102,19 +153,15 @@ export function useGenerateSpeech<TTransformed = void>(
102
153
  hookName: 'useGenerateSpeech',
103
154
  outputKind: 'audio' as const,
104
155
  }
105
- const { generate, result, isLoading, error, status, stop, reset } =
106
- useGeneration<SpeechGenerateInput, TTSResult, TTransformed>({
107
- ...options,
108
- devtools,
109
- })
156
+ const generation = useGeneration<
157
+ SpeechGenerateInput,
158
+ TTSResult,
159
+ TTransformed
160
+ >({
161
+ ...options,
162
+ devtools,
163
+ reconstructResult: reconstructSpeechResult,
164
+ })
110
165
 
111
- return {
112
- generate: generate as (input: SpeechGenerateInput) => Promise<void>,
113
- result,
114
- isLoading,
115
- error,
116
- status,
117
- stop,
118
- reset,
119
- }
166
+ return generation
120
167
  }
@@ -7,6 +7,7 @@ import type {
7
7
  ConnectConnectionAdapter,
8
8
  GenerationClientState,
9
9
  GenerationFetcher,
10
+ GenerationPersistenceOptions,
10
11
  InferGenerationOutputFromReturn,
11
12
  VideoGenerateInput,
12
13
  VideoGenerateResult,
@@ -21,12 +22,51 @@ export interface UseGenerateVideoOptions<TOutput = VideoGenerateResult> {
21
22
  connection?: ConnectConnectionAdapter
22
23
  /** Direct async function that returns a completed video result */
23
24
  fetcher?: GenerationFetcher<VideoGenerateInput, VideoGenerateResult>
24
- /** Unique identifier for this generation instance */
25
+ /**
26
+ * @deprecated Prefer `threadId`. Only allowed when `threadId` is omitted (see `GenerationPersistenceOptions`).
27
+ */
25
28
  id?: string
26
29
  /** Additional body parameters to send with connect-based adapter requests */
27
30
  body?: Record<string, any>
28
31
  /** Display options for TanStack AI Devtools. */
29
32
  devtools?: AIDevtoolsDisplayOptions
33
+ /**
34
+ * How this generation persists across reloads.
35
+ * - Omit / `false`: ephemeral, in-memory only.
36
+ * - `true`: server-driven — on mount the client hydrates the last generation
37
+ * for its `threadId` from the server (needs a connection with a
38
+ * `hydrateGeneration` handler) and repaints it; it never auto-starts a run.
39
+ */
40
+ persistence?: boolean
41
+ /**
42
+ * The **scope** this generation belongs to: a stable, app-chosen name for the
43
+ * slot successive runs fill — not a link to a chat conversation.
44
+ *
45
+ * The hook starts empty and produces many runs over its life; each gets its
46
+ * own `runId`, but all belong to one scope. Persistence keys on this, so
47
+ * derive it from your own domain and keep it identical across reloads (e.g.
48
+ * `` `video-${videoId}-start-frame` ``). It is also sent as the AG-UI thread
49
+ * id on the wire, which the protocol requires.
50
+ *
51
+ * **Required whenever `persistence` is set** — an app that cannot name the
52
+ * scope has nothing to restore to. Optional for ephemeral generations, where
53
+ * it falls back to `id` purely to satisfy the wire.
54
+ */
55
+ threadId?: string
56
+ /**
57
+ * Server-driven hydration handler for `persistence: true` when the
58
+ * connection doesn't carry one (e.g. alongside `fetcher`, or a `stream()` /
59
+ * `rpcStream()` adapter built without handlers) — typically a one-line
60
+ * server-function call. The connection's own handler takes precedence.
61
+ */
62
+ hydrateGeneration?: ConnectConnectionAdapter['hydrateGeneration']
63
+ /**
64
+ * Re-attach handler that replays a run still generating to completion on
65
+ * mount, when the connection doesn't carry one. Without it, a restored
66
+ * `running` snapshot surfaces as an (interrupted) error. The connection's
67
+ * own handler takes precedence.
68
+ */
69
+ joinRun?: ConnectConnectionAdapter['joinRun']
30
70
  /**
31
71
  * Callback when video generation completes. Can optionally return a transformed value.
32
72
  *
@@ -71,6 +111,13 @@ export interface UseGenerateVideoReturn<TOutput = VideoGenerateResult> {
71
111
  stop: () => void
72
112
  /** Clear all state and return to idle */
73
113
  reset: () => void
114
+ /**
115
+ * The id of the generation job currently running, or `null` when nothing is in
116
+ * flight. Each call to `generate` is one job with its own id. Pass it to your
117
+ * own endpoint to cancel or poll the provider job — `stop()` only aborts the
118
+ * local stream, it does not stop work already running on the provider.
119
+ */
120
+ runId: string | null
74
121
  }
75
122
 
76
123
  /**
@@ -108,9 +155,12 @@ export interface UseGenerateVideoReturn<TOutput = VideoGenerateResult> {
108
155
  // parameter is typed as `VideoGenerateResult` and `result` narrows to the
109
156
  // transform's return. See issue #848.
110
157
  export function useGenerateVideo<TTransformed = void>(
111
- options: Omit<UseGenerateVideoOptions, 'onResult'> & {
158
+ options: Omit<
159
+ UseGenerateVideoOptions,
160
+ 'onResult' | 'persistence' | 'threadId' | 'id'
161
+ > & {
112
162
  onResult?: (result: VideoGenerateResult) => TTransformed
113
- },
163
+ } & GenerationPersistenceOptions,
114
164
  ): UseGenerateVideoReturn<
115
165
  InferGenerationOutputFromReturn<VideoGenerateResult, TTransformed>
116
166
  > {
@@ -119,7 +169,8 @@ export function useGenerateVideo<TTransformed = void>(
119
169
  TTransformed
120
170
  >
121
171
  const hookId = useId()
122
- const clientId = options.id || hookId
172
+ // Single identity: prefer `threadId`; deprecated `id` only when no threadId.
173
+ const clientIdentity = options.threadId ?? options.id ?? hookId
123
174
 
124
175
  const [result, setResult] = useState<TOutput | null>(null)
125
176
  const [jobId, setJobId] = useState<string | null>(null)
@@ -127,9 +178,11 @@ export function useGenerateVideo<TTransformed = void>(
127
178
  const [isLoading, setIsLoading] = useState(false)
128
179
  const [error, setError] = useState<Error | undefined>(undefined)
129
180
  const [status, setStatus] = useState<GenerationClientState>('idle')
181
+ const [runId, setRunId] = useState<string | null>(null)
130
182
 
131
183
  const optionsRef = useRef(options)
132
184
  optionsRef.current = options
185
+ const disposedRef = useRef(false)
133
186
 
134
187
  const client = useMemo(() => {
135
188
  const opts = optionsRef.current
@@ -139,9 +192,17 @@ export function useGenerateVideo<TTransformed = void>(
139
192
  // `?.()`'s implicit `undefined` doesn't widen the function
140
193
  // return type (which `exactOptionalPropertyTypes` rejects
141
194
  // against the strict-optional target).
195
+ // Identity: pass `threadId` alone when set (never also pass deprecated `id`).
142
196
  const baseOptions = {
143
- id: clientId,
144
197
  body: opts.body,
198
+ ...(opts.threadId !== undefined
199
+ ? { threadId: opts.threadId }
200
+ : { id: opts.id ?? hookId }),
201
+ ...(opts.persistence !== undefined && { persistence: opts.persistence }),
202
+ ...(opts.hydrateGeneration !== undefined && {
203
+ hydrateGeneration: opts.hydrateGeneration,
204
+ }),
205
+ ...(opts.joinRun !== undefined && { joinRun: opts.joinRun }),
145
206
  devtoolsBridgeFactory: createVideoDevtoolsBridge,
146
207
  devtools: {
147
208
  ...opts.devtools,
@@ -157,26 +218,41 @@ export function useGenerateVideo<TTransformed = void>(
157
218
  result: VideoGenerateResult,
158
219
  ) => TOutput | null | void,
159
220
  onError: (e: Error) => {
160
- optionsRef.current.onError?.(e)
221
+ if (!disposedRef.current) optionsRef.current.onError?.(e)
161
222
  },
162
223
  onProgress: (p: number, m?: string) => {
163
- optionsRef.current.onProgress?.(p, m)
224
+ if (!disposedRef.current) optionsRef.current.onProgress?.(p, m)
164
225
  },
165
226
  onChunk: (c: StreamChunk) => {
166
- optionsRef.current.onChunk?.(c)
227
+ if (!disposedRef.current) optionsRef.current.onChunk?.(c)
167
228
  },
168
229
  onJobCreated: (id: string) => {
169
- optionsRef.current.onJobCreated?.(id)
230
+ if (!disposedRef.current) optionsRef.current.onJobCreated?.(id)
170
231
  },
171
232
  onStatusUpdate: (s: VideoStatusInfo) => {
172
- optionsRef.current.onStatusUpdate?.(s)
233
+ if (!disposedRef.current) optionsRef.current.onStatusUpdate?.(s)
234
+ },
235
+ onResultChange: (r: TOutput | null) => {
236
+ if (!disposedRef.current) setResult(r)
237
+ },
238
+ onLoadingChange: (l: boolean) => {
239
+ if (!disposedRef.current) setIsLoading(l)
240
+ },
241
+ onErrorChange: (e: Error | undefined) => {
242
+ if (!disposedRef.current) setError(e)
243
+ },
244
+ onStatusChange: (s: GenerationClientState) => {
245
+ if (!disposedRef.current) setStatus(s)
246
+ },
247
+ onJobIdChange: (id: string | null) => {
248
+ if (!disposedRef.current) setJobId(id)
249
+ },
250
+ onVideoStatusChange: (s: VideoStatusInfo | null) => {
251
+ if (!disposedRef.current) setVideoStatus(s)
252
+ },
253
+ onResumeStateChange: (rs: { runId: string } | null) => {
254
+ if (!disposedRef.current) setRunId(rs?.runId ?? null)
173
255
  },
174
- onResultChange: setResult,
175
- onLoadingChange: setIsLoading,
176
- onErrorChange: setError,
177
- onStatusChange: setStatus,
178
- onJobIdChange: setJobId,
179
- onVideoStatusChange: setVideoStatus,
180
256
  }
181
257
 
182
258
  if (opts.connection) {
@@ -196,7 +272,7 @@ export function useGenerateVideo<TTransformed = void>(
196
272
  throw new Error(
197
273
  'useGenerateVideo requires either a connection or fetcher option',
198
274
  )
199
- }, [clientId])
275
+ }, [clientIdentity, hookId])
200
276
 
201
277
  // Sync body changes without recreating client
202
278
  useEffect(() => {
@@ -206,11 +282,15 @@ export function useGenerateVideo<TTransformed = void>(
206
282
  })
207
283
  }, [client, options.body])
208
284
 
209
- // Cleanup on unmount
285
+ // Mount devtools and clean up on unmount. Generation runs are never
286
+ // auto-started on mount — persisted state is only displayed. Mounting
287
+ // revives the client after a StrictMode dispose → remount replay.
210
288
  useEffect(() => {
289
+ disposedRef.current = false
211
290
  client.mountDevtools()
212
291
 
213
292
  return () => {
293
+ disposedRef.current = true
214
294
  client.dispose()
215
295
  }
216
296
  }, [client])
@@ -240,5 +320,6 @@ export function useGenerateVideo<TTransformed = void>(
240
320
  status,
241
321
  stop,
242
322
  reset,
323
+ runId,
243
324
  }
244
325
  }