2026-02-08 16:25:25 +09:00
import type { PluginContext } from "./types"
2026-03-06 22:37:41 +09:00
import { randomUUID } from "node:crypto"
2026-02-08 16:25:25 +09:00
import { getMainSessionID } from "../features/claude-code-session-state"
import { clearBoulderState } from "../features/boulder-state"
import { log } from "../shared"
2026-02-13 12:02:40 +09:00
import { resolveSessionAgent } from "./session-agent-resolver"
2026-02-21 02:27:57 +09:00
import { parseRalphLoopArguments } from "../hooks/ralph-loop/command-arguments"
2026-03-06 22:00:14 +09:00
import { ULTRAWORK_VERIFICATION_PROMISE } from "../hooks/ralph-loop/constants"
2026-03-06 22:37:41 +09:00
import { readState , writeState } from "../hooks/ralph-loop/storage"
2026-02-08 16:25:25 +09:00
import type { CreatedHooks } from "../create-hooks"
export function createToolExecuteBeforeHandler ( args : {
ctx : PluginContext
hooks : CreatedHooks
} ) : (
input : { tool : string ; sessionID : string ; callID : string } ,
output : { args : Record < string , unknown > } ,
) = > Promise < void > {
const { ctx , hooks } = args
return async ( input , output ) : Promise < void > = > {
await hooks . writeExistingFileGuard ? . [ "tool.execute.before" ] ? . ( input , output )
await hooks . questionLabelTruncator ? . [ "tool.execute.before" ] ? . ( input , output )
await hooks . claudeCodeHooks ? . [ "tool.execute.before" ] ? . ( input , output )
await hooks . nonInteractiveEnv ? . [ "tool.execute.before" ] ? . ( input , output )
await hooks . commentChecker ? . [ "tool.execute.before" ] ? . ( input , output )
await hooks . directoryAgentsInjector ? . [ "tool.execute.before" ] ? . ( input , output )
await hooks . directoryReadmeInjector ? . [ "tool.execute.before" ] ? . ( input , output )
await hooks . rulesInjector ? . [ "tool.execute.before" ] ? . ( input , output )
await hooks . tasksTodowriteDisabler ? . [ "tool.execute.before" ] ? . ( input , output )
await hooks . prometheusMdOnly ? . [ "tool.execute.before" ] ? . ( input , output )
await hooks . sisyphusJuniorNotepad ? . [ "tool.execute.before" ] ? . ( input , output )
await hooks . atlasHook ? . [ "tool.execute.before" ] ? . ( input , output )
2026-02-21 15:49:23 -07:00
const normalizedToolName = input . tool . toLowerCase ( )
if (
normalizedToolName === "question"
|| normalizedToolName === "ask_user_question"
|| normalizedToolName === "askuserquestion"
) {
2026-02-21 16:42:23 -07:00
const sessionID = input . sessionID || getMainSessionID ( )
2026-02-21 15:49:23 -07:00
await hooks . sessionNotification ? . ( {
event : {
type : "tool.execute.before" ,
properties : {
2026-02-21 16:42:23 -07:00
sessionID ,
2026-02-21 15:49:23 -07:00
tool : input.tool ,
args : output.args ,
} ,
} ,
} )
}
2026-02-08 16:25:25 +09:00
if ( input . tool === "task" ) {
const argsObject = output . args
const category = typeof argsObject . category === "string" ? argsObject.category : undefined
const subagentType = typeof argsObject . subagent_type === "string" ? argsObject.subagent_type : undefined
2026-02-13 12:02:40 +09:00
const sessionId = typeof argsObject . session_id === "string" ? argsObject.session_id : undefined
if ( category ) {
2026-02-08 16:25:25 +09:00
argsObject . subagent_type = "sisyphus-junior"
2026-02-13 12:02:40 +09:00
} else if ( ! subagentType && sessionId ) {
const resolvedAgent = await resolveSessionAgent ( ctx . client , sessionId )
argsObject . subagent_type = resolvedAgent ? ? "continue"
2026-02-08 16:25:25 +09:00
}
2026-03-06 22:00:14 +09:00
const normalizedSubagentType =
typeof argsObject . subagent_type === "string" ? argsObject.subagent_type : undefined
const prompt = typeof argsObject . prompt === "string" ? argsObject . prompt : ""
const loopState = typeof ctx . directory === "string" ? readState ( ctx . directory ) : null
const shouldInjectOracleVerification =
normalizedSubagentType === "oracle"
&& loopState ? . active === true
&& loopState . ultrawork === true
&& loopState . verification_pending === true
&& loopState . session_id === input . sessionID
if ( shouldInjectOracleVerification ) {
2026-03-06 22:37:41 +09:00
const verificationAttemptId = randomUUID ( )
2026-03-17 16:20:27 +09:00
log ( "[tool-execute-before] Injecting ULW oracle verification attempt" , {
sessionID : input.sessionID ,
callID : input.callID ,
verificationAttemptId ,
loopSessionID : loopState.session_id ,
} )
2026-03-06 22:37:41 +09:00
writeState ( ctx . directory , {
. . . loopState ,
verification_attempt_id : verificationAttemptId ,
verification_session_id : undefined ,
} )
2026-03-06 22:00:14 +09:00
argsObject . run_in_background = false
2026-03-06 22:37:41 +09:00
argsObject . prompt = ` ${ prompt ? ` ${ prompt } \ n \ n ` : "" } You are verifying the active ULTRAWORK loop result for this session. Review whether the original task is truly complete: ${ loopState . prompt } \ n \ nIf the work is fully complete, end your response with <promise> ${ ULTRAWORK_VERIFICATION_PROMISE } </promise>. If the work is not complete, explain the blocking issues clearly and DO NOT emit that promise. \ n \ n<ulw_verification_attempt_id> ${ verificationAttemptId } </ulw_verification_attempt_id> `
2026-03-06 22:00:14 +09:00
}
2026-02-08 16:25:25 +09:00
}
2026-02-18 18:40:10 +08:00
if ( hooks . ralphLoop && input . tool === "skill" ) {
const rawName = typeof output . args . name === "string" ? output.args.name : undefined
const command = rawName ? . replace ( /^\// , "" ) . toLowerCase ( )
2026-02-08 16:25:25 +09:00
const sessionID = input . sessionID || getMainSessionID ( )
if ( command === "ralph-loop" && sessionID ) {
2026-02-18 18:40:10 +08:00
const rawArgs = rawName ? . replace ( /^\/?(ralph-loop)\s*/i , "" ) || ""
2026-02-21 02:27:57 +09:00
const parsedArguments = parseRalphLoopArguments ( rawArgs )
2026-02-08 16:25:25 +09:00
2026-02-21 02:27:57 +09:00
hooks . ralphLoop . startLoop ( sessionID , parsedArguments . prompt , {
maxIterations : parsedArguments.maxIterations ,
completionPromise : parsedArguments.completionPromise ,
strategy : parsedArguments.strategy ,
2026-02-08 16:25:25 +09:00
} )
} else if ( command === "cancel-ralph" && sessionID ) {
hooks . ralphLoop . cancelLoop ( sessionID )
} else if ( command === "ulw-loop" && sessionID ) {
2026-02-18 18:40:10 +08:00
const rawArgs = rawName ? . replace ( /^\/?(ulw-loop)\s*/i , "" ) || ""
2026-02-21 02:27:57 +09:00
const parsedArguments = parseRalphLoopArguments ( rawArgs )
2026-02-08 16:25:25 +09:00
2026-02-21 02:27:57 +09:00
hooks . ralphLoop . startLoop ( sessionID , parsedArguments . prompt , {
2026-02-08 16:25:25 +09:00
ultrawork : true ,
2026-02-21 02:27:57 +09:00
maxIterations : parsedArguments.maxIterations ,
completionPromise : parsedArguments.completionPromise ,
strategy : parsedArguments.strategy ,
2026-02-08 16:25:25 +09:00
} )
}
}
2026-02-18 18:40:10 +08:00
if ( input . tool === "skill" ) {
const rawName = typeof output . args . name === "string" ? output.args.name : undefined
const command = rawName ? . replace ( /^\// , "" ) . toLowerCase ( )
2026-02-08 16:25:25 +09:00
const sessionID = input . sessionID || getMainSessionID ( )
if ( command === "stop-continuation" && sessionID ) {
hooks . stopContinuationGuard ? . stop ( sessionID )
hooks . todoContinuationEnforcer ? . cancelAllCountdowns ( )
hooks . ralphLoop ? . cancelLoop ( sessionID )
clearBoulderState ( ctx . directory )
log ( "[stop-continuation] All continuation mechanisms stopped" , {
sessionID ,
} )
}
}
}
}