2026-01-13 20:55:48 +00:00
import z from "zod"
import path from "path"
import { Tool } from "./tool"
import { Question } from "../question"
import { Session } from "../session"
import { MessageV2 } from "../session/message-v2"
import { Provider } from "../provider/provider"
import { Instance } from "../project/instance"
2026-03-11 23:40:50 +00:00
import { type SessionID , MessageID , PartID } from "../session/schema"
2026-01-13 20:55:48 +00:00
import EXIT_DESCRIPTION from "./plan-exit.txt"
2026-03-11 23:16:56 +00:00
async function getLastModel ( sessionID : SessionID ) {
2026-01-13 20:55:48 +00:00
for await ( const item of MessageV2 . stream ( sessionID ) ) {
if ( item . info . role === "user" && item . info . model ) return item . info . model
}
return Provider . defaultModel ( )
}
export const PlanExitTool = Tool . define ( "plan_exit" , {
description : EXIT_DESCRIPTION ,
parameters : z.object ( { } ) ,
async execute ( _params , ctx ) {
const session = await Session . get ( ctx . sessionID )
const plan = path . relative ( Instance . worktree , Session . plan ( session ) )
const answers = await Question . ask ( {
sessionID : ctx.sessionID ,
questions : [
{
question : ` Plan at ${ plan } is complete. Would you like to switch to the build agent and start implementing? ` ,
header : "Build Agent" ,
custom : false ,
options : [
{ label : "Yes" , description : "Switch to build agent and start implementing the plan" } ,
{ label : "No" , description : "Stay with plan agent to continue refining the plan" } ,
] ,
} ,
] ,
tool : ctx.callID ? { messageID : ctx.messageID , callID : ctx.callID } : undefined ,
} )
const answer = answers [ 0 ] ? . [ 0 ]
if ( answer === "No" ) throw new Question . RejectedError ( )
const model = await getLastModel ( ctx . sessionID )
const userMsg : MessageV2.User = {
2026-03-11 23:30:17 +00:00
id : MessageID.ascending ( ) ,
2026-01-13 20:55:48 +00:00
sessionID : ctx.sessionID ,
role : "user" ,
time : {
created : Date.now ( ) ,
} ,
agent : "build" ,
model ,
}
await Session . updateMessage ( userMsg )
await Session . updatePart ( {
2026-03-11 23:40:50 +00:00
id : PartID.ascending ( ) ,
2026-01-13 20:55:48 +00:00
messageID : userMsg.id ,
sessionID : ctx.sessionID ,
type : "text" ,
text : ` The plan at ${ plan } has been approved, you can now edit files. Execute the plan ` ,
synthetic : true ,
} satisfies MessageV2 . TextPart )
return {
title : "Switching to build agent" ,
output : "User approved switching to build agent. Wait for further instructions." ,
metadata : { } ,
}
} ,
} )
2026-02-25 00:16:17 +00:00
/ *
2026-01-13 20:55:48 +00:00
export const PlanEnterTool = Tool . define ( "plan_enter" , {
description : ENTER_DESCRIPTION ,
parameters : z.object ( { } ) ,
async execute ( _params , ctx ) {
const session = await Session . get ( ctx . sessionID )
const plan = path . relative ( Instance . worktree , Session . plan ( session ) )
const answers = await Question . ask ( {
sessionID : ctx.sessionID ,
questions : [
{
question : ` Would you like to switch to the plan agent and create a plan saved to ${ plan } ? ` ,
header : "Plan Mode" ,
custom : false ,
options : [
{ label : "Yes" , description : "Switch to plan agent for research and planning" } ,
{ label : "No" , description : "Stay with build agent to continue making changes" } ,
] ,
} ,
] ,
tool : ctx.callID ? { messageID : ctx.messageID , callID : ctx.callID } : undefined ,
} )
const answer = answers [ 0 ] ? . [ 0 ]
if ( answer === "No" ) throw new Question . RejectedError ( )
const model = await getLastModel ( ctx . sessionID )
const userMsg : MessageV2.User = {
2026-03-11 23:30:17 +00:00
id : MessageID.ascending ( ) ,
2026-01-13 20:55:48 +00:00
sessionID : ctx.sessionID ,
role : "user" ,
time : {
created : Date.now ( ) ,
} ,
agent : "plan" ,
model ,
}
await Session . updateMessage ( userMsg )
await Session . updatePart ( {
2026-03-11 23:40:50 +00:00
id : PartID.ascending ( ) ,
2026-01-13 20:55:48 +00:00
messageID : userMsg.id ,
sessionID : ctx.sessionID ,
type : "text" ,
text : "User has requested to enter plan mode. Switch to plan mode and begin planning." ,
synthetic : true ,
} satisfies MessageV2 . TextPart )
return {
title : "Switching to plan agent" ,
output : ` User confirmed to switch to plan mode. A new message has been created to switch you to plan mode. The plan file will be at ${ plan } . Begin planning. ` ,
metadata : { } ,
}
} ,
} )
2026-02-25 00:16:17 +00:00
* /