Share via


AgentStatePropertyAccessor class

Provides typed access to an Agent state property with automatic state loading and persistence management.

Example

Basic Usage

// Create a property accessor
const userProfile = userState.createProperty<UserProfile>("userProfile");

// Get with default value
const profile = await userProfile.get(context, {
  name: "",
  preferences: { theme: "light", language: "en" }
});

// Modify the profile
profile.preferences.theme = "dark";

// Save the changes
await userProfile.set(context, profile);
await userState.saveChanges(context); // Persist to storage

Example

Working with Primitive Types

const counterProperty = userState.createProperty<number>("counter");

// Increment counter
const currentCount = await counterProperty.get(context, 0);
await counterProperty.set(context, currentCount + 1);
await userState.saveChanges(context);

Example

Conditional Logic

const settingsProperty = userState.createProperty<Settings>("settings");

// Check if property exists
const settings = await settingsProperty.get(context);
if (settings === undefined) {
  // Property doesn't exist, initialize with defaults
  await settingsProperty.set(context, getDefaultSettings());
}

Example

Custom Storage Keys

// Store state with a custom key for multi-tenant scenarios
const customKey = { key: `tenant_${tenantId}` };
const tenantData = await dataProperty.get(context, defaultData, customKey);
await dataProperty.set(context, updatedData, customKey);

Important Notes

  • Thread Safety: This class is not thread-safe. Ensure proper synchronization in concurrent scenarios.
  • Memory Usage: State objects are kept in memory until the context is disposed.
  • Persistence: Always call state.saveChanges(context) to persist changes to storage.
  • Deep Cloning: Default values are deep cloned using JSON serialization, which may not work with complex objects containing functions or circular references.

See createProperty for creating property accessors See StatePropertyAccessor for the interface definition

Remarks

AgentStatePropertyAccessor simplifies working with persisted state by abstracting the complexity of loading state from storage and manipulating specific properties. It provides a type-safe interface for state management with automatic handling of:

  • Lazy Loading: State is loaded from storage only when first accessed
  • Type Safety: Full TypeScript support with generic type parameters
  • Default Values: Automatic deep cloning of default values to prevent reference issues
  • Memory Management: Efficient in-memory caching with explicit persistence control
  • Custom Keys: Support for custom storage keys for advanced scenarios

Key Features

Key features of AgentStatePropertyAccessor include:

Type Safety

The accessor provides compile-time type checking when using TypeScript:

interface UserProfile {
  name: string;
  preferences: { theme: string; language: string };
}
const userProfile = userState.createProperty<UserProfile>("userProfile");

Automatic Default Value Handling

When a property doesn't exist, default values are automatically cloned and stored:

// If userProfile doesn't exist, the default will be cloned and saved
const profile = await userProfile.get(context, {
  name: "Anonymous",
  preferences: { theme: "light", language: "en" }
});

Explicit Persistence Control

Changes are kept in memory until explicitly persisted:

// Modify the state
const counter = await counterProperty.get(context, 0);
await counterProperty.set(context, counter + 1);

// Changes are only in memory at this point
// Persist to storage
await userState.saveChanges(context);

Usage Examples

Constructors

AgentStatePropertyAccessor<T>(AgentState, string)

Creates a new instance of AgentStatePropertyAccessor.

Example

// Recommended way - use AgentState.createProperty
const userProfile = userState.createProperty<UserProfile>("userProfile");

// Direct construction (not recommended)
const accessor = new AgentStatePropertyAccessor<UserProfile>(userState, "userProfile");

Properties

name

Methods

delete(TurnContext, CustomKey)

Deletes the property from the state storage.

Example

Basic usage

const userSettings = userState.createProperty<UserSettings>("settings");

// Delete the user settings
await userSettings.delete(context);

// Persist the deletion to storage
await userState.saveChanges(context);

// Verify deletion
const settings = await userSettings.get(context); // Returns undefined

Example

Custom key usage

const tenantKey = { key: `tenant_${tenantId}` };
await userSettings.delete(context, tenantKey);
await userState.saveChanges(context);
get(TurnContext, T, CustomKey)

Retrieves the value of the property from state storage.

Example

Basic usage

const counterProperty = userState.createProperty<number>("counter");

// Get with default value
const count = await counterProperty.get(context, 0);
console.log(count); // 0 if property doesn't exist, otherwise the stored value

Example

Complex object with default

interface UserProfile {
  name: string;
  preferences: { theme: string; notifications: boolean };
}

const userProfile = userState.createProperty<UserProfile>("profile");
const profile = await userProfile.get(context, {
  name: "Anonymous",
  preferences: { theme: "light", notifications: true }
});

Example

Checking for existence

const profile = await userProfile.get(context);
if (profile === undefined) {
  console.log("Profile has not been set yet");
} else {
  console.log(`Welcome back, ${profile.name}!`);
}

Example

Custom key usage

const tenantKey = { key: `tenant_${tenantId}` };
const tenantData = await dataProperty.get(context, defaultData, tenantKey);
set(TurnContext, T, CustomKey)

Sets the value of the property in state storage.

Example

Basic usage

const counterProperty = userState.createProperty<number>("counter");

// Set a new value
await counterProperty.set(context, 42);

// Persist to storage
await userState.saveChanges(context);

Example

Complex object

const userProfile = userState.createProperty<UserProfile>("profile");

const newProfile: UserProfile = {
  name: "John Doe",
  preferences: { theme: "dark", notifications: false }
};

await userProfile.set(context, newProfile);
await userState.saveChanges(context);

Example

Incremental updates

// Get current value, modify, then set
const settings = await settingsProperty.get(context, getDefaultSettings());
settings.theme = "dark";
settings.lastUpdated = new Date();

await settingsProperty.set(context, settings);
await userState.saveChanges(context);

Example

Custom key usage

const tenantKey = { key: `tenant_${tenantId}` };
await dataProperty.set(context, updatedData, tenantKey);
await userState.saveChanges(context);

Constructor Details

AgentStatePropertyAccessor<T>(AgentState, string)

Creates a new instance of AgentStatePropertyAccessor.

Example

// Recommended way - use AgentState.createProperty
const userProfile = userState.createProperty<UserProfile>("userProfile");

// Direct construction (not recommended)
const accessor = new AgentStatePropertyAccessor<UserProfile>(userState, "userProfile");
new AgentStatePropertyAccessor(state: AgentState, name: string)

Parameters

state
AgentState

The agent state object that manages the backing storage for this property

name

string

The unique name of the property within the state object. This name is used as the key in the state storage.

Remarks

This constructor is typically not called directly. Instead, use createProperty to create property accessors, which ensures proper integration with the state management system.

Property Details

name

name: string

Property Value

string

Method Details

delete(TurnContext, CustomKey)

Deletes the property from the state storage.

Example

Basic usage

const userSettings = userState.createProperty<UserSettings>("settings");

// Delete the user settings
await userSettings.delete(context);

// Persist the deletion to storage
await userState.saveChanges(context);

// Verify deletion
const settings = await userSettings.get(context); // Returns undefined

Example

Custom key usage

const tenantKey = { key: `tenant_${tenantId}` };
await userSettings.delete(context, tenantKey);
await userState.saveChanges(context);
function delete(context: TurnContext, customKey?: CustomKey): Promise<void>

Parameters

context
TurnContext

The turn context for the current conversation turn

customKey
CustomKey

Optional custom key for accessing state in a specific storage location. Useful for multi-tenant scenarios or when state needs to be partitioned.

Returns

Promise<void>

A promise that resolves when the delete operation is complete

Remarks

This operation removes the property from the in-memory state object but does not automatically persist the change to the underlying storage. You must call state.saveChanges(context) afterwards to persist the deletion.

  • If the property doesn't exist, this operation is a no-op
  • The deletion only affects the in-memory state until saveChanges() is called
  • After deletion, subsequent get() calls will return undefined (or the default value if provided)

get(TurnContext, T, CustomKey)

Retrieves the value of the property from state storage.

Example

Basic usage

const counterProperty = userState.createProperty<number>("counter");

// Get with default value
const count = await counterProperty.get(context, 0);
console.log(count); // 0 if property doesn't exist, otherwise the stored value

Example

Complex object with default

interface UserProfile {
  name: string;
  preferences: { theme: string; notifications: boolean };
}

const userProfile = userState.createProperty<UserProfile>("profile");
const profile = await userProfile.get(context, {
  name: "Anonymous",
  preferences: { theme: "light", notifications: true }
});

Example

Checking for existence

const profile = await userProfile.get(context);
if (profile === undefined) {
  console.log("Profile has not been set yet");
} else {
  console.log(`Welcome back, ${profile.name}!`);
}

Example

Custom key usage

const tenantKey = { key: `tenant_${tenantId}` };
const tenantData = await dataProperty.get(context, defaultData, tenantKey);
function get(context: TurnContext, defaultValue?: T, customKey?: CustomKey): Promise<T>

Parameters

context
TurnContext

The turn context for the current conversation turn

defaultValue

T

Optional default value to use if the property doesn't exist. When provided, this value is deep cloned and stored in state.

customKey
CustomKey

Optional custom key for accessing state in a specific storage location. Useful for multi-tenant scenarios or when state needs to be partitioned.

Returns

Promise<T>

A promise that resolves to the property value, the cloned default value, or undefined

Remarks

This method provides intelligent default value handling:

  • If the property exists, its value is returned
  • If the property doesn't exist and a default value is provided, the default is deep cloned, stored in state, and returned
  • If the property doesn't exist and no default is provided, undefined is returned

Deep Cloning: Default values are deep cloned using JSON serialization to prevent reference sharing issues. This means:

  • Functions, symbols, and circular references will be lost
  • Dates become strings (use Date constructor to restore)
  • Complex objects with prototypes lose their prototype chain

Performance: The first access loads state from storage; subsequent accesses use the in-memory cached version until the context is disposed.

set(TurnContext, T, CustomKey)

Sets the value of the property in state storage.

Example

Basic usage

const counterProperty = userState.createProperty<number>("counter");

// Set a new value
await counterProperty.set(context, 42);

// Persist to storage
await userState.saveChanges(context);

Example

Complex object

const userProfile = userState.createProperty<UserProfile>("profile");

const newProfile: UserProfile = {
  name: "John Doe",
  preferences: { theme: "dark", notifications: false }
};

await userProfile.set(context, newProfile);
await userState.saveChanges(context);

Example

Incremental updates

// Get current value, modify, then set
const settings = await settingsProperty.get(context, getDefaultSettings());
settings.theme = "dark";
settings.lastUpdated = new Date();

await settingsProperty.set(context, settings);
await userState.saveChanges(context);

Example

Custom key usage

const tenantKey = { key: `tenant_${tenantId}` };
await dataProperty.set(context, updatedData, tenantKey);
await userState.saveChanges(context);
function set(context: TurnContext, value: T, customKey?: CustomKey): Promise<void>

Parameters

context
TurnContext

The turn context for the current conversation turn

value

T

The value to assign to the property. Can be any serializable value.

customKey
CustomKey

Optional custom key for accessing state in a specific storage location. Useful for multi-tenant scenarios or when state needs to be partitioned.

Returns

Promise<void>

A promise that resolves when the set operation is complete

Remarks

This operation updates the property in the in-memory state object but does not automatically persist the change to the underlying storage. You must call state.saveChanges(context) afterwards to persist the changes.

Memory vs Storage: Changes are immediately reflected in memory and will be available to subsequent get() calls within the same context, but are not persisted to storage until saveChanges() is called.

Value References: The exact value reference is stored (no cloning occurs). Ensure you don't modify objects after setting them unless you intend for those changes to be reflected in state.

Type Safety: When using TypeScript, the value must match the property's declared type parameter.