91 lines
2.7 KiB
TypeScript
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,
|
|
}
|
|
}
|