Browse Source

修复远程Agent媒体与文件访问

LSamien 2 months ago
parent
commit
4b332f7b87

+ 4 - 1
src/client/components/messages/MarkdownContent.vue

@@ -117,7 +117,10 @@ function decorateFileLinks() {
     if (link.dataset.fileCard) return
     let url: URL
     try { url = new URL(link.href, window.location.href) } catch { return }
-    if (url.origin !== window.location.origin || !/^\/Users\/[^/]+\/Agents\/.+/.test(url.pathname)) return
+    const isAgentFile = /^\/Users\/[^/]+\/Agents\/.+/.test(url.pathname)
+    const isRemoteNodeFile = /^\/api\/plugins\/yaoyao\/v1\/nodes\/[0-9a-f-]{36}\/files$/i.test(url.pathname)
+      && Boolean(url.searchParams.get('path'))
+    if (url.origin !== window.location.origin || (!isAgentFile && !isRemoteNodeFile)) return
     link.dataset.fileCard = 'true'
     link.classList.add('file-link-card')
     link.removeAttribute('target')

+ 4 - 3
src/client/components/workspace/viewModels.ts

@@ -16,6 +16,7 @@ import type { SidebarItem } from '@/components/app/types'
 import type { UiAgent, UiRoom } from '@/components/groups/types'
 import type { UiLibraryItem } from '@/components/library/types'
 import type { UiInteraction, UiMessage, UiMessageAttachment, UiToolCall } from '@/components/messages/types'
+import { normalizeAssistantMediaMarkdown } from '@/utils/mediaMarkdown'
 
 function record(value: unknown): Record<string, unknown> {
   return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}
@@ -239,7 +240,7 @@ export function groupMessageToUi(message: GroupMessage, agents: GroupAgent[] = [
     ? agents.find(agent => agent.id === message.senderId)
     : undefined
   const content = sender && sender.nodeId !== 'local'
-    ? rewriteRemoteNodeFiles(message.content, sender.nodeId)
+    ? rewriteRemoteNodeFiles(message.content, sender.nodeId, message.status === 'streaming')
     : message.content
   return {
     id: message.id,
@@ -264,9 +265,9 @@ export function groupMessageToUi(message: GroupMessage, agents: GroupAgent[] = [
   }
 }
 
-export function rewriteRemoteNodeFiles(content: string, nodeId: string): string {
+export function rewriteRemoteNodeFiles(content: string, nodeId: string, streaming = false): string {
   if (!/^[0-9a-f-]{36}$/i.test(nodeId)) return content
-  return content.replace(
+  return normalizeAssistantMediaMarkdown(content, streaming).replace(
     /(!?\[[^\]]*\]\()<?(?:file:\/\/)?(\/(?:Users|private|var|tmp)\/[^)>\n]+)>?(\))/g,
     (_match, prefix: string, path: string, suffix: string) => (
       `${prefix}/api/plugins/yaoyao/v1/nodes/${encodeURIComponent(nodeId)}/files?path=${encodeURIComponent(path)}${suffix}`

+ 20 - 0
tests/client/remoteNodeFiles.test.ts

@@ -16,6 +16,26 @@ describe('remote node file links', () => {
     expect(rewritten).not.toContain('](</Users/remote')
   })
 
+  it('normalizes real Hermes MEDIA directives before routing them', () => {
+    const node = '11111111-1111-4111-8111-111111111111'
+    const content = [
+      'MEDIA:/Users/samien/Agents/smoke_zimage_turbo_00001_.png',
+      'MEDIA:`/Users/samien/Agents/远程 报告.pdf`',
+    ].join('\n')
+
+    const rewritten = rewriteRemoteNodeFiles(content, node)
+
+    expect(rewritten).toContain(`![smoke_zimage_turbo_00001_.png](/api/plugins/yaoyao/v1/nodes/${node}/files?path=%2FUsers%2Fsamien%2FAgents%2Fsmoke_zimage_turbo_00001_.png)`)
+    expect(rewritten).toContain(`[远程 报告.pdf](/api/plugins/yaoyao/v1/nodes/${node}/files?path=%2FUsers%2Fsamien%2FAgents%2F%E8%BF%9C%E7%A8%8B%20%E6%8A%A5%E5%91%8A.pdf)`)
+    expect(rewritten).not.toContain('MEDIA:')
+  })
+
+  it('keeps an incomplete streaming MEDIA directive untouched', () => {
+    const node = '11111111-1111-4111-8111-111111111111'
+    expect(rewriteRemoteNodeFiles('MEDIA:/Users/samien/Agents/partial.png', node, true))
+      .toBe('MEDIA:/Users/samien/Agents/partial.png')
+  })
+
   it('does not rewrite web URLs or local-agent content', () => {
     expect(rewriteRemoteNodeFiles('[官网](https://example.com/a.png)', 'local'))
       .toBe('[官网](https://example.com/a.png)')