Upgrade to v0.7
Hashbrown v0.7 moves from AG-UI 0.0.59 to AG-UI 1.0. Hashbrown's public types, such as AGUIEvent and RunAgentInput, come from @ag-ui/core, so they now describe AG-UI 1.0 events and inputs. This brings Hashbrown in line with other AG-UI 1.0 tools, such as CopilotKit, so apps that use both install one copy of AG-UI.
Before the details, a big congratulations to the AG-UI team on shipping 1.0! That's a huge milestone, and it's been a real pleasure to build on. When we moved Hashbrown onto AG-UI in v0.6, we were betting on an open protocol for connecting agents to user interfaces. A 1.0 release makes that a foundation the whole community can build on with confidence. Thank you for all the work that went into it. Really well done.
Your React components and hooks don't change. The breaking changes land in code that works with AG-UI directly: your server routes, custom providers and custom transports. Most React apps only need the first step. Apps with an AG-UI server of their own should also read steps 2 to 5.
Checklist
- Update Hashbrown and AG-UI packages together.
- Import validators from
@ag-ui/core/schemas. Only if you validate AG-UI events or inputs yourself. - Replace
THINKING_*events withREASONING_*. Only if your server or transport emits thinking events. - Handle tool results with content parts. Only if you read tool messages or tool result events.
- Treat a missing message role as assistant. Only if you read AG-UI events yourself.
- Switch to
createUiJsonSchema(). Only if you calledɵcreateUiKitto build a schema for an agent.
Prefer to let an AI coding assistant do the mechanical parts? Jump to Migrate with an AI assistant.
Update Packages
Hashbrown packages are released together, and every @hashbrownai/* package in your app must be on the same version.
npm install @hashbrownai/core@0.7.0 @hashbrownai/react@0.7.0
On the server, update your provider adapter and move every @ag-ui/* package you install to 1.x in the same step. Hashbrown's types and your own @ag-ui/core imports must resolve to the same AG-UI version, or TypeScript reports two incompatible AGUIEvent types.
npm install @hashbrownai/openai@0.7.0 @ag-ui/core@^1 @ag-ui/encoder@^1
Swap @hashbrownai/openai for anthropic, azure, bedrock, google or ollama as needed, and include @ag-ui/client if you use it. EventEncoder from @ag-ui/encoder works as before, so an endpoint like the one in the v0.6 guide needs no changes.
Run npm ls @ag-ui/core afterwards. Every entry should be on 1.x.
Import Validators from the Schemas Entry Point
AG-UI 1.0 moved its zod validators out of the main @ag-ui/core entry point. @ag-ui/core now exports only types and constants, and every *Schema value, such as EventSchemas, RunAgentInputSchema and MessageSchema, comes from @ag-ui/core/schemas.
Before:
import { RunAgentInputSchema } from '@ag-ui/core';
After:
import { RunAgentInputSchema } from '@ag-ui/core/schemas';
export function parseRunInput(body: unknown) {
return RunAgentInputSchema.parse(body);
}
The schemas need zod, which is now an optional peer of @ag-ui/core. If your app imports @ag-ui/core/schemas itself, add zod to its dependencies.
The schemas also changed how they parse:
- Objects keep unknown keys.
RunAgentInputSchema.parse(...)used to strip extension fields. It now keeps them, including thehashbrownfield that Hashbrown's client sends. If you forward the parsed input to a system that rejects unknown keys, remove the extra fields yourself. toolsandcontextdefault to[], andstateandforwardedPropsare optional.timestampmust be an integer, andrawEventcan't benull.
Replace Thinking Events with Reasoning Events
AG-UI 1.0 removed the THINKING_* events: THINKING_START, THINKING_END and THINKING_TEXT_MESSAGE_START, _CONTENT and _END. Hashbrown's HTTP transport validates every event it receives, so a server that still emits them now fails the run.
Hashbrown's own provider adapters already emit REASONING_* events. Update any server, custom provider or custom transport that emits thinking events:
| AG-UI 0.0.59 | AG-UI 1.0 |
|---|---|
THINKING_START | REASONING_START |
THINKING_TEXT_MESSAGE_START | REASONING_MESSAGE_START |
THINKING_TEXT_MESSAGE_CONTENT | REASONING_MESSAGE_CONTENT |
THINKING_TEXT_MESSAGE_END | REASONING_MESSAGE_END |
THINKING_END | REASONING_END |
Reasoning events carry a messageId, and a reasoning message starts with the role reasoning:
import { EventType, type AGUIEvent } from '@ag-ui/core';
export function* reasoningEvents(
messageId: string,
text: string,
): Generator<AGUIEvent> {
yield { type: EventType.REASONING_START, messageId };
yield {
type: EventType.REASONING_MESSAGE_START,
messageId,
role: 'reasoning',
};
yield { type: EventType.REASONING_MESSAGE_CONTENT, messageId, delta: text };
yield { type: EventType.REASONING_MESSAGE_END, messageId };
yield { type: EventType.REASONING_END, messageId };
}
Handle Tool Results with Content Parts
In AG-UI 1.0, the content of a tool message and of a TOOL_CALL_RESULT event is string | ContentPart[], the same as a user message. Code that assumed a string no longer type-checks.
Hashbrown's provider adapters (openai, azure, anthropic, google, bedrock and ollama) accept text tool results only. When a tool result carries content parts, they throw, or emit a RUN_ERROR in the case of OpenAI, with a message such as "OpenAI provider currently requires text tool result content". Tool results produced by Hashbrown's client tools are strings, so this only affects servers that build tool messages themselves.
If your own code reads tool results, handle both shapes:
import type { ContentPart, ToolMessage } from '@ag-ui/core';
export function toolResultText(message: ToolMessage): string {
return typeof message.content === 'string'
? message.content
: message.content
.filter(
(part): part is Extract<ContentPart, { type: 'text' }> =>
part.type === 'text',
)
.map((part) => part.text)
.join('');
}
InputContent and the other content type names were renamed, for example to ContentPart and TextPart. The old names remain as deprecated aliases. The legacy binary content type was removed.
If you wrote a custom provider, see Custom Provider for how tool results and validation work with AG-UI 1.0.
Treat a Missing Message Role as Assistant
AG-UI's validators used to fill in role: 'assistant' on a TEXT_MESSAGE_START event that had no role. AG-UI 1.0 still says an absent role means assistant, but it no longer fills it in. Hashbrown's reducers apply that default themselves, so servers that omit role keep working.
If you read AG-UI events yourself, for example in a custom transport or a server that inspects its own stream, apply the same default:
import { EventType, type AGUIEvent } from '@ag-ui/core';
export function messageRole(event: AGUIEvent) {
if (event.type !== EventType.TEXT_MESSAGE_START) {
return undefined;
}
return event.role ?? 'assistant';
}
AG-UI 1.0 also closed its event types. Members of AGUIEvent no longer have an index signature, so reading a field before narrowing by type, such as event.role on any event, or adding extension fields to an event literal no longer type-checks. Narrow on event.type first, as above.
Switch to createUiJsonSchema
v0.7 adds createUiJsonSchema() and UiComponentDefinition to @hashbrownai/core, for agents that Hashbrown doesn't run but that should answer with UI your kit can render. If you called the internal ɵcreateUiKit to build that schema on a server, switch to the public function. ɵ exports can change in any release.
Before:
import { s, ɵcreateUiKit } from '@hashbrownai/core';
import { metricDefinition } from './sales-components';
const kit = ɵcreateUiKit({
components: [{ ...metricDefinition, component: {} }],
});
const uiSchema = s.toJsonSchema(kit.schema);
After:
import { createUiJsonSchema } from '@hashbrownai/core';
import { metricDefinition } from './sales-components';
const uiSchema = createUiJsonSchema({ components: [metricDefinition] });
createUiJsonSchema() takes component definitions without their implementations, plus optional examples, and returns plain JSON that matches the schema useUiKit() builds from the same definitions. See Use a Kit with an Agent Hashbrown Doesn't Run for the browser side.
@hashbrownai/core also now publishes one file per source module, so bundlers can tree-shake it again. The public API is unchanged. Deep imports of files inside core's dist, which were never supported, no longer resolve.
Migrate with an AI Assistant
Most of this upgrade is mechanical, which makes it a good job for an AI coding assistant such as Claude Code. Paste this prompt into your assistant from the root of your repository. It inventories your code first and shows you a plan before it changes anything.
Upgrade this repository from Hashbrown v0.6 to v0.7. The full guide is at https://hashbrown.dev/docs/react/migrations/v0-7 (React) and https://hashbrown.dev/docs/angular/migrations/v0-7 (Angular). Read the guide for my framework before changing anything, and do not invent APIs that are not in it.
First, inventory. Search the repository and list every place that:
1. Depends on any `@hashbrownai/*` or `@ag-ui/*` package, with its current version.
2. Imports a value whose name ends in `Schema` or `Schemas` (such as `EventSchemas`, `RunAgentInputSchema` or `MessageSchema`) from `@ag-ui/core`.
3. Emits, handles or names a `THINKING_*` event, such as `EventType.THINKING_START` or `THINKING_TEXT_MESSAGE_CONTENT`.
4. Reads `content` from a tool message or a `TOOL_CALL_RESULT` event, or builds tool messages for a Hashbrown provider adapter.
5. Reads `role` from AG-UI events, or reads event fields without first narrowing on `event.type`.
6. Imports `ɵcreateUiKit` or any other `ɵ`-prefixed export from `@hashbrownai/core`.
7. Deep-imports files from inside `@hashbrownai/core`'s package, such as `@hashbrownai/core/index2.esm.js`.
Show me the inventory and a short plan before editing.
Then make these changes, only where the inventory found something:
- Update every `@hashbrownai/*` package to exactly 0.7.0, together, and every `@ag-ui/*` package to 1.x in the same step. Confirm with `npm ls @ag-ui/core` that only 1.x versions remain.
- Import every AG-UI validator from `@ag-ui/core/schemas` instead of `@ag-ui/core`, and add `zod` as a dependency of the package that imports it. Remember that `RunAgentInputSchema.parse` now keeps unknown keys such as `hashbrown`.
- Replace `THINKING_*` events with `REASONING_*` events: `THINKING_START` becomes `REASONING_START`, `THINKING_TEXT_MESSAGE_START/CONTENT/END` become `REASONING_MESSAGE_START/CONTENT/END`, and `THINKING_END` becomes `REASONING_END`. Reasoning events need a `messageId`, and `REASONING_MESSAGE_START` has the role `reasoning`.
- Handle tool result content that is `string | ContentPart[]`. Hashbrown's provider adapters only accept string tool results, so send text to them.
- Treat a `TEXT_MESSAGE_START` without a `role` as an assistant message, and narrow AG-UI events on `event.type` before reading their fields.
- Replace schema-building code that uses `ɵcreateUiKit` with `createUiJsonSchema({ components, examples })` from `@hashbrownai/core`, using `UiComponentDefinition` objects.
- Replace deep imports of core's files with imports from `@hashbrownai/core`.
Finally, run the project's type check, build, lint and tests, fix what fails, and summarize every file you changed and anything you could not migrate automatically.
Review the inventory and plan it shows you before letting it continue.