@cloudflare/think@0.3.0
cloudflare/agents@cloudflare/think@0.3.0Apr 18, 2026by github-actions[bot]
AI Summary
This minor release (0.3.0) aligns Think lifecycle hooks with the AI SDK, fixing latent bugs around tool-call hooks and extension dispatch. Key improvements include proper timing for beforeToolCall/afterToolCall hooks, functional ToolCallDecision support, and full dispatch of all five extension hooks. A patch fix also resolves an issue where async iterable returns from tools were incorrectly handled.
Key Highlights
- Lifecycle hook context types now derived from AI SDK with full typed access to reasoning, sources, files, providerMetadata, and request/response
- beforeToolCall now runs before tool execution and afterToolCall runs after with durationMs and discriminated success/output/error outcome
- ToolCallDecision is now functional - block, substitute, and allow actions properly intercept execution
- All five extension hooks (beforeToolCall, afterToolCall, onStepFinish, onChunk, beforeTurn) now dispatch correctly with JSON-safe snapshots
- Fixed bug in _wrapToolsWithDecision where Promise<AsyncIterable> returns were mishandled
Breaking Changes
- ToolCallContext.args renamed to input
- ToolCallResultContext.args renamed to input
- ToolCallResultContext.result renamed to output
- afterToolCall is now a discriminated union - must check ctx.success before reading output or error
- Equivalent renames applied to ToolCallDecision (args→input, result→output)
New Features
- Full typed access to AI SDK types including reasoning, sources, files, providerMetadata, request/response
- beforeToolCall hook fires before tool execution with ability to intercept via ToolCallDecision
- afterToolCall hook fires after execution with durationMs and success/error tracking
- ToolCallDecision supports block (with reason), substitute (with output), and allow (with input) actions
- Extension hook handlers now receive (snapshot, host) symmetric with tool execute handlers
Full Release Notes
### Minor Changes
- [#1340](https://github.com/cloudflare/agents/pull/1340) [`3cbe776`](https://github.com/cloudflare/agents/commit/3cbe77668df356906244db6a75c4cfba2daa1836) Thanks [@threepointone](https://github.com/threepointone)! - Align Think lifecycle hooks with the AI SDK and fix latent bugs around tool-call hooks and extension dispatch.
**Lifecycle hook context types are now derived from the AI SDK** (resolves [#1339](https://github.com/cloudflare/agents/issues/1339)). `StepContext`, `ChunkContext`, `ToolCallContext`, and `ToolCallResultContext` are derived from `StepResult`, `TextStreamPart`, and `TypedToolCall` so users get full typed access to `reasoning`, `sources`, `files`, `providerMetadata` (where Anthropic cache tokens live), `request`/`response`, etc., instead of `unknown`. The relevant AI SDK types are re-exported from `@cloudflare/think`.
**`beforeToolCall` / `afterToolCall` now fire with correct timing.** `beforeToolCall` runs **before** the tool's `execute` (Think wraps every tool's `execute`), and `afterToolCall` runs **after** with `durationMs` and a discriminated `success`/`output`/`error` outcome (backed by `experimental_onToolCallFinish`).
**`ToolCallDecision` is now functional.** Returning `{ action: "block", reason }`, `{ action: "substitute", output }`, or `{ action: "allow", input }` from `beforeToolCall` actually intercepts execution.
**Extension hook dispatch.** `ExtensionManifest.hooks` claimed support for `beforeToolCall`/`afterToolCall`/`onStepFinish`/`onChunk` but Think only ever dispatched `beforeTurn`. All five hooks now dispatch to subscribed extensions with JSON-safe snapshots. Extension hook handlers also receive `(snapshot, host)` (symmetric with tool `execute`); previously only tool executes got the host bridge.
**Breaking renames** (per AI SDK conventions): `ToolCallContext.args` → `input`, `ToolCallResultContext.args` → `input`, `ToolCallResultContext.result` → `output`. `afterToolCall` is now a discriminated union — read `output` only when `ctx.success === true`, and `error` when `ctx.success === false`. Equivalent renames on `ToolCallDecision`.
See [docs/think/lifecycle-hooks.md](https://github.com/cloudflare/agents/blob/main/docs/think/lifecycle-hooks.md) for the full hook reference.
### Patch Changes
- [#1340](https://github.com/cloudflare/agents/pull/1340) [`3cbe776`](https://github.com/cloudflare/agents/commit/3cbe77668df356906244db6a75c4cfba2daa1836) Thanks [@threepointone](https://github.com/threepointone)! - Fix `_wrapToolsWithDecision` to `await originalExecute(...)` before checking for `Symbol.asyncIterator`. The previous code missed `Promise<AsyncIterable>` returns from plain async functions (`async function execute(...) { return makeIter(); }`) — `Symbol.asyncIterator in promise` is always false, the collapse logic was skipped, and the AI SDK ended up treating the iterator instance itself as the final output value (which the wrapper's own comment warned about). Both sync-returned-iterable and async-returned-iterable cases are now covered, with regression tests for each.