Files
oh-my-opencode/src/cli/run/completion.ts
T

115 lines
2.9 KiB
TypeScript
Raw Normal View History

import pc from "picocolors"
import type { RunContext, Todo, ChildSession, SessionStatus } from "./types"
import { normalizeSDKResponse } from "../../shared"
import { getContinuationState } from "./continuation-state"
export async function checkCompletionConditions(ctx: RunContext): Promise<boolean> {
try {
if (!await areAllTodosComplete(ctx)) {
return false
}
if (!await areAllChildrenIdle(ctx)) {
return false
}
if (!areContinuationHooksIdle(ctx)) {
return false
}
return true
} catch (err) {
console.error(pc.red(`[completion] API error: ${err}`))
return false
}
}
function areContinuationHooksIdle(ctx: RunContext): boolean {
const continuationState = getContinuationState(ctx.directory, ctx.sessionID)
if (continuationState.hasActiveBoulder) {
console.log(pc.dim(" Waiting: boulder continuation is active"))
return false
}
if (continuationState.hasActiveRalphLoop) {
console.log(pc.dim(" Waiting: ralph-loop continuation is active"))
return false
}
return true
}
async function areAllTodosComplete(ctx: RunContext): Promise<boolean> {
const todosRes = await ctx.client.session.todo({
path: { id: ctx.sessionID },
query: { directory: ctx.directory },
})
const todos = normalizeSDKResponse(todosRes, [] as Todo[])
const incompleteTodos = todos.filter(
(t) => t.status !== "completed" && t.status !== "cancelled"
)
if (incompleteTodos.length > 0) {
console.log(pc.dim(` Waiting: ${incompleteTodos.length} todos remaining`))
return false
}
return true
}
async function areAllChildrenIdle(ctx: RunContext): Promise<boolean> {
const allStatuses = await fetchAllStatuses(ctx)
return areAllDescendantsIdle(ctx, ctx.sessionID, allStatuses)
}
async function fetchAllStatuses(
ctx: RunContext
): Promise<Record<string, SessionStatus>> {
const statusRes = await ctx.client.session.status({
query: { directory: ctx.directory },
})
return normalizeSDKResponse(statusRes, {} as Record<string, SessionStatus>)
}
async function areAllDescendantsIdle(
ctx: RunContext,
sessionID: string,
allStatuses: Record<string, SessionStatus>
): Promise<boolean> {
const childrenRes = await ctx.client.session.children({
path: { id: sessionID },
query: { directory: ctx.directory },
})
const children = normalizeSDKResponse(childrenRes, [] as ChildSession[])
for (const child of children) {
const status = allStatuses[child.id]
if (!status) {
console.log(
pc.dim(` Waiting: session ${child.id.slice(0, 8)}... status unknown`)
)
return false
}
if (status.type !== "idle") {
console.log(
pc.dim(` Waiting: session ${child.id.slice(0, 8)}... is ${status.type}`)
)
return false
}
const descendantsIdle = await areAllDescendantsIdle(
ctx,
child.id,
allStatuses
)
if (!descendantsIdle) {
return false
}
}
return true
}