diff --git a/src/features/background-agent/background-task-notification-template.test.ts b/src/features/background-agent/background-task-notification-template.test.ts new file mode 100644 index 000000000..5555e909a --- /dev/null +++ b/src/features/background-agent/background-task-notification-template.test.ts @@ -0,0 +1,130 @@ +import { describe, expect, test } from "bun:test" +import { buildBackgroundTaskNotificationText } from "./background-task-notification-template" + +describe("buildBackgroundTaskNotificationText", () => { + describe("#given one task still running after a completed task notification", () => { + test("#when building the partial notification #then it preserves the existing completed-task format", () => { + // given + const notification = buildBackgroundTaskNotificationText({ + task: { + id: "task-1", + description: "Index repo", + status: "completed", + }, + duration: "42s", + statusText: "COMPLETED", + allComplete: false, + remainingCount: 1, + completedTasks: [], + }) + + // when + const expectedNotification = ` +[BACKGROUND TASK COMPLETED] +**ID:** \`task-1\` +**Description:** Index repo +**Duration:** 42s + +**1 task still in progress.** You WILL be notified when ALL complete. +Do NOT poll - continue productive work. + +Use \`background_output(task_id="task-1")\` to retrieve this result when ready. +` + + // then + expect(notification).toBe(expectedNotification) + }) + }) + + describe("#given one task still running after a failed task notification", () => { + test("#when building the partial notification #then it preserves the existing failure format", () => { + // given + const notification = buildBackgroundTaskNotificationText({ + task: { + id: "task-2", + description: "Summarize logs", + status: "error", + error: "Timed out", + }, + duration: "3m 4s", + statusText: "ERROR", + allComplete: false, + remainingCount: 2, + completedTasks: [], + }) + + // when + const expectedNotification = ` +[BACKGROUND TASK ERROR] +**ID:** \`task-2\` +**Description:** Summarize logs +**Duration:** 3m 4s +**Error:** Timed out + +**2 tasks still in progress.** You WILL be notified when ALL complete. +**ACTION REQUIRED:** This task failed. Check the error and decide whether to retry, cancel remaining tasks, or continue. + +Use \`background_output(task_id="task-2")\` to retrieve this result when ready. +` + + // then + expect(notification).toBe(expectedNotification) + }) + }) + + describe("#given all sibling tasks completed with mixed outcomes", () => { + test("#when building the final notification #then it preserves the existing summary format", () => { + // given + const notification = buildBackgroundTaskNotificationText({ + task: { + id: "task-3", + description: "Fallback task", + status: "error", + error: "Denied", + }, + duration: "10s", + statusText: "ERROR", + allComplete: true, + remainingCount: 0, + completedTasks: [ + { + id: "task-1", + description: "Index repo", + status: "completed", + }, + { + id: "task-2", + description: "Summarize logs", + status: "cancelled", + error: "User aborted", + }, + { + id: "task-3", + description: "Fallback task", + status: "error", + error: "Denied", + }, + ], + }) + + // when + const expectedNotification = ` +[ALL BACKGROUND TASKS FINISHED - 2 FAILED] + +**Completed:** +- \`task-1\`: Index repo + +**Failed:** +- \`task-2\`: Summarize logs [CANCELLED] - User aborted +- \`task-3\`: Fallback task [ERROR] - Denied + +Use \`background_output(task_id="")\` to retrieve each result. + +**ACTION REQUIRED:** 2 task(s) failed. Check errors above and decide whether to retry or proceed. +` + + // then + expect(notification).toBe(expectedNotification) + }) + }) +}) diff --git a/src/features/background-agent/background-task-notification-template.ts b/src/features/background-agent/background-task-notification-template.ts index e2e74cc78..240efe9c0 100644 --- a/src/features/background-agent/background-task-notification-template.ts +++ b/src/features/background-agent/background-task-notification-template.ts @@ -1,14 +1,21 @@ -import type { BackgroundTask } from "./types" +import type { BackgroundTaskStatus } from "./types" export type BackgroundTaskNotificationStatus = "COMPLETED" | "CANCELLED" | "INTERRUPTED" | "ERROR" +export interface BackgroundTaskNotificationTask { + id: string + description: string + status: BackgroundTaskStatus + error?: string +} + export function buildBackgroundTaskNotificationText(input: { - task: BackgroundTask + task: BackgroundTaskNotificationTask duration: string statusText: BackgroundTaskNotificationStatus allComplete: boolean remainingCount: number - completedTasks: BackgroundTask[] + completedTasks: BackgroundTaskNotificationTask[] }): string { const { task, duration, statusText, allComplete, remainingCount, completedTasks } = input @@ -50,14 +57,12 @@ Use \`background_output(task_id="")\` to retrieve each result.${hasFailures ` } - const agentInfo = task.category ? `${task.agent} (${task.category})` : task.agent const isFailure = statusText !== "COMPLETED" return ` [BACKGROUND TASK ${statusText}] **ID:** \`${task.id}\` **Description:** ${task.description} -**Agent:** ${agentInfo} **Duration:** ${duration}${errorInfo} **${remainingCount} task${remainingCount === 1 ? "" : "s"} still in progress.** You WILL be notified when ALL complete.