fix: add stream debug visibility for tool calls and UUID gap detection (#264)

Tool call events between assistant messages were invisible in [Stream]
debug output, making it hard to understand message finalization timing.

- Add sawNonAssistantSinceLastUuid tracking to warn when assistant UUID
  changes with no visible tool_call/reasoning events in between
- Replace [Bot] tool logging with [Stream]-prefixed structured summaries
  (>>> TOOL CALL, <<< TOOL RESULT) for greppable output
- Bridge DEBUG=1 to DEBUG_SDK=1 so SDK-level dropped wire messages are
  visible when debugging

Written by Cameron ◯ Letta Code

"The stream does not resist the rock; it flows around it, revealing its shape." -- Unknown
This commit is contained in:
Cameron
2026-02-10 14:49:06 -08:00
committed by GitHub
parent f5371a9ba7
commit e8a97a2cbb
2 changed files with 26 additions and 5 deletions

View File

@@ -622,6 +622,7 @@ export class LettaBot implements AgentSession {
let lastAssistantUuid: string | null = null;
let sentAnyMessage = false;
let receivedAnyData = false;
let sawNonAssistantSinceLastUuid = false;
const msgTypeCounts: Record<string, number> = {};
const finalizeMessage = async () => {
@@ -685,22 +686,37 @@ export class LettaBot implements AgentSession {
break;
}
// Log meaningful events
// Log meaningful events with structured summaries
if (streamMsg.type === 'tool_call') {
console.log(`[Bot] Calling tool: ${streamMsg.toolName || 'unknown'}`);
console.log(`[Stream] >>> TOOL CALL: ${streamMsg.toolName || 'unknown'} (id: ${streamMsg.toolCallId?.slice(0, 12) || '?'})`);
sawNonAssistantSinceLastUuid = true;
} else if (streamMsg.type === 'tool_result') {
console.log(`[Bot] Tool completed: error=${streamMsg.isError}, resultLen=${(streamMsg as any).content?.length || 0}`);
console.log(`[Stream] <<< TOOL RESULT: error=${streamMsg.isError}, len=${(streamMsg as any).content?.length || 0}`);
sawNonAssistantSinceLastUuid = true;
} else if (streamMsg.type === 'assistant' && lastMsgType !== 'assistant') {
console.log(`[Bot] Generating response...`);
} else if (streamMsg.type === 'reasoning' && lastMsgType !== 'reasoning') {
console.log(`[Bot] Reasoning...`);
sawNonAssistantSinceLastUuid = true;
} else if (streamMsg.type !== 'assistant') {
sawNonAssistantSinceLastUuid = true;
}
lastMsgType = streamMsg.type;
if (streamMsg.type === 'assistant') {
const msgUuid = streamMsg.uuid;
if (msgUuid && lastAssistantUuid && msgUuid !== lastAssistantUuid && response.trim()) {
await finalizeMessage();
if (msgUuid && lastAssistantUuid && msgUuid !== lastAssistantUuid) {
if (response.trim()) {
if (!sawNonAssistantSinceLastUuid) {
console.warn(`[Stream] WARNING: Assistant UUID changed (${lastAssistantUuid.slice(0, 8)} -> ${msgUuid.slice(0, 8)}) with no visible tool_call/reasoning events between them. Tool call events may have been dropped by SDK transformMessage().`);
}
await finalizeMessage();
}
// Start tracking tool/reasoning visibility for the new assistant UUID.
sawNonAssistantSinceLastUuid = false;
} else if (msgUuid && !lastAssistantUuid) {
// Clear any pre-assistant noise so the first UUID becomes a clean baseline.
sawNonAssistantSinceLastUuid = false;
}
lastAssistantUuid = msgUuid || lastAssistantUuid;

View File

@@ -30,6 +30,11 @@ if (yamlConfig.agent?.model) {
}
applyConfigToEnv(yamlConfig);
// Bridge DEBUG=1 to DEBUG_SDK so SDK-level dropped wire messages are visible
if (process.env.DEBUG === '1' && !process.env.DEBUG_SDK) {
process.env.DEBUG_SDK = '1';
}
// Sync BYOK providers on startup (async, don't block)
syncProviders(yamlConfig).catch(err => console.error('[Config] Failed to sync providers:', err));