Skip to content

.NET: Improve Todo multithreading and inject todos into message list#5655

Merged
westey-m merged 2 commits intomicrosoft:mainfrom
westey-m:harness-todo-multi-thread-support-and-inject-remaining
May 5, 2026
Merged

.NET: Improve Todo multithreading and inject todos into message list#5655
westey-m merged 2 commits intomicrosoft:mainfrom
westey-m:harness-todo-multi-thread-support-and-inject-remaining

Conversation

@westey-m
Copy link
Copy Markdown
Contributor

@westey-m westey-m commented May 5, 2026

Motivation and Context

Making sure that we have no issues when multiple Function Tools are called in parallel and ensuring that the LLM is kept up to date on any remaining todo items.

Description

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Copilot AI review requested due to automatic review settings May 5, 2026 13:50
@moonbox3 moonbox3 added the .NET label May 5, 2026
@github-actions github-actions Bot changed the title Improve Todo multithreading and inject todos into message list .NET: Improve Todo multithreading and inject todos into message list May 5, 2026
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Automated Code Review

Reviewers: 4 | Confidence: 87%

✓ Correctness

This PR converts TodoProvider to be thread-safe using per-session SemaphoreSlim locks, makes command handlers async, and adds todo list message injection into the AI context. The implementation is correct: snapshots are taken under the lock, user callbacks are invoked outside the lock, ConditionalWeakTable provides thread-safe lock-per-session management, and the async conversions (both sync ValueTask.FromResult in ModeCommandHandler and async/await in TodoCommandHandler) are appropriate. No correctness issues found.

✓ Security Reliability

The PR adds thread-safety via per-session SemaphoreSlim locks to TodoProvider, converting synchronous methods to async. The locking pattern is correct: all state reads/writes are performed under the lock, snapshots are returned via ToList(), and the ConditionalWeakTable ensures lock lifetime is tied to session lifetime. The IDisposable implementation only disposes _nullSessionLock (acceptable since ConditionalWeakTable entries are GC'd with their keys). One minor reliability concern: public async methods and tool lambdas don't accept CancellationToken for the semaphore wait, which could theoretically cause indefinite waits if a bug prevents lock release, though practically the operations under the lock are trivially fast in-memory operations making this low risk.

✓ Test Coverage

The PR adds solid test coverage for new features (message injection, suppression, custom builder, snapshot safety, and concurrency). However, there are a few gaps: the new .Trim() behavior on Title/Description during add operations has no test verifying whitespace is actually trimmed, the description-formatting branch in FormatTodoListMessage is never exercised, and the null-session lock path is untested.

✗ Design Approach

I found two design-level problems in the new todo-context approach. First, the provider now injects the full todo list through AIContext.Messages on every invocation, but that channel is part of the request/history pipeline, so this change solves state visibility by appending repeated snapshots into durable chat history rather than supplying transient context. Second, the new "snapshot" reads only clone the list container; they still expose live mutable TodoItem instances, so callers and custom message builders can mutate provider state from outside the lock.

Flagged Issues

  • The snapshot API (state.Items.ToList()) only copies the list container, not the TodoItem objects themselves. Since TodoItem exposes public seters for Title, Description, and IsComplete, calers and custom message builders can mutate internal provider state from outside the synchronization lock. Items should be cloned or exposed as immutable projections.

Automated review by westey-m's agents

Comment thread dotnet/src/Microsoft.Agents.AI/Harness/Todo/TodoProvider.cs
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the .NET todo harness so todo state is serialized per session during concurrent tool calls, and so the todo list can be injected into the agent’s request context before each invocation. It also updates the shared console sample to use async command handling for the new async todo APIs.

Changes:

  • Made TodoProvider operations async and added per-session locking around todo reads/writes.
  • Added configurable todo-list message injection via TodoProviderOptions.
  • Updated tests and console command handlers to use the new async APIs.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
dotnet/tests/Microsoft.Agents.AI.UnitTests/Harness/Todo/TodoProviderTests.cs Expands coverage for async helper methods, todo message injection, and concurrent tool execution.
dotnet/src/Microsoft.Agents.AI/Harness/Todo/TodoProviderOptions.cs Adds options for suppressing or customizing injected todo-list messages.
dotnet/src/Microsoft.Agents.AI/Harness/Todo/TodoProvider.cs Implements per-session locking, async todo accessors, and injected todo-list context messages.
dotnet/samples/02-agents/Harness/Harness_Shared_Console/HarnessConsole.cs Awaits async command handlers in the console loop.
dotnet/samples/02-agents/Harness/Harness_Shared_Console/Commands/TodoCommandHandler.cs Switches /todos handling to async and uses GetAllTodosAsync.
dotnet/samples/02-agents/Harness/Harness_Shared_Console/Commands/ModeCommandHandler.cs Adapts /mode handling to the async command-handler interface.
dotnet/samples/02-agents/Harness/Harness_Shared_Console/Commands/ICommandHandler.cs Changes the command-handler contract from synchronous to async.

Comment thread dotnet/src/Microsoft.Agents.AI/Harness/Todo/TodoProvider.cs
Comment thread dotnet/src/Microsoft.Agents.AI/Harness/Todo/TodoProvider.cs
Comment thread dotnet/src/Microsoft.Agents.AI/Harness/Todo/TodoProvider.cs
Comment thread dotnet/src/Microsoft.Agents.AI/Harness/Todo/TodoProvider.cs
@westey-m westey-m enabled auto-merge May 5, 2026 14:58
@westey-m westey-m added this pull request to the merge queue May 5, 2026
Merged via the queue into microsoft:main with commit e9a6d43 May 5, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants