fix: relocate out-of-allowed outbound attachments so files actually send
Agent-generated files written to an arbitrary working path (e.g. TTS audio under /home/claude/jarvis-tts) were rejected by validateOutboundAttachments as "outside-allowed-dirs". The rejection was only logged; the MEDIA: directive had already been stripped from the text, so the user got a message claiming a file was attached with no file and no error. - Stage attachments outside the room's allowed dirs into a safe per-group dir (data/attachments/outbound/<group>) at the universal delivery choke point, so the path delivery uses and revalidates is one the validator accepts. Files already inside an allowed dir are untouched, preserving isolation checks. - Surface any still-rejected attachment in the visible Discord body via appendRejectionNotice, so delivery can never again silently drop a file. Verified: outbound-attachments 22/22, discord 46/46 (incl. new integration test asserting the notice lands in the sent body), final-delivery 5/5, tsc clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -977,6 +977,28 @@ describe('sendMessage', () => {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('surfaces rejected attachments in the sent message instead of dropping them silently', async () => {
|
||||
const opts = createTestOpts();
|
||||
const channel = new DiscordChannel('test-token', opts);
|
||||
await channel.connect();
|
||||
const mockChannel = {
|
||||
send: vi.fn().mockResolvedValue({ id: 'discord-message-1' }),
|
||||
sendTyping: vi.fn(),
|
||||
};
|
||||
currentClient().channels.fetch.mockResolvedValue(mockChannel);
|
||||
|
||||
await channel.sendMessage('dc:1234567890123456', '샘플을 첨부합니다.', {
|
||||
attachments: [{ path: '/home/claude/missing-sample.wav' }],
|
||||
});
|
||||
|
||||
expect(mockChannel.send).toHaveBeenCalledTimes(1);
|
||||
const sent = mockChannel.send.mock.calls[0][0];
|
||||
expect(sent.files).toBeUndefined();
|
||||
expect(sent.content).toContain('샘플을 첨부합니다.');
|
||||
expect(sent.content).toContain('첨부 1건을 전송하지 못했습니다');
|
||||
expect(sent.content).toContain('missing-sample.wav (파일을 찾을 수 없음)');
|
||||
});
|
||||
|
||||
it('uses legacy image tags as Discord attachment fallback', async () => {
|
||||
const opts = createTestOpts();
|
||||
const channel = new DiscordChannel('test-token', opts);
|
||||
|
||||
Reference in New Issue
Block a user