fix: remove conversation list and message history limits
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
List and search all active conversations instead of only the latest 100. Preserve message history across consecutive sends instead of truncating it to 500 messages. Existing detail loading and context compression remain unchanged. Validation: 7 focused tests, typecheck and lint passed. Full test run timed out after reporting failures in App completion visibility and WorkspacePrimitives forced-color contracts. Release note: 会话列表和搜索不再限制为最近 100 个会话;发送消息不再截断超过 500 条的历史记录。
This commit is contained in:
@@ -390,8 +390,8 @@ describe('AssistantDatabase', () => {
|
||||
expect(database.searchConversations(' ')).toEqual([])
|
||||
const added = { id: randomUUID(), title: 'Newer', updatedAt: 1000 }
|
||||
database.saveLocalConversations([{ header: added, messages: [] }])
|
||||
expect(database.listConversationSummaries().some(item => item.id === id)).toBe(false)
|
||||
expect(database.searchConversations('äpfel 中文 0 %_', [id])).toEqual([id])
|
||||
expect(database.listConversationSummaries().some(item => item.id === id)).toBe(true)
|
||||
expect(database.searchConversations('äpfel 中文 0 %_')).toEqual([id])
|
||||
expect(database.listConversationSummaries([id]).find(item => item.id === id)?.messages).toHaveLength(16)
|
||||
// A header-only save of a summary cannot replace or erase messages.
|
||||
database.saveLocalConversations([{ header: { id, title: 'Renamed', updatedAt: 101 }, messages: [] }])
|
||||
@@ -5832,7 +5832,7 @@ describe('AssistantDatabase', () => {
|
||||
database.close()
|
||||
})
|
||||
|
||||
it('gets a targeted conversation outside the latest 100', async () => {
|
||||
it('lists and searches all conversations beyond the latest 100', async () => {
|
||||
const database = await createDatabase()
|
||||
database.saveLocalConversations(
|
||||
Array.from({ length: 100 }, (_, index) => ({
|
||||
@@ -5856,12 +5856,14 @@ describe('AssistantDatabase', () => {
|
||||
}
|
||||
])
|
||||
|
||||
expect(database.listConversations()).toHaveLength(100)
|
||||
expect(database.listConversations()).toHaveLength(101)
|
||||
expect(
|
||||
database.listConversations().some(
|
||||
(conversation) => conversation.id === oldestId
|
||||
)
|
||||
).toBe(false)
|
||||
).toBe(true)
|
||||
expect(database.listConversationSummaries()).toHaveLength(101)
|
||||
expect(database.searchConversations('第 101 个对话')).toEqual([oldestId])
|
||||
expect(database.getConversation(oldestId)).toMatchObject({
|
||||
id: oldestId,
|
||||
title: '第 101 个对话',
|
||||
|
||||
@@ -2791,7 +2791,7 @@ export class AssistantDatabase {
|
||||
return this.readConversationList(new Set(detailIds))
|
||||
}
|
||||
|
||||
private listConversationRows(extraIds: ReadonlySet<string> = new Set()): ConversationRow[] {
|
||||
private listConversationRows(): ConversationRow[] {
|
||||
const database = this.requireDatabase()
|
||||
return database
|
||||
.prepare(
|
||||
@@ -2803,24 +2803,14 @@ export class AssistantDatabase {
|
||||
pinned, updated_at
|
||||
FROM conversations
|
||||
WHERE status = 'active'
|
||||
AND (
|
||||
id IN (SELECT id FROM conversations WHERE status = 'active'
|
||||
ORDER BY updated_at DESC LIMIT 100)
|
||||
OR id IN (SELECT conversation_id FROM (${activeVisibleTaskSelect}))
|
||||
OR id IN (SELECT value FROM json_each(?))
|
||||
OR pinned = 1
|
||||
OR EXISTS (SELECT 1 FROM messages
|
||||
WHERE conversation_id = conversations.id
|
||||
AND state = 'streaming')
|
||||
)
|
||||
ORDER BY pinned DESC, updated_at DESC`
|
||||
)
|
||||
.all(JSON.stringify([...extraIds])) as ConversationRow[]
|
||||
.all() as ConversationRow[]
|
||||
}
|
||||
|
||||
private readConversationList(detailIds?: ReadonlySet<string>): import('../../shared/assistant-contracts').ConversationListSnapshot[] {
|
||||
const database = this.requireDatabase()
|
||||
const conversations = this.listConversationRows(detailIds)
|
||||
const conversations = this.listConversationRows()
|
||||
const messageStatement = database.prepare(
|
||||
`SELECT id, conversation_id, role, content, state, metadata_json,
|
||||
created_at
|
||||
@@ -2859,14 +2849,14 @@ export class AssistantDatabase {
|
||||
})
|
||||
}
|
||||
|
||||
searchConversations(query: string, conversationIds: string[] = []): string[] {
|
||||
searchConversations(query: string): string[] {
|
||||
const normalized = query.trim().toLocaleLowerCase()
|
||||
if (!normalized) return []
|
||||
const database = this.requireDatabase()
|
||||
// Read only searchable text, never tool/subagent/image metadata. Use the
|
||||
// same Unicode case folding as the renderer's existing search.
|
||||
const messages = database.prepare('SELECT content FROM messages WHERE conversation_id = ?')
|
||||
return this.listConversationRows(new Set(conversationIds)).filter(conversation =>
|
||||
return this.listConversationRows().filter(conversation =>
|
||||
conversation.title.toLocaleLowerCase().includes(normalized) ||
|
||||
(messages.all(conversation.id) as { content: string }[]).some(message =>
|
||||
message.content.toLocaleLowerCase().includes(normalized))
|
||||
|
||||
+2
-2
@@ -7040,8 +7040,8 @@ export function registerIpcHandlers(
|
||||
})
|
||||
registerHandler(ipcChannels.conversationsSearch, (event, input: unknown) => {
|
||||
assertTrustedSender(event, window)
|
||||
const { query, conversationIds } = conversationSearchRequestSchema.parse(input)
|
||||
return assistantDatabase.searchConversations(query, conversationIds)
|
||||
const { query } = conversationSearchRequestSchema.parse(input)
|
||||
return assistantDatabase.searchConversations(query)
|
||||
})
|
||||
|
||||
registerHandler(
|
||||
|
||||
@@ -5628,7 +5628,7 @@ describe("App", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("sends all persisted history beyond 500 messages", async () => {
|
||||
it("retains all history beyond 500 messages across consecutive sends", async () => {
|
||||
const messages = Array.from({ length: 502 }, (_, index) => ({
|
||||
id: crypto.randomUUID(),
|
||||
role: index % 2 ? "assistant" as const : "user" as const,
|
||||
@@ -5653,6 +5653,22 @@ describe("App", () => {
|
||||
expect(run.mock.calls[0]?.[0].historyMessageIds).toEqual(
|
||||
messages.map(({ id }) => id),
|
||||
);
|
||||
const requestId = run.mock.calls[0]![0].requestId;
|
||||
act(() => {
|
||||
agentListener?.({ requestId, type: "text", delta: "Continued reply" });
|
||||
agentListener?.({ requestId, type: "done" });
|
||||
});
|
||||
fireEvent.change(screen.getByLabelText("向 GoodBuddy 提问"), {
|
||||
target: { value: "Continue again" },
|
||||
});
|
||||
await waitFor(() => expect(screen.getByLabelText("发送")).toBeEnabled());
|
||||
fireEvent.click(screen.getByLabelText("发送"));
|
||||
await waitFor(() => expect(run).toHaveBeenCalledTimes(2));
|
||||
expect(run.mock.calls[1]?.[0].history).toEqual([
|
||||
...messages.map(({ role, content }) => ({ role, content })),
|
||||
{ role: "user", content: "Continue" },
|
||||
{ role: "assistant", content: "Continued reply" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps a tool failure in details and hides retry after continuing", async () => {
|
||||
|
||||
@@ -7684,7 +7684,7 @@ function App(): React.JSX.Element {
|
||||
? prompt.slice(0, 24)
|
||||
: conversation.title,
|
||||
updatedAt: Date.now(),
|
||||
messages: [...conversation.messages.slice(-499), userMessage],
|
||||
messages: [...conversation.messages, userMessage],
|
||||
}
|
||||
: conversation,
|
||||
),
|
||||
@@ -7754,7 +7754,7 @@ function App(): React.JSX.Element {
|
||||
...conversation,
|
||||
updatedAt: Date.now(),
|
||||
messages: [
|
||||
...conversation.messages.slice(-499),
|
||||
...conversation.messages,
|
||||
assistantMessage,
|
||||
],
|
||||
activeRequest: undefined,
|
||||
|
||||
Reference in New Issue
Block a user