@stackmemoryai/stackmemory 1.12.0 → 1.14.0
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/LICENSE +131 -64
- package/README.md +3 -1
- package/bin/claude-sm +16 -1
- package/bin/claude-smd +16 -1
- package/bin/codex-smd +16 -1
- package/bin/gemini-sm +16 -1
- package/bin/hermes-sm +21 -0
- package/bin/hermes-smd +21 -0
- package/bin/opencode-sm +16 -1
- package/dist/src/cli/codex-sm.js +51 -11
- package/dist/src/cli/commands/brain.js +206 -0
- package/dist/src/cli/commands/company-os.js +184 -0
- package/dist/src/cli/commands/context.js +5 -0
- package/dist/src/cli/commands/operator.js +127 -0
- package/dist/src/cli/commands/orchestrate.js +2 -0
- package/dist/src/cli/commands/orchestrator.js +3 -2
- package/dist/src/cli/commands/patterns.js +254 -0
- package/dist/src/cli/commands/portal.js +161 -0
- package/dist/src/cli/commands/scaffold.js +92 -0
- package/dist/src/cli/commands/setup.js +1 -4
- package/dist/src/cli/commands/sync.js +253 -0
- package/dist/src/cli/commands/tasks.js +130 -1
- package/dist/src/cli/commands/vision.js +221 -0
- package/dist/src/cli/hermes-sm.js +224 -0
- package/dist/src/cli/index.js +15 -10
- package/dist/src/cli/utils/real-cli-bin.js +72 -0
- package/dist/src/core/brain/brain-store.js +187 -0
- package/dist/src/core/brain/brain-sync.js +193 -0
- package/dist/src/core/brain/index.js +78 -0
- package/dist/src/core/brain/types.js +10 -0
- package/dist/src/core/cache/token-estimator.js +24 -1
- package/dist/src/core/config/feature-flags.js +2 -6
- package/dist/src/core/context/frame-database.js +44 -0
- package/dist/src/core/context/recursive-context-manager.js +1 -1
- package/dist/src/core/context/rehydration.js +2 -1
- package/dist/src/core/database/sqlite-adapter.js +14 -1
- package/dist/src/core/models/model-router.js +33 -1
- package/dist/src/core/models/provider-pricing.js +58 -4
- package/dist/src/core/patterns/index.js +22 -0
- package/dist/src/core/patterns/pattern-applier.js +39 -0
- package/dist/src/core/patterns/pattern-observer.js +157 -0
- package/dist/src/core/patterns/pattern-store.js +259 -0
- package/dist/src/core/patterns/types.js +19 -0
- package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
- package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
- package/dist/src/core/skill-packs/types.js +14 -1
- package/dist/src/core/storage/cloud-sync-manager.js +116 -0
- package/dist/src/core/storage/cloud-sync.js +574 -0
- package/dist/src/core/storage/two-tier-storage.js +5 -1
- package/dist/src/core/tasks/master-tasks-template.js +43 -0
- package/dist/src/core/tasks/md-task-parser.js +138 -0
- package/dist/src/core/vision/index.js +27 -0
- package/dist/src/core/vision/signals.js +79 -0
- package/dist/src/core/vision/types.js +22 -0
- package/dist/src/core/vision/vision-file.js +146 -0
- package/dist/src/core/vision/vision-loop.js +220 -0
- package/dist/src/core/wiki/wiki-compiler.js +103 -1
- package/dist/src/daemon/daemon-config.js +45 -0
- package/dist/src/daemon/services/desire-path-service.js +566 -0
- package/dist/src/daemon/services/research-stream-service.js +320 -0
- package/dist/src/daemon/services/telemetry-service.js +192 -0
- package/dist/src/daemon/unified-daemon.js +28 -1
- package/dist/src/features/browser/cli-browser-agent.js +417 -0
- package/dist/src/features/browser/stagehand-workflows.js +578 -0
- package/dist/src/features/operator/adapter-factory.js +62 -0
- package/dist/src/features/operator/browser-adapter.js +109 -0
- package/dist/src/features/operator/desktop-adapter.js +125 -0
- package/dist/src/features/operator/index.js +39 -0
- package/dist/src/features/operator/llm-decision.js +137 -0
- package/dist/src/features/operator/operator-logger.js +92 -0
- package/dist/src/features/operator/overnight-runner.js +327 -0
- package/dist/src/features/operator/screen-adapter.js +91 -0
- package/dist/src/features/operator/session-manager.js +127 -0
- package/dist/src/features/operator/state-machine.js +227 -0
- package/dist/src/features/operator/task-queue.js +81 -0
- package/dist/src/features/portal/index.js +26 -0
- package/dist/src/features/portal/server.js +240 -0
- package/dist/src/features/portal/types.js +14 -0
- package/dist/src/features/portal/ui.js +195 -0
- package/dist/src/features/tasks/task-aware-context.js +2 -1
- package/dist/src/features/tui/simple-monitor.js +0 -23
- package/dist/src/features/tui/swarm-monitor.js +8 -66
- package/dist/src/{integrations/diffmem/index.js → features/web/client/hooks/use-socket.js} +6 -5
- package/dist/src/features/web/client/lib/utils.js +12 -0
- package/dist/src/features/web/client/stores/session-store.js +12 -0
- package/dist/src/features/web/server/gcp-billing.js +76 -0
- package/dist/src/features/web/server/index.js +10 -0
- package/dist/src/features/web/server/spend-calculator.js +228 -0
- package/dist/src/hooks/schemas.js +4 -1
- package/dist/src/integrations/anthropic/client.js +3 -2
- package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
- package/dist/src/integrations/claude-code/subagent-client.js +218 -11
- package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
- package/dist/src/integrations/linear/webhook-retry.js +196 -0
- package/dist/src/integrations/linear/webhook-server.js +18 -22
- package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
- package/dist/src/integrations/mcp/handlers/index.js +27 -52
- package/dist/src/integrations/mcp/server.js +122 -335
- package/dist/src/integrations/mcp/tool-alias-registry.js +0 -73
- package/dist/src/integrations/mcp/tool-definitions.js +111 -510
- package/dist/src/mcp/stackmemory-mcp-server.js +404 -379
- package/dist/src/orchestrators/multimodal/determinism.js +2 -1
- package/dist/src/orchestrators/multimodal/harness.js +2 -1
- package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
- package/dist/src/utils/process-cleanup.js +1 -7
- package/docs/README.md +42 -0
- package/docs/guides/README_INSTALL.md +208 -0
- package/package.json +18 -9
- package/scripts/claude-code-wrapper.sh +11 -0
- package/scripts/claude-sm-setup.sh +12 -1
- package/scripts/codex-wrapper.sh +11 -0
- package/scripts/git-hooks/branch-context-manager.sh +11 -0
- package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
- package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
- package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
- package/scripts/hooks/cleanup-shell.sh +12 -1
- package/scripts/hooks/task-complete.sh +12 -1
- package/scripts/install-code-execution-hooks.sh +12 -1
- package/scripts/install-sweep-hook.sh +12 -0
- package/scripts/install.sh +11 -0
- package/scripts/opencode-wrapper.sh +11 -0
- package/scripts/portal/cloud-init.yaml +69 -0
- package/scripts/portal/setup.sh +69 -0
- package/scripts/portal/stackmemory-portal.service +34 -0
- package/scripts/setup-claude-integration.sh +12 -1
- package/scripts/smoke-init-db.sh +23 -0
- package/scripts/stackmemory-daemon.sh +11 -0
- package/scripts/verify-dist.cjs +11 -4
- package/dist/src/cli/commands/ralph.js +0 -1053
- package/dist/src/hooks/diffmem-hooks.js +0 -376
- package/dist/src/integrations/diffmem/client.js +0 -208
- package/dist/src/integrations/diffmem/config.js +0 -14
- package/dist/src/integrations/greptile/client.js +0 -101
- package/dist/src/integrations/greptile/config.js +0 -14
- package/dist/src/integrations/greptile/index.js +0 -11
- package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +0 -188
- package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
- package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
- package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
- package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
- package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -354
- package/dist/src/integrations/ralph/index.js +0 -17
- package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -416
- package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
- package/dist/src/integrations/ralph/loopmax.js +0 -488
- package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
- package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
- package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
- package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
- package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1005
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
- package/scripts/ralph-loop-implementation.js +0 -404
- /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
- /package/dist/src/{integrations/greptile → features/operator}/types.js +0 -0
- /package/dist/src/{integrations/ralph/types.js → features/web/client/next-env.d.js} +0 -0
|
@@ -1,376 +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
|
-
const DEFAULT_CONFIG = {
|
|
7
|
-
enabled: !!process.env.DIFFMEM_ENDPOINT,
|
|
8
|
-
endpoint: process.env.DIFFMEM_ENDPOINT || "http://localhost:3100",
|
|
9
|
-
autoFetchCategories: ["preference", "expertise", "pattern"],
|
|
10
|
-
autoLearnEnabled: true,
|
|
11
|
-
learningConfidenceThreshold: 0.7,
|
|
12
|
-
maxMemoriesPerSession: 50
|
|
13
|
-
};
|
|
14
|
-
class DiffMemHooks {
|
|
15
|
-
config;
|
|
16
|
-
fetchedMemories = [];
|
|
17
|
-
learningBuffer = [];
|
|
18
|
-
isConnected = false;
|
|
19
|
-
frameManager;
|
|
20
|
-
sessionStartTime = 0;
|
|
21
|
-
constructor(config = {}) {
|
|
22
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Register session hooks with the event emitter
|
|
26
|
-
*/
|
|
27
|
-
register(emitter, frameManager) {
|
|
28
|
-
this.frameManager = frameManager;
|
|
29
|
-
if (!this.config.enabled) {
|
|
30
|
-
logger.debug("DiffMem hooks disabled - skipping registration");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
emitter.registerHandler("session_start", this.onSessionStart.bind(this));
|
|
34
|
-
emitter.registerHandler("session_end", this.onSessionEnd.bind(this));
|
|
35
|
-
logger.info("DiffMem hooks registered", {
|
|
36
|
-
endpoint: this.config.endpoint,
|
|
37
|
-
autoFetchCategories: this.config.autoFetchCategories,
|
|
38
|
-
autoLearnEnabled: this.config.autoLearnEnabled
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Handle session start - fetch user knowledge
|
|
43
|
-
*/
|
|
44
|
-
async onSessionStart(event) {
|
|
45
|
-
const sessionEvent = event;
|
|
46
|
-
this.sessionStartTime = Date.now();
|
|
47
|
-
try {
|
|
48
|
-
const status = await this.checkStatus();
|
|
49
|
-
this.isConnected = status.connected;
|
|
50
|
-
if (!this.isConnected) {
|
|
51
|
-
logger.debug("DiffMem not available - skipping memory fetch");
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const query = {
|
|
55
|
-
categories: this.config.autoFetchCategories,
|
|
56
|
-
limit: this.config.maxMemoriesPerSession,
|
|
57
|
-
minConfidence: this.config.learningConfidenceThreshold
|
|
58
|
-
};
|
|
59
|
-
this.fetchedMemories = await this.fetchMemories(query);
|
|
60
|
-
if (this.frameManager && this.fetchedMemories.length > 0) {
|
|
61
|
-
await this.injectAsAnchors(sessionEvent.data.sessionId);
|
|
62
|
-
}
|
|
63
|
-
logger.info("DiffMem session start completed", {
|
|
64
|
-
memoriesFetched: this.fetchedMemories.length,
|
|
65
|
-
sessionId: sessionEvent.data.sessionId
|
|
66
|
-
});
|
|
67
|
-
} catch (error) {
|
|
68
|
-
logger.warn("DiffMem session start failed", {
|
|
69
|
-
error: error instanceof Error ? error.message : String(error)
|
|
70
|
-
});
|
|
71
|
-
this.isConnected = false;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Handle session end - sync buffered learnings
|
|
76
|
-
*/
|
|
77
|
-
async onSessionEnd(event) {
|
|
78
|
-
const sessionEvent = event;
|
|
79
|
-
if (!this.config.autoLearnEnabled || this.learningBuffer.length === 0) {
|
|
80
|
-
logger.debug("No learnings to sync on session end");
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
if (!this.isConnected) {
|
|
84
|
-
const status = await this.checkStatus();
|
|
85
|
-
if (!status.connected) {
|
|
86
|
-
logger.warn("DiffMem not available - learnings not synced", {
|
|
87
|
-
bufferedCount: this.learningBuffer.length
|
|
88
|
-
});
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
this.isConnected = true;
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
await this.syncLearnings();
|
|
95
|
-
const sessionDuration = Date.now() - this.sessionStartTime;
|
|
96
|
-
logger.info("DiffMem session end completed", {
|
|
97
|
-
learningSynced: this.learningBuffer.length,
|
|
98
|
-
sessionId: sessionEvent.data.sessionId,
|
|
99
|
-
sessionDurationMs: sessionDuration
|
|
100
|
-
});
|
|
101
|
-
this.learningBuffer = [];
|
|
102
|
-
} catch (error) {
|
|
103
|
-
logger.warn("DiffMem session end sync failed", {
|
|
104
|
-
error: error instanceof Error ? error.message : String(error),
|
|
105
|
-
bufferedCount: this.learningBuffer.length
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Record a learning during session
|
|
111
|
-
*/
|
|
112
|
-
recordLearning(insight) {
|
|
113
|
-
if (!this.config.autoLearnEnabled) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
if (insight.confidence < this.config.learningConfidenceThreshold) {
|
|
117
|
-
logger.debug("Learning below confidence threshold", {
|
|
118
|
-
confidence: insight.confidence,
|
|
119
|
-
threshold: this.config.learningConfidenceThreshold
|
|
120
|
-
});
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
const learning = {
|
|
124
|
-
...insight,
|
|
125
|
-
timestamp: Date.now()
|
|
126
|
-
};
|
|
127
|
-
this.learningBuffer.push(learning);
|
|
128
|
-
logger.debug("Learning recorded", {
|
|
129
|
-
category: learning.category,
|
|
130
|
-
confidence: learning.confidence,
|
|
131
|
-
bufferSize: this.learningBuffer.length
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Get fetched memories
|
|
136
|
-
*/
|
|
137
|
-
getUserKnowledge() {
|
|
138
|
-
return [...this.fetchedMemories];
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Format memories for LLM context with token budget
|
|
142
|
-
*/
|
|
143
|
-
formatForContext(maxTokens = 2e3) {
|
|
144
|
-
if (this.fetchedMemories.length === 0) {
|
|
145
|
-
return "";
|
|
146
|
-
}
|
|
147
|
-
const sortedMemories = [...this.fetchedMemories].sort((a, b) => {
|
|
148
|
-
if (b.confidence !== a.confidence) {
|
|
149
|
-
return b.confidence - a.confidence;
|
|
150
|
-
}
|
|
151
|
-
return b.timestamp - a.timestamp;
|
|
152
|
-
});
|
|
153
|
-
const sections = /* @__PURE__ */ new Map();
|
|
154
|
-
for (const memory of sortedMemories) {
|
|
155
|
-
const category = memory.category;
|
|
156
|
-
if (!sections.has(category)) {
|
|
157
|
-
sections.set(category, []);
|
|
158
|
-
}
|
|
159
|
-
const categoryMemories = sections.get(category);
|
|
160
|
-
if (categoryMemories) {
|
|
161
|
-
categoryMemories.push(memory.content);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
const lines = ["## User Knowledge"];
|
|
165
|
-
let estimatedTokens = 5;
|
|
166
|
-
const categoryLabels = {
|
|
167
|
-
preference: "Preferences",
|
|
168
|
-
expertise: "Expertise",
|
|
169
|
-
project_knowledge: "Project Knowledge",
|
|
170
|
-
pattern: "Patterns",
|
|
171
|
-
correction: "Corrections"
|
|
172
|
-
};
|
|
173
|
-
for (const [category, contents] of sections) {
|
|
174
|
-
const label = categoryLabels[category] || category;
|
|
175
|
-
const categoryHeader = `
|
|
176
|
-
### ${label}`;
|
|
177
|
-
const headerTokens = Math.ceil(categoryHeader.length / 4);
|
|
178
|
-
if (estimatedTokens + headerTokens > maxTokens) {
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
lines.push(categoryHeader);
|
|
182
|
-
estimatedTokens += headerTokens;
|
|
183
|
-
for (const content of contents) {
|
|
184
|
-
const contentLine = `- ${content}`;
|
|
185
|
-
const contentTokens = Math.ceil(contentLine.length / 4);
|
|
186
|
-
if (estimatedTokens + contentTokens > maxTokens) {
|
|
187
|
-
lines.push("- (additional items truncated for token budget)");
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
lines.push(contentLine);
|
|
191
|
-
estimatedTokens += contentTokens;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return lines.join("\n");
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Get current connection status
|
|
198
|
-
*/
|
|
199
|
-
getStatus() {
|
|
200
|
-
return {
|
|
201
|
-
connected: this.isConnected,
|
|
202
|
-
memoriesLoaded: this.fetchedMemories.length,
|
|
203
|
-
learningsBuffered: this.learningBuffer.length
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Update configuration
|
|
208
|
-
*/
|
|
209
|
-
updateConfig(config) {
|
|
210
|
-
this.config = { ...this.config, ...config };
|
|
211
|
-
logger.debug("DiffMem config updated", { config: this.config });
|
|
212
|
-
}
|
|
213
|
-
// Private methods
|
|
214
|
-
/**
|
|
215
|
-
* Check DiffMem service status
|
|
216
|
-
*/
|
|
217
|
-
async checkStatus() {
|
|
218
|
-
try {
|
|
219
|
-
const controller = new AbortController();
|
|
220
|
-
const timeoutId = setTimeout(() => controller.abort(), 3e3);
|
|
221
|
-
const response = await fetch(`${this.config.endpoint}/status`, {
|
|
222
|
-
method: "GET",
|
|
223
|
-
signal: controller.signal
|
|
224
|
-
});
|
|
225
|
-
clearTimeout(timeoutId);
|
|
226
|
-
if (!response.ok) {
|
|
227
|
-
return { connected: false, memoryCount: 0, lastSync: null };
|
|
228
|
-
}
|
|
229
|
-
const status = await response.json();
|
|
230
|
-
return {
|
|
231
|
-
connected: true,
|
|
232
|
-
memoryCount: status.memoryCount || 0,
|
|
233
|
-
lastSync: status.lastSync || null,
|
|
234
|
-
version: status.version
|
|
235
|
-
};
|
|
236
|
-
} catch {
|
|
237
|
-
return { connected: false, memoryCount: 0, lastSync: null };
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Fetch memories from DiffMem
|
|
242
|
-
*/
|
|
243
|
-
async fetchMemories(query) {
|
|
244
|
-
try {
|
|
245
|
-
const controller = new AbortController();
|
|
246
|
-
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
247
|
-
const response = await fetch(`${this.config.endpoint}/memories/query`, {
|
|
248
|
-
method: "POST",
|
|
249
|
-
headers: { "Content-Type": "application/json" },
|
|
250
|
-
body: JSON.stringify(query),
|
|
251
|
-
signal: controller.signal
|
|
252
|
-
});
|
|
253
|
-
clearTimeout(timeoutId);
|
|
254
|
-
if (!response.ok) {
|
|
255
|
-
logger.warn("DiffMem query failed", { status: response.status });
|
|
256
|
-
return [];
|
|
257
|
-
}
|
|
258
|
-
const data = await response.json();
|
|
259
|
-
return data.memories || [];
|
|
260
|
-
} catch (error) {
|
|
261
|
-
logger.debug("DiffMem fetch failed", {
|
|
262
|
-
error: error instanceof Error ? error.message : String(error)
|
|
263
|
-
});
|
|
264
|
-
return [];
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Sync buffered learnings to DiffMem
|
|
269
|
-
*/
|
|
270
|
-
async syncLearnings() {
|
|
271
|
-
if (this.learningBuffer.length === 0) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
const controller = new AbortController();
|
|
275
|
-
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
276
|
-
try {
|
|
277
|
-
const response = await fetch(`${this.config.endpoint}/memories/learn`, {
|
|
278
|
-
method: "POST",
|
|
279
|
-
headers: { "Content-Type": "application/json" },
|
|
280
|
-
body: JSON.stringify({ insights: this.learningBuffer }),
|
|
281
|
-
signal: controller.signal
|
|
282
|
-
});
|
|
283
|
-
clearTimeout(timeoutId);
|
|
284
|
-
if (!response.ok) {
|
|
285
|
-
throw new Error(`Sync failed with status ${response.status}`);
|
|
286
|
-
}
|
|
287
|
-
logger.info("Learnings synced to DiffMem", {
|
|
288
|
-
count: this.learningBuffer.length
|
|
289
|
-
});
|
|
290
|
-
} catch (error) {
|
|
291
|
-
clearTimeout(timeoutId);
|
|
292
|
-
throw error;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Inject fetched memories as frame anchors
|
|
297
|
-
*/
|
|
298
|
-
async injectAsAnchors(sessionId) {
|
|
299
|
-
if (!this.frameManager || this.fetchedMemories.length === 0) {
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
try {
|
|
303
|
-
const byCategory = /* @__PURE__ */ new Map();
|
|
304
|
-
for (const memory of this.fetchedMemories) {
|
|
305
|
-
if (!byCategory.has(memory.category)) {
|
|
306
|
-
byCategory.set(memory.category, []);
|
|
307
|
-
}
|
|
308
|
-
const categoryList = byCategory.get(memory.category);
|
|
309
|
-
if (categoryList) {
|
|
310
|
-
categoryList.push(memory);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
for (const [category, memories] of byCategory) {
|
|
314
|
-
const highConfidence = memories.filter((m) => m.confidence >= 0.8);
|
|
315
|
-
for (const memory of highConfidence.slice(0, 5)) {
|
|
316
|
-
const anchorType = this.categoryToAnchorType(category);
|
|
317
|
-
const priority = Math.round(memory.confidence * 10);
|
|
318
|
-
this.frameManager.addAnchor(anchorType, memory.content, priority, {
|
|
319
|
-
source: "diffmem",
|
|
320
|
-
category: memory.category,
|
|
321
|
-
memoryId: memory.id,
|
|
322
|
-
confidence: memory.confidence,
|
|
323
|
-
sessionId
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
logger.debug("Memories injected as anchors", {
|
|
328
|
-
totalMemories: this.fetchedMemories.length,
|
|
329
|
-
anchorsCreated: Math.min(
|
|
330
|
-
this.fetchedMemories.filter((m) => m.confidence >= 0.8).length,
|
|
331
|
-
25
|
|
332
|
-
)
|
|
333
|
-
});
|
|
334
|
-
} catch (error) {
|
|
335
|
-
logger.warn("Failed to inject memories as anchors", {
|
|
336
|
-
error: error instanceof Error ? error.message : String(error)
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Map memory category to anchor type
|
|
342
|
-
*/
|
|
343
|
-
categoryToAnchorType(category) {
|
|
344
|
-
switch (category) {
|
|
345
|
-
case "preference":
|
|
346
|
-
return "CONSTRAINT";
|
|
347
|
-
case "expertise":
|
|
348
|
-
return "FACT";
|
|
349
|
-
case "project_knowledge":
|
|
350
|
-
return "FACT";
|
|
351
|
-
case "pattern":
|
|
352
|
-
return "INTERFACE_CONTRACT";
|
|
353
|
-
case "correction":
|
|
354
|
-
return "DECISION";
|
|
355
|
-
default:
|
|
356
|
-
return "FACT";
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
let instance = null;
|
|
361
|
-
function getDiffMemHooks(config) {
|
|
362
|
-
if (!instance) {
|
|
363
|
-
instance = new DiffMemHooks(config);
|
|
364
|
-
} else if (config) {
|
|
365
|
-
instance.updateConfig(config);
|
|
366
|
-
}
|
|
367
|
-
return instance;
|
|
368
|
-
}
|
|
369
|
-
function resetDiffMemHooks() {
|
|
370
|
-
instance = null;
|
|
371
|
-
}
|
|
372
|
-
export {
|
|
373
|
-
DiffMemHooks,
|
|
374
|
-
getDiffMemHooks,
|
|
375
|
-
resetDiffMemHooks
|
|
376
|
-
};
|
|
@@ -1,208 +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 { DEFAULT_DIFFMEM_CONFIG } from "./config.js";
|
|
6
|
-
class DiffMemClientError extends Error {
|
|
7
|
-
constructor(message, code, statusCode) {
|
|
8
|
-
super(message);
|
|
9
|
-
this.code = code;
|
|
10
|
-
this.statusCode = statusCode;
|
|
11
|
-
this.name = "DiffMemClientError";
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
class DiffMemClient {
|
|
15
|
-
endpoint;
|
|
16
|
-
userId;
|
|
17
|
-
timeout;
|
|
18
|
-
maxRetries;
|
|
19
|
-
constructor(config = {}) {
|
|
20
|
-
const mergedConfig = { ...DEFAULT_DIFFMEM_CONFIG, ...config };
|
|
21
|
-
this.endpoint = mergedConfig.endpoint.replace(/\/$/, "");
|
|
22
|
-
this.userId = mergedConfig.userId;
|
|
23
|
-
this.timeout = mergedConfig.timeout;
|
|
24
|
-
this.maxRetries = mergedConfig.maxRetries;
|
|
25
|
-
}
|
|
26
|
-
async request(path, options = {}) {
|
|
27
|
-
const controller = new AbortController();
|
|
28
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
29
|
-
let lastError;
|
|
30
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
31
|
-
try {
|
|
32
|
-
const response = await fetch(`${this.endpoint}${path}`, {
|
|
33
|
-
...options,
|
|
34
|
-
signal: controller.signal,
|
|
35
|
-
headers: {
|
|
36
|
-
"Content-Type": "application/json",
|
|
37
|
-
...options.headers
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
clearTimeout(timeoutId);
|
|
41
|
-
if (!response.ok) {
|
|
42
|
-
const _errorBody = await response.text().catch(() => "");
|
|
43
|
-
throw new DiffMemClientError(
|
|
44
|
-
`Request failed: ${response.statusText}`,
|
|
45
|
-
"HTTP_ERROR",
|
|
46
|
-
response.status
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
return await response.json();
|
|
50
|
-
} catch (error) {
|
|
51
|
-
lastError = error;
|
|
52
|
-
if (error instanceof DiffMemClientError) {
|
|
53
|
-
throw error;
|
|
54
|
-
}
|
|
55
|
-
if (error.name === "AbortError") {
|
|
56
|
-
throw new DiffMemClientError("Request timeout", "TIMEOUT");
|
|
57
|
-
}
|
|
58
|
-
if (attempt < this.maxRetries) {
|
|
59
|
-
await new Promise(
|
|
60
|
-
(resolve) => setTimeout(resolve, Math.pow(2, attempt) * 100)
|
|
61
|
-
);
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
clearTimeout(timeoutId);
|
|
67
|
-
throw new DiffMemClientError(
|
|
68
|
-
lastError?.message || "Request failed after retries",
|
|
69
|
-
"NETWORK_ERROR"
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Get user context/memories from DiffMem
|
|
74
|
-
* Maps to POST /memory/{user_id}/context
|
|
75
|
-
*/
|
|
76
|
-
async getMemories(query = {}) {
|
|
77
|
-
try {
|
|
78
|
-
const conversation = query.query ? [{ role: "user", content: query.query }] : [{ role: "user", content: "What do you know about me?" }];
|
|
79
|
-
const response = await this.request(`/memory/${this.userId}/context`, {
|
|
80
|
-
method: "POST",
|
|
81
|
-
body: JSON.stringify({
|
|
82
|
-
conversation,
|
|
83
|
-
depth: "wide"
|
|
84
|
-
})
|
|
85
|
-
});
|
|
86
|
-
if (response.entities) {
|
|
87
|
-
return response.entities.slice(0, query.limit || 10).map((entity) => ({
|
|
88
|
-
id: entity.id,
|
|
89
|
-
content: entity.content,
|
|
90
|
-
category: "project_knowledge",
|
|
91
|
-
confidence: entity.score || 0.7,
|
|
92
|
-
timestamp: Date.now()
|
|
93
|
-
}));
|
|
94
|
-
}
|
|
95
|
-
return [];
|
|
96
|
-
} catch {
|
|
97
|
-
return [];
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Store an insight/learning in DiffMem
|
|
102
|
-
* Maps to POST /memory/{user_id}/process-and-commit
|
|
103
|
-
*/
|
|
104
|
-
async storeInsight(insight) {
|
|
105
|
-
const response = await this.request(`/memory/${this.userId}/process-and-commit`, {
|
|
106
|
-
method: "POST",
|
|
107
|
-
body: JSON.stringify({
|
|
108
|
-
memory_input: `[${insight.category}] ${insight.content}`,
|
|
109
|
-
session_id: `sm-${Date.now()}`,
|
|
110
|
-
session_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
111
|
-
})
|
|
112
|
-
});
|
|
113
|
-
return { id: response.session_id || `insight-${Date.now()}` };
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Search memories in DiffMem
|
|
117
|
-
* Maps to POST /memory/{user_id}/search
|
|
118
|
-
*/
|
|
119
|
-
async search(query) {
|
|
120
|
-
try {
|
|
121
|
-
const response = await this.request(`/memory/${this.userId}/search`, {
|
|
122
|
-
method: "POST",
|
|
123
|
-
body: JSON.stringify({
|
|
124
|
-
query: query.query || "",
|
|
125
|
-
k: query.limit || 10
|
|
126
|
-
})
|
|
127
|
-
});
|
|
128
|
-
if (response.results) {
|
|
129
|
-
return response.results.map((result) => ({
|
|
130
|
-
id: result.snippet.id,
|
|
131
|
-
content: result.snippet.content,
|
|
132
|
-
category: "project_knowledge",
|
|
133
|
-
confidence: result.score,
|
|
134
|
-
timestamp: Date.now(),
|
|
135
|
-
metadata: { filePath: result.snippet.file_path }
|
|
136
|
-
}));
|
|
137
|
-
}
|
|
138
|
-
return [];
|
|
139
|
-
} catch {
|
|
140
|
-
return [];
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Get DiffMem server status
|
|
145
|
-
* Maps to GET /health
|
|
146
|
-
*/
|
|
147
|
-
async getStatus() {
|
|
148
|
-
try {
|
|
149
|
-
const health = await this.request("/health", { method: "GET" });
|
|
150
|
-
return {
|
|
151
|
-
connected: health.status === "healthy",
|
|
152
|
-
memoryCount: health.active_contexts || 0,
|
|
153
|
-
lastSync: Date.now(),
|
|
154
|
-
version: health.version
|
|
155
|
-
};
|
|
156
|
-
} catch {
|
|
157
|
-
return {
|
|
158
|
-
connected: false,
|
|
159
|
-
memoryCount: 0,
|
|
160
|
-
lastSync: null
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Batch sync multiple insights
|
|
166
|
-
* Processes each insight individually since DiffMem doesn't have batch API
|
|
167
|
-
*/
|
|
168
|
-
async batchSync(insights) {
|
|
169
|
-
if (insights.length === 0) {
|
|
170
|
-
return { synced: 0, failed: 0 };
|
|
171
|
-
}
|
|
172
|
-
let synced = 0;
|
|
173
|
-
let failed = 0;
|
|
174
|
-
for (const insight of insights) {
|
|
175
|
-
try {
|
|
176
|
-
await this.storeInsight(insight);
|
|
177
|
-
synced++;
|
|
178
|
-
} catch {
|
|
179
|
-
failed++;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return { synced, failed };
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Onboard a new user in DiffMem
|
|
186
|
-
*/
|
|
187
|
-
async onboardUser(userInfo) {
|
|
188
|
-
try {
|
|
189
|
-
const response = await this.request(
|
|
190
|
-
`/memory/${this.userId}/onboard`,
|
|
191
|
-
{
|
|
192
|
-
method: "POST",
|
|
193
|
-
body: JSON.stringify({
|
|
194
|
-
user_info: userInfo,
|
|
195
|
-
session_id: `onboard-${Date.now()}`
|
|
196
|
-
})
|
|
197
|
-
}
|
|
198
|
-
);
|
|
199
|
-
return { success: response.status === "success" };
|
|
200
|
-
} catch {
|
|
201
|
-
return { success: false };
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
export {
|
|
206
|
-
DiffMemClient,
|
|
207
|
-
DiffMemClientError
|
|
208
|
-
};
|
|
@@ -1,14 +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
|
-
const DEFAULT_DIFFMEM_CONFIG = {
|
|
6
|
-
endpoint: process.env.DIFFMEM_ENDPOINT || "http://localhost:8000",
|
|
7
|
-
userId: process.env.DIFFMEM_USER_ID || "default",
|
|
8
|
-
timeout: 5e3,
|
|
9
|
-
maxRetries: 3,
|
|
10
|
-
enabled: process.env.DIFFMEM_ENABLED === "true" || !!process.env.DIFFMEM_ENDPOINT
|
|
11
|
-
};
|
|
12
|
-
export {
|
|
13
|
-
DEFAULT_DIFFMEM_CONFIG
|
|
14
|
-
};
|
|
@@ -1,101 +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 { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
7
|
-
import { DEFAULT_GREPTILE_CONFIG } from "./config.js";
|
|
8
|
-
class GreptileClientError extends Error {
|
|
9
|
-
constructor(message, code) {
|
|
10
|
-
super(message);
|
|
11
|
-
this.code = code;
|
|
12
|
-
this.name = "GreptileClientError";
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
class GreptileClient {
|
|
16
|
-
config;
|
|
17
|
-
client = null;
|
|
18
|
-
transport = null;
|
|
19
|
-
connecting = null;
|
|
20
|
-
constructor(config = {}) {
|
|
21
|
-
this.config = { ...DEFAULT_GREPTILE_CONFIG, ...config };
|
|
22
|
-
if (!this.config.enabled || !this.config.apiKey) {
|
|
23
|
-
throw new GreptileClientError(
|
|
24
|
-
"Greptile integration disabled (GREPTILE_API_KEY not set)",
|
|
25
|
-
"DISABLED"
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
async ensureConnected() {
|
|
30
|
-
if (this.client) return this.client;
|
|
31
|
-
if (this.connecting) {
|
|
32
|
-
await this.connecting;
|
|
33
|
-
return this.client;
|
|
34
|
-
}
|
|
35
|
-
this.connecting = this.connect();
|
|
36
|
-
try {
|
|
37
|
-
await this.connecting;
|
|
38
|
-
return this.client;
|
|
39
|
-
} finally {
|
|
40
|
-
this.connecting = null;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
async connect() {
|
|
44
|
-
const transport = new StreamableHTTPClientTransport(
|
|
45
|
-
new URL(this.config.mcpEndpoint),
|
|
46
|
-
{
|
|
47
|
-
requestInit: {
|
|
48
|
-
headers: {
|
|
49
|
-
Authorization: `Bearer ${this.config.apiKey}`
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
reconnectionOptions: {
|
|
53
|
-
maxRetries: this.config.maxRetries,
|
|
54
|
-
initialReconnectionDelay: 1e3,
|
|
55
|
-
reconnectionDelayGrowFactor: 1.5,
|
|
56
|
-
maxReconnectionDelay: 1e4
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
);
|
|
60
|
-
const client = new Client(
|
|
61
|
-
{ name: "stackmemory-greptile", version: "1.0.0" },
|
|
62
|
-
{ capabilities: {} }
|
|
63
|
-
);
|
|
64
|
-
transport.onclose = () => {
|
|
65
|
-
this.client = null;
|
|
66
|
-
this.transport = null;
|
|
67
|
-
};
|
|
68
|
-
await client.connect(transport);
|
|
69
|
-
this.client = client;
|
|
70
|
-
this.transport = transport;
|
|
71
|
-
}
|
|
72
|
-
async callTool(name, args = {}) {
|
|
73
|
-
const client = await this.ensureConnected();
|
|
74
|
-
const result = await client.callTool({ name, arguments: args });
|
|
75
|
-
if (result.content && Array.isArray(result.content)) {
|
|
76
|
-
const textParts = result.content.filter(
|
|
77
|
-
(c) => c.type === "text" && typeof c.text === "string"
|
|
78
|
-
).map((c) => c.text);
|
|
79
|
-
if (textParts.length === 0) return result;
|
|
80
|
-
const combined = textParts.join("\n");
|
|
81
|
-
try {
|
|
82
|
-
return JSON.parse(combined);
|
|
83
|
-
} catch {
|
|
84
|
-
return combined;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return result;
|
|
88
|
-
}
|
|
89
|
-
async disconnect() {
|
|
90
|
-
if (this.transport) {
|
|
91
|
-
await this.transport.close();
|
|
92
|
-
}
|
|
93
|
-
this.client = null;
|
|
94
|
-
this.transport = null;
|
|
95
|
-
this.connecting = null;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
export {
|
|
99
|
-
GreptileClient,
|
|
100
|
-
GreptileClientError
|
|
101
|
-
};
|
|
@@ -1,14 +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
|
-
const DEFAULT_GREPTILE_CONFIG = {
|
|
6
|
-
enabled: !!process.env.GREPTILE_API_KEY,
|
|
7
|
-
mcpEndpoint: process.env.GREPTILE_MCP_ENDPOINT || "https://api.greptile.com/mcp",
|
|
8
|
-
apiKey: process.env.GREPTILE_API_KEY || "",
|
|
9
|
-
timeoutMs: 15e3,
|
|
10
|
-
maxRetries: 2
|
|
11
|
-
};
|
|
12
|
-
export {
|
|
13
|
-
DEFAULT_GREPTILE_CONFIG
|
|
14
|
-
};
|