fix: retain submitted questions and answers in conversations

Preserve read-only question and answer reviews after successful submission or skipping, including multiple rounds and local conversation reloads. Keep failed submissions retryable and preserve newer questions and terminal states when responses arrive late.

Validation: typecheck and lint passed; full suite had 3900 passes, 66 skips and one Agent package timeout that passed on isolated rerun. Real OpenCode UI acceptance was not run. Previously discarded answers cannot be recovered.

Release note: 修复 OpenCode 提交回答后会话仅显示已提交的问题;现在保留原问题及对应答案,多轮问答和跳过记录在重新打开会话后仍可查看。
This commit is contained in:
mesalogo
2026-09-10 23:47:44 +08:00
parent 1274c762e8
commit 3bf720a32c
11 changed files with 400 additions and 11 deletions
@@ -273,6 +273,116 @@ describe('AssistantDatabase', () => {
database.close()
})
it('retains answeredQuestions through replacement, incremental saves, and reopening', async () => {
const database = await createDatabase()
const header = {
id: '00000000-0000-4000-8000-000000000921',
title: 'Answered questions',
updatedAt: 1
}
const customAnswer = `Custom answer: ${'x'.repeat(1_001)}\nEnd of answer.`
const answeredQuestions = [
{
questionId: 'question-choices',
skipped: false,
questions: [
{
header: 'Targets',
question: 'Which platforms should be supported?',
options: [
{ label: 'Windows', description: 'Windows desktop' },
{ label: 'Linux', description: 'Linux desktop' }
],
multiple: true,
custom: false,
answer: ['Windows', 'Linux']
},
{
header: 'Details',
question: 'What else should be included?',
options: [],
multiple: false,
custom: true,
answer: [customAnswer]
}
]
},
{
questionId: 'question-skipped',
skipped: true,
questions: [
{
header: 'Timing',
question: 'When should this ship?',
options: [{ label: 'Now', description: 'Ship immediately' }],
multiple: false,
custom: true
}
]
}
]
const message = {
id: '00000000-0000-4000-8000-000000000922',
role: 'assistant' as const,
content: 'Recorded answers',
createdAt: 1,
state: 'complete' as const,
answeredQuestions
}
try {
database.replaceConversations([{ ...header, messages: [message] }])
expect(database.getConversation(header.id).messages).toEqual([message])
database.close()
database.initialize('C:\\Workspace')
expect(database.getConversation(header.id).messages).toEqual([message])
const updatedMessage = {
...message,
answeredQuestions: [
...answeredQuestions,
{
questionId: 'question-follow-up',
skipped: false,
questions: [
{
header: 'Confirm',
question: 'Proceed with the selected platforms?',
options: [{ label: 'Yes', description: 'Proceed' }],
multiple: false,
custom: false,
answer: ['Yes']
}
]
}
]
}
const insertedMessage = {
...message,
id: '00000000-0000-4000-8000-000000000923',
createdAt: 2
}
database.saveLocalConversations([
{
header: { ...header, updatedAt: 2 },
messages: [updatedMessage, insertedMessage]
}
])
expect(database.getConversation(header.id).messages).toEqual([
updatedMessage,
insertedMessage
])
database.close()
database.initialize('C:\\Workspace')
expect(database.getConversation(header.id).messages).toEqual([
updatedMessage,
insertedMessage
])
} finally {
database.close()
}
})
it('persists complete Run history beyond the former Renderer limits', async () => {
const directory = await mkdtemp(
join(tmpdir(), 'goodbuddy-activity-history-')
+9 -4
View File
@@ -275,6 +275,7 @@ type MessageMetadata = {
imageContextNotice?: ConversationSnapshot['messages'][number]['imageContextNotice']
task?: ConversationSnapshot['messages'][number]['task']
attachments?: ConversationSnapshot['messages'][number]['attachments']
answeredQuestions?: ConversationSnapshot['messages'][number]['answeredQuestions']
}
const MAX_CHANNEL_OUTBOX_RETRY_BYTES = 20 * 1024 * 1024
@@ -1163,7 +1164,8 @@ function toConversationSnapshot(
artifactIds: metadata.artifactIds,
imageContextNotice: metadata.imageContextNotice,
task: metadata.task,
attachments: metadata.attachments
attachments: metadata.attachments,
answeredQuestions: metadata.answeredQuestions
}
})
}
@@ -1189,7 +1191,8 @@ function serializeConversationMessageMetadata(
artifactIds: message.artifactIds,
imageContextNotice: message.imageContextNotice,
task: message.task,
attachments: message.attachments
attachments: message.attachments,
answeredQuestions: message.answeredQuestions
})
}
@@ -4633,7 +4636,8 @@ export class AssistantDatabase {
knowledgeRetrieval: metadata.knowledgeRetrieval,
artifactIds: metadata.artifactIds,
task: metadata.task,
attachments: metadata.attachments
attachments: metadata.attachments,
answeredQuestions: metadata.answeredQuestions
})
const failed = reduceRecoveredAgentEvent(current, {
requestId: taskId,
@@ -5041,7 +5045,8 @@ export class AssistantDatabase {
knowledgeRetrieval: metadata.knowledgeRetrieval,
artifactIds: metadata.artifactIds,
task: metadata.task,
attachments: metadata.attachments
attachments: metadata.attachments,
answeredQuestions: metadata.answeredQuestions
})
const reduced = reduceRecoveredAgentEvent(message, publicEvent)
const messageUpdate = database