fix: patch bug in approve plan mode (handle empty toolArgs for tools with no parameters) (#387)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2025-12-24 15:41:50 -08:00
committed by GitHub
parent a4fa3023c1
commit afbe253de2
2 changed files with 30 additions and 9 deletions

View File

@@ -56,6 +56,9 @@ const PARALLEL_SAFE_TOOLS = new Set([
"BashOutput",
// Task spawns independent subagents
"Task",
// Plan mode tools (no parameters, no file operations)
"EnterPlanMode",
"ExitPlanMode",
]);
function isParallelSafe(toolName: string): boolean {
@@ -194,10 +197,18 @@ async function executeSingleDecision(
// Execute the approved tool
try {
const parsedArgs =
typeof decision.approval.toolArgs === "string"
? JSON.parse(decision.approval.toolArgs)
: decision.approval.toolArgs || {};
// Safe parse - toolArgs should be "{}" but handle edge cases
let parsedArgs: Record<string, unknown> = {};
if (typeof decision.approval.toolArgs === "string") {
try {
parsedArgs = JSON.parse(decision.approval.toolArgs);
} catch {
// Empty or malformed args - use empty object
parsedArgs = {};
}
} else {
parsedArgs = decision.approval.toolArgs || {};
}
const toolResult = await executeTool(
decision.approval.toolName,
@@ -328,10 +339,18 @@ export async function executeApprovalBatch(
parallelIndices.push(i);
} else {
// Get resource key for write tools
const args =
typeof decision.approval.toolArgs === "string"
? JSON.parse(decision.approval.toolArgs)
: decision.approval.toolArgs || {};
// Safe parse - handle empty or malformed toolArgs
let args: Record<string, unknown> = {};
if (typeof decision.approval.toolArgs === "string") {
try {
args = JSON.parse(decision.approval.toolArgs);
} catch {
// Empty or malformed args - use empty object (will use global lock)
args = {};
}
} else {
args = decision.approval.toolArgs || {};
}
const resourceKey = getResourceKey(toolName, args);
const indices = writeToolsByResource.get(resourceKey) || [];

View File

@@ -215,10 +215,12 @@ export async function drainStream(
// Include ALL tool_call_ids - don't filter out incomplete entries
// Missing name/args will be handled by denial logic in App.tsx
// Default empty toolArgs to "{}" - empty string causes JSON.parse("") to fail
// This happens for tools with no parameters (e.g., EnterPlanMode, ExitPlanMode)
approvals = allPending.map((a) => ({
toolCallId: a.toolCallId,
toolName: a.toolName || "",
toolArgs: a.toolArgs || "",
toolArgs: a.toolArgs || "{}",
}));
if (approvals.length === 0) {