fix(debug): unify LETTA_DEBUG and DEBUG gating (#1408)

Co-authored-by: Letta Code <noreply@letta.com>
This commit is contained in:
jnjpng
2026-03-16 13:14:25 -07:00
committed by GitHub
parent 95304a557e
commit f65a751ff0
8 changed files with 47 additions and 33 deletions

View File

@@ -6,7 +6,7 @@ import { APIError } from "@letta-ai/letta-client/core/error";
import type { AgentState } from "@letta-ai/letta-client/resources/agents/agents";
import type { Message } from "@letta-ai/letta-client/resources/agents/messages";
import type { ApprovalRequest } from "../cli/helpers/stream";
import { debugWarn } from "../utils/debug";
import { debugWarn, isDebugEnabled } from "../utils/debug";
// Backfill should feel like "the last turn(s)", not "the last N raw messages".
// Tool-heavy turns can generate many tool_call/tool_return messages that would
@@ -484,7 +484,7 @@ export async function getResumeData(
});
messages = sortChronological(messagesPage.getPaginatedItems());
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.log(
`[DEBUG] conversations.messages.list(default, agent_id=${agent.id}) returned ${messages.length} messages`,
);

View File

@@ -14,7 +14,7 @@ import {
captureToolExecutionContext,
waitForToolsetReady,
} from "../tools/manager";
import { debugLog, debugWarn } from "../utils/debug";
import { debugLog, debugWarn, isDebugEnabled } from "../utils/debug";
import { isTimingsEnabled } from "../utils/timing";
import {
type ApprovalNormalizationOptions,
@@ -138,7 +138,7 @@ export async function sendMessageStream(
clientSkills,
);
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.log(
`[DEBUG] sendMessageStream: conversationId=${conversationId}, agentId=${opts.agentId ?? "(none)"}`,
);

View File

@@ -121,7 +121,12 @@ import {
} from "../tools/manager";
import type { ToolsetName, ToolsetPreference } from "../tools/toolset";
import { formatToolsetName } from "../tools/toolset-labels";
import { debugLog, debugLogFile, debugWarn } from "../utils/debug";
import {
debugLog,
debugLogFile,
debugWarn,
isDebugEnabled,
} from "../utils/debug";
import { getVersion } from "../version";
import {
handleMcpAdd,
@@ -3050,7 +3055,7 @@ export default function App({
const agentName = agentState?.name || "Unnamed Agent";
const isResumingConversation =
resumedExistingConversation || messageHistory.length > 0;
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.log(
`[DEBUG] Header: resumedExistingConversation=${resumedExistingConversation}, messageHistory.length=${messageHistory.length}`,
);
@@ -4721,7 +4726,7 @@ export default function App({
})
.catch((err) => {
// Silently ignore - not critical
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.error(
"[DEBUG] Failed to set conversation summary:",
err,

View File

@@ -120,7 +120,7 @@ import type {
StreamEvent,
SystemInitMessage,
} from "./types/protocol";
import { debugLog, debugWarn } from "./utils/debug";
import { debugLog, debugWarn, isDebugEnabled } from "./utils/debug";
import {
markMilestone,
measureSinceMilestone,
@@ -1493,7 +1493,7 @@ ${SYSTEM_REMINDER_CLOSE}
agentId: agent.id,
skillSources: resolvedSkillSources,
logger: (message) => {
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.warn(`[DEBUG] ${message}`);
}
},

View File

@@ -49,7 +49,7 @@ import { startStartupAutoUpdateCheck } from "./startup-auto-update";
import { telemetry } from "./telemetry";
import { loadTools } from "./tools/manager";
import { clearPersistedClientToolRules } from "./tools/toolset";
import { debugLog, debugWarn } from "./utils/debug";
import { debugLog, debugWarn, isDebugEnabled } from "./utils/debug";
import { markMilestone } from "./utils/timing";
// Stable empty array constants to prevent new references on every render
@@ -777,7 +777,7 @@ async function main(): Promise<void> {
const message =
err instanceof Error ? err.message : "An unexpected error occurred";
console.error(`\nError: ${message}`);
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.error(err);
}
process.exit(1);
@@ -1824,7 +1824,7 @@ async function main(): Promise<void> {
let conversationIdToUse!: string;
// Debug: log resume flag status
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.log(`[DEBUG] shouldContinue=${shouldContinue}`);
console.log(`[DEBUG] shouldResume=${shouldResume}`);
console.log(
@@ -1865,7 +1865,7 @@ async function main(): Promise<void> {
settingsManager.getLocalLastSession(process.cwd()) ??
settingsManager.getGlobalLastSession();
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.log(`[DEBUG] lastSession=${JSON.stringify(lastSession)}`);
console.log(`[DEBUG] agent.id=${agent.id}`);
}
@@ -2029,7 +2029,7 @@ async function main(): Promise<void> {
// Handle errors gracefully without showing raw stack traces
const message = formatErrorDetails(err);
console.error(`\nError during initialization: ${message}`);
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.error(err);
}
process.exit(1);

View File

@@ -6,6 +6,7 @@ import type {
QueueItemKind,
QueueItemSource,
} from "../types/protocol";
import { isDebugEnabled } from "../utils/debug";
export type { QueueBlockedReason, QueueClearedReason, QueueItemKind };
@@ -362,7 +363,7 @@ export class QueueRuntime {
...args,
);
} catch (err) {
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.error(`[QueueRuntime] callback "${name}" threw:`, err);
}
}

View File

@@ -1,7 +1,8 @@
// src/utils/debug.ts
// Debug logging utility.
//
// Screen output: controlled by LETTA_DEBUG=1 (or LETTA_DEBUG_FILE for a custom path).
// Screen output: controlled by LETTA_DEBUG=1 (or DEBUG=1 for legacy compatibility),
// or LETTA_DEBUG_FILE for a custom path.
// File output: always written to ~/.letta/logs/debug/{agent-id}/{session-id}.log
// once debugLogFile.init() has been called. Before init, lines are
// silently dropped (no file path yet).
@@ -23,12 +24,18 @@ import { format } from "node:util";
// ---------------------------------------------------------------------------
/**
* Check if debug mode is enabled via LETTA_DEBUG env var
* Set LETTA_DEBUG=1 or LETTA_DEBUG=true to enable debug logging
* Check if debug mode is enabled via LETTA_DEBUG env var.
* Also accepts DEBUG=1|true for legacy compatibility.
*/
export function isDebugEnabled(): boolean {
const debug = process.env.LETTA_DEBUG;
return debug === "1" || debug === "true";
const lettaDebug = process.env.LETTA_DEBUG;
const legacyDebug = process.env.DEBUG;
return (
lettaDebug === "1" ||
lettaDebug === "true" ||
legacyDebug === "1" ||
legacyDebug === "true"
);
}
function getDebugFile(): string | null {

View File

@@ -84,6 +84,7 @@ import type {
TranscriptBackfillMessage,
TranscriptSupplementMessage,
} from "../types/protocol";
import { isDebugEnabled } from "../utils/debug";
import { getListenerBlockedReason } from "./helpers/listenerQueueAdapter";
import {
handleTerminalInput,
@@ -458,7 +459,7 @@ function handleModeChange(msg: ModeChangeMessage, socket: WebSocket): void {
success: true,
});
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.log(`[Listen] Mode changed to: ${msg.mode}`);
}
} catch (error) {
@@ -470,7 +471,7 @@ function handleModeChange(msg: ModeChangeMessage, socket: WebSocket): void {
error: error instanceof Error ? error.message : "Mode change failed",
});
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.error("[Listen] Mode change failed:", error);
}
}
@@ -1325,7 +1326,7 @@ function scheduleQueuePump(
})
.catch((error: unknown) => {
runtime.queuePumpScheduled = false;
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.error("[Listen] Error in queue pump:", error);
}
opts.onStatusChange?.("idle", opts.connectionId);
@@ -2115,7 +2116,7 @@ function populateInterruptQueue(
return true;
}
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.warn(
"[Listen] Cancel during approval loop but no tool_call_ids available " +
"for interrupted queue — next turn may hit pre-stream conflict. " +
@@ -2849,7 +2850,7 @@ async function connectWithRetry(
raw,
});
}
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.log(
`[Listen] Received message: ${JSON.stringify(parsed, null, 2)}`,
);
@@ -3075,7 +3076,7 @@ async function connectWithRetry(
);
})
.catch((error: unknown) => {
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.error("[Listen] Error handling queued get_state:", error);
}
});
@@ -3114,7 +3115,7 @@ async function connectWithRetry(
}
})
.catch((error: unknown) => {
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.error(
"[Listen] Error handling queued pending approval recovery:",
error,
@@ -3187,7 +3188,7 @@ async function connectWithRetry(
scheduleQueuePump(runtime, socket, opts);
})
.catch((error: unknown) => {
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.error("[Listen] Error handling queued message:", error);
}
opts.onStatusChange?.("idle", opts.connectionId);
@@ -3212,7 +3213,7 @@ async function connectWithRetry(
runtime.queuedMessagesByItemId.clear();
runtime.queueRuntime.clear("shutdown");
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.log(
`[Listen] WebSocket disconnected (code: ${code}, reason: ${reason.toString()})`,
);
@@ -3230,7 +3231,7 @@ async function connectWithRetry(
// 1008: Environment not found - need to re-register
if (code === 1008) {
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.log("[Listen] Environment not found, re-registering...");
}
// Stop retry loop and signal that we need to re-register
@@ -3261,7 +3262,7 @@ async function connectWithRetry(
type: "_ws_error",
message: error.message,
});
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.error("[Listen] WebSocket error:", error);
}
// Error triggers close(), which handles retry logic.
@@ -3320,7 +3321,7 @@ async function handleIncomingMessage(
return;
}
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.log(
`[Listen] Handling message: agentId=${agentId}, requestedConversationId=${requestedConversationId}, conversationId=${conversationId}`,
);
@@ -4048,7 +4049,7 @@ async function handleIncomingMessage(
stopReason: "error",
});
if (process.env.DEBUG) {
if (isDebugEnabled()) {
console.error("[Listen] Error handling message:", error);
}
} finally {