fix: improve error handling and logging in summary and transcript processing

- Enhanced error handling in summary generation by using fallback messages for missing assistant messages.
- Updated the `buildSummaryPrompt` function to streamline the retrieval of the last assistant message.
- Improved the `extractLastMessage` function to log errors when transcript files are missing or empty, and to ensure proper handling of messages without content.
- Added checks to ensure that messages of the specified role are found in the transcript, with appropriate logging for missing messages.
- Refactored the logging mechanism to provide clearer insights into processing failures and successes.
This commit is contained in:
Alex Newman
2025-12-14 16:28:57 -05:00
parent eb76a76a5b
commit 43db22728e
5 changed files with 81 additions and 37 deletions
+12
View File
@@ -0,0 +1,12 @@
{
"name": "claude-mem-plugin",
"version": "7.2.1",
"private": true,
"description": "Runtime dependencies for claude-mem bundled hooks",
"type": "module",
"dependencies": {},
"engines": {
"node": ">=18.0.0",
"bun": ">=1.0.0"
}
}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -781,7 +781,7 @@ MEMORY PROCESSING START
Write progress notes of what was done, what was learned, and what's next. This is a checkpoint to capture progress so far. The session is ongoing - you may receive more requests and tool executions after this summary. Write "next_steps" as the current trajectory of work (what's actively being worked on or coming up next), not as post-session future work. Always write at least a minimal summary explaining current progress, even if work is still in early stages, so that users see a summary output tied to each request.
Claude's Full Response to User:
${vr("Missing last_assistant_message in session for summary prompt",a,a.last_assistant_message||"")}
${a.last_assistant_message||vr("Missing last_assistant_message in session for summary prompt",a,"")}
Respond in this XML format:
<summary>
+2 -2
View File
@@ -177,10 +177,10 @@ export function buildObservationPrompt(obs: Observation): string {
* Build prompt to generate progress summary
*/
export function buildSummaryPrompt(session: SDKSession): string {
const lastAssistantMessage = happy_path_error__with_fallback(
const lastAssistantMessage = session.last_assistant_message || happy_path_error__with_fallback(
'Missing last_assistant_message in session for summary prompt',
session,
session.last_assistant_message || ''
''
);
return `PROGRESS SUMMARY CHECKPOINT
+50 -18
View File
@@ -1,5 +1,6 @@
import { readFileSync, existsSync } from 'fs';
import { logger } from '../utils/logger.js';
import { happy_path_error__with_fallback } from '../utils/silent-debug.js';
/**
* Extract last message of specified role from transcript JSONL file
@@ -13,42 +14,73 @@ export function extractLastMessage(
stripSystemReminders: boolean = false
): string {
if (!transcriptPath || !existsSync(transcriptPath)) {
happy_path_error__with_fallback(
'Transcript path missing or file does not exist',
{ transcriptPath, role }
);
return '';
}
try {
const content = readFileSync(transcriptPath, 'utf-8').trim();
if (!content) return '';
if (!content) {
happy_path_error__with_fallback(
'Transcript file exists but is empty',
{ transcriptPath, role }
);
return '';
}
const lines = content.split('\n');
let foundMatchingRole = false;
for (let i = lines.length - 1; i >= 0; i--) {
try {
const line = JSON.parse(lines[i]);
if (line.type === role && line.message?.content) {
let text = '';
const msgContent = line.message.content;
if (line.type === role) {
foundMatchingRole = true;
if (typeof msgContent === 'string') {
text = msgContent;
} else if (Array.isArray(msgContent)) {
text = msgContent
.filter((c: any) => c.type === 'text')
.map((c: any) => c.text)
.join('\n');
if (line.message?.content) {
let text = '';
const msgContent = line.message.content;
if (typeof msgContent === 'string') {
text = msgContent;
} else if (Array.isArray(msgContent)) {
text = msgContent
.filter((c: any) => c.type === 'text')
.map((c: any) => c.text)
.join('\n');
}
if (stripSystemReminders) {
text = text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '');
text = text.replace(/\n{3,}/g, '\n\n').trim();
}
// Log if we found the role but the text is empty after processing
if (!text || text.trim() === '') {
happy_path_error__with_fallback(
'Found message but content is empty after processing',
{ role, transcriptPath, msgContentType: typeof msgContent, stripSystemReminders }
);
}
return text;
}
if (stripSystemReminders) {
text = text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '');
text = text.replace(/\n{3,}/g, '\n\n').trim();
}
return text;
}
} catch {
continue;
}
}
// If we searched the whole transcript and didn't find any message of this role
if (!foundMatchingRole) {
happy_path_error__with_fallback(
'No message found for role in transcript',
{ role, transcriptPath, totalLines: lines.length }
);
}
} catch (error) {
logger.error('HOOK', 'Failed to read transcript', { transcriptPath }, error as Error);
}