diff --git a/mateclaw-server/src/main/java/vip/mate/agent/context/ConversationWindowManager.java b/mateclaw-server/src/main/java/vip/mate/agent/context/ConversationWindowManager.java index 7015be02..aac41650 100644 --- a/mateclaw-server/src/main/java/vip/mate/agent/context/ConversationWindowManager.java +++ b/mateclaw-server/src/main/java/vip/mate/agent/context/ConversationWindowManager.java @@ -1027,6 +1027,33 @@ public class ConversationWindowManager { + "' can be called again if its result is needed.]"; } + /** + * One-line informative summary for a cleared tool result: tool name, + * original size, and the first line as a gist. Far more useful to the + * model than a bare "removed" marker — it can decide whether re-running + * the tool is worth it without guessing what the output was. + */ + static String buildInformativeCleared(String toolName, String body) { + String safeName = (toolName == null || toolName.isBlank()) ? "tool" : toolName; + int length = body == null ? 0 : body.length(); + String gist = ""; + if (body != null) { + for (String line : body.split("\n", 8)) { + String candidate = line.strip(); + if (!candidate.isEmpty()) { + gist = candidate.length() > 80 ? candidate.substring(0, 80) + "…" : candidate; + break; + } + } + } + StringBuilder sb = new StringBuilder("[").append(safeName) + .append(" → ").append(length).append(" chars, cleared to save context"); + if (!gist.isEmpty()) { + sb.append("; began: \"").append(gist).append('"'); + } + return sb.append("; call the tool again if the result is still needed]").toString(); + } + /** * Phase 1 - Soft trim:对工具结果做 head+tail 裁剪(保留首尾各 200 字符)。 *
Spill-marker responses are left untouched so their on-disk pointer
@@ -1081,7 +1108,8 @@ public class ConversationWindowManager {
replaced.add(r);
continue;
}
- replaced.add(new ToolResponseMessage.ToolResponse(r.id(), r.name(), "[tool result removed]"));
+ replaced.add(new ToolResponseMessage.ToolResponse(r.id(), r.name(),
+ buildInformativeCleared(r.name(), r.responseData())));
changed = true;
}
if (changed) {
diff --git a/mateclaw-server/src/test/java/vip/mate/agent/context/ConversationWindowManagerSpillMarkerPreservationTest.java b/mateclaw-server/src/test/java/vip/mate/agent/context/ConversationWindowManagerSpillMarkerPreservationTest.java
index b6112f82..b434e3ed 100644
--- a/mateclaw-server/src/test/java/vip/mate/agent/context/ConversationWindowManagerSpillMarkerPreservationTest.java
+++ b/mateclaw-server/src/test/java/vip/mate/agent/context/ConversationWindowManagerSpillMarkerPreservationTest.java
@@ -71,9 +71,12 @@ class ConversationWindowManagerSpillMarkerPreservationTest {
ToolResponseMessage trm0 = (ToolResponseMessage) messages.get(0);
ToolResponseMessage trm1 = (ToolResponseMessage) messages.get(1);
assertEquals(SPILL_BODY, trm0.getResponses().getFirst().responseData(),
- "Phase 2 hard clear must not replace a spill-marker body with [tool result removed]");
- assertEquals("[tool result removed]", trm1.getResponses().getFirst().responseData(),
+ "Phase 2 hard clear must not replace a spill-marker body with the cleared placeholder");
+ String clearedBody = trm1.getResponses().getFirst().responseData();
+ assertTrue(clearedBody.contains("cleared to save context"),
"non-spill bodies should still be replaced by Phase 2");
+ assertTrue(clearedBody.contains("search") && clearedBody.contains("2000 chars"),
+ "the cleared placeholder should carry the tool name and original size");
assertEquals(1, cleared,
"clear counter should reflect only the non-spill body that was actually replaced");
}
@@ -120,7 +123,7 @@ class ConversationWindowManagerSpillMarkerPreservationTest {
ToolResponseMessage trm = (ToolResponseMessage) messages.getFirst();
assertEquals(SPILL_BODY, trm.getResponses().get(0).responseData(),
"the spill response in a mixed message must survive Phase 2");
- assertEquals("[tool result removed]", trm.getResponses().get(1).responseData(),
+ assertTrue(trm.getResponses().get(1).responseData().contains("cleared to save context"),
"the non-spill response in a mixed message must still be cleared");
}
diff --git a/mateclaw-server/src/test/java/vip/mate/agent/context/InformativeClearedPlaceholderTest.java b/mateclaw-server/src/test/java/vip/mate/agent/context/InformativeClearedPlaceholderTest.java
new file mode 100644
index 00000000..89c3f04a
--- /dev/null
+++ b/mateclaw-server/src/test/java/vip/mate/agent/context/InformativeClearedPlaceholderTest.java
@@ -0,0 +1,46 @@
+package vip.mate.agent.context;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests for the informative cleared-tool-output placeholder — name, size and
+ * first-line gist survive the clearing so the model can judge whether a
+ * re-run is worthwhile.
+ */
+class InformativeClearedPlaceholderTest {
+
+ @Test
+ @DisplayName("placeholder carries tool name, original size and first-line gist")
+ void carriesNameSizeAndGist() {
+ String body = "total 47 tests, 0 failures\n