Skip to content

Commit 9ef7f24

Browse files
authored
fix: keep media provider inventory internal (#75550)
Summary: - The branch removes the compact image/video provider-inventory output bypass, adds media handler regression tests for hidden and verbose modes, and adds an Unreleased changelog fix entry. - Reproducibility: yes. On current main, a synthetic image_generate or video_generate list result with details ... ut false reaches emitToolOutput; the existing media handler test locks in that old video_generate behavior. ClawSweeper fixups: - Included follow-up commit: chore: rerun ci - Included follow-up commit: chore: move changelog entry below media fixes Validation: - ClawSweeper review passed for head 56069df. - Required merge gates passed before the squash merge. Prepared head SHA: 56069df Review: #75550 (comment) Co-authored-by: mkdev11 <MkDev11@users.noreply.github.com>
1 parent 7be0d6e commit 9ef7f24

3 files changed

Lines changed: 69 additions & 71 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ Docs: https://docs.openclaw.ai
474474
- Telegram/agents: keep typing indicators and optional generation tools off the reply critical path, so fresh Telegram replies no longer stall while provider catalogs and media models load. (#75360) Thanks @obviyus.
475475
- Agents/commitments: run hidden follow-up extraction on the configured agent/default model instead of falling back to direct OpenAI, so OpenAI Codex OAuth-only gateways no longer spam background API-key failures. Fixes #75334. Thanks @sene1337.
476476
- Agents/media: keep async music generation completions on the requester-session wake path even when direct-send completion is enabled, so finished audio stays agent-mediated while video can still opt into direct channel delivery. (#75335) Thanks @vincentkoc.
477+
- Agents/media: keep image and video provider inventory internal when tool output is hidden, so shared chat surfaces no longer expose provider/model/auth-hint details from list results. Fixes #75166. Thanks @MkDev11.
477478
- Security/config-audit: redact CLI argv and execArgv secrets before persisting config audit records, covering write, observe, and recovery paths. Fixes #60826. Thanks @koshaji.
478479
- Gateway/models: keep default and configured model-list views responsive when provider catalog discovery stalls, without hiding real catalog load failures, while `--all` still waits for the exact full catalog. Fixes #75297; refs #74404. Thanks @lisandromachado and @najef1979-code.
479480
- Plugins/runtime-deps: accept already materialized package-level runtime-deps supersets as converged, so later lazy plugin activation no longer prunes and relaunches `pnpm install` after gateway startup pre-staging, reducing event-loop pressure from repeated runtime-deps repair on packaged installs. Fixes #75283; refs #75297 and #72338. Thanks @brokemac79, @lisandromachado, and @midhunmonachan.

src/agents/pi-embedded-subscribe.handlers.tools.media.test.ts

Lines changed: 67 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,44 @@ async function handleCaseVariantBuiltinMedia(mediaPathOrUrl: string) {
142142
return ctx;
143143
}
144144

145+
const providerInventoryText = [
146+
"openai: default=sora-2 | models=sora-2",
147+
"google: default=veo-3.1-fast-generate-preview | models=veo-3.1-fast-generate-preview",
148+
].join("\n");
149+
150+
async function handleProviderInventoryListResult(params: {
151+
toolName: "image_generate" | "video_generate";
152+
shouldEmitToolOutput: boolean;
153+
}) {
154+
const ctx = createMockContext({
155+
shouldEmitToolOutput: params.shouldEmitToolOutput,
156+
onToolResult: vi.fn(),
157+
toolResultFormat: "plain",
158+
});
159+
160+
await handleToolExecutionEnd(ctx, {
161+
type: "tool_execution_end",
162+
toolName: params.toolName,
163+
toolCallId: "tc-1",
164+
isError: false,
165+
result: {
166+
content: [{ type: "text", text: providerInventoryText }],
167+
details: {
168+
providers: [
169+
{ id: "openai", defaultModel: "sora-2", models: ["sora-2"] },
170+
{
171+
id: "google",
172+
defaultModel: "veo-3.1-fast-generate-preview",
173+
models: ["veo-3.1-fast-generate-preview"],
174+
},
175+
],
176+
},
177+
},
178+
});
179+
180+
return ctx;
181+
}
182+
145183
describe("handleToolExecutionEnd media emission", () => {
146184
it("does not warn for read tool when path is provided via file_path alias", async () => {
147185
const ctx = createMockContext();
@@ -424,52 +462,36 @@ describe("handleToolExecutionEnd media emission", () => {
424462
expect(ctx.state.pendingToolMediaUrls).toEqual(["/tmp/generated.png"]);
425463
});
426464

427-
it("emits provider inventory output for compact video_generate list results", async () => {
428-
const ctx = createMockContext({
429-
shouldEmitToolOutput: false,
430-
onToolResult: vi.fn(),
431-
toolResultFormat: "plain",
432-
});
433-
434-
await handleToolExecutionEnd(ctx, {
435-
type: "tool_execution_end",
436-
toolName: "video_generate",
437-
toolCallId: "tc-1",
438-
isError: false,
439-
result: {
440-
content: [
441-
{
442-
type: "text",
443-
text: [
444-
"openai: default=sora-2 | models=sora-2",
445-
"google: default=veo-3.1-fast-generate-preview | models=veo-3.1-fast-generate-preview",
446-
].join("\n"),
447-
},
448-
],
449-
details: {
450-
providers: [
451-
{ id: "openai", defaultModel: "sora-2", models: ["sora-2"] },
452-
{
453-
id: "google",
454-
defaultModel: "veo-3.1-fast-generate-preview",
455-
models: ["veo-3.1-fast-generate-preview"],
456-
},
457-
],
458-
},
459-
},
460-
});
465+
it.each(["image_generate", "video_generate"] as const)(
466+
"keeps %s provider inventory internal when tool output is hidden",
467+
async (toolName) => {
468+
const ctx = await handleProviderInventoryListResult({
469+
toolName,
470+
shouldEmitToolOutput: false,
471+
});
461472

462-
expect(ctx.emitToolOutput).toHaveBeenCalledWith(
463-
"video_generate",
464-
undefined,
465-
[
466-
"openai: default=sora-2 | models=sora-2",
467-
"google: default=veo-3.1-fast-generate-preview | models=veo-3.1-fast-generate-preview",
468-
].join("\n"),
469-
expect.any(Object),
470-
);
471-
expect(ctx.state.pendingToolMediaUrls).toEqual([]);
472-
});
473+
expect(ctx.emitToolOutput).not.toHaveBeenCalled();
474+
expect(ctx.state.pendingToolMediaUrls).toEqual([]);
475+
},
476+
);
477+
478+
it.each(["image_generate", "video_generate"] as const)(
479+
"emits %s provider inventory when verbose tool output is enabled",
480+
async (toolName) => {
481+
const ctx = await handleProviderInventoryListResult({
482+
toolName,
483+
shouldEmitToolOutput: true,
484+
});
485+
486+
expect(ctx.emitToolOutput).toHaveBeenCalledWith(
487+
toolName,
488+
undefined,
489+
providerInventoryText,
490+
expect.any(Object),
491+
);
492+
expect(ctx.state.pendingToolMediaUrls).toEqual([]);
493+
},
494+
);
473495

474496
it("does NOT emit media for error results", async () => {
475497
const onToolResult = vi.fn();

src/agents/pi-embedded-subscribe.handlers.tools.ts

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -361,30 +361,6 @@ async function collectEmittedToolOutputMediaUrls(
361361
return filterToolResultMediaUrls(toolName, mediaUrls, result);
362362
}
363363

364-
const COMPACT_PROVIDER_INVENTORY_TOOLS = new Set(["image_generate", "video_generate"]);
365-
366-
function hasProviderInventoryDetails(result: unknown): boolean {
367-
if (!result || typeof result !== "object") {
368-
return false;
369-
}
370-
const details = readToolResultDetailsRecord(result);
371-
return Array.isArray(details?.providers);
372-
}
373-
374-
function shouldEmitCompactToolOutput(params: {
375-
toolName: string;
376-
result: unknown;
377-
outputText?: string;
378-
}): boolean {
379-
if (!COMPACT_PROVIDER_INVENTORY_TOOLS.has(params.toolName)) {
380-
return false;
381-
}
382-
if (!hasProviderInventoryDetails(params.result)) {
383-
return false;
384-
}
385-
return Boolean(params.outputText?.trim());
386-
}
387-
388364
function readExecApprovalPendingDetails(result: unknown): {
389365
approvalId: string;
390366
approvalSlug: string;
@@ -560,8 +536,7 @@ async function emitToolResultOutput(params: {
560536
isToolError,
561537
hasDeliverableStructuredMedia: hasStructuredMedia && mediaUrls.length > 0,
562538
builtinToolNames: ctx.builtinToolNames,
563-
}) &&
564-
(ctx.shouldEmitToolOutput() || shouldEmitCompactToolOutput({ toolName, result, outputText }));
539+
}) && ctx.shouldEmitToolOutput();
565540
if (shouldEmitOutput) {
566541
if (outputText) {
567542
ctx.emitToolOutput(rawToolName, meta, outputText, result);

0 commit comments

Comments
 (0)