Skip to content

Commit a743b30

Browse files
fix: honor configured store limits (openclaw#66240) (thanks @neeravmakwana)
* fix(media): honor configured store limits * fix(media): report effective source limits * refactor(media): distill configured limit wiring --------- Co-authored-by: Ayaan Zaidi <hi@obviy.us>
1 parent 0381852 commit a743b30

12 files changed

Lines changed: 103 additions & 30 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Docs: https://docs.openclaw.ai
6060
- Slack/doctor: add a dedicated doctor-contract sidecar so config warmup paths such as `openclaw cron` no longer fall back to Slack's broader contract surface, which could trigger Slack-related config-read crashes on affected setups. (#63192) Thanks @shhtheonlyperson.
6161
- Hooks/session-memory: pass the resolved agent workspace into gateway `/new` and `/reset` session-memory hooks so reset snapshots stay scoped to the right agent workspace instead of leaking into the default workspace. (#64735) Thanks @suboss87 and @vincentkoc.
6262
- CLI/approvals: raise the default `openclaw approvals get` gateway timeout and report config-load timeouts explicitly, so slow hosts stop showing a misleading `Config unavailable.` note when the approvals snapshot succeeds but the follow-up config RPC needs more time. (#66239) Thanks @neeravmakwana.
63+
- Media/store: honor configured agent media limits when saving generated media and persisting outbound reply media, so the store no longer hard-stops those flows at 5 MB before the configured limit applies. (#66229) Thanks @neeravmakwana and @vincentkoc.
6364

6465
## 2026.4.12
6566

src/agents/tools/image-generate-tool.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ describe("createImageGenerateTool", () => {
348348
config: {
349349
agents: {
350350
defaults: {
351+
mediaMaxMb: 8,
351352
imageGenerationModel: {
352353
primary: "openai/gpt-image-1",
353354
},
@@ -375,6 +376,7 @@ describe("createImageGenerateTool", () => {
375376
cfg: {
376377
agents: {
377378
defaults: {
379+
mediaMaxMb: 8,
378380
imageGenerationModel: {
379381
primary: "openai/gpt-image-1",
380382
},
@@ -394,15 +396,15 @@ describe("createImageGenerateTool", () => {
394396
Buffer.from("png-1"),
395397
"image/png",
396398
"tool-image-generation",
397-
undefined,
399+
8 * 1024 * 1024,
398400
"cats/output.png",
399401
);
400402
expect(saveMediaBuffer).toHaveBeenNthCalledWith(
401403
2,
402404
Buffer.from("png-2"),
403405
"image/png",
404406
"tool-image-generation",
405-
undefined,
407+
8 * 1024 * 1024,
406408
"cats/output.png",
407409
);
408410
expect(result).toMatchObject({

src/agents/tools/image-generate-tool.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
ImageGenerationResolution,
1313
ImageGenerationSourceImage,
1414
} from "../../image-generation/types.js";
15+
import { resolveConfiguredMediaMaxBytes } from "../../media/configured-max-bytes.js";
1516
import { getImageMetadata } from "../../media/image-ops.js";
1617
import { saveMediaBuffer } from "../../media/store.js";
1718
import { loadWebMedia } from "../../media/web-media.js";
@@ -178,14 +179,6 @@ function normalizeReferenceImages(args: Record<string, unknown>): string[] {
178179
});
179180
}
180181

181-
function pickConfiguredMediaMaxBytes(cfg?: OpenClawConfig): number | undefined {
182-
const configured = cfg?.agents?.defaults?.mediaMaxMb;
183-
if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) {
184-
return Math.floor(configured * 1024 * 1024);
185-
}
186-
return undefined;
187-
}
188-
189182
function resolveSelectedImageGenerationProvider(params: {
190183
config?: OpenClawConfig;
191184
imageGenerationModelConfig: ToolModelConfig;
@@ -467,9 +460,10 @@ export function createImageGenerateTool(options?: {
467460
modelOverride: model,
468461
});
469462
const count = resolveRequestedCount(params);
463+
const configuredMediaMaxBytes = resolveConfiguredMediaMaxBytes(effectiveCfg);
470464
const loadedReferenceImages = await loadReferenceImages({
471465
imageInputs,
472-
maxBytes: pickConfiguredMediaMaxBytes(effectiveCfg),
466+
maxBytes: configuredMediaMaxBytes,
473467
workspaceDir: options?.workspaceDir,
474468
sandboxConfig,
475469
});
@@ -542,7 +536,7 @@ export function createImageGenerateTool(options?: {
542536
image.buffer,
543537
image.mimeType,
544538
"tool-image-generation",
545-
undefined,
539+
configuredMediaMaxBytes,
546540
filename || image.fileName,
547541
),
548542
),

src/agents/tools/music-generate-tool.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ describe("createMusicGenerateTool", () => {
167167
lyrics: ["wake the city up"],
168168
metadata: { taskId: "music-task-1" },
169169
});
170-
vi.spyOn(mediaStore, "saveMediaBuffer").mockResolvedValueOnce({
170+
const saveSpy = vi.spyOn(mediaStore, "saveMediaBuffer").mockResolvedValueOnce({
171171
path: "/tmp/generated-night-drive.mp3",
172172
id: "generated-night-drive.mp3",
173173
size: 11,
@@ -178,6 +178,7 @@ describe("createMusicGenerateTool", () => {
178178
config: asConfig({
179179
agents: {
180180
defaults: {
181+
mediaMaxMb: 8,
181182
musicGenerationModel: { primary: "google/lyria-3-clip-preview" },
182183
},
183184
},
@@ -194,6 +195,13 @@ describe("createMusicGenerateTool", () => {
194195
});
195196
const text = (result.content?.[0] as { text: string } | undefined)?.text ?? "";
196197

198+
expect(saveSpy).toHaveBeenCalledWith(
199+
Buffer.from("music-bytes"),
200+
"audio/mpeg",
201+
"tool-music-generation",
202+
8 * 1024 * 1024,
203+
"night-drive.mp3",
204+
);
197205
expect(text).toContain("Generated 1 track with google/lyria-3-clip-preview.");
198206
expect(text).toContain("Lyrics returned.");
199207
expect(text).toContain("MEDIA:/tmp/generated-night-drive.mp3");

src/agents/tools/music-generate-tool.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { loadConfig } from "../../config/config.js";
33
import type { OpenClawConfig } from "../../config/types.openclaw.js";
44
import { formatErrorMessage } from "../../infra/errors.js";
55
import { createSubsystemLogger } from "../../logging/subsystem.js";
6+
import { resolveConfiguredMediaMaxBytes } from "../../media/configured-max-bytes.js";
67
import { saveMediaBuffer } from "../../media/store.js";
78
import { loadWebMedia } from "../../media/web-media.js";
89
import { resolveMusicGenerationModeCapabilities } from "../../music-generation/capabilities.js";
@@ -359,13 +360,14 @@ async function executeMusicGenerationJob(params: {
359360
progressSummary: "Saving generated music",
360361
});
361362
}
363+
const configuredMediaMaxBytes = resolveConfiguredMediaMaxBytes(params.effectiveCfg);
362364
const savedTracks = await Promise.all(
363365
result.tracks.map((track) =>
364366
saveMediaBuffer(
365367
track.buffer,
366368
track.mimeType,
367369
"tool-music-generation",
368-
undefined,
370+
configuredMediaMaxBytes,
369371
params.filename || track.fileName,
370372
),
371373
),

src/agents/tools/video-generate-tool.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ describe("createVideoGenerateTool", () => {
134134
],
135135
metadata: { taskId: "task-1" },
136136
});
137-
vi.spyOn(mediaStore, "saveMediaBuffer").mockResolvedValueOnce({
137+
const saveSpy = vi.spyOn(mediaStore, "saveMediaBuffer").mockResolvedValueOnce({
138138
path: "/tmp/generated-lobster.mp4",
139139
id: "generated-lobster.mp4",
140140
size: 11,
@@ -145,6 +145,7 @@ describe("createVideoGenerateTool", () => {
145145
config: asConfig({
146146
agents: {
147147
defaults: {
148+
mediaMaxMb: 8,
148149
videoGenerationModel: { primary: "qwen/wan2.6-t2v" },
149150
},
150151
},
@@ -158,6 +159,13 @@ describe("createVideoGenerateTool", () => {
158159
const result = await tool.execute("call-1", { prompt: "friendly lobster surfing" });
159160
const text = (result.content?.[0] as { text: string } | undefined)?.text ?? "";
160161

162+
expect(saveSpy).toHaveBeenCalledWith(
163+
Buffer.from("video-bytes"),
164+
"video/mp4",
165+
"tool-video-generation",
166+
8 * 1024 * 1024,
167+
"lobster.mp4",
168+
);
161169
expect(text).toContain("Generated 1 video with qwen/wan2.6-t2v.");
162170
expect(text).toContain("MEDIA:/tmp/generated-lobster.mp4");
163171
expect(result.details).toMatchObject({

src/agents/tools/video-generate-tool.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { loadConfig } from "../../config/config.js";
33
import type { OpenClawConfig } from "../../config/types.openclaw.js";
44
import { formatErrorMessage } from "../../infra/errors.js";
55
import { createSubsystemLogger } from "../../logging/subsystem.js";
6+
import { resolveConfiguredMediaMaxBytes } from "../../media/configured-max-bytes.js";
67
import { saveMediaBuffer } from "../../media/store.js";
78
import { loadWebMedia } from "../../media/web-media.js";
89
import { readSnakeCaseParamRaw } from "../../param-key.js";
@@ -611,13 +612,14 @@ async function executeVideoGenerationJob(params: {
611612
);
612613
}
613614

615+
const configuredMediaMaxBytes = resolveConfiguredMediaMaxBytes(params.effectiveCfg);
614616
const savedVideos = await Promise.all(
615617
bufferVideos.map((video) =>
616618
saveMediaBuffer(
617619
video.buffer,
618620
video.mimeType,
619621
"tool-video-generation",
620-
undefined,
622+
configuredMediaMaxBytes,
621623
params.filename || video.fileName,
622624
),
623625
),

src/auto-reply/reply/reply-media-paths.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ describe("createReplyMediaPathNormalizer", () => {
165165
path: "/Users/peter/.openclaw/media/outbound/persisted.png",
166166
});
167167
const normalize = createReplyMediaPathNormalizer({
168-
cfg: {},
168+
cfg: { agents: { defaults: { mediaMaxMb: 8 } } },
169169
sessionKey: "session-key",
170170
workspaceDir: "/Users/peter/.openclaw/workspace",
171171
});
@@ -180,6 +180,7 @@ describe("createReplyMediaPathNormalizer", () => {
180180
"/Users/peter/.openclaw/workspace/.openclaw/media/tool-image-generation/generated.png",
181181
undefined,
182182
"outbound",
183+
8 * 1024 * 1024,
183184
);
184185
expect(result).toMatchObject({
185186
mediaUrl: "/Users/peter/.openclaw/media/outbound/persisted.png",
@@ -206,7 +207,7 @@ describe("createReplyMediaPathNormalizer", () => {
206207
mediaUrls: [tmpVoicePath],
207208
});
208209

209-
expect(saveMediaSource).toHaveBeenCalledWith(tmpVoicePath, undefined, "outbound");
210+
expect(saveMediaSource).toHaveBeenCalledWith(tmpVoicePath, undefined, "outbound", undefined);
210211
expect(result).toMatchObject({
211212
mediaUrl: "/Users/peter/.openclaw/media/outbound/tts-voice.opus",
212213
mediaUrls: ["/Users/peter/.openclaw/media/outbound/tts-voice.opus"],

src/auto-reply/reply/reply-media-paths.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { resolveEffectiveToolFsWorkspaceOnly } from "../../agents/tool-fs-policy
88
import type { OpenClawConfig } from "../../config/types.openclaw.js";
99
import { logVerbose } from "../../globals.js";
1010
import { resolvePreferredOpenClawTmpDir } from "../../infra/tmp-openclaw-dir.js";
11+
import { resolveConfiguredMediaMaxBytes } from "../../media/configured-max-bytes.js";
1112
import { saveMediaSource } from "../../media/store.js";
1213
import { resolveConfigDir } from "../../utils.js";
1314
import type { ReplyPayload } from "../types.js";
@@ -103,6 +104,7 @@ export function createReplyMediaPathNormalizer(params: {
103104
cfg: params.cfg,
104105
agentId,
105106
});
107+
const configuredMediaMaxBytes = resolveConfiguredMediaMaxBytes(params.cfg);
106108
let sandboxRootPromise: Promise<string | undefined> | undefined;
107109
const persistedMediaBySource = new Map<string, Promise<string>>();
108110

@@ -133,7 +135,7 @@ export function createReplyMediaPathNormalizer(params: {
133135
if (cached) {
134136
return await cached;
135137
}
136-
const persistPromise = saveMediaSource(media, undefined, "outbound")
138+
const persistPromise = saveMediaSource(media, undefined, "outbound", configuredMediaMaxBytes)
137139
.then((saved) => saved.path)
138140
.catch((err) => {
139141
persistedMediaBySource.delete(media);

src/media/configured-max-bytes.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { OpenClawConfig } from "../config/types.openclaw.js";
2+
3+
const MB = 1024 * 1024;
4+
5+
export function resolveConfiguredMediaMaxBytes(cfg?: OpenClawConfig): number | undefined {
6+
const configured = cfg?.agents?.defaults?.mediaMaxMb;
7+
if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) {
8+
return Math.floor(configured * MB);
9+
}
10+
return undefined;
11+
}

0 commit comments

Comments
 (0)