Skip to content

feat(agents): move state into an opt-in Lifecycle capability (agents/state) - #2179

Open
AntoniTok wants to merge 3 commits into
cloudflare:mainfrom
AntoniTok:feat/state-capability
Open

feat(agents): move state into an opt-in Lifecycle capability (agents/state)#2179
AntoniTok wants to merge 3 commits into
cloudflare:mainfrom
AntoniTok:feat/state-capability

Conversation

@AntoniTok

Copy link
Copy Markdown
Contributor

State was one method (_setStateInternal) doing four jobs inside the Agent god-class — validate, persist, broadcast, notify — with the state row, in-memory cache, and schema all threaded through the class. This moves it wholesale into a StateManager capability that owns storage and change ordering, so any Lifecycle host gets durable, validated state without inheriting Agent. Same pattern as the WebSockets (#2169) and MCP client (#1895) capabilities.

Agent's public API and wire protocol are unchanged.

Architecture: before then after

Before, one method, four responsibilities, all in the god-class:

+--------------------------------------+
| Agent (god-class)                    |
|                                      |
|  get state -> SQL load/cache/seed    |
|  _setStateInternal():                |
|    1. validate                       |
|    2. persist ----------+            |
|    3. broadcast --------> clients    |
|    4. notify hook       |            |
|  _state (cache)         v            |
|         cf_agents_state (SQLite)     |
+--------------------------------------+

After, capability stores and announces; Agent reacts to the announcement:

+--------------------------------------+
| Agent (god-class)                    |
|  get state -> #state.get()           |
|  setState  -> #state.set()           |
|  _handleStateChanged() (subscriber): |
|    3. broadcast --------> clients    |
|    4. notify hook                    |
|         ^                            |
|         | onStateChanged (Emitter)   |
+---------|----------------------------+
          |
+---------+----------------------------+
| StateManager (capability)            |
|  onStart: schema + own version key   |
|  get(): load / cache / seed          |
|  set(): 1.validate 2.persist         |
|         -> fire onStateChanged       |
|  _state (cache)                      |
+---------+----------------------------+
          | this.lifecycle.storage
          v
   cf_agents_state (SQLite)

The capability never references connections, the Agent, env, or ctx. It fires a typed onStateChanged emitter (mirroring MCP's onServerStateChanged), and the Agent subscribes to do the WebSockets-specific work.

Data flows

Outbound, server sets state (source is "server", broadcast to all):

dev code -> setState(next)
  -> #state.set(next, "server")
       1. validate  (injected -> Agent.validateStateChange)
       2. persist   -> cf_agents_state
       3. fire onStateChanged({ state, sourceId: undefined })
  -> Agent._handleStateChanged
       3. _broadcastProtocol(...)  -> ALL protocol clients (exclude none)
       4. waitUntil -> onStateChanged / onStateUpdate dev hook

Inbound, client sends state over WS (source is the connection, echo excludes sender):

browser -> onMessage           (WebSockets concern, STAYS in Agent)
   parse + readonly check + CF_AGENT_STATE_ERROR responses
   -> #state.set(next, connection)
        1. validate  2. persist
        3. fire onStateChanged({ state, sourceId: connection.id })
  -> Agent._handleStateChanged
        3. _broadcastProtocol(..., exclude=[sourceId])  -> all EXCEPT sender
        4. dev hook

Both paths funnel through #state.set(); the only difference is sourceId, which drives broadcast exclusion and is forwarded to the notify hook.

What moved vs. what stays

Concern Owner
state row, load/cache/seed, validate + persist StateManager
own schema version key (cf_agents:state_schema_version) StateManager
broadcast to connections Agent (_handleStateChanged)
dev hooks (validateStateChange, onStateChanged/onStateUpdate) Agent (override surface)
onMessage parse / readonly / error responses Agent / WebSockets
cf_agents_state table creation both, idempotent

Host-owned behavior is injected, not moved: validateStateChange stays an overridable Agent method, and initialState is resolved lazily so a subclass field, initialized after the base constructor, is read at its final value.

The cf_agents_state table is shared: StateManager ensures it in onStart and owns the state row, while Agent keeps its global schema-version row in _ensureSchema and ensures the table there too. Each side is idempotent and tracks its own version, the same pattern as Scheduler's ensureScheduleTable.

Compatibility

state, setState(), onStateChanged, and the CF_AGENT_STATE frames behave identically. The full Agent state suite (22 cases) and the schema suite pass as-is; the only test change replaces a fixture reach-in to the removed _state cache with a proper reset method.

Tests

New capability suites install StateManager on a bare Durable Object through withCapabilityHarness and cover: persist/read, initial-state seeding, falsy-value row existence, rehydration across a simulated eviction, injected-validation rejection, and the onStateChanged source-exclusion payload on both server and client origins.

@changeset-bot

changeset-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 65cf89e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
agents Minor
@cloudflare/agent-think Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Aug 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

agents

npm i https://pkg.pr.new/agents@2179

@cloudflare/ai-chat

npm i https://pkg.pr.new/@cloudflare/ai-chat@2179

@cloudflare/codemode

npm i https://pkg.pr.new/@cloudflare/codemode@2179

hono-agents

npm i https://pkg.pr.new/hono-agents@2179

@cloudflare/shell

npm i https://pkg.pr.new/@cloudflare/shell@2179

@cloudflare/think

npm i https://pkg.pr.new/@cloudflare/think@2179

@cloudflare/voice

npm i https://pkg.pr.new/@cloudflare/voice@2179

@cloudflare/worker-bundler

npm i https://pkg.pr.new/@cloudflare/worker-bundler@2179

commit: 65cf89e

@AntoniTok
AntoniTok force-pushed the feat/state-capability branch from f5a1e1f to 4ed2efc Compare September 1, 2026 09:48
@AntoniTok
AntoniTok marked this pull request as ready for review September 1, 2026 10:00
devin-ai-integration[bot]

This comment was marked as resolved.

Comment thread packages/agents/src/index.ts Outdated
* absent. Mirrors a hibernation wake-up; for host-internal use and tests
* that exercise the lazy-load path in a single live instance.
*/
__DO_NOT_USE__resetStateCacheForTesting(): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

no testing code in the main class

Comment thread packages/agents/src/index.ts Outdated
this.lifecycle
.use(this.scheduler)
.use(this.mcp)
.use(this.#state)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this._state.

Comment thread packages/agents/src/index.ts Outdated
* existed; the typed `State` boundary is re-established at the delegating
* call sites below.
*/
readonly #state: StateManager<unknown> = new StateManager<State>({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

call this State.

let me pass a hook to do stuff on changed. and pass the initial state. dont need a setter I think it can be static.

Comment thread packages/agents/src/index.ts Outdated
type: MessageType.CF_AGENT_STATE
}),
source !== "server" ? [source.id] : []
sourceId !== undefined ? [sourceId] : []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

huh?

Comment thread packages/agents/src/index.ts Outdated
// Notification hook (non-gating). Run after broadcast and do not block.
// Use waitUntil for reliability after the handler returns.
const { connection, request, email } = agentContext.getStore() || {};
const source: Connection | "server" =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🪦

@AntoniTok
AntoniTok force-pushed the feat/state-capability branch from 4ed2efc to 9d170ac Compare September 2, 2026 09:42
devin-ai-integration[bot]

This comment was marked as resolved.

@AntoniTok
AntoniTok force-pushed the feat/state-capability branch from 9d170ac to e1a785a Compare September 2, 2026 10:12
…state)

State was one method doing four jobs inside the Agent god-class —
validate, persist, broadcast, notify — with the state row, cache, and
schema all threaded through the class. It moves wholesale into a
StateManager capability that owns storage and change ordering, so any
Lifecycle host gets durable, validated state without inheriting Agent:

  new StateManager({
    resolveInitialState: () => ({ value: this.initialState }),
    validateStateChange: (next, source) => this.validateStateChange(next, source)
  })

The capability owns the cf_agents_state state row, lazy load with an
in-memory cache, initial-state seeding, and validated persistence. It
runs only the onStart hook (versioned schema init under its own
cf_agents:state_schema_version key) and reaches Lifecycle only for
storage — no alarm, no request path. It never touches connections:
after validate + persist it fires a typed onStateChanged emitter,
mirroring the MCP client's onServerStateChanged seam. The getter,
write path, and corrupt-row fallback move verbatim; the only changes
are ctx->lifecycle storage and the broadcast becoming the emitter.

Host-owned behavior is injected, not moved: validateStateChange stays
an overridable Agent method, and initialState is resolved lazily so a
subclass field — initialized after the base constructor — is read at
its final value.

Broadcast and the notification hook stay on the Agent as an
onStateChanged subscriber (_handleStateChanged): it broadcasts
CF_AGENT_STATE to protocol-enabled connections excluding the source
id, then runs onStateChanged/onStateUpdate off the invocation tail.
The onMessage state branch stays too — parse, readonly check, and
CF_AGENT_STATE_ERROR responses are WebSockets concerns; only its inner
write becomes #state.set(state, connection). Agent installs the
capability in the .use() chain and delegates state/setState to it.

The cf_agents_state table is shared: StateManager ensures it in
onStart and owns the state row, while Agent keeps its global
schema-version row in _ensureSchema and ensures the table there too —
each side idempotent, each tracking its own version, the same pattern
as Scheduler's ensureScheduleTable.

Agent's public API and wire protocol are unchanged — state,
setState(), onStateChanged, and the CF_AGENT_STATE frames behave
identically, and the full Agent state suite (22 cases) plus the schema
suite pass as-is. New capability suites install StateManager on a bare
Durable Object through withCapabilityHarness and cover persist/read,
initial-state seeding, falsy-value row existence, rehydration across a
simulated eviction, injected-validation rejection, and the
onStateChanged source-exclusion payload on both server and client
origins.
@AntoniTok
AntoniTok force-pushed the feat/state-capability branch from e1a785a to 1e8f967 Compare September 2, 2026 10:29

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

Comment on lines +173 to +176
try {
this._options.onChanged?.(nextState, source);
} catch (error) {
console.error("StateManager onChanged hook failed:", error);

@devin-ai-integration devin-ai-integration Bot Sep 2, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Async state callbacks can be abandoned

When a standalone capability's onChanged awaits work after its handler returns, the unregistered promise can be canceled. Its side effects then disappear despite persisted state.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@agent-think

agent-think Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🟡 agents import sizes

Measured 286 runtime imports as minified bundles. The primary size is gzip; raw minified size is included for diagnosis. An existing import growing by more than 10% fails CI. New exports do not affect the gate.

Red Yellow Green Unchanged New Removed
0 53 0 232 1 0

Compared 99e5e2ec with 65cf89e5. Open workflow run.

Changed imports (54)
Status Import Base gzip Head gzip Delta
🟡 agents/lifecycle#Lifecycle 7.9 KiB 7.9 KiB +10 B (+0.12%)
🟡 agents#isPlatformTransientError 257.6 KiB 257.9 KiB +324 B (+0.12%)
🟡 agents#getCurrentAgent 257.6 KiB 257.9 KiB +323 B (+0.12%)
🟡 agents#routeAgentRequest 258.2 KiB 258.5 KiB +320 B (+0.12%)
🟡 agents#normalizeServerId 257.6 KiB 257.9 KiB +315 B (+0.12%)
🟡 agents#getAgentByName 257.6 KiB 257.9 KiB +306 B (+0.12%)
🟡 agents#parseSubAgentPath 257.6 KiB 257.9 KiB +306 B (+0.12%)
🟡 agents#isDurableObjectCodeUpdateReset 257.6 KiB 257.9 KiB +305 B (+0.12%)
🟡 agents#SqlError 257.6 KiB 257.9 KiB +305 B (+0.12%)
🟡 agents#isDurableObjectMemoryLimitReset 257.6 KiB 257.9 KiB +304 B (+0.12%)
🟡 agents#DurableObjectOAuthClientProvider 257.6 KiB 257.9 KiB +303 B (+0.11%)
🟡 agents#__DO_NOT_USE_WILL_BREAK__agentContext 257.6 KiB 257.9 KiB +303 B (+0.11%)
🟡 agents#DEFAULT_AGENT_STATIC_OPTIONS 257.6 KiB 257.9 KiB +302 B (+0.11%)
🟡 agents#AGENT_TOOL_MILESTONE_PART 257.6 KiB 257.9 KiB +300 B (+0.11%)
🟡 agents#camelCaseToKebabCase 257.6 KiB 257.9 KiB +297 B (+0.11%)
🟡 agents#__DO_NOT_USE_WILL_BREAK__withInvocationScope 257.6 KiB 257.9 KiB +294 B (+0.11%)
🟡 agents/workflows#WorkflowRejectedError 257.6 KiB 257.9 KiB +293 B (+0.11%)
🟡 agents#getSubAgentByName 257.9 KiB 258.2 KiB +288 B (+0.11%)
🟡 agents#callable 257.6 KiB 257.9 KiB +287 B (+0.11%)
🟡 agents/chat-sdk#defaultKeyShard 257.7 KiB 258.0 KiB +286 B (+0.11%)
🟡 agents/chat-sdk#defaultThreadShard 257.6 KiB 257.9 KiB +282 B (+0.11%)
🟡 agents#StreamingResponse 257.6 KiB 257.9 KiB +281 B (+0.11%)
🟡 agents#unstable_callable 257.7 KiB 258.0 KiB +281 B (+0.11%)
🟡 agents/chat-sdk#ChatSdkStateAgent 259.3 KiB 259.6 KiB +282 B (+0.11%)
🟡 agents#Agent 257.6 KiB 257.9 KiB +280 B (+0.11%)
🟡 agents/workflows#AgentWorkflow 258.9 KiB 259.2 KiB +280 B (+0.11%)
🟡 agents#MCP_SERVER_ID_MAX_LENGTH 257.6 KiB 257.9 KiB +271 B (+0.1%)
🟡 agents#AGENT_TOOL_PROGRESS_PART 257.6 KiB 257.9 KiB +270 B (+0.1%)
🟡 agents#MessageType 257.7 KiB 258.0 KiB +270 B (+0.1%)
🟡 agents#SUB_PREFIX 257.6 KiB 257.9 KiB +269 B (+0.1%)
🟡 agents#routeSubAgentRequest 257.8 KiB 258.1 KiB +268 B (+0.1%)
🟡 agents#createHeaderBasedEmailResolver 257.8 KiB 258.0 KiB +266 B (+0.1%)
🟡 agents#routeAgentEmail 257.9 KiB 258.1 KiB +265 B (+0.1%)
🟡 agents#buildAgentPath 258.1 KiB 258.4 KiB +264 B (+0.1%)
🟡 agents/chat-sdk#ChatSdkStateAdapter 260.0 KiB 260.2 KiB +264 B (+0.1%)
🟡 agents/chat-sdk#createChatSdkState 260.0 KiB 260.3 KiB +263 B (+0.1%)
🟡 agents#isDurableObjectStorageReset 257.6 KiB 257.9 KiB +256 B (+0.1%)
🟡 agents#buildAgentUrl 258.3 KiB 258.5 KiB +256 B (+0.1%)
🟡 agents/mcp#createMcpHandler 387.1 KiB 387.4 KiB +327 B (+0.08%)
🟡 agents/mcp#experimental_createMcpHandler 375.1 KiB 375.4 KiB +313 B (+0.08%)
🟡 agents/mcp#ElicitRequestSchema 341.5 KiB 341.8 KiB +276 B (+0.08%)
🟡 agents/mcp#createLegacyMcpHandler 375.0 KiB 375.3 KiB +289 B (+0.08%)
🟡 agents/mcp#normalizeServerId 341.5 KiB 341.8 KiB +263 B (+0.08%)
🟡 agents/mcp#RPCServerTransport 341.5 KiB 341.8 KiB +263 B (+0.08%)
🟡 agents/mcp#RPCClientTransport 341.5 KiB 341.8 KiB +263 B (+0.08%)
🟡 agents/mcp#RPC_DO_PREFIX 341.5 KiB 341.7 KiB +242 B (+0.07%)
🟡 agents/mcp#SSEEdgeClientTransport 341.6 KiB 341.8 KiB +241 B (+0.07%)
🟡 agents/mcp#DurableObjectEventStore 341.5 KiB 341.7 KiB +240 B (+0.07%)
🟡 agents/mcp#getMcpAuthContext 341.5 KiB 341.7 KiB +240 B (+0.07%)
🟡 agents/mcp#WorkerTransport 344.9 KiB 345.1 KiB +240 B (+0.07%)
🟡 agents/mcp#StreamableHTTPEdgeClientTransport 341.6 KiB 341.8 KiB +232 B (+0.07%)
🟡 agents/mcp#McpAgent 341.5 KiB 341.7 KiB +231 B (+0.07%)
🟡 agents/mcp#MCP_SERVER_ID_MAX_LENGTH 341.5 KiB 341.7 KiB +231 B (+0.07%)
agents/state#StateManager 844 B
All 286 current runtime imports
Status Import Gzip Raw minified
🟡 agents#__DO_NOT_USE_WILL_BREAK__agentContext 257.9 KiB 1126.5 KiB
🟡 agents#__DO_NOT_USE_WILL_BREAK__withInvocationScope 257.9 KiB 1126.5 KiB
🟡 agents#Agent 257.9 KiB 1126.5 KiB
🟡 agents#AGENT_TOOL_MILESTONE_PART 257.9 KiB 1126.5 KiB
🟡 agents#AGENT_TOOL_PROGRESS_PART 257.9 KiB 1126.5 KiB
🟡 agents#buildAgentPath 258.4 KiB 1128.8 KiB
🟡 agents#buildAgentUrl 258.5 KiB 1129.2 KiB
🟡 agents#callable 257.9 KiB 1126.5 KiB
🟡 agents#camelCaseToKebabCase 257.9 KiB 1126.5 KiB
🟡 agents#createHeaderBasedEmailResolver 258.0 KiB 1126.9 KiB
🟡 agents#DEFAULT_AGENT_STATIC_OPTIONS 257.9 KiB 1126.5 KiB
🟡 agents#DurableObjectOAuthClientProvider 257.9 KiB 1126.5 KiB
🟡 agents#getAgentByName 257.9 KiB 1126.5 KiB
🟡 agents#getCurrentAgent 257.9 KiB 1126.5 KiB
🟡 agents#getSubAgentByName 258.2 KiB 1127.1 KiB
🟡 agents#isDurableObjectCodeUpdateReset 257.9 KiB 1126.5 KiB
🟡 agents#isDurableObjectMemoryLimitReset 257.9 KiB 1126.5 KiB
🟡 agents#isDurableObjectStorageReset 257.9 KiB 1126.5 KiB
🟡 agents#isPlatformTransientError 257.9 KiB 1126.5 KiB
🟡 agents#MCP_SERVER_ID_MAX_LENGTH 257.9 KiB 1126.5 KiB
🟡 agents#MessageType 258.0 KiB 1126.8 KiB
🟡 agents#normalizeServerId 257.9 KiB 1126.5 KiB
🟡 agents#parseSubAgentPath 257.9 KiB 1126.5 KiB
🟡 agents#routeAgentEmail 258.1 KiB 1127.2 KiB
🟡 agents#routeAgentRequest 258.5 KiB 1128.4 KiB
🟡 agents#routeSubAgentRequest 258.1 KiB 1127.0 KiB
🟡 agents#SqlError 257.9 KiB 1126.5 KiB
🟡 agents#StreamingResponse 257.9 KiB 1126.5 KiB
🟡 agents#SUB_PREFIX 257.9 KiB 1126.5 KiB
🟡 agents#unstable_callable 258.0 KiB 1126.7 KiB
agents/agent-tools#agentTool 112.5 KiB 538.2 KiB
agents/browser#BrowserConnector 50.5 KiB 176.6 KiB
agents/browser#browserContent 36.3 KiB 127.4 KiB
agents/browser#browserExtract 36.3 KiB 127.4 KiB
agents/browser#browserLinks 36.3 KiB 127.4 KiB
agents/browser#browserMarkdown 36.3 KiB 127.4 KiB
agents/browser#browserPdf 36.3 KiB 127.3 KiB
agents/browser#BrowserRenderingError 36.0 KiB 126.7 KiB
agents/browser#browserScrape 36.3 KiB 127.4 KiB
agents/browser#browserScreenshot 36.3 KiB 127.3 KiB
agents/browser#browserSnapshot 36.3 KiB 127.4 KiB
agents/browser#CdpSession 37.2 KiB 129.8 KiB
agents/browser#CodemodeRuntime 39.6 KiB 139.0 KiB
agents/browser#connectBrowser 37.8 KiB 131.4 KiB
agents/browser#connectBrowserSession 37.5 KiB 130.4 KiB
agents/browser#connectUrl 37.6 KiB 130.5 KiB
agents/browser#createBrowserSession 36.3 KiB 127.5 KiB
agents/browser#DEFAULT_EXEC_SWEEP_IDLE_MS 36.0 KiB 126.6 KiB
agents/browser#DEFAULT_SWEEP_IDLE_MS 36.0 KiB 126.6 KiB
agents/browser#deleteBrowserSession 36.1 KiB 126.9 KiB
agents/browser#DurableBrowserSessionStore 36.4 KiB 127.6 KiB
agents/browser#getBrowserRecording 36.2 KiB 127.1 KiB
agents/browser#listBrowserTargets 36.1 KiB 126.9 KiB
agents/browser#loadCdpSpec 36.6 KiB 128.3 KiB
agents/browser#runQuickAction 36.0 KiB 126.6 KiB
agents/browser/ai#createBrowserRuntime 146.0 KiB 630.3 KiB
agents/browser/ai#createBrowserTools 146.0 KiB 630.3 KiB
agents/browser/ai#createQuickActionTools 122.5 KiB 554.3 KiB
agents/browser/tanstack-ai#createBrowserTools 161.7 KiB 699.2 KiB
agents/chat#AbortRegistry 2.5 KiB 8.9 KiB
agents/chat#AGENT_TOOL_STREAM_PROGRESS_BUMP_THROTTLE_MS 2.3 KiB 8.2 KiB
agents/chat#AgentToolProgressEmitter 2.6 KiB 9.5 KiB
agents/chat#AgentToolStreamProgressThrottle 2.3 KiB 8.3 KiB
agents/chat#aiSdkRecoveryCodec 2.3 KiB 8.2 KiB
agents/chat#applyAgentToolEvent 3.2 KiB 10.9 KiB
agents/chat#applyChunkToParts 2.3 KiB 8.2 KiB
agents/chat#applyToolUpdate 2.4 KiB 8.4 KiB
agents/chat#AutoContinuationController 2.3 KiB 8.2 KiB
agents/chat#awaitWithDeadline 2.4 KiB 8.4 KiB
agents/chat#broadcastTransition 3.1 KiB 11.4 KiB
agents/chat#buildChatRecoveringFrame 2.4 KiB 8.3 KiB
agents/chat#buildInClauseStrings 2.4 KiB 8.4 KiB
agents/chat#bumpChatRecoveryProgress 2.3 KiB 8.3 KiB
agents/chat#byteLength 2.3 KiB 8.2 KiB
agents/chat#CHAT_LAST_TERMINAL_KEY 2.3 KiB 8.2 KiB
agents/chat#CHAT_MESSAGE_TYPES 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERING_FLAG_TTL_MS 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERING_KEY 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERY_ALARM_DEBOUNCE_MS 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERY_INCIDENT_KEY_PREFIX 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERY_INCIDENT_TTL_MS 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERY_PROGRESS_KEY 2.3 KiB 8.2 KiB
agents/chat#CHAT_RECOVERY_STABLE_RETRY_DELAY_SECONDS 2.3 KiB 8.2 KiB
agents/chat#CHAT_STREAM_PROGRESS_CREDIT_THROTTLE_MS 2.3 KiB 8.2 KiB
agents/chat#ChatRecoveryEngine 4.4 KiB 15.2 KiB
agents/chat#chatRecoveryRedeferPolicy 2.3 KiB 8.2 KiB
agents/chat#chatRecoverySchedulePolicy 2.3 KiB 8.2 KiB
agents/chat#ChatStreamStalledError 2.3 KiB 8.3 KiB
agents/chat#classifyAgentToolChildRecovery 2.4 KiB 8.5 KiB
agents/chat#cleanupStreamBuffers 2.3 KiB 8.2 KiB
agents/chat#clearChatTerminal 2.3 KiB 8.2 KiB
agents/chat#clientResolvableToolNames 2.3 KiB 8.3 KiB
agents/chat#ContinuationState 2.6 KiB 9.8 KiB
agents/chat#createAgentToolEventState 2.3 KiB 8.2 KiB
agents/chat#createChatFiberSnapshot 2.4 KiB 8.6 KiB
agents/chat#createChatStreams 5.5 KiB 19.3 KiB
agents/chat#createChatTurnTaskDefinition 2.6 KiB 8.9 KiB
agents/chat#createToolsFromClientSchemas 114.3 KiB 545.4 KiB
agents/chat#crossMessageToolResultUpdate 2.4 KiB 8.6 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_MAX_ATTEMPTS 2.3 KiB 8.2 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_MAX_OOM_RETRIES 2.3 KiB 8.2 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_MAX_WORK 2.3 KiB 8.2 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_NO_PROGRESS_TIMEOUT_MS 2.3 KiB 8.2 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_STABLE_TIMEOUT_MS 2.3 KiB 8.2 KiB
agents/chat#DEFAULT_CHAT_RECOVERY_TERMINAL_MESSAGE 2.3 KiB 8.3 KiB
agents/chat#drainInteractionApplies 2.3 KiB 8.3 KiB
agents/chat#enforceRowSizeLimit 3.4 KiB 11.0 KiB
agents/chat#hasIncompleteToolBatch 2.4 KiB 8.6 KiB
agents/chat#interceptAgentToolBroadcast 2.5 KiB 8.6 KiB
agents/chat#isPlatformFailure 2.6 KiB 8.9 KiB
agents/chat#isReplayChunk 2.4 KiB 8.6 KiB
agents/chat#iterateWithStallWatchdog 2.6 KiB 8.8 KiB
agents/chat#KV_DELETE_MAX_KEYS 2.3 KiB 8.2 KiB
agents/chat#listActiveChatRecoveryIncidents 2.4 KiB 8.4 KiB
agents/chat#MAX_BOUND_PARAMS 2.3 KiB 8.2 KiB
agents/chat#MessageType 2.4 KiB 9.0 KiB
agents/chat#normalizeToolInput 2.3 KiB 8.2 KiB
agents/chat#parseProtocolMessage 2.5 KiB 9.0 KiB
agents/chat#partAwaitsClientInteraction 2.4 KiB 8.5 KiB
agents/chat#pausedExecutionUpdate 2.4 KiB 8.4 KiB
agents/chat#pendingChatTerminal 2.3 KiB 8.3 KiB
agents/chat#persistReconstructedOrphan 3.0 KiB 11.0 KiB
agents/chat#PreStreamTurns 2.6 KiB 9.2 KiB
agents/chat#readChatRecoveryProgress 2.3 KiB 8.3 KiB
agents/chat#reconcileMessages 2.8 KiB 9.5 KiB
agents/chat#reconcileOrphanPartial 2.4 KiB 8.4 KiB
agents/chat#recordChatTerminal 2.3 KiB 8.3 KiB
agents/chat#repairInterruptedToolParts 2.6 KiB 9.1 KiB
agents/chat#resolveChatRecoveryConfig 2.5 KiB 8.9 KiB
agents/chat#resolveToolMergeId 2.4 KiB 8.5 KiB
agents/chat#ResumableStream 4.6 KiB 15.3 KiB
agents/chat#ResumeHandshake 2.9 KiB 10.4 KiB
agents/chat#ROW_MAX_BYTES 2.3 KiB 8.2 KiB
agents/chat#runChatRecoveryExhaustion 2.5 KiB 8.9 KiB
agents/chat#sanitizeMessage 2.5 KiB 9.0 KiB
agents/chat#sendIfOpen 2.4 KiB 8.3 KiB
agents/chat#setChatRecovering 2.4 KiB 8.5 KiB
agents/chat#shouldCreditStreamProgress 2.3 KiB 8.3 KiB
agents/chat#STREAM_CLEANUP_DELAY_SECONDS 2.3 KiB 8.2 KiB
agents/chat#STREAM_RESUME_NONE_REASONS 2.3 KiB 8.2 KiB
agents/chat#StreamAccumulator 2.9 KiB 10.7 KiB
agents/chat#StreamProgressCreditThrottle 2.3 KiB 8.3 KiB
agents/chat#SubmitConcurrencyController 2.9 KiB 10.2 KiB
agents/chat#sweepStaleChatRecoveryIncidents 2.4 KiB 8.4 KiB
agents/chat#TextSegmentJoiner 2.7 KiB 9.2 KiB
agents/chat#TIMED_OUT 2.3 KiB 8.2 KiB
agents/chat#toolApprovalUpdate 2.4 KiB 8.5 KiB
agents/chat#toolPartHasSettledResult 2.3 KiB 8.3 KiB
agents/chat#toolResultUpdate 2.4 KiB 8.4 KiB
agents/chat#TurnQueue 2.6 KiB 9.2 KiB
agents/chat#unwrapChatFiberSnapshot 2.4 KiB 8.5 KiB
agents/chat#wrapChatFiberSnapshot 2.3 KiB 8.2 KiB
🟡 agents/chat-sdk#ChatSdkStateAdapter 260.2 KiB 1138.0 KiB
🟡 agents/chat-sdk#ChatSdkStateAgent 259.6 KiB 1135.5 KiB
🟡 agents/chat-sdk#createChatSdkState 260.3 KiB 1138.1 KiB
🟡 agents/chat-sdk#defaultKeyShard 258.0 KiB 1126.7 KiB
🟡 agents/chat-sdk#defaultThreadShard 257.9 KiB 1126.5 KiB
agents/chat/react#detectToolsRequiringConfirmation 3.3 KiB 8.3 KiB
agents/chat/react#extractClientToolSchemas 3.2 KiB 8.3 KiB
agents/chat/react#getAgentMessages 3.4 KiB 8.6 KiB
agents/chat/react#getToolApproval 3.1 KiB 8.0 KiB
agents/chat/react#getToolCallId 3.1 KiB 8.0 KiB
agents/chat/react#getToolInput 3.1 KiB 8.0 KiB
agents/chat/react#getToolOutput 3.1 KiB 8.0 KiB
agents/chat/react#getToolPartState 3.2 KiB 8.2 KiB
agents/chat/react#useAgentChat 132.9 KiB 609.7 KiB
agents/chat/react#WebSocketChatTransport 5.7 KiB 17.1 KiB
agents/chat/transport#WebSocketChatTransport 2.8 KiB 9.2 KiB
agents/client#AgentClient 5.7 KiB 16.6 KiB
agents/client#AgentConnectionError 582 B 993 B
agents/client#agentFetch 4.2 KiB 12.3 KiB
agents/client#createStubProxy 638 B 1.0 KiB
agents/client#DEFAULT_CALL_TIMEOUT_MS 473 B 770 B
agents/client#isTerminalCloseEvent 509 B 822 B
agents/email#createAddressBasedEmailResolver 193 B 227 B
agents/email#createCatchAllEmailResolver 110 B 97 B
agents/email#createHeaderBasedEmailResolver 334 B 492 B
agents/email#createSecureReplyEmailResolver 718 B 1.3 KiB
agents/email#DEFAULT_MAX_AGE_SECONDS 56 B 39 B
agents/email#isAutoReplyEmail 201 B 249 B
agents/email#signAgentHeaders 424 B 812 B
agents/experimental/memory/session#AgentContextProvider 425 B 810 B
agents/experimental/memory/session#AgentSearchProvider 821 B 2.0 KiB
agents/experimental/memory/session#AgentSessionProvider 2.5 KiB 8.8 KiB
agents/experimental/memory/session#isSearchProvider 128 B 134 B
agents/experimental/memory/session#isSkillProvider 127 B 130 B
agents/experimental/memory/session#isWritableProvider 126 B 128 B
agents/experimental/memory/session#PostgresContextProvider 422 B 671 B
agents/experimental/memory/session#PostgresSearchProvider 630 B 1.1 KiB
agents/experimental/memory/session#PostgresSessionProvider 1.7 KiB 5.3 KiB
agents/experimental/memory/session#R2SkillProvider 436 B 791 B
agents/experimental/memory/session#Session 93.4 KiB 454.0 KiB
agents/experimental/memory/session#SessionManager 94.5 KiB 460.1 KiB
agents/experimental/memory/utils#alignBoundaryBackward 291 B 584 B
agents/experimental/memory/utils#alignBoundaryForward 275 B 539 B
agents/experimental/memory/utils#buildSummaryPrompt 867 B 2.0 KiB
agents/experimental/memory/utils#CHARS_PER_TOKEN 51 B 31 B
agents/experimental/memory/utils#COMPACTION_PREFIX 63 B 43 B
agents/experimental/memory/utils#computeSummaryBudget 363 B 634 B
agents/experimental/memory/utils#createCompactFunction 1.8 KiB 4.2 KiB
agents/experimental/memory/utils#estimateMessageTokens 336 B 567 B
agents/experimental/memory/utils#estimateStringTokens 142 B 145 B
agents/experimental/memory/utils#findTailCutByTokens 597 B 1.3 KiB
agents/experimental/memory/utils#isCompactionMessage 98 B 83 B
agents/experimental/memory/utils#sanitizeToolPairs 537 B 1.1 KiB
agents/experimental/memory/utils#TOKENS_PER_MESSAGE 51 B 31 B
agents/experimental/memory/utils#truncateOlderMessages 1022 B 2.2 KiB
agents/experimental/memory/utils#WORDS_TOKEN_MULTIPLIER 53 B 33 B
agents/experimental/webmcp#registerWebMcp 85.2 KiB 295.8 KiB
agents/lifecycle#getCurrentAgent 366 B 745 B
🟡 agents/lifecycle#Lifecycle 7.9 KiB 24.5 KiB
agents/lifecycle#LifecycleCapability 472 B 922 B
🟡 agents/mcp#createLegacyMcpHandler 375.3 KiB 1568.6 KiB
🟡 agents/mcp#createMcpHandler 387.4 KiB 1613.8 KiB
🟡 agents/mcp#DurableObjectEventStore 341.7 KiB 1427.1 KiB
🟡 agents/mcp#ElicitRequestSchema 341.8 KiB 1427.1 KiB
🟡 agents/mcp#experimental_createMcpHandler 375.4 KiB 1568.9 KiB
🟡 agents/mcp#getMcpAuthContext 341.7 KiB 1427.2 KiB
🟡 agents/mcp#MCP_SERVER_ID_MAX_LENGTH 341.7 KiB 1427.1 KiB
🟡 agents/mcp#McpAgent 341.7 KiB 1427.1 KiB
🟡 agents/mcp#normalizeServerId 341.8 KiB 1427.1 KiB
🟡 agents/mcp#RPC_DO_PREFIX 341.7 KiB 1427.1 KiB
🟡 agents/mcp#RPCClientTransport 341.8 KiB 1427.1 KiB
🟡 agents/mcp#RPCServerTransport 341.8 KiB 1427.1 KiB
🟡 agents/mcp#SSEEdgeClientTransport 341.8 KiB 1427.4 KiB
🟡 agents/mcp#StreamableHTTPEdgeClientTransport 341.8 KiB 1427.4 KiB
🟡 agents/mcp#WorkerTransport 345.1 KiB 1444.0 KiB
agents/mcp/client#getNamespacedData 62.9 KiB 240.0 KiB
agents/mcp/client#MCP_SERVER_ID_MAX_LENGTH 62.9 KiB 239.9 KiB
agents/mcp/client#MCPClientManager 158.6 KiB 702.6 KiB
agents/mcp/client#normalizeServerId 63.0 KiB 240.2 KiB
agents/mcp/do-oauth-client-provider#DurableObjectOAuthClientProvider 2.1 KiB 6.6 KiB
agents/mcp/server#createMcpHandler 80.5 KiB 307.2 KiB
agents/mcp/server#getMcpAuthContext 64.0 KiB 245.5 KiB
agents/observability#channels 259 B 549 B
agents/observability#genericObservability 470 B 1.2 KiB
agents/observability#subscribe 324 B 668 B
agents/observability/ai#wrapAISDK 8.8 KiB 30.5 KiB
agents/react#_testUtils 3.8 KiB 9.5 KiB
agents/react#useAgent 10.8 KiB 31.1 KiB
agents/react#useAgentToolEvents 5.6 KiB 16.8 KiB
agents/routing#getAgentByName 795 B 1.7 KiB
agents/routing#routeAgentRequest 1.6 KiB 3.6 KiB
agents/routing#RoutedAgents 2.4 KiB 6.2 KiB
agents/schedule#getSchedulePrompt 85.8 KiB 424.7 KiB
agents/schedule#scheduleSchema 85.3 KiB 423.6 KiB
agents/schedule#unstable_getSchedulePrompt 85.9 KiB 424.9 KiB
agents/schedule#unstable_scheduleSchema 85.3 KiB 423.6 KiB
agents/schedules#Scheduler 7.0 KiB 22.6 KiB
agents/schedules/parser#getSchedulePrompt 85.8 KiB 424.7 KiB
agents/schedules/parser#scheduleSchema 85.3 KiB 423.6 KiB
agents/skills#fromManifest 309.8 KiB 1084.0 KiB
agents/skills#parseSkillFrontmatter 328.4 KiB 1146.2 KiB
agents/skills#parseSkillMarkdown 328.6 KiB 1146.5 KiB
agents/skills#r2 330.2 KiB 1150.4 KiB
agents/skills#runner 369.0 KiB 1297.8 KiB
agents/skills#SkillRegistry 397.5 KiB 1513.3 KiB
agents/skills/compile#compileSkillScript 15.4 KiB 43.4 KiB
agents/skills/compile#isCompilableSkillScript 15.4 KiB 43.3 KiB
agents/state#StateManager 844 B 1.8 KiB
agents/streams#DEFAULT_MAX_CHUNK_BYTES 83 B 81 B
agents/streams#sseResponse 843 B 1.6 KiB
agents/streams#StreamClosedError 161 B 197 B
agents/streams#StreamNotFoundError 201 B 261 B
agents/streams#Streams 3.4 KiB 11.1 KiB
agents/streams#StreamSerializationError 158 B 186 B
agents/tasks#DuplicateTaskStepError 326 B 463 B
agents/tasks#MAX_SERIALIZED_BYTES 190 B 232 B
agents/tasks#MissingTaskDefinitionError 359 B 536 B
agents/tasks#NonRetryableError 237 B 308 B
agents/tasks#TaskReplayDivergedError 341 B 483 B
agents/tasks#Tasks 7.8 KiB 27.8 KiB
agents/tasks#TaskSerializationError 258 B 339 B
agents/types#MessageType 211 B 365 B
agents/vite#default 353.8 KiB 1356.1 KiB
agents/websockets#CALLABLES_RPC_QUERY 12.4 KiB 43.3 KiB
agents/websockets#CALLABLES_RPC_VALUE 12.4 KiB 43.3 KiB
agents/websockets#callablesFromDecorated 12.7 KiB 44.2 KiB
agents/websockets#callablesRpcUrl 12.4 KiB 43.4 KiB
agents/websockets#isCallablesRpcUpgrade 12.4 KiB 43.4 KiB
agents/websockets#WebSockets 17.7 KiB 62.2 KiB
🟡 agents/workflows#AgentWorkflow 259.2 KiB 1131.2 KiB
🟡 agents/workflows#WorkflowRejectedError 257.9 KiB 1126.7 KiB
agents/x402#normalizeNetwork 14.7 KiB 61.1 KiB
agents/x402#withX402 23.0 KiB 89.2 KiB
agents/x402#withX402Client 104.1 KiB 346.5 KiB

Reported by agent-think[bot].

@AntoniTok

Copy link
Copy Markdown
Contributor Author
  • Rebased onto current main; PR is mergeable.
  • Moved state storage, caching, validation, and notifications into StateManager.
  • _state is public.
  • Added direct initialState and onChanged options.
  • Preserved the real state-change source.
  • Removed _setStateInternal.
  • Removed all shipped test-only cache reset methods.
  • Exposed the Lifecycle SQL helper.
  • Added public agents/state export and type tests.
  • State table is ensured before Lifecycle startup.
  • onChanged supports sync and async hooks; errors are handled while setState() stays synchronous.
  • Tests now use real eviction or fresh instances.
  • Added regression tests; all 2,001 Workers tests pass locally.

@AntoniTok

Copy link
Copy Markdown
Contributor Author

I assume you didn’t want me to change StateManager to State because State already names the agent’s state type and would just be confusing. Anyway #state is now _state.

Anyways now initialState passes the starting value, and onChanged passes the hook that runs after state changes; no initial-state setter is needed.

The wierd stuff marked by your last 2 comments was from splitting the state-change logic into two functions connected by an event causing us to lose the original connection value, so the code had to fake it back. Fixed now.

Devin flagged that async onChanged promises were not handled. Agent state changes now use the existing ctx.waitUntil, while standalone StateManager instances catch rejected promises. No Lifecycle API or behavior changed.

@AntoniTok
AntoniTok requested a review from mattzcarey September 2, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants