fix: keep replies streaming while switching windows
Switching away from the desktop window and back could mark a reply that was still being generated as "上次运行意外中断", freeze its output partway through, and push the next message into the pending queue even though nothing had actually failed. The conversation area also shifted on each switch because the OpenCode runtime controls were unmounted and re-detected on every refresh. Reading the conversation store no longer treats an in-progress reply as interrupted; only startup recovery marks a run that an application exit truly cut off. Runtime feature detection is now keyed on the resolved runtime scope, so the OpenCode toolbar and manual Compact control stay in place across refreshes. No request timeout or recovery behavior changed. Release note: 修复来回切换桌面窗口时,正在生成的回复被误判为"上次运行意外中断"、输出中途停住、之后发送的消息只能进入待发送队列的问题。使用 OpenCode Runtime 时,会话区也不再因重复检测专属功能而抖动。
This commit is contained in:
@@ -4082,8 +4082,7 @@ describe('AssistantDatabase', () => {
|
||||
}),
|
||||
expect.objectContaining({
|
||||
role: 'assistant',
|
||||
state: 'error',
|
||||
status: expect.stringContaining('意外中断'),
|
||||
state: 'streaming',
|
||||
reasoning: '先分析发布范围',
|
||||
contextCompression: {
|
||||
state: 'completed',
|
||||
@@ -4100,7 +4099,7 @@ describe('AssistantDatabase', () => {
|
||||
type: 'tool',
|
||||
tool: expect.objectContaining({
|
||||
callId: 'call-1',
|
||||
state: 'interrupted'
|
||||
state: 'running'
|
||||
})
|
||||
}),
|
||||
expect.objectContaining({
|
||||
@@ -5437,6 +5436,85 @@ describe('AssistantDatabase', () => {
|
||||
durable.close()
|
||||
})
|
||||
|
||||
it('keeps a live streaming message streaming until startup recovery runs', async () => {
|
||||
const directory = await mkdtemp(
|
||||
join(tmpdir(), 'goodbuddy-live-streaming-read-')
|
||||
)
|
||||
temporaryDirectories.push(directory)
|
||||
const databasePath = join(directory, 'assistant.sqlite')
|
||||
const conversationId =
|
||||
'00000000-0000-4000-8000-000000000971'
|
||||
const assistantMessageId =
|
||||
'00000000-0000-4000-8000-000000000972'
|
||||
const database = new AssistantDatabase(databasePath)
|
||||
database.initialize('C:\\Workspace')
|
||||
database.saveLocalConversations([
|
||||
{
|
||||
header: {
|
||||
id: conversationId,
|
||||
title: '流式进行中',
|
||||
updatedAt: 1_775_000_000_000
|
||||
},
|
||||
messages: [
|
||||
{
|
||||
id: assistantMessageId,
|
||||
role: 'assistant',
|
||||
content: '正在回答',
|
||||
createdAt: 1_775_000_000_001,
|
||||
state: 'streaming',
|
||||
status: '正在生成',
|
||||
tools: [
|
||||
{
|
||||
callId: 'call-1',
|
||||
name: 'read',
|
||||
state: 'running',
|
||||
summary: '正在调用'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
const liveExpectation = {
|
||||
id: assistantMessageId,
|
||||
state: 'streaming',
|
||||
status: '正在生成',
|
||||
tools: [
|
||||
expect.objectContaining({
|
||||
callId: 'call-1',
|
||||
state: 'running'
|
||||
})
|
||||
]
|
||||
}
|
||||
// Reading during a live run must not manufacture an interruption; the
|
||||
// renderer keeps saving the streaming message while the run continues.
|
||||
expect(
|
||||
database.getConversation(conversationId).messages[0]
|
||||
).toMatchObject(liveExpectation)
|
||||
expect(
|
||||
database.listConversations()[0]?.messages[0]
|
||||
).toMatchObject(liveExpectation)
|
||||
database.close()
|
||||
|
||||
const recovered = new AssistantDatabase(databasePath)
|
||||
recovered.initialize('C:\\Workspace')
|
||||
expect(
|
||||
recovered.getConversation(conversationId).messages[0]
|
||||
).toMatchObject({
|
||||
id: assistantMessageId,
|
||||
state: 'error',
|
||||
status: '上次运行意外中断,可以重新发送问题',
|
||||
tools: [
|
||||
expect.objectContaining({
|
||||
callId: 'call-1',
|
||||
state: 'interrupted'
|
||||
})
|
||||
]
|
||||
})
|
||||
recovered.close()
|
||||
})
|
||||
|
||||
it('keeps duplicate chat replies out of artifact listings', async () => {
|
||||
const database = await createDatabase()
|
||||
const project = database.listProjects()[0]!
|
||||
|
||||
Reference in New Issue
Block a user