Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .changeset/ag-ui-core-zod-free.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
'@tanstack/ai': minor
---

Remove zod from `@tanstack/ai`'s dependency graph entirely.

`@ag-ui/core` is bumped to `0.1.1-canary.beta.0`, which drops zod from its
runtime dependencies and declares it as an optional peer instead. Previously
every `@tanstack/ai` install pulled zod in transitively through it.

`chatParamsFromRequest` / `chatParamsFromRequestBody` were the only zod
consumers in this package β€” they validated request bodies with AG-UI's
`RunAgentInputSchema`. They now validate the same `RunAgentInput` contract
structurally, so `@tanstack/ai` ships with no schema-validation runtime at all
and neither requires nor suggests zod.

No API change: both helpers keep their signatures, still reject non-conforming
bodies with a migration-pointing `AGUIError` (`chatParamsFromRequest` still
throws a 400 `Response`), and still carry TanStack's canonical `parts` field
through on messages. Validation errors now name the offending field β€”
`messages[1].content must be a string` instead of a zod issue dump.

zod remains fully supported for defining tools; it is simply no longer
installed on your behalf. If you relied on getting zod transitively without
declaring it, add it explicitly: `npm install zod`.
7 changes: 4 additions & 3 deletions docs/comparison/vercel-ai-sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,10 @@ const logger: ChatMiddleware = {
console.log(`[${ctx.requestId}] Chat started`)
},
onChunk: (ctx, chunk) => {
// Transform, expand, or drop chunks
if ('delta' in chunk && 'messageId' in chunk) {
return { ...chunk, delta: chunk.delta!.replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[REDACTED]') }
// Transform, expand, or drop chunks. `type` is the discriminant, so it
// narrows `chunk` to the matching event and types `delta` as `string`.
if (chunk.type === EventType.TEXT_MESSAGE_CONTENT) {
return { ...chunk, delta: chunk.delta.replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[REDACTED]') }
}
},
onBeforeToolCall: (ctx, hookCtx) => {
Expand Down
2 changes: 1 addition & 1 deletion docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@
"label": "AG-UI Client Compliance",
"to": "migration/ag-ui-compliance",
"addedAt": "2026-05-16",
"updatedAt": "2026-07-08"
"updatedAt": "2026-07-31"
},
{
"label": "Sampling β†’ modelOptions",
Expand Down
32 changes: 31 additions & 1 deletion docs/migration/ag-ui-compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,37 @@ Pure AG-UI `RunAgentInput` payloads (no TanStack `parts` field) work end-to-end:

## `@ag-ui/core` bump

`@tanstack/ai` now depends on `@ag-ui/core@^0.0.52`. If your code imports types from `@tanstack/ai` that re-export AG-UI types, you may need minor type adjustments β€” see the changeset for specifics.
`@tanstack/ai` now depends on `@ag-ui/core@0.1.1-canary.beta.0`. If your code imports types from `@tanstack/ai` that re-export AG-UI types, you may need minor type adjustments β€” see the changeset for specifics.

### zod is no longer installed for you

`@ag-ui/core` used to list `zod` as a runtime dependency, so every
`@tanstack/ai` install pulled zod in transitively. As of `0.1.x` it declares zod
as an optional peer instead, and `@tanstack/ai` no longer uses zod anywhere β€”
the package now ships with no schema-validation runtime at all.

`chatParamsFromRequest` / `chatParamsFromRequestBody` were the only zod
consumers: they validated the request body with AG-UI's `RunAgentInputSchema`.
They now validate the same `RunAgentInput` contract structurally. Their
signatures, their thrown types (`AGUIError`, and a 400 `Response` from
`chatParamsFromRequest`), and the `parts` passthrough on messages are all
unchanged. The one visible difference is friendlier failures β€” the error names
the offending field, e.g.:
Comment on lines +375 to +381

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ“ Maintainability & Code Quality | 🟑 Minor | ⚑ Quick win

Update the earlier Tier 2 text, which still credits zod.

This section states that zod is no longer used. Lines 133 and 157 of the same document still describe "Zod validation against RunAgentInputSchema" and validation "against AG-UI RunAgentInputSchema". Readers see two different mechanisms in one page. Reword those two lines to describe structural validation of the RunAgentInput contract.

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/migration/ag-ui-compliance.md` around lines 375 - 381, Update the
earlier Tier 2 documentation statements that reference Zod or AG-UI’s
RunAgentInputSchema, replacing them with structural validation against the
RunAgentInput contract; keep the surrounding behavior and terminology consistent
with the later chatParamsFromRequest and chatParamsFromRequestBody description.


```
Request body is not a valid AG-UI RunAgentInput. ... Validation errors: messages[1].content must be a string
```

zod is still fully supported for defining tools; it is just no longer installed
on your behalf. If your project used zod without declaring it β€” relying on the
transitive copy β€” add it explicitly:

```bash
npm install zod
```

If you define tools with a different Standard Schema library (ArkType, Valibot),
you can now drop zod entirely.

## Out of scope (existing behavior preserved)

Expand Down
2 changes: 1 addition & 1 deletion packages/ai/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
"tanstack-intent"
],
"dependencies": {
"@ag-ui/core": "^0.0.57",
"@ag-ui/core": "0.1.1-canary.beta.0",
"@standard-schema/spec": "^1.1.0",
"@tanstack/ai-event-client": "workspace:*",
"@tanstack/ai-utils": "workspace:*",
Expand Down
248 changes: 199 additions & 49 deletions packages/ai/src/utilities/chat-params.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { AGUIError, RunAgentInputSchema } from '@ag-ui/core'
import type { Context as AGUIContext } from '@ag-ui/core'
import { AGUIError } from '@ag-ui/core'
import type {
Context as AGUIContext,
Message as AGUIMessage,
ResumeEntry as AGUIResumeEntry,
Role as AGUIRole,
} from '@ag-ui/core'
import type {
AnyTool,
JSONSchema,
Expand Down Expand Up @@ -29,6 +34,162 @@ function isValidParts(value: unknown): value is Array<{ type: string }> {
return true
}

/**
* Keyed by `AGUIRole` so a role added upstream fails to compile here until it
* is handled, rather than silently falling through as an unknown role.
*/
const AGUI_ROLES: Record<AGUIRole, true> = {
developer: true,
system: true,
assistant: true,
user: true,
tool: true,
activity: true,
reasoning: true,
}

function isAGUIRole(value: unknown): value is AGUIRole {
return typeof value === 'string' && value in AGUI_ROLES
}
Comment on lines +41 to +53

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ”’ Security & Privacy | 🟠 Major | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Show the AG-UI Role union and Message variants for the pinned canary.
set -euo pipefail

fd -t f -g '*.d.ts' . node_modules/@ag-ui/core 2>/dev/null | head -20 || true
rg -n -C3 'type Role|ToolCall|toolCalls' --glob '**/@ag-ui/core/**/*.d.ts' . 2>/dev/null | head -60 || \
  echo 'Package not installed in sandbox; inspect the published types instead.'

Repository: TanStack/ai

Length of output: 513


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate chat-params and relevant package files =="
fd -t f -g 'chat-params.ts' .
fd -t f -g 'package.json' packages/ai packages 2>/dev/null | head -40

echo "== chat-params outline =="
ast-grep outline packages/ai/src/utilities/chat-params.ts || true

echo "== chat-params relevant content =="
nl -ba packages/ai/src/utilities/chat-params.ts | sed -n '1,140p'

echo "== usage of assertAGUIMessage / AGUI roles =="
rg -n "assertAGUIMessage|AGUIRole|isAGUIRole|AGUI_ROLES|`@ag-ui/core`" packages/ai/src packages -g '*.{ts,tsx}' | head -100

Repository: TanStack/ai

Length of output: 2860


🌐 Web query:

@ag-ui/core 0.1.1-canary.beta.0 type Role AGUI Role

πŸ’‘ Result:

In the @ag-ui/core library, the Role type defines the possible participants in the Agent User Interaction (AG-UI) Protocol [1][2]. It is implemented as a union of string literals in TypeScript [1] and a Literal type in Python [2]. The Role type includes the following values: - developer [1][2] - system [1][2] - assistant [1][2] - user [1][2] - tool [1][2] - activity [1][2] - reasoning [1][2] These roles act as a discriminator for message types within the protocol, determining the structure and permitted fields of a message [3][4]. For example, a message with the role "tool" requires a toolCallId field [3], while a "user" role may support multimodal content parts [5]. Regarding the version specified, @ag-ui/core 0.1.1-canary.beta.0 does not appear in the official npm registry version history as of July 31, 2026; the package has active development under the 0.0.x branch [6][7][8]. Users are advised to rely on the latest stable or canary releases documented in the official AG-UI protocol documentation [9][10].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== chat-params.ts relevant content =="
awk '{printf "%5d\t%s\n", NR, $0}' packages/ai/src/utilities/chat-params.ts | sed -n '1,160p'

echo "== usage of assertAGUIMessage / AGUI roles / package version =="
rg -n "assertAGUIMessage|AGUIRole|isAGUIRole|AGUI_ROLES|`@ag-ui/core`" packages/ai/src packages -g '*.{ts,tsx}' | head -120
awk '{printf "%5d\t%s\n", NR, $0}' packages/ai/package.json | sed -n '1,220p'

echo "== JavaScript prototype membership behavior =="
node - <<'JS'
const AGUI_ROLES = {
  developer: true,
  system: true,
  assistant: true,
  user: true,
  tool: true,
  activity: true,
  reasoning: true,
}
for (const value of ['toString', 'valueOf', 'constructor', 'activity', 'unknown']) {
  console.log(JSON.stringify(value), {
    typeof: typeof value,
    in_member: value in AGUI_ROLES,
    hasOwn_member: Object.hasOwn(AGUI_ROLES, value),
    ownKeys: Object.keys(AGUI_ROLES),
  })
}
JS

Repository: TanStack/ai

Length of output: 19770


Use Object.hasOwn for the role membership check.

in checks the prototype chain, so messages with role: 'toString', role: 'valueOf', or role: 'constructor' pass isAGUIRole(). Those values then fall through the switch without any role-specific validation.

πŸ”’ Proposed fix
 function isAGUIRole(value: unknown): value is AGUIRole {
-  return typeof value === 'string' && value in AGUI_ROLES
+  return typeof value === 'string' && Object.hasOwn(AGUI_ROLES, value)
 }
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const AGUI_ROLES: Record<AGUIRole, true> = {
developer: true,
system: true,
assistant: true,
user: true,
tool: true,
activity: true,
reasoning: true,
}
function isAGUIRole(value: unknown): value is AGUIRole {
return typeof value === 'string' && value in AGUI_ROLES
}
const AGUI_ROLES: Record<AGUIRole, true> = {
developer: true,
system: true,
assistant: true,
user: true,
tool: true,
activity: true,
reasoning: true,
}
function isAGUIRole(value: unknown): value is AGUIRole {
return typeof value === 'string' && Object.hasOwn(AGUI_ROLES, value)
}
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai/src/utilities/chat-params.ts` around lines 41 - 53, Update
isAGUIRole to use an own-property membership check with Object.hasOwn against
AGUI_ROLES instead of the in operator, so inherited names such as toString,
valueOf, and constructor are rejected.


function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}

/**
* Reject the request body, pointing at the migration guide. Mirrors the
* message the previous `RunAgentInputSchema.safeParse` failure produced.
*/
function invalidBody(reason: string): never {
throw new AGUIError(
`Request body is not a valid AG-UI RunAgentInput. ` +
`If you're upgrading from a previous @tanstack/ai-client release, ` +
`see docs/migration/ag-ui-compliance.md. ` +
`Validation errors: ${reason}`,
)
}

function requireString(value: unknown, at: string): string {
if (typeof value !== 'string') invalidBody(`${at} must be a string`)
return value
}

function requireArray(value: unknown, at: string): Array<unknown> {
if (!Array.isArray(value)) invalidBody(`${at} must be an array`)
return value
}

/**
* Assert one AG-UI `Message`, discriminating on `role` exactly as the upstream
* `MessageSchema` discriminated union does. The record view is retained on the
* asserted type so callers can still inspect non-AG-UI extras like `parts`.
*/
function assertAGUIMessage(
value: Record<string, unknown>,
at: string,
): asserts value is Record<string, unknown> & AGUIMessage {
requireString(value.id, `${at}.id`)

const role = value.role
if (!isAGUIRole(role)) {
invalidBody(
`${at}.role must be one of ${Object.keys(AGUI_ROLES).join(' | ')}`,
)
}

switch (role) {
case 'assistant':
// Both optional: a tool-calling turn carries no text content.
if (value.content !== undefined) {
requireString(value.content, `${at}.content`)
}
if (value.toolCalls !== undefined) {
requireArray(value.toolCalls, `${at}.toolCalls`)
}
break
case 'user':
if (typeof value.content !== 'string' && !Array.isArray(value.content)) {
invalidBody(
`${at}.content must be a string or an array of content parts`,
)
}
break
Comment on lines +100 to +116

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find downstream reads of AG-UI toolCalls entries that assume a validated shape.
set -euo pipefail

rg -nP --type=ts -C3 'toolCalls\s*(\?\.|\[)|\.function\.(name|arguments)' packages/ai/src | head -80

Repository: TanStack/ai

Length of output: 6902


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file size/outline =="
wc -l packages/ai/src/utilities/chat-params.ts packages/ai/src/activities/chat/messages.ts
ast-grep outline packages/ai/src/utilities/chat-params.ts packages/ai/src/activities/chat/messages.ts --view compact 2>/dev/null | sed -n '1,180p'

echo "== chat-params relevant slice =="
cat -n packages/ai/src/utilities/chat-params.ts | sed -n '1,180p'

echo "== messages relevant slice =="
cat -n packages/ai/src/activities/chat/messages.ts | sed -n '380,475p'

echo "== search requireArray/isRecord definitions =="
rg -n --type=ts -C2 'function requireArray|const requireArray|requireArray\s*=|function isRecord|const isRecord|export function invalidBody|export function requireString' packages/ai/src

Repository: TanStack/ai

Length of output: 282


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== chat-params relevant slice =="
cat -n packages/ai/src/utilities/chat-params.ts | sed -n '1,180p'

echo "== messages relevant slice =="
cat -n packages/ai/src/activities/chat/messages.ts | sed -n '380,475p'

echo "== search requireArray/isRecord definitions =="
rg -n --type=ts -C2 'function requireArray|const requireArray|requireArray\s*=|function isRecord|const isRecord|export function invalidBody|export function requireString' packages/ai/src || true

echo "== behavioral probe for current validation shape access =="
node - <<'JS'
function requireArray(value, at) {
  if (!Array.isArray(value)) throw new TypeError(`${at} must be an array`)
  return value
}
function requireString(value, at) {}

function assertAGUIMessage(value) {}

for (const v of [null, {}, [{id: 1}], [{function: {}}], [{function: {name: 'x'}}]]) {
  const role = 'assistant'
  try {
    if (value.content !== undefined) requireString(value.content, `${role}.content`)
    if (v.toolCalls !== undefined) requireArray(v.toolCalls, `${role}.toolCalls`)
  } catch (error) {
    console.log('validation rejects', JSON.stringify(v), 'error:', error.message)
    continue
  }

  // Downstream shape access modeled from packages/ai/src/activities/chat/messages.ts lines 452/459/460
  try {
    const toolCalls = v.toolCalls
    for (const toolCall of toolCalls) {
      JSON.parse(toolCall.function.arguments)
      toolCall.function.name
      toolCall.function.arguments
    }
    console.log('shape access succeeds', JSON.stringify(v))
  } catch (error) {
    console.log('shape access throws', JSON.stringify(v), 'name:', error.name, 'message:', error.message)
  }
}
JS

Repository: TanStack/ai

Length of output: 12025


Validate each toolCalls object before downstream conversion.

requireArray only checks the parent array, so toolCalls: [{}] or toolCalls: [{function: {}}] passes validation into modelMessageToUIMessage(). That code then reads toolCall.function.arguments, id, and function.name, which throws on malformed entries instead of returning the intended validation error. Add per-element checks for each expected property.

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai/src/utilities/chat-params.ts` around lines 100 - 116, Update the
assistant branch in the role validation switch to validate every element of
value.toolCalls after requireArray, checking each expected tool-call property,
including id and function.name/function.arguments, before
modelMessageToUIMessage() can process it; malformed entries must produce the
existing validation error rather than throwing during conversion.

case 'tool':
requireString(value.content, `${at}.content`)
requireString(value.toolCallId, `${at}.toolCallId`)
break
case 'activity':
requireString(value.activityType, `${at}.activityType`)
if (!isRecord(value.content)) {
invalidBody(`${at}.content must be an object`)
}
break
case 'developer':
case 'system':
case 'reasoning':
requireString(value.content, `${at}.content`)
break
}
}

function validateMessage(value: unknown, index: number): AGUIMessage {
const at = `messages[${index}]`
if (!isRecord(value)) invalidBody(`${at} must be an object`)
assertAGUIMessage(value, at)

// `parts` is TanStack's canonical extra, carried through so the UIMessage
// path inside `chat()` can use it. Keep it only when it holds recognized
// part types β€” the previous schema-based path dropped `parts` during parse
// and re-attached it from the raw body behind this same check.
if ('parts' in value && !isValidParts(value.parts)) {
const withoutParts = { ...value }
Reflect.deleteProperty(withoutParts, 'parts')
return withoutParts
}
return value
}

function validateTool(
value: unknown,
index: number,
): { name: string; description: string; parameters: JSONSchema } {
const at = `tools[${index}]`
if (!isRecord(value)) invalidBody(`${at} must be an object`)
return {
name: requireString(value.name, `${at}.name`),
description: requireString(value.description, `${at}.description`),
// Upstream `ToolSchema` types this as optional `any`; it reaches the
// provider as a raw JSON Schema either way.
parameters: value.parameters as JSONSchema,
}
}

function validateContext(value: unknown, index: number): AGUIContext {
const at = `context[${index}]`
if (!isRecord(value)) invalidBody(`${at} must be an object`)
return {
description: requireString(value.description, `${at}.description`),
value: requireString(value.value, `${at}.value`),
}
}

function validateResumeEntry(value: unknown, index: number): AGUIResumeEntry {
const at = `resume[${index}]`
if (!isRecord(value)) invalidBody(`${at} must be an object`)
const status = value.status
if (status !== 'resolved' && status !== 'cancelled') {
invalidBody(`${at}.status must be "resolved" or "cancelled"`)
}
const entry: AGUIResumeEntry = {
interruptId: requireString(value.interruptId, `${at}.interruptId`),
status,
}
// Omit the key entirely when absent, matching the optional-field shape the
// schema produced.
if (value.payload !== undefined) entry.payload = value.payload
return entry
}

/**
* Parse and validate an HTTP request body as an AG-UI `RunAgentInput`.
*
Expand All @@ -37,11 +198,14 @@ function isValidParts(value: unknown): value is Array<{ type: string }> {
* `convertMessagesToModelMessages` handles AG-UI fan-out dedup and
* reasoning/activity/developer-role normalization internally.
*
* Validated structurally against the AG-UI `RunAgentInput` contract without a
* schema library, so this package pulls in no validation runtime of its own.
*
* @throws An error with a migration-pointing message when the body does
* not conform to AG-UI `RunAgentInputSchema`. Surface this as a
* not conform to AG-UI `RunAgentInput`. Surface this as a
* 400 Bad Request to the client.
*/
export function chatParamsFromRequestBody(body: unknown): Promise<{
export async function chatParamsFromRequestBody(body: unknown): Promise<{
messages: Array<UIMessage | ModelMessage>
threadId: string
runId: string
Expand All @@ -57,55 +221,41 @@ export function chatParamsFromRequestBody(body: unknown): Promise<{
context: Array<AGUIContext>
aguiContext: Array<AGUIContext>
}> {
const parseResult = RunAgentInputSchema.safeParse(body)
if (!parseResult.success) {
return Promise.reject(
new AGUIError(
`Request body is not a valid AG-UI RunAgentInput. ` +
`If you're upgrading from a previous @tanstack/ai-client release, ` +
`see docs/migration/ag-ui-compliance.md. ` +
`Validation errors: ${parseResult.error.message}`,
),
)
if (!isRecord(body)) invalidBody('body must be a JSON object')

const threadId = requireString(body.threadId, 'threadId')
const runId = requireString(body.runId, 'runId')
const parentRunId =
body.parentRunId === undefined
? undefined
: requireString(body.parentRunId, 'parentRunId')

const messages = requireArray(body.messages, 'messages').map(validateMessage)
const tools = requireArray(body.tools, 'tools').map(validateTool)
const aguiContext = requireArray(body.context, 'context').map(validateContext)
const resume =
body.resume === undefined
? undefined
: requireArray(body.resume, 'resume').map(validateResumeEntry)

if (body.forwardedProps !== undefined && !isRecord(body.forwardedProps)) {
invalidBody('forwardedProps must be an object')
}

const parsed = parseResult.data
const aguiContext = parsed.context

// AG-UI Zod uses `.strip()` so extra fields like `parts` on messages are
// dropped during parse. We re-attach them from the original body so the
// existing UIMessage path inside `chat()` can use them directly.
const rawMessages =
(body as { messages?: Array<Record<string, unknown>> }).messages ?? []
const messages = parsed.messages.map((m, i) => {
const raw = rawMessages[i]
if (
raw &&
typeof raw === 'object' &&
'parts' in raw &&
isValidParts(raw.parts)
) {
return { ...m, parts: raw.parts } as UIMessage | ModelMessage
}
return m as ModelMessage
})

return Promise.resolve({
messages,
threadId: parsed.threadId,
runId: parsed.runId,
parentRunId: parsed.parentRunId,
tools: parsed.tools as Array<{
name: string
description: string
parameters: JSONSchema
}>,
forwardedProps: (parsed.forwardedProps ?? {}) as Record<string, unknown>,
state: parsed.state,
resume: parsed.resume,
return {
// Unknown top-level fields (e.g. a legacy `cursor`) are dropped by
// construction: only the fields below are copied onto the result.
messages: messages as Array<UIMessage | ModelMessage>,
threadId,
runId,
parentRunId,
tools,
forwardedProps: (body.forwardedProps ?? {}) as Record<string, unknown>,
state: body.state,
resume: resume as Array<RunAgentResumeItem> | undefined,
context: aguiContext,
aguiContext,
})
}
}

/**
Expand Down
Loading
Loading