Think·续
// submit — durable acceptance, check status later const submission = await this.runTurn({ mode: "submit", input: "Process this webhook", idempotencyKey: inboundEventId, // dedupe; safe to retry }); // submission.accepted is true on first accept; submission.status is "pending"
// stream — drive a callback (the same surface as chat()) await this.runTurn({ mode: "stream", input: "Stream me", callback: { onStart({ requestId }) {}, onEvent(json) {}, // UIMessageChunk JSON onDone() {}, onError(error) {}, }, });
// continuation — continue the last assistant turn instead of sending input await this.runTurn({ continuation: true }); } }
Key behaviors:
- **Blocking modes cannot nest.** Calling `wait`/ `stream`/ `continuation` (or the equivalent shortcut) from *inside* an active turn — for example, from a tool's `execute` — throws, because it would deadlock the turn queue. From inside a turn, use `runTurn({ mode: "submit" })` (durable, runs after the current turn frees the queue) or [`addMessages()`](#add-messages-without-a-turn) (transcript only, no inference).
- **`submit` is idempotent.** Pass `submissionId` and/or `idempotencyKey`; re-submitting a known key returns the existing record with `accepted: false` instead of starting a second turn. See [Programmatic submissions](https://developers.cloudflare.com/agents/harnesses/think/programmatic-submissions/).
- **Recovery-safe.** The `wait`, `stream`, and drained `submit` paths run inference inside a recovery fiber, so an interrupted turn resumes after eviction.
`runTurn` is exported alongside its option and result types: `RunTurnOptions`, `RunTurnWait`, `RunTurnSubmit`, `RunTurnStream`, `TurnInputMessages`, and `TurnResult`.
### Pick a shortcut
The table below maps each scenario to the most direct call. Each shortcut has an unchanged signature; reach for them when you want the narrower surface, or use `runTurn()` when you want one mental model.
| Use case | API |
| --- | --- |
| A browser user sends chat messages | `useAgentChat` over the WebSocket chat protocol |
| Server code can wait for the model response | `saveMessages()` |
| Server code needs fast durable acceptance and later status | `submitMessages()` |
| Code should create recurring prompt-driven turns or handlers | `getScheduledTasks()` |
| Parent code needs direct streaming RPC to a specific child | `subAgent(...).chat()` |
| A parent delegates work to a retained child agent | `agentTool()` or `runAgentTool()` |
| Surround a turn with idempotent app-owned side effects | `startFiber()` |
| Coordinate multi-step durable orchestration | Workflows |
| Add context or messages without starting a model turn | `addMessages()` |
| Advanced subclass or recovery code continues an assistant turn | `continueLastTurn()` |
Use `saveMessages()` when the caller owns the trigger and can wait for the turn to finish. Use [`submitMessages()`](https://developers.cloudflare.com/agents/harnesses/think/programmatic-submissions/) when timeout ambiguity would make retries unsafe.
### Add messages without a turn
Use `addMessages()` to write to the transcript **without** starting a model turn — for importing prior history or injecting background context the next turn should see:
```js
export class Assistant extends Think {
async importContext() {
await this.addMessages([
{
id: crypto.randomUUID(),
role: "user",
parts: [{ type: "text", text: "Imported context" }],
},
]);
}
}
export class Assistant extends Think<Env> {
async importContext() {
await this.addMessages([
{
id: crypto.randomUUID(),
role: "user",
parts: [{ type: "text", text: "Imported context" }],
},
]);
}
}
addMessages() appends (or upserts) into the Session tree:
- It does not run inference and does not enter the turn queue, so it is safe to call from inside a tool's
executewithout deadlocking. - Array entries are appended linearly (each attaches under the previous one), so imported history stays a single path. By default the first message attaches to the latest committed leaf; pass
parentIdto attach elsewhere, ornullfor a root message. - Appends are idempotent by message id. Pass
{ mode: "upsert" }to update an existing message in place instead.
The supported pattern is "add context, then run a turn": call addMessages(), then runTurn().
Use chat() for low-level parent-to-child streaming when your code owns forwarding, cancellation, and replay policy. Use Agents as tools when a parent model or workflow delegates to a child agent and you want retained child runs, event replay, abort bridging, and UI drill-in.
Use startFiber() outside Think when the durable unit is an application job around a turn: accepting a webhook once, restoring a serialized channel or thread target, posting a visible reply, or recording app-level recovery policy. Think submissions own conversation admission and turn serialization; managed fibers own external job acceptance, idempotent side effects, and application recovery.
In this section
Getting started
Build a Think agent step by step.
Configuration
Configuration overrides, dynamic configuration, and Session integration.
Tools
Workspace tools, code execution, browser tools, and extensions.
Actions
Server actions with idempotency, approvals, authorization, and reply attachments.
Channels
Per-channel policy, channel selection, and out-of-band notices.
Lifecycle hooks
beforeTurn, beforeStep, onStepFinish, onChatResponse, and more.
Client tools
Browser-side tools, approvals, and concurrency.
Messengers
Receive and reply to Chat SDK messenger webhooks.
Scheduled tasks
Declarative recurring prompts and handlers.
Workflows
Durable model-driven reasoning steps inside Cloudflare Workflows.
Sub-agent RPC
chat() streaming, saveMessages, continueLastTurn, and abort.
Programmatic submissions
Durable turn admission for webhooks and RPC callers.
Durable recovery
Chat recovery, stream-stall watchdog, and stability detection.
Agent Skills
On-demand instructions, resources, and scripts via getSkills().
Acknowledgments
Think's design is inspired by Pi ↗︎.
Example
Assistant example
Explore a multi-session Think assistant with sub-agent routing, shared workspace, MCP, chat recovery, and GitHub OAuth.
Related
- Sessions — context blocks, compaction, search, multi-session (the storage layer Think builds on)
- Sub-agents —
subAgent(),abortSubAgent(),deleteSubAgent()(the base Agent methods for spawning children) - Chat agents —
AIChatAgentfor when you need full control over the LLM call - Long-running agents — sub-agent delegation patterns for multi-week agent lifetimes
- Durable execution —
runFiber()and crash recovery (used bychatRecovery) - Browse the web — full CDP helper API reference
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/agents/harnesses/think/#page","headline":"Think","description":"Opinionated chat agent framework with built-in tools, persistent memory, lifecycle hooks, streaming, messengers, scheduled tasks, Workflows, and sub-agent RPC.","url":"https://developers.cloudflare.com/agents/harnesses/think/","inLanguage":"en","image":"https://developers.cloudflare.com/agents/harnesses/think/og.png?v=e3ee81dca3f64261","dateModified":"2026-10-22","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"},"keywords":["AI"]}
description: Server-side Think tools with idempotency, human approvals, authorization, and reply attachments. title: Actions image: https://developers.cloudflare.com/agents/harnesses/think/actions/og.png?v=e3979758f52a77d8
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/agents/llms.txt
Use this file to discover all available pages before exploring further.
Docs