Skip to content

Version Packages#1373

Merged
threepointone merged 1 commit into
mainfrom
changeset-release/main
Apr 27, 2026
Merged

Version Packages#1373
threepointone merged 1 commit into
mainfrom
changeset-release/main

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented Apr 23, 2026

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

agents@0.11.6

Patch Changes

  • #1393 5aaf7c4 Thanks @threepointone! - Migrate facet (sub-agent) bootstrap to the documented Cloudflare facet API: pass id: parentNs.idFromName(name) to ctx.facets.get() so the facet has its own ctx.id.name. Drops the __ps_name storage write and setName() bootstrap from _cf_initAsFacet.

    Why this matters. Facets spawned without an explicit id inherit the parent DO's ctx.id, so on a facet ctx.id.name was the parent's name and this.name silently misreported as the parent's name. Anything that read this.name from inside a sub-agent (including selfPath, parentPath, and any user code) was getting the wrong value. With the explicit id passed at facet creation time, the runtime gives the facet a real ctx.id.name === name and PartyServer's existing 0.5.x name getter resolves this.name correctly without any override mechanism, storage write, or cold-wake hydrate cost. Cold-wake recovery happens for free because idFromName is deterministic and the factory re-runs on resume.

    This requires partyserver ≥ 0.5.3 (bumped in this release); 0.5.3 is byte-identical to 0.5.2 at runtime, only adds documentation and test coverage of the explicit-id facet pattern.

    Other changes:

    • New error path. If subAgent() is called from a parent class that isn't bound as a Durable Object namespace, the framework now throws a descriptive error pointing at wrangler.jsonc. If this.constructor.name looks minified (e.g. _a), the message includes a bundler-config hint about preserving class names.
    • Defensive runtime check. _cf_initAsFacet now asserts this.name === name so any future bug in the parent's id construction surfaces immediately instead of silently mis-identifying the facet.
    • alarm() docstring clarified to reflect the new resolution path (this.name from ctx.id.name, not from a storage hydrate).
    • MCP test cleanup. Vestigial setName("default") + explicit onStart() call pairs in oauth2-mcp-client, wait-connections-e2e, and create-oauth-provider test files have been removed; they were originally needed for partyserver 0.4.x bootstrap but became actual ctx.id.name mismatches under partyserver 0.5.x.

    Backward-compatible for all public APIs: subAgent(), parentAgent(), hasSubAgent(), listSubAgents(), deleteSubAgent(), and abortSubAgent() keep their signatures and semantics. The change is purely in the facet bootstrap internals; the user-facing effect is that this.name inside a sub-agent now correctly reports the sub-agent's own name (was previously the parent's name when run against partyserver 0.5.x).

    See Document facet bootstrap pattern, no runtime change partykit#386 for the partyserver-side documentation companion.

  • #1395 63cfae6 Thanks @threepointone! - Share submit concurrency bookkeeping through agents/chat and use it from both chat agents.

    This extracts the latest/merge/drop/debounce admission state machine into a SubmitConcurrencyController exported from agents/chat. AIChatAgent semantics (including merge persistence) are preserved. Think now picks up the same pending-enqueue protection, so an overlapping submit is still detected while an accepted request is between admission and turn queue registration.

    Additional fixes:

    • Think now captures the turn generation immediately after admission and threads it into _turnQueue.enqueue, so a clear that lands between admission and queue registration cannot run a stale turn.
    • Pending-enqueue tracking is now bound to a release function tied to the controller's reset epoch, so a release from a pre-reset submit can no longer erase a post-reset submit's marker and let a third submit slip through as non-overlapping.
    • Debounce cancellation correctly resolves all in-flight waiters instead of overwriting a single timer slot.
  • #1396 fdf5a8a Thanks @threepointone! - Fix Think persisting a duplicate orphan assistant row when a user submits during a streaming tool turn (#1381).

    When useAgentChat posts an in-flight assistant snapshot it minted optimistically (client-generated ID, state: "input-available"), Session's INSERT-OR-IGNORE-by-ID would store it as a separate row alongside the eventual server-owned assistant for the same toolCallId. The next turn's convertToModelMessages then produced a malformed Anthropic prompt and the provider rejected it.

    reconcileMessages and resolveToolMergeId now live in agents/chat and Think runs them in _handleChatRequest before persistence. Stale input-available snapshots pick up the server's tool output via mergeServerToolOutputs, and any incoming assistant whose toolCallId already exists on a server row adopts the server's ID so persistence updates the existing row instead of inserting an orphan.

    @cloudflare/ai-chat keeps its existing reconciler behavior; the only change is that it now imports reconcileMessages / resolveToolMergeId from agents/chat instead of a local file.

@cloudflare/ai-chat@0.5.2

Patch Changes

  • #1374 a6e22c3 Thanks @threepointone! - Fix useAgentChat recreating the AI SDK Chat instance — and orphaning any in-flight resumeStream — whenever agent.name transitions in place.

    The useAgent({ basePath }) + static options = { sendIdentityOnConnect: true } pattern lets the server own the Durable Object instance name. The browser starts with a placeholder ("default"), then useAgent mutates the agent object's .name to the server-assigned value when the identity frame arrives. useAgentChat previously included agent.name in the stable chat id it passed to useChat({ id }), so the transition changed the id and the AI SDK recreated the underlying Chat instance. The useEffect that fires chatRef.current.resumeStream() is keyed on the ref object, not the Chat instance, so it does not re-fire on recreation — the resumed stream kept feeding chunks into the orphaned Chat's state while React subscribed to the new Chat's state, so the user saw an empty assistant reply after a mid-stream refresh until the server's final CF_AGENT_CHAT_MESSAGES broadcast landed.

    useAgentChat now distinguishes an in-place agent.name mutation from a genuine "consumer switched chats" event by checking the agent object's reference identity:

    • same agent reference, name mutation → not a chat switch; keep the Chat instance stable.
    • new agent reference → chat switch; recompute the stable chat id so the AI SDK recreates the Chat against the new conversation.

    The stable id is also still upgraded once from the identity-only fallback to the URL-resolved key when the WebSocket handshake completes.

    Consumers who want to switch chats without remounting should pass a different agent object (e.g. a new useAgent({...}) call with a different name). To get a completely fresh Chat (e.g. when mounting a different chat tab), the conventional React pattern — key={chatId} on the parent or swapping the subtree — continues to work.

  • #1395 63cfae6 Thanks @threepointone! - Share submit concurrency bookkeeping through agents/chat and use it from both chat agents.

    This extracts the latest/merge/drop/debounce admission state machine into a SubmitConcurrencyController exported from agents/chat. AIChatAgent semantics (including merge persistence) are preserved. Think now picks up the same pending-enqueue protection, so an overlapping submit is still detected while an accepted request is between admission and turn queue registration.

    Additional fixes:

    • Think now captures the turn generation immediately after admission and threads it into _turnQueue.enqueue, so a clear that lands between admission and queue registration cannot run a stale turn.
    • Pending-enqueue tracking is now bound to a release function tied to the controller's reset epoch, so a release from a pre-reset submit can no longer erase a post-reset submit's marker and let a third submit slip through as non-overlapping.
    • Debounce cancellation correctly resolves all in-flight waiters instead of overwriting a single timer slot.
  • #1396 fdf5a8a Thanks @threepointone! - Fix Think persisting a duplicate orphan assistant row when a user submits during a streaming tool turn (#1381).

    When useAgentChat posts an in-flight assistant snapshot it minted optimistically (client-generated ID, state: "input-available"), Session's INSERT-OR-IGNORE-by-ID would store it as a separate row alongside the eventual server-owned assistant for the same toolCallId. The next turn's convertToModelMessages then produced a malformed Anthropic prompt and the provider rejected it.

    reconcileMessages and resolveToolMergeId now live in agents/chat and Think runs them in _handleChatRequest before persistence. Stale input-available snapshots pick up the server's tool output via mergeServerToolOutputs, and any incoming assistant whose toolCallId already exists on a server row adopts the server's ID so persistence updates the existing row instead of inserting an orphan.

    @cloudflare/ai-chat keeps its existing reconciler behavior; the only change is that it now imports reconcileMessages / resolveToolMergeId from agents/chat instead of a local file.

@cloudflare/shell@0.3.4

Patch Changes

  • #1384 a7059d4 Thanks @threepointone! - Introduce WorkspaceFsLike — the minimum Workspace surface required by WorkspaceFileSystem and createWorkspaceStateBackend.

    WorkspaceFileSystem's constructor and createWorkspaceStateBackend's parameter both now accept any WorkspaceFsLike (a Pick<Workspace, …> of the 16 filesystem methods the adapter reaches for) rather than a concrete Workspace. Non-breaking — Workspace still satisfies WorkspaceFsLike so every existing call site keeps working without changes.

    This unlocks wrapping a real Workspace behind your own layer — most commonly a cross-DO proxy that forwards each call to a parent agent's workspace over RPC — and still using it as the storage for codemode's state.* sandbox API via createWorkspaceStateBackend. See examples/assistant for the end-to-end pattern with SharedWorkspace.

@cloudflare/think@0.4.1

Patch Changes

  • #1395 63cfae6 Thanks @threepointone! - Share submit concurrency bookkeeping through agents/chat and use it from both chat agents.

    This extracts the latest/merge/drop/debounce admission state machine into a SubmitConcurrencyController exported from agents/chat. AIChatAgent semantics (including merge persistence) are preserved. Think now picks up the same pending-enqueue protection, so an overlapping submit is still detected while an accepted request is between admission and turn queue registration.

    Additional fixes:

    • Think now captures the turn generation immediately after admission and threads it into _turnQueue.enqueue, so a clear that lands between admission and queue registration cannot run a stale turn.
    • Pending-enqueue tracking is now bound to a release function tied to the controller's reset epoch, so a release from a pre-reset submit can no longer erase a post-reset submit's marker and let a third submit slip through as non-overlapping.
    • Debounce cancellation correctly resolves all in-flight waiters instead of overwriting a single timer slot.
  • #1394 a0a0d17 Thanks @threepointone! - think: add beforeStep lifecycle hook and output passthrough on TurnConfig.

    • beforeStep(ctx) — new lifecycle hook called before each AI SDK step in the agentic loop, wired to streamText({ prepareStep }). Receives a PrepareStepContext (the AI SDK's PrepareStepFunction parameter — steps, stepNumber, model, messages, experimental_context) and may return a StepConfig (PrepareStepResult) to override model, toolChoice, activeTools, system, messages, experimental_context, or providerOptions for the current step. Use beforeTurn for turn-wide assembly and beforeStep when the decision depends on the step number or previous step results. Resolves #1363.
    • TurnConfig.output — new optional field on TurnConfig forwarded to streamText. Accepts the AI SDK's structured-output spec (e.g. Output.object({ schema }), Output.text()) so a single agent can keep tools enabled on intermediate turns and return schema-validated structured output on a designated turn — without losing tools at model construction. Combine with activeTools: [] for providers that strip tools when responseFormat: "json" is active (e.g. workers-ai-provider). Resolves #1383.
    • New re-exports from @cloudflare/think: PrepareStepFunction, PrepareStepResult, PrepareStepContext, StepConfig.

    beforeStep is available to subclasses; it is not dispatched to extensions (the AI SDK prepareStep boundary surfaces non-serializable inputs like LanguageModel instances). The AI SDK does not expose output or maxSteps per step — set those at the turn level via TurnConfig. All other extension hook subscriptions are unchanged.

  • #1372 040da0f Thanks @threepointone! - Remove Think's unused internal session_id config scaffolding and move Think's private config into a dedicated think_config table.

    Older builds wrote Think-owned config into Session's shared assistant_config(session_id, key, value) table even though Think never actually had top-level multi-session support and _sessionId() always returned the empty string. Think now stores its private config rows in think_config(key, value), which better matches the shipped model of one Think Durable Object per conversation and avoids overloading Session's shared metadata table.

    Existing Durable Objects are migrated automatically on startup: legacy Think-owned keys stored in assistant_config with session_id = '' are copied into think_config before config reads and writes continue.

  • #1396 fdf5a8a Thanks @threepointone! - Fix Think persisting a duplicate orphan assistant row when a user submits during a streaming tool turn (#1381).

    When useAgentChat posts an in-flight assistant snapshot it minted optimistically (client-generated ID, state: "input-available"), Session's INSERT-OR-IGNORE-by-ID would store it as a separate row alongside the eventual server-owned assistant for the same toolCallId. The next turn's convertToModelMessages then produced a malformed Anthropic prompt and the provider rejected it.

    reconcileMessages and resolveToolMergeId now live in agents/chat and Think runs them in _handleChatRequest before persistence. Stale input-available snapshots pick up the server's tool output via mergeServerToolOutputs, and any incoming assistant whose toolCallId already exists on a server row adopts the server's ID so persistence updates the existing row instead of inserting an orphan.

    @cloudflare/ai-chat keeps its existing reconciler behavior; the only change is that it now imports reconcileMessages / resolveToolMergeId from agents/chat instead of a local file.

  • #1374 a6e22c3 Thanks @threepointone! - Fix stream resumption on page refresh: do not broadcast cf_agent_chat_messages from Think's onConnect while a resumable stream is in flight.

    Previously, Think unconditionally sent a cf_agent_chat_messages frame on every new WebSocket connection. When a client refreshed during an active chat turn, that broadcast arrived in the same connect sequence as cf_agent_stream_resuming and overwrote the in-progress assistant message the client was about to rebuild from the resumed stream. The assistant reply would stay hidden until the server finished the turn and re-broadcast the persisted history.

    Now Think only broadcasts cf_agent_chat_messages on connect when there is no active resumable stream. During an active stream the resume flow is the authoritative source of state: STREAM_RESUMING triggers replay of buffered chunks, and the final state broadcast happens when the turn completes. This matches the behavior that AIChatAgent already had.

    Marked the internal _resumableStream field as protected (previously private) so framework subclasses and focused tests can coordinate around the resume lifecycle.

  • #1384 a7059d4 Thanks @threepointone! - Introduce WorkspaceLike — type the this.workspace field as the minimum surface Think actually uses instead of the concrete Workspace class.

    Think's workspace is now typed as WorkspaceLike (Pick<Workspace, "readFile" | "writeFile" | "readDir" | "rm" | "glob" | "mkdir" | "stat">) rather than Workspace. createWorkspaceTools() likewise accepts any WorkspaceLike. The default runtime value is unchanged — a full Workspace backed by the DO's SQLite — so the vast majority of consumers need no changes.

    This unlocks patterns like a shared workspace across multiple agents: a child agent can override workspace with a proxy that forwards each call to a parent DO via RPC, and the rest of Think's workspace-aware code (the builtin tools, lifecycle hooks) keeps working without cast gymnastics. See examples/assistant for the cross-chat shared workspace built on this.

    Consumers who use createWorkspaceStateBackend(workspace) from @cloudflare/shell (codemode's state.* API) still need a concrete Workspace — that helper reaches for more of the filesystem surface than WorkspaceLike covers.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@github-actions github-actions Bot force-pushed the changeset-release/main branch 10 times, most recently from 54f8dac to b067526 Compare April 26, 2026 21:36
@github-actions github-actions Bot force-pushed the changeset-release/main branch from b067526 to e04d0ef Compare April 26, 2026 22:26
@threepointone threepointone merged commit 1ad4cde into main Apr 27, 2026
@threepointone threepointone deleted the changeset-release/main branch April 27, 2026 08:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant