fix: ACP 模式下文本重复显示 — 流式事件与助手消息双重推送

stream_event 和 assistant 消息对同一文本内容各发一次 agent_message_chunk,
导致 ACP 客户端显示两遍。添加 streamingActive 标志,在收到 stream_event 后
过滤掉 assistant 消息中已被流式路径处理的 text/thinking 块。
This commit is contained in:
claude-code-best
2026-06-05 10:37:59 +08:00
parent 55a932df68
commit a077ec8d85

View File

@@ -633,6 +633,7 @@ export async function forwardSessionUpdates(
let lastAssistantTotalUsage: number | null = null let lastAssistantTotalUsage: number | null = null
let lastAssistantModel: string | null = null let lastAssistantModel: string | null = null
let lastContextWindowSize = 200000 let lastContextWindowSize = 200000
let streamingActive = false
try { try {
while (!abortSignal.aborted) { while (!abortSignal.aborted) {
@@ -788,6 +789,7 @@ export async function forwardSessionUpdates(
for (const notification of notifications) { for (const notification of notifications) {
await conn.sessionUpdate(notification) await conn.sessionUpdate(notification)
} }
streamingActive = true
break break
} }
@@ -828,6 +830,7 @@ export async function forwardSessionUpdates(
clientCapabilities, clientCapabilities,
cwd, cwd,
parentToolUseId, parentToolUseId,
streamingActive,
}, },
) )
for (const notification of notifications) { for (const notification of notifications) {
@@ -943,6 +946,7 @@ function assistantMessageToAcpNotifications(
clientCapabilities?: ClientCapabilities clientCapabilities?: ClientCapabilities
parentToolUseId?: string | null parentToolUseId?: string | null
cwd?: string cwd?: string
streamingActive?: boolean
}, },
): SessionNotification[] { ): SessionNotification[] {
const message = msg.message as Record<string, unknown> | undefined const message = msg.message as Record<string, unknown> | undefined
@@ -967,8 +971,20 @@ function assistantMessageToAcpNotifications(
] ]
} }
// When streaming is active, text/thinking were already sent via stream_event
// messages. Filter them out to avoid duplicate agent_message_chunk /
// agent_thought_chunk notifications. String content (synthetic messages)
// is unaffected — those have no corresponding stream_events.
const contentToProcess = options?.streamingActive
? content.filter(
block => block.type !== 'text' && block.type !== 'thinking',
)
: content
if (contentToProcess.length === 0) return []
return toAcpNotifications( return toAcpNotifications(
content, contentToProcess,
'assistant', 'assistant',
sessionId, sessionId,
toolUseCache, toolUseCache,
@@ -988,6 +1004,7 @@ function streamEventToAcpNotifications(
options?: { options?: {
clientCapabilities?: ClientCapabilities clientCapabilities?: ClientCapabilities
cwd?: string cwd?: string
streamingActive?: boolean
}, },
): SessionNotification[] { ): SessionNotification[] {
const event = (msg as unknown as { event: Record<string, unknown> }).event const event = (msg as unknown as { event: Record<string, unknown> }).event
@@ -1056,6 +1073,7 @@ function toAcpNotifications(
clientCapabilities?: ClientCapabilities clientCapabilities?: ClientCapabilities
parentToolUseId?: string | null parentToolUseId?: string | null
cwd?: string cwd?: string
streamingActive?: boolean
}, },
): SessionNotification[] { ): SessionNotification[] {
const output: SessionNotification[] = [] const output: SessionNotification[] = []