feat(background-agent): add parentAgent tracking to preserve agent context in background tasks
- Add parentAgent field to BackgroundTask, LaunchInput, and ResumeInput interfaces
- Pass parentAgent through background task manager to preserve agent identity
- Update sisyphus-orchestrator to set orchestrator-sisyphus agent context
- Add session tracking for background agents to prevent context loss
- Propagate agent context in background-task and sisyphus-task tools
This ensures background/subagent spawned tasks maintain proper agent context for notifications and continuity.
🤖 Generated with assistance of oh-my-opencode
This commit is contained in:
@@ -98,6 +98,7 @@ export class BackgroundManager {
|
||||
lastUpdate: new Date(),
|
||||
},
|
||||
parentModel: input.parentModel,
|
||||
parentAgent: input.parentAgent,
|
||||
model: input.model,
|
||||
concurrencyKey,
|
||||
}
|
||||
@@ -236,6 +237,7 @@ export class BackgroundManager {
|
||||
existingTask.parentSessionID = input.parentSessionID
|
||||
existingTask.parentMessageID = input.parentMessageID
|
||||
existingTask.parentModel = input.parentModel
|
||||
existingTask.parentAgent = input.parentAgent
|
||||
|
||||
existingTask.progress = {
|
||||
toolCalls: existingTask.progress?.toolCalls ?? 0,
|
||||
@@ -438,8 +440,8 @@ export class BackgroundManager {
|
||||
}
|
||||
|
||||
try {
|
||||
// Use only parentModel - don't fallback to prevMessage.model
|
||||
// This prevents accidentally changing parent session's model
|
||||
// Use only parentModel/parentAgent - don't fallback to prevMessage
|
||||
// This prevents accidentally changing parent session's model/agent
|
||||
const modelField = task.parentModel?.providerID && task.parentModel?.modelID
|
||||
? { providerID: task.parentModel.providerID, modelID: task.parentModel.modelID }
|
||||
: undefined
|
||||
@@ -447,6 +449,7 @@ export class BackgroundManager {
|
||||
await this.client.session.prompt({
|
||||
path: { id: task.parentSessionID },
|
||||
body: {
|
||||
agent: task.parentAgent,
|
||||
model: modelField,
|
||||
parts: [{ type: "text", text: message }],
|
||||
},
|
||||
|
||||
@@ -30,6 +30,8 @@ export interface BackgroundTask {
|
||||
model?: { providerID: string; modelID: string }
|
||||
/** Agent name used for concurrency tracking */
|
||||
concurrencyKey?: string
|
||||
/** Parent session's agent name for notification */
|
||||
parentAgent?: string
|
||||
}
|
||||
|
||||
export interface LaunchInput {
|
||||
@@ -39,6 +41,7 @@ export interface LaunchInput {
|
||||
parentSessionID: string
|
||||
parentMessageID: string
|
||||
parentModel?: { providerID: string; modelID: string }
|
||||
parentAgent?: string
|
||||
model?: { providerID: string; modelID: string }
|
||||
skills?: string[]
|
||||
skillContent?: string
|
||||
@@ -50,4 +53,5 @@ export interface ResumeInput {
|
||||
parentSessionID: string
|
||||
parentMessageID: string
|
||||
parentModel?: { providerID: string; modelID: string }
|
||||
parentAgent?: string
|
||||
}
|
||||
|
||||
@@ -352,6 +352,7 @@ export function createSisyphusOrchestratorHook(
|
||||
await ctx.client.session.prompt({
|
||||
path: { id: sessionID },
|
||||
body: {
|
||||
agent: "orchestrator-sisyphus",
|
||||
parts: [{ type: "text", text: prompt }],
|
||||
},
|
||||
query: { directory: ctx.directory },
|
||||
|
||||
@@ -74,6 +74,7 @@ export function createBackgroundTask(manager: BackgroundManager): ToolDefinition
|
||||
parentSessionID: ctx.sessionID,
|
||||
parentMessageID: ctx.messageID,
|
||||
parentModel,
|
||||
parentAgent: prevMessage?.agent,
|
||||
})
|
||||
|
||||
ctx.metadata?.({
|
||||
|
||||
@@ -9,6 +9,7 @@ import { findNearestMessageWithFields, MESSAGE_STORAGE } from "../../features/ho
|
||||
import { resolveMultipleSkills } from "../../features/opencode-skill-loader/skill-content"
|
||||
import { createBuiltinSkills } from "../../features/builtin-skills/skills"
|
||||
import { getTaskToastManager } from "../../features/task-toast-manager"
|
||||
import { subagentSessions } from "../../features/claude-code-session-state"
|
||||
|
||||
type OpencodeClient = PluginInput["client"]
|
||||
|
||||
@@ -159,6 +160,7 @@ export function createSisyphusTask(options: SisyphusTaskToolOptions): ToolDefini
|
||||
parentSessionID: ctx.sessionID,
|
||||
parentMessageID: ctx.messageID,
|
||||
parentModel,
|
||||
parentAgent: prevMessage?.agent,
|
||||
})
|
||||
|
||||
ctx.metadata?.({
|
||||
@@ -325,6 +327,7 @@ ${textContent || "(No text output)"}`
|
||||
parentSessionID: ctx.sessionID,
|
||||
parentMessageID: ctx.messageID,
|
||||
parentModel,
|
||||
parentAgent: prevMessage?.agent,
|
||||
model: categoryModel,
|
||||
skills: args.skills,
|
||||
skillContent: systemContent,
|
||||
@@ -352,6 +355,7 @@ System notifies on completion. Use \`background_output\` with task_id="${task.id
|
||||
|
||||
const toastManager = getTaskToastManager()
|
||||
let taskId: string | undefined
|
||||
let syncSessionID: string | undefined
|
||||
|
||||
try {
|
||||
const createResult = await client.session.create({
|
||||
@@ -366,6 +370,8 @@ System notifies on completion. Use \`background_output\` with task_id="${task.id
|
||||
}
|
||||
|
||||
const sessionID = createResult.data.id
|
||||
syncSessionID = sessionID
|
||||
subagentSessions.add(sessionID)
|
||||
taskId = `sync_${sessionID.slice(0, 8)}`
|
||||
const startTime = new Date()
|
||||
|
||||
@@ -461,6 +467,8 @@ System notifies on completion. Use \`background_output\` with task_id="${task.id
|
||||
toastManager.removeTask(taskId)
|
||||
}
|
||||
|
||||
subagentSessions.delete(sessionID)
|
||||
|
||||
return `Task completed in ${duration}.
|
||||
|
||||
Agent: ${agentToUse}${args.category ? ` (category: ${args.category})` : ""}
|
||||
@@ -473,6 +481,9 @@ ${textContent || "(No text output)"}`
|
||||
if (toastManager && taskId !== undefined) {
|
||||
toastManager.removeTask(taskId)
|
||||
}
|
||||
if (syncSessionID) {
|
||||
subagentSessions.delete(syncSessionID)
|
||||
}
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
return `❌ Task failed: ${message}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user