fix: preserve tool details and prevent repeated history growth
Show bounded operation summaries for Continue, DeepSeek Harness and direct-model tools, retaining input details across incremental updates. Use one canonical workspace path for DeepSeek Host startup and ACP sessions so equivalent Windows paths reuse a working Runtime. Compare persisted subagent tool fields by value, release terminal task caches, and avoid rebuilding caches for duplicate remote events. Upgrade to schema 35 to recompact repeated schema-34 tool blocks while preserving events and provenance. Include regression coverage, validation records and temporary-resource test exclusions. Validation: 4155 tests passed, 66 skipped; typecheck and lint passed. Full desktop real-model validation covered all three tool-summary paths and both Windows path spellings. Storage validation evidence includes local and remote tool workloads and real database upgrade checks. Release note: Tool records now show paths, commands and search details across runtimes. Fix DeepSeek Windows workspace failures and repeated subagent history growth; schema 35 reclaims redundant history data while preserving execution records, and older clients cannot reopen the upgraded database.
This commit is contained in:
@@ -4,9 +4,28 @@ import type { DatabaseSync } from 'node:sqlite'
|
||||
import {
|
||||
assistantIdSchema,
|
||||
conversationMessageBlockSchema,
|
||||
type ConversationMessageBlock
|
||||
conversationToolActivitySchema,
|
||||
type ConversationMessageBlock,
|
||||
type ConversationToolActivity
|
||||
} from '../../shared/assistant-contracts'
|
||||
|
||||
export const SUBAGENT_PROGRESS_STORAGE_SCHEMA_VERSION = 35
|
||||
|
||||
const toolFields = Object.keys(conversationToolActivitySchema.shape) as
|
||||
Array<keyof ConversationToolActivity>
|
||||
|
||||
function sameProgressBlock(
|
||||
previous: ConversationMessageBlock,
|
||||
next: ConversationMessageBlock
|
||||
): boolean {
|
||||
if (previous.type === 'tool' && next.type === 'tool') {
|
||||
// JSON omits undefined optional fields. Compare their values, not key presence.
|
||||
return previous.id === next.id &&
|
||||
toolFields.every((field) => previous.tool[field] === next.tool[field])
|
||||
}
|
||||
return isDeepStrictEqual(previous, next)
|
||||
}
|
||||
|
||||
export const subagentProgressUpdateSchema = z.discriminatedUnion('type', [
|
||||
z.object({
|
||||
type: z.literal('append'),
|
||||
@@ -89,7 +108,7 @@ export function diffSubagentProgress(
|
||||
type: 'append', id: block.id, blockType: block.type, delta
|
||||
})
|
||||
}
|
||||
} else if (!prior || !isDeepStrictEqual(prior, block)) {
|
||||
} else if (!prior || !sameProgressBlock(prior, block)) {
|
||||
updates.push({ type: 'upsert', block })
|
||||
}
|
||||
}
|
||||
@@ -114,12 +133,15 @@ export function compactSubagentPayload(
|
||||
progress: ReadonlyMap<string, ConversationMessageBlock[]>
|
||||
): unknown {
|
||||
const event = asSubagent(payload)
|
||||
if (!event || !Array.isArray(event.progress)) return payload
|
||||
if (!event || (
|
||||
!Array.isArray(event.progress) && !Array.isArray(event.progressUpdates)
|
||||
)) return payload
|
||||
const previous = progress.get(event.childTaskId) ?? []
|
||||
const compact = { ...event }
|
||||
delete compact.progress
|
||||
compact.progressUpdates = diffSubagentProgress(
|
||||
progress.get(event.childTaskId) ?? [],
|
||||
event.progress
|
||||
previous,
|
||||
applySubagentProgress(previous, event) ?? []
|
||||
)
|
||||
return compact
|
||||
}
|
||||
@@ -158,7 +180,17 @@ export class SubagentProgressStorage {
|
||||
}
|
||||
|
||||
inserted(taskId: string, kind: string, id: number, payloadJson: string): void {
|
||||
if (kind !== 'subagent') return
|
||||
if (kind !== 'subagent') {
|
||||
if (
|
||||
kind === 'done' || kind === 'error' ||
|
||||
(kind === 'status' && [
|
||||
'completed', 'failed', 'cancelled', 'interrupted'
|
||||
].includes((JSON.parse(payloadJson) as { status?: string }).status ?? ''))
|
||||
) {
|
||||
this.tasks.delete(taskId)
|
||||
}
|
||||
return
|
||||
}
|
||||
const task = this.tasks.get(taskId)
|
||||
if (!task) return
|
||||
restoreSubagentPayload(JSON.parse(payloadJson), task.blocks)
|
||||
|
||||
Reference in New Issue
Block a user