agents@0.17.0

cloudflare/agentsagents@0.17.0Jun 26, 2026by github-actions[bot]

AI Summary

Adds progress signalling and durable milestones for sub-agents, alongside first-class detached runs and improved observability events for action ledgers.

Key Highlights

  • First-class detached agent-tool runs with durable `onFinish` hooks
  • Progress signalling and durable milestones for sub-agents
  • Typed observability events for action ledgers and durable-pause approvals
  • Support for `ReplyAttachment` type and `ChatResponseResult.attachments`
  • WebSocket connection error handling via `connectionError` and `onConnectionError`

New Features

  • Detached run lifecycle management
  • Action ledger observability
  • Attachment type definitions
  • WebSocket error exposure

Full Release Notes

### Minor Changes

-   [#1758](https://github.com/cloudflare/agents/pull/1758) [`6b46b04`](https://github.com/cloudflare/agents/commit/6b46b044c03e9fda280c9916fef6ec8b6baa7d73) Thanks [@threepointone](https://github.com/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:

    ```ts
    // 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:

    ```ts
    // 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/think` and
        `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.

        ```ts
        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.

-   [#1790](https://github.com/cloudflare/agents/pull/1790) [`190ea81`](https://github.com/cloudflare/agents/commit/190ea814c4ea61c216509a431baa8be06d917256) Thanks [@threepointone](https://github.com/threepointone)! - Add typed action ledger observability events to the diagnostics channel event union.

-   [#1790](https://github.com/cloudflare/agents/pull/1790) [`190ea81`](https://github.com/cloudflare/agents/commit/190ea814c4ea61c216509a431baa8be06d917256) Thanks [@threepointone](https://github.com/threepointone)! - Add typed `action:pause:*` (created/approved/rejected/swept) observability events to the diagnostics channel event union for durable-pause action approvals.

-   [#1790](https://github.com/cloudflare/agents/pull/1790) [`190ea81`](https://github.com/cloudflare/agents/commit/190ea814c4ea61c216509a431baa8be06d917256) Thanks [@threepointone](https://github.com/threepointone)! - Add the `ReplyAttachment` type and an optional `attachments` field on `ChatResponseResult`, plus an `action:reply-attached` diagnostics event, to support the Think actions reply-attachment side-channel.

-   [#1799](https://github.com/cloudflare/agents/pull/1799) [`3c2afc9`](https://github.com/cloudflare/agents/commit/3c2afc9379f34fe51e401999ec03e9efc0fe93f2) Thanks [@threepointone](https://github.com/threepointone)! - Stop reconnecting on terminal WebSocket close events and expose terminal connection failures via `connectionError` / `onConnectionError` on `AgentClient`, `useAgent`, and `useAgentChat`.

-   [#1758](https://github.com/cloudflare/agents/pull/1758) [`6b46b04`](https://github.com/cloudflare/agents/commit/6b46b044c03e9fda280c9916fef6ec8b6baa7d73) Thanks [@threepointone](https://github.com/threepointone)! - Add first-class detached ("background") agent-tool runs with a durable
    completion hook (cloudflare/agents#1752).

    `runAgentTool(cls, { detached })` now dispatches a sub-agent **without blocking
    the calling turn**, returning a `{ runId, agentType, status: "running" }` handle
    immediately:

    ```ts
    // Fire-and-forget — observe via agent-tool-event frames + onAgentToolFinish.
    const { runId } = await this.runAgentTool(ImportAgent, {
      input,
      detached: true
    });

    // Or wire a durable, eviction-surviving completion callback (by METHOD NAME,
    // like schedule()):
    await this.runAgentTool(ImportAgent, {
      input,
      detached: { onFinish: "onImportDone", maxBudgetMs: 60 * 60 * 1000 }
    });

    async onImportDone(run: AgentToolRunInfo, result: AgentToolLifecycleResult) {
      // Branch on result.status: "completed" | "error" | "aborted" | "interrupted".
      // A budget give-up arrives as interrupted / reason "budget-exceeded".
    }
    ```

    Highlights:

    -   **Durable, exactly-once-on-the-happy-path completion.** A warm fast path
        (low-latency while the isolate is alive) plus a self-scheduling reconcile
        backbone (survives eviction / deploys) route through one guarded delivery
        funnel. Two independent ledger slots (finish / give-up) with a claim+lease
        mean a premature give-up can never dedupe a child's real late completion away.
    -   **No silent abandonment.** Detached runs are never sealed `interrupted` just
        because their dispatching turn ended (the normal state for a background run);
        the backbone owns them and re-arms on restart.
    -   **Bounded.** An absolute `maxBudgetMs` ceiling (default 24h, configurable via
        the `detachedMaxBudgetMs` static option) gives up — surfaced as `interrupted`
        with the new `budget-exceeded` reason — and tears the child down so an
        abandoned run cannot hold a concurrency slot forever.
    -   **`cancelAgentTool(runId)`** cancels a detached (or awaited) run by id through
        the same guarded path, so a wired `onFinish` still fires once with
        `status: "aborted"`, and the terminal `agent-tool-event` is always broadcast
        to connected clients (a cancelled run's UI settles immediately).
    -   **Recovery-safe delivery.** A chat host (`@cloudflare/think` / `AIChatAgent`)
        runs the completion callback serialized on its turn queue, so an `onFinish`
        that mutates chat state can never interleave with a live LLM turn. Concurrent
        detached dispatches in one turn no longer race to arm multiple reconcile
        backbones (arming is serialized).
    -   **Observability.** New events `agent_tool:detached:delivery_failed` (a wired
        callback threw; the slot stays open for retry) and
        `agent_tool:detached:live_count_warning` (edge-triggered when live detached
        runs cross a threshold — a leak smoke alarm, since detached runs hold a
        concurrency slot for their whole life).

    A detached run deliberately does NOT inherit `options.signal` (it must outlive
    the spawning turn); cancel it explicitly with `cancelAgentTool`.

-   [#1801](https://github.com/cloudflare/agents/pull/1801) [`c58b401`](https://github.com/cloudflare/agents/commit/c58b4015b7616581b3d7fca86a5fde6e49bd9cd3) Thanks [@threepointone](https://github.com/threepointone)! - Add the shared `agents/chat/react` entry with `useAgentChat`, chat transport helpers, and shared chat wire types. The hook also adds `syncMessagesToServer` so hosts with server-authoritative transcript storage can keep `setMessages` local-only.

### Patch Changes

-   [#1790](https://github.com/cloudflare/agents/pull/1790) [`190ea81`](https://github.com/cloudflare/agents/commit/190ea814c4ea61c216509a431baa8be06d917256) Thanks [@threepointone](https://github.com/threepointone)! - De-duplicate three adapter-spine helpers shared by `@cloudflare/ai-chat` and
    `@cloudflare/think` into `agents/chat`.

    Three fragments that were duplicated byte-for-byte across both hosts now live
    once as shared, `@internal` primitives:

    -   `async-helpers.ts` — the `TIMED_OUT` sentinel, `awaitWithDeadline` (a
        deadline-bounded promise race that always clears its timer), and
        `drainInteractionApplies` (the substrate-free interaction-apply completeness
        drain, parameterized by `hasPending` / `getTail`).
    -   `classifyAgentToolChildRecovery(storage)` (in `recovery-incident.ts`) — the
        parent's agent-tool reattach incident scan, with `in-progress > failed > none`
        precedence so a parent never gives up on a still-recovering child.
    -   `interceptAgentToolBroadcast(msg, hooks)` (in `agent-tools.ts`) — the [#1575](https://github.com/cloudflare/agents/issues/1575)
        outgoing-frame snoop that forwards an agent-tool child's streamed progress to
        its live tailers (or captures its error), parameterized by an
        `AgentToolBroadcastHooks` substrate (the per-run forwarder / live-sequence /
        last-error maps, the host's response-frame type, and the host run-lookup).

    Both hosts delegate through their existing private method names and
    `broadcast()` overrides (which still call `super.broadcast`), so every call site
    is untouched. This is a pure internal de-duplication with no observable behavior
    or API change: the new symbols are `@internal` sibling-package support, not
    public API, and both hosts' existing test suites pass unchanged.
    `@cloudflare/ai-chat` and `@cloudflare/think` need no changeset for this
    extraction.

-   [#1790](https://github.com/cloudflare/agents/pull/1790) [`190ea81`](https://github.com/cloudflare/agents/commit/190ea814c4ea61c216509a431baa8be06d917256) Thanks [@threepointone](https://github.com/threepointone)! - Add the `action:ledger:reclaimed` diagnostics event to `AgentObservabilityEvent`, emitted when a stale `pending` action ledger row is reclaimed and re-run under the Think pending-retry lease.

-   [#1790](https://github.com/cloudflare/agents/pull/1790) [`190ea81`](https://github.com/cloudflare/agents/commit/190ea814c4ea61c216509a431baa8be06d917256) Thanks [@threepointone](https://github.com/threepointone)! - Add `channel:resolved`, `channel:delivered`, `notice:delivered`, and `notice:failed` observability events to `AgentObservabilityEvent` for the Think channels surface. These route to a dedicated `agents:channel` diagnostics channel and are reachable via the typed `subscribe("channel", cb)` API (new `ChannelEventMap` bucket) rather than falling through to the catch-all `lifecycle` channel.

-   [#1796](https://github.com/cloudflare/agents/pull/1796) [`058a781`](https://github.com/cloudflare/agents/commit/058a781dae3e28a52fbd81a8b4b1bbd9d07c19c0) Thanks [@mattzcarey](https://github.com/mattzcarey)! - Make the `ai` peer optional again. The root `agents` runtime and declaration graph no longer reference AI SDK types; AI-specific entry points still require the peer when imported.

-   [#1802](https://github.com/cloudflare/agents/pull/1802) [`391b034`](https://github.com/cloudflare/agents/commit/391b0340299fa2079c8665ee5343c4667ed3ddcf) Thanks [@threepointone](https://github.com/threepointone)! - Ensure tool approval updates always retain a provider-facing approval id.

    Older or hand-seeded transcripts can contain an `approval-requested` tool part
    without an `approval.id`. When that part is approved and auto-continuation
    re-enters inference, the AI SDK requires a matching approval id in the converted
    model messages. Approval updates now synthesize a stable id from the
    `toolCallId` when the transcript is missing one, preventing invalid prompt
    errors while preserving existing approval metadata. `@cloudflare/ai-chat` now
    routes its approval merge through the shared `toolApprovalUpdate` builder so it
    benefits from the same fallback instead of its own divergent copy.

-   [#1790](https://github.com/cloudflare/agents/pull/1790) [`190ea81`](https://github.com/cloudflare/agents/commit/190ea814c4ea61c216509a431baa8be06d917256) Thanks [@threepointone](https://github.com/threepointone)! - Extract the shared auto-continuation barrier into an `AutoContinuationController`
    primitive in `agents/chat`.

    The event-driven auto-continuation barrier (the tool-result → auto-continue flow,
    [#1649](https://github.com/cloudflare/agents/issues/1649) / [#1650](https://github.com/cloudflare/agents/issues/1650)) was duplicated, line-for-line, across `@cloudflare/ai-chat`
    (`AIChatAgent`) and `@cloudflare/think` (`Think`) — the coalesce timer, the
    double-fire guard, the create/update/defer scheduling branch, and the
    completeness-gated drain orchestration. It now lives once as
    `AutoContinuationController`, parameterized over a small `AutoContinuationHost`
    interface (the stream-active signal, the incomplete-batch / pending-interaction
    predicates, the apply-drain primitive, and each host's continuation-turn
    pipeline). Both hosts delegate to it through thin wrappers, so every call site is
    untouched.

    This is a pure internal de-duplication with no observable behavior or API change:
    the new symbols are `@internal` sibling-package support, not public API, and both
    hosts' existing test suites pass unchanged. `@cloudflare/ai-chat` and
    `@cloudflare/think` need no changeset for this extraction.

-   [#1805](https://github.com/cloudflare/agents/pull/1805) [`60e53e0`](https://github.com/cloudflare/agents/commit/60e53e01a46d212a525d19aaac8835afb7ae27e2) Thanks [@threepointone](https://github.com/threepointone)! - Fix `useAgentChat` reordering a terminal/authoritative `CF_AGENT_CHAT_MESSAGES` snapshot when a protected streaming assistant is followed by a later assistant message ([#1778](https://github.com/cloudflare/agents/issues/1778)). The protected-tail merge is still applied for stale mid-stream snapshots, but when the incoming snapshot already contains the protected assistant followed by a newer assistant (e.g. a Think HITL denial that persists the denied tool message and then appends a follow-up assistant response), protection is cleared and the snapshot is rendered in its authoritative order instead of moving the protected assistant to the end.

-   [#1788](https://github.com/cloudflare/agents/pull/1788) [`3b2af54`](https://github.com/cloudflare/agents/commit/3b2af5444af5002cd54fd493452e03c721d31999) Thanks [@threepointone](https://github.com/threepointone)! - Converge recovery forward-progress crediting between `AIChatAgent` and `Think`.

    Both hosts now credit the recovery no-progress counter through one shared, host-agnostic rule (`shouldCreditStreamProgress`): a progress milestone (a started text/reasoning segment or a settled tool input/output) credits unconditionally, and mid-segment streaming deltas (`text-delta`/`reasoning-delta`/`tool-input-delta`) credit at most once per throttle window via a per-isolate `StreamProgressCreditThrottle`. Previously `AIChatAgent` credited only on chunk-type milestones while `Think` credited on its flush cadence, so a long single content segment spanning repeated crashes could read as "no progress" under `AIChatAgent` and false-fire its `no_progress_timeout`. The new rule is never coarser than either host's prior cadence, so it can only delay or avoid a false no-progress timeout, never hasten give-up.

-   [#1430](https://github.com/cloudflare/agents/pull/1430) [`d1c4342`](https://github.com/cloudflare/agents/commit/d1c4342b03adac6d5962318e63c682009614a406) Thanks [@threepointone](https://github.com/threepointone)! - Support starting Agent workflows from sub-agent facets by preserving the originating facet path for callbacks and workflow Agent RPC.

    -   Route workflow callbacks, `this.agent` RPC, progress/completion/error, durable events, and state updates back to the exact originating facet via path-based dispatch.
    -   Match real Durable Object stub RPC semantics in path dispatch: reject built-in/prototype and JS-internal method names.
    -   Validate the workflow origin payload version so a mismatched SDK fails with a clear error instead of misreading the shape.
    -   Document the callback routing constraints (name-based resolution, sub-agent workflow tracking is facet-local, class names must survive bundling).

-   [#1818](https://github.com/cloudflare/agents/pull/1818) [`fa7bfec`](https://github.com/cloudflare/agents/commit/fa7bfec9e7a3524bfadf589b582aee1e960f4615) Thanks [@threepointone](https://github.com/threepointone)! - Stop a `tool-output-denied` chunk from clobbering a settled or user-approved
    tool part in `agents/chat`.

    `applyChunkToParts` now treats `tool-output-denied` as first-write-wins: it
    leaves a part already in `output-available` / `output-error` / `output-denied`
    untouched, and — importantly — no longer flips an `approval-responded`
    (user-approved) part to `output-denied`. An auto-continuation that re-validates
    the transcript can legitimately emit `tool-output-denied` for an approval the
    AI SDK deems unneeded (e.g. a tool without `needsApproval`); previously that
    silently turned a granted approval into a denial in the persisted message. This
    matches the first-write-wins guards already on the `tool-input-*` handlers and
    benefits both `@cloudflare/ai-chat` and `@cloudflare/think`.

    `isReplayChunk` now recognizes the same replayed `tool-output-denied` (for a
    part already settled or in `approval-responded`). The server-side guard only
    protects the persisted message; without this, the stale denial chunk was still
    stored in the resumable-stream buffer and broadcast to connected clients, where
    AI SDK v6's in-place `updateToolPart` would regress the rendered tool part back
    to `output-denied` (and replay it on reconnect). Filtering it at the broadcast
    boundary keeps the client UI consistent with the persisted state.

    `tool-approval-request` gains the same treatment on both paths: a
    first-write-wins guard in `applyChunkToParts` (so a replayed approval request
    can't regress an already-`approval-responded` or settled part back to
    `approval-requested`, discarding the user's decision) and a matching
    `isReplayChunk` branch (so the replayed request isn't stored or broadcast,
    which would otherwise revert an approved tool to re-showing Approve/Reject on
    the client and on reconnect).

-   [#1734](https://github.com/cloudflare/agents/pull/1734) [`cd2c430`](https://github.com/cloudflare/agents/commit/cd2c43075738a52093f8fb1b8261ce3871de2526) Thanks [@whoiskatrin](https://github.com/whoiskatrin)! - Allow `McpAgent` server-to-client requests to send from callbacks that do not inherit the agent's async context, including callbacks reached through Worker Loader RPC.

-   [#1794](https://github.com/cloudflare/agents/pull/1794) [`b6ad4d5`](https://github.com/cloudflare/agents/commit/b6ad4d5bc078ede978b9b68fd7beb6ed3194f848) Thanks [@threepointone](https://github.com/threepointone)! - De-duplicate the orphan-persist core shared by `@cloudflare/ai-chat` and
    `@cloudflare/think` into `agents/chat`.

    The genuinely-common skeleton of both hosts' `_persistOrphanedStream` — the
    accumulate loop plus the `getMessage → updateMessage(merge) XOR appendMessage`
    upsert — now lives once as the `@internal` `persistReconstructedOrphan` helper.
    The deliberately host-specific bits stay in the callers: the buffer flush, the
    fallback id, the `prepare` hook (Think strips internal parts and may skip;
    ai-chat resolves the persist-target id), the `merge` hook (Think replaces;
    ai-chat reconciles partials), and broadcast (Think after; ai-chat inside its
    store's `persistMessages`).

    Pure internal de-duplication with no observable behavior or API change: the new
    symbol is `@internal` sibling-package support, not public API, and both hosts'
    recovery suites pass unchanged. `@cloudflare/ai-chat` and `@cloudflare/think`
    need no changeset for this extraction.

-   [#1788](https://github.com/cloudflare/agents/pull/1788) [`3b2af54`](https://github.com/cloudflare/agents/commit/3b2af5444af5002cd54fd493452e03c721d31999) Thanks [@threepointone](https://github.com/threepointone)! - Export `reconcileOrphanPartial` from `agents/chat`.

    This is the shared primitive that merges a freshly-reconstructed orphaned stream
    partial onto an assistant message that already owns its target id (an early
    persist at tool-approval time, or a continuation resuming the prior assistant
    message). It keeps all existing parts, appends only reconstructed parts whose
    `toolCallId` is not already present (so a recovery replay never duplicates a
    tool call), and overlays incoming metadata onto existing — preserving an
    in-place tool result that lives only in storage rather than letting a replayed
    chunk re-advance it. `@cloudflare/ai-chat`'s orphan-persist path now uses it;
    hosts whose orphan persist only runs at stream finalize don't need it (the
    shared reconstruction is already idempotent by `toolCallId`).

    Additive export only — no behavior change to existing APIs.

-   [#1788](https://github.com/cloudflare/agents/pull/1788) [`3b2af54`](https://github.com/cloudflare/agents/commit/3b2af5444af5002cd54fd493452e03c721d31999) Thanks [@threepointone](https://github.com/threepointone)! - Export the `OrphanPersistStore<M = UIMessage>` type from `agents/chat`.

    This is the minimal message store the chat-recovery orphan-persist write goes
    through — the write subset of `SessionProvider` (the `getMessage`,
    `appendMessage`, and `updateMessage` methods). It is parameterized over the
    host's message type so the seam itself is not AI-SDK-specific: the AI-SDK chat
    hosts (`@cloudflare/ai-chat`, `@cloudflare/think`) instantiate it at the
    `UIMessage` default, while `SessionProvider` satisfies it at its own
    `SessionMessage`. Both hosts now route their orphan-persist write through a host
    adapter typed against this interface, turning the previous by-convention
    alignment into a type-enforced contract.

    Additive export only — no behavior change to existing APIs.

-   [#1803](https://github.com/cloudflare/agents/pull/1803) [`c476265`](https://github.com/cloudflare/agents/commit/c476265c9f18a2a6eb5f01137515a8776ca8b63c) Thanks [@threepointone](https://github.com/threepointone)! - Fix AI SDK `status` getting stuck after a reconnect that races a turn's
    pre-stream window ([#1784](https://github.com/cloudflare/agents/issues/1784)).

    A turn is "accepted but pre-stream" while it is queued, debouncing, or awaiting
    async setup before its resumable stream starts. A client that connected or sent
    a `STREAM_RESUME_REQUEST` in that window was answered with `STREAM_RESUME_NONE`
    ("nothing to resume"), so its short resume probe resolved `null` and AI SDK
    `status` settled on `ready` even though the server went on to stream — leaving
    the UI unable to render the in-flight turn until a full remount.

    This adds a shared `PreStreamTurns` tracker (`agents/chat`) and a new
    server→client `cf_agent_stream_pending` frame:

    -   The resume handshake now parks resume requests that arrive during the
        pre-stream window and emits `STREAM_PENDING` ("keep waiting") instead of
        `STREAM_RESUME_NONE`, then flushes parked connections into the normal
        `STREAM_RESUMING` handshake once the stream actually starts (and releases them
        with `STREAM_RESUME_NONE` if the turn is superseded/cleared before streaming).
    -   On `STREAM_PENDING` the client transport extends its resume probe from the
        5s fast-path to a 60s backstop so the probe stays open across the gap.
    -   `useAgentChat` re-probes the stream on a transparent socket reopen (e.g. a
        1006 reconnect that does not remount the component) so `status` recovers.
    -   Continuation affinity is relaxed via an optional `isConnectionPresent` host
        hook so a transparent reconnect (whose connection id changed) can resume a
        continuation whose original owner connection is gone.

    Wired into both `AIChatAgent` and `@cloudflare/think`.

    The pre-stream tracker is in-memory only; it is hibernation-safe because a turn
    in its pre-stream window is an unresolved message-handler promise that pins the
    Durable Object in memory, so eviction only happens once a stream is durably
    recorded (and resumes via `ResumableStream`) or the turn has finished. Skipped
    turns (supersede/generation change) settle without releasing parked
    connections, so a client parked during the window survives onto the successor
    turn instead of being cut loose by a premature `STREAM_RESUME_NONE`.

-   [#1794](https://github.com/cloudflare/agents/pull/1794) [`b6ad4d5`](https://github.com/cloudflare/agents/commit/b6ad4d5bc078ede978b9b68fd7beb6ed3194f848) Thanks [@threepointone](https://github.com/threepointone)! - Extract transcript repair into a shared `agents/chat` primitive.

    `@cloudflare/think`'s `_repairToolTranscriptParts` — which flips an interrupted
    tool call (a `tool-*` / `dynamic-tool` part with no settled result, left behind
    when a stream was cut off mid-flight) into an errored tool-result so the next
    provider call doesn't 400 with `AI_MissingToolResultsError`, and normalizes
    malformed tool `input` — now lives once as the shared, `@internal`
    `repairInterruptedToolParts` primitive (plus the `toolPartHasSettledResult`
    terminal-state check) in `agents/chat`.

    The primitive is pure (returns a new messages array plus repair stats; never
    touches storage, broadcast, or events) and is parameterized by an overridable
    `repairPart` hook plus an optional `shouldRepair(part)` skip predicate (defaults
    to repairing every interrupted part), so both AI-SDK chat hosts can run repair
    logic before re-entering inference on a recovered turn — a host whose default
    errors the part (ai-chat) uses `shouldRepair` to leave a part still awaiting a
    client interaction verbatim. `@cloudflare/think` delegates through its existing
    `repairInterruptedToolPart` hook with no `shouldRepair` (repairs everything) — a
    pure internal refactor with no observable behavior or API change; its suites pass
    unchanged.

-   [#1772](https://github.com/cloudflare/agents/pull/1772) [`d4f27fe`](https://github.com/cloudflare/agents/commit/d4f27fededefebc17cf455218e952ff76ade847b) Thanks [@mattzcarey](https://github.com/mattzcarey)! - Include each package's documentation in its published package.

-   [#1815](https://github.com/cloudflare/agents/pull/1815) [`b20d6f6`](https://github.com/cloudflare/agents/commit/b20d6f649c79f472e11b02bd6e065e1b645286f2) Thanks [@threepointone](https://github.com/threepointone)! - Move `just-bash` from a runtime dependency to an optional peer dependency so apps only install it when they use the skills bash runner.

-   [#1790](https://github.com/cloudflare/agents/pull/1790) [`190ea81`](https://github.com/cloudflare/agents/commit/190ea814c4ea61c216509a431baa8be06d917256) Thanks [@threepointone](https://github.com/threepointone)! - Add stable approval descriptors for Think actions and preserve approval descriptor metadata on chat tool parts.

-   [#1790](https://github.com/cloudflare/agents/pull/1790) [`190ea81`](https://github.com/cloudflare/agents/commit/190ea814c4ea61c216509a431baa8be06d917256) Thanks [@threepointone](https://github.com/threepointone)! - Add `chat:turn:start` and `chat:turn:finish` observability events for Think
    turn execution.