test(background-agent): remove forbidden assertions in manager tests

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-05-01 02:11:38 +09:00
parent c90be7f9a6
commit d737f2e214
+72 -69
View File
@@ -185,6 +185,10 @@ function createMockTask(overrides: Partial<BackgroundTask> & { id: string; paren
} }
} }
function cast<T>(value: unknown): T {
return value as T
}
function createBackgroundManager(): BackgroundManager { function createBackgroundManager(): BackgroundManager {
const client = { const client = {
session: { session: {
@@ -212,56 +216,56 @@ function createBackgroundManagerWithOptions(options: Partial<ConstructorParamete
} }
function getConcurrencyManager(manager: BackgroundManager): ConcurrencyManager { function getConcurrencyManager(manager: BackgroundManager): ConcurrencyManager {
return (manager as unknown as { concurrencyManager: ConcurrencyManager }).concurrencyManager return (cast<{ concurrencyManager: ConcurrencyManager }>(manager)).concurrencyManager
} }
function getTaskMap(manager: BackgroundManager): Map<string, BackgroundTask> { function getTaskMap(manager: BackgroundManager): Map<string, BackgroundTask> {
return (manager as unknown as { tasks: Map<string, BackgroundTask> }).tasks return (cast<{ tasks: Map<string, BackgroundTask> }>(manager)).tasks
} }
function getPendingByParent(manager: BackgroundManager): Map<string, Set<string>> { function getPendingByParent(manager: BackgroundManager): Map<string, Set<string>> {
return (manager as unknown as { pendingByParent: Map<string, Set<string>> }).pendingByParent return (cast<{ pendingByParent: Map<string, Set<string>> }>(manager)).pendingByParent
} }
function getPendingNotifications(manager: BackgroundManager): Map<string, string[]> { function getPendingNotifications(manager: BackgroundManager): Map<string, string[]> {
return (manager as unknown as { pendingNotifications: Map<string, string[]> }).pendingNotifications return (cast<{ pendingNotifications: Map<string, string[]> }>(manager)).pendingNotifications
} }
function getCompletionTimers(manager: BackgroundManager): Map<string, ReturnType<typeof setTimeout>> { function getCompletionTimers(manager: BackgroundManager): Map<string, ReturnType<typeof setTimeout>> {
return (manager as unknown as { completionTimers: Map<string, ReturnType<typeof setTimeout>> }).completionTimers return (cast<{ completionTimers: Map<string, ReturnType<typeof setTimeout>> }>(manager)).completionTimers
} }
function getRootDescendantCounts(manager: BackgroundManager): Map<string, number> { function getRootDescendantCounts(manager: BackgroundManager): Map<string, number> {
return (manager as unknown as { rootDescendantCounts: Map<string, number> }).rootDescendantCounts return (cast<{ rootDescendantCounts: Map<string, number> }>(manager)).rootDescendantCounts
} }
function getPreStartDescendantReservations(manager: BackgroundManager): Set<string> { function getPreStartDescendantReservations(manager: BackgroundManager): Set<string> {
return (manager as unknown as { preStartDescendantReservations: Set<string> }).preStartDescendantReservations return (cast<{ preStartDescendantReservations: Set<string> }>(manager)).preStartDescendantReservations
} }
function getQueuesByKey( function getQueuesByKey(
manager: BackgroundManager manager: BackgroundManager
): Map<string, Array<{ task: BackgroundTask; input: import("./types").LaunchInput }>> { ): Map<string, Array<{ task: BackgroundTask; input: import("./types").LaunchInput }>> {
return (manager as unknown as { return (cast<{
queuesByKey: Map<string, Array<{ task: BackgroundTask; input: import("./types").LaunchInput }>> queuesByKey: Map<string, Array<{ task: BackgroundTask; input: import("./types").LaunchInput }>>
}).queuesByKey }>(manager)).queuesByKey
} }
async function processKeyForTest(manager: BackgroundManager, key: string): Promise<void> { async function processKeyForTest(manager: BackgroundManager, key: string): Promise<void> {
return (manager as unknown as { processKey: (key: string) => Promise<void> }).processKey(key) return (cast<{ processKey: (key: string) => Promise<void> }>(manager)).processKey(key)
} }
function pruneStaleTasksAndNotificationsForTest(manager: BackgroundManager): void { function pruneStaleTasksAndNotificationsForTest(manager: BackgroundManager): void {
;(manager as unknown as { pruneStaleTasksAndNotifications: () => void }).pruneStaleTasksAndNotifications() ;(cast<{ pruneStaleTasksAndNotifications: () => void }>(manager)).pruneStaleTasksAndNotifications()
} }
async function tryCompleteTaskForTest(manager: BackgroundManager, task: BackgroundTask): Promise<boolean> { async function tryCompleteTaskForTest(manager: BackgroundManager, task: BackgroundTask): Promise<boolean> {
return (manager as unknown as { tryCompleteTask: (task: BackgroundTask, source: string) => Promise<boolean> }) return (cast<{ tryCompleteTask: (task: BackgroundTask, source: string) => Promise<boolean> }>(manager))
.tryCompleteTask(task, "test") .tryCompleteTask(task, "test")
} }
function stubNotifyParentSession(manager: BackgroundManager): void { function stubNotifyParentSession(manager: BackgroundManager): void {
;(manager as unknown as { notifyParentSession: () => Promise<void> }).notifyParentSession = async () => {} ;(cast<{ notifyParentSession: () => Promise<void> }>(manager)).notifyParentSession = async () => {}
} }
async function flushBackgroundNotifications(): Promise<void> { async function flushBackgroundNotifications(): Promise<void> {
@@ -272,9 +276,9 @@ async function flushBackgroundNotifications(): Promise<void> {
function createToastRemoveTaskTracker(): { removeTaskCalls: string[]; resetToastManager: () => void } { function createToastRemoveTaskTracker(): { removeTaskCalls: string[]; resetToastManager: () => void } {
_resetTaskToastManagerForTesting() _resetTaskToastManagerForTesting()
const toastManager = initTaskToastManager({ const toastManager = initTaskToastManager(cast<PluginInput["client"]>({
tui: { showToast: async () => {} }, tui: { showToast: async () => {} },
} as unknown as PluginInput["client"]) }))
const removeTaskCalls: string[] = [] const removeTaskCalls: string[] = []
const originalRemoveTask = toastManager.removeTask.bind(toastManager) const originalRemoveTask = toastManager.removeTask.bind(toastManager)
toastManager.removeTask = (taskId: string): void => { toastManager.removeTask = (taskId: string): void => {
@@ -311,22 +315,22 @@ describe("BackgroundManager session.error fallback hydration", () => {
fallbackChain: undefined, fallbackChain: undefined,
}) })
let capturedFallbackChain: BackgroundTask["fallbackChain"] let capturedFallbackChain: BackgroundTask["fallbackChain"]
;(manager as unknown as { ;(cast<{
tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean> tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean>
}).tryFallbackRetry = async (retryTask) => { }>(manager)).tryFallbackRetry = async (retryTask) => {
capturedFallbackChain = retryTask.fallbackChain capturedFallbackChain = retryTask.fallbackChain
return true return true
} }
//#when //#when
await (manager as unknown as { await (cast<{
handleSessionErrorEvent: (args: { handleSessionErrorEvent: (args: {
task: BackgroundTask task: BackgroundTask
errorInfo: { name?: string; message?: string } errorInfo: { name?: string; message?: string }
errorName: string | undefined errorName: string | undefined
errorMessage: string | undefined errorMessage: string | undefined
}) => Promise<void> }) => Promise<void>
}).handleSessionErrorEvent({ }>(manager)).handleSessionErrorEvent({
task, task,
errorInfo: { errorInfo: {
name: "APIError", name: "APIError",
@@ -362,23 +366,23 @@ describe("BackgroundManager prompt rejection fallback routing", () => {
} }
const manager = new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput }) const manager = new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput })
stubNotifyParentSession(manager) stubNotifyParentSession(manager)
;(manager as unknown as { ;(cast<{
reserveSubagentSpawn: () => Promise<{ reserveSubagentSpawn: () => Promise<{
spawnContext: { rootSessionID: string; parentDepth: number; childDepth: number } spawnContext: { rootSessionID: string; parentDepth: number; childDepth: number }
descendantCount: number descendantCount: number
commit: () => number commit: () => number
rollback: () => void rollback: () => void
}> }>
}).reserveSubagentSpawn = async () => ({ }>(manager)).reserveSubagentSpawn = async () => ({
spawnContext: { rootSessionID: "parent-session", parentDepth: 0, childDepth: 1 }, spawnContext: { rootSessionID: "parent-session", parentDepth: 0, childDepth: 1 },
descendantCount: 1, descendantCount: 1,
commit: () => 1, commit: () => 1,
rollback: () => {}, rollback: () => {},
}) })
const retried: Array<{ taskId: string; errorInfo: { name?: string; message?: string }; source: string }> = [] const retried: Array<{ taskId: string; errorInfo: { name?: string; message?: string }; source: string }> = []
;(manager as unknown as { ;(cast<{
tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean> tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean>
}).tryFallbackRetry = async (task, errorInfo, source) => { }>(manager)).tryFallbackRetry = async (task, errorInfo, source) => {
retried.push({ taskId: task.id, errorInfo, source }) retried.push({ taskId: task.id, errorInfo, source })
task.status = "pending" task.status = "pending"
task.error = undefined task.error = undefined
@@ -441,9 +445,9 @@ describe("BackgroundManager prompt rejection fallback routing", () => {
} }
getTaskMap(manager).set(task.id, task) getTaskMap(manager).set(task.id, task)
const retried: Array<{ taskId: string; errorInfo: { name?: string; message?: string }; source: string }> = [] const retried: Array<{ taskId: string; errorInfo: { name?: string; message?: string }; source: string }> = []
;(manager as unknown as { ;(cast<{
tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean> tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean>
}).tryFallbackRetry = async (retryTask, errorInfo, source) => { }>(manager)).tryFallbackRetry = async (retryTask, errorInfo, source) => {
retried.push({ taskId: retryTask.id, errorInfo, source }) retried.push({ taskId: retryTask.id, errorInfo, source })
retryTask.status = "pending" retryTask.status = "pending"
retryTask.error = undefined retryTask.error = undefined
@@ -505,9 +509,9 @@ describe("BackgroundManager retry observability", () => {
}).queuePendingNotification = queuePendingNotification }).queuePendingNotification = queuePendingNotification
//#when //#when
await (manager as unknown as { await (cast<{
tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean> tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean>
}).tryFallbackRetry(task, { }>(manager)).tryFallbackRetry(task, {
name: "APIError", name: "APIError",
message: "Forbidden: Selected provider is forbidden", message: "Forbidden: Selected provider is forbidden",
}, "promptAsync.launch") }, "promptAsync.launch")
@@ -591,12 +595,12 @@ describe("BackgroundManager retry observability", () => {
} }
//#when //#when
await (manager as unknown as { await (cast<{
startTask: (queueItem: RetryReadyQueueItem) => Promise<void> startTask: (queueItem: RetryReadyQueueItem) => Promise<void>
}).startTask(item) }>(manager)).startTask(item)
//#then //#then
const notifications = queuePendingNotification.mock.calls.map((call) => call[1]) const notifications = cast<Array<[string | undefined, string]>>(queuePendingNotification.mock.calls).map((call) => call[1])
const retryReadyNotification = notifications.find((notification) => notification.includes("[BACKGROUND TASK RETRY SESSION READY]")) const retryReadyNotification = notifications.find((notification) => notification.includes("[BACKGROUND TASK RETRY SESSION READY]"))
const expectedRetryLink = `http://127.0.0.1:4096/${Buffer.from(tmpdir()).toString("base64url")}/session/ses_retry_created` const expectedRetryLink = `http://127.0.0.1:4096/${Buffer.from(tmpdir()).toString("base64url")}/session/ses_retry_created`
expect(retryReadyNotification).toBeDefined() expect(retryReadyNotification).toBeDefined()
@@ -667,14 +671,14 @@ describe("BackgroundManager retry observability", () => {
} }
//#when //#when
await (manager as unknown as { await (cast<{
startTask: (queueItem: { task: BackgroundTask; input: typeof taskInput; attemptID: string }) => Promise<void> startTask: (queueItem: { task: BackgroundTask; input: typeof taskInput; attemptID: string }) => Promise<void>
}).startTask({ task, input: taskInput, attemptID: "att_retry_ready_parent_dir" }) }>(manager)).startTask({ task, input: taskInput, attemptID: "att_retry_ready_parent_dir" })
//#then //#then
const retryReadyNotification = queuePendingNotification.mock.calls const retryReadyNotification = cast<Array<[string | undefined, string]>>(queuePendingNotification.mock.calls)
.map((call) => call[1]) .map((call) => call[1])
.find((notification) => notification.includes("[BACKGROUND TASK RETRY SESSION READY]")) .find((notification) => notification.includes("[BACKGROUND TASK RETRY SESSION READY]"))
const expectedRetryLink = `http://127.0.0.1:4096/${Buffer.from(parentDirectory).toString("base64url")}/session/ses_retry_created_parent_dir` const expectedRetryLink = `http://127.0.0.1:4096/${Buffer.from(parentDirectory).toString("base64url")}/session/ses_retry_created_parent_dir`
expect(retryReadyNotification).toBeDefined() expect(retryReadyNotification).toBeDefined()
expect(retryReadyNotification).toContain(expectedRetryLink) expect(retryReadyNotification).toContain(expectedRetryLink)
@@ -1293,7 +1297,7 @@ describe("BackgroundManager.notifyParentSession - dynamic message lookup", () =>
getPendingByParent(manager).set("session-parent", new Set([task.id, "still-running"])) getPendingByParent(manager).set("session-parent", new Set([task.id, "still-running"]))
//#when //#when
await (manager as unknown as { notifyParentSession: (value: BackgroundTask) => Promise<void> }) await (cast<{ notifyParentSession: (value: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task) .notifyParentSession(task)
//#then //#then
@@ -1449,7 +1453,7 @@ describe("BackgroundManager.notifyParentSession - aborted parent", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id, "task-remaining"])) getPendingByParent(manager).set("session-parent", new Set([task.id, "task-remaining"]))
//#when //#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> }) await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task) .notifyParentSession(task)
//#then //#then
@@ -1491,7 +1495,7 @@ describe("BackgroundManager.notifyParentSession - aborted parent", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id])) getPendingByParent(manager).set("session-parent", new Set([task.id]))
//#when //#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> }) await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task) .notifyParentSession(task)
//#then //#then
@@ -1531,7 +1535,7 @@ describe("BackgroundManager.notifyParentSession - aborted parent", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id])) getPendingByParent(manager).set("session-parent", new Set([task.id]))
//#when //#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> }) await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task) .notifyParentSession(task)
//#then //#then
@@ -1589,7 +1593,7 @@ describe("BackgroundManager.notifyParentSession - notifications toggle", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id])) getPendingByParent(manager).set("session-parent", new Set([task.id]))
//#when //#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> }) await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task) .notifyParentSession(task)
//#then //#then
@@ -1642,7 +1646,7 @@ describe("BackgroundManager.notifyParentSession - variant propagation", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id])) getPendingByParent(manager).set("session-parent", new Set([task.id]))
//#when //#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> }) await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task) .notifyParentSession(task)
//#then //#then
@@ -1683,7 +1687,7 @@ describe("BackgroundManager.notifyParentSession - variant propagation", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id])) getPendingByParent(manager).set("session-parent", new Set([task.id]))
//#when //#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> }) await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task) .notifyParentSession(task)
//#then //#then
@@ -1935,7 +1939,7 @@ describe("BackgroundManager.tryCompleteTask", () => {
getTaskMap(manager).set(task.id, task) getTaskMap(manager).set(task.id, task)
getQueuesByKey(manager).set(concurrencyKey, [{ task, input }]) getQueuesByKey(manager).set(concurrencyKey, [{ task, input }])
;(manager as unknown as { startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise<void> }).startTask = async (item) => { ;(cast<{ startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise<void> }>(manager)).startTask = async (item) => {
item.task.concurrencyKey = concurrencyKey item.task.concurrencyKey = concurrencyKey
throw new Error("startTask failed after assigning concurrencyKey") throw new Error("startTask failed after assigning concurrencyKey")
} }
@@ -1972,7 +1976,7 @@ describe("BackgroundManager.tryCompleteTask", () => {
getTaskMap(manager).set(task.id, task) getTaskMap(manager).set(task.id, task)
getQueuesByKey(manager).set(concurrencyKey, [{ task, input }]) getQueuesByKey(manager).set(concurrencyKey, [{ task, input }])
;(manager as unknown as { startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise<void> }).startTask = async (item) => { ;(cast<{ startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise<void> }>(manager)).startTask = async (item) => {
item.task.status = "running" item.task.status = "running"
item.task.sessionId = "ses_zombie_child" item.task.sessionId = "ses_zombie_child"
item.task.startedAt = new Date() item.task.startedAt = new Date()
@@ -2957,9 +2961,9 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
getPreStartDescendantReservations(manager).add(task.id) getPreStartDescendantReservations(manager).add(task.id)
stubNotifyParentSession(manager) stubNotifyParentSession(manager)
;(manager as unknown as { ;(cast<{
startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise<void> startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise<void>
}).startTask = async () => { }>(manager)).startTask = async () => {
throw new Error("session create failed") throw new Error("session create failed")
} }
@@ -3455,7 +3459,7 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
parentMessageId: "parent-message", parentMessageId: "parent-message",
} }
const task1 = await manager.launch(input) await manager.launch(input)
const task2 = await manager.launch(input) const task2 = await manager.launch(input)
await new Promise(resolve => setTimeout(resolve, 50)) await new Promise(resolve => setTimeout(resolve, 50))
@@ -3509,7 +3513,7 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
parentMessageId: "parent-message", parentMessageId: "parent-message",
} }
const task1 = await manager.launch(input) await manager.launch(input)
const task2 = await manager.launch(input) const task2 = await manager.launch(input)
const task3 = await manager.launch(input) const task3 = await manager.launch(input)
await new Promise(resolve => setTimeout(resolve, 100)) await new Promise(resolve => setTimeout(resolve, 100))
@@ -4645,13 +4649,13 @@ describe("BackgroundManager.handleEvent - session.error", () => {
const mockVerifySessionExists = (manager: BackgroundManager, sessionExists: boolean): void => { const mockVerifySessionExists = (manager: BackgroundManager, sessionExists: boolean): void => {
verifySessionExistsSpy?.mockRestore() verifySessionExistsSpy?.mockRestore()
verifySessionExistsSpy = spyOn( verifySessionExistsSpy = spyOn(
manager as unknown as { verifySessionExists: (sessionID: string) => Promise<boolean> }, cast<{ verifySessionExists: (sessionID: string) => Promise<boolean> }>(manager),
"verifySessionExists", "verifySessionExists",
).mockResolvedValue(sessionExists) ).mockResolvedValue(sessionExists)
} }
const stubProcessKey = (manager: BackgroundManager) => { const stubProcessKey = (manager: BackgroundManager) => {
;(manager as unknown as { processKey: (key: string) => Promise<void> }).processKey = async () => {} ;(cast<{ processKey: (key: string) => Promise<void> }>(manager)).processKey = async () => {}
} }
const createRetryTask = (manager: BackgroundManager, input: { const createRetryTask = (manager: BackgroundManager, input: {
@@ -4802,7 +4806,7 @@ describe("BackgroundManager.handleEvent - session.error", () => {
manager.handleEvent({ manager.handleEvent({
type: "session.error", type: "session.error",
properties: { properties: {
sessionId: "ses_unknown", sessionID: "ses_unknown",
error: { name: "UnknownError", message: "Model not found" }, error: { name: "UnknownError", message: "Model not found" },
}, },
}) })
@@ -4833,7 +4837,7 @@ describe("BackgroundManager.handleEvent - session.error", () => {
manager.handleEvent({ manager.handleEvent({
type: "session.error", type: "session.error",
properties: { properties: {
sessionId: task.sessionId, sessionID: task.sessionId,
error: { error: {
name: "UnknownError", name: "UnknownError",
message: "Out of memory", message: "Out of memory",
@@ -4873,7 +4877,7 @@ describe("BackgroundManager.handleEvent - session.error", () => {
manager.handleEvent({ manager.handleEvent({
type: "session.error", type: "session.error",
properties: { properties: {
sessionId: task.sessionId, sessionID: task.sessionId,
error: { error: {
name: "UnknownError", name: "UnknownError",
message: "Out of memory", message: "Out of memory",
@@ -5115,7 +5119,7 @@ describe("BackgroundManager queue processing - error tasks are skipped", () => {
} }
let startCalled = false let startCalled = false
;(manager as unknown as { startTask: (item: unknown) => Promise<void> }).startTask = async () => { ;(cast<{ startTask: (item: unknown) => Promise<void> }>(manager)).startTask = async () => {
startCalled = true startCalled = true
} }
@@ -5302,13 +5306,13 @@ describe("BackgroundManager.completionTimers - Memory Leak Fix", () => {
} }
getTaskMap(manager).set(taskA.id, taskA) getTaskMap(manager).set(taskA.id, taskA)
getTaskMap(manager).set(taskB.id, taskB) getTaskMap(manager).set(taskB.id, taskB)
;(manager as unknown as { pendingByParent: Map<string, Set<string>> }).pendingByParent.set( ;(cast<{ pendingByParent: Map<string, Set<string>> }>(manager)).pendingByParent.set(
"parent-session", "parent-session",
new Set([taskA.id, taskB.id]) new Set([taskA.id, taskB.id])
) )
// when // when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> }) await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(taskA) .notifyParentSession(taskA)
// then // then
@@ -5316,7 +5320,7 @@ describe("BackgroundManager.completionTimers - Memory Leak Fix", () => {
expect(completionTimers.size).toBe(1) expect(completionTimers.size).toBe(1)
// when // when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> }) await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(taskB) .notifyParentSession(taskB)
// then // then
@@ -5423,7 +5427,6 @@ describe("BackgroundManager.handleEvent - early session.idle deferral", () => {
const manager = new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput }) const manager = new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput })
stubNotifyParentSession(manager) stubNotifyParentSession(manager)
const remainingMs = 1200
const task: BackgroundTask = { const task: BackgroundTask = {
id: "task-early-idle", id: "task-early-idle",
sessionId: sessionID, sessionId: sessionID,
@@ -5910,7 +5913,7 @@ describe("BackgroundManager regression fixes - resume and aborted notification",
getPendingByParent(manager).set(task.parentSessionId, new Set([task.id])) getPendingByParent(manager).set(task.parentSessionId, new Set([task.id]))
//#when //#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> }).notifyParentSession(task) await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager)).notifyParentSession(task)
//#then //#then
expect(getCompletionTimers(manager).has(task.id)).toBe(true) expect(getCompletionTimers(manager).has(task.id)).toBe(true)
@@ -5953,7 +5956,7 @@ describe("BackgroundManager - tool permission spread order", () => {
} }
//#when //#when
await (manager as unknown as { startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput }) => Promise<void> }) await (cast<{ startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput }) => Promise<void> }>(manager))
.startTask({ task, input }) .startTask({ task, input })
//#then //#then
@@ -6001,7 +6004,7 @@ describe("BackgroundManager - tool permission spread order", () => {
} }
//#when //#when
await (manager as unknown as { startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput }) => Promise<void> }) await (cast<{ startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput }) => Promise<void> }>(manager))
.startTask({ task, input }) .startTask({ task, input })
//#then //#then
@@ -6107,14 +6110,14 @@ describe("BackgroundManager.launch - attempt state initialization", () => {
test("newly launched task has attempt state with attemptNumber 1 and currentAttemptID pointing at it", async () => { test("newly launched task has attempt state with attemptNumber 1 and currentAttemptID pointing at it", async () => {
//#given //#given
const manager = createBackgroundManager() const manager = createBackgroundManager()
;(manager as unknown as { ;(cast<{
reserveSubagentSpawn: () => Promise<{ reserveSubagentSpawn: () => Promise<{
spawnContext: { rootSessionID: string; parentDepth: number; childDepth: number } spawnContext: { rootSessionID: string; parentDepth: number; childDepth: number }
descendantCount: number descendantCount: number
commit: () => number commit: () => number
rollback: () => void rollback: () => void
}> }>
}).reserveSubagentSpawn = async () => ({ }>(manager)).reserveSubagentSpawn = async () => ({
spawnContext: { rootSessionID: "parent-session", parentDepth: 0, childDepth: 1 }, spawnContext: { rootSessionID: "parent-session", parentDepth: 0, childDepth: 1 },
descendantCount: 1, descendantCount: 1,
commit: () => 1, commit: () => 1,
@@ -6210,9 +6213,9 @@ describe("BackgroundManager attempt lifecycle bindings", () => {
} }
//#when //#when
await (manager as unknown as { await (cast<{
startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput; attemptID: string }) => Promise<void> startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput; attemptID: string }) => Promise<void>
}).startTask({ task, input, attemptID: "attempt-2" }) }>(manager)).startTask({ task, input, attemptID: "attempt-2" })
//#then //#then
const activeAttempt = task.attempts?.find((attempt) => attempt.attemptId === "attempt-2") const activeAttempt = task.attempts?.find((attempt) => attempt.attemptId === "attempt-2")
@@ -6324,9 +6327,9 @@ describe("BackgroundManager attempt lifecycle bindings", () => {
} }
const manager = new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput }) const manager = new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput })
stubNotifyParentSession(manager) stubNotifyParentSession(manager)
;(manager as unknown as { ;(cast<{
tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean> tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean>
}).tryFallbackRetry = async () => false }>(manager)).tryFallbackRetry = async () => false
const task: BackgroundTask = { const task: BackgroundTask = {
id: "task-stale-prompt-error", id: "task-stale-prompt-error",
status: "pending", status: "pending",
@@ -6358,9 +6361,9 @@ describe("BackgroundManager attempt lifecycle bindings", () => {
model: task.model, model: task.model,
} }
await (manager as unknown as { await (cast<{
startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput; attemptID: string }) => Promise<void> startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput; attemptID: string }) => Promise<void>
}).startTask({ task, input, attemptID: "attempt-1" }) }>(manager)).startTask({ task, input, attemptID: "attempt-1" })
task.attempts = [ task.attempts = [
{ {