fix: retain conversation retrieval settings for scheduled messages

Persist the conversation knowledge retrieval mode across restarts so timed messages use the saved setting. Clarify shared conversation authorization in the scheduled-task documentation and correct the renderer test query type.

Validation: 374 focused tests passed; typecheck and lint passed. The full suite had two failures that passed on focused reruns. Real remote validation remains blocked by the temporary harness SDK module loading error; no model requests were made.

Release note: 修复会话知识检索模式在重启后丢失的问题,定时消息继续使用会话保存的检索设置,并同步任务授权说明。
This commit is contained in:
mesalogo
2026-09-10 10:36:26 +08:00
parent 444902d8ad
commit 020a9ad0dc
4 changed files with 38 additions and 32 deletions
@@ -162,12 +162,10 @@ GoodBuddy Desktop · 项目工作目录
### 6.3 审批
- Execute 继续遵守当前 Runtime、GoodBuddy 原生能力和工具审批控制。
- 已启用且按现有策略允许自动执行的工具可以在后台运行。
- 需要额外确认的动作进入 `waiting_approval`,暂停所属 Job 并发送应用内及桌面通知。
- 用户批准后继续同一个 Job/Run;拒绝后按协议失败、跳过或请求调整。
- 定时触发不能把高风险、越界或未授权动作转换成自动批准。
- 结果未知的外部副作用进入 `outcome_unknown`,不得自动重试。
- 使用普通会话的授权和提问流程。Agent Runtime 的 Execute 工具调用无需通用 GoodBuddy 审批,工具活动仍显示在会话中。
- GoodBuddy 配置修改等原生确认保持各自的确认流程。需要用户回答的问题显示在关联会话中,不因定时触发直接判为失败。
- Ask 在 Runtime 边界保持只读;计划不增加另一层工具限制。
- `outcome_unknown` 等高级执行治理尚未实现,见第 2 节的范围说明。
## 7. 触发器
@@ -148,6 +148,7 @@ describe('AssistantDatabase', () => {
const { messages, ...header } = conversation
database.saveLocalConversations([{ header: {
...header, workMode: 'execute',
knowledgeRetrievalMode: 'always',
runtimeSelection: { provider: 'continue' }
}, messages }])
const existing = database.createSchedule({
@@ -159,7 +160,8 @@ describe('AssistantDatabase', () => {
database.close()
database.initialize('C:\\Workspace')
expect(database.getConversation(conversation.id)).toMatchObject({
workMode: 'execute', runtimeSelection: { provider: 'continue' }
workMode: 'execute', runtimeSelection: { provider: 'continue' },
knowledgeRetrievalMode: 'always'
})
database.close()
})
+30 -24
View File
@@ -1049,6 +1049,7 @@ function interruptActiveToolBlocks(
const conversationContextStateSchema = conversationSnapshotSchema.pick({
workMode: true,
knowledgeLibraryIds: true,
knowledgeRetrievalMode: true,
contextMetrics: true,
contextCompressionState: true
})
@@ -1057,7 +1058,7 @@ function parseConversationContextState(
value: string | null
): Pick<
ConversationSnapshot,
'workMode' | 'knowledgeLibraryIds' | 'contextMetrics' | 'contextCompressionState'
'workMode' | 'knowledgeLibraryIds' | 'knowledgeRetrievalMode' | 'contextMetrics' | 'contextCompressionState'
> {
if (!value) {
return {}
@@ -1075,15 +1076,18 @@ function parseConversationContextState(
function serializeConversationContextState(
conversation: Pick<
ConversationSnapshot,
'workMode' | 'knowledgeLibraryIds' | 'contextMetrics' | 'contextCompressionState'
'workMode' | 'knowledgeLibraryIds' | 'knowledgeRetrievalMode' | 'contextMetrics' | 'contextCompressionState'
>
): string | null {
return conversation.workMode !== undefined || conversation.knowledgeLibraryIds !== undefined ||
return conversation.workMode !== undefined ||
conversation.knowledgeLibraryIds !== undefined ||
conversation.knowledgeRetrievalMode !== undefined ||
conversation.contextMetrics ||
conversation.contextCompressionState
? JSON.stringify({
workMode: conversation.workMode,
knowledgeLibraryIds: conversation.knowledgeLibraryIds,
knowledgeRetrievalMode: conversation.knowledgeRetrievalMode,
contextMetrics: conversation.contextMetrics,
contextCompressionState: conversation.contextCompressionState
})
@@ -5974,29 +5978,31 @@ export class AssistantDatabase {
const database = this.requireDatabase()
database.exec('BEGIN IMMEDIATE')
try {
const result = database
.prepare(
`UPDATE conversation_queue_items
SET status = 'pending'
WHERE id = ? AND status = 'dispatching'`
)
.run(itemId)
if (
result.changes !== 1 &&
!this.requireDatabase()
const result = database
.prepare(
`SELECT 1
FROM conversation_queue_items
WHERE id = ? AND status = 'pending'`
`UPDATE conversation_queue_items
SET status = 'pending'
WHERE id = ? AND status = 'dispatching'`
)
.get(itemId)
) {
throw new Error('待发送消息不存在或状态已变化')
}
database.prepare(`UPDATE schedule_runs SET status = 'pending'
WHERE id = (SELECT schedule_run_id FROM conversation_queue_items WHERE id = ?)
AND status = 'running'`).run(itemId)
database.exec('COMMIT')
.run(itemId)
if (
result.changes !== 1 &&
!database
.prepare(
`SELECT 1
FROM conversation_queue_items
WHERE id = ? AND status = 'pending'`
)
.get(itemId)
) {
throw new Error('待发送消息不存在或状态已变化')
}
database.prepare(
`UPDATE schedule_runs SET status = 'pending'
WHERE id = (SELECT schedule_run_id FROM conversation_queue_items WHERE id = ?)
AND status = 'running'`
).run(itemId)
database.exec('COMMIT')
} catch (error) {
database.exec('ROLLBACK')
throw error
+1 -1
View File
@@ -6403,7 +6403,7 @@ describe("App", () => {
fireEvent.change(screen.getByLabelText("向 GoodBuddy 提问"), {
target: { value: "Verify inherited model" },
});
fireEvent.click(screen.getByRole("button", { name: "发送", exact: true }));
fireEvent.click(screen.getByRole("button", { name: "发送" }));
await waitFor(() => {
const saved = vi.mocked(api.conversations.saveLocal).mock.calls
.flatMap(([conversations]) => conversations)