feat(headless): expose memory extraction diagnostics
This commit is contained in:
parent
f00762ffdb
commit
90b8e7edf8
2 changed files with 74 additions and 0 deletions
|
|
@ -183,6 +183,17 @@ export interface QuerySnapshot {
|
|||
// section itself so this is an estimate, not a meter.
|
||||
estimated_total_tokens: number;
|
||||
};
|
||||
memoryExtraction?: {
|
||||
total_attempts: number;
|
||||
recent_attempts: Array<{
|
||||
unit_key: string;
|
||||
status: string;
|
||||
failure_class: string | null;
|
||||
reason: string | null;
|
||||
error: string | null;
|
||||
created_at: string;
|
||||
}>;
|
||||
};
|
||||
schedule?: {
|
||||
pending_count: number;
|
||||
overdue_count: number;
|
||||
|
|
@ -413,6 +424,36 @@ export async function buildQuerySnapshot(
|
|||
// runtime_counters unavailable on legacy DBs — fine, drop the section.
|
||||
}
|
||||
|
||||
let memoryExtraction: QuerySnapshot["memoryExtraction"];
|
||||
try {
|
||||
const memoryDbModule = (await jiti.import(
|
||||
sfExtensionPath("sf-db/sf-db-memory"),
|
||||
{},
|
||||
)) as { listMemoryExtractionAttempts: (limit?: number) => any[] };
|
||||
const sfDbModule = (await jiti.import(sfExtensionPath("sf-db"), {})) as {
|
||||
_getAdapter: () => any;
|
||||
};
|
||||
const recent = memoryDbModule.listMemoryExtractionAttempts(5);
|
||||
const adapter = sfDbModule._getAdapter();
|
||||
const total =
|
||||
adapter
|
||||
?.prepare("SELECT count(*) AS cnt FROM memory_extraction_attempts")
|
||||
.get()?.cnt ?? recent.length;
|
||||
memoryExtraction = {
|
||||
total_attempts: Number(total),
|
||||
recent_attempts: recent.map((row) => ({
|
||||
unit_key: String(row.unit_key ?? ""),
|
||||
status: String(row.status ?? ""),
|
||||
failure_class: row.failure_class ? String(row.failure_class) : null,
|
||||
reason: row.reason ? String(row.reason) : null,
|
||||
error: row.error ? String(row.error) : null,
|
||||
created_at: String(row.created_at ?? ""),
|
||||
})),
|
||||
};
|
||||
} catch {
|
||||
// Older DBs may not have the extraction attempt table yet.
|
||||
}
|
||||
|
||||
const snapshot: QuerySnapshot = {
|
||||
schemaVersion: 1,
|
||||
state,
|
||||
|
|
@ -428,6 +469,7 @@ export async function buildQuerySnapshot(
|
|||
},
|
||||
uokDiagnostics,
|
||||
...(memoryInjection ? { memoryInjection } : {}),
|
||||
...(memoryExtraction ? { memoryExtraction } : {}),
|
||||
schedule: scheduleEntries,
|
||||
};
|
||||
|
||||
|
|
|
|||
32
src/tests/headless-query-memory-extraction.test.ts
Normal file
32
src/tests/headless-query-memory-extraction.test.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* Smoke test for memoryExtraction diagnostics in headless-query output.
|
||||
*
|
||||
* Source-level guard: the query snapshot exposes recent extraction attempts
|
||||
* and their failure_class so operators can inspect memory closeout health
|
||||
* without sqlite3 or interactive /memory status.
|
||||
*/
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { test } from "vitest";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const querySrc = readFileSync(
|
||||
join(__dirname, "..", "headless-query.ts"),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
test("QuerySnapshot type declares memoryExtraction section", () => {
|
||||
assert.match(querySrc, /memoryExtraction\?:/);
|
||||
assert.match(querySrc, /total_attempts:\s*number/);
|
||||
assert.match(querySrc, /recent_attempts:/);
|
||||
assert.match(querySrc, /failure_class:\s*string \| null/);
|
||||
});
|
||||
|
||||
test("buildQuerySnapshot reads memory extraction attempts", () => {
|
||||
assert.match(querySrc, /listMemoryExtractionAttempts\(5\)/);
|
||||
assert.match(querySrc, /memory_extraction_attempts/);
|
||||
assert.match(querySrc, /failure_class/);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue