feat: Enhanced logging and SDK prompt improvements (#94)
* Initial plan * Initial analysis: Found root cause of double entries bug Co-authored-by: thedotmack <683968+thedotmack@users.noreply.github.com> * Fix double entries by assigning generatorPromise in handleSessionInit Co-authored-by: thedotmack <683968+thedotmack@users.noreply.github.com> * feat(logging): Enhance HTTP request logging and session management - Added middleware for logging HTTP requests and responses, excluding static assets and health checks. - Introduced a method to summarize request bodies for specific endpoints. - Improved logging for user prompt synchronization with Chroma, including duration tracking. - Enhanced session initialization logging to include additional session details. - Updated observation and summary logging to provide more context and error handling during Chroma synchronization. - Refactored tool name formatting for logging in the SessionManager. - Expanded logger component types to include 'HTTP', 'SESSION', and 'CHROMA'. * Refactor SDK prompts and logging for improved clarity and functionality - Updated buildInitPrompt to clarify the observer's role and what to record. - Enhanced buildSummaryPrompt with clearer instructions for summarizing ongoing sessions. - Improved buildContinuationPrompt to emphasize the focus on deliverables and capabilities. - Refactored WorkerService to utilize a centralized tool formatting function for logging. - Added truncation for logged responses and observations to improve readability. - Updated SessionManager to log the queuing of summarize actions with session details. - Enhanced App and Sidebar components to support refreshing stats on sidebar open. - Refactored useStats hook to allow manual refreshing of stats while maintaining automatic loading on mount. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: thedotmack <683968+thedotmack@users.noreply.github.com>
This commit is contained in:
@@ -69,13 +69,20 @@ export class SDKAgent {
|
||||
: typeof content === 'string' ? content : '';
|
||||
|
||||
const responseSize = textContent.length;
|
||||
logger.dataOut('SDK', `Response received (${responseSize} chars)`, {
|
||||
sessionId: session.sessionDbId,
|
||||
promptNumber: session.lastPromptNumber
|
||||
});
|
||||
|
||||
// Parse and process response
|
||||
await this.processSDKResponse(session, textContent, worker);
|
||||
// Only log non-empty responses (filter out noise)
|
||||
if (responseSize > 0) {
|
||||
const truncatedResponse = responseSize > 100
|
||||
? textContent.substring(0, 100) + '...'
|
||||
: textContent;
|
||||
logger.dataOut('SDK', `Response received (${responseSize} chars)`, {
|
||||
sessionId: session.sessionDbId,
|
||||
promptNumber: session.lastPromptNumber
|
||||
}, truncatedResponse);
|
||||
|
||||
// Parse and process response
|
||||
await this.processSDKResponse(session, textContent, worker);
|
||||
}
|
||||
}
|
||||
|
||||
// Log result messages
|
||||
@@ -185,7 +192,20 @@ export class SDKAgent {
|
||||
session.lastPromptNumber
|
||||
);
|
||||
|
||||
// Sync to Chroma (fire-and-forget)
|
||||
// Log observation details
|
||||
logger.info('SDK', 'Observation saved', {
|
||||
sessionId: session.sessionDbId,
|
||||
obsId,
|
||||
type: obs.type,
|
||||
title: obs.title.substring(0, 60) + (obs.title.length > 60 ? '...' : ''),
|
||||
files: obs.files?.length || 0,
|
||||
concepts: obs.concepts?.length || 0
|
||||
});
|
||||
|
||||
// Sync to Chroma with error logging
|
||||
const chromaStart = Date.now();
|
||||
const obsType = obs.type;
|
||||
const obsTitle = obs.title;
|
||||
this.dbManager.getChromaSync().syncObservation(
|
||||
obsId,
|
||||
session.claudeSessionId,
|
||||
@@ -193,7 +213,25 @@ export class SDKAgent {
|
||||
obs,
|
||||
session.lastPromptNumber,
|
||||
createdAtEpoch
|
||||
).catch(() => {});
|
||||
).then(() => {
|
||||
const chromaDuration = Date.now() - chromaStart;
|
||||
const truncatedTitle = obsTitle.length > 50
|
||||
? obsTitle.substring(0, 50) + '...'
|
||||
: obsTitle;
|
||||
logger.debug('CHROMA', 'Observation synced', {
|
||||
obsId,
|
||||
duration: `${chromaDuration}ms`,
|
||||
type: obsType,
|
||||
title: truncatedTitle
|
||||
});
|
||||
}).catch(err => {
|
||||
logger.error('CHROMA', 'Failed to sync observation', {
|
||||
obsId,
|
||||
sessionId: session.sessionDbId,
|
||||
type: obsType,
|
||||
title: obsTitle.substring(0, 50)
|
||||
}, err);
|
||||
});
|
||||
|
||||
// Broadcast to SSE clients (for web UI)
|
||||
if (worker && worker.sseBroadcaster) {
|
||||
@@ -218,8 +256,6 @@ export class SDKAgent {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
logger.info('SDK', 'Observation saved', { obsId, type: obs.type });
|
||||
}
|
||||
|
||||
// Parse summary
|
||||
@@ -234,7 +270,18 @@ export class SDKAgent {
|
||||
session.lastPromptNumber
|
||||
);
|
||||
|
||||
// Sync to Chroma (fire-and-forget)
|
||||
// Log summary details
|
||||
logger.info('SDK', 'Summary saved', {
|
||||
sessionId: session.sessionDbId,
|
||||
summaryId,
|
||||
request: summary.request.substring(0, 60) + (summary.request.length > 60 ? '...' : ''),
|
||||
hasCompleted: !!summary.completed,
|
||||
hasNextSteps: !!summary.next_steps
|
||||
});
|
||||
|
||||
// Sync to Chroma with error logging
|
||||
const chromaStart = Date.now();
|
||||
const summaryRequest = summary.request;
|
||||
this.dbManager.getChromaSync().syncSummary(
|
||||
summaryId,
|
||||
session.claudeSessionId,
|
||||
@@ -242,7 +289,23 @@ export class SDKAgent {
|
||||
summary,
|
||||
session.lastPromptNumber,
|
||||
createdAtEpoch
|
||||
).catch(() => {});
|
||||
).then(() => {
|
||||
const chromaDuration = Date.now() - chromaStart;
|
||||
const truncatedRequest = summaryRequest.length > 50
|
||||
? summaryRequest.substring(0, 50) + '...'
|
||||
: summaryRequest;
|
||||
logger.debug('CHROMA', 'Summary synced', {
|
||||
summaryId,
|
||||
duration: `${chromaDuration}ms`,
|
||||
request: truncatedRequest
|
||||
});
|
||||
}).catch(err => {
|
||||
logger.error('CHROMA', 'Failed to sync summary', {
|
||||
summaryId,
|
||||
sessionId: session.sessionDbId,
|
||||
request: summaryRequest.substring(0, 50)
|
||||
}, err);
|
||||
});
|
||||
|
||||
// Broadcast to SSE clients (for web UI)
|
||||
if (worker && worker.sseBroadcaster) {
|
||||
@@ -263,8 +326,6 @@ export class SDKAgent {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
logger.info('SDK', 'Summary saved', { summaryId });
|
||||
}
|
||||
|
||||
// Check and stop spinner after processing (debounced)
|
||||
|
||||
@@ -55,7 +55,13 @@ export class SessionManager {
|
||||
const emitter = new EventEmitter();
|
||||
this.sessionQueues.set(sessionDbId, emitter);
|
||||
|
||||
logger.info('WORKER', 'Session initialized', { sessionDbId, project: session.project });
|
||||
logger.info('SESSION', 'Session initialized', {
|
||||
sessionId: sessionDbId,
|
||||
project: session.project,
|
||||
claudeSessionId: session.claudeSessionId,
|
||||
queueDepth: 0,
|
||||
hasGenerator: false
|
||||
});
|
||||
|
||||
return session;
|
||||
}
|
||||
@@ -78,6 +84,8 @@ export class SessionManager {
|
||||
session = this.initializeSession(sessionDbId);
|
||||
}
|
||||
|
||||
const beforeDepth = session.pendingMessages.length;
|
||||
|
||||
session.pendingMessages.push({
|
||||
type: 'observation',
|
||||
tool_name: data.tool_name,
|
||||
@@ -87,13 +95,19 @@ export class SessionManager {
|
||||
cwd: data.cwd
|
||||
});
|
||||
|
||||
const afterDepth = session.pendingMessages.length;
|
||||
|
||||
// Notify generator immediately (zero latency)
|
||||
const emitter = this.sessionQueues.get(sessionDbId);
|
||||
emitter?.emit('message');
|
||||
|
||||
logger.debug('WORKER', 'Observation queued', {
|
||||
sessionDbId,
|
||||
queueLength: session.pendingMessages.length
|
||||
// Format tool name for logging
|
||||
const toolSummary = logger.formatTool(data.tool_name, data.tool_input);
|
||||
|
||||
logger.info('SESSION', `Observation queued (${beforeDepth}→${afterDepth})`, {
|
||||
sessionId: sessionDbId,
|
||||
tool: toolSummary,
|
||||
hasGenerator: !!session.generatorPromise
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,12 +122,19 @@ export class SessionManager {
|
||||
session = this.initializeSession(sessionDbId);
|
||||
}
|
||||
|
||||
const beforeDepth = session.pendingMessages.length;
|
||||
|
||||
session.pendingMessages.push({ type: 'summarize' });
|
||||
|
||||
const afterDepth = session.pendingMessages.length;
|
||||
|
||||
const emitter = this.sessionQueues.get(sessionDbId);
|
||||
emitter?.emit('message');
|
||||
|
||||
logger.debug('WORKER', 'Summarize queued', { sessionDbId });
|
||||
logger.info('SESSION', `Summarize queued (${beforeDepth}→${afterDepth})`, {
|
||||
sessionId: sessionDbId,
|
||||
hasGenerator: !!session.generatorPromise
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,6 +146,8 @@ export class SessionManager {
|
||||
return; // Already deleted
|
||||
}
|
||||
|
||||
const sessionDuration = Date.now() - session.startTime;
|
||||
|
||||
// Abort the SDK agent
|
||||
session.abortController.abort();
|
||||
|
||||
@@ -137,7 +160,11 @@ export class SessionManager {
|
||||
this.sessions.delete(sessionDbId);
|
||||
this.sessionQueues.delete(sessionDbId);
|
||||
|
||||
logger.info('WORKER', 'Session deleted', { sessionDbId });
|
||||
logger.info('SESSION', 'Session deleted', {
|
||||
sessionId: sessionDbId,
|
||||
duration: `${(sessionDuration / 1000).toFixed(1)}s`,
|
||||
project: session.project
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user