@stackmemoryai/stackmemory 1.2.2 → 1.2.6
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 +10 -6
- package/dist/src/cli/claude-sm.js +33 -4
- package/dist/src/cli/codex-sm-danger.js +4 -1
- package/dist/src/cli/codex-sm.js +6 -8
- package/dist/src/cli/commands/config.js +0 -81
- package/dist/src/cli/commands/context-rehydrate.js +133 -47
- package/dist/src/cli/commands/db.js +35 -8
- package/dist/src/cli/commands/handoff.js +1 -1
- package/dist/src/cli/commands/linear.js +9 -0
- package/dist/src/cli/commands/ralph.js +2 -2
- package/dist/src/cli/commands/setup.js +2 -2
- package/dist/src/cli/commands/signup.js +3 -1
- package/dist/src/cli/commands/skills.js +108 -1
- package/dist/src/cli/commands/storage-tier.js +26 -8
- package/dist/src/cli/index.js +1 -57
- package/dist/src/core/config/feature-flags.js +0 -4
- package/dist/src/core/context/dual-stack-manager.js +10 -3
- package/dist/src/core/context/frame-database.js +32 -0
- package/dist/src/core/context/frame-handoff-manager.js +2 -2
- package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
- package/dist/src/core/context/index.js +2 -2
- package/dist/src/core/database/sqlite-adapter.js +161 -1
- package/dist/src/core/digest/frame-digest-integration.js +1 -1
- package/dist/src/core/digest/index.js +1 -1
- package/dist/src/core/execution/parallel-executor.js +5 -1
- package/dist/src/core/projects/project-isolation.js +18 -4
- package/dist/src/core/security/index.js +2 -0
- package/dist/src/core/security/input-sanitizer.js +23 -0
- package/dist/src/core/utils/update-checker.js +10 -6
- package/dist/src/daemon/daemon-config.js +2 -1
- package/dist/src/daemon/services/auto-save-service.js +121 -0
- package/dist/src/daemon/services/maintenance-service.js +76 -1
- package/dist/src/features/sweep/prompt-builder.js +2 -2
- package/dist/src/hooks/daemon.js +0 -8
- package/dist/src/integrations/linear/config.js +3 -1
- package/dist/src/integrations/linear/sync.js +18 -5
- package/dist/src/integrations/linear/webhook.js +0 -16
- package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
- package/dist/src/integrations/mcp/handlers/index.js +55 -9
- package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
- package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.js +28 -9
- package/dist/src/integrations/mcp/index.js +2 -2
- package/dist/src/integrations/mcp/refactored-server.js +31 -10
- package/dist/src/integrations/mcp/server.js +0 -130
- package/dist/src/integrations/mcp/tool-definitions.js +196 -67
- package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
- package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
- package/dist/src/skills/claude-skills.js +37 -103
- package/dist/src/skills/theory-skill.js +191 -0
- package/dist/src/utils/hook-installer.js +16 -0
- package/package.json +6 -6
- package/scripts/install-claude-hooks-auto.js +8 -0
- package/templates/claude-hooks/cord-trace.js +225 -0
- package/templates/claude-hooks/theory-capture.js +100 -0
- package/dist/src/core/config/storage-config.js +0 -114
- package/dist/src/core/storage/chromadb-adapter.js +0 -379
- package/dist/src/hooks/graphiti-hooks.js +0 -253
- package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
- package/dist/src/integrations/graphiti/client.js +0 -115
- package/dist/src/integrations/graphiti/config.js +0 -17
- package/dist/src/integrations/graphiti/linear-graphiti-bridge.js +0 -115
- package/dist/src/integrations/graphiti/types.js +0 -4
- package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
- package/dist/src/skills/repo-ingestion-skill.js +0 -631
- package/templates/claude-hooks/chromadb-wrapper +0 -21
- /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
- /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
- /package/dist/src/core/session/{enhanced-handoff.js → handoff.js} +0 -0
|
@@ -1,379 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
-
import { dirname as __pathDirname } from 'path';
|
|
3
|
-
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
-
const __dirname = __pathDirname(__filename);
|
|
5
|
-
import { v4 as uuidv4 } from "uuid";
|
|
6
|
-
import { Logger } from "../monitoring/logger.js";
|
|
7
|
-
let chromadbModule = null;
|
|
8
|
-
async function getChromaDB() {
|
|
9
|
-
if (!chromadbModule) {
|
|
10
|
-
try {
|
|
11
|
-
chromadbModule = await import("chromadb");
|
|
12
|
-
} catch {
|
|
13
|
-
throw new Error(
|
|
14
|
-
"chromadb is not installed. Install it with: npm install chromadb"
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return chromadbModule;
|
|
19
|
-
}
|
|
20
|
-
class ChromaDBAdapter {
|
|
21
|
-
client = null;
|
|
22
|
-
collection = null;
|
|
23
|
-
logger;
|
|
24
|
-
config;
|
|
25
|
-
userId;
|
|
26
|
-
teamId;
|
|
27
|
-
constructor(config, userId, teamId) {
|
|
28
|
-
this.config = config;
|
|
29
|
-
this.userId = userId;
|
|
30
|
-
this.teamId = teamId;
|
|
31
|
-
this.logger = new Logger("ChromaDBAdapter");
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Factory method to create and initialize the adapter
|
|
35
|
-
*/
|
|
36
|
-
static async create(config, userId, teamId) {
|
|
37
|
-
const adapter = new ChromaDBAdapter(config, userId, teamId);
|
|
38
|
-
await adapter.initClient();
|
|
39
|
-
return adapter;
|
|
40
|
-
}
|
|
41
|
-
async initClient() {
|
|
42
|
-
const chromadb = await getChromaDB();
|
|
43
|
-
this.client = new chromadb.CloudClient({
|
|
44
|
-
apiKey: this.config.apiKey,
|
|
45
|
-
tenant: this.config.tenant,
|
|
46
|
-
database: this.config.database
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
async initialize() {
|
|
50
|
-
try {
|
|
51
|
-
if (!this.client) {
|
|
52
|
-
await this.initClient();
|
|
53
|
-
}
|
|
54
|
-
const collectionName = this.config.collectionName || "stackmemory_contexts";
|
|
55
|
-
this.collection = await this.client.getOrCreateCollection({
|
|
56
|
-
name: collectionName,
|
|
57
|
-
metadata: {
|
|
58
|
-
description: "StackMemory context storage",
|
|
59
|
-
version: "1.0.0",
|
|
60
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
this.logger.info(`ChromaDB collection '${collectionName}' initialized`);
|
|
64
|
-
} catch (error) {
|
|
65
|
-
this.logger.error("Failed to initialize ChromaDB collection", error);
|
|
66
|
-
throw error;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Store a frame in ChromaDB
|
|
71
|
-
*/
|
|
72
|
-
async storeFrame(frame) {
|
|
73
|
-
if (!this.collection) {
|
|
74
|
-
throw new Error("ChromaDB not initialized");
|
|
75
|
-
}
|
|
76
|
-
try {
|
|
77
|
-
const frameMetadata = {
|
|
78
|
-
user_id: this.userId,
|
|
79
|
-
frame_id: frame.frameId,
|
|
80
|
-
session_id: frame.sessionId || "unknown",
|
|
81
|
-
project_name: frame.projectName || "default",
|
|
82
|
-
timestamp: frame.timestamp,
|
|
83
|
-
type: "frame",
|
|
84
|
-
score: frame.score,
|
|
85
|
-
tags: frame.tags || []
|
|
86
|
-
};
|
|
87
|
-
if (this.teamId) {
|
|
88
|
-
frameMetadata.team_id = this.teamId;
|
|
89
|
-
}
|
|
90
|
-
const document = {
|
|
91
|
-
id: `frame_${frame.frameId}_${this.userId}`,
|
|
92
|
-
document: this.frameToDocument(frame),
|
|
93
|
-
metadata: frameMetadata
|
|
94
|
-
};
|
|
95
|
-
await this.collection.add({
|
|
96
|
-
ids: [document.id],
|
|
97
|
-
documents: [document.document],
|
|
98
|
-
metadatas: [document.metadata]
|
|
99
|
-
});
|
|
100
|
-
this.logger.debug(
|
|
101
|
-
`Stored frame ${frame.frameId} for user ${this.userId}`
|
|
102
|
-
);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
this.logger.error(`Failed to store frame ${frame.frameId}`, error);
|
|
105
|
-
throw error;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Store a decision or observation
|
|
110
|
-
*/
|
|
111
|
-
async storeContext(type, content, metadata) {
|
|
112
|
-
if (!this.collection) {
|
|
113
|
-
throw new Error("ChromaDB not initialized");
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
const contextId = `${type}_${uuidv4()}_${this.userId}`;
|
|
117
|
-
const documentMetadata = {
|
|
118
|
-
user_id: this.userId,
|
|
119
|
-
frame_id: metadata?.frame_id || "none",
|
|
120
|
-
session_id: metadata?.session_id || "unknown",
|
|
121
|
-
project_name: metadata?.project_name || "default",
|
|
122
|
-
timestamp: Date.now(),
|
|
123
|
-
type,
|
|
124
|
-
...metadata
|
|
125
|
-
};
|
|
126
|
-
if (this.teamId) {
|
|
127
|
-
documentMetadata.team_id = this.teamId;
|
|
128
|
-
}
|
|
129
|
-
const document = {
|
|
130
|
-
id: contextId,
|
|
131
|
-
document: content,
|
|
132
|
-
metadata: documentMetadata
|
|
133
|
-
};
|
|
134
|
-
await this.collection.add({
|
|
135
|
-
ids: [document.id],
|
|
136
|
-
documents: [document.document],
|
|
137
|
-
metadatas: [document.metadata]
|
|
138
|
-
});
|
|
139
|
-
this.logger.debug(`Stored ${type} for user ${this.userId}`);
|
|
140
|
-
} catch (error) {
|
|
141
|
-
this.logger.error(`Failed to store ${type}`, error);
|
|
142
|
-
throw error;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Query contexts by semantic similarity
|
|
147
|
-
*/
|
|
148
|
-
async queryContexts(query, limit = 10, filters) {
|
|
149
|
-
if (!this.collection) {
|
|
150
|
-
throw new Error("ChromaDB not initialized");
|
|
151
|
-
}
|
|
152
|
-
try {
|
|
153
|
-
const whereClause = {
|
|
154
|
-
user_id: this.userId
|
|
155
|
-
};
|
|
156
|
-
if (this.teamId) {
|
|
157
|
-
whereClause["$or"] = [
|
|
158
|
-
{ team_id: this.teamId },
|
|
159
|
-
{ user_id: this.userId }
|
|
160
|
-
];
|
|
161
|
-
}
|
|
162
|
-
if (filters?.type && filters.type.length > 0) {
|
|
163
|
-
whereClause.type = { $in: filters.type };
|
|
164
|
-
}
|
|
165
|
-
if (filters?.projectName) {
|
|
166
|
-
whereClause.project_name = filters.projectName;
|
|
167
|
-
}
|
|
168
|
-
if (filters?.sessionId) {
|
|
169
|
-
whereClause.session_id = filters.sessionId;
|
|
170
|
-
}
|
|
171
|
-
if (filters?.startTime || filters?.endTime) {
|
|
172
|
-
whereClause.timestamp = {};
|
|
173
|
-
if (filters.startTime) {
|
|
174
|
-
whereClause.timestamp.$gte = filters.startTime;
|
|
175
|
-
}
|
|
176
|
-
if (filters.endTime) {
|
|
177
|
-
whereClause.timestamp.$lte = filters.endTime;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
const results = await this.collection.query({
|
|
181
|
-
queryTexts: [query],
|
|
182
|
-
nResults: limit,
|
|
183
|
-
where: whereClause,
|
|
184
|
-
include: ["documents", "metadatas", "distances"]
|
|
185
|
-
});
|
|
186
|
-
const contexts = [];
|
|
187
|
-
if (results.documents && results.documents[0]) {
|
|
188
|
-
for (let i = 0; i < results.documents[0].length; i++) {
|
|
189
|
-
contexts.push({
|
|
190
|
-
content: results.documents[0][i] || "",
|
|
191
|
-
metadata: results.metadatas?.[0]?.[i] || {},
|
|
192
|
-
distance: results.distances?.[0]?.[i] || 0
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
this.logger.debug(`Found ${contexts.length} contexts for query`);
|
|
197
|
-
return contexts;
|
|
198
|
-
} catch (error) {
|
|
199
|
-
this.logger.error("Failed to query contexts", error);
|
|
200
|
-
throw error;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Get user's recent contexts
|
|
205
|
-
*/
|
|
206
|
-
async getRecentContexts(limit = 20, type) {
|
|
207
|
-
if (!this.collection) {
|
|
208
|
-
throw new Error("ChromaDB not initialized");
|
|
209
|
-
}
|
|
210
|
-
try {
|
|
211
|
-
const whereClause = {
|
|
212
|
-
user_id: this.userId
|
|
213
|
-
};
|
|
214
|
-
if (type) {
|
|
215
|
-
whereClause.type = type;
|
|
216
|
-
}
|
|
217
|
-
const results = await this.collection.get({
|
|
218
|
-
where: whereClause,
|
|
219
|
-
include: ["documents", "metadatas"]
|
|
220
|
-
});
|
|
221
|
-
const contexts = [];
|
|
222
|
-
if (results.documents) {
|
|
223
|
-
const indexed = results.documents.map((doc, i) => ({
|
|
224
|
-
content: doc || "",
|
|
225
|
-
metadata: results.metadatas?.[i] || {}
|
|
226
|
-
}));
|
|
227
|
-
indexed.sort(
|
|
228
|
-
(a, b) => (b.metadata.timestamp || 0) - (a.metadata.timestamp || 0)
|
|
229
|
-
);
|
|
230
|
-
contexts.push(...indexed.slice(0, limit));
|
|
231
|
-
}
|
|
232
|
-
return contexts;
|
|
233
|
-
} catch (error) {
|
|
234
|
-
this.logger.error("Failed to get recent contexts", error);
|
|
235
|
-
throw error;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Delete old contexts (retention policy)
|
|
240
|
-
*/
|
|
241
|
-
async deleteOldContexts(olderThanDays = 30) {
|
|
242
|
-
if (!this.collection) {
|
|
243
|
-
throw new Error("ChromaDB not initialized");
|
|
244
|
-
}
|
|
245
|
-
try {
|
|
246
|
-
const cutoffTime = Date.now() - olderThanDays * 24 * 60 * 60 * 1e3;
|
|
247
|
-
const results = await this.collection.get({
|
|
248
|
-
where: {
|
|
249
|
-
user_id: this.userId,
|
|
250
|
-
timestamp: { $lt: cutoffTime }
|
|
251
|
-
},
|
|
252
|
-
include: ["ids"]
|
|
253
|
-
});
|
|
254
|
-
if (!results.ids || results.ids.length === 0) {
|
|
255
|
-
return 0;
|
|
256
|
-
}
|
|
257
|
-
await this.collection.delete({
|
|
258
|
-
ids: results.ids
|
|
259
|
-
});
|
|
260
|
-
this.logger.info(`Deleted ${results.ids.length} old contexts`);
|
|
261
|
-
return results.ids.length;
|
|
262
|
-
} catch (error) {
|
|
263
|
-
this.logger.error("Failed to delete old contexts", error);
|
|
264
|
-
throw error;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Get team contexts (if user is part of a team)
|
|
269
|
-
*/
|
|
270
|
-
async getTeamContexts(limit = 20) {
|
|
271
|
-
if (!this.collection || !this.teamId) {
|
|
272
|
-
return [];
|
|
273
|
-
}
|
|
274
|
-
try {
|
|
275
|
-
const results = await this.collection.get({
|
|
276
|
-
where: {
|
|
277
|
-
team_id: this.teamId
|
|
278
|
-
},
|
|
279
|
-
include: ["documents", "metadatas"],
|
|
280
|
-
limit
|
|
281
|
-
});
|
|
282
|
-
const contexts = [];
|
|
283
|
-
if (results.documents) {
|
|
284
|
-
for (let i = 0; i < results.documents.length; i++) {
|
|
285
|
-
contexts.push({
|
|
286
|
-
content: results.documents[i] || "",
|
|
287
|
-
metadata: results.metadatas?.[i] || {}
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
return contexts;
|
|
292
|
-
} catch (error) {
|
|
293
|
-
this.logger.error("Failed to get team contexts", error);
|
|
294
|
-
return [];
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Convert frame to searchable document
|
|
299
|
-
*/
|
|
300
|
-
frameToDocument(frame) {
|
|
301
|
-
const parts = [
|
|
302
|
-
`Frame: ${frame.title}`,
|
|
303
|
-
`Type: ${frame.type}`,
|
|
304
|
-
`Status: ${frame.status}`
|
|
305
|
-
];
|
|
306
|
-
if (frame.description) {
|
|
307
|
-
parts.push(`Description: ${frame.description}`);
|
|
308
|
-
}
|
|
309
|
-
if (frame.inputs && frame.inputs.length > 0) {
|
|
310
|
-
parts.push(`Inputs: ${frame.inputs.join(", ")}`);
|
|
311
|
-
}
|
|
312
|
-
if (frame.outputs && frame.outputs.length > 0) {
|
|
313
|
-
parts.push(`Outputs: ${frame.outputs.join(", ")}`);
|
|
314
|
-
}
|
|
315
|
-
if (frame.tags && frame.tags.length > 0) {
|
|
316
|
-
parts.push(`Tags: ${frame.tags.join(", ")}`);
|
|
317
|
-
}
|
|
318
|
-
if (frame.digest_json) {
|
|
319
|
-
try {
|
|
320
|
-
const digest = JSON.parse(frame.digest_json);
|
|
321
|
-
if (digest.summary) {
|
|
322
|
-
parts.push(`Summary: ${digest.summary}`);
|
|
323
|
-
}
|
|
324
|
-
if (digest.keyDecisions) {
|
|
325
|
-
parts.push(`Decisions: ${digest.keyDecisions.join(". ")}`);
|
|
326
|
-
}
|
|
327
|
-
} catch {
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
return parts.join("\n");
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Update team ID for a user
|
|
334
|
-
*/
|
|
335
|
-
async updateTeamId(newTeamId) {
|
|
336
|
-
this.teamId = newTeamId;
|
|
337
|
-
this.logger.info(`Updated team ID to ${newTeamId} for user ${this.userId}`);
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Get storage statistics
|
|
341
|
-
*/
|
|
342
|
-
async getStats() {
|
|
343
|
-
if (!this.collection) {
|
|
344
|
-
throw new Error("ChromaDB not initialized");
|
|
345
|
-
}
|
|
346
|
-
try {
|
|
347
|
-
const userResults = await this.collection.get({
|
|
348
|
-
where: { user_id: this.userId },
|
|
349
|
-
include: ["metadatas"]
|
|
350
|
-
});
|
|
351
|
-
const stats = {
|
|
352
|
-
totalDocuments: 0,
|
|
353
|
-
userDocuments: userResults.ids?.length || 0,
|
|
354
|
-
documentsByType: {}
|
|
355
|
-
};
|
|
356
|
-
if (userResults.metadatas) {
|
|
357
|
-
for (const metadata of userResults.metadatas) {
|
|
358
|
-
const type = metadata?.type || "unknown";
|
|
359
|
-
stats.documentsByType[type] = (stats.documentsByType[type] || 0) + 1;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
if (this.teamId) {
|
|
363
|
-
const teamResults = await this.collection.get({
|
|
364
|
-
where: { team_id: this.teamId },
|
|
365
|
-
include: ["ids"]
|
|
366
|
-
});
|
|
367
|
-
stats.teamDocuments = teamResults.ids?.length || 0;
|
|
368
|
-
}
|
|
369
|
-
stats.totalDocuments = stats.userDocuments + (stats.teamDocuments || 0);
|
|
370
|
-
return stats;
|
|
371
|
-
} catch (error) {
|
|
372
|
-
this.logger.error("Failed to get stats", error);
|
|
373
|
-
throw error;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
export {
|
|
378
|
-
ChromaDBAdapter
|
|
379
|
-
};
|
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
-
import { dirname as __pathDirname } from 'path';
|
|
3
|
-
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
-
const __dirname = __pathDirname(__filename);
|
|
5
|
-
import { logger } from "../core/monitoring/logger.js";
|
|
6
|
-
import { GraphitiClient } from "../integrations/graphiti/client.js";
|
|
7
|
-
import { DEFAULT_GRAPHITI_CONFIG } from "../integrations/graphiti/config.js";
|
|
8
|
-
class GraphitiHooks {
|
|
9
|
-
client;
|
|
10
|
-
config;
|
|
11
|
-
constructor(config = {}) {
|
|
12
|
-
this.config = { ...DEFAULT_GRAPHITI_CONFIG, ...config };
|
|
13
|
-
this.client = new GraphitiClient(this.config);
|
|
14
|
-
}
|
|
15
|
-
register(emitter) {
|
|
16
|
-
if (!this.config.enabled) {
|
|
17
|
-
logger.debug("Graphiti hooks disabled");
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
emitter.registerHandler("session_start", this.onSessionStart.bind(this));
|
|
21
|
-
emitter.registerHandler("file_change", this.onFileChange.bind(this));
|
|
22
|
-
emitter.registerHandler("session_end", this.onSessionEnd.bind(this));
|
|
23
|
-
emitter.registerHandler("input_idle", this.onInputIdle.bind(this));
|
|
24
|
-
emitter.registerHandler("context_switch", this.onContextSwitch.bind(this));
|
|
25
|
-
emitter.registerHandler("prompt_submit", this.onPromptSubmit.bind(this));
|
|
26
|
-
emitter.registerHandler("tool_use", this.onToolUse.bind(this));
|
|
27
|
-
emitter.registerHandler(
|
|
28
|
-
"suggestion_ready",
|
|
29
|
-
this.onSuggestionReady.bind(this)
|
|
30
|
-
);
|
|
31
|
-
emitter.registerHandler("agent_start", this.onAgentStart.bind(this));
|
|
32
|
-
emitter.registerHandler("agent_complete", this.onAgentComplete.bind(this));
|
|
33
|
-
emitter.registerHandler("agent_error", this.onAgentError.bind(this));
|
|
34
|
-
logger.info("Graphiti hooks registered", {
|
|
35
|
-
endpoint: this.config.endpoint,
|
|
36
|
-
backend: this.config.backend,
|
|
37
|
-
maxHops: this.config.maxHops
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
async onSessionStart(event) {
|
|
41
|
-
try {
|
|
42
|
-
const status = await this.client.getStatus();
|
|
43
|
-
if (!status.connected) {
|
|
44
|
-
logger.warn("Graphiti not available - operating in degraded mode");
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
const episode = {
|
|
48
|
-
type: "session_start",
|
|
49
|
-
content: event.data || {},
|
|
50
|
-
timestamp: Date.now(),
|
|
51
|
-
source: "stackmemory",
|
|
52
|
-
metadata: { severity: "info" }
|
|
53
|
-
};
|
|
54
|
-
await this.client.upsertEpisode(episode);
|
|
55
|
-
} catch (error) {
|
|
56
|
-
logger.debug("Graphiti session_start failed", {
|
|
57
|
-
error: error instanceof Error ? error.message : String(error)
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
async onFileChange(event) {
|
|
62
|
-
const fileEvent = event;
|
|
63
|
-
try {
|
|
64
|
-
const episode = {
|
|
65
|
-
type: "file_change",
|
|
66
|
-
content: {
|
|
67
|
-
path: fileEvent.data.path,
|
|
68
|
-
changeType: fileEvent.data.changeType,
|
|
69
|
-
size: typeof fileEvent.data.content === "string" ? fileEvent.data.content.length : void 0
|
|
70
|
-
},
|
|
71
|
-
timestamp: Date.now(),
|
|
72
|
-
source: "stackmemory"
|
|
73
|
-
};
|
|
74
|
-
await this.client.upsertEpisode(episode);
|
|
75
|
-
} catch (error) {
|
|
76
|
-
logger.debug("Graphiti file_change episode failed", {
|
|
77
|
-
error: error instanceof Error ? error.message : String(error)
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
async onSessionEnd(event) {
|
|
82
|
-
try {
|
|
83
|
-
const episode = {
|
|
84
|
-
type: "session_end",
|
|
85
|
-
content: event.data || {},
|
|
86
|
-
timestamp: Date.now(),
|
|
87
|
-
source: "stackmemory"
|
|
88
|
-
};
|
|
89
|
-
await this.client.upsertEpisode(episode);
|
|
90
|
-
} catch (error) {
|
|
91
|
-
logger.debug("Graphiti session_end failed", {
|
|
92
|
-
error: error instanceof Error ? error.message : String(error)
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
async onInputIdle(event) {
|
|
97
|
-
const idle = event;
|
|
98
|
-
try {
|
|
99
|
-
const episode = {
|
|
100
|
-
type: "input_idle",
|
|
101
|
-
content: {
|
|
102
|
-
idleDuration: idle.data.idleDuration,
|
|
103
|
-
lastInput: idle.data.lastInput
|
|
104
|
-
},
|
|
105
|
-
timestamp: Date.now(),
|
|
106
|
-
source: "stackmemory"
|
|
107
|
-
};
|
|
108
|
-
await this.client.upsertEpisode(episode);
|
|
109
|
-
} catch (error) {
|
|
110
|
-
logger.debug("Graphiti input_idle episode failed", {
|
|
111
|
-
error: error instanceof Error ? error.message : String(error)
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
async onContextSwitch(event) {
|
|
116
|
-
const ctx = event;
|
|
117
|
-
try {
|
|
118
|
-
const episode = {
|
|
119
|
-
type: "context_switch",
|
|
120
|
-
content: {
|
|
121
|
-
fromBranch: ctx.data.fromBranch,
|
|
122
|
-
toBranch: ctx.data.toBranch,
|
|
123
|
-
fromProject: ctx.data.fromProject,
|
|
124
|
-
toProject: ctx.data.toProject
|
|
125
|
-
},
|
|
126
|
-
timestamp: Date.now(),
|
|
127
|
-
source: "stackmemory"
|
|
128
|
-
};
|
|
129
|
-
await this.client.upsertEpisode(episode);
|
|
130
|
-
} catch (error) {
|
|
131
|
-
logger.debug("Graphiti context_switch episode failed", {
|
|
132
|
-
error: error instanceof Error ? error.message : String(error)
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
async onPromptSubmit(event) {
|
|
137
|
-
try {
|
|
138
|
-
const episode = {
|
|
139
|
-
type: "prompt_submit",
|
|
140
|
-
content: event.data || {},
|
|
141
|
-
timestamp: Date.now(),
|
|
142
|
-
source: "stackmemory"
|
|
143
|
-
};
|
|
144
|
-
await this.client.upsertEpisode(episode);
|
|
145
|
-
} catch (error) {
|
|
146
|
-
logger.debug("Graphiti prompt_submit episode failed", {
|
|
147
|
-
error: error instanceof Error ? error.message : String(error)
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
async onToolUse(event) {
|
|
152
|
-
try {
|
|
153
|
-
const episode = {
|
|
154
|
-
type: "tool_use",
|
|
155
|
-
content: event.data || {},
|
|
156
|
-
timestamp: Date.now(),
|
|
157
|
-
source: "stackmemory"
|
|
158
|
-
};
|
|
159
|
-
await this.client.upsertEpisode(episode);
|
|
160
|
-
} catch (error) {
|
|
161
|
-
logger.debug("Graphiti tool_use episode failed", {
|
|
162
|
-
error: error instanceof Error ? error.message : String(error)
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
async onSuggestionReady(event) {
|
|
167
|
-
const suggestion = event;
|
|
168
|
-
try {
|
|
169
|
-
const episode = {
|
|
170
|
-
type: "suggestion_ready",
|
|
171
|
-
content: {
|
|
172
|
-
source: suggestion.data.source,
|
|
173
|
-
confidence: suggestion.data.confidence,
|
|
174
|
-
preview: suggestion.data.preview
|
|
175
|
-
},
|
|
176
|
-
timestamp: Date.now(),
|
|
177
|
-
source: "stackmemory"
|
|
178
|
-
};
|
|
179
|
-
await this.client.upsertEpisode(episode);
|
|
180
|
-
} catch (error) {
|
|
181
|
-
logger.debug("Graphiti suggestion_ready episode failed", {
|
|
182
|
-
error: error instanceof Error ? error.message : String(error)
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
async onAgentStart(event) {
|
|
187
|
-
const e = event;
|
|
188
|
-
try {
|
|
189
|
-
const episode = {
|
|
190
|
-
type: "agent_start",
|
|
191
|
-
content: { agentType: e.data.agentType, task: e.data.task },
|
|
192
|
-
timestamp: Date.now(),
|
|
193
|
-
source: "stackmemory"
|
|
194
|
-
};
|
|
195
|
-
await this.client.upsertEpisode(episode);
|
|
196
|
-
} catch (error) {
|
|
197
|
-
logger.debug("Graphiti agent_start episode failed", {
|
|
198
|
-
error: error instanceof Error ? error.message : String(error)
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
async onAgentComplete(event) {
|
|
203
|
-
const e = event;
|
|
204
|
-
try {
|
|
205
|
-
const episode = {
|
|
206
|
-
type: "agent_complete",
|
|
207
|
-
content: { ...e.data },
|
|
208
|
-
timestamp: Date.now(),
|
|
209
|
-
source: "stackmemory"
|
|
210
|
-
};
|
|
211
|
-
await this.client.upsertEpisode(episode);
|
|
212
|
-
} catch (error) {
|
|
213
|
-
logger.debug("Graphiti agent_complete episode failed", {
|
|
214
|
-
error: error instanceof Error ? error.message : String(error)
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
async onAgentError(event) {
|
|
219
|
-
const e = event;
|
|
220
|
-
try {
|
|
221
|
-
const episode = {
|
|
222
|
-
type: "agent_error",
|
|
223
|
-
content: { agentType: e.data.agentType, error: e.data.error },
|
|
224
|
-
timestamp: Date.now(),
|
|
225
|
-
source: "stackmemory"
|
|
226
|
-
};
|
|
227
|
-
await this.client.upsertEpisode(episode);
|
|
228
|
-
} catch (error) {
|
|
229
|
-
logger.debug("Graphiti agent_error episode failed", {
|
|
230
|
-
error: error instanceof Error ? error.message : String(error)
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
// Expose a simple temporal query helper for future MCP tooling
|
|
235
|
-
async buildTemporalContext(query = {}) {
|
|
236
|
-
const now = Date.now();
|
|
237
|
-
const q = {
|
|
238
|
-
query: query.query || void 0,
|
|
239
|
-
entityTypes: query.entityTypes || void 0,
|
|
240
|
-
relationTypes: query.relationTypes || void 0,
|
|
241
|
-
validFrom: query.validFrom ?? now - 1e3 * 60 * 60 * 24 * 30,
|
|
242
|
-
// 30d default
|
|
243
|
-
validTo: query.validTo ?? now,
|
|
244
|
-
maxHops: query.maxHops ?? this.config.maxHops,
|
|
245
|
-
k: query.k ?? 20,
|
|
246
|
-
rerank: query.rerank ?? true
|
|
247
|
-
};
|
|
248
|
-
return this.client.queryTemporal(q);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
export {
|
|
252
|
-
GraphitiHooks
|
|
253
|
-
};
|