chat.local to create typed, run-scoped data that persists across turns and is accessible from anywhere — the run function, tools, nested helpers. Each run gets its own isolated copy, and locals are automatically cleared between runs.
Lifecycle hooks and run also receive ctx (TaskRunContext) — the same object as on a standard task() — for tags, metadata, and cleanup that needs the full run record.
When a subtask is invoked via ai.toolExecute() (or the deprecated ai.tool()), initialized locals are automatically serialized into the subtask’s metadata and hydrated on first access — no extra code needed. Subtask changes to hydrated locals are local to the subtask and don’t propagate back to the parent.
Declaring and initializing
Declare locals at module level with a uniqueid, then initialize them inside a lifecycle hook where you have context (chatId, clientData, etc.):
Accessing from tools
Locals are accessible from anywhere during task execution — including AI SDK tools:Accessing from subtasks
When you useai.toolExecute() inside AI SDK tool() to expose a subtask, chat locals are automatically available read-only:
Values must be JSON-serializable for subtask access. Non-serializable values (functions, class instances, etc.) will be lost during transfer.
Dirty tracking and persistence
ThehasChanged() method returns true if any property was set since the last check, then resets the flag. Use it in lifecycle hooks to only persist when data actually changed:
API
| Method | Description |
|---|---|
chat.local<T>({ id }) | Create a typed local with a unique id (declare at module level) |
local.init(value) | Initialize with a value (call in hooks or run) |
local.hasChanged() | Returns true if modified since last check, resets flag |
local.get() | Returns a plain object copy (for serialization) |
local.property | Direct property access (read/write via Proxy) |
Locals use shallow proxying. Nested object mutations like
local.prefs.theme = "dark" won’t trigger the dirty flag. Instead, replace the whole property: local.prefs = { ...local.prefs, theme: "dark" }.See also
- Lifecycle hooks —
onBootis the canonical init site forchat.local. - Database persistence pattern — full per-hook breakdown using
chat.localalongside DB rows. - Code execution sandbox pattern — example of using
chat.localto hold a sandbox handle across turns. - Database connections — why the database client and its connection pool belong at module scope, not in
chat.local.

