fix(background-agent): resolve parent wake agent aliases
This commit is contained in:
@@ -7,7 +7,7 @@ import { getSessionPromptParams, clearSessionPromptParams } from "../../shared/s
|
|||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
import * as sharedModule from "../../shared"
|
import * as sharedModule from "../../shared"
|
||||||
import { _resetForTesting as resetClaudeCodeSessionState, subagentSessions } from "../claude-code-session-state"
|
import { _resetForTesting as resetClaudeCodeSessionState, registerAgentName, subagentSessions } from "../claude-code-session-state"
|
||||||
import type { BackgroundTask, ResumeInput } from "./types"
|
import type { BackgroundTask, ResumeInput } from "./types"
|
||||||
import { MIN_IDLE_TIME_MS } from "./constants"
|
import { MIN_IDLE_TIME_MS } from "./constants"
|
||||||
import { BackgroundManager } from "./manager"
|
import { BackgroundManager } from "./manager"
|
||||||
@@ -5204,6 +5204,51 @@ describe("BackgroundManager.handleEvent - session.error", () => {
|
|||||||
manager.shutdown()
|
manager.shutdown()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("pins the registered parent agent alias before dispatching a deferred parent wake", async () => {
|
||||||
|
//#given
|
||||||
|
resetClaudeCodeSessionState()
|
||||||
|
registerAgentName("\u200B\u200B\u200B\u200BAtlas - Plan Executor")
|
||||||
|
const promptCalls: Array<{ path: { id: string }; body: Record<string, unknown> }> = []
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
status: async () => ({ data: { "parent-session-alias": { type: "idle" } } }),
|
||||||
|
promptAsync: async (args: { path: { id: string }; body: Record<string, unknown> }) => {
|
||||||
|
promptCalls.push(args)
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
abort: async () => ({}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const manager = new BackgroundManager({ pluginContext: createPluginInput(client) })
|
||||||
|
const managerInternals = cast<{
|
||||||
|
queuePendingParentWake: (
|
||||||
|
sessionID: string,
|
||||||
|
notification: string,
|
||||||
|
promptContext: Record<string, unknown>,
|
||||||
|
shouldReply: boolean,
|
||||||
|
delayMs?: number,
|
||||||
|
) => void
|
||||||
|
flushPendingParentWake: (sessionID: string) => Promise<void>
|
||||||
|
}>(manager)
|
||||||
|
|
||||||
|
//#when
|
||||||
|
managerInternals.queuePendingParentWake(
|
||||||
|
"parent-session-alias",
|
||||||
|
"<system-reminder>done</system-reminder>",
|
||||||
|
{ agent: "atlas" },
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
await managerInternals.flushPendingParentWake("parent-session-alias")
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(promptCalls).toHaveLength(1)
|
||||||
|
expect(promptCalls[0]?.body.agent).toBe("\u200B\u200B\u200B\u200BAtlas - Plan Executor")
|
||||||
|
|
||||||
|
manager.shutdown()
|
||||||
|
resetClaudeCodeSessionState()
|
||||||
|
})
|
||||||
|
|
||||||
test("does not requeue dispatched parent wake when session.error arrives before accepted history is visible", async () => {
|
test("does not requeue dispatched parent wake when session.error arrives before accepted history is visible", async () => {
|
||||||
//#given
|
//#given
|
||||||
const promptCalls: Array<{ path: { id: string }; body: Record<string, unknown> }> = []
|
const promptCalls: Array<{ path: { id: string }; body: Record<string, unknown> }> = []
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import {
|
|||||||
type QueueItem,
|
type QueueItem,
|
||||||
} from "./constants"
|
} from "./constants"
|
||||||
|
|
||||||
import { subagentSessions } from "../claude-code-session-state"
|
import { resolveRegisteredAgentName, subagentSessions } from "../claude-code-session-state"
|
||||||
import { getTaskToastManager } from "../task-toast-manager"
|
import { getTaskToastManager } from "../task-toast-manager"
|
||||||
import { formatDuration } from "./duration-formatter"
|
import { formatDuration } from "./duration-formatter"
|
||||||
import {
|
import {
|
||||||
@@ -1385,13 +1385,20 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
this.observedOutputSessions.add(sessionID)
|
this.observedOutputSessions.add(sessionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
private cloneParentWake(wake: PendingParentWake): PendingParentWake {
|
private resolveParentWakePromptContext(promptContext: ParentWakePromptContext): ParentWakePromptContext {
|
||||||
|
const resolvedAgent = resolveRegisteredAgentName(promptContext.agent)
|
||||||
return {
|
return {
|
||||||
promptContext: {
|
...promptContext,
|
||||||
...wake.promptContext,
|
...(resolvedAgent ? { agent: resolvedAgent } : {}),
|
||||||
...(wake.promptContext.model ? { model: { ...wake.promptContext.model } } : {}),
|
...(promptContext.model ? { model: { ...promptContext.model } } : {}),
|
||||||
...(wake.promptContext.tools ? { tools: { ...wake.promptContext.tools } } : {}),
|
...(promptContext.tools ? { tools: { ...promptContext.tools } } : {}),
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private cloneParentWake(wake: PendingParentWake): PendingParentWake {
|
||||||
|
const promptContext = this.resolveParentWakePromptContext(wake.promptContext)
|
||||||
|
return {
|
||||||
|
promptContext,
|
||||||
notifications: [...wake.notifications],
|
notifications: [...wake.notifications],
|
||||||
shouldReply: wake.shouldReply,
|
shouldReply: wake.shouldReply,
|
||||||
...(wake.dispatchedAt !== undefined ? { dispatchedAt: wake.dispatchedAt } : {}),
|
...(wake.dispatchedAt !== undefined ? { dispatchedAt: wake.dispatchedAt } : {}),
|
||||||
@@ -2692,14 +2699,15 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
shouldReply: boolean,
|
shouldReply: boolean,
|
||||||
delayMs?: number,
|
delayMs?: number,
|
||||||
): void {
|
): void {
|
||||||
|
const resolvedPromptContext = this.resolveParentWakePromptContext(promptContext)
|
||||||
const pendingWake = this.pendingParentWakes.get(sessionID)
|
const pendingWake = this.pendingParentWakes.get(sessionID)
|
||||||
if (pendingWake) {
|
if (pendingWake) {
|
||||||
pendingWake.notifications.push(notification)
|
pendingWake.notifications.push(notification)
|
||||||
pendingWake.promptContext = promptContext
|
pendingWake.promptContext = resolvedPromptContext
|
||||||
pendingWake.shouldReply = pendingWake.shouldReply || shouldReply
|
pendingWake.shouldReply = pendingWake.shouldReply || shouldReply
|
||||||
} else {
|
} else {
|
||||||
this.pendingParentWakes.set(sessionID, {
|
this.pendingParentWakes.set(sessionID, {
|
||||||
promptContext,
|
promptContext: resolvedPromptContext,
|
||||||
notifications: [notification],
|
notifications: [notification],
|
||||||
shouldReply,
|
shouldReply,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user