Skip to content

.NET: Persist hosted agent state in Foundry - #7649

Open
Roger Barreto (rogerbarreto) wants to merge 6 commits into
microsoft:mainfrom
rogerbarreto:hosted-agents-storage-api
Open

.NET: Persist hosted agent state in Foundry#7649
Roger Barreto (rogerbarreto) wants to merge 6 commits into
microsoft:mainfrom
rogerbarreto:hosted-agents-storage-api

Conversation

@rogerbarreto

Copy link
Copy Markdown
Member

Motivation & Context

Hosted agent sessions were stored on the container filesystem. That state could be lost when a container was replaced and could not be read by another instance.

Workflow checkpoints were serialized inside the agent session. Long-running workflows could therefore grow the session until it exceeded the platform item-size limit.

This change stores sessions and workflow checkpoints through the AgentServer FoundryStateStore, allowing conversations and workflows to resume across container replacements.

Description & Review Guide

  • What are the major changes?

    • Add FoundryAgentSessionStore for serialized agent sessions.
    • Add FoundryJsonCheckpointStore, with one item per checkpoint and an ordered per-session index.
    • Configure workflow agents with state-store checkpointing when the hosting layer resolves them.
    • Use Foundry Storage when hosted and the AgentServer file-backed state-store fallback locally.
    • Delete obsolete checkpoints when resuming from the latest checkpoint.
    • Report hosted workflow agents configured with their own checkpoint manager through a dedicated readiness check.
    • Expose WorkflowAgentMetadata through AIAgent.GetService for workflow detection through wrappers.
    • Give Hosted-Workflow-Simple stable inner-agent identities so its checkpoints remain compatible after container replacement.
    • Prevent readiness probes from running configured history or context providers.
    • Update the private-preview AgentServer packages required by the StateStore API.
  • What is the impact of these changes?

    • Hosted sessions and workflow state survive container replacement and can be read by another instance.
    • Workflow checkpoints no longer increase the serialized session size.
    • Local development exercises the same storage adapters without requiring Azure credentials.
    • Existing callers that provide an explicit AgentSessionStore remain unchanged.
    • Hosted workflow agents must leave checkpoint storage to the hosting layer.
  • What do you want reviewers to focus on?

    • Session partitioning by agent, user, and conversation.
    • Checkpoint layout, concurrency handling, retention, and recovery behavior.
    • Resolution-time workflow decoration and caching.
    • The readiness failure for workflows with caller-configured checkpoint storage.
    • The use of the AgentServer local fallback outside Foundry.

Related Issue

None. This draft tracks private-preview integration before the StateStore packages are published.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

The durable state-store API this branch is built on ships in Core
beta.28, which is not on nuget.org yet. The local feed is a stopgap for
developing against it and must be removed before this branch ships.
A hosted agent kept its sessions, and a hosted workflow its checkpoints,
in files under the container's own directory. That state is lost when
the container is replaced and cannot be read by another instance of the
same agent, so a conversation could not survive a restart or be served
by more than one instance.

Both now go to the Foundry durable state store when the process runs in
a Foundry container, and stay on disk everywhere else:

- FoundryAgentSessionStore holds the agent sessions, partitioned by
  agent, conversation and end user.
- FoundryJsonCheckpointStore holds the workflow checkpoints, one item
  per checkpoint plus a per-session index that keeps them in commit
  order. Retrieving a checkpoint deletes the rest of that session's
  checkpoints, which is the only point at which nothing can still reach
  them, and is what stops the index growing past the size the platform
  accepts for one item.

A workflow agent is redirected to that checkpoint store when it is
resolved for a request, so nothing changes in how a container registers
one. An agent built with a checkpoint manager of its own is left alone
and reported by the new foundry-workflow-checkpointing readiness check,
because its state would go somewhere hosting does not manage.

Workflow agents are recognised through a new WorkflowAgentMetadata
returned by GetService, which still finds them behind middleware.
The stored-output probe ran the registered agent with its chat client
replaced, which still set the agent's chat history provider and context
providers running. Those are the parts most likely to reach outside the
container and to write state, so every readiness probe could make
external calls and add its own empty turn to real conversations.

The probe now runs a stand-in built from the agent's own options with
both kinds of provider dropped. It keeps what decides the setting, the
chat options and the raw request factory, and cannot see a decorator
wrapped around the agent, which is accepted for a readiness check.
Core beta.29 adds the shared local state-store fallback used by hosted
sessions and workflow checkpoints. Align its Azure Core and System
package dependencies to avoid assembly and downgrade conflicts.
Use FoundryStateStore for sessions and workflow checkpoints in every
environment. Core beta.29 selects Foundry Storage when hosted and a
file-backed local store otherwise, so local runs exercise the production
storage shape without requiring Azure credentials.

Give the hosted workflow sample stable inner-agent identities so its
checkpoints remain compatible after container replacement.
Copilot AI balanced review requested due to automatic review settings August 13, 2026 15:03
@agent-framework-automation agent-framework-automation Bot added .NET Usage: [Issues, PRs], Target: .Net workflows Usage: [Issues, PRs], Target: Workflows labels Aug 13, 2026

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

Adds durable Foundry-backed persistence for hosted .NET agent sessions and workflow checkpoints.

Changes:

  • Adds Foundry session and checkpoint stores with local fallback.
  • Redirects hosted workflows to durable checkpointing and adds readiness validation.
  • Updates tests, packages, and the hosted workflow sample.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
dotnet/Directory.Packages.props Updates AgentServer dependencies.
dotnet/nuget.config Adds preview package source.
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/AgentFrameworkResponseHandler.cs Applies workflow checkpointing during resolution.
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/FoundryAgentSessionStore.cs Implements durable session persistence.
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/FoundryJsonCheckpointStore.cs Implements durable workflow checkpoints.
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/FoundryStateStoreBinding.cs Caches state-store binding.
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/HostedStoredOutputHealthCheck.cs Avoids invoking configured providers.
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/HostedWorkflowCheckpointingHealthCheck.cs Detects unsupported checkpoint configuration.
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/Microsoft.Agents.AI.Foundry.Hosting.csproj Adds storage dependencies.
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/ServiceCollectionExtensions.cs Registers stores, checks, and workflow decoration.
dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowAgentMetadata.cs Exposes workflow metadata.
dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowHostAgent.cs Supports checkpoint-manager substitution.
dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowHostingExtensions.cs Adds checkpointing extension.
dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/Program.cs Stabilizes workflow executor identities.
dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/FoundryAgentSessionStoreTests.cs Tests session persistence and partitioning.
dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/FoundryJsonCheckpointStoreTests.cs Tests checkpoint behavior and concurrency.
dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/FoundryStateStoreLocalFallbackTests.cs Tests local fallback storage.
dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/HostedOutboundUserAgentTests.cs Retains in-memory test isolation.
dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/HostedStoredOutputHealthCheckTests.cs Tests provider-free readiness probes.
dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/HostedWorkflowCheckpointingHealthCheckTests.cs Tests workflow readiness validation.
dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/ServiceCollectionExtensionsTests.cs Tests default store registration.
dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/Microsoft.Agents.AI.Foundry.UnitTests.csproj Adds async-interface dependency.
dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowHostingExtensionsTests.cs Tests workflow metadata and redirection.

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

Comment on lines +74 to +76
return agent is WorkflowHostAgent workflowAgent
? workflowAgent.WithCheckpointing(checkpointManager)
: agent;
Comment on lines +300 to +322
List<IndexEntry> obsolete = [];
IndexEntry? resumed = null;
foreach (IndexEntry entry in ReadEntries(indexItem))
{
if (entry.CheckpointId == resumedCheckpointId)
{
resumed = entry;
}
else
{
obsolete.Add(entry);
}
}

if (resumed is null || obsolete.Count == 0)
{
return;
}

// The index is shortened first. A checkpoint item that is still listed but already gone
// would be read as a missing checkpoint, whereas one that is listed nowhere is simply
// never asked for.
await WriteEntriesAsync(store, sessionIndexKey, sessionId, [resumed], indexItem?.Etag).ConfigureAwait(false);
Comment on lines +49 to +60
catch (Exception ex) when (ex is not OperationCanceledException)
{
lock (this._gate)
{
if (ReferenceEquals(this._pending, binding))
{
this._pending = null;
}
}

throw;
}
Comment thread dotnet/nuget.config Outdated
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="agentserver-preview-local" value="C:\local_packages" />

/// <summary>
/// Covers <see cref="WorkflowHostingExtensions.WithCheckpointing"/>, which lets a host redirect
/// where a already-built workflow agent writes its checkpoints.
Comment on lines +202 to +205
builder.Append("u-").Append(userId).Append(':');
}

return builder.Append("c-").Append(conversationId).ToString();
Comment on lines +195 to +198
if (!string.IsNullOrEmpty(agent.Name))
{
builder.Append("a-").Append(agent.Name).Append(':');
}
Comment on lines +132 to +133
var probeAgent = new ChatClientAgent(probe, probeOptions);
await probeAgent.RunAsync([], cancellationToken: cancellationToken).ConfigureAwait(false);

@github-actions github-actions 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.

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (5 commit(s)): 13d59beaf64d, f5f4eba3cbaf, f73cc27d5ace, 0f0fa8f1143c, 9da7d9d1e7ee
Model: gpt-5.6-sol

Overview

The review found 2 verified inline finding(s).

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
2 verified findings remained after source verification (1 high, 1 medium) across 2 files. Details are attached to the affected lines below.

Affected areas: dotnet/nuget.config, dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/ServiceCollectionExtensions.cs

Comment thread dotnet/nuget.config Outdated
{
return s_workflowCheckpointingAgents.GetValue(
agent,
source => source.WithCheckpointing(GetFoundryWorkflowCheckpointManager(loggerFactory)));

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.

For a workflow behind DelegatingAIAgent/middleware, WithCheckpointing returns the wrapper unchanged, so its checkpoints remain inside the serialized session and are lost or hit the item-size limit after container replacement. GetService<WorkflowAgentMetadata>() still sees through that same wrapper, but readiness reports it healthy because UsesOwnCheckpointStorage is false. If the wrapper cannot be safely rebuilt around the redirected inner agent, please make readiness reject this configuration instead of serving it without durable checkpointing.

Use published AgentServer packages so CI no longer depends on a local package source.

@github-actions github-actions 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.

MAF Automated Review — Iteration 2

Result: Findings reported
Scope: 1 net-new commit(s): 08b57eb9a00e
Model: gpt-5.6-sol

Overview

The review found 3 verified inline finding(s).

Reviewed the supplied incremental change set across correctness, security/reliability, architecture, and failure behavior.
3 verified findings remained after source verification (3 medium) across 3 files. Details are attached to the affected lines below.

Affected areas: dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/AgentFrameworkResponseHandler.cs, dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/FoundryAgentSessionStore.cs, dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/FoundryJsonCheckpointStore.cs

return;
}

List<IndexEntry> obsolete = entries.GetRange(0, resumedIndex);

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.

At FoundryJsonCheckpointStore.cs:304, resuming a checkpoint deletes every earlier index entry regardless of recorded parent, so an earlier sibling checkpoint can be removed while another persisted session still references it, causing that session’s resume to fail; a safe fix must preserve ordered pruning for legacy or parentless checkpoint entries.

{
FoundryHostingExtensions.TryApplyUserAgent(agent);
return FoundryHostingExtensions.ApplyOpenTelemetry(agent);
storageIdentity = $"key:{agentName}";

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.

AddFoundryResponses(agent) registers the same named instance as both keyed and default, but this path stores it as key:<name> while an unnamed request stores it as name:<name>. Alternating between these supported request forms therefore loads a fresh session for the same agent, user, and conversation. Please assign one canonical storage identity to both aliases of the same registration.

return builder.ToString();
}

private static string ResolveAgentIdentity(AIAgent agent) =>

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.

For direct calls through the public AgentSessionStore overrides, an unnamed agent is partitioned by its generated instance ID. After a container replacement, an equivalent unnamed agent receives a new ID and cannot retrieve the durable session saved by the prior instance. Please require a stable explicit identity for unnamed agents, or reject this case instead of persisting state under an ephemeral key.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

.NET Usage: [Issues, PRs], Target: .Net workflows Usage: [Issues, PRs], Target: Workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants