Files
oh-my-opencode/src/tools/delegate-task/sync-task.ts
T

186 lines
5.7 KiB
TypeScript
Raw Normal View History

import type { ModelFallbackInfo } from "../../features/task-toast-manager/types"
import type { DelegateTaskArgs, ToolContextWithMetadata, DelegatedModelConfig } from "./types"
import type { ExecutorContext, ParentContext } from "./executor-types"
import { getTaskToastManager } from "../../features/task-toast-manager"
import { storeToolMetadata } from "../../features/tool-metadata-store"
import { subagentSessions, syncSubagentSessions, setSessionAgent } from "../../features/claude-code-session-state"
import { log } from "../../shared/logger"
import { SessionCategoryRegistry } from "../../shared/session-category-registry"
import { formatDuration } from "./time-formatter"
import { formatDetailedError } from "./error-formatting"
import { syncTaskDeps, type SyncTaskDeps } from "./sync-task-deps"
2026-02-20 00:02:17 +02:00
import { setSessionFallbackChain, clearSessionFallbackChain } from "../../hooks/model-fallback/hook"
export async function executeSyncTask(
args: DelegateTaskArgs,
ctx: ToolContextWithMetadata,
executorCtx: ExecutorContext,
parentContext: ParentContext,
agentToUse: string,
categoryModel: DelegatedModelConfig | undefined,
systemContent: string | undefined,
modelInfo?: ModelFallbackInfo,
2026-02-20 00:02:17 +02:00
fallbackChain?: import("../../shared/model-requirements").FallbackEntry[],
deps: SyncTaskDeps = syncTaskDeps
): Promise<string> {
2026-03-11 17:46:04 +09:00
const { manager, client, directory, onSyncSessionCreated, syncPollTimeoutMs } = executorCtx
const toastManager = getTaskToastManager()
let taskId: string | undefined
let syncSessionID: string | undefined
let spawnReservation:
| Awaited<ReturnType<ExecutorContext["manager"]["reserveSubagentSpawn"]>>
| undefined
try {
if (typeof manager?.reserveSubagentSpawn === "function") {
spawnReservation = await manager.reserveSubagentSpawn(parentContext.sessionID)
}
const spawnContext = spawnReservation?.spawnContext
?? (typeof manager?.assertCanSpawn === "function"
? await manager.assertCanSpawn(parentContext.sessionID)
: {
rootSessionID: parentContext.sessionID,
parentDepth: 0,
childDepth: 1,
})
2026-03-11 17:46:04 +09:00
const createSessionResult = await deps.createSyncSession(client, {
parentSessionID: parentContext.sessionID,
agentToUse,
description: args.description,
defaultDirectory: directory,
})
if (!createSessionResult.ok) {
spawnReservation?.rollback()
return createSessionResult.error
}
const sessionID = createSessionResult.sessionID
spawnReservation?.commit()
syncSessionID = sessionID
subagentSessions.add(sessionID)
syncSubagentSessions.add(sessionID)
setSessionAgent(sessionID, agentToUse)
2026-02-20 00:02:17 +02:00
setSessionFallbackChain(sessionID, fallbackChain)
if (args.category) {
SessionCategoryRegistry.register(sessionID, args.category)
}
if (onSyncSessionCreated) {
log("[task] Invoking onSyncSessionCreated callback", { sessionID, parentID: parentContext.sessionID })
await onSyncSessionCreated({
sessionID,
parentID: parentContext.sessionID,
title: args.description,
}).catch((err) => {
log("[task] onSyncSessionCreated callback failed", { error: String(err) })
})
await new Promise(r => setTimeout(r, 200))
}
taskId = `sync_${sessionID.slice(0, 8)}`
const startTime = new Date()
if (toastManager) {
toastManager.addTask({
id: taskId,
sessionID,
description: args.description,
agent: agentToUse,
isBackground: false,
category: args.category,
skills: args.load_skills,
modelInfo,
})
}
const syncTaskMeta = {
title: args.description,
metadata: {
prompt: args.prompt,
agent: agentToUse,
category: args.category,
load_skills: args.load_skills,
description: args.description,
run_in_background: args.run_in_background,
sessionId: sessionID,
sync: true,
2026-03-11 17:46:04 +09:00
spawnDepth: spawnContext.childDepth,
command: args.command,
model: categoryModel ? { providerID: categoryModel.providerID, modelID: categoryModel.modelID } : undefined,
},
}
await ctx.metadata?.(syncTaskMeta)
if (ctx.callID) {
storeToolMetadata(ctx.sessionID, ctx.callID, syncTaskMeta)
}
const promptError = await deps.sendSyncPrompt(client, {
sessionID,
agentToUse,
args,
systemContent,
categoryModel,
toastManager,
taskId,
})
if (promptError) {
return promptError
}
try {
const pollError = await deps.pollSyncSession(ctx, client, {
sessionID,
agentToUse,
toastManager,
taskId,
}, syncPollTimeoutMs)
if (pollError) {
return pollError
}
const result = await deps.fetchSyncResult(client, sessionID)
if (!result.ok) {
return result.error
}
const duration = formatDuration(startTime)
return `Task completed in ${duration}.
Agent: ${agentToUse}${args.category ? ` (category: ${args.category})` : ""}
---
${result.textContent || "(No text output)"}
<task_metadata>
session_id: ${sessionID}
</task_metadata>`
} finally {
if (toastManager && taskId !== undefined) {
toastManager.removeTask(taskId)
}
}
} catch (error) {
spawnReservation?.rollback()
return formatDetailedError(error, {
operation: "Execute task",
args,
sessionID: syncSessionID,
agent: agentToUse,
category: args.category,
})
} finally {
if (syncSessionID) {
subagentSessions.delete(syncSessionID)
syncSubagentSessions.delete(syncSessionID)
2026-02-20 00:02:17 +02:00
clearSessionFallbackChain(syncSessionID)
SessionCategoryRegistry.remove(syncSessionID)
}
}
}