AgentScope-Java is an open-source project. To involve a broader community, we recommend asking your questions in English.
Describe the bug
When using HarnessAgent.Builder.fromAgent(ReActAgent) to wrap a ReActAgent into a HarnessAgent, the GracefulShutdownMiddleware appears twice in the middleware chain. This causes onReasoning and onModelCall completion callbacks to fire
twice, and the graceful-shutdown interruptIfShuttingDown check to run twice at each checkpoint.
To Reproduce
- Build a
ReActAgent with any middlewares:
ReActAgent agent = ReActAgent.builder()
.name("test-agent")
.sysPrompt("You are a helpful assistant.")
.model(model)
.build();
2. Wrap it into a HarnessAgent via fromAgent:
HarnessAgent harnessAgent = HarnessAgent.Builder.fromAgent(agent)
.workspace(Path.of("/tmp/workspace"))
.filesystem(new LocalFilesystemSpec())
.build();
3. Check the middleware list of the resulting HarnessAgent's inner ReActAgent:
// The middlewares list will contain two GracefulShutdownMiddleware instances
System.out.println(agent.getMiddlewares());
Expected behavior
GracefulShutdownMiddleware should appear exactly once in the middleware chain, since it is a system-level middleware automatically added by the ReActAgent constructor.
Root cause
The two-phase build in AgentFactory causes the duplication:
1. ReActAgent.builder().build() → constructor (line 301) adds a GracefulShutdownMiddleware to this.middlewares.
2. HarnessAgent.Builder.fromAgent(reactAgent) → filterCopyableMiddlewares() copies it from agent.getMiddlewares() into inner.middlewares.
3. HarnessAgent.Builder.build() → inner.build() → ReActAgent constructor (line 301) adds another GracefulShutdownMiddleware.
filterCopyableMiddlewares only excludes HarnessRuntimeMiddleware, but GracefulShutdownMiddleware is also a system middleware that should not be copied — it is always auto-added by the constructor.
Fix
In HarnessAgent.java's filterCopyableMiddlewares, exclude GracefulShutdownMiddleware:
private static List<MiddlewareBase> filterCopyableMiddlewares(
List<MiddlewareBase> middlewares) {
List<MiddlewareBase> copyable = new ArrayList<>(middlewares.size());
for (MiddlewareBase middleware : middlewares) {
if (middleware != null
&& !(middleware instanceof HarnessRuntimeMiddleware)
&& !(middleware instanceof GracefulShutdownMiddleware)) {
copyable.add(middleware);
}
}
return copyable;
}
Error messages
No error thrown. The symptom is duplicate debug log output:
[DEBUG-MW] ononModelCall 完成, mw=GracefulShutdownMiddleware, agent=commodity-operation-agent
[DEBUG-MW] ononModelCall 完成, mw=GracefulShutdownMiddleware, agent=commodity-operation-agent
[DEBUG-MW] ononReasoning 完成, mw=GracefulShutdownMiddleware, agent=commodity-operation-agent
[DEBUG-MW] ononReasoning 完成, mw=GracefulShutdownMiddleware, agent=commodity-operation-agent
Environment (please complete the following information):
- AgentScope-Java Version: 3.0.5-RELEASE
- Java Version: 17
- OS: macOS
AgentScope-Java is an open-source project. To involve a broader community, we recommend asking your questions in English.
Describe the bug
When using
HarnessAgent.Builder.fromAgent(ReActAgent)to wrap aReActAgentinto aHarnessAgent, theGracefulShutdownMiddlewareappears twice in the middleware chain. This causesonReasoningandonModelCallcompletion callbacks to firetwice, and the graceful-shutdown
interruptIfShuttingDowncheck to run twice at each checkpoint.To Reproduce
ReActAgentwith any middlewares: