refactor(task-toast): improve task toast manager types and logic
Add new toast types and improve state management for background task notifications. Update tests to cover new scenarios. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
aa2b052d28
commit
72098213ee
@ -160,7 +160,7 @@ describe("TaskToastManager", () => {
|
|||||||
// #then - toast should NOT show warning - category default is expected
|
// #then - toast should NOT show warning - category default is expected
|
||||||
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
||||||
const call = mockClient.tui.showToast.mock.calls[0][0]
|
const call = mockClient.tui.showToast.mock.calls[0][0]
|
||||||
expect(call.body.message).not.toContain("⚠️")
|
expect(call.body.message).not.toContain("[FALLBACK]")
|
||||||
expect(call.body.message).not.toContain("(category default)")
|
expect(call.body.message).not.toContain("(category default)")
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ describe("TaskToastManager", () => {
|
|||||||
// #then - toast should show fallback warning
|
// #then - toast should show fallback warning
|
||||||
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
||||||
const call = mockClient.tui.showToast.mock.calls[0][0]
|
const call = mockClient.tui.showToast.mock.calls[0][0]
|
||||||
expect(call.body.message).toContain("⚠️")
|
expect(call.body.message).toContain("[FALLBACK]")
|
||||||
expect(call.body.message).toContain("anthropic/claude-sonnet-4-5")
|
expect(call.body.message).toContain("anthropic/claude-sonnet-4-5")
|
||||||
expect(call.body.message).toContain("(system default fallback)")
|
expect(call.body.message).toContain("(system default fallback)")
|
||||||
})
|
})
|
||||||
@ -201,7 +201,7 @@ describe("TaskToastManager", () => {
|
|||||||
// #then - toast should show fallback warning
|
// #then - toast should show fallback warning
|
||||||
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
||||||
const call = mockClient.tui.showToast.mock.calls[0][0]
|
const call = mockClient.tui.showToast.mock.calls[0][0]
|
||||||
expect(call.body.message).toContain("⚠️")
|
expect(call.body.message).toContain("[FALLBACK]")
|
||||||
expect(call.body.message).toContain("cliproxy/claude-opus-4-5")
|
expect(call.body.message).toContain("cliproxy/claude-opus-4-5")
|
||||||
expect(call.body.message).toContain("(inherited from parent)")
|
expect(call.body.message).toContain("(inherited from parent)")
|
||||||
})
|
})
|
||||||
@ -222,7 +222,7 @@ describe("TaskToastManager", () => {
|
|||||||
// #then - toast should NOT show model warning
|
// #then - toast should NOT show model warning
|
||||||
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
||||||
const call = mockClient.tui.showToast.mock.calls[0][0]
|
const call = mockClient.tui.showToast.mock.calls[0][0]
|
||||||
expect(call.body.message).not.toContain("⚠️ Model:")
|
expect(call.body.message).not.toContain("[FALLBACK] Model:")
|
||||||
expect(call.body.message).not.toContain("(inherited)")
|
expect(call.body.message).not.toContain("(inherited)")
|
||||||
expect(call.body.message).not.toContain("(category default)")
|
expect(call.body.message).not.toContain("(category default)")
|
||||||
expect(call.body.message).not.toContain("(system default)")
|
expect(call.body.message).not.toContain("(system default)")
|
||||||
@ -243,7 +243,7 @@ describe("TaskToastManager", () => {
|
|||||||
// #then - toast should NOT show model warning
|
// #then - toast should NOT show model warning
|
||||||
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
expect(mockClient.tui.showToast).toHaveBeenCalled()
|
||||||
const call = mockClient.tui.showToast.mock.calls[0][0]
|
const call = mockClient.tui.showToast.mock.calls[0][0]
|
||||||
expect(call.body.message).not.toContain("⚠️ Model:")
|
expect(call.body.message).not.toContain("[FALLBACK] Model:")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -24,6 +24,7 @@ export class TaskToastManager {
|
|||||||
agent: string
|
agent: string
|
||||||
isBackground: boolean
|
isBackground: boolean
|
||||||
status?: TaskStatus
|
status?: TaskStatus
|
||||||
|
category?: string
|
||||||
skills?: string[]
|
skills?: string[]
|
||||||
modelInfo?: ModelFallbackInfo
|
modelInfo?: ModelFallbackInfo
|
||||||
}): void {
|
}): void {
|
||||||
@ -34,6 +35,7 @@ export class TaskToastManager {
|
|||||||
status: task.status ?? "running",
|
status: task.status ?? "running",
|
||||||
startedAt: new Date(),
|
startedAt: new Date(),
|
||||||
isBackground: task.isBackground,
|
isBackground: task.isBackground,
|
||||||
|
category: task.category,
|
||||||
skills: task.skills,
|
skills: task.skills,
|
||||||
modelInfo: task.modelInfo,
|
modelInfo: task.modelInfo,
|
||||||
}
|
}
|
||||||
@ -116,7 +118,7 @@ export class TaskToastManager {
|
|||||||
"system-default": " (system default fallback)",
|
"system-default": " (system default fallback)",
|
||||||
}
|
}
|
||||||
const suffix = suffixMap[newTask.modelInfo!.type as "inherited" | "system-default"]
|
const suffix = suffixMap[newTask.modelInfo!.type as "inherited" | "system-default"]
|
||||||
lines.push(`⚠️ Model fallback: ${newTask.modelInfo!.model}${suffix}`)
|
lines.push(`[FALLBACK] Model: ${newTask.modelInfo!.model}${suffix}`)
|
||||||
lines.push("")
|
lines.push("")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,10 +126,11 @@ export class TaskToastManager {
|
|||||||
lines.push(`Running (${running.length}):${concurrencyInfo}`)
|
lines.push(`Running (${running.length}):${concurrencyInfo}`)
|
||||||
for (const task of running) {
|
for (const task of running) {
|
||||||
const duration = this.formatDuration(task.startedAt)
|
const duration = this.formatDuration(task.startedAt)
|
||||||
const bgIcon = task.isBackground ? "⚡" : "🔄"
|
const bgIcon = task.isBackground ? "[BG]" : "[RUN]"
|
||||||
const isNew = task.id === newTask.id ? " ← NEW" : ""
|
const isNew = task.id === newTask.id ? " ← NEW" : ""
|
||||||
|
const categoryInfo = task.category ? `/${task.category}` : ""
|
||||||
const skillsInfo = task.skills?.length ? ` [${task.skills.join(", ")}]` : ""
|
const skillsInfo = task.skills?.length ? ` [${task.skills.join(", ")}]` : ""
|
||||||
lines.push(`${bgIcon} ${task.description} (${task.agent})${skillsInfo} - ${duration}${isNew}`)
|
lines.push(`${bgIcon} ${task.description} (${task.agent}${categoryInfo})${skillsInfo} - ${duration}${isNew}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,10 +138,11 @@ export class TaskToastManager {
|
|||||||
if (lines.length > 0) lines.push("")
|
if (lines.length > 0) lines.push("")
|
||||||
lines.push(`Queued (${queued.length}):`)
|
lines.push(`Queued (${queued.length}):`)
|
||||||
for (const task of queued) {
|
for (const task of queued) {
|
||||||
const bgIcon = task.isBackground ? "⏳" : "⏸️"
|
const bgIcon = task.isBackground ? "[Q]" : "[W]"
|
||||||
|
const categoryInfo = task.category ? `/${task.category}` : ""
|
||||||
const skillsInfo = task.skills?.length ? ` [${task.skills.join(", ")}]` : ""
|
const skillsInfo = task.skills?.length ? ` [${task.skills.join(", ")}]` : ""
|
||||||
const isNew = task.id === newTask.id ? " ← NEW" : ""
|
const isNew = task.id === newTask.id ? " ← NEW" : ""
|
||||||
lines.push(`${bgIcon} ${task.description} (${task.agent})${skillsInfo} - Queued${isNew}`)
|
lines.push(`${bgIcon} ${task.description} (${task.agent}${categoryInfo})${skillsInfo} - Queued${isNew}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,8 +162,8 @@ export class TaskToastManager {
|
|||||||
const queued = this.getQueuedTasks()
|
const queued = this.getQueuedTasks()
|
||||||
|
|
||||||
const title = newTask.isBackground
|
const title = newTask.isBackground
|
||||||
? `⚡ New Background Task`
|
? `New Background Task`
|
||||||
: `🔄 New Task Executed`
|
: `New Task Executed`
|
||||||
|
|
||||||
tuiClient.tui.showToast({
|
tuiClient.tui.showToast({
|
||||||
body: {
|
body: {
|
||||||
@ -184,7 +188,7 @@ export class TaskToastManager {
|
|||||||
const remaining = this.getRunningTasks()
|
const remaining = this.getRunningTasks()
|
||||||
const queued = this.getQueuedTasks()
|
const queued = this.getQueuedTasks()
|
||||||
|
|
||||||
let message = `✅ "${task.description}" finished in ${task.duration}`
|
let message = `"${task.description}" finished in ${task.duration}`
|
||||||
if (remaining.length > 0 || queued.length > 0) {
|
if (remaining.length > 0 || queued.length > 0) {
|
||||||
message += `\n\nStill running: ${remaining.length} | Queued: ${queued.length}`
|
message += `\n\nStill running: ${remaining.length} | Queued: ${queued.length}`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
|
import type { ModelSource } from "../../shared/model-resolver"
|
||||||
|
|
||||||
export type TaskStatus = "running" | "queued" | "completed" | "error"
|
export type TaskStatus = "running" | "queued" | "completed" | "error"
|
||||||
|
|
||||||
export interface ModelFallbackInfo {
|
export interface ModelFallbackInfo {
|
||||||
model: string
|
model: string
|
||||||
type: "user-defined" | "inherited" | "category-default" | "system-default"
|
type: "user-defined" | "inherited" | "category-default" | "system-default"
|
||||||
|
source?: ModelSource
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TrackedTask {
|
export interface TrackedTask {
|
||||||
@ -12,6 +15,7 @@ export interface TrackedTask {
|
|||||||
status: TaskStatus
|
status: TaskStatus
|
||||||
startedAt: Date
|
startedAt: Date
|
||||||
isBackground: boolean
|
isBackground: boolean
|
||||||
|
category?: string
|
||||||
skills?: string[]
|
skills?: string[]
|
||||||
modelInfo?: ModelFallbackInfo
|
modelInfo?: ModelFallbackInfo
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user