fix(agent): apply config reload eagerly #100

Merged
jasoncouture merged 1 commit from fix/agent-config-reload-eager into main 2026-05-22 18:57:34 -04:00
jasoncouture commented 2026-05-22 18:57:32 -04:00 (Migrated from github.com)

Summary

When idle, the agent's iteration loop doesn't run — so the previous "apply pending config at top of iteration" mechanism left stale config in the data scope indefinitely. Heartbeat read the old HeartbeatPeriod, so changes to the agent JSON didn't take effect until the user sent a message.

Apply the new config directly in HandleAsync(ConfigurationChangedNotification). The event resumes on the agent's execution context so _dataScope is the right one, and DataContextScope's backing ConcurrentDictionary makes the write safe alongside concurrent reads.

Test plan

  • Edit agent JSON to change HeartbeatPeriod while idle; heartbeat fires at the new interval without sending a message
  • dotnet build + dotnet test clean
## Summary When idle, the agent's iteration loop doesn't run — so the previous "apply pending config at top of iteration" mechanism left stale config in the data scope indefinitely. Heartbeat read the old `HeartbeatPeriod`, so changes to the agent JSON didn't take effect until the user sent a message. Apply the new config directly in `HandleAsync(ConfigurationChangedNotification)`. The event resumes on the agent's execution context so `_dataScope` is the right one, and `DataContextScope`'s backing `ConcurrentDictionary` makes the write safe alongside concurrent reads. ## Test plan - [ ] Edit agent JSON to change `HeartbeatPeriod` while idle; heartbeat fires at the new interval without sending a message - [ ] `dotnet build` + `dotnet test` clean
copilot-pull-request-reviewer[bot] (Migrated from github.com) reviewed 2026-05-22 19:00:21 -04:00
copilot-pull-request-reviewer[bot] (Migrated from github.com) left a comment

Pull request overview

This PR aims to make agent config reload take effect even while the agent is idle by applying updated configuration immediately when a ConfigurationChangedNotification is received (rather than waiting until the next iteration/turn).

Changes:

  • Remove the _pendingConfig staging mechanism and the per-iteration ApplyPendingConfig() call.
  • Apply the updated AgentConfig directly to the agent’s _dataScope inside HandleAsync(ConfigurationChangedNotification).
  • Adjust the reload log message to no longer imply “next turn” semantics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

## Pull request overview This PR aims to make agent config reload take effect even while the agent is idle by applying updated configuration immediately when a `ConfigurationChangedNotification` is received (rather than waiting until the next iteration/turn). **Changes:** - Remove the `_pendingConfig` staging mechanism and the per-iteration `ApplyPendingConfig()` call. - Apply the updated `AgentConfig` directly to the agent’s `_dataScope` inside `HandleAsync(ConfigurationChangedNotification)`. - Adjust the reload log message to no longer imply “next turn” semantics. --- 💡 <a href="/jasoncouture/llama-shears/new/main?filename=.github/instructions/*.instructions.md" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Add Copilot custom instructions</a> for smarter, more guided reviews. <a href="https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Learn how to get started</a>.
@ -166,7 +165,8 @@ public sealed partial class Agent :
{
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-22 19:00:21 -04:00

Applying the updated config directly into _dataScope from the event handler can be lost (or partially applied) if a turn is currently running. CreateAsyncScopeWithData() opens a IDataContextScope.BeginScope() frame for the duration of a turn, and DataContextScope only persists IPersistentDataContextItem values when that frame is popped; AgentConfig is not persistent, so a config update arriving mid-turn would be written into the inner frame and then discarded at the end of the turn, reverting to the old config. This also breaks the stated per-turn consistency of AgentConfig (one turn should see one config end-to-end). Consider deferring the write until no turn scope is active (e.g., acquire _agentLock before calling SetItem, so updates during a turn wait until the turn completes) or otherwise ensure the update targets the outer/root scope.

Applying the updated config directly into `_dataScope` from the event handler can be lost (or partially applied) if a turn is currently running. `CreateAsyncScopeWithData()` opens a `IDataContextScope.BeginScope()` frame for the duration of a turn, and `DataContextScope` only persists `IPersistentDataContextItem` values when that frame is popped; `AgentConfig` is not persistent, so a config update arriving mid-turn would be written into the inner frame and then discarded at the end of the turn, reverting to the old config. This also breaks the stated per-turn consistency of `AgentConfig` (one turn should see one config end-to-end). Consider deferring the write until no turn scope is active (e.g., acquire `_agentLock` before calling `SetItem`, so updates during a turn wait until the turn completes) or otherwise ensure the update targets the outer/root scope.
Sign in to join this conversation.
No description provided.