fix(ResponseProcessor): salvage synthetic summary when AI returns <observation> instead of <summary>
Fixes Issue #1312: AI sometimes returns <observation> XML tags instead of <summary> tags during the summarize phase, despite clear instructions in buildSummaryPrompt() requiring <summary> ONLY output. When this occurs, parseSummary() returns null and the entire session summary is lost. This fix detects the condition (summary missing + observations present) and synthesizes a summary from the observation data, ensuring session summaries are not completely lost. The salvage mapping: - request: observation title - investigated: observation narrative or facts - learned: observation facts joined - completed: title if type is feature/bugfix - notes: indicates this is a synthetic salvage summary Observations are stored normally regardless of this fallback. Co-authored-by: Sisyphus <sisyphus@openclaw>
This commit is contained in:
@@ -85,6 +85,27 @@ export async function processAgentResponse(
|
|||||||
// Convert nullable fields to empty strings for storeSummary (if summary exists)
|
// Convert nullable fields to empty strings for storeSummary (if summary exists)
|
||||||
const summaryForStore = normalizeSummaryForStorage(summary);
|
const summaryForStore = normalizeSummaryForStorage(summary);
|
||||||
|
|
||||||
|
// Fallback: When summary parse fails but observations exist, salvage a synthetic summary.
|
||||||
|
// Fixes Issue #1312: AI sometimes returns <observation> instead of <summary> despite clear instructions.
|
||||||
|
// Observations are stored normally; this only affects the session summary.
|
||||||
|
let finalSummaryForStore = summaryForStore;
|
||||||
|
if (!summaryForStore && observations.length > 0) {
|
||||||
|
const primary = observations[0];
|
||||||
|
finalSummaryForStore = {
|
||||||
|
request: primary.title || `Session observations (${observations.length} items)`,
|
||||||
|
investigated: primary.narrative || primary.facts?.join('; ') || '',
|
||||||
|
learned: primary.facts?.join('; ') || '',
|
||||||
|
completed: primary.type === 'feature' || primary.type === 'bugfix' ? (primary.title || '') : '',
|
||||||
|
next_steps: '',
|
||||||
|
notes: `[Salvaged from ${observations.length} observation(s)] AI returned <observation> instead of <summary>`
|
||||||
|
};
|
||||||
|
logger.warn('PARSER', `SALVAGED summary from ${observations.length} observation(s) — AI did not output <summary> tags`, {
|
||||||
|
sessionId: session.sessionDbId,
|
||||||
|
agentName,
|
||||||
|
observationIds: observations.map(o => o.title).filter(Boolean).slice(0, 3)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Get session store for atomic transaction
|
// Get session store for atomic transaction
|
||||||
const sessionStore = dbManager.getSessionStore();
|
const sessionStore = dbManager.getSessionStore();
|
||||||
|
|
||||||
@@ -102,7 +123,7 @@ export async function processAgentResponse(
|
|||||||
sessionStore.ensureMemorySessionIdRegistered(session.sessionDbId, session.memorySessionId);
|
sessionStore.ensureMemorySessionIdRegistered(session.sessionDbId, session.memorySessionId);
|
||||||
|
|
||||||
// Log pre-storage with session ID chain for verification
|
// Log pre-storage with session ID chain for verification
|
||||||
logger.info('DB', `STORING | sessionDbId=${session.sessionDbId} | memorySessionId=${session.memorySessionId} | obsCount=${observations.length} | hasSummary=${!!summaryForStore}`, {
|
logger.info('DB', `STORING | sessionDbId=${session.sessionDbId} | memorySessionId=${session.memorySessionId} | obsCount=${observations.length} | hasSummary=${!!finalSummaryForStore}`, {
|
||||||
sessionId: session.sessionDbId,
|
sessionId: session.sessionDbId,
|
||||||
memorySessionId: session.memorySessionId
|
memorySessionId: session.memorySessionId
|
||||||
});
|
});
|
||||||
@@ -113,7 +134,7 @@ export async function processAgentResponse(
|
|||||||
session.memorySessionId,
|
session.memorySessionId,
|
||||||
session.project,
|
session.project,
|
||||||
observations,
|
observations,
|
||||||
summaryForStore,
|
finalSummaryForStore,
|
||||||
session.lastPromptNumber,
|
session.lastPromptNumber,
|
||||||
discoveryTokens,
|
discoveryTokens,
|
||||||
originalTimestamp ?? undefined,
|
originalTimestamp ?? undefined,
|
||||||
@@ -153,7 +174,7 @@ export async function processAgentResponse(
|
|||||||
// Sync and broadcast summary if present
|
// Sync and broadcast summary if present
|
||||||
await syncAndBroadcastSummary(
|
await syncAndBroadcastSummary(
|
||||||
summary,
|
summary,
|
||||||
summaryForStore,
|
finalSummaryForStore,
|
||||||
result,
|
result,
|
||||||
session,
|
session,
|
||||||
dbManager,
|
dbManager,
|
||||||
|
|||||||
Reference in New Issue
Block a user