agent-refactor: shrink Agent, event-driven compaction/interrupt, scope ownership #88
No reviewers
Labels
No labels
bug
commercial
documentation
duplicate
enhancement
feature
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
jasoncouture/llama-shears!88
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "agent-refactor"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
IAgent.Id,LockAsync/UnlockAsync,RequestCompactionAsync,InterruptAsync— IAgent's public surface is now justStartAsync.IAgentregistered scoped, resolved viascope.ServiceProvider.GetRequiredService<IAgent>(), scope disposal cascades throughIAsyncDisposable.AgentManagerno longer disposes the agent directly.AgentManagerctor sheds unusedIProviderFactoryandIContextStore.IAgentLockManager(singleton) hands out per-agentSemaphoreSlim-backedILockScopes with finalizer-safe release.IAgentLockis a scoped passthrough that reads the agent id from the ambientIDataContextScope.SemaphoreSlim_processGateis gone — turn/compaction serialization ridesIAgentLock.InferenceRunnerinjectsILanguageModelandISystemPromptProvider;RunAsyncdropsmodel,eventId,correlationId, andemitTurnsparameters.PromptOptionscarriesSystemPromptTemplate(null = skip) andEmitTurns.AgentState; system-prompt rendering moves out of Agent and ContextCompactor into the runner.CompactAsyncdropsILanguageModelandModelConfigurationparameters; configuration comes from the data scope, language model from the runner.SystemPromptTemplate: "COMPACTION.md".command:*event source.command:compaction-request:<agentId>(payloadAgentCompactionRequestwithForce) andcommand:interrupt-agent:<agentId>(payloadAgentInterruptRequest.Instance) replace direct method calls into the agent.CompactionAgentServiceis a per-agent scoped service that subscribes to both the agent's Idle (normal pass) and CompactionRequest (forced per payload) events and dispatches toIContextCompactor. Agent itself knows nothing about compaction.EagerCompactordeleted.command:interrupt-agentand cancels its active turn CTS; the old publicInterruptAsyncmethod is gone.LoopbackBearerHandlerresolves the calling agent fromIDataContextFactory.Current(dropsICurrentAgentAccessor, which is deleted entirely).IAgentServiceintroduced for agent-lifetime services; Agent ctor takesIEnumerable<IAgentService>and orchestrates Start/Stop around its run loop.IAgentDirectoryis frozen —RequestCompactionAsync/InterruptAsyncare flagged[Obsolete]with migration pointers to the command events; do-not-extend note at the interface level.EventType($"{type}:{id}"); passEventType with { Id = ... }and rely on the implicitstringconversion. Wildcard (:+) is the only remaining exception.AgentLifecycleMarkerandAgentCompactionRequest(renamed fromAgentCompactionMarker) get singletonInstance/Normal/Forcedstatics so callers stop allocating.Test plan
dotnet buildclean (warnings only on[Obsolete]callers, expected)dotnet test— 519/519 pass/compactslash command on a loaded agent still forces compaction/interruptslash command still cancels the active turn🤖 Generated with Claude Code
Pull request overview
This PR refactors agent ownership and control flow by shrinking
IAgent, moving compaction/interrupt operations onto command events, and making inference/compaction use scoped dependencies and ambient agent state.Changes:
IAgentLock/IAgentLockManagerandcommand:*events.InferenceRunnerand updates compaction accordingly.Reviewed changes
Copilot reviewed 58 out of 58 changed files in this pull request and generated 2 comments.
Show a summary per file
tests/LlamaShears.UnitTests/Agent/Core/InferenceRunnerToolDispatchTests.csRunAsynccalls.tests/LlamaShears.UnitTests/Agent/Core/ContextCompactorTests.cstests/LlamaShears.UnitTests/Agent/Core/CapturingTurnSubscriber.cstests/LlamaShears.UnitTests/Agent/Core/AgentTurnFlowTests.cstests/LlamaShears.UnitTests/Agent/Core/AgentLoopTests.cstests/LlamaShears.UnitTests/Agent/Core/AgentLockTests.cstests/LlamaShears.UnitTests/Agent/Core/AgentLockManagerTests.cstests/LlamaShears.UnitTests/Agent/Core/AgentInterruptTests.cstests/LlamaShears.UnitTests/Agent/Core/AgentInterruptGracefulTests.cstests/LlamaShears.UnitTests/Agent/Core/AgentEventPublishingTests.cstests/LlamaShears.IntegrationTests/Hosting/IsolatedAppFactory.cssrc/public/LlamaShears.Core.Abstractions/Provider/PromptOptions.cssrc/public/LlamaShears.Core.Abstractions/Provider/IInferenceRunner.cssrc/public/LlamaShears.Core.Abstractions/Events/Event.cssrc/public/LlamaShears.Core.Abstractions/Events/Agent/AgentLifecycleMarker.cssrc/public/LlamaShears.Core.Abstractions/Events/Agent/AgentInterruptRequest.cssrc/public/LlamaShears.Core.Abstractions/Events/Agent/AgentCompactionRequest.cssrc/public/LlamaShears.Core.Abstractions/Events/Agent/AgentCompactionMarker.cssrc/public/LlamaShears.Core.Abstractions/Context/IContextCompactor.cssrc/public/LlamaShears.Core.Abstractions/Agent/ILockScope.cssrc/public/LlamaShears.Core.Abstractions/Agent/IAgentService.cssrc/public/LlamaShears.Core.Abstractions/Agent/IAgentLockManager.cssrc/public/LlamaShears.Core.Abstractions/Agent/IAgentLock.cssrc/public/LlamaShears.Core.Abstractions/Agent/IAgent.csStartAsync.src/LlamaShears.Core/Tools/ModelContextProtocol/LoopbackBearerHandler.cssrc/LlamaShears.Core/Tools/ModelContextProtocol/ICurrentAgentAccessor.cssrc/LlamaShears.Core/Tools/ModelContextProtocol/CurrentAgentAccessor.cssrc/LlamaShears.Core/InferenceRunner.cssrc/LlamaShears.Core/EagerCompactor.cssrc/LlamaShears.Core/CoreServiceCollectionExtensions.cssrc/LlamaShears.Core/ContextCompactor.cssrc/LlamaShears.Core/CompactionAgentService.cssrc/LlamaShears.Core/AgentManager.cssrc/LlamaShears.Core/AgentLockManager.cssrc/LlamaShears.Core/AgentLock.cssrc/LlamaShears.Core/Agent.cssrc/LlamaShears.Api/Web/AgentDirectory.cssrc/LlamaShears.Api.Web/Services/IAgentDirectory.cssrc/LlamaShears.Api.Web/Services/ChatSession.csdocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Provider/PromptOptions.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Provider/index.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Provider/IInferenceRunner.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Events/Event/WellKnown/index.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Events/Event/WellKnown/Command.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Events/Event/Sources.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Events/Agent/index.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Events/Agent/AgentLifecycleMarker.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Events/Agent/AgentInterruptRequest.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Events/Agent/AgentCompactionRequest.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Events/Agent/AgentCompactionMarker.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Context/IContextCompactor.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Agent/index.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Agent/ILockScope.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Agent/IAgentService.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Agent/IAgentLockManager.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Agent/IAgentLock.mddocs/api/LlamaShears.Core.Abstractions/LlamaShears/Core/Abstractions/Agent/IAgent.mddocs/api/LlamaShears.Core.Abstractions/index.mdComments suppressed due to low confidence (1)
src/LlamaShears.Core/ContextCompactor.cs:149
forcevalue by publishing a defaultAgentCompactionRequest, so forced compactions are reported as normal completions. Reuse the same payload derived from the method'sforceargument for both started and finished events.💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
After publishing the Busy lifecycle event,
isIdleremainstrue, so the next loop iteration never satisfies!_sessionQueue.HasQueuedMessages() && !isIdleand the Idle event is never emitted. That meansCompactionAgentServicenever receives the normal idle compaction trigger; flipisIdletofalsewhen the agent transitions to busy.The compaction-started event now uses
AgentCompactionRequestas its payload, but this always publishesForce = falseeven whenCompactAsyncwas invoked withforce: true(for example from/compact). Publish the payload derived from theforceargument so observers of the lifecycle event receive accurate compaction metadata.This issue also appears on line 146 of the same file.