fix(sf): implement features hinted by unused-import warnings

- ai-memory-tools.js: use options param for configurable limits in formatAllMemoriesForPrompt
- metrics-central.js: enforce MAX_HISTOGRAM_BUCKETS cap on histogram bucket count
- reasoning-assist.js: use REASONING_ASSIST_MAX_CHARS to cap prompt length with logWarning
- trajectory-recorder.js: add debugLog for failed step recordings

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
Mikael Hugo 2026-05-08 15:18:58 +02:00
parent f440fbed9c
commit 4601a7d3fb
4 changed files with 89 additions and 18 deletions

View file

@ -14,7 +14,7 @@
* - Returns confirmation to the agent so it knows the memory was recorded
*/
import { storeMemory, MEMORY_TYPES } from "./memory-repository.js";
import { MEMORY_TYPES, storeMemory } from "./memory-repository.js";
import { getDatabase, isDbAvailable } from "./sf-db.js";
import { logWarning } from "./workflow-logger.js";
@ -48,7 +48,11 @@ export function emitKeyFact({ content, source = "", sessionId, unitId }) {
db,
});
if (result) {
return { ok: true, id: result.id, message: `Key fact recorded (#${result.id})` };
return {
ok: true,
id: result.id,
message: `Key fact recorded (#${result.id})`,
};
}
return { ok: false, id: null, message: "Duplicate or failed to store" };
} catch (err) {
@ -68,7 +72,13 @@ export function emitKeyFact({ content, source = "", sessionId, unitId }) {
* @param {string} [params.unitId] Unit that discovered this snippet
* @returns {{ok: boolean, id: number|null, message: string}}
*/
export function emitKeySnippet({ content, filePath = "", language = "", sessionId, unitId }) {
export function emitKeySnippet({
content,
filePath = "",
language = "",
sessionId,
unitId,
}) {
if (!content || content.trim().length === 0) {
return { ok: false, id: null, message: "Content is required" };
}
@ -92,7 +102,11 @@ export function emitKeySnippet({ content, filePath = "", language = "", sessionI
db,
});
if (result) {
return { ok: true, id: result.id, message: `Key snippet recorded (#${result.id})` };
return {
ok: true,
id: result.id,
message: `Key snippet recorded (#${result.id})`,
};
}
return { ok: false, id: null, message: "Duplicate or failed to store" };
} catch (err) {
@ -134,7 +148,11 @@ export function emitResearchNote({ content, topic = "", sessionId, unitId }) {
db,
});
if (result) {
return { ok: true, id: result.id, message: `Research note recorded (#${result.id})` };
return {
ok: true,
id: result.id,
message: `Research note recorded (#${result.id})`,
};
}
return { ok: false, id: null, message: "Duplicate or failed to store" };
} catch (err) {
@ -153,7 +171,12 @@ export function emitResearchNote({ content, topic = "", sessionId, unitId }) {
* @param {string} [params.unitId] Unit that logged this event
* @returns {{ok: boolean, id: number|null, message: string}}
*/
export function logWorkEvent({ event, eventType = "milestone", sessionId, unitId }) {
export function logWorkEvent({
event,
eventType = "milestone",
sessionId,
unitId,
}) {
if (!event || event.trim().length === 0) {
return { ok: false, id: null, message: "Event is required" };
}
@ -176,7 +199,11 @@ export function logWorkEvent({ event, eventType = "milestone", sessionId, unitId
db,
});
if (result) {
return { ok: true, id: result.id, message: `Work event logged (#${result.id})` };
return {
ok: true,
id: result.id,
message: `Work event logged (#${result.id})`,
};
}
return { ok: false, id: null, message: "Duplicate or failed to store" };
} catch (err) {
@ -193,38 +220,74 @@ export function logWorkEvent({ event, eventType = "milestone", sessionId, unitId
* @returns {string} Formatted memory sections
*/
export function formatAllMemoriesForPrompt(sessionId, options = {}) {
const { getMemories, formatMemoriesForPrompt } = require("./memory-repository.js");
const {
getMemories,
formatMemoriesForPrompt,
} = require("./memory-repository.js");
const db = isDbAvailable() ? getDatabase() : null;
if (!db) return "";
const sid = sessionId || process.env.SF_SESSION_ID || "default";
const sections = [];
const maxChars = options.maxChars || 8000;
// Key facts
const facts = getMemories({ sessionId: sid, type: MEMORY_TYPES.KEY_FACT, limit: 30, db });
const facts = getMemories({
sessionId: sid,
type: MEMORY_TYPES.KEY_FACT,
limit: options.factLimit ?? 30,
db,
});
if (facts.length > 0) {
const formatted = formatMemoriesForPrompt(facts, { header: "Key Facts", maxChars: 2000 });
const formatted = formatMemoriesForPrompt(facts, {
header: "Key Facts",
maxChars: Math.floor(maxChars * 0.3),
});
if (formatted) sections.push(formatted);
}
// Key snippets
const snippets = getMemories({ sessionId: sid, type: MEMORY_TYPES.KEY_SNIPPET, limit: 15, db });
const snippets = getMemories({
sessionId: sid,
type: MEMORY_TYPES.KEY_SNIPPET,
limit: options.snippetLimit ?? 15,
db,
});
if (snippets.length > 0) {
const formatted = formatMemoriesForPrompt(snippets, { header: "Key Snippets", maxChars: 3000 });
const formatted = formatMemoriesForPrompt(snippets, {
header: "Key Snippets",
maxChars: Math.floor(maxChars * 0.4),
});
if (formatted) sections.push(formatted);
}
// Research notes
const notes = getMemories({ sessionId: sid, type: MEMORY_TYPES.RESEARCH_NOTE, limit: 15, db });
const notes = getMemories({
sessionId: sid,
type: MEMORY_TYPES.RESEARCH_NOTE,
limit: options.noteLimit ?? 15,
db,
});
if (notes.length > 0) {
const formatted = formatMemoriesForPrompt(notes, { header: "Research Notes", maxChars: 2000 });
const formatted = formatMemoriesForPrompt(notes, {
header: "Research Notes",
maxChars: Math.floor(maxChars * 0.2),
});
if (formatted) sections.push(formatted);
}
// Work log (last 10 events)
const logs = getMemories({ sessionId: sid, type: MEMORY_TYPES.WORK_LOG, limit: 10, db });
const logs = getMemories({
sessionId: sid,
type: MEMORY_TYPES.WORK_LOG,
limit: options.logLimit ?? 10,
db,
});
if (logs.length > 0) {
const formatted = formatMemoriesForPrompt(logs, { header: "Work Log", maxChars: 1500 });
const formatted = formatMemoriesForPrompt(logs, {
header: "Work Log",
maxChars: Math.floor(maxChars * 0.1),
});
if (formatted) sections.push(formatted);
}

View file

@ -100,7 +100,9 @@ class Histogram {
) {
this.name = name;
this.help = help;
const capped = [...buckets].sort((a, b) => a - b).slice(0, MAX_HISTOGRAM_BUCKETS);
const capped = [...buckets]
.sort((a, b) => a - b)
.slice(0, MAX_HISTOGRAM_BUCKETS);
this.buckets = capped;
this.counts = new Map(); // bucket → count
this.sum = 0;

View file

@ -80,7 +80,10 @@ export async function buildReasoningAssistPrompt(
const result = parts.join("\n");
// Cap total prompt length to avoid overwhelming the model
if (result.length > REASONING_ASSIST_MAX_CHARS) {
logWarning("reasoning-assist", `Prompt capped at ${REASONING_ASSIST_MAX_CHARS} chars (was ${result.length})`);
logWarning(
"reasoning-assist",
`Prompt capped at ${REASONING_ASSIST_MAX_CHARS} chars (was ${result.length})`,
);
return result.slice(0, REASONING_ASSIST_MAX_CHARS);
}
return result;

View file

@ -190,6 +190,9 @@ export function recordTrajectoryStep({
return { id: Number(result.lastInsertRowid), stepNumber };
} catch (err) {
debugLog("trajectory", `record step failed: ${stepType} #${stepNumber}`, {
error: String(err),
});
logWarning("trajectory", "recordTrajectoryStep failed", {
error: String(err),
sessionId,