Conversation
37ef754 to
cfe2a34
Compare
Greptile SummaryThis PR correctly fixes a behavioral inconsistency in WhatsApp media handling by routing both outbound direct sends and auto-reply sends through Key changes:
Test coverage concern: Confidence Score: 4/5
Last reviewed commit: cfe2a34 |
| it("prefers per-account WhatsApp media caps for outbound auto-replies", async () => { | ||
| const bigPng = await sharp({ | ||
| create: { | ||
| width: 256, | ||
| height: 256, | ||
| channels: 3, | ||
| background: { r: 255, g: 0, b: 0 }, | ||
| }, | ||
| }) | ||
| .png({ compressionLevel: 0 }) | ||
| .toBuffer(); | ||
| expect(bigPng.length).toBeGreaterThan(SMALL_MEDIA_CAP_BYTES); | ||
|
|
||
| setLoadConfigMock(() => ({ | ||
| channels: { | ||
| whatsapp: { | ||
| allowFrom: ["*"], | ||
| mediaMaxMb: 1, | ||
| accounts: { | ||
| work: { | ||
| mediaMaxMb: SMALL_MEDIA_CAP_MB, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| })); | ||
|
|
||
| try { | ||
| const sendMedia = vi.fn(); | ||
| const { reply, dispatch } = await setupSingleInboundMessage({ | ||
| resolverValue: { text: "hi", mediaUrl: "https://example.com/account-big.png" }, | ||
| sendMedia, | ||
| }); | ||
| const fetchMock = mockFetchMediaBuffer(bigPng, "image/png"); | ||
|
|
||
| await dispatch("msg-account-cap", { accountId: "work" }); | ||
|
|
||
| const payload = getSingleImagePayload(sendMedia); | ||
| expect(payload.image.length).toBeLessThanOrEqual(SMALL_MEDIA_CAP_BYTES); | ||
| expect(payload.mimetype).toBe("image/jpeg"); | ||
| expect(reply).not.toHaveBeenCalled(); | ||
| fetchMock.mockRestore(); | ||
| } finally { | ||
| resetLoadConfigMock(); | ||
| } | ||
| }); |
There was a problem hiding this comment.
This test may not actually verify per-account cap behavior. The monitor is started via setupSingleInboundMessage without a tuning.accountId, so monitorWebChannel resolves the default account using the root-level mediaMaxMb: 1. That means maxMediaBytes = 1 MB throughout the monitor's lifetime.
When dispatch("msg-account-cap", { accountId: "work" }) is called, msg.accountId = "work" is used only for routing — maxMediaBytes in processMessage and deliverWebReply remains 1 MB (the default account's cap), not the work account's 0.1 MB cap.
The assertion payload.image.length <= SMALL_MEDIA_CAP_BYTES most likely passes coincidentally: a 256×256 solid-red image produces a JPEG of only a few KB regardless of the cap, so the test would pass even if the per-account override was never applied.
To correctly test per-account cap behavior, the monitor should be started for the "work" account so it actually resolves and applies the 0.1 MB override:
// Start the monitor with tuning.accountId = "work" so account.mediaMaxMb = 0.1 is used
await monitorWebChannel(false, listenerFactory, false, resolver, undefined, undefined, {
accountId: "work",
});
// Then dispatch a plain message (no accountId override needed — monitor IS the work account)
await dispatch("msg-account-cap");Alternatively, use a non-trivially-compressible image (e.g. random-noise pixels) together with two cap levels that produce measurably different output sizes, so the assertion fails when the wrong cap is applied.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/web/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.test.ts
Line: 246-291
Comment:
This test may not actually verify per-account cap behavior. The monitor is started via `setupSingleInboundMessage` without a `tuning.accountId`, so `monitorWebChannel` resolves the *default* account using the root-level `mediaMaxMb: 1`. That means `maxMediaBytes = 1 MB` throughout the monitor's lifetime.
When `dispatch("msg-account-cap", { accountId: "work" })` is called, `msg.accountId = "work"` is used only for routing — `maxMediaBytes` in `processMessage` and `deliverWebReply` remains `1 MB` (the default account's cap), not the work account's `0.1 MB` cap.
The assertion `payload.image.length <= SMALL_MEDIA_CAP_BYTES` most likely passes coincidentally: a 256×256 solid-red image produces a JPEG of only a few KB regardless of the cap, so the test would pass even if the per-account override was never applied.
To correctly test per-account cap behavior, the monitor should be started for the "work" account so it actually resolves and applies the `0.1 MB` override:
```ts
// Start the monitor with tuning.accountId = "work" so account.mediaMaxMb = 0.1 is used
await monitorWebChannel(false, listenerFactory, false, resolver, undefined, undefined, {
accountId: "work",
});
// Then dispatch a plain message (no accountId override needed — monitor IS the work account)
await dispatch("msg-account-cap");
```
Alternatively, use a non-trivially-compressible image (e.g. random-noise pixels) together with two cap levels that produce measurably different output sizes, so the assertion fails when the wrong cap is applied.
How can I resolve this? If you propose a fix, please make it concise.* main: Mattermost: harden interaction callback binding (openclaw#38057) WhatsApp: honor outbound mediaMaxMb (openclaw#38097) openai-image-gen: validate --background and --style options (openclaw#36762) Docs: align BlueBubbles media cap wording Telegram/Discord: honor outbound mediaMaxMb uploads (openclaw#38065) CI: run changed-scope on main pushes Skills/nano-banana-pro: clarify MEDIA token comment (openclaw#38063) nano-banana-pro: respect explicit --resolution when editing images (openclaw#36880) CI: drop unused install-smoke bootstrap fix(nano-banana-pro): remove space after MEDIA: token in generate_image.py (openclaw#18706) docs: context engine docs(config): list the context engine plugin slot docs(plugins): add context-engine manifest kind example docs(plugins): document context engine slots and registration docs(protocol): document slash-delimited schema lookup plugin ids docs(tools): document slash-delimited config schema lookup paths fix(session): tighten direct-session webchat routing matching (openclaw#37867) feature(context): extend plugin system to support custom context management (openclaw#22201) Gateway: allow slash-delimited schema lookup paths
* WhatsApp: add media cap helper * WhatsApp: cap outbound media loads * WhatsApp: align auto-reply media caps * WhatsApp: add outbound media cap test * WhatsApp: update auto-reply cap tests * Docs: update WhatsApp media caps * Changelog: note WhatsApp media cap fix
* WhatsApp: add media cap helper * WhatsApp: cap outbound media loads * WhatsApp: align auto-reply media caps * WhatsApp: add outbound media cap test * WhatsApp: update auto-reply cap tests * Docs: update WhatsApp media caps * Changelog: note WhatsApp media cap fix
* WhatsApp: add media cap helper * WhatsApp: cap outbound media loads * WhatsApp: align auto-reply media caps * WhatsApp: add outbound media cap test * WhatsApp: update auto-reply cap tests * Docs: update WhatsApp media caps * Changelog: note WhatsApp media cap fix
* WhatsApp: add media cap helper * WhatsApp: cap outbound media loads * WhatsApp: align auto-reply media caps * WhatsApp: add outbound media cap test * WhatsApp: update auto-reply cap tests * Docs: update WhatsApp media caps * Changelog: note WhatsApp media cap fix
* WhatsApp: add media cap helper * WhatsApp: cap outbound media loads * WhatsApp: align auto-reply media caps * WhatsApp: add outbound media cap test * WhatsApp: update auto-reply cap tests * Docs: update WhatsApp media caps * Changelog: note WhatsApp media cap fix
* WhatsApp: add media cap helper * WhatsApp: cap outbound media loads * WhatsApp: align auto-reply media caps * WhatsApp: add outbound media cap test * WhatsApp: update auto-reply cap tests * Docs: update WhatsApp media caps * Changelog: note WhatsApp media cap fix
* WhatsApp: add media cap helper * WhatsApp: cap outbound media loads * WhatsApp: align auto-reply media caps * WhatsApp: add outbound media cap test * WhatsApp: update auto-reply cap tests * Docs: update WhatsApp media caps * Changelog: note WhatsApp media cap fix (cherry picked from commit 222d635)
* WhatsApp: add media cap helper * WhatsApp: cap outbound media loads * WhatsApp: align auto-reply media caps * WhatsApp: add outbound media cap test * WhatsApp: update auto-reply cap tests * Docs: update WhatsApp media caps * Changelog: note WhatsApp media cap fix (cherry picked from commit 222d635)
* fix(subagents): recover announce cleanup after kill/complete race (cherry picked from commit 2f86ae7) * feat(hooks): emit compaction lifecycle hooks (openclaw#16788) (cherry picked from commit 71ec421) * fix(agent): harden undici stream timeouts for long openai-completions runs (cherry picked from commit 05fb16d) * fix(agents): allow configured ollama endpoints without dummy api keys (cherry picked from commit 36afd1b) * feat(openai): add gpt-5.4 support for API and Codex OAuth (openclaw#36590) Co-authored-by: Tyler Yust <[email protected]> (cherry picked from commit 5d4b040) * Fix failover for zhipuai 1310 Weekly/Monthly Limit Exhausted (openclaw#33813) Co-authored-by: zhouhe-xydt <[email protected]> Co-authored-by: altaywtf <[email protected]> (cherry picked from commit a65d70f) * fix(failover): classify HTTP 402 as rate_limit when payload indicates usage limit (openclaw#30484) (openclaw#36802) Co-authored-by: Val Alexander <[email protected]> (cherry picked from commit 01b2017) * feat(onboarding): add web search to onboarding flow (openclaw#34009) * add web search to onboarding flow * remove post onboarding step (now redundant) * post-onboarding nudge if no web search set up * address comments * fix test mocking * add enabled: false assertion to the no-key test * --skip-search cli flag * use provider that a user has a key for * add assertions, replace the duplicated switch blocks * test for quickstart fast-path with existing config key * address comments * cover quickstart falls through to key test * bring back key source * normalize secret inputs instead of direct string trimming * preserve enabled: false if it's already set * handle missing API keys in flow * doc updates * hasExistingKey to detect both plaintext strings and SecretRef objects * preserve enabled state only on the "keep current" paths * add test for preserving * better gate flows * guard against invalid provider values in config * Update src/commands/configure.wizard.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * format fix * only mentions env var when it's actually available * search apiKey fields now typed as SecretInput * if no provider check if any search provider key is detectable * handle both kimi keys * remove .filter(Boolean) * do not disable web_search after user enables it * update resolveSearchProvider * fix(onboarding): skip search key prompt in ref mode * fix: add onboarding web search step (openclaw#34009) (thanks @kesku) --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Shadow <[email protected]> (cherry picked from commit 3d7bc59) * fix(ci): restore protocol and schema checks (openclaw#37470) (cherry picked from commit ee6f7b1) * Infra: require explicit opt-in for prerelease npm installs (openclaw#38117) * Infra: tighten npm registry spec parsing * Infra: block implicit prerelease npm installs * Plugins: cover prerelease install policy * Infra: add npm registry spec tests * Hooks: cover prerelease install policy * Docs: clarify plugin guide version policy * Docs: clarify plugin install version policy * Docs: clarify hooks install version policy * Docs: clarify hook pack version policy (cherry picked from commit f392b81) * ci: trigger workflow * fix: resolve cherry-pick adaptation failures from upstream web search commit - Add missing hasConfiguredSecretInput() to types.secrets.ts - Add SecretInput import to types.tools.ts for apiKey fields - Replace OpenClawConfig with RemoteClawConfig in onboard-search - Remove non-existent DEFAULT_SECRET_PROVIDER_ALIAS import - Remove invalid 'provider' field from SecretRef literal - Stub resolveSyntheticLocalProviderAuth (models.providers was gutted) - Replace undefined registerRun/emitLifecycleEnd test helpers with existing mod.registerSubagentRun/lifecycleHandler patterns Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * Plugins: clarify registerHttpHandler migration errors (openclaw#36794) * Changelog: note plugin HTTP route migration diagnostics * Tests: cover registerHttpHandler migration diagnostics * Plugins: clarify registerHttpHandler migration errors * Tests: cover registerHttpHandler diagnostic edge cases * Plugins: tighten registerHttpHandler migration hint (cherry picked from commit d4021f4) * Plugins: avoid false integrity drift prompts on unpinned updates (openclaw#37179) * Plugins: skip drift prompts for unpinned updates * Plugins: cover unpinned integrity update behavior (cherry picked from commit 428d176) * docs(protocol): document slash-delimited schema lookup plugin ids (cherry picked from commit f788ba1) * docs(plugins): document context engine slots and registration (cherry picked from commit eb2eeba) * docs(plugins): add context-engine manifest kind example (cherry picked from commit 7cc3376) * docs(config): list the context engine plugin slot (cherry picked from commit 5470337) * fix: Windows: openclaw plugins install fails with spawn EINVAL Fixes openclaw#7631 (cherry picked from commit d000316) * fix(whatsapp): remove implicit [openclaw] self-chat prefix (cherry picked from commit 4d9134f) * WhatsApp: honor outbound mediaMaxMb (openclaw#38097) * WhatsApp: add media cap helper * WhatsApp: cap outbound media loads * WhatsApp: align auto-reply media caps * WhatsApp: add outbound media cap test * WhatsApp: update auto-reply cap tests * Docs: update WhatsApp media caps * Changelog: note WhatsApp media cap fix (cherry picked from commit 222d635) * fix(mattermost): allow reachable interaction callback URLs (openclaw#37543) Merged via squash. Prepared head SHA: 4d59373 Co-authored-by: mukhtharcm <[email protected]> Co-authored-by: mukhtharcm <[email protected]> Reviewed-by: @mukhtharcm (cherry picked from commit 4a80d48) * Mattermost: harden interaction callback binding (openclaw#38057) (cherry picked from commit a274ef9) * chore: code/dead tests cleanup (openclaw#38286) * Discord: assert bot-self filter queue guard * Tests: remove dead gateway SIGTERM placeholder (cherry picked from commit 38f46e8) * Delete changelog/fragments directory (cherry picked from commit 8f69e07) * fix(feishu): restore explicit media request timeouts (cherry picked from commit b127333) * fix: adapt upstream naming to fork conventions Replace OpenClawConfig with RemoteClawConfig, openclaw/plugin-sdk with remoteclaw/plugin-sdk, and fix import paths for fork type exports. * fix: resolve type errors from upstream cherry-picks - Add missing RemoteClawConfig import in discord test - Use spread with as-any for feishu SDK timeout property - Remove allowUnresolvedSecretRef from mattermost test * ci: re-trigger CI after force push * fix: revert unintended onboarding changes from linter bleed * ci: sync PR head with branch --------- Co-authored-by: Vignesh Natarajan <[email protected]> Co-authored-by: Vincent Koc <[email protected]> Co-authored-by: dorukardahan <[email protected]> Co-authored-by: Tyler Yust <[email protected]> Co-authored-by: zhouhe-xydt <[email protected]> Co-authored-by: zhouhe-xydt <[email protected]> Co-authored-by: altaywtf <[email protected]> Co-authored-by: Xinhua Gu <[email protected]> Co-authored-by: Val Alexander <[email protected]> Co-authored-by: Kesku <[email protected]> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Shadow <[email protected]> Co-authored-by: Altay <[email protected]> Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> Co-authored-by: 0xlin2023 <[email protected]> Co-authored-by: Muhammed Mukhthar CM <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]>
Summary
channels.whatsapp.mediaMaxMb, but outbound sends skipped that cap and auto-replies still usedagents.defaults.mediaMaxMb.channels.whatsapp.mediaMaxMb, including per-account overrides; docs/tests/changelog were updated to match.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
channels.whatsapp.mediaMaxMbinstead of bypassing channel-level media caps.channels.whatsapp.accounts.<accountId>.mediaMaxMboverrides.Security Impact (required)
Yes/No) NoYes/No) NoYes/No) NoYes/No) NoYes/No) NoYes, explain risk + mitigation:Repro + Verification
Environment
channels.whatsapp.mediaMaxMb,channels.whatsapp.accounts.<accountId>.mediaMaxMbSteps
channels.whatsapp.mediaMaxMb(and optionally an account override).Expected
Actual
agents.defaults.mediaMaxMbinstead.Evidence
Attach at least one:
Human Verification (required)
What you personally verified (not just CI), and how:
Compatibility / Migration
Yes/No) YesYes/No) NoYes/No) NoFailure Recovery (if this breaks)
channels.whatsapp.mediaMaxMbto a known-safe value.src/web/outbound.ts,src/web/auto-reply/monitor.tsRisks and Mitigations
agents.defaults.mediaMaxMbfor WhatsApp auto-replies may see the WhatsApp channel cap take precedence instead.channels.whatsapp.mediaMaxMb, and the effective default remains 50MB to match existing WhatsApp channel docs.