cloudflare/agents @cloudflare/think@0.11.0
cloudflare/agents
Captured source
source ↗@cloudflare/think@0.11.0
Repository: cloudflare/agents
Tag: @cloudflare/think@0.11.0
Published: 2026-06-26T12:20:01Z
Prerelease: no
Release notes:
Minor Changes
- #1758 `6b46b04` Thanks @threepointone! - Add progress signalling and durable milestones for agent-tool sub-agents
(cloudflare/agents#1758, rfc-detached-agent-tools §progress, phases 4a + 4b).
A sub-agent running as an agent tool (awaited or detached/background) can now report mid-run progress:
// Inside the child sub-agent (e.g. from a tool's execute):
await this.reportProgress({
fraction: 0.6,
phase: "deploying",
message: "Generating menu page…"
});These signals ride the child's own turn stream as a transient data-agent-progress part, so they re-broadcast to the parent's connected clients and surface on AgentToolRunState.progress via useAgentToolEvents — a background-runs tray can render a live bar / phase / status line without drilling in. Highlights:
- `reportProgress({ fraction?, message?, phase?, data? }, { persist? })` on
chat agents (@cloudflare/think, AIChatAgent); a no-op with a dev warning on the base Agent and when called outside an active agent-tool run. The framework resolves the run id from the active turn — no threading required. Bursts are coalesced (latest-wins; a fraction >= 1 "done" frame always flushes). data is live-only unless { persist: true }.
- `onProgress(run, progress)` parent hook, fired best-effort from the tail
for both awaited and detached runs.
- Latest-snapshot persistence + recovery inspect. The child stores a
progress_json + last_signal_at on its run row and surfaces it through inspectAgentToolRun().progress, so a rehydrated parent reconstructs progress after eviction.
- Resetting no-progress budget for detached runs. Once a detached child has
reported at least one signal, the backbone gives up if it then goes silent for detachedNoProgressBudgetMs (default 1h; per-run override via detached: { noProgressBudgetMs }), surfaced as interrupted with the no-progress reason. A child that never reports is bounded only by the absolute detachedMaxBudgetMs ceiling — we never give up on a run merely for being slow.
Durable milestones (phase 4b)
Naming a milestone promotes a signal from the ephemeral tier to a durable one — there is still only one emit method:
// Inside the child sub-agent:
await this.reportProgress({
milestone: "sources-gathered",
data: { sources: 2 }
});- Persisted + replayable. Each milestone is one row on the child
(cf_agent_tool_milestones / cf_ai_chat_agent_tool_milestones) with a monotonic per-run sequence. It rides the stream as a persisted data-agent-milestone part (vs. transient progress), so drill-in replay and a rehydrated parent both see it. Surfaced via inspectAgentToolRun().milestones and AgentToolRunState.milestones (deduped by sequence).
- `onProgress` fires for milestones too — the snapshot carries
progress.milestone, so a consumer can branch on milestone vs. ephemeral.
- `detached: { onMilestones }` chat convenience (
@cloudflare/thinkand
AIChatAgent). When a configured milestone lands, the chat agent surfaces an idempotent synthetic chat message (keyed/idempotent per (runId, name)) _before_ the run finishes. Delivered from both the warm tail and the cold backbone reconcile; the deterministic id collapses them to at-most-once. Two modes (the string[] shorthand defaults to "narrate"):
"narrate"(default) — a synthetic assistant message injected directly
(no inference): a cheap, honest status line that does not trigger a turn.
"react"— a user-role turn so the model responds to the milestone
(steer, start dependent work). Costs a model turn.
detached: { onMilestones: ["preview-ready"] } // narrate (default)
detached: { onMilestones: { names: ["needs-approval"], mode: "react" } }Override the wording via formatDetachedMilestone(run, milestone). These synthetic messages carry metadata.source so clients can render them as an agent event rather than a human turn (the example does this).
The awaitable join point (awaitAgentToolMilestone, phase 4c) is intentionally not included here — it is gated behind a design addendum.
- #1758 `6b46b04` Thanks @threepointone! - Add
detached: { notify: true }support forrunAgentToolon chat agents
(@cloudflare/think and AIChatAgent) (cloudflare/agents#1752).
When a detached sub-agent run finishes, a chat agent can inject a message back into the chat so the model reacts to the result — without you wiring onFinish by hand:
await this.runAgentTool(ResearchAgent, {
input,
detached: { notify: { source: "research-background" } }
});The injected turn is idempotent per run + terminal status, so an exactly-once finish never duplicates, while a soft give-up followed by a real late completion surfaces as two distinct turns. (Think dedupes via a submitMessages idempotency key; AIChatAgent, which has no durable-submission layer, persists under a deterministic message id and runs the follow-up turn inline within the already-serialized delivery slot.) Use notify: true for the default metadata.source, pass notify: { source } to match your app's message taxonomy, and override formatDetachedCompletion(run, result) to customize (or suppress) the injected text.
- #1817 `7f367d8` Thanks @threepointone! -
create-thinknow prompts for a starter template when--templateis omitted (and falls back tobasicwhen stdin is non-interactive).npm create thinkandthink initinitialize a git repository — skipping cleanly when the target is already inside one — and scaffold projects with Oxlint/Oxfmt config plus acheckscript. Removes the unused declarativeagent()framework helper and the identity helpers (defineMessengers,defineScheduledTasks,defineChannels) in favor of class-based agents and typed object...
Excerpt shown — open the source for the full document.
Notability
notability 3.0/10Routine release of a Cloudflare package.