@stackmemoryai/stackmemory
Version:
Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.
177 lines (176 loc) • 5.08 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { EnhancedHybridDigestGenerator } from "./enhanced-hybrid-digest.js";
import { logger } from "../monitoring/logger.js";
class FrameDigestIntegration {
frameManager;
digestGenerator;
frameActivityMap = /* @__PURE__ */ new Map();
constructor(frameManager, db, llmProvider) {
this.frameManager = frameManager;
this.digestGenerator = new EnhancedHybridDigestGenerator(
db,
{
enableAIGeneration: true,
maxTokens: 200
},
llmProvider
);
this.setupHooks();
}
/**
* Setup frame lifecycle hooks
*/
setupHooks() {
const originalAddEvent = this.frameManager.addEvent.bind(this.frameManager);
this.frameManager.addEvent = (eventType, payload, frameId) => {
const result = originalAddEvent(eventType, payload, frameId);
if (eventType === "tool_call") {
this.digestGenerator.recordToolCall();
}
if (eventType === "user_message") {
this.digestGenerator.recordUserInput();
}
const targetFrameId = frameId || this.frameManager.getCurrentFrameId();
if (targetFrameId) {
this.frameActivityMap.set(targetFrameId, Date.now());
}
return result;
};
const originalCreateFrame = this.frameManager.createFrame.bind(
this.frameManager
);
this.frameManager.createFrame = (options) => {
const frameId = originalCreateFrame(options);
this.digestGenerator.onFrameOpened(frameId);
this.frameActivityMap.set(frameId, Date.now());
return frameId;
};
const originalCloseFrame = this.frameManager.closeFrame.bind(
this.frameManager
);
this.frameManager.closeFrame = (frameId, outputs) => {
const targetFrameId = frameId || this.frameManager.getCurrentFrameId();
if (targetFrameId) {
const digest = this.generateEnhancedDigest(targetFrameId);
const enhancedOutputs = {
...outputs,
digest: digest.json,
digestText: digest.text
};
this.digestGenerator.onFrameClosed(targetFrameId);
this.frameActivityMap.delete(targetFrameId);
originalCloseFrame(frameId, enhancedOutputs);
} else {
originalCloseFrame(frameId, outputs);
}
};
}
/**
* Generate enhanced digest for a frame
*/
generateEnhancedDigest(frameId) {
const frame = this.frameManager.getFrame(frameId);
if (!frame) {
logger.warn("Frame not found for digest generation", { frameId });
return { text: "", json: {} };
}
const events = this.frameManager.getFrameEvents(frameId);
const anchors = this.getFrameAnchors(frameId);
const digestInput = {
frame: this.convertFrame(frame),
events: events.map(this.convertEvent),
anchors: anchors.map(this.convertAnchor)
};
const hybridDigest = this.digestGenerator.generateDigest(digestInput);
return {
text: hybridDigest.text,
json: {
deterministic: hybridDigest.deterministic,
aiGenerated: hybridDigest.aiGenerated,
status: hybridDigest.status,
generatedAt: Date.now()
}
};
}
/**
* Convert FrameManager frame to DigestInput frame
*/
convertFrame(frame) {
return frame;
}
/**
* Convert FrameManager event to DigestInput event
*/
convertEvent(event) {
return event;
}
/**
* Convert FrameManager anchor to DigestInput anchor
*/
convertAnchor(anchor) {
return anchor;
}
/**
* Calculate importance score based on frame characteristics
*/
calculateImportanceScore(frame) {
let score = 0.5;
const typeScores = {
task: 0.6,
debug: 0.8,
review: 0.7,
write: 0.5,
tool_scope: 0.3,
subtask: 0.4
};
score = typeScores[frame.type] || score;
score -= frame.depth * 0.05;
if (frame.closed_at) {
const durationMinutes = (frame.closed_at - frame.created_at) / 60;
if (durationMinutes > 10) score += 0.1;
if (durationMinutes > 30) score += 0.1;
}
return Math.max(0, Math.min(1, score));
}
/**
* Get frame anchors (wrapper for proper typing)
*/
getFrameAnchors(frameId) {
return [];
}
/**
* Handle user interruption
*/
handleUserInterruption() {
this.digestGenerator.handleInterruption();
}
/**
* Get idle status
*/
getIdleStatus() {
return this.digestGenerator.getIdleStatus();
}
/**
* Force process digest queue
*/
async forceProcessQueue() {
await this.digestGenerator.forceProcessQueue();
}
/**
* Cleanup
*/
shutdown() {
this.digestGenerator.shutdown();
}
}
function enhanceFrameManagerWithDigest(frameManager, db, llmProvider) {
return new FrameDigestIntegration(frameManager, db, llmProvider);
}
export {
FrameDigestIntegration,
enhanceFrameManagerWithDigest
};
//# sourceMappingURL=frame-digest-integration.js.map