diff --git a/plugins/memory/extract.ts b/plugins/memory/extract.ts index 83e8b20..4b3f90d 100644 --- a/plugins/memory/extract.ts +++ b/plugins/memory/extract.ts @@ -218,6 +218,15 @@ export function redactSecrets(text: string): string { export interface ExtractionOutcome { candidates: Candidate[] rejected: { candidate: Candidate; reason: string }[] + /** + * Set when the round-trip itself failed. Absent means the model answered. + * Without this, a broken call and a conversation with nothing worth keeping + * both come back as zero candidates — which is how a permanently broken + * extractor can look like a healthy one that simply rarely fires. + */ + error?: string + /** Raw model text, truncated, for diagnosing a surprising empty result. */ + rawText?: string } /** @@ -302,7 +311,13 @@ export interface RunExtractionInput { signal?: AbortSignal } -/** One model round-trip. Returns screened candidates and why things were dropped. */ +/** + * One model round-trip. Returns screened candidates and why things were dropped. + * + * A transport failure is reported in `error` rather than collapsed into an + * empty result: the caller's only other signal would be "no candidates", which + * is also what a correct answer looks like when nothing was worth keeping. + */ export async function extract(input: RunExtractionInput): Promise { const prompt = `${SYSTEM}\n\n---\n\n${input.transcript}` let result: unknown @@ -314,11 +329,34 @@ export async function extract(input: RunExtractionInput): Promise