import { describe, expect, it } from 'vitest'; import { deliveredFilePaths } from '@/pages/CloudAgents/cloud-delivered-files'; import type { CloudHistory, CloudMessage } from '../../shared/cloud-agents'; const threadId = 'thread-1'; function message(content: string, role: CloudMessage['role'] = 'assistant', extra: Partial = {}): CloudMessage { return { id: 1, role, content, request_id: 'request-1', run_id: 'run-1', created_at: '2026-10-12T00:00:00Z', ...extra }; } function history(...messages: CloudMessage[]): CloudHistory { return { thread_id: threadId, messages, run: null, next_offset: null }; } function paths(content: string): string[] { return [...deliveredFilePaths(history(message(content)), threadId)]; } describe('saved assistant file delivery evidence', () => { it.each([ ['sandbox:/mnt/data/report.xlsx', '/mnt/data/report.xlsx'], ['outputs/story.txt', '/outputs/story.txt'], ['/report.pdf', '/report.pdf'], ['/api/chat/thread/thread-1/artifacts/outputs/report.pdf', '/outputs/report.pdf'], ['/api/makelore/threads/thread-1/artifacts/outputs/report.pdf', '/outputs/report.pdf'], ['sandbox:/outputs/%E6%95%85%E4%BA%8B%20%E7%BB%88%E7%A8%BF.txt', '/outputs/故事 终稿.txt'], ['/api/makelore/threads/thread-1/artifacts/%E8%A1%A8%E6%A0%BC%20%E7%BB%88%E7%A8%BF.xlsx', '/表格 终稿.xlsx'], ])('normalizes an explicit local deliverable %s', (target, expected) => { expect(paths(`[保存成果](${target})`)).toEqual([expected]); }); it('accepts actual link and image references, resolves case and whitespace, and ignores unused definitions', () => { expect(paths([ '[故事][My File] 和 ![封面][cover],还有 [快捷成果][]。', '', '[my file]: sandbox:/outputs/story.txt', '[COVER]: /outputs/cover.png', '[快捷成果]: /outputs/quick.pdf', '[unused]: /internal/unmentioned.json', ].join('\n'))).toEqual(['/outputs/story.txt', '/outputs/cover.png', '/outputs/quick.pdf']); }); it('accepts inline images and follows the first reference definition', () => { expect(paths([ '![完成的图片](/outputs/drawing.png) [文件][file]', '', '[file]: /outputs/final.txt', '[file]: /internal/duplicate.txt', ].join('\n'))).toEqual(['/outputs/drawing.png', '/outputs/final.txt']); }); it('keeps explicitly delivered code and data files regardless of their name or extension and deduplicates paths', () => { expect(paths([ '[脚本](/batch.py) [数据](/result.json) [运行文件](/output.pyc)', '[再次保存](sandbox:/batch.py) [重复](batch.py)', ].join('\n'))).toEqual(['/batch.py', '/result.json', '/output.pyc']); }); it('uses only assistant replies and never user text, system text, or reasoning', () => { const saved = history( message('[用户资料](/user-link.pdf)', 'user'), message('[系统文件](/system.json)', 'system'), message('[已交付](/final.pdf)', 'assistant', { reasoning_content: '[工作记录](/thinking.json)' }), ); expect([...deliveredFilePaths(saved, threadId)]).toEqual(['/final.pdf']); }); it('does not resolve definitions from a different message', () => { expect([...deliveredFilePaths(history( message('[file]: /internal/only-defined.txt'), message('[没有本条定义的文件][file]', 'assistant', { id: 2 }), ), threadId)]).toEqual([]); }); it('ignores raw paths, fenced and indented code, inline code, raw HTML and code examples', () => { expect(paths([ '过程位置 /internal/log.json,outputs/script.py,sandbox:/outputs/raw.pdf。', '', '```markdown', '[示例](/internal/fenced.txt)', '[hidden]: /internal/fenced-reference.txt', '```', '', ' [示例](/internal/indented.txt)', '', '`[示例](/internal/inline.txt)`', '', 'HTML 示例', '', '', '[不存在的引用][hidden]', '', '[真正的成果](/outputs/final.docx)', ].join('\n'))).toEqual(['/outputs/final.docx']); }); it.each([ 'https://example.com/report.pdf', '//example.com/report.pdf', 'mailto:person@example.com', 'javascript:alert(1)', 'data:text/plain,hello', 'file:///private/file.txt', 'C:/private/file.txt', '#section', '/report.pdf?download=true', '/report.pdf#section', '/report.pdf%3Fdownload=true', '/report.pdf%23section', '/../private.txt', 'outputs/../private.txt', '/outputs/%2e%2e/private.txt', '/outputs/%252e%252e/private.txt', '/outputs%2f..%2fprivate.txt', '/outputs/./report.txt', '/outputs\\private.txt', '/outputs/%5Cprivate.txt', '/outputs/%255Cprivate.txt', '/outputs/%00private.txt', '/outputs/\u0000private.txt', '/outputs/%0Aprivate.txt', '/outputs/%E0%A4%A', 'sandbox://example.com/report.txt', '/outputs/', '/api/chat/thread/other-thread/artifacts/final.pdf', '/api/makelore/threads/other-thread/artifacts/final.pdf', '/api/chat/thread/thread-1/artifacts/', '/api/makelore/threads/thread-1/attachments/final.pdf', ])('rejects an unsafe, ambiguous, external, or cross-thread target %s', target => { expect(paths(`[文件](<${target}>)`)).toEqual([]); }); it('returns no evidence until matching conversation history exists', () => { expect(deliveredFilePaths(null, threadId).size).toBe(0); expect(deliveredFilePaths(undefined, threadId).size).toBe(0); expect(deliveredFilePaths(history(message('[文件](/final.pdf)')), 'other-thread').size).toBe(0); expect(deliveredFilePaths(history(message('[文件](/final.pdf)')), '').size).toBe(0); }); });