import path from "path" import { Effect, Schema } from "effect" import * as Tool from "./tool" import { Question } from "../question" import { Session } from "../session" import { MessageV2 } from "../session/message-v2" import { Provider } from "../provider" import { Instance } from "../project/instance" import { type SessionID, MessageID, PartID } from "../session/schema" import EXIT_DESCRIPTION from "./plan-exit.txt" function getLastModel(sessionID: SessionID) { for (const item of MessageV2.stream(sessionID)) { if (item.info.role === "user" && item.info.model) return item.info.model } return undefined } export const Parameters = Schema.Struct({}) export const PlanExitTool = Tool.define( "plan_exit", Effect.gen(function* () { const session = yield* Session.Service const question = yield* Question.Service const provider = yield* Provider.Service return { description: EXIT_DESCRIPTION, parameters: Parameters, execute: (_params: {}, ctx: Tool.Context) => Effect.gen(function* () { const info = yield* session.get(ctx.sessionID) const plan = path.relative(Instance.worktree, Session.plan(info)) const answers = yield* 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, }) if (answers[0]?.[0] === "No") yield* new Question.RejectedError() const model = getLastModel(ctx.sessionID) ?? (yield* provider.defaultModel()) const msg: MessageV2.User = { id: MessageID.ascending(), sessionID: ctx.sessionID, role: "user", time: { created: Date.now() }, agent: "build", model, } yield* session.updateMessage(msg) yield* session.updatePart({ id: PartID.ascending(), messageID: msg.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: {}, } }).pipe(Effect.orDie), } }), )