feat: add detailed logging to agent engine and task controller

Log every SDK message type, event emission, and stream lifecycle
to diagnose why text events are missing in voice-agent flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-24 02:56:09 -08:00
parent 0dbe711ed3
commit a7b42e6b98
2 changed files with 17 additions and 1 deletions

View File

@ -354,6 +354,14 @@ export class ClaudeAgentSdkEngine implements AgentEnginePort {
private mapSdkMessage(message: any): EngineStreamEvent[] { private mapSdkMessage(message: any): EngineStreamEvent[] {
const events: EngineStreamEvent[] = []; const events: EngineStreamEvent[] = [];
// Log every SDK message type for debugging
if (message.type === 'stream_event') {
const se = message.event;
this.logger.debug(`SDK msg: stream_event → ${se?.type} / ${se?.delta?.type ?? 'no-delta'}`);
} else {
this.logger.log(`SDK msg: type=${message.type}${message.subtype ? ` subtype=${message.subtype}` : ''}`);
}
if (message.type === 'stream_event') { if (message.type === 'stream_event') {
// Token-level streaming from includePartialMessages: true // Token-level streaming from includePartialMessages: true
const streamEvent = message.event; const streamEvent = message.event;

View File

@ -1,4 +1,4 @@
import { Controller, Post, Body, Param, Delete, Get, NotFoundException, BadRequestException } from '@nestjs/common'; import { Controller, Post, Body, Param, Delete, Get, NotFoundException, BadRequestException, Logger } from '@nestjs/common';
import { TenantId } from '@it0/common'; import { TenantId } from '@it0/common';
import { EngineRegistry } from '../../../infrastructure/engines/engine-registry'; import { EngineRegistry } from '../../../infrastructure/engines/engine-registry';
import { AgentStreamGateway } from '../../ws/agent-stream.gateway'; import { AgentStreamGateway } from '../../ws/agent-stream.gateway';
@ -12,6 +12,8 @@ import * as crypto from 'crypto';
@Controller('api/v1/agent') @Controller('api/v1/agent')
export class AgentController { export class AgentController {
private readonly logger = new Logger(AgentController.name);
constructor( constructor(
private readonly engineRegistry: EngineRegistry, private readonly engineRegistry: EngineRegistry,
private readonly gateway: AgentStreamGateway, private readonly gateway: AgentStreamGateway,
@ -51,6 +53,7 @@ export class AgentController {
// Fire-and-forget: iterate engine stream and emit events via gateway // Fire-and-forget: iterate engine stream and emit events via gateway
(async () => { (async () => {
try { try {
this.logger.log(`[Task ${task.id}] Starting engine stream for session=${session.id}, prompt="${body.prompt.slice(0, 80)}"`);
const stream = engine.executeTask({ const stream = engine.executeTask({
sessionId: session.id, sessionId: session.id,
prompt: body.prompt, prompt: body.prompt,
@ -59,7 +62,10 @@ export class AgentController {
maxTurns: body.maxTurns || 10, maxTurns: body.maxTurns || 10,
}); });
let eventCount = 0;
for await (const event of stream) { for await (const event of stream) {
eventCount++;
this.logger.log(`[Task ${task.id}] Event #${eventCount}: type=${event.type}${event.type === 'text' ? ` len=${(event as any).content?.length}` : ''}`);
this.gateway.emitStreamEvent(session.id, event); this.gateway.emitStreamEvent(session.id, event);
if (event.type === 'completed') { if (event.type === 'completed') {
@ -90,7 +96,9 @@ export class AgentController {
await this.taskRepository.save(task); await this.taskRepository.save(task);
} }
} }
this.logger.log(`[Task ${task.id}] Stream ended after ${eventCount} events`);
} catch (error) { } catch (error) {
this.logger.error(`[Task ${task.id}] Stream error: ${error instanceof Error ? error.message : error}`);
task.status = TaskStatus.FAILED; task.status = TaskStatus.FAILED;
task.result = error instanceof Error ? error.message : 'Unknown error'; task.result = error instanceof Error ? error.message : 'Unknown error';
task.completedAt = new Date(); task.completedAt = new Date();