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:
YeonGyu-Kim
2026-01-08 23:01:23 +09:00
parent 9481770a39
commit 1e239e6155
5 changed files with 22 additions and 2 deletions

View File

@@ -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 }],
},

View File

@@ -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
}

View File

@@ -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 },

View File

@@ -74,6 +74,7 @@ export function createBackgroundTask(manager: BackgroundManager): ToolDefinition
parentSessionID: ctx.sessionID,
parentMessageID: ctx.messageID,
parentModel,
parentAgent: prevMessage?.agent,
})
ctx.metadata?.({

View File

@@ -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}`
}