Skip to content

Commit 11dc5cb

Browse files
committed
fix: compact tool progress display
1 parent 1150497 commit 11dc5cb

21 files changed

Lines changed: 197 additions & 47 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai
2121

2222
### Fixes
2323

24+
- Agents/UI: compact exec and tool progress rows by hiding redundant shell tool names, replacing known workspace paths with short context markers, and preserving Discord trace scrubbing for compact command lines.
2425
- ACPX: run and await the embedded ACP backend startup probe by default so the gateway `ready` signal no longer fires before the acpx runtime has either become usable or reported a probe failure; set `OPENCLAW_ACPX_RUNTIME_STARTUP_PROBE=0` to restore lazy startup. Fixes #79596. Thanks @bzelones.
2526
- OpenAI-compatible models: strip prior assistant reasoning fields from replayed Chat Completions history by default, preventing oMLX/vLLM Qwen follow-up turns from rejecting or stalling on stale `reasoning` payloads. Fixes #46637. Thanks @zipzagster and @lexhoefsloot.
2627
- CLI/onboarding: give non-Azure custom providers a safe generated context window and heal legacy 4k wizard entries without overwriting explicit valid small model limits, preventing first-turn compaction loops. Fixes #79428. (#79911) Thanks @Jefsky.

extensions/codex/src/app-server/event-projector.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,7 @@ describe("CodexAppServerEventProjector", () => {
10341034

10351035
expect(onToolResult).toHaveBeenCalledTimes(1);
10361036
expect(onToolResult).toHaveBeenCalledWith({
1037-
text: "🛠️ Bash: `run tests (in /workspace)`",
1037+
text: "🛠️ `run tests (workspace)`",
10381038
});
10391039
});
10401040

@@ -1066,7 +1066,7 @@ describe("CodexAppServerEventProjector", () => {
10661066
);
10671067

10681068
expect(onToolResult).toHaveBeenCalledWith({
1069-
text: "🛠️ Bash: `` run tests (in /workspace), `pnpm test extensions/codex` ``",
1069+
text: "🛠️ `` run tests (workspace), `pnpm test extensions/codex` ``",
10701070
});
10711071
});
10721072

extensions/discord/src/monitor/message-handler.process.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1624,7 +1624,7 @@ describe("processDiscordMessage draft streaming", () => {
16241624
await runProcessDiscordMessage(ctx);
16251625

16261626
expect(draftStream.update).toHaveBeenCalledWith(
1627-
"Shelling\n🛠️ Exec: run tests, `pnpm test -- --watch=false`\n• done",
1627+
"Shelling\n🛠️ run tests, `pnpm test -- --watch=false`\n• done",
16281628
);
16291629
});
16301630

extensions/discord/src/monitor/reply-delivery.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,11 @@ describe("deliverDiscordReply", () => {
135135
{
136136
text: [
137137
"📊 Session Status: current",
138-
"🛠️ Exec: run git status",
138+
"🛠️ run git status",
139+
"🛠️ `gh pr view`",
140+
"🛠️ `docker compose up`",
141+
"🛠️ elevated · `cd /tmp && pnpm test`",
142+
"🛠️ pty · `apply_patch update`",
139143
"📖 Read: lines 1-40 from secret.md",
140144
"Visible reply.",
141145
].join("\n"),

extensions/discord/src/monitor/reply-safety.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-runtime";
44

55
const DISCORD_INTERNAL_TRACE_LINE_RE =
66
/^(?:>\s*)?(?:📊|🛠|📖|📝|🔍|🔎|)\s*(?:Session Status|Exec|Read|Edit|Write|Patch|Search|Open|Click|Find|Screenshot|Update Plan|Tool Call|Tool Result|Function Call|Shell|Command)\s*:/i;
7+
const DISCORD_INTERNAL_COMPACT_COMMAND_TRACE_LINE_RE =
8+
/^(?:>\s*)?🛠\s*(?:(?:(?:elevated|pty)\b\s*(?:·|,)\s*)+)?(?:`{1,2}\s*\S|(?:run|check|fetch|pull|push|view|show|list|switch|create|merge|rebase|stage|restore|reset|stash|search|find|print|copy|move|remove|install|start|cd|git|pnpm|npm|yarn|bun|node|python|python3|bash|sh)\b)/i;
79
const DISCORD_INTERNAL_CHANNEL_LINE_RE =
810
/^(?:>\s*)?(?:analysis|commentary|tool[-_ ]?call|tool[-_ ]?result|function[-_ ]?call|thinking|reasoning)\s*[:=]/i;
911

@@ -46,6 +48,7 @@ function stripDiscordInternalTraceLines(text: string): string {
4648
const trimmed = line.trim();
4749
if (
4850
DISCORD_INTERNAL_TRACE_LINE_RE.test(trimmed) ||
51+
DISCORD_INTERNAL_COMPACT_COMMAND_TRACE_LINE_RE.test(trimmed) ||
4952
DISCORD_INTERNAL_CHANNEL_LINE_RE.test(trimmed)
5053
) {
5154
continue;

extensions/feishu/src/reply-dispatcher.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1218,7 +1218,7 @@ describe("createFeishuReplyDispatcher streaming behavior", () => {
12181218
);
12191219
expect(updateTexts).toEqual(
12201220
expect.arrayContaining([
1221-
expect.stringContaining("🛠️ Exec: run tests, `pnpm test -- --watch=false`"),
1221+
expect.stringContaining("🛠️ run tests, `pnpm test -- --watch=false`"),
12221222
]),
12231223
);
12241224
});

extensions/mattermost/src/mattermost/draft-stream.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ describe("buildMattermostToolStatusText", () => {
254254
args: { command: "pnpm test -- --watch=false" },
255255
detailMode: "raw",
256256
}),
257-
).toBe("🛠️ Exec: run tests, `pnpm test -- --watch=false`");
257+
).toBe("🛠️ run tests, `pnpm test -- --watch=false`");
258258
});
259259

260260
it("can hide raw exec detail from status text", () => {

extensions/telegram/src/bot-message-dispatch.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1135,7 +1135,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
11351135
});
11361136

11371137
expect(answerDraftStream.update).toHaveBeenCalledWith(
1138-
expect.stringMatching(/`🛠 Exec: git rev-parse --abbrev-ref HEAD`$/),
1138+
expect.stringMatching(/`🛠 git rev-parse --abbrev-ref HEAD`$/),
11391139
);
11401140
expect(answerDraftStream.update).not.toHaveBeenCalledWith("Branch is up to date");
11411141
expect(answerDraftStream.clear).toHaveBeenCalledTimes(1);

src/agents/tool-display-exec.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,49 @@ type ExecSummary = {
297297
allGeneric?: boolean;
298298
};
299299

300+
function normalizePathForDisplay(path: string): string {
301+
return path.replace(/\\/g, "/").replace(/\/+$/g, "");
302+
}
303+
304+
function classifyWorkspacePath(path: string): "agent" | "repo" | "workspace" | undefined {
305+
const normalized = normalizePathForDisplay(path);
306+
const segments = normalized.split("/").filter(Boolean);
307+
if (segments.length === 0) {
308+
return undefined;
309+
}
310+
311+
for (let index = 0; index < segments.length; index += 1) {
312+
const segment = segments[index];
313+
if (!segment) {
314+
continue;
315+
}
316+
if (segment === ".openclaw" && segments[index + 1] === "workspace") {
317+
return "agent";
318+
}
319+
if (/[-_]workspace$/i.test(segment) && segment.toLowerCase() !== "workspace") {
320+
return "agent";
321+
}
322+
if (/^workspace[-_]/i.test(segment)) {
323+
return "agent";
324+
}
325+
}
326+
327+
if (segments.includes("Projects") || segments.includes("projects")) {
328+
return "repo";
329+
}
330+
331+
if (segments.at(-1)?.toLowerCase() === "workspace") {
332+
return "workspace";
333+
}
334+
335+
return undefined;
336+
}
337+
338+
function formatCwdSuffix(cwd: string): string {
339+
const workspace = classifyWorkspacePath(cwd);
340+
return workspace ? `(${workspace})` : `(in ${cwd})`;
341+
}
342+
300343
function summarizeExecCommand(command: string): ExecSummary | undefined {
301344
const { command: cleaned, chdirPath } = stripShellPreamble(command);
302345
if (!cleaned) {
@@ -415,10 +458,10 @@ export function resolveExecDetail(
415458

416459
const compact = compactRawCommand(unwrapped);
417460
if (result?.allGeneric !== false && isGenericSummary(summary)) {
418-
return cwd ? `${compact} (in ${cwd})` : compact;
461+
return cwd ? `${compact} ${formatCwdSuffix(cwd)}` : compact;
419462
}
420463

421-
const displaySummary = cwd ? `${summary} (in ${cwd})` : summary;
464+
const displaySummary = cwd ? `${summary} ${formatCwdSuffix(cwd)}` : summary;
422465
if (
423466
options?.detailMode !== "explain" &&
424467
compact &&

src/agents/tool-display.test.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "vitest";
2-
import { formatToolDetail, resolveToolDisplay } from "./tool-display.js";
2+
import { formatToolDetail, formatToolSummary, resolveToolDisplay } from "./tool-display.js";
33

44
describe("tool display details", () => {
55
it("skips zero/false values for optional detail fields", () => {
@@ -128,7 +128,7 @@ describe("tool display details", () => {
128128
);
129129

130130
expect(detail).toContain("check git status -> show first 3 lines");
131-
expect(detail).toContain(".openclaw/workspace)");
131+
expect(detail).toContain("(agent)");
132132
});
133133

134134
it("summarizes bash commands with the same command explainer", () => {
@@ -166,6 +166,49 @@ describe("tool display details", () => {
166166
expect(detail).toBe("install dependencies (in ~/my-project)");
167167
});
168168

169+
it("uses compact workspace markers for common workspace paths", () => {
170+
expect(
171+
formatToolDetail(
172+
resolveToolDisplay({
173+
name: "bash",
174+
args: { command: "git fetch", workdir: "/Users/peter/mantis-workspace/openclaw" },
175+
detailMode: "explain",
176+
}),
177+
),
178+
).toBe("fetch git changes (agent)");
179+
180+
expect(
181+
formatToolDetail(
182+
resolveToolDisplay({
183+
name: "bash",
184+
args: { command: "git status", workdir: "/Users/peter/Projects/openclaw" },
185+
detailMode: "explain",
186+
}),
187+
),
188+
).toBe("check git status (repo)");
189+
});
190+
191+
it("omits bash and exec names from compact tool summaries", () => {
192+
expect(
193+
formatToolSummary(
194+
resolveToolDisplay({
195+
name: "bash",
196+
args: { command: "git fetch", workdir: "/Users/peter/mantis-workspace/openclaw" },
197+
detailMode: "explain",
198+
}),
199+
),
200+
).toBe("🛠️ fetch git changes (agent)");
201+
202+
expect(
203+
formatToolSummary(
204+
resolveToolDisplay({
205+
name: "web_search",
206+
args: { query: "OpenClaw docs" },
207+
}),
208+
),
209+
).toBe('🔎 Web Search: for "OpenClaw docs"');
210+
});
211+
169212
it("moves cd path to context suffix with multiple stages and raw command", () => {
170213
const detail = formatToolDetail(
171214
resolveToolDisplay({

0 commit comments

Comments
 (0)