mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 23:30:42 +00:00
fix: show web search queries in progress drafts
This commit is contained in:
@@ -7,7 +7,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Changes
|
||||
|
||||
- Agents/failover: harden state-aware lane suspension by persisting quota resume transitions, restoring configured lane concurrency, preserving non-quota failure reasons, and exporting model failover events through diagnostics OTLP. Thanks @BunsDev.
|
||||
- Channels/streaming: make progress draft labels scroll away with other progress lines, render structured tool rows as compact emoji/details, and skip empty Discord apply-patch starts until a patch summary exists. (#79146)
|
||||
- Channels/streaming: make progress draft labels scroll away with other progress lines, render structured tool rows as compact emoji/details, show web-search queries from provider-native argument shapes, and skip empty Discord apply-patch starts until a patch summary exists. (#79146)
|
||||
- Telegram: preserve the channel-specific 10-option poll cap in the unified outbound adapter so over-limit polls are rejected before send. (#78762) Thanks @obviyus.
|
||||
- Runtime/install: raise the supported Node 22 floor to `22.16+` so native SQLite query handling can rely on the `node:sqlite` statement metadata API while continuing to recommend Node 24. (#78921)
|
||||
- Discord/voice: include a bounded one-line STT transcript preview in verbose voice logs so live voice debugging shows what speakers said before the agent reply.
|
||||
|
||||
@@ -687,7 +687,7 @@ Default slash command settings:
|
||||
- `block` emits draft-sized chunks (use `draftChunk` to tune size and breakpoints, clamped to `textChunkLimit`).
|
||||
- Media, error, and explicit-reply finals cancel pending preview edits.
|
||||
- `streaming.preview.toolProgress` (default `true`) controls whether tool/progress updates reuse the preview message.
|
||||
- Tool/progress rows render as compact emoji + detail when available, for example `🛠️ run tests`, and omit repeated tool names unless no clearer detail exists.
|
||||
- Tool/progress rows render as compact emoji + title + detail when available, for example `🛠️ Bash: run tests` or `🔎 Web Search: for "query"`.
|
||||
- `streaming.preview.commandText` / `streaming.progress.commandText` controls command/exec detail in compact progress lines: `raw` (default) or `status` (tool label only).
|
||||
|
||||
Hide raw command/exec text while keeping compact progress lines:
|
||||
|
||||
@@ -19,8 +19,8 @@ into the final answer when the channel can do that safely.
|
||||
```text
|
||||
Shelling...
|
||||
📖 from docs/concepts/progress-drafts.md
|
||||
🔎 for "discord edit message"
|
||||
🛠️ run tests
|
||||
🔎 Web Search: for "discord edit message"
|
||||
🛠️ Bash: run tests
|
||||
```
|
||||
|
||||
Use progress drafts when you want one tidy status message during tool-heavy work
|
||||
@@ -60,9 +60,9 @@ The label appears after the agent starts meaningful work and either remains busy
|
||||
for five seconds or emits a second work event. It is part of the rolling progress
|
||||
line list, so the starter status scrolls away once enough concrete work appears.
|
||||
Plain text-only replies do not show a progress draft. Progress lines are added
|
||||
only when the agent emits useful work updates, for example `🛠️ run tests`,
|
||||
`🔎 for "discord edit message"`, or `✍️ to /tmp/file`. By default they use the
|
||||
same compact explain mode as `/verbose`; set
|
||||
only when the agent emits useful work updates, for example `🛠️ Bash: run tests`,
|
||||
`🔎 Web Search: for "discord edit message"`, or `✍️ Write: to /tmp/file`.
|
||||
By default they use the same compact explain mode as `/verbose`; set
|
||||
`agents.defaults.toolProgressDetail: "raw"` when debugging and you also want raw
|
||||
commands/details appended.
|
||||
The final answer replaces the draft when possible; otherwise
|
||||
|
||||
@@ -280,19 +280,76 @@ function resolveWebSearchDetail(args: unknown): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const query = normalizeOptionalString(record.query);
|
||||
const queries = collectWebSearchQueries(record);
|
||||
const count =
|
||||
typeof record.count === "number" && Number.isFinite(record.count) && record.count > 0
|
||||
? Math.floor(record.count)
|
||||
: undefined;
|
||||
: typeof record.max_results === "number" &&
|
||||
Number.isFinite(record.max_results) &&
|
||||
record.max_results > 0
|
||||
? Math.floor(record.max_results)
|
||||
: typeof record.num_results === "number" &&
|
||||
Number.isFinite(record.num_results) &&
|
||||
record.num_results > 0
|
||||
? Math.floor(record.num_results)
|
||||
: typeof record.limit === "number" && Number.isFinite(record.limit) && record.limit > 0
|
||||
? Math.floor(record.limit)
|
||||
: typeof record.top_k === "number" && Number.isFinite(record.top_k) && record.top_k > 0
|
||||
? Math.floor(record.top_k)
|
||||
: undefined;
|
||||
|
||||
if (!query) {
|
||||
if (queries.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return count !== undefined ? `for "${query}" (top ${count})` : `for "${query}"`;
|
||||
const displayedQueries = queries.slice(0, 3).map((query) => `"${query}"`);
|
||||
const queryText =
|
||||
queries.length > displayedQueries.length
|
||||
? `${displayedQueries.join(", ")}…`
|
||||
: displayedQueries.join(", ");
|
||||
|
||||
return count !== undefined ? `for ${queryText} (top ${count})` : `for ${queryText}`;
|
||||
}
|
||||
|
||||
function collectWebSearchQueries(record: Record<string, unknown>): string[] {
|
||||
const queries: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
const add = (value: unknown) => {
|
||||
const normalized = normalizeOptionalString(value);
|
||||
if (!normalized || seen.has(normalized)) {
|
||||
return;
|
||||
}
|
||||
seen.add(normalized);
|
||||
queries.push(normalized);
|
||||
};
|
||||
|
||||
add(record.query);
|
||||
add(record.q);
|
||||
add(record.search);
|
||||
add(record.input);
|
||||
|
||||
for (const key of ["search_query", "image_query", "queries"]) {
|
||||
const value = record[key];
|
||||
if (!Array.isArray(value)) {
|
||||
continue;
|
||||
}
|
||||
for (const entry of value) {
|
||||
if (typeof entry === "string") {
|
||||
add(entry);
|
||||
continue;
|
||||
}
|
||||
const entryRecord = asRecord(entry);
|
||||
if (!entryRecord) {
|
||||
continue;
|
||||
}
|
||||
add(entryRecord.query);
|
||||
add(entryRecord.q);
|
||||
add(entryRecord.search);
|
||||
}
|
||||
}
|
||||
|
||||
return queries;
|
||||
}
|
||||
function resolveWebFetchDetail(args: unknown): string | undefined {
|
||||
const record = asRecord(args);
|
||||
if (!record) {
|
||||
|
||||
@@ -88,6 +88,33 @@ describe("tool display details", () => {
|
||||
expect(detail).toBe('for "OpenClaw docs" (top 3)');
|
||||
});
|
||||
|
||||
it("formats web_search provider query shapes", () => {
|
||||
expect(
|
||||
formatToolDetail(
|
||||
resolveToolDisplay({
|
||||
name: "web_search",
|
||||
args: { q: "Codex OAuth API key", max_results: 5 },
|
||||
}),
|
||||
),
|
||||
).toBe('for "Codex OAuth API key" (top 5)');
|
||||
|
||||
expect(
|
||||
formatToolDetail(
|
||||
resolveToolDisplay({
|
||||
name: "web_search",
|
||||
args: {
|
||||
search_query: [
|
||||
{ q: "latest Kimi model" },
|
||||
{ q: "latest Gemini model" },
|
||||
{ q: "latest Claude model" },
|
||||
{ q: "latest OpenAI model" },
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toBe('for "latest Kimi model", "latest Gemini model", "latest Claude model"…');
|
||||
});
|
||||
|
||||
it("summarizes exec commands with context", () => {
|
||||
const detail = formatToolDetail(
|
||||
resolveToolDisplay({
|
||||
|
||||
@@ -332,6 +332,13 @@ describe("channel-streaming", () => {
|
||||
args: { command: "sed -n '1,80p' extensions/discord/src/draft-stream.ts" },
|
||||
}),
|
||||
).toBe("🛠️ Bash: print lines 1-80 from extensions/discord/src/draft-stream.ts");
|
||||
expect(
|
||||
formatChannelProgressDraftLine({
|
||||
event: "tool",
|
||||
name: "web_search",
|
||||
args: { search_query: [{ q: "Codex OAuth API key" }], response_length: "short" },
|
||||
}),
|
||||
).toBe('🔎 Web Search: for "Codex OAuth API key"');
|
||||
expect(
|
||||
formatChannelProgressDraftLine({
|
||||
event: "item",
|
||||
|
||||
Reference in New Issue
Block a user