fix: invalid tool call ID recovery and system-reminder tag centralization (#627)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2026-01-21 20:43:35 -08:00
committed by GitHub
parent 35b0d658f3
commit 2e7fe42658
14 changed files with 367 additions and 35 deletions

View File

@@ -6,6 +6,11 @@ import { APPROVAL_RECOVERY_PROMPT } from "./promptAssets";
const APPROVAL_RECOVERY_DETAIL_FRAGMENT =
"no tool call is currently awaiting approval";
// Error when approval tool call IDs don't match what server expects
// Format: "Invalid tool call IDs: Expected [...], got [...]"
// This is a specific subtype of desync - server HAS approvals but with different IDs
const INVALID_TOOL_CALL_IDS_FRAGMENT = "invalid tool call ids";
// Error when trying to SEND message but server has pending approval waiting
// This is the CONFLICT error - opposite of desync
const APPROVAL_PENDING_DETAIL_FRAGMENT = "cannot send a new message";
@@ -27,7 +32,27 @@ type RunErrorMetadata =
export function isApprovalStateDesyncError(detail: unknown): boolean {
if (typeof detail !== "string") return false;
return detail.toLowerCase().includes(APPROVAL_RECOVERY_DETAIL_FRAGMENT);
const lower = detail.toLowerCase();
return (
lower.includes(APPROVAL_RECOVERY_DETAIL_FRAGMENT) ||
lower.includes(INVALID_TOOL_CALL_IDS_FRAGMENT)
);
}
/**
* Check if error specifically indicates tool call ID mismatch.
* This is a subtype of desync where the server HAS pending approvals,
* but they have different IDs than what the client sent.
*
* Unlike "no tool call is currently awaiting approval" (server has nothing),
* this error means we need to FETCH the actual pending approvals to resync.
*
* Error format:
* { detail: "Invalid tool call IDs: Expected ['tc_abc'], got ['tc_xyz']" }
*/
export function isInvalidToolCallIdsError(detail: unknown): boolean {
if (typeof detail !== "string") return false;
return detail.toLowerCase().includes(INVALID_TOOL_CALL_IDS_FRAGMENT);
}
/**

View File

@@ -13,7 +13,11 @@ import {
addToolCall,
updateSubagent,
} from "../../cli/helpers/subagentState.js";
import { INTERRUPTED_BY_USER } from "../../constants";
import {
INTERRUPTED_BY_USER,
SYSTEM_REMINDER_CLOSE,
SYSTEM_REMINDER_OPEN,
} from "../../constants";
import { cliPermissions } from "../../permissions/cli";
import { permissionMode } from "../../permissions/mode";
import { sessionPermissions } from "../../permissions/session";
@@ -643,11 +647,11 @@ function buildDeploySystemReminder(
? "read-only tools (Read, Glob, Grep)"
: "local tools (Bash, Read, Write, Edit, etc.)";
return `<system-reminder>
return `${SYSTEM_REMINDER_OPEN}
This task is from "${senderAgentName}" (agent ID: ${senderAgentId}), which deployed you as a subagent inside the Letta Code CLI (docs.letta.com/letta-code).
You have access to ${toolDescription} in their codebase.
Your final message will be returned to the caller.
</system-reminder>
${SYSTEM_REMINDER_CLOSE}
`;
}