feat(background-agent): add TaskHistory class for persistent task tracking
In-memory tracker that survives BackgroundManager's cleanup cycles. Records agent delegations with defensive copies, MAX 100 cap per parent, undefined-safe upsert, and newline-sanitized formatForCompaction output.
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import type { BackgroundTaskStatus } from "./types"
|
||||
|
||||
const MAX_ENTRIES_PER_PARENT = 100
|
||||
|
||||
export interface TaskHistoryEntry {
|
||||
id: string
|
||||
sessionID?: string
|
||||
agent: string
|
||||
description: string
|
||||
status: BackgroundTaskStatus
|
||||
category?: string
|
||||
startedAt?: Date
|
||||
completedAt?: Date
|
||||
}
|
||||
|
||||
export class TaskHistory {
|
||||
private entries: Map<string, TaskHistoryEntry[]> = new Map()
|
||||
|
||||
record(parentSessionID: string | undefined, entry: TaskHistoryEntry): void {
|
||||
if (!parentSessionID) return
|
||||
|
||||
const list = this.entries.get(parentSessionID) ?? []
|
||||
const existing = list.findIndex((e) => e.id === entry.id)
|
||||
|
||||
if (existing !== -1) {
|
||||
const current = list[existing]
|
||||
list[existing] = {
|
||||
...current,
|
||||
...(entry.sessionID !== undefined ? { sessionID: entry.sessionID } : {}),
|
||||
...(entry.agent !== undefined ? { agent: entry.agent } : {}),
|
||||
...(entry.description !== undefined ? { description: entry.description } : {}),
|
||||
...(entry.status !== undefined ? { status: entry.status } : {}),
|
||||
...(entry.category !== undefined ? { category: entry.category } : {}),
|
||||
...(entry.startedAt !== undefined ? { startedAt: entry.startedAt } : {}),
|
||||
...(entry.completedAt !== undefined ? { completedAt: entry.completedAt } : {}),
|
||||
}
|
||||
} else {
|
||||
if (list.length >= MAX_ENTRIES_PER_PARENT) {
|
||||
list.shift()
|
||||
}
|
||||
list.push({ ...entry })
|
||||
}
|
||||
|
||||
this.entries.set(parentSessionID, list)
|
||||
}
|
||||
|
||||
getByParentSession(parentSessionID: string): TaskHistoryEntry[] {
|
||||
const list = this.entries.get(parentSessionID)
|
||||
if (!list) return []
|
||||
return list.map((e) => ({ ...e }))
|
||||
}
|
||||
|
||||
clearSession(parentSessionID: string): void {
|
||||
this.entries.delete(parentSessionID)
|
||||
}
|
||||
|
||||
formatForCompaction(parentSessionID: string): string | null {
|
||||
const list = this.getByParentSession(parentSessionID)
|
||||
if (list.length === 0) return null
|
||||
|
||||
const lines = list.map((e) => {
|
||||
const desc = e.description.replace(/[\n\r]+/g, " ").trim()
|
||||
const parts = [
|
||||
`- **${e.agent}**`,
|
||||
e.category ? `[${e.category}]` : null,
|
||||
`(${e.status})`,
|
||||
`: ${desc}`,
|
||||
e.sessionID ? ` | session: \`${e.sessionID}\`` : null,
|
||||
]
|
||||
return parts.filter(Boolean).join("")
|
||||
})
|
||||
|
||||
return lines.join("\n")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user