@stackmemoryai/stackmemory 0.3.15 → 0.3.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -8
- package/dist/cli/commands/context-rehydrate.js +747 -0
- package/dist/cli/commands/context-rehydrate.js.map +7 -0
- package/dist/cli/commands/context.js +2 -0
- package/dist/cli/commands/context.js.map +2 -2
- package/dist/core/context/enhanced-rehydration.js +644 -0
- package/dist/core/context/enhanced-rehydration.js.map +7 -0
- package/dist/core/context/frame-database.js +2 -6
- package/dist/core/context/frame-database.js.map +2 -2
- package/dist/skills/claude-skills.js.map +2 -2
- package/dist/skills/recursive-agent-orchestrator.js +4 -1
- package/dist/skills/recursive-agent-orchestrator.js.map +2 -2
- package/dist/skills/unified-rlm-orchestrator.js.map +2 -2
- package/package.json +1 -1
- package/templates/claude-hooks/on-compact-detected +57 -0
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { logger } from "../monitoring/logger.js";
|
|
4
|
+
class EnhancedRehydrationManager {
|
|
5
|
+
frameManager;
|
|
6
|
+
compactionHandler;
|
|
7
|
+
snapshotThreshold = 10;
|
|
8
|
+
// Take snapshot every N significant events
|
|
9
|
+
eventCount = 0;
|
|
10
|
+
rehydrationStorage = /* @__PURE__ */ new Map();
|
|
11
|
+
constructor(frameManager, compactionHandler) {
|
|
12
|
+
this.frameManager = frameManager;
|
|
13
|
+
this.compactionHandler = compactionHandler;
|
|
14
|
+
this.setupCompactDetection();
|
|
15
|
+
this.initializeStackTraceStorage();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Initialize dedicated stack trace storage in database
|
|
19
|
+
*/
|
|
20
|
+
initializeStackTraceStorage() {
|
|
21
|
+
try {
|
|
22
|
+
const db = this.frameManager.db;
|
|
23
|
+
db.exec(`
|
|
24
|
+
CREATE TABLE IF NOT EXISTS stack_traces (
|
|
25
|
+
trace_id TEXT PRIMARY KEY,
|
|
26
|
+
frame_id TEXT,
|
|
27
|
+
project_id TEXT NOT NULL,
|
|
28
|
+
error_message TEXT NOT NULL,
|
|
29
|
+
stack_frames TEXT NOT NULL,
|
|
30
|
+
file_path TEXT,
|
|
31
|
+
line_number INTEGER,
|
|
32
|
+
function_name TEXT,
|
|
33
|
+
context TEXT,
|
|
34
|
+
resolution_attempted TEXT,
|
|
35
|
+
resolution_status TEXT NOT NULL DEFAULT 'pending',
|
|
36
|
+
error_type TEXT,
|
|
37
|
+
error_severity TEXT DEFAULT 'medium',
|
|
38
|
+
created_at INTEGER DEFAULT (unixepoch()),
|
|
39
|
+
updated_at INTEGER DEFAULT (unixepoch()),
|
|
40
|
+
FOREIGN KEY(frame_id) REFERENCES frames(frame_id)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_stack_traces_frame ON stack_traces(frame_id);
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_stack_traces_status ON stack_traces(resolution_status);
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_stack_traces_type ON stack_traces(error_type);
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_stack_traces_severity ON stack_traces(error_severity);
|
|
47
|
+
CREATE INDEX IF NOT EXISTS idx_stack_traces_created ON stack_traces(created_at);
|
|
48
|
+
`);
|
|
49
|
+
logger.info("Stack trace storage initialized");
|
|
50
|
+
} catch (error) {
|
|
51
|
+
logger.error("Failed to initialize stack trace storage:", error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Set up automatic compact detection and recovery
|
|
56
|
+
*/
|
|
57
|
+
setupCompactDetection() {
|
|
58
|
+
setInterval(() => this.checkForCompactionEvent(), 3e4);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Enhanced file content snapshot with context
|
|
62
|
+
*/
|
|
63
|
+
async captureFileSnapshot(filePath, contextTags = []) {
|
|
64
|
+
try {
|
|
65
|
+
const stats = await fs.stat(filePath);
|
|
66
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
67
|
+
const hash = this.simpleHash(content);
|
|
68
|
+
return {
|
|
69
|
+
path: filePath,
|
|
70
|
+
content,
|
|
71
|
+
size: stats.size,
|
|
72
|
+
lastModified: stats.mtimeMs,
|
|
73
|
+
hash,
|
|
74
|
+
contextTags
|
|
75
|
+
};
|
|
76
|
+
} catch (error) {
|
|
77
|
+
logger.warn(`Failed to capture snapshot for ${filePath}:`, error);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Capture conversation reasoning and decisions including stack traces
|
|
83
|
+
*/
|
|
84
|
+
captureConversationContext(reasoning, decisions, nextSteps = [], userPrefs = {}, painPoints = [], stackTraces = [], errorPatterns = []) {
|
|
85
|
+
return {
|
|
86
|
+
timestamp: Date.now(),
|
|
87
|
+
reasoning,
|
|
88
|
+
decisions_made: decisions,
|
|
89
|
+
next_steps: nextSteps,
|
|
90
|
+
user_preferences: userPrefs,
|
|
91
|
+
pain_points: painPoints,
|
|
92
|
+
stack_traces: stackTraces,
|
|
93
|
+
error_patterns: errorPatterns
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Capture stack trace from error with context and store in database
|
|
98
|
+
*/
|
|
99
|
+
captureStackTrace(error, context, filePath, resolutionAttempts = [], frameId) {
|
|
100
|
+
const errorMessage = typeof error === "string" ? error : error.message;
|
|
101
|
+
const stackFrames = typeof error === "string" ? [] : error.stack?.split("\n") || [];
|
|
102
|
+
let extractedFilePath = filePath;
|
|
103
|
+
let lineNumber;
|
|
104
|
+
let functionName;
|
|
105
|
+
if (stackFrames.length > 0) {
|
|
106
|
+
const firstFrame = stackFrames.find((frame) => frame.includes("at "));
|
|
107
|
+
if (firstFrame) {
|
|
108
|
+
const match = firstFrame.match(/at (.+?) \((.+):(\d+):(\d+)\)/);
|
|
109
|
+
if (match) {
|
|
110
|
+
functionName = match[1];
|
|
111
|
+
extractedFilePath = extractedFilePath || match[2];
|
|
112
|
+
lineNumber = parseInt(match[3]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const stackTrace = {
|
|
117
|
+
error_message: errorMessage,
|
|
118
|
+
stack_frames: stackFrames,
|
|
119
|
+
file_path: extractedFilePath,
|
|
120
|
+
line_number: lineNumber,
|
|
121
|
+
function_name: functionName,
|
|
122
|
+
timestamp: Date.now(),
|
|
123
|
+
context,
|
|
124
|
+
resolution_attempted: resolutionAttempts,
|
|
125
|
+
resolution_status: "pending"
|
|
126
|
+
};
|
|
127
|
+
this.storeStackTrace(stackTrace, frameId);
|
|
128
|
+
return stackTrace;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Store stack trace in database
|
|
132
|
+
*/
|
|
133
|
+
storeStackTrace(stackTrace, frameId) {
|
|
134
|
+
try {
|
|
135
|
+
const db = this.frameManager.db;
|
|
136
|
+
const traceId = this.generateTraceId();
|
|
137
|
+
const currentFrameId = frameId || this.frameManager.getCurrentFrameId();
|
|
138
|
+
const errorType = this.extractErrorType(stackTrace.error_message);
|
|
139
|
+
const severity = this.determineErrorSeverity(stackTrace);
|
|
140
|
+
const stmt = db.prepare(`
|
|
141
|
+
INSERT INTO stack_traces (
|
|
142
|
+
trace_id, frame_id, project_id, error_message, stack_frames,
|
|
143
|
+
file_path, line_number, function_name, context, resolution_attempted,
|
|
144
|
+
resolution_status, error_type, error_severity
|
|
145
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
146
|
+
`);
|
|
147
|
+
stmt.run(
|
|
148
|
+
traceId,
|
|
149
|
+
currentFrameId,
|
|
150
|
+
this.frameManager.projectId,
|
|
151
|
+
stackTrace.error_message,
|
|
152
|
+
JSON.stringify(stackTrace.stack_frames),
|
|
153
|
+
stackTrace.file_path,
|
|
154
|
+
stackTrace.line_number,
|
|
155
|
+
stackTrace.function_name,
|
|
156
|
+
stackTrace.context,
|
|
157
|
+
JSON.stringify(stackTrace.resolution_attempted),
|
|
158
|
+
stackTrace.resolution_status,
|
|
159
|
+
errorType,
|
|
160
|
+
severity
|
|
161
|
+
);
|
|
162
|
+
logger.info(`Stored stack trace ${traceId} for frame ${currentFrameId}`);
|
|
163
|
+
return traceId;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
logger.error("Failed to store stack trace:", error);
|
|
166
|
+
return "";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Retrieve stack traces from database
|
|
171
|
+
*/
|
|
172
|
+
getStackTraces(frameId, limit = 50) {
|
|
173
|
+
try {
|
|
174
|
+
const db = this.frameManager.db;
|
|
175
|
+
const traces = [];
|
|
176
|
+
let query;
|
|
177
|
+
let params;
|
|
178
|
+
if (frameId) {
|
|
179
|
+
query = `
|
|
180
|
+
SELECT * FROM stack_traces
|
|
181
|
+
WHERE frame_id = ?
|
|
182
|
+
ORDER BY created_at DESC
|
|
183
|
+
LIMIT ?
|
|
184
|
+
`;
|
|
185
|
+
params = [frameId, limit];
|
|
186
|
+
} else {
|
|
187
|
+
query = `
|
|
188
|
+
SELECT * FROM stack_traces
|
|
189
|
+
WHERE project_id = ?
|
|
190
|
+
ORDER BY created_at DESC
|
|
191
|
+
LIMIT ?
|
|
192
|
+
`;
|
|
193
|
+
params = [this.frameManager.projectId, limit];
|
|
194
|
+
}
|
|
195
|
+
const rows = db.prepare(query).all(...params);
|
|
196
|
+
for (const row of rows) {
|
|
197
|
+
traces.push({
|
|
198
|
+
error_message: row.error_message,
|
|
199
|
+
stack_frames: JSON.parse(row.stack_frames || "[]"),
|
|
200
|
+
file_path: row.file_path,
|
|
201
|
+
line_number: row.line_number,
|
|
202
|
+
function_name: row.function_name,
|
|
203
|
+
timestamp: row.created_at * 1e3,
|
|
204
|
+
// Convert from unix to JS timestamp
|
|
205
|
+
context: row.context,
|
|
206
|
+
resolution_attempted: JSON.parse(row.resolution_attempted || "[]"),
|
|
207
|
+
resolution_status: row.resolution_status
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return traces;
|
|
211
|
+
} catch (error) {
|
|
212
|
+
logger.error("Failed to retrieve stack traces:", error);
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Update stack trace resolution status
|
|
218
|
+
*/
|
|
219
|
+
updateStackTraceStatus(traceId, status, resolutionAttempts) {
|
|
220
|
+
try {
|
|
221
|
+
const db = this.frameManager.db;
|
|
222
|
+
const stmt = db.prepare(`
|
|
223
|
+
UPDATE stack_traces
|
|
224
|
+
SET resolution_status = ?, resolution_attempted = ?, updated_at = unixepoch()
|
|
225
|
+
WHERE trace_id = ?
|
|
226
|
+
`);
|
|
227
|
+
const result = stmt.run(
|
|
228
|
+
status,
|
|
229
|
+
resolutionAttempts ? JSON.stringify(resolutionAttempts) : void 0,
|
|
230
|
+
traceId
|
|
231
|
+
);
|
|
232
|
+
return result.changes > 0;
|
|
233
|
+
} catch (error) {
|
|
234
|
+
logger.error("Failed to update stack trace status:", error);
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Helper methods for stack trace processing
|
|
240
|
+
*/
|
|
241
|
+
generateTraceId() {
|
|
242
|
+
return `trace_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
243
|
+
}
|
|
244
|
+
extractErrorType(errorMessage) {
|
|
245
|
+
const typeMatch = errorMessage.match(/^(\w+Error?):/);
|
|
246
|
+
return typeMatch ? typeMatch[1] : "Unknown";
|
|
247
|
+
}
|
|
248
|
+
determineErrorSeverity(stackTrace) {
|
|
249
|
+
const message = stackTrace.error_message.toLowerCase();
|
|
250
|
+
if (message.includes("critical") || message.includes("fatal") || message.includes("cannot read properties")) {
|
|
251
|
+
return "high";
|
|
252
|
+
} else if (message.includes("warning") || message.includes("deprecated")) {
|
|
253
|
+
return "low";
|
|
254
|
+
} else {
|
|
255
|
+
return "medium";
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Auto-detect project structure and relationships
|
|
260
|
+
*/
|
|
261
|
+
async analyzeProjectMapping(workingDir) {
|
|
262
|
+
const mapping = {
|
|
263
|
+
file_relationships: {},
|
|
264
|
+
workflow_sequences: [],
|
|
265
|
+
key_directories: [],
|
|
266
|
+
entry_points: [],
|
|
267
|
+
configuration_files: []
|
|
268
|
+
};
|
|
269
|
+
try {
|
|
270
|
+
const configPatterns = [
|
|
271
|
+
"package.json",
|
|
272
|
+
"tsconfig.json",
|
|
273
|
+
".env",
|
|
274
|
+
"docker-compose.yml",
|
|
275
|
+
"*.config.js",
|
|
276
|
+
"*.config.ts",
|
|
277
|
+
"Dockerfile",
|
|
278
|
+
"README.md"
|
|
279
|
+
];
|
|
280
|
+
const files = await this.getDirectoryFiles(workingDir);
|
|
281
|
+
for (const file of files) {
|
|
282
|
+
const ext = path.extname(file);
|
|
283
|
+
const basename = path.basename(file);
|
|
284
|
+
if (configPatterns.some(
|
|
285
|
+
(pattern) => pattern.includes("*") ? basename.includes(pattern.replace("*", "")) : basename === pattern
|
|
286
|
+
)) {
|
|
287
|
+
mapping.configuration_files.push(file);
|
|
288
|
+
}
|
|
289
|
+
if (basename === "index.js" || basename === "index.ts" || basename === "main.js") {
|
|
290
|
+
mapping.entry_points.push(file);
|
|
291
|
+
}
|
|
292
|
+
const filePrefix = basename.split(".")[0];
|
|
293
|
+
const relatedFiles = files.filter(
|
|
294
|
+
(f) => f !== file && path.basename(f).startsWith(filePrefix)
|
|
295
|
+
);
|
|
296
|
+
if (relatedFiles.length > 0) {
|
|
297
|
+
mapping.file_relationships[file] = relatedFiles;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const dirs = files.map((f) => path.dirname(f)).filter((v, i, a) => a.indexOf(v) === i);
|
|
301
|
+
mapping.key_directories = dirs.filter(
|
|
302
|
+
(dir) => ["src", "lib", "components", "pages", "api", "utils", "types"].some((key) => dir.includes(key))
|
|
303
|
+
);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
logger.warn("Failed to analyze project mapping:", error);
|
|
306
|
+
}
|
|
307
|
+
return mapping;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Create comprehensive rehydration context before compaction
|
|
311
|
+
*/
|
|
312
|
+
async createRehydrationCheckpoint() {
|
|
313
|
+
const sessionId = this.frameManager.getSessionId() || "unknown";
|
|
314
|
+
const checkpointId = `${sessionId}_${Date.now()}`;
|
|
315
|
+
try {
|
|
316
|
+
const workingDir = process.cwd();
|
|
317
|
+
const fileSnapshots = [];
|
|
318
|
+
const recentFiles = await this.getRecentlyModifiedFiles(workingDir);
|
|
319
|
+
for (const file of recentFiles.slice(0, 20)) {
|
|
320
|
+
const snapshot = await this.captureFileSnapshot(file, this.inferContextTags(file));
|
|
321
|
+
if (snapshot) {
|
|
322
|
+
fileSnapshots.push(snapshot);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const projectMapping = await this.analyzeProjectMapping(workingDir);
|
|
326
|
+
const conversationContext = this.extractConversationContext();
|
|
327
|
+
const rehydrationContext = {
|
|
328
|
+
session_id: sessionId,
|
|
329
|
+
compact_detected_at: Date.now(),
|
|
330
|
+
pre_compact_state: {
|
|
331
|
+
file_snapshots: fileSnapshots,
|
|
332
|
+
conversation_context: conversationContext,
|
|
333
|
+
project_mapping: projectMapping,
|
|
334
|
+
active_workflows: this.detectActiveWorkflows(fileSnapshots),
|
|
335
|
+
current_focus: this.inferCurrentFocus(fileSnapshots, conversationContext)
|
|
336
|
+
},
|
|
337
|
+
recovery_anchors: this.createRecoveryAnchors(fileSnapshots, conversationContext)
|
|
338
|
+
};
|
|
339
|
+
this.rehydrationStorage.set(checkpointId, rehydrationContext);
|
|
340
|
+
await this.persistRehydrationContext(checkpointId, rehydrationContext);
|
|
341
|
+
logger.info(`Created rehydration checkpoint ${checkpointId} with ${fileSnapshots.length} file snapshots`);
|
|
342
|
+
return checkpointId;
|
|
343
|
+
} catch (error) {
|
|
344
|
+
logger.error("Failed to create rehydration checkpoint:", error);
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Inject rich context after compaction detection
|
|
350
|
+
*/
|
|
351
|
+
async rehydrateContext(checkpointId) {
|
|
352
|
+
try {
|
|
353
|
+
let context;
|
|
354
|
+
if (checkpointId) {
|
|
355
|
+
context = this.rehydrationStorage.get(checkpointId);
|
|
356
|
+
if (!context) {
|
|
357
|
+
context = await this.loadPersistedContext(checkpointId);
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
context = await this.findMostRecentContext();
|
|
361
|
+
}
|
|
362
|
+
if (!context) {
|
|
363
|
+
logger.warn("No rehydration context available");
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
await this.injectRichContext(context);
|
|
367
|
+
return true;
|
|
368
|
+
} catch (error) {
|
|
369
|
+
logger.error("Failed to rehydrate context:", error);
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Inject rich context into current session
|
|
375
|
+
*/
|
|
376
|
+
async injectRichContext(context) {
|
|
377
|
+
const frameId = this.frameManager.getCurrentFrameId();
|
|
378
|
+
if (!frameId) {
|
|
379
|
+
logger.warn("No active frame for context injection");
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
for (const snapshot of context.pre_compact_state.file_snapshots.slice(0, 5)) {
|
|
383
|
+
this.frameManager.addAnchor(
|
|
384
|
+
"FACT",
|
|
385
|
+
`File: ${snapshot.path} (${snapshot.contextTags.join(", ")})
|
|
386
|
+
Last modified: ${new Date(snapshot.lastModified).toISOString()}
|
|
387
|
+
Size: ${snapshot.size} bytes
|
|
388
|
+
Content preview: ${this.getContentPreview(snapshot.content)}`,
|
|
389
|
+
9,
|
|
390
|
+
{
|
|
391
|
+
rehydration: true,
|
|
392
|
+
file_path: snapshot.path,
|
|
393
|
+
context_tags: snapshot.contextTags
|
|
394
|
+
},
|
|
395
|
+
frameId
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
const conv = context.pre_compact_state.conversation_context;
|
|
399
|
+
if (conv.decisions_made.length > 0) {
|
|
400
|
+
this.frameManager.addAnchor(
|
|
401
|
+
"DECISION",
|
|
402
|
+
`Previous decisions: ${conv.decisions_made.join("; ")}`,
|
|
403
|
+
8,
|
|
404
|
+
{ rehydration: true },
|
|
405
|
+
frameId
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
if (conv.next_steps.length > 0) {
|
|
409
|
+
this.frameManager.addAnchor(
|
|
410
|
+
"FACT",
|
|
411
|
+
`Next steps identified: ${conv.next_steps.join("; ")}`,
|
|
412
|
+
7,
|
|
413
|
+
{ rehydration: true },
|
|
414
|
+
frameId
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
if (conv.stack_traces.length > 0) {
|
|
418
|
+
for (const trace of conv.stack_traces.slice(0, 3)) {
|
|
419
|
+
this.frameManager.addAnchor(
|
|
420
|
+
"ERROR",
|
|
421
|
+
`Error context: ${trace.error_message}
|
|
422
|
+
Context: ${trace.context}
|
|
423
|
+
File: ${trace.file_path || "unknown"}${trace.line_number ? `:${trace.line_number}` : ""}
|
|
424
|
+
Function: ${trace.function_name || "unknown"}
|
|
425
|
+
Status: ${trace.resolution_status}
|
|
426
|
+
Stack preview: ${trace.stack_frames.slice(0, 3).join("\n")}`,
|
|
427
|
+
9,
|
|
428
|
+
{
|
|
429
|
+
rehydration: true,
|
|
430
|
+
error_type: trace.error_message.split(":")[0],
|
|
431
|
+
resolution_status: trace.resolution_status,
|
|
432
|
+
file_path: trace.file_path
|
|
433
|
+
},
|
|
434
|
+
frameId
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (conv.error_patterns.length > 0) {
|
|
439
|
+
this.frameManager.addAnchor(
|
|
440
|
+
"PATTERN",
|
|
441
|
+
`Recurring error patterns detected: ${conv.error_patterns.join(", ")}`,
|
|
442
|
+
7,
|
|
443
|
+
{ rehydration: true },
|
|
444
|
+
frameId
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
const mapping = context.pre_compact_state.project_mapping;
|
|
448
|
+
if (mapping.entry_points.length > 0) {
|
|
449
|
+
this.frameManager.addAnchor(
|
|
450
|
+
"FACT",
|
|
451
|
+
`Project entry points: ${mapping.entry_points.join(", ")}`,
|
|
452
|
+
6,
|
|
453
|
+
{ rehydration: true },
|
|
454
|
+
frameId
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
if (context.pre_compact_state.current_focus) {
|
|
458
|
+
this.frameManager.addAnchor(
|
|
459
|
+
"CONSTRAINT",
|
|
460
|
+
`Previous focus: ${context.pre_compact_state.current_focus}`,
|
|
461
|
+
8,
|
|
462
|
+
{ rehydration: true },
|
|
463
|
+
frameId
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
logger.info("Rich context injected successfully");
|
|
467
|
+
}
|
|
468
|
+
// Helper methods
|
|
469
|
+
async getDirectoryFiles(dir) {
|
|
470
|
+
return [];
|
|
471
|
+
}
|
|
472
|
+
async getRecentlyModifiedFiles(dir) {
|
|
473
|
+
return [];
|
|
474
|
+
}
|
|
475
|
+
inferContextTags(filePath) {
|
|
476
|
+
const tags = [];
|
|
477
|
+
const content = filePath.toLowerCase();
|
|
478
|
+
if (content.includes("pipeline") || content.includes("migrate")) tags.push("migration");
|
|
479
|
+
if (content.includes("hubspot")) tags.push("hubspot");
|
|
480
|
+
if (content.includes("pipedream")) tags.push("pipedream");
|
|
481
|
+
if (content.includes("test")) tags.push("test");
|
|
482
|
+
if (content.includes("config")) tags.push("configuration");
|
|
483
|
+
return tags;
|
|
484
|
+
}
|
|
485
|
+
extractConversationContext() {
|
|
486
|
+
const recentErrors = this.extractRecentStackTraces();
|
|
487
|
+
const errorPatterns = this.detectErrorPatterns(recentErrors);
|
|
488
|
+
return {
|
|
489
|
+
timestamp: Date.now(),
|
|
490
|
+
reasoning: [],
|
|
491
|
+
decisions_made: [],
|
|
492
|
+
next_steps: [],
|
|
493
|
+
user_preferences: {},
|
|
494
|
+
pain_points: [],
|
|
495
|
+
stack_traces: recentErrors,
|
|
496
|
+
error_patterns: errorPatterns
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Extract recent stack traces from database and frame events
|
|
501
|
+
*/
|
|
502
|
+
extractRecentStackTraces() {
|
|
503
|
+
try {
|
|
504
|
+
const dbTraces = this.getStackTraces(void 0, 10);
|
|
505
|
+
const eventTraces = this.extractStackTracesFromFrameEvents();
|
|
506
|
+
const allTraces = [...dbTraces, ...eventTraces];
|
|
507
|
+
const uniqueTraces = allTraces.filter(
|
|
508
|
+
(trace, index, array) => array.findIndex(
|
|
509
|
+
(t) => t.error_message === trace.error_message && t.file_path === trace.file_path
|
|
510
|
+
) === index
|
|
511
|
+
);
|
|
512
|
+
return uniqueTraces.sort((a, b) => b.timestamp - a.timestamp).slice(0, 5);
|
|
513
|
+
} catch (error) {
|
|
514
|
+
logger.warn("Failed to extract stack traces:", error);
|
|
515
|
+
return [];
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Extract stack traces from frame events (fallback method)
|
|
520
|
+
*/
|
|
521
|
+
extractStackTracesFromFrameEvents() {
|
|
522
|
+
const traces = [];
|
|
523
|
+
try {
|
|
524
|
+
const frames = this.frameManager.getActiveFramePath();
|
|
525
|
+
for (const frame of frames.slice(-3)) {
|
|
526
|
+
const frameData = this.frameManager.getFrame(frame.frame_id);
|
|
527
|
+
if (frameData?.events) {
|
|
528
|
+
for (const event of frameData.events) {
|
|
529
|
+
if (event.type === "error" || event.type === "exception") {
|
|
530
|
+
const trace = this.parseStackTraceFromEvent(event);
|
|
531
|
+
if (trace) {
|
|
532
|
+
traces.push(trace);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
} catch (error) {
|
|
539
|
+
logger.warn("Failed to extract frame event traces:", error);
|
|
540
|
+
}
|
|
541
|
+
return traces;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Parse stack trace from frame event
|
|
545
|
+
*/
|
|
546
|
+
parseStackTraceFromEvent(event) {
|
|
547
|
+
try {
|
|
548
|
+
const data = typeof event.data === "string" ? JSON.parse(event.data) : event.data;
|
|
549
|
+
return {
|
|
550
|
+
error_message: data.error || data.message || "Unknown error",
|
|
551
|
+
stack_frames: data.stack ? data.stack.split("\n") : [],
|
|
552
|
+
file_path: data.file || data.fileName,
|
|
553
|
+
line_number: data.line || data.lineNumber,
|
|
554
|
+
function_name: data.function || data.functionName,
|
|
555
|
+
timestamp: event.timestamp || Date.now(),
|
|
556
|
+
context: data.context || "Error occurred during frame processing",
|
|
557
|
+
resolution_attempted: data.resolutionAttempts || [],
|
|
558
|
+
resolution_status: data.resolved ? "resolved" : "pending"
|
|
559
|
+
};
|
|
560
|
+
} catch (error) {
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Detect recurring error patterns
|
|
566
|
+
*/
|
|
567
|
+
detectErrorPatterns(traces) {
|
|
568
|
+
const patterns = /* @__PURE__ */ new Map();
|
|
569
|
+
for (const trace of traces) {
|
|
570
|
+
const errorType = trace.error_message.split(":")[0].trim();
|
|
571
|
+
patterns.set(errorType, (patterns.get(errorType) || 0) + 1);
|
|
572
|
+
}
|
|
573
|
+
return Array.from(patterns.entries()).filter(([, count]) => count > 1).map(([pattern]) => pattern);
|
|
574
|
+
}
|
|
575
|
+
detectActiveWorkflows(snapshots) {
|
|
576
|
+
const workflows = [];
|
|
577
|
+
for (const snapshot of snapshots) {
|
|
578
|
+
if (snapshot.contextTags.includes("migration")) {
|
|
579
|
+
workflows.push("data_migration");
|
|
580
|
+
}
|
|
581
|
+
if (snapshot.path.includes("test")) {
|
|
582
|
+
workflows.push("testing");
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return [...new Set(workflows)];
|
|
586
|
+
}
|
|
587
|
+
inferCurrentFocus(snapshots, context) {
|
|
588
|
+
if (snapshots.some((s) => s.contextTags.includes("migration"))) {
|
|
589
|
+
return "Data migration and transformation";
|
|
590
|
+
}
|
|
591
|
+
if (snapshots.some((s) => s.path.includes("test"))) {
|
|
592
|
+
return "Testing and validation";
|
|
593
|
+
}
|
|
594
|
+
return "Development";
|
|
595
|
+
}
|
|
596
|
+
createRecoveryAnchors(snapshots, context) {
|
|
597
|
+
const anchors = [];
|
|
598
|
+
for (const snapshot of snapshots.slice(0, 3)) {
|
|
599
|
+
anchors.push(`File context: ${snapshot.path} with ${snapshot.contextTags.join(", ")}`);
|
|
600
|
+
}
|
|
601
|
+
return anchors;
|
|
602
|
+
}
|
|
603
|
+
async persistRehydrationContext(id, context) {
|
|
604
|
+
const contextDir = path.join(process.cwd(), ".stackmemory", "rehydration");
|
|
605
|
+
await fs.mkdir(contextDir, { recursive: true });
|
|
606
|
+
await fs.writeFile(
|
|
607
|
+
path.join(contextDir, `${id}.json`),
|
|
608
|
+
JSON.stringify(context, null, 2)
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
async loadPersistedContext(id) {
|
|
612
|
+
try {
|
|
613
|
+
const contextPath = path.join(process.cwd(), ".stackmemory", "rehydration", `${id}.json`);
|
|
614
|
+
const content = await fs.readFile(contextPath, "utf8");
|
|
615
|
+
return JSON.parse(content);
|
|
616
|
+
} catch {
|
|
617
|
+
return void 0;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
async findMostRecentContext() {
|
|
621
|
+
return void 0;
|
|
622
|
+
}
|
|
623
|
+
checkForCompactionEvent() {
|
|
624
|
+
if (this.compactionHandler.detectCompactionEvent("")) {
|
|
625
|
+
this.rehydrateContext();
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
simpleHash(content) {
|
|
629
|
+
let hash = 0;
|
|
630
|
+
for (let i = 0; i < content.length; i++) {
|
|
631
|
+
const char = content.charCodeAt(i);
|
|
632
|
+
hash = (hash << 5) - hash + char;
|
|
633
|
+
hash = hash & hash;
|
|
634
|
+
}
|
|
635
|
+
return hash.toString(16);
|
|
636
|
+
}
|
|
637
|
+
getContentPreview(content, maxLength = 200) {
|
|
638
|
+
return content.length > maxLength ? content.substring(0, maxLength) + "..." : content;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
export {
|
|
642
|
+
EnhancedRehydrationManager
|
|
643
|
+
};
|
|
644
|
+
//# sourceMappingURL=enhanced-rehydration.js.map
|