RateLimitRule interface

Configures one rate limit rule for incoming turns.

A rule first decides whether it applies to the turn, then derives a scope key, then increments the fixed-window counter for that scope key. Multiple rules can apply to the same turn; the first rule whose counter is at the limit throttles the turn and prevents the route handler from running.

The fixed window starts when the first matching activity for a scope key is accepted. It does not start when the limit is reached. For example, with limit: 3 and windowMs: 10_000:

  • at 0s, the first matching activity is accepted and starts a window ending at 10s
  • at 1s and 2s, the second and third matching activities are accepted
  • at 3s, the fourth matching activity is throttled with retryAfterMs near 7000
  • at 10s, the window has expired and the next accepted activity starts a new window

Example

Limit all activities per user:

{
  scope: (context) => context.activity.from?.id,
  limit: 10,
  windowMs: 60_000
}

Example

Limit only message activities per conversation:

{
  scope: (context) => context.activity.conversation?.id,
  activityTypes: [ActivityTypes.Message],
  limit: 20,
  windowMs: 60_000
}

Example

Limit only an expensive command per user:

{
  appliesTo: (context) => context.activity.text?.startsWith('/expensive') === true,
  scope: (context) => context.activity.from?.id,
  limit: 3,
  windowMs: 60_000
}

Properties

activityTypes

Activity types counted by this rule.

When provided, the rule only applies to activities whose type is in this array.

When omitted, the rule applies to all activity types, subject to appliesTo if that predicate is also configured.

Example

activityTypes: [ActivityTypes.Message]
appliesTo

Optional predicate that decides whether this rule participates in the turn.

When provided, it runs after the activityTypes check. If it returns true, the rule continues to scope and may increment a counter. If it returns false, the rule is skipped and no window state is read or written.

When omitted, every activity that passed activityTypes is considered to match this rule.

Use this for policy or feature filtering, such as limiting only a command, tenant, channel, or class of users.

Keep this separate from scope: appliesTo answers "does this rule apply?", while scope answers "which scope key should be counted?".

Example

appliesTo: (context) => context.activity.text?.startsWith('/expensive') === true
limit

Maximum number of activities allowed for each scope key over windowMs.

This property is required.

Each accepted turn that matches the rule increments the counter for its scope key.

maxStorageRetries

Number of times to retry storage operations. Defaults to 3.

When provided, failed window evaluations and writes are retried up to this many times before applying storageErrorBehavior.

When omitted, failed window evaluations and writes are retried up to 3 times. Retries help when shared storage reports transient read or write failures.

message

Message sent when this rule throttles a turn.

When provided, this message is used for throttled message turns. It can be a plain string, an Activity, or a factory. Use a factory when the response should include details such as retryAfterMs.

When omitted, the default message is sent: "Too many requests. Please try again later."

scope

Derives the scope key that will be counted when this rule applies.

This property is required.

This is for grouping, not filtering. Return the same key for activities that should share a quota, such as a user ID for per-user limits or a conversation ID for per-conversation limits.

If this rule applies and the returned key is missing or blank, the turn is throttled instead of being allowed through without a counter.

storage

Storage used for this rule's fixed-window state.

When provided, the rule reads and writes window state through this storage provider.

When omitted, the rule uses the AgentApplication storage. If the application does not have storage configured, the limiter falls back to app-local memory storage. Provide rule-specific durable or shared storage when this rule should use a different store than the application state, or when limits must survive restarts or apply across multiple app instances.

storageErrorBehavior

Behavior when rate limit storage fails. Defaults to throttle.

When provided, the selected behavior is used when storage cannot be read or written. throttle fails closed and rejects the turn, allow fails open and lets the turn continue, and throw surfaces the storage error.

When omitted, storage failures use throttle.

windowMs

Time window, in milliseconds, used for the rule's fixed-window counter.

This property is required.

For example, limit: 5 and windowMs: 60_000 allows up to five activities per minute for each scope key. The window starts when the first matching activity for the scope key is accepted, and resets after windowMs. If the counter reaches limit before the window resets, additional matching activities are throttled and retryAfterMs reports the remaining time in that same window.

Property Details

activityTypes

Activity types counted by this rule.

When provided, the rule only applies to activities whose type is in this array.

When omitted, the rule applies to all activity types, subject to appliesTo if that predicate is also configured.

Example

activityTypes: [ActivityTypes.Message]
activityTypes?: ActivityTypes[]

Property Value

appliesTo

Optional predicate that decides whether this rule participates in the turn.

When provided, it runs after the activityTypes check. If it returns true, the rule continues to scope and may increment a counter. If it returns false, the rule is skipped and no window state is read or written.

When omitted, every activity that passed activityTypes is considered to match this rule.

Use this for policy or feature filtering, such as limiting only a command, tenant, channel, or class of users.

Keep this separate from scope: appliesTo answers "does this rule apply?", while scope answers "which scope key should be counted?".

Example

appliesTo: (context) => context.activity.text?.startsWith('/expensive') === true
appliesTo?: (context: TurnContext) => boolean | Promise<boolean>

Property Value

(context: TurnContext) => boolean | Promise<boolean>

limit

Maximum number of activities allowed for each scope key over windowMs.

This property is required.

Each accepted turn that matches the rule increments the counter for its scope key.

limit: number

Property Value

number

maxStorageRetries

Number of times to retry storage operations. Defaults to 3.

When provided, failed window evaluations and writes are retried up to this many times before applying storageErrorBehavior.

When omitted, failed window evaluations and writes are retried up to 3 times. Retries help when shared storage reports transient read or write failures.

maxStorageRetries?: number

Property Value

number

message

Message sent when this rule throttles a turn.

When provided, this message is used for throttled message turns. It can be a plain string, an Activity, or a factory. Use a factory when the response should include details such as retryAfterMs.

When omitted, the default message is sent: "Too many requests. Please try again later."

message?: string | Activity | RateLimitMessageFactory

Property Value

scope

Derives the scope key that will be counted when this rule applies.

This property is required.

This is for grouping, not filtering. Return the same key for activities that should share a quota, such as a user ID for per-user limits or a conversation ID for per-conversation limits.

If this rule applies and the returned key is missing or blank, the turn is throttled instead of being allowed through without a counter.

scope: RateLimitScope

Property Value

storage

Storage used for this rule's fixed-window state.

When provided, the rule reads and writes window state through this storage provider.

When omitted, the rule uses the AgentApplication storage. If the application does not have storage configured, the limiter falls back to app-local memory storage. Provide rule-specific durable or shared storage when this rule should use a different store than the application state, or when limits must survive restarts or apply across multiple app instances.

storage?: StorageProvider

Property Value

storageErrorBehavior

Behavior when rate limit storage fails. Defaults to throttle.

When provided, the selected behavior is used when storage cannot be read or written. throttle fails closed and rejects the turn, allow fails open and lets the turn continue, and throw surfaces the storage error.

When omitted, storage failures use throttle.

storageErrorBehavior?: RateLimitStorageErrorBehavior

Property Value

windowMs

Time window, in milliseconds, used for the rule's fixed-window counter.

This property is required.

For example, limit: 5 and windowMs: 60_000 allows up to five activities per minute for each scope key. The window starts when the first matching activity for the scope key is accepted, and resets after windowMs. If the counter reaches limit before the window resets, additional matching activities are throttled and retryAfterMs reports the remaining time in that same window.

windowMs: number

Property Value

number