Files
oh-my-openagent/src/hooks/ralph-loop/ralph-loop-hook.ts

91 lines
2.7 KiB
TypeScript

import type { PluginInput } from "@opencode-ai/plugin"
import type { RalphLoopOptions, RalphLoopState } from "./types"
import { getTranscriptPath as getDefaultTranscriptPath } from "../claude-code-hooks/transcript"
import { createLoopSessionRecovery } from "./loop-session-recovery"
import { createLoopStateController } from "./loop-state-controller"
import { createRalphLoopEventHandler } from "./ralph-loop-event-handler"
export interface RalphLoopHook {
event: (input: { event: { type: string; properties?: unknown } }) => Promise<void>
startLoop: (
sessionID: string,
prompt: string,
options?: {
maxIterations?: number
completionPromise?: string
messageCountAtStart?: number
ultrawork?: boolean
strategy?: "reset" | "continue"
}
) => boolean
cancelLoop: (sessionID: string) => boolean
getState: () => RalphLoopState | null
}
const DEFAULT_API_TIMEOUT = 5000 as const
function getMessageCountFromResponse(messagesResponse: unknown): number {
if (Array.isArray(messagesResponse)) {
return messagesResponse.length
}
if (typeof messagesResponse === "object" && messagesResponse !== null && "data" in messagesResponse) {
const data = (messagesResponse as { data?: unknown }).data
return Array.isArray(data) ? data.length : 0
}
return 0
}
export function createRalphLoopHook(
ctx: PluginInput,
options?: RalphLoopOptions
): RalphLoopHook {
const config = options?.config
const stateDir = config?.state_dir
const getTranscriptPath = options?.getTranscriptPath ?? getDefaultTranscriptPath
const apiTimeout = options?.apiTimeout ?? DEFAULT_API_TIMEOUT
const checkSessionExists = options?.checkSessionExists
const loopState = createLoopStateController({
directory: ctx.directory,
stateDir,
config,
})
const sessionRecovery = createLoopSessionRecovery()
const event = createRalphLoopEventHandler(ctx, {
directory: ctx.directory,
apiTimeoutMs: apiTimeout,
getTranscriptPath,
checkSessionExists,
sessionRecovery,
loopState,
})
return {
event,
startLoop: (sessionID, prompt, loopOptions): boolean => {
const startSuccess = loopState.startLoop(sessionID, prompt, loopOptions)
if (!startSuccess || typeof loopOptions?.messageCountAtStart === "number") {
return startSuccess
}
ctx.client.session
.messages({
path: { id: sessionID },
query: { directory: ctx.directory },
})
.then((messagesResponse: unknown) => {
const messageCountAtStart = getMessageCountFromResponse(messagesResponse)
loopState.setMessageCountAtStart(sessionID, messageCountAtStart)
})
.catch(() => {})
return startSuccess
},
cancelLoop: loopState.cancelLoop,
getState: loopState.getState as () => RalphLoopState | null,
}
}