Matrix: scope reply media to agent roots

This commit is contained in:
Gustavo Madeira Santana
2026-03-12 04:09:30 +00:00
parent 26eea79f96
commit 0f14b359a8
4 changed files with 17 additions and 2 deletions

View File

@@ -3,6 +3,7 @@ import {
createTypingCallbacks, createTypingCallbacks,
ensureConfiguredAcpRouteReady, ensureConfiguredAcpRouteReady,
formatAllowlistMatchMeta, formatAllowlistMatchMeta,
getAgentScopedMediaLocalRoots,
logInboundDrop, logInboundDrop,
logTypingFailure, logTypingFailure,
resolveControlCommandGate, resolveControlCommandGate,
@@ -662,6 +663,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
channel: "matrix", channel: "matrix",
accountId: route.accountId, accountId: route.accountId,
}); });
const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({ const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
cfg, cfg,
agentId: route.agentId, agentId: route.agentId,
@@ -705,6 +707,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
replyToMode, replyToMode,
threadId: threadTarget, threadId: threadTarget,
accountId: route.accountId, accountId: route.accountId,
mediaLocalRoots,
tableMode, tableMode,
}); });
didSendReply = true; didSendReply = true;

View File

@@ -96,18 +96,27 @@ describe("deliverMatrixReplies", () => {
runtime: runtimeEnv, runtime: runtimeEnv,
textLimit: 4000, textLimit: 4000,
replyToMode: "all", replyToMode: "all",
mediaLocalRoots: ["/tmp/openclaw-matrix-test"],
}); });
expect(sendMessageMatrixMock).toHaveBeenCalledTimes(3); expect(sendMessageMatrixMock).toHaveBeenCalledTimes(3);
expect(sendMessageMatrixMock.mock.calls[0]).toEqual([ expect(sendMessageMatrixMock.mock.calls[0]).toEqual([
"room:2", "room:2",
"caption", "caption",
expect.objectContaining({ mediaUrl: "https://example.com/a.jpg", replyToId: "reply-media" }), expect.objectContaining({
mediaUrl: "https://example.com/a.jpg",
mediaLocalRoots: ["/tmp/openclaw-matrix-test"],
replyToId: "reply-media",
}),
]); ]);
expect(sendMessageMatrixMock.mock.calls[1]).toEqual([ expect(sendMessageMatrixMock.mock.calls[1]).toEqual([
"room:2", "room:2",
"", "",
expect.objectContaining({ mediaUrl: "https://example.com/b.jpg", replyToId: "reply-media" }), expect.objectContaining({
mediaUrl: "https://example.com/b.jpg",
mediaLocalRoots: ["/tmp/openclaw-matrix-test"],
replyToId: "reply-media",
}),
]); ]);
expect(sendMessageMatrixMock.mock.calls[2]?.[2]).toEqual( expect(sendMessageMatrixMock.mock.calls[2]?.[2]).toEqual(
expect.objectContaining({ replyToId: "reply-text" }), expect.objectContaining({ replyToId: "reply-text" }),

View File

@@ -43,6 +43,7 @@ export async function deliverMatrixReplies(params: {
replyToMode: "off" | "first" | "all"; replyToMode: "off" | "first" | "all";
threadId?: string; threadId?: string;
accountId?: string; accountId?: string;
mediaLocalRoots?: readonly string[];
tableMode?: MarkdownTableMode; tableMode?: MarkdownTableMode;
}): Promise<void> { }): Promise<void> {
const core = getMatrixRuntime(); const core = getMatrixRuntime();
@@ -122,6 +123,7 @@ export async function deliverMatrixReplies(params: {
client: params.client, client: params.client,
cfg: params.cfg, cfg: params.cfg,
mediaUrl, mediaUrl,
mediaLocalRoots: params.mediaLocalRoots,
replyToId: replyToIdForReply, replyToId: replyToIdForReply,
threadId: params.threadId, threadId: params.threadId,
audioAsVoice: reply.audioAsVoice, audioAsVoice: reply.audioAsVoice,

View File

@@ -122,6 +122,7 @@ export {
resolveMatrixMigrationSnapshotMarkerPath, resolveMatrixMigrationSnapshotMarkerPath,
resolveMatrixMigrationSnapshotOutputDir, resolveMatrixMigrationSnapshotOutputDir,
} from "../infra/matrix-migration-snapshot.js"; } from "../infra/matrix-migration-snapshot.js";
export { getAgentScopedMediaLocalRoots } from "../media/local-roots.js";
export { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js"; export { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
export { isPrivateOrLoopbackHost } from "../gateway/net.js"; export { isPrivateOrLoopbackHost } from "../gateway/net.js";
export { export {