fix: keep inbound images readable on upgraded installs (#59971) (thanks @neeravmakwana)

* fix(telegram): allow inbound media from config dir

Made-with: Cursor

* test: simplify local roots regression

* fix: keep inbound images readable on upgraded installs (#59971) (thanks @neeravmakwana)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
This commit is contained in:
Neerav Makwana 2026-04-02 22:54:29 -04:00 committed by GitHub
parent 0fdb55c4e4
commit 7c7098fd1d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 37 additions and 9 deletions

View File

@ -31,6 +31,7 @@ Docs: https://docs.openclaw.ai
- Telegram/native commands: clean up metadata-driven progress placeholders when replies fall back, edits fail, or local exec approval prompts are suppressed. (#59300) Thanks @jalehman.
- Matrix: allow secret-storage recreation during automatic repair bootstrap so clients that lose their recovery key can recover and persist new cross-signing keys. (#59846) Thanks @al3mart.
- Matrix/crypto persistence: capture and write the IndexedDB snapshot while holding the snapshot file lock so concurrent gateway and CLI persists cannot overwrite newer crypto state. (#59851) Thanks @al3mart.
- Telegram/media: keep inbound image attachments readable on upgraded installs where legacy state roots still differ from the managed config-dir media cache. (#59971) Thanks @neeravmakwana.
## 2026.4.2

View File

@ -1,7 +1,9 @@
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
buildMediaLocalRoots,
getAgentScopedMediaLocalRoots,
getAgentScopedMediaLocalRootsForSources,
getDefaultMediaLocalRoots,
@ -177,4 +179,19 @@ describe("local media roots", () => {
expectPicturesRootAbsent(roots, moviesDir);
expect(roots.map(normalizeHostPath)).not.toContain(normalizeHostPath("/"));
});
it("includes the config media root when legacy state and config dirs diverge", () => {
const homeRoot = path.join(os.tmpdir(), "openclaw-legacy-home-test");
const roots = buildMediaLocalRoots(
path.join(homeRoot, ".clawdbot"),
path.join(homeRoot, ".openclaw"),
);
expectNormalizedRootsContain(roots, [
path.join(homeRoot, ".clawdbot", "media"),
path.join(homeRoot, ".clawdbot", "workspace"),
path.join(homeRoot, ".clawdbot", "sandboxes"),
path.join(homeRoot, ".openclaw", "media"),
]);
});
});

View File

@ -3,6 +3,7 @@ import { resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
import type { OpenClawConfig } from "../config/config.js";
import { resolveStateDir } from "../config/paths.js";
import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
import { resolveConfigDir } from "../utils.js";
type BuildMediaLocalRootsOptions = {
preferredTmpDir?: string;
@ -17,29 +18,38 @@ function resolveCachedPreferredTmpDir(): string {
return cachedPreferredTmpDir;
}
function buildMediaLocalRoots(
export function buildMediaLocalRoots(
stateDir: string,
configDir: string,
options: BuildMediaLocalRootsOptions = {},
): string[] {
const resolvedStateDir = path.resolve(stateDir);
const resolvedConfigDir = path.resolve(configDir);
const preferredTmpDir = options.preferredTmpDir ?? resolveCachedPreferredTmpDir();
return [
preferredTmpDir,
path.join(resolvedStateDir, "media"),
path.join(resolvedStateDir, "workspace"),
path.join(resolvedStateDir, "sandboxes"),
];
return Array.from(
new Set([
preferredTmpDir,
path.join(resolvedStateDir, "media"),
path.join(resolvedStateDir, "workspace"),
path.join(resolvedStateDir, "sandboxes"),
// Upgraded installs can still resolve the active state dir to the legacy
// ~/.clawdbot tree while new media writes already go under ~/.openclaw/media.
// Keep inbound media readable across that split without widening roots beyond
// the managed media cache.
path.join(resolvedConfigDir, "media"),
]),
);
}
export function getDefaultMediaLocalRoots(): readonly string[] {
return buildMediaLocalRoots(resolveStateDir());
return buildMediaLocalRoots(resolveStateDir(), resolveConfigDir());
}
export function getAgentScopedMediaLocalRoots(
cfg: OpenClawConfig,
agentId?: string,
): readonly string[] {
const roots = buildMediaLocalRoots(resolveStateDir());
const roots = buildMediaLocalRoots(resolveStateDir(), resolveConfigDir());
if (!agentId?.trim()) {
return roots;
}