Skip to content

Add per-turn conversation and user state - #729

Open
Mehak Bindra (MehakBindra) wants to merge 19 commits into
mainfrom
mehakbindra-add-per-turn-state
Open

Add per-turn conversation and user state#729
Mehak Bindra (MehakBindra) wants to merge 19 commits into
mainfrom
mehakbindra-add-per-turn-state

Conversation

@MehakBindra

@MehakBindra Mehak Bindra (MehakBindra) commented Aug 13, 2026

Copy link
Copy Markdown
Member

Summary

  • add dirty-tracked, sealable per-turn conversation and user state scopes
  • expose state through AppOptions.state and ctx.state, loading before dispatch and saving in finally
  • keep keyed reads clean and require set() for persisted mutations
  • persist each scope as a raw-dictionary JSON string through IStorage
  • keep IStorage.set as a minimal key/value contract; expiration and other policies remain owned by the configured provider
  • preserve state mutations on handler failures, isolate per-turn values, and seal state even when persistence fails
  • deprecate direct AppOptions.storage, app.storage, and ctx.storage access in favor of state.storage or application-owned persistence
  • deprecate, but retain, IListStorage and ListLocalStorage; Python still exposes list storage, while the .NET state/storage design does not include this generic abstraction
  • add a TypeScript state example modeled on the .NET state bot

Ports the combined state foundation from microsoft/teams.py#553 and microsoft/teams.py#554, aligned with microsoft/teams.net while keeping TypeScript keyed state explicit and JSON-native. Standardized TTL and other per-write options are intentionally deferred until the SDK has a concrete cross-provider requirement.

Validation

  • 17 focused state tests pass
  • changed-file lint passes
  • common and apps package builds pass

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Copilot AI lite review requested due to automatic review settings August 13, 2026 22:18

Copilot AI 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.

Pull request overview

Introduces a new per-turn state subsystem for the apps package, enabling conversation-scoped and user-scoped mutable state that is loaded before dispatch, persisted at end-of-turn, and sealed after processing to prevent post-turn access.

Changes:

  • Added TurnState / TurnStateContainer abstractions with dirty-tracking and sealing semantics.
  • Implemented TurnStateLoader + createStateLoader to load/save JSON state envelopes with configurable key prefix and TTL.
  • Wired state into the activity processing lifecycle (AppOptions.state, ctx.state) and added focused unit + dispatch lifecycle tests.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/apps/src/state/turn-state.ts New turn-scoped state object with dirty tracking, snapshot iterators, and sealing behavior.
packages/apps/src/state/container.ts New container for per-turn conversation + user scopes, with delete + seal helpers.
packages/apps/src/state/loader.ts New loader for persisted state (keying, TTL checks, load/save/delete) and state-loader factory.
packages/apps/src/state/options.ts New StateOptions type for storage, key prefix, and TTL configuration.
packages/apps/src/state/index.ts Barrel exports for the new state subsystem.
packages/apps/src/index.ts Exposes the state subsystem as part of the public apps package API.
packages/apps/src/contexts/activity.ts Adds optional state to activity context options and propagates it in toInterface().
packages/apps/src/app.ts Adds AppOptions.state and constructs a TurnStateLoader for the processor.
packages/apps/src/app.process.ts Loads state before dispatch (when enabled) and saves+seals it in finally.
packages/apps/src/state/state.spec.ts Unit tests covering TurnState, TurnStateLoader, and createStateLoader.
packages/apps/src/app.process.spec.ts End-to-end lifecycle tests for load/expose/persist/seal, including failure cases.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/apps/src/state/state.spec.ts
Comment thread packages/apps/src/state/loader.ts Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
@MehakBindra
Mehak Bindra (MehakBindra) marked this pull request as draft August 14, 2026 00:48
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
@MehakBindra
Mehak Bindra (MehakBindra) marked this pull request as ready for review August 14, 2026 20:10
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e719380b-bd6a-4ae8-9650-6ffcee53fb7d
Comment on lines +332 to +338
try {
await this.options.stateLoader.save(
context.state,
conversationId,
userId
);
} finally {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

catch?

* @param userId User ID.
*/
userKey(conversationId: string, userId: string): string {
return `${this.keyPrefix}:user:${conversationId}:${userId}`;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

These should be escaped

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.

3 participants