fix: plan mode flexibility (#517)
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
// src/cli/App.tsx
|
||||
|
||||
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { APIError, APIUserAbortError } from "@letta-ai/letta-client/core/error";
|
||||
import type {
|
||||
AgentState,
|
||||
@@ -6396,10 +6398,11 @@ DO NOT respond to these messages or otherwise consider them in your response unl
|
||||
const approval = pendingApprovals[currentIndex];
|
||||
if (approval?.toolName === "ExitPlanMode" && !planFileExists()) {
|
||||
const planFilePath = permissionMode.getPlanFilePath();
|
||||
const plansDir = join(homedir(), ".letta", "plans");
|
||||
handlePlanKeepPlanning(
|
||||
`You must write your plan to the plan file before exiting plan mode.\n` +
|
||||
`Plan file path: ${planFilePath || "not set"}\n` +
|
||||
`Use a write tool (e.g. Write, ApplyPatch, etc.) to create your plan, then call ExitPlanMode again.`,
|
||||
`You must write your plan to a plan file before exiting plan mode.\n` +
|
||||
(planFilePath ? `Plan file path: ${planFilePath}\n` : "") +
|
||||
`Use a write tool to create your plan in ${plansDir}, then use ExitPlanMode to present the plan to the user.`,
|
||||
);
|
||||
}
|
||||
}, [pendingApprovals, approvalResults.length, handlePlanKeepPlanning]);
|
||||
@@ -6792,6 +6795,18 @@ Plan file path: ${planFilePath}`;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Skip tool calls that were eagerly committed to staticItems
|
||||
// (e.g., ExitPlanMode preview) - but only AFTER approval is complete
|
||||
// We still need to render the approval options while awaiting approval
|
||||
if (
|
||||
ln.kind === "tool_call" &&
|
||||
ln.toolCallId &&
|
||||
eagerCommittedPreviewsRef.current.has(ln.toolCallId) &&
|
||||
ln.toolCallId !== currentApproval?.toolCallId
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if this tool call matches the current ExitPlanMode approval
|
||||
const isExitPlanModeApproval =
|
||||
ln.kind === "tool_call" &&
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// src/permissions/mode.ts
|
||||
// Permission mode management (default, acceptEdits, plan, bypassPermissions)
|
||||
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { isReadOnlyShellCommand } from "./readOnlyShell";
|
||||
|
||||
export type PermissionMode =
|
||||
@@ -171,9 +174,12 @@ class PermissionModeManager {
|
||||
return "allow";
|
||||
}
|
||||
|
||||
// Special case: allow writes to the plan file only
|
||||
// Special case: allow writes to any plan file in ~/.letta/plans/
|
||||
// NOTE: We allow writing to ANY plan file, not just the assigned one.
|
||||
// This is intentional - it allows the agent to "resume" planning after
|
||||
// plan mode was exited/reset by simply writing to any plan file.
|
||||
if (writeTools.includes(toolName)) {
|
||||
const planFilePath = this.getPlanFilePath();
|
||||
const plansDir = join(homedir(), ".letta", "plans");
|
||||
let targetPath =
|
||||
(toolArgs?.file_path as string) || (toolArgs?.path as string);
|
||||
|
||||
@@ -192,7 +198,12 @@ class PermissionModeManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (planFilePath && targetPath && targetPath === planFilePath) {
|
||||
// Allow if target is any .md file in the plans directory
|
||||
if (
|
||||
targetPath &&
|
||||
targetPath.startsWith(plansDir) &&
|
||||
targetPath.endsWith(".md")
|
||||
) {
|
||||
return "allow";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ export async function exit_plan_mode(): Promise<{ message: string }> {
|
||||
// The UI layer checks if the plan file exists and auto-rejects if not
|
||||
return {
|
||||
message:
|
||||
"User has approved your plan. You can now start coding.\nStart with updating your todo list if applicable",
|
||||
"User has approved your plan. You can now start coding.\n" +
|
||||
"Start with updating your todo list if applicable.\n\n" +
|
||||
"Tip: If this plan will be referenced in the future by your future-self, " +
|
||||
"other agents, or humans, consider renaming the plan file to something easily " +
|
||||
"identifiable with a timestamp (e.g., `2026-01-auth-refactor.md`) rather than the random name.",
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user