Welcome back! In Chapter 1: Tool Definition & Lifecycle, we built the "Skill Card" for our tool. We defined its name, its inputs, and when it is allowed to appear.
Now, we need to program the logic for the most critical moment: The Transition.
Imagine your AI Agent is an astronaut on a spaceship.
The ExitPlanModeTool is the green button on the wall of the Decontamination Chamber.
When the Agent presses this button, two things must happen:
Before we change anything, we must verify the Agent is in the correct state. It makes no sense to "Exit Plan Mode" if you aren't in Plan Mode to begin with.
In the call method of our tool (or validateInput), we perform this check.
// Inside ExitPlanModeV2Tool.ts
async validateInput(_input, { getAppState }) {
// Get the current "mental state" of the application
const mode = getAppState().toolPermissionContext.mode
// The Guard Logic
if (mode !== 'plan') {
return {
result: false,
message: 'You are not in plan mode.',
errorCode: 1,
}
}
return { result: true }
}
Before we look at the code, let's visualize what happens when the button is pressed.
When the Agent exits the "Decontamination Chamber," where do they go? Usually, they go back to where they came from.
The application remembers the Pre-Plan Mode.
There is a catch! If the user was in Auto Mode, but something dangerous happened (like a security trigger), the system might have engaged a "Circuit Breaker" (Gate).
Even if the Agent wants to go back to Auto Mode, the tool checks if the gate is locked. If it is, it forces a safe landing into Default Mode.
// Simplified logic inside the tool
let restoreMode = prev.toolPermissionContext.prePlanMode ?? 'default'
// If trying to go back to Auto Mode, check the safety gate
if (restoreMode === 'auto' && !isAutoModeGateEnabled()) {
// Safety Gate is closed! Force manual mode.
restoreMode = 'default'
// Notify the user why we downgraded them
context.addNotification({
text: 'Falling back to default mode for safety'
})
}
This is the code that actually "opens the door." We use context.setAppState to atomically update the global state of the application.
This block looks intimidating in the source file, so let's break it down into its simplest parts.
First, we tell the system the planning phase is officially over.
context.setAppState(prev => {
// Double check we are in plan mode to avoid race conditions
if (prev.toolPermissionContext.mode !== 'plan') return prev
// Set global flags used by the UI and other tools
setHasExitedPlanMode(true)
setNeedsPlanModeExitAttachment(true)
// ... continued below
When you enter Plan Mode, "Dangerous Permissions" (like executing shell commands) are stripped away. Now, we must give them back.
// ... continued from above
// Calculate which permissions to give back
let baseContext = prev.toolPermissionContext
// If we are NOT going to auto mode, give back the dangerous tools
if (restoreMode !== 'auto' && prev.toolPermissionContext.strippedDangerousRules) {
baseContext = restoreDangerousPermissions(baseContext)
}
// ... continued below
Finally, we return the new state object. This effectively teleports the Agent from "Plan" to "Default" (or "Auto").
// ... continued from above
return {
...prev,
toolPermissionContext: {
...baseContext,
mode: restoreMode, // The new destination (e.g., 'default')
prePlanMode: undefined, // Clear the history
},
}
})
Once the state transition is complete, the tool returns a tool_result. This is the text the Agent reads to know it succeeded.
If the transition works, the Agent sees:
"User has approved your plan. You can now start coding."
If the transition fails (validation error), the Agent sees:
"You are not in plan mode."
In this chapter, we learned how the ExitPlanModeTool acts as a secure airlock between the "Planning" and "Coding" phases.
setAppState to update the global mode and restore dangerous permissions.However, sometimes you aren't working alone. What if you are a junior Agent and you need your Team Lead to approve the plan before you can open the airlock?
That brings us to the Teammate Approval Protocol.
Next Chapter: Teammate Approval Protocol
Generated by Code IQ