Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f89c9fd
refactor(agents): add shared AutoContinuationController primitive
threepointone Jun 20, 2026
a47457d
refactor(ai-chat, think): adopt AutoContinuationController; converge …
threepointone Jun 20, 2026
d45976f
docs(design): mark AutoContinuationController done; archive recovery …
threepointone Jun 20, 2026
7af8668
refactor(agents): add shared adapter-spine helpers for the chat hosts
threepointone Jun 20, 2026
a385baa
refactor(ai-chat, think): delegate adapter-spine helpers to agents/chat
threepointone Jun 20, 2026
9189843
docs(design): mark adapter-spine helpers (Tier A+B) done
threepointone Jun 20, 2026
ac258fa
feat(think): add runTurn facade
threepointone Jun 20, 2026
3e7a7f7
refactor(think): route turns through admitTurn
threepointone Jun 20, 2026
d40c582
feat(think): emit admitted turn observability
threepointone Jun 20, 2026
8f5f2aa
docs(design): align turn observability RFC
threepointone Jun 20, 2026
35fd949
feat(think): add action descriptors
threepointone Jun 20, 2026
26d23ff
fix(think): harden action descriptors
threepointone Jun 21, 2026
243106a
feat(think): add action approval descriptors
threepointone Jun 21, 2026
7937c41
feat(think): add action authorization hooks
threepointone Jun 21, 2026
3a4ee73
fix(think): harden action approval edge cases
threepointone Jun 21, 2026
67dc293
feat(think): add action ledger
threepointone Jun 21, 2026
a5d12a7
feat(think): add durable action approvals and reply attachments
threepointone Jun 21, 2026
3d49411
feat(think): add action ledger pending leases
threepointone Jun 21, 2026
7be46b9
feat(think): generalize messengers into channels + deliverNotice
threepointone Jun 22, 2026
05ea79b
feat(think): out-of-turn messenger notices + channels follow-up tracking
threepointone Jun 22, 2026
4aac9a6
fix(observability): route channel:* and notice:* to a dedicated chann…
threepointone Jun 22, 2026
fe824ce
fix(think): merge web channel policy overrides over implicit defaults
threepointone Jun 22, 2026
ede4476
fix(think): reserve web channel id in the getMessengers() loop too
threepointone Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .changeset/adapter-spine-helpers-dedup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
"agents": patch
---

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
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.
5 changes: 5 additions & 0 deletions .changeset/agents-action-ledger-observability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"agents": minor
---

Add typed action ledger observability events to the diagnostics channel event union.
5 changes: 5 additions & 0 deletions .changeset/agents-action-ledger-reclaimed-event.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"agents": patch
---

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.
5 changes: 5 additions & 0 deletions .changeset/agents-action-pause-observability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"agents": minor
---

Add typed `action:pause:*` (created/approved/rejected/swept) observability events to the diagnostics channel event union for durable-pause action approvals.
5 changes: 5 additions & 0 deletions .changeset/agents-channel-observability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"agents": patch
---

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.
5 changes: 5 additions & 0 deletions .changeset/agents-reply-attachment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"agents": minor
---

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.
22 changes: 22 additions & 0 deletions .changeset/auto-continuation-controller-extraction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"agents": patch
---

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 / #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.
6 changes: 6 additions & 0 deletions .changeset/think-action-approval-descriptors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@cloudflare/think": patch
"agents": patch
---

Add stable approval descriptors for Think actions and preserve approval descriptor metadata on chat tool parts.
5 changes: 5 additions & 0 deletions .changeset/think-action-approval-followups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/think": patch
---

Harden action approval and authorization edge cases around approved inputs and continuation rechecks.
5 changes: 5 additions & 0 deletions .changeset/think-action-attach-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/think": minor
---

Add `ctx.attachReply(attachment)` for actions: an advisory, recording-only reply-attachment side-channel surfaced on `ChatResponseResult.attachments` (in `onChatResponse`) and a public `replyAttachments(requestId?)` getter. Attachments are JSON-normalized, deep-copied on read, capped per turn, and never alter the model-visible tool output; policy callbacks are no-ops, failed executions discard their attachments, approval-gated approved actions support it, and durable-pause approved actions are a v1 no-op.
5 changes: 5 additions & 0 deletions .changeset/think-action-authorization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/think": patch
---

Add action permission metadata and default-full-grant authorization hooks for Think actions.
5 changes: 5 additions & 0 deletions .changeset/think-action-ledger-pending-retry-lease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/think": minor
---

Add a pending-retry lease for the action ledger via the new `actionLedgerPendingRetryLeaseMs` config (default 5 minutes). A `pending` ledger row left behind by a crashed executor is now reclaimed and re-run once it is stale, but ONLY for actions that declare an explicit `idempotencyKey` — the key is the developer's assertion that re-running the keyed side effect is safe. Behavior change: such a stale row previously blocked forever with `ActionPendingError`; it now reclaims (refreshing `updated_at` in place, still `pending`), emits `action:ledger:reclaimed`, and re-runs `execute`. Fresh rows, fallback `tool:${toolCallId}` keys, and a disabled lease (`actionLedgerPendingRetryLeaseMs = false`) keep the conservative `ActionPendingError` behavior. Same-isolate coalescing still wins first, so an in-flight run is never reclaimed.
5 changes: 5 additions & 0 deletions .changeset/think-action-ledger.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/think": minor
---

Add a durable action ledger for `action()` descriptors so settled server action outputs can be replayed by stable idempotency key without re-running side effects.
6 changes: 6 additions & 0 deletions .changeset/think-actions-descriptor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@cloudflare/think": patch
---

Add the `action()` descriptor and `getActions()` hook for compiling guarded
server actions into Think tools.
6 changes: 6 additions & 0 deletions .changeset/think-actions-hardening.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@cloudflare/think": patch
---

Harden Think action descriptors with schema-inferred inputs and JSON-safe output
normalization.
7 changes: 7 additions & 0 deletions .changeset/think-admit-turn-spine.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@cloudflare/think": patch
---

Route Think turn entry points through a shared internal `_admitTurn` spine and
throw a clear error for nested blocking turn admissions that previously could
deadlock.
5 changes: 5 additions & 0 deletions .changeset/think-channels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/think": minor
---

Generalize the messenger runtime into a public channel surface. Add `configureChannels()` and `ChannelDefinition` (web, voice, messenger, and custom channels) wrapping `getMessengers()`, a no-turn `deliverNotice()` with `informModel`, additive `DeliveryTag` (kind + turnEnded) on messenger snapshots, per-channel policy (instructions, tool-narrowing, `maxTurns`) applied as overridable defaults, turn-scoped channel context threaded through `runTurn` (persisted for recovery), reply-attachment rendering at delivery, and `channel:*`/`notice:*` observability events.
5 changes: 5 additions & 0 deletions .changeset/think-durable-pause-descriptors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/think": minor
---

Add durable-pause approval descriptors: `durable-pause` actions now park in a dedicated `cf_think_action_pending_approvals` store and resume via `approveExecution`/`rejectExecution` with a connection-independent continuation, so a turn can be approved from a dashboard with no live socket (this also fixes codemode `approveExecution` from a dashboard). A unified `ActionApprovalDescriptor` is attached to durable-pause, codemode, and approval-gated parts, `pendingApprovals()` lists all pending approvals for cold-load reconciliation, and an overridable `describePausedExecution()` hook enriches codemode descriptors.
9 changes: 9 additions & 0 deletions .changeset/think-runturn-facade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@cloudflare/think": minor
---

Add public `runTurn(options)` facade (Turns RFC step 2): unified turn admission
with `mode: "wait" | "submit" | "stream"` delegating to the existing
`saveMessages`, `continueLastTurn`, `submitMessages`, and `chat` methods.
Exports `TurnInputMessages`, `RunTurnWait`, `RunTurnSubmit`, `RunTurnStream`,
`RunTurnOptions`, and `TurnResult`.
7 changes: 7 additions & 0 deletions .changeset/think-turn-observability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@cloudflare/think": patch
"agents": patch
---

Add `chat:turn:start` and `chat:turn:finish` observability events for Think
turn execution.
17 changes: 17 additions & 0 deletions .changeset/think-waituntilstable-armed-continuation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@cloudflare/think": patch
---

`Think.waitUntilStable()` now waits out an armed-but-unfired auto-continuation
before reporting stable, converging onto `@cloudflare/ai-chat`.

Previously, when a turn ended with no pending human/client interaction,
`waitUntilStable()` reported stable immediately — even if an auto-continuation
was armed (its ~50ms coalesce timer still pending, or its completeness drain in
flight). In that window idle eviction or chat recovery could act on a transcript
that was about to be continued. `Think` now mirrors `@cloudflare/ai-chat`: while
a continuation is armed (`pending && !pastCoalesce` and the shared
`AutoContinuationController` reports armed), `waitUntilStable()` reports
not-stable and waits out the coalesce window, then re-checks (the continuation
either fires and enqueues a turn the loop drains, or parks and clears, at which
point the agent is genuinely stable).
6 changes: 5 additions & 1 deletion design/chat-shared-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ packages/agents/src/chat/ ← shared foundation
submit-concurrency.ts SubmitConcurrencyController
broadcast-state.ts broadcastTransition state machine
continuation-state.ts ContinuationState
auto-continuation-controller.ts AutoContinuationController (barrier timer + double-fire guard)
async-helpers.ts TIMED_OUT, awaitWithDeadline, drainInteractionApplies
abort-registry.ts AbortRegistry
resumable-stream.ts ResumableStream (SQLite chunk buffer)
sql-batch.ts bound-param batching for IN-clause deletes
Expand All @@ -36,7 +38,7 @@ packages/agents/src/chat/ ← shared foundation
protocol.ts CHAT_MESSAGE_TYPES constants (chat + resume + tool)
parse-protocol.ts parseProtocolMessage
tool-state.ts tool-part update / interaction helpers
agent-tools.ts agent-tool-as-child event state
agent-tools.ts agent-tool-as-child event state + broadcast snoop (interceptAgentToolBroadcast)
message-reconciler.ts reconcileMessages, resolveToolMergeId, reconcileOrphanPartial, assistantContentKey
orphan-store.ts OrphanPersistStore interface
lifecycle.ts shared lifecycle / result / config types
Expand Down Expand Up @@ -326,3 +328,5 @@ The machine handles accumulator creation (including continuation context walking
- Client tool primitives (`ClientToolSchema`, `createToolsFromClientSchemas`) moved to `agents/chat/client-tools.ts`. Tool protocol constants (`TOOL_RESULT`, `TOOL_APPROVAL`, `MESSAGE_UPDATED`) added. Think implements client-side tools with debounce-based auto-continuation.
- Think now has: MCP `waitForMcpConnections`, message push on connect, feature parity with AIChatAgent's core chat experience.
- Durable chat-recovery orchestration unified in `agents/chat/recovery-engine.ts` (`ChatRecoveryEngine` over a `ChatRecoveryAdapter` + per-wake `ChatFiberWakeHooks`); `AIChatAgent`, `Think`, and the `experimental/pi-recovery` fixture all drive it. The orphan-persist path was factored into named seams — (a) shared `StreamAccumulator` reconstruction, (b) host `resolveOrphanTargetId`, (c) shared `reconcileOrphanPartial`, (d) `SessionProvider`-subset upsert. See [rfc-chat-recovery-foundation.md](./rfc-chat-recovery-foundation.md).
- Auto-continuation barrier (#1649 / #1650) extracted to `agents/chat/auto-continuation-controller.ts` (`AutoContinuationController`). The controller owns the coalesce timer, the `_barrierActive` double-fire guard, and the schedule/coalesce/fire lifecycle (`schedule` / `rearmForBatch` / `armTimer` / `fireWhenStable` / `activateDeferredAndReschedule` / `reset`), parameterized by an `AutoContinuationHost` (stream-active signal, pending-interaction signal, incomplete-batch test, apply-drain, `keepAliveWhile`, and the host `fire()` turn pipeline). Both hosts retain thin delegating wrappers over their original method names; `COALESCE_MS` (50ms) is now single-sourced on the controller. Exported `@internal` for sibling packages. Fast-follow: Think's `waitUntilStable()` now consults `controller.isArmed()` to wait out an armed continuation, converging its idle definition with ai-chat's `waitForIdle()`. See [rfc-chat-recovery-foundation.md](./rfc-chat-recovery-foundation.md).
- Adapter-spine helpers de-duplicated (Tier A + B, pure leaf lifts, no behavior change). Three byte-identical fragments shared by both hosts now live once in `agents/chat`: (1) `async-helpers.ts` — the `TIMED_OUT` sentinel, `awaitWithDeadline` (deadline-bounded race), and `drainInteractionApplies` (the substrate-free interaction-apply completeness drain, parameterized by `hasPending` / `getTail`); (2) `classifyAgentToolChildRecovery(storage)` in `recovery-incident.ts` — the parent's agent-tool reattach incident scan (in-progress > failed > none precedence); (3) `interceptAgentToolBroadcast(msg, hooks)` in `agent-tools.ts` — the #1575 outgoing-frame snoop that tails an agent-tool child's progress, parameterized by an `AgentToolBroadcastHooks` substrate (forwarders / liveSequences / lastErrors maps, the host response-type constant, and the host run-lookup). Both hosts delegate through their existing private method names and `broadcast()` overrides (which keep a cheap size-guard so the common no-child path stays allocation-free, then call `super.broadcast`), so all call sites are untouched. The recovery-engine adapter seam (`_chatRecoveryEngine` / `_runChatRecoveryFiber`) and the genuinely product-substrate `dispatch`/`classify`/`terminalize` methods were deliberately left in the hosts (RFC bucket 3) for the Turns effort. See [rfc-chat-recovery-foundation.md](./rfc-chat-recovery-foundation.md).
Loading
Loading