From 9c31ea689aea0cd130973b88e3e80cc6a09ef007 Mon Sep 17 00:00:00 2001 From: rudy2steiner Date: Sun, 9 Aug 2026 01:13:27 +0800 Subject: [PATCH 1/3] fix(middleware): avoid event-loop blocking in WorkspaceContextMiddleware onSystemPrompt --- .../WorkspaceContextMiddleware.java | 21 +++++++++------ ...paceContextMiddlewareMemoryPromptTest.java | 26 +++++++++++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java b/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java index 30c532f5c1..a08f921bfa 100644 --- a/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java +++ b/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.stream.Collectors; import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; /** * Appends workspace context (session info, AGENTS.md, MEMORY.md, knowledge) to the @@ -181,14 +182,18 @@ public boolean isDisableMemoryHooks() { @Override public Mono onSystemPrompt(Agent agent, RuntimeContext ctx, String currentPrompt) { - RuntimeContext rc = ctx != null ? ctx : RuntimeContext.empty(); - String section = buildWorkspaceSection(rc); - if (section.isEmpty()) { - return Mono.just(currentPrompt); - } - String base = currentPrompt != null ? currentPrompt : ""; - String separator = base.isEmpty() || base.endsWith("\n") ? "" : "\n"; - return Mono.just(base + separator + section); + return Mono.fromCallable( + () -> { + RuntimeContext rc = ctx != null ? ctx : RuntimeContext.empty(); + String section = buildWorkspaceSection(rc); + if (section.isEmpty()) { + return currentPrompt; + } + String base = currentPrompt != null ? currentPrompt : ""; + String separator = base.isEmpty() || base.endsWith("\n") ? "" : "\n"; + return base + separator + section; + }) + .subscribeOn(Schedulers.boundedElastic()); } private String buildWorkspaceSection(RuntimeContext rc) { diff --git a/agentscope-harness/src/test/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddlewareMemoryPromptTest.java b/agentscope-harness/src/test/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddlewareMemoryPromptTest.java index af841b9a1d..ce7a72b249 100644 --- a/agentscope-harness/src/test/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddlewareMemoryPromptTest.java +++ b/agentscope-harness/src/test/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddlewareMemoryPromptTest.java @@ -17,6 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; import io.agentscope.core.agent.RuntimeContext; @@ -25,6 +26,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -52,6 +54,30 @@ private WorkspaceManager track(WorkspaceManager wm) { @TempDir Path workspace; + @Test + void onSystemPromptBuildsWorkspaceContextOnBoundedElastic() { + Thread callerThread = Thread.currentThread(); + AtomicReference readThread = new AtomicReference<>(); + WorkspaceManager wm = + track( + new WorkspaceManager(workspace) { + @Override + public String readAgentsMd(RuntimeContext rc) { + readThread.set(Thread.currentThread()); + return "agent persona"; + } + }); + WorkspaceContextMiddleware mw = new WorkspaceContextMiddleware(wm); + + String prompt = mw.onSystemPrompt(null, RuntimeContext.empty(), "BASE\n").block(); + + assertNotNull(prompt); + assertTrue(prompt.contains("agent persona")); + assertNotNull(readThread.get()); + assertNotSame( + callerThread, readThread.get(), "workspace context read ran on caller thread"); + } + @Test void defaultFlags_includeMemoryRecallPersistenceAndContext() throws Exception { Files.writeString(workspace.resolve("MEMORY.md"), "remember: cats prefer windowsills"); From f1c544d475468c6947f6d58488448dba5e404b35 Mon Sep 17 00:00:00 2001 From: Joe Zou Date: Sun, 9 Aug 2026 23:16:14 +0800 Subject: [PATCH 2/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../harness/agent/middleware/WorkspaceContextMiddleware.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java b/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java index a08f921bfa..8a5130fe04 100644 --- a/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java +++ b/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java @@ -185,11 +185,11 @@ public Mono onSystemPrompt(Agent agent, RuntimeContext ctx, String curre return Mono.fromCallable( () -> { RuntimeContext rc = ctx != null ? ctx : RuntimeContext.empty(); + String base = currentPrompt != null ? currentPrompt : ""; String section = buildWorkspaceSection(rc); if (section.isEmpty()) { - return currentPrompt; + return base; } - String base = currentPrompt != null ? currentPrompt : ""; String separator = base.isEmpty() || base.endsWith("\n") ? "" : "\n"; return base + separator + section; }) From f9e4ca471d078393ab5cc343145a0b273dc62ef0 Mon Sep 17 00:00:00 2001 From: rudy2steiner Date: Tue, 11 Aug 2026 23:20:10 +0800 Subject: [PATCH 3/3] fix(test): coverage issue --- .../middleware/WorkspaceContextMiddleware.java | 3 --- ...orkspaceContextMiddlewareMemoryPromptTest.java | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java b/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java index 8a5130fe04..8f0abf3999 100644 --- a/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java +++ b/agentscope-harness/src/main/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddleware.java @@ -187,9 +187,6 @@ public Mono onSystemPrompt(Agent agent, RuntimeContext ctx, String curre RuntimeContext rc = ctx != null ? ctx : RuntimeContext.empty(); String base = currentPrompt != null ? currentPrompt : ""; String section = buildWorkspaceSection(rc); - if (section.isEmpty()) { - return base; - } String separator = base.isEmpty() || base.endsWith("\n") ? "" : "\n"; return base + separator + section; }) diff --git a/agentscope-harness/src/test/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddlewareMemoryPromptTest.java b/agentscope-harness/src/test/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddlewareMemoryPromptTest.java index ce7a72b249..c3d229f9c3 100644 --- a/agentscope-harness/src/test/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddlewareMemoryPromptTest.java +++ b/agentscope-harness/src/test/java/io/agentscope/harness/agent/middleware/WorkspaceContextMiddlewareMemoryPromptTest.java @@ -78,6 +78,21 @@ public String readAgentsMd(RuntimeContext rc) { callerThread, readThread.get(), "workspace context read ran on caller thread"); } + @Test + void onSystemPromptHandlesNullAndNonNewlineBasePrompts() { + WorkspaceManager wm = track(new WorkspaceManager(workspace)); + WorkspaceContextMiddleware mw = new WorkspaceContextMiddleware(wm); + + String promptWithoutBase = mw.onSystemPrompt(null, null, null).block(); + String promptWithBase = mw.onSystemPrompt(null, RuntimeContext.empty(), "BASE").block(); + + assertNotNull(promptWithoutBase); + assertFalse(promptWithoutBase.startsWith("null")); + assertTrue(promptWithoutBase.contains("## Domain Knowledge")); + assertNotNull(promptWithBase); + assertTrue(promptWithBase.startsWith("BASE\n")); + } + @Test void defaultFlags_includeMemoryRecallPersistenceAndContext() throws Exception { Files.writeString(workspace.resolve("MEMORY.md"), "remember: cats prefer windowsills");