refactor(features/background-agent): remove dead code submodules
Delete 15 unused files in background-agent module: - background-task-completer.ts - format-duration.ts - message-dir.ts - parent-session-context-resolver.ts - parent-session-notifier.ts (and its test file) - result-handler-context.ts - result-handler.ts - session-output-validator.ts - session-task-cleanup.ts - session-todo-checker.ts - spawner/background-session-creator.ts - spawner/concurrency-key-from-launch-input.ts - spawner/spawner-context.ts - spawner/tmux-callback-invoker.ts Update index.ts barrel and manager.ts/spawner.ts imports Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
import type { BackgroundTask } from "./types"
|
||||
import type { ResultHandlerContext } from "./result-handler-context"
|
||||
import { log } from "../../shared"
|
||||
import { notifyParentSession } from "./parent-session-notifier"
|
||||
|
||||
export async function tryCompleteTask(
|
||||
task: BackgroundTask,
|
||||
source: string,
|
||||
ctx: ResultHandlerContext
|
||||
): Promise<boolean> {
|
||||
const { concurrencyManager, state } = ctx
|
||||
|
||||
if (task.status !== "running") {
|
||||
log("[background-agent] Task already completed, skipping:", {
|
||||
taskId: task.id,
|
||||
status: task.status,
|
||||
source,
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
task.status = "completed"
|
||||
task.completedAt = new Date()
|
||||
|
||||
if (task.concurrencyKey) {
|
||||
concurrencyManager.release(task.concurrencyKey)
|
||||
task.concurrencyKey = undefined
|
||||
}
|
||||
|
||||
state.markForNotification(task)
|
||||
|
||||
try {
|
||||
await notifyParentSession(task, ctx)
|
||||
log(`[background-agent] Task completed via ${source}:`, task.id)
|
||||
} catch (error) {
|
||||
log("[background-agent] Error in notifyParentSession:", { taskId: task.id, error })
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
export function formatDuration(start: Date, end?: Date): string {
|
||||
const duration = (end ?? new Date()).getTime() - start.getTime()
|
||||
const seconds = Math.floor(duration / 1000)
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const hours = Math.floor(minutes / 60)
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}h ${minutes % 60}m ${seconds % 60}s`
|
||||
}
|
||||
if (minutes > 0) {
|
||||
return `${minutes}m ${seconds % 60}s`
|
||||
}
|
||||
return `${seconds}s`
|
||||
}
|
||||
@@ -1,5 +1,2 @@
|
||||
export * from "./types"
|
||||
export { BackgroundManager, type SubagentSessionCreatedEvent, type OnSubagentSessionCreated } from "./manager"
|
||||
export { TaskHistory, type TaskHistoryEntry } from "./task-history"
|
||||
export { ConcurrencyManager } from "./concurrency"
|
||||
export { TaskStateManager } from "./state"
|
||||
|
||||
@@ -268,7 +268,7 @@ export class BackgroundManager {
|
||||
body: {
|
||||
parentID: input.parentSessionID,
|
||||
title: `${input.description} (@${input.agent} subagent)`,
|
||||
} as any,
|
||||
} as Record<string, unknown>,
|
||||
query: {
|
||||
directory: parentDirectory,
|
||||
},
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { getMessageDir } from "../../shared"
|
||||
@@ -1,81 +0,0 @@
|
||||
import type { OpencodeClient } from "./constants"
|
||||
import type { BackgroundTask } from "./types"
|
||||
import { findNearestMessageWithFields } from "../hook-message-injector"
|
||||
import { getMessageDir } from "../../shared"
|
||||
import { normalizePromptTools, resolveInheritedPromptTools } from "../../shared"
|
||||
|
||||
type AgentModel = { providerID: string; modelID: string }
|
||||
|
||||
function isObject(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null
|
||||
}
|
||||
|
||||
function extractAgentAndModelFromMessage(message: unknown): {
|
||||
agent?: string
|
||||
model?: AgentModel
|
||||
tools?: Record<string, boolean>
|
||||
} {
|
||||
if (!isObject(message)) return {}
|
||||
const info = message["info"]
|
||||
if (!isObject(info)) return {}
|
||||
|
||||
const agent = typeof info["agent"] === "string" ? info["agent"] : undefined
|
||||
const modelObj = info["model"]
|
||||
const tools = normalizePromptTools(isObject(info["tools"]) ? info["tools"] as Record<string, unknown> as Record<string, boolean | "allow" | "deny" | "ask"> : undefined)
|
||||
if (isObject(modelObj)) {
|
||||
const providerID = modelObj["providerID"]
|
||||
const modelID = modelObj["modelID"]
|
||||
if (typeof providerID === "string" && typeof modelID === "string") {
|
||||
return { agent, model: { providerID, modelID }, tools }
|
||||
}
|
||||
}
|
||||
|
||||
const providerID = info["providerID"]
|
||||
const modelID = info["modelID"]
|
||||
if (typeof providerID === "string" && typeof modelID === "string") {
|
||||
return { agent, model: { providerID, modelID }, tools }
|
||||
}
|
||||
|
||||
return { agent, tools }
|
||||
}
|
||||
|
||||
export async function resolveParentSessionAgentAndModel(input: {
|
||||
client: OpencodeClient
|
||||
task: BackgroundTask
|
||||
}): Promise<{ agent?: string; model?: AgentModel; tools?: Record<string, boolean> }> {
|
||||
const { client, task } = input
|
||||
|
||||
let agent: string | undefined = task.parentAgent
|
||||
let model: AgentModel | undefined
|
||||
let tools: Record<string, boolean> | undefined = task.parentTools
|
||||
|
||||
try {
|
||||
const messagesResp = await client.session.messages({
|
||||
path: { id: task.parentSessionID },
|
||||
})
|
||||
|
||||
const messagesRaw = "data" in messagesResp ? messagesResp.data : []
|
||||
const messages = Array.isArray(messagesRaw) ? messagesRaw : []
|
||||
|
||||
for (let i = messages.length - 1; i >= 0; i--) {
|
||||
const extracted = extractAgentAndModelFromMessage(messages[i])
|
||||
if (extracted.agent || extracted.model || extracted.tools) {
|
||||
agent = extracted.agent ?? task.parentAgent
|
||||
model = extracted.model
|
||||
tools = extracted.tools ?? tools
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
const messageDir = getMessageDir(task.parentSessionID)
|
||||
const currentMessage = messageDir ? findNearestMessageWithFields(messageDir) : null
|
||||
agent = currentMessage?.agent ?? task.parentAgent
|
||||
model =
|
||||
currentMessage?.model?.providerID && currentMessage?.model?.modelID
|
||||
? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID }
|
||||
: undefined
|
||||
tools = normalizePromptTools(currentMessage?.tools) ?? tools
|
||||
}
|
||||
|
||||
return { agent, model, tools: resolveInheritedPromptTools(task.parentSessionID, tools) }
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
declare const require: (name: string) => any
|
||||
const { describe, test, expect } = require("bun:test")
|
||||
import type { BackgroundTask } from "./types"
|
||||
import { buildBackgroundTaskNotificationText } from "./background-task-notification-template"
|
||||
|
||||
describe("notifyParentSession", () => {
|
||||
test("displays INTERRUPTED for interrupted tasks", () => {
|
||||
// given
|
||||
const task: BackgroundTask = {
|
||||
id: "test-task",
|
||||
parentSessionID: "parent-session",
|
||||
parentMessageID: "parent-message",
|
||||
description: "Test task",
|
||||
prompt: "Test prompt",
|
||||
agent: "test-agent",
|
||||
status: "interrupt",
|
||||
startedAt: new Date(),
|
||||
completedAt: new Date(),
|
||||
}
|
||||
const duration = "1s"
|
||||
const statusText = task.status === "completed" ? "COMPLETED" : task.status === "interrupt" ? "INTERRUPTED" : "CANCELLED"
|
||||
const allComplete = false
|
||||
const remainingCount = 1
|
||||
const completedTasks: BackgroundTask[] = []
|
||||
|
||||
// when
|
||||
const notification = buildBackgroundTaskNotificationText({
|
||||
task,
|
||||
duration,
|
||||
statusText,
|
||||
allComplete,
|
||||
remainingCount,
|
||||
completedTasks,
|
||||
})
|
||||
|
||||
// then
|
||||
expect(notification).toContain("INTERRUPTED")
|
||||
})
|
||||
})
|
||||
@@ -1,103 +0,0 @@
|
||||
import type { BackgroundTask } from "./types"
|
||||
import type { ResultHandlerContext } from "./result-handler-context"
|
||||
import { TASK_CLEANUP_DELAY_MS } from "./constants"
|
||||
import { createInternalAgentTextPart, log } from "../../shared"
|
||||
import { getTaskToastManager } from "../task-toast-manager"
|
||||
import { formatDuration } from "./duration-formatter"
|
||||
import { buildBackgroundTaskNotificationText } from "./background-task-notification-template"
|
||||
import { resolveParentSessionAgentAndModel } from "./parent-session-context-resolver"
|
||||
|
||||
export async function notifyParentSession(
|
||||
task: BackgroundTask,
|
||||
ctx: ResultHandlerContext
|
||||
): Promise<void> {
|
||||
const { client, state } = ctx
|
||||
|
||||
const duration = formatDuration(task.startedAt ?? task.completedAt ?? new Date(), task.completedAt)
|
||||
log("[background-agent] notifyParentSession called for task:", task.id)
|
||||
|
||||
const toastManager = getTaskToastManager()
|
||||
if (toastManager) {
|
||||
toastManager.showCompletionToast({
|
||||
id: task.id,
|
||||
description: task.description,
|
||||
duration,
|
||||
})
|
||||
}
|
||||
|
||||
const pendingSet = state.pendingByParent.get(task.parentSessionID)
|
||||
if (pendingSet) {
|
||||
pendingSet.delete(task.id)
|
||||
if (pendingSet.size === 0) {
|
||||
state.pendingByParent.delete(task.parentSessionID)
|
||||
}
|
||||
}
|
||||
|
||||
const allComplete = !pendingSet || pendingSet.size === 0
|
||||
const remainingCount = pendingSet?.size ?? 0
|
||||
|
||||
const statusText = task.status === "completed" ? "COMPLETED" : task.status === "interrupt" ? "INTERRUPTED" : "CANCELLED"
|
||||
|
||||
const completedTasks = allComplete
|
||||
? Array.from(state.tasks.values()).filter(
|
||||
(t) =>
|
||||
t.parentSessionID === task.parentSessionID &&
|
||||
t.status !== "running" &&
|
||||
t.status !== "pending"
|
||||
)
|
||||
: []
|
||||
|
||||
const notification = buildBackgroundTaskNotificationText({
|
||||
task,
|
||||
duration,
|
||||
statusText,
|
||||
allComplete,
|
||||
remainingCount,
|
||||
completedTasks,
|
||||
})
|
||||
|
||||
const { agent, model, tools } = await resolveParentSessionAgentAndModel({ client, task })
|
||||
|
||||
log("[background-agent] notifyParentSession context:", {
|
||||
taskId: task.id,
|
||||
resolvedAgent: agent,
|
||||
resolvedModel: model,
|
||||
})
|
||||
|
||||
try {
|
||||
await client.session.promptAsync({
|
||||
path: { id: task.parentSessionID },
|
||||
body: {
|
||||
noReply: !allComplete,
|
||||
...(agent !== undefined ? { agent } : {}),
|
||||
...(model !== undefined ? { model } : {}),
|
||||
...(tools ? { tools } : {}),
|
||||
parts: [createInternalAgentTextPart(notification)],
|
||||
},
|
||||
})
|
||||
|
||||
log("[background-agent] Sent notification to parent session:", {
|
||||
taskId: task.id,
|
||||
allComplete,
|
||||
noReply: !allComplete,
|
||||
})
|
||||
} catch (error) {
|
||||
log("[background-agent] Failed to send notification:", error)
|
||||
}
|
||||
|
||||
if (!allComplete) return
|
||||
|
||||
for (const completedTask of completedTasks) {
|
||||
const taskId = completedTask.id
|
||||
state.clearCompletionTimer(taskId)
|
||||
const timer = setTimeout(() => {
|
||||
state.completionTimers.delete(taskId)
|
||||
if (state.tasks.has(taskId)) {
|
||||
state.clearNotificationsForTask(taskId)
|
||||
state.tasks.delete(taskId)
|
||||
log("[background-agent] Removed completed task from memory:", taskId)
|
||||
}
|
||||
}, TASK_CLEANUP_DELAY_MS)
|
||||
state.setCompletionTimer(taskId, timer)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { OpencodeClient } from "./constants"
|
||||
import type { ConcurrencyManager } from "./concurrency"
|
||||
import type { TaskStateManager } from "./state"
|
||||
|
||||
export interface ResultHandlerContext {
|
||||
client: OpencodeClient
|
||||
concurrencyManager: ConcurrencyManager
|
||||
state: TaskStateManager
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export type { ResultHandlerContext } from "./result-handler-context"
|
||||
export { formatDuration } from "./duration-formatter"
|
||||
export { getMessageDir } from "../../shared"
|
||||
export { checkSessionTodos } from "./session-todo-checker"
|
||||
export { validateSessionHasOutput } from "./session-output-validator"
|
||||
export { tryCompleteTask } from "./background-task-completer"
|
||||
export { notifyParentSession } from "./parent-session-notifier"
|
||||
@@ -1,89 +0,0 @@
|
||||
import type { OpencodeClient } from "./constants"
|
||||
import { log } from "../../shared"
|
||||
|
||||
type SessionMessagePart = {
|
||||
type?: string
|
||||
text?: string
|
||||
content?: unknown
|
||||
}
|
||||
|
||||
function isObject(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null
|
||||
}
|
||||
|
||||
function getMessageRole(message: unknown): string | undefined {
|
||||
if (!isObject(message)) return undefined
|
||||
const info = message["info"]
|
||||
if (!isObject(info)) return undefined
|
||||
const role = info["role"]
|
||||
return typeof role === "string" ? role : undefined
|
||||
}
|
||||
|
||||
function getMessageParts(message: unknown): SessionMessagePart[] {
|
||||
if (!isObject(message)) return []
|
||||
const parts = message["parts"]
|
||||
if (!Array.isArray(parts)) return []
|
||||
|
||||
return parts
|
||||
.filter((part): part is SessionMessagePart => isObject(part))
|
||||
.map((part) => ({
|
||||
type: typeof part["type"] === "string" ? part["type"] : undefined,
|
||||
text: typeof part["text"] === "string" ? part["text"] : undefined,
|
||||
content: part["content"],
|
||||
}))
|
||||
}
|
||||
|
||||
function partHasContent(part: SessionMessagePart): boolean {
|
||||
if (part.type === "text" || part.type === "reasoning") {
|
||||
return Boolean(part.text && part.text.trim().length > 0)
|
||||
}
|
||||
if (part.type === "tool") return true
|
||||
if (part.type === "tool_result") {
|
||||
if (typeof part.content === "string") return part.content.trim().length > 0
|
||||
if (Array.isArray(part.content)) return part.content.length > 0
|
||||
return Boolean(part.content)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export async function validateSessionHasOutput(
|
||||
client: OpencodeClient,
|
||||
sessionID: string
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const response = await client.session.messages({
|
||||
path: { id: sessionID },
|
||||
})
|
||||
|
||||
const messagesRaw =
|
||||
isObject(response) && "data" in response ? (response as { data?: unknown }).data : response
|
||||
const messages = Array.isArray(messagesRaw) ? messagesRaw : []
|
||||
|
||||
const hasAssistantOrToolMessage = messages.some((message) => {
|
||||
const role = getMessageRole(message)
|
||||
return role === "assistant" || role === "tool"
|
||||
})
|
||||
|
||||
if (!hasAssistantOrToolMessage) {
|
||||
log("[background-agent] No assistant/tool messages found in session:", sessionID)
|
||||
return false
|
||||
}
|
||||
|
||||
const hasContent = messages.some((message) => {
|
||||
const role = getMessageRole(message)
|
||||
if (role !== "assistant" && role !== "tool") return false
|
||||
const parts = getMessageParts(message)
|
||||
return parts.some(partHasContent)
|
||||
})
|
||||
|
||||
if (!hasContent) {
|
||||
log("[background-agent] Messages exist but no content found in session:", sessionID)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
log("[background-agent] Error validating session output:", error)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { subagentSessions } from "../claude-code-session-state"
|
||||
import type { BackgroundTask } from "./types"
|
||||
|
||||
export function cleanupTaskAfterSessionEnds(args: {
|
||||
task: BackgroundTask
|
||||
tasks: Map<string, BackgroundTask>
|
||||
idleDeferralTimers: Map<string, ReturnType<typeof setTimeout>>
|
||||
completionTimers: Map<string, ReturnType<typeof setTimeout>>
|
||||
cleanupPendingByParent: (task: BackgroundTask) => void
|
||||
clearNotificationsForTask: (taskId: string) => void
|
||||
releaseConcurrencyKey?: (key: string) => void
|
||||
}): void {
|
||||
const {
|
||||
task,
|
||||
tasks,
|
||||
idleDeferralTimers,
|
||||
completionTimers,
|
||||
cleanupPendingByParent,
|
||||
clearNotificationsForTask,
|
||||
releaseConcurrencyKey,
|
||||
} = args
|
||||
|
||||
const completionTimer = completionTimers.get(task.id)
|
||||
if (completionTimer) {
|
||||
clearTimeout(completionTimer)
|
||||
completionTimers.delete(task.id)
|
||||
}
|
||||
|
||||
const idleTimer = idleDeferralTimers.get(task.id)
|
||||
if (idleTimer) {
|
||||
clearTimeout(idleTimer)
|
||||
idleDeferralTimers.delete(task.id)
|
||||
}
|
||||
|
||||
if (task.concurrencyKey && releaseConcurrencyKey) {
|
||||
releaseConcurrencyKey(task.concurrencyKey)
|
||||
task.concurrencyKey = undefined
|
||||
}
|
||||
|
||||
cleanupPendingByParent(task)
|
||||
clearNotificationsForTask(task.id)
|
||||
tasks.delete(task.id)
|
||||
if (task.sessionID) {
|
||||
subagentSessions.delete(task.sessionID)
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import type { OpencodeClient, Todo } from "./constants"
|
||||
|
||||
function isTodo(value: unknown): value is Todo {
|
||||
if (typeof value !== "object" || value === null) return false
|
||||
const todo = value as Record<string, unknown>
|
||||
return (
|
||||
(typeof todo["id"] === "string" || todo["id"] === undefined) &&
|
||||
typeof todo["content"] === "string" &&
|
||||
typeof todo["status"] === "string" &&
|
||||
typeof todo["priority"] === "string"
|
||||
)
|
||||
}
|
||||
|
||||
export async function checkSessionTodos(
|
||||
client: OpencodeClient,
|
||||
sessionID: string
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const response = await client.session.todo({
|
||||
path: { id: sessionID },
|
||||
})
|
||||
|
||||
const todosRaw = "data" in response ? response.data : response
|
||||
if (!Array.isArray(todosRaw) || todosRaw.length === 0) return false
|
||||
|
||||
const incomplete = todosRaw
|
||||
.filter(isTodo)
|
||||
.filter((todo) => todo.status !== "completed" && todo.status !== "cancelled")
|
||||
return incomplete.length > 0
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -61,9 +61,7 @@ export async function startTask(
|
||||
const createResult = await client.session.create({
|
||||
body: {
|
||||
parentID: input.parentSessionID,
|
||||
title: `Background: ${input.description}`,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any,
|
||||
} as Record<string, unknown>,
|
||||
query: {
|
||||
directory: parentDirectory,
|
||||
},
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import type { OpencodeClient } from "../constants"
|
||||
import type { ConcurrencyManager } from "../concurrency"
|
||||
import type { LaunchInput } from "../types"
|
||||
import { log } from "../../../shared"
|
||||
|
||||
export async function createBackgroundSession(options: {
|
||||
client: OpencodeClient
|
||||
input: LaunchInput
|
||||
parentDirectory: string
|
||||
concurrencyManager: ConcurrencyManager
|
||||
concurrencyKey: string
|
||||
}): Promise<string> {
|
||||
const { client, input, parentDirectory, concurrencyManager, concurrencyKey } = options
|
||||
|
||||
const body = {
|
||||
parentID: input.parentSessionID,
|
||||
title: `Background: ${input.description}`,
|
||||
}
|
||||
|
||||
const createResult = await client.session
|
||||
.create({
|
||||
body,
|
||||
query: {
|
||||
directory: parentDirectory,
|
||||
},
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
concurrencyManager.release(concurrencyKey)
|
||||
throw error
|
||||
})
|
||||
|
||||
if (createResult.error) {
|
||||
concurrencyManager.release(concurrencyKey)
|
||||
throw new Error(`Failed to create background session: ${createResult.error}`)
|
||||
}
|
||||
|
||||
if (!createResult.data?.id) {
|
||||
concurrencyManager.release(concurrencyKey)
|
||||
throw new Error("Failed to create background session: API returned no session ID")
|
||||
}
|
||||
|
||||
const sessionID = createResult.data.id
|
||||
log("[background-agent] Background session created", { sessionID })
|
||||
return sessionID
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { LaunchInput } from "../types"
|
||||
|
||||
export function getConcurrencyKeyFromLaunchInput(input: LaunchInput): string {
|
||||
return input.model
|
||||
? `${input.model.providerID}/${input.model.modelID}`
|
||||
: input.agent
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import type { BackgroundTask } from "../types"
|
||||
import type { ConcurrencyManager } from "../concurrency"
|
||||
import type { OpencodeClient, OnSubagentSessionCreated } from "../constants"
|
||||
|
||||
export interface SpawnerContext {
|
||||
client: OpencodeClient
|
||||
directory: string
|
||||
concurrencyManager: ConcurrencyManager
|
||||
tmuxEnabled: boolean
|
||||
onSubagentSessionCreated?: OnSubagentSessionCreated
|
||||
onTaskError: (task: BackgroundTask, error: Error) => void
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import { setTimeout } from "timers/promises"
|
||||
import type { OnSubagentSessionCreated } from "../constants"
|
||||
import { TMUX_CALLBACK_DELAY_MS } from "../constants"
|
||||
import { log } from "../../../shared"
|
||||
import { isInsideTmux } from "../../../shared/tmux"
|
||||
|
||||
export async function maybeInvokeTmuxCallback(options: {
|
||||
onSubagentSessionCreated?: OnSubagentSessionCreated
|
||||
tmuxEnabled: boolean
|
||||
sessionID: string
|
||||
parentID: string
|
||||
title: string
|
||||
}): Promise<void> {
|
||||
const { onSubagentSessionCreated, tmuxEnabled, sessionID, parentID, title } = options
|
||||
|
||||
log("[background-agent] tmux callback check", {
|
||||
hasCallback: !!onSubagentSessionCreated,
|
||||
tmuxEnabled,
|
||||
isInsideTmux: isInsideTmux(),
|
||||
sessionID,
|
||||
parentID,
|
||||
})
|
||||
|
||||
if (!onSubagentSessionCreated || !tmuxEnabled || !isInsideTmux()) {
|
||||
log("[background-agent] SKIP tmux callback - conditions not met")
|
||||
return
|
||||
}
|
||||
|
||||
log("[background-agent] Invoking tmux callback NOW", { sessionID })
|
||||
await onSubagentSessionCreated({
|
||||
sessionID,
|
||||
parentID,
|
||||
title,
|
||||
}).catch((error: unknown) => {
|
||||
log("[background-agent] Failed to spawn tmux pane:", error)
|
||||
})
|
||||
|
||||
log("[background-agent] tmux callback completed, waiting")
|
||||
await setTimeout(TMUX_CALLBACK_DELAY_MS)
|
||||
}
|
||||
Reference in New Issue
Block a user