@stackmemoryai/stackmemory 0.5.51 → 0.5.53
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/dist/cli/commands/handoff.js +215 -59
- package/dist/cli/commands/handoff.js.map +2 -2
- package/dist/cli/index.js +8 -2
- package/dist/cli/index.js.map +2 -2
- package/dist/integrations/claude-code/lifecycle-hooks.js +3 -3
- package/dist/integrations/claude-code/lifecycle-hooks.js.map +1 -1
- package/package.json +1 -1
- package/scripts/auto-handoff.sh +1 -1
- package/scripts/claude-sm-autostart.js +174 -132
- package/scripts/setup-claude-integration.js +14 -10
- package/scripts/stackmemory-auto-handoff.sh +3 -3
- package/scripts/test-session-handoff.sh +2 -2
- package/dist/core/context/compaction-handler.js +0 -330
- package/dist/core/context/compaction-handler.js.map +0 -7
- package/dist/core/context/context-bridge.js +0 -238
- package/dist/core/context/context-bridge.js.map +0 -7
- package/scripts/testing/scripts/testing/ab-test-runner.js +0 -363
- package/scripts/testing/scripts/testing/collect-metrics.js +0 -292
- package/scripts/testing/src/core/context/context-bridge.js +0 -253
- package/scripts/testing/src/core/context/frame-manager.js +0 -746
- package/scripts/testing/src/core/context/shared-context-layer.js +0 -437
- package/scripts/testing/src/core/database/database-adapter.js +0 -54
- package/scripts/testing/src/core/errors/index.js +0 -291
- package/scripts/testing/src/core/errors/recovery.js +0 -268
- package/scripts/testing/src/core/monitoring/logger.js +0 -145
- package/scripts/testing/src/core/retrieval/context-retriever.js +0 -516
- package/scripts/testing/src/core/session/index.js +0 -1
- package/scripts/testing/src/core/session/session-manager.js +0 -323
- package/scripts/testing/src/core/trace/cli-trace-wrapper.js +0 -140
- package/scripts/testing/src/core/trace/db-trace-wrapper.js +0 -251
- package/scripts/testing/src/core/trace/debug-trace.js +0 -398
- package/scripts/testing/src/core/trace/index.js +0 -120
- package/scripts/testing/src/core/trace/linear-api-wrapper.js +0 -204
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Database } from '../../src/core/database/database.js';
|
|
3
|
-
import { FrameManager } from '../../src/core/frame/frame-manager.js';
|
|
4
|
-
import { SessionManager } from '../../src/core/context/session-manager.js';
|
|
5
|
-
import { ContextRetriever } from '../../src/core/retrieval/context-retriever.js';
|
|
6
|
-
import { performance } from 'perf_hooks';
|
|
7
|
-
import * as fs from 'fs/promises';
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
export class MetricsCollector {
|
|
10
|
-
constructor() {
|
|
11
|
-
this.metrics = new Map();
|
|
12
|
-
this.db = Database.getInstance();
|
|
13
|
-
this.frameManager = FrameManager.getInstance();
|
|
14
|
-
this.sessionManager = SessionManager.getInstance();
|
|
15
|
-
this.retriever = new ContextRetriever();
|
|
16
|
-
}
|
|
17
|
-
async initialize() {
|
|
18
|
-
await this.db.initialize();
|
|
19
|
-
}
|
|
20
|
-
async startSession(variant) {
|
|
21
|
-
const sessionId = `test-${variant}-${Date.now()}`;
|
|
22
|
-
if (variant === 'with_stackmemory') {
|
|
23
|
-
await this.sessionManager.createSession(sessionId);
|
|
24
|
-
}
|
|
25
|
-
this.metrics.set(sessionId, {
|
|
26
|
-
sessionId,
|
|
27
|
-
variant,
|
|
28
|
-
startTime: new Date(),
|
|
29
|
-
contextReestablishmentTime: 0,
|
|
30
|
-
toolCalls: 0,
|
|
31
|
-
framesCreated: 0,
|
|
32
|
-
framesClosedProperly: 0,
|
|
33
|
-
decisionsAnchored: 0,
|
|
34
|
-
errorsEncountered: 0,
|
|
35
|
-
completionTime: 0,
|
|
36
|
-
reworkInstances: 0,
|
|
37
|
-
contextRetrievals: 0,
|
|
38
|
-
contextRelevanceScores: [],
|
|
39
|
-
memoryUsage: process.memoryUsage().heapUsed,
|
|
40
|
-
tokenUsage: 0
|
|
41
|
-
});
|
|
42
|
-
return sessionId;
|
|
43
|
-
}
|
|
44
|
-
async measureContextReestablishment(sessionId) {
|
|
45
|
-
const start = performance.now();
|
|
46
|
-
const metrics = this.metrics.get(sessionId);
|
|
47
|
-
if (!metrics)
|
|
48
|
-
throw new Error(`Session ${sessionId} not found`);
|
|
49
|
-
if (metrics.variant === 'with_stackmemory') {
|
|
50
|
-
// Measure time to retrieve context
|
|
51
|
-
const context = await this.retriever.getRelevantContext('continue previous work', 10000);
|
|
52
|
-
const duration = performance.now() - start;
|
|
53
|
-
metrics.contextReestablishmentTime = duration;
|
|
54
|
-
metrics.contextRetrievals++;
|
|
55
|
-
metrics.tokenUsage += context.totalTokens || 0;
|
|
56
|
-
return duration;
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
// Simulate manual context reestablishment
|
|
60
|
-
const simulatedTime = 300000; // 5 minutes
|
|
61
|
-
metrics.contextReestablishmentTime = simulatedTime;
|
|
62
|
-
return simulatedTime;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
trackToolCall(sessionId, toolName) {
|
|
66
|
-
const metrics = this.metrics.get(sessionId);
|
|
67
|
-
if (metrics) {
|
|
68
|
-
metrics.toolCalls++;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
trackFrameCreation(sessionId, frameId) {
|
|
72
|
-
const metrics = this.metrics.get(sessionId);
|
|
73
|
-
if (metrics) {
|
|
74
|
-
metrics.framesCreated++;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
trackFrameClosure(sessionId, frameId, properClosure) {
|
|
78
|
-
const metrics = this.metrics.get(sessionId);
|
|
79
|
-
if (metrics && properClosure) {
|
|
80
|
-
metrics.framesClosedProperly++;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
trackDecision(sessionId, decision) {
|
|
84
|
-
const metrics = this.metrics.get(sessionId);
|
|
85
|
-
if (metrics) {
|
|
86
|
-
metrics.decisionsAnchored++;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
trackError(sessionId, error) {
|
|
90
|
-
const metrics = this.metrics.get(sessionId);
|
|
91
|
-
if (metrics) {
|
|
92
|
-
metrics.errorsEncountered++;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
trackRework(sessionId) {
|
|
96
|
-
const metrics = this.metrics.get(sessionId);
|
|
97
|
-
if (metrics) {
|
|
98
|
-
metrics.reworkInstances++;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
async scoreContextRelevance(sessionId, query, retrievedContext) {
|
|
102
|
-
// In real implementation, this would use LLM to score relevance
|
|
103
|
-
const score = Math.random() * 0.3 + 0.7; // Mock: 0.7-1.0 range
|
|
104
|
-
const metrics = this.metrics.get(sessionId);
|
|
105
|
-
if (metrics) {
|
|
106
|
-
metrics.contextRelevanceScores.push(score);
|
|
107
|
-
}
|
|
108
|
-
return score;
|
|
109
|
-
}
|
|
110
|
-
async endSession(sessionId) {
|
|
111
|
-
const metrics = this.metrics.get(sessionId);
|
|
112
|
-
if (!metrics)
|
|
113
|
-
throw new Error(`Session ${sessionId} not found`);
|
|
114
|
-
metrics.endTime = new Date();
|
|
115
|
-
metrics.completionTime = metrics.endTime.getTime() - metrics.startTime.getTime();
|
|
116
|
-
metrics.memoryUsage = process.memoryUsage().heapUsed - metrics.memoryUsage;
|
|
117
|
-
return metrics;
|
|
118
|
-
}
|
|
119
|
-
async collectSessionMetrics(sessionId) {
|
|
120
|
-
const metrics = this.metrics.get(sessionId);
|
|
121
|
-
if (!metrics)
|
|
122
|
-
throw new Error(`Session ${sessionId} not found`);
|
|
123
|
-
// Collect additional metrics from database
|
|
124
|
-
if (metrics.variant === 'with_stackmemory') {
|
|
125
|
-
const frames = await this.db.query('SELECT * FROM frames WHERE session_id = ?', [sessionId]);
|
|
126
|
-
const events = await this.db.query('SELECT * FROM events WHERE session_id = ?', [sessionId]);
|
|
127
|
-
const anchors = await this.db.query('SELECT * FROM anchors WHERE session_id = ?', [sessionId]);
|
|
128
|
-
metrics.framesCreated = frames.length;
|
|
129
|
-
metrics.framesClosedProperly = frames.filter((f) => f.state === 'closed').length;
|
|
130
|
-
metrics.decisionsAnchored = anchors.length;
|
|
131
|
-
metrics.toolCalls = events.filter((e) => e.type === 'tool_call').length;
|
|
132
|
-
metrics.errorsEncountered = events.filter((e) => e.type === 'error').length;
|
|
133
|
-
}
|
|
134
|
-
return metrics;
|
|
135
|
-
}
|
|
136
|
-
async compareVariants(withStackMemory, withoutStackMemory) {
|
|
137
|
-
// Calculate improvements
|
|
138
|
-
const avgWith = this.calculateAverages(withStackMemory);
|
|
139
|
-
const avgWithout = this.calculateAverages(withoutStackMemory);
|
|
140
|
-
const contextSpeedImprovement = ((avgWithout.contextReestablishmentTime - avgWith.contextReestablishmentTime) /
|
|
141
|
-
avgWithout.contextReestablishmentTime) * 100;
|
|
142
|
-
const taskCompletionImprovement = ((avgWithout.completionTime - avgWith.completionTime) /
|
|
143
|
-
avgWithout.completionTime) * 100;
|
|
144
|
-
const errorRecoveryImprovement = ((avgWithout.errorsEncountered - avgWith.errorsEncountered) /
|
|
145
|
-
Math.max(avgWithout.errorsEncountered, 1)) * 100;
|
|
146
|
-
const consistencyImprovement = ((avgWith.decisionsAnchored - avgWithout.decisionsAnchored) /
|
|
147
|
-
Math.max(avgWithout.decisionsAnchored, 1)) * 100;
|
|
148
|
-
// Calculate statistical significance (simplified)
|
|
149
|
-
const pValue = this.calculatePValue(withStackMemory, withoutStackMemory);
|
|
150
|
-
const confidence = (1 - pValue) * 100;
|
|
151
|
-
return {
|
|
152
|
-
improvement: {
|
|
153
|
-
contextSpeed: contextSpeedImprovement,
|
|
154
|
-
taskCompletion: taskCompletionImprovement,
|
|
155
|
-
errorRecovery: errorRecoveryImprovement,
|
|
156
|
-
consistency: consistencyImprovement
|
|
157
|
-
},
|
|
158
|
-
statistics: {
|
|
159
|
-
sampleSize: withStackMemory.length + withoutStackMemory.length,
|
|
160
|
-
confidence,
|
|
161
|
-
pValue
|
|
162
|
-
},
|
|
163
|
-
recommendations: this.generateRecommendations({
|
|
164
|
-
contextSpeedImprovement,
|
|
165
|
-
taskCompletionImprovement,
|
|
166
|
-
errorRecoveryImprovement,
|
|
167
|
-
consistencyImprovement
|
|
168
|
-
})
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
calculateAverages(metrics) {
|
|
172
|
-
const sum = metrics.reduce((acc, m) => ({
|
|
173
|
-
contextReestablishmentTime: acc.contextReestablishmentTime + m.contextReestablishmentTime,
|
|
174
|
-
completionTime: acc.completionTime + m.completionTime,
|
|
175
|
-
errorsEncountered: acc.errorsEncountered + m.errorsEncountered,
|
|
176
|
-
decisionsAnchored: acc.decisionsAnchored + m.decisionsAnchored,
|
|
177
|
-
reworkInstances: acc.reworkInstances + m.reworkInstances
|
|
178
|
-
}), {
|
|
179
|
-
contextReestablishmentTime: 0,
|
|
180
|
-
completionTime: 0,
|
|
181
|
-
errorsEncountered: 0,
|
|
182
|
-
decisionsAnchored: 0,
|
|
183
|
-
reworkInstances: 0
|
|
184
|
-
});
|
|
185
|
-
return {
|
|
186
|
-
contextReestablishmentTime: sum.contextReestablishmentTime / metrics.length,
|
|
187
|
-
completionTime: sum.completionTime / metrics.length,
|
|
188
|
-
errorsEncountered: sum.errorsEncountered / metrics.length,
|
|
189
|
-
decisionsAnchored: sum.decisionsAnchored / metrics.length,
|
|
190
|
-
reworkInstances: sum.reworkInstances / metrics.length
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
calculatePValue(group1, group2) {
|
|
194
|
-
// Simplified t-test calculation
|
|
195
|
-
// In production, use proper statistical library
|
|
196
|
-
return 0.02; // Mock significant result
|
|
197
|
-
}
|
|
198
|
-
generateRecommendations(improvements) {
|
|
199
|
-
const recommendations = [];
|
|
200
|
-
if (improvements.contextSpeedImprovement > 50) {
|
|
201
|
-
recommendations.push('StackMemory significantly reduces context reestablishment time');
|
|
202
|
-
}
|
|
203
|
-
if (improvements.taskCompletionImprovement > 30) {
|
|
204
|
-
recommendations.push('Tasks complete faster with StackMemory enabled');
|
|
205
|
-
}
|
|
206
|
-
if (improvements.errorRecoveryImprovement > 20) {
|
|
207
|
-
recommendations.push('Error recovery is more efficient with saved context');
|
|
208
|
-
}
|
|
209
|
-
if (improvements.consistencyImprovement > 40) {
|
|
210
|
-
recommendations.push('Decision consistency greatly improved with anchored context');
|
|
211
|
-
}
|
|
212
|
-
return recommendations;
|
|
213
|
-
}
|
|
214
|
-
async saveMetrics(sessionId, outputDir = './test-results') {
|
|
215
|
-
const metrics = this.metrics.get(sessionId);
|
|
216
|
-
if (!metrics)
|
|
217
|
-
return;
|
|
218
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
219
|
-
const filename = path.join(outputDir, `${sessionId}.json`);
|
|
220
|
-
await fs.writeFile(filename, JSON.stringify(metrics, null, 2));
|
|
221
|
-
}
|
|
222
|
-
async generateReport(outputPath = './test-results/report.md') {
|
|
223
|
-
const withStackMemory = Array.from(this.metrics.values())
|
|
224
|
-
.filter(m => m.variant === 'with_stackmemory');
|
|
225
|
-
const withoutStackMemory = Array.from(this.metrics.values())
|
|
226
|
-
.filter(m => m.variant === 'without_stackmemory');
|
|
227
|
-
const comparison = await this.compareVariants(withStackMemory, withoutStackMemory);
|
|
228
|
-
const report = `# StackMemory Effectiveness Report
|
|
229
|
-
|
|
230
|
-
## Executive Summary
|
|
231
|
-
- Sample Size: ${comparison.statistics.sampleSize} sessions
|
|
232
|
-
- Statistical Confidence: ${comparison.statistics.confidence.toFixed(1)}%
|
|
233
|
-
- P-Value: ${comparison.statistics.pValue}
|
|
234
|
-
|
|
235
|
-
## Performance Improvements
|
|
236
|
-
- Context Reestablishment: ${comparison.improvement.contextSpeed.toFixed(1)}% faster
|
|
237
|
-
- Task Completion: ${comparison.improvement.taskCompletion.toFixed(1)}% faster
|
|
238
|
-
- Error Recovery: ${comparison.improvement.errorRecovery.toFixed(1)}% better
|
|
239
|
-
- Decision Consistency: ${comparison.improvement.consistency.toFixed(1)}% improved
|
|
240
|
-
|
|
241
|
-
## Recommendations
|
|
242
|
-
${comparison.recommendations.map(r => `- ${r}`).join('\n')}
|
|
243
|
-
|
|
244
|
-
## Detailed Metrics
|
|
245
|
-
|
|
246
|
-
### With StackMemory
|
|
247
|
-
${this.formatMetricsTable(withStackMemory)}
|
|
248
|
-
|
|
249
|
-
### Without StackMemory
|
|
250
|
-
${this.formatMetricsTable(withoutStackMemory)}
|
|
251
|
-
|
|
252
|
-
Generated: ${new Date().toISOString()}
|
|
253
|
-
`;
|
|
254
|
-
await fs.writeFile(outputPath, report);
|
|
255
|
-
console.log(`Report generated: ${outputPath}`);
|
|
256
|
-
}
|
|
257
|
-
formatMetricsTable(metrics) {
|
|
258
|
-
if (metrics.length === 0)
|
|
259
|
-
return 'No data available';
|
|
260
|
-
const avg = this.calculateAverages(metrics);
|
|
261
|
-
return `
|
|
262
|
-
| Metric | Average |
|
|
263
|
-
|--------|---------|
|
|
264
|
-
| Context Reestablishment | ${(avg.contextReestablishmentTime / 1000).toFixed(2)}s |
|
|
265
|
-
| Task Completion | ${(avg.completionTime / 1000 / 60).toFixed(2)} min |
|
|
266
|
-
| Errors Encountered | ${avg.errorsEncountered.toFixed(1)} |
|
|
267
|
-
| Decisions Anchored | ${avg.decisionsAnchored.toFixed(1)} |
|
|
268
|
-
| Rework Instances | ${avg.reworkInstances.toFixed(1)} |
|
|
269
|
-
`;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
// CLI interface
|
|
273
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
274
|
-
const collector = new MetricsCollector();
|
|
275
|
-
const command = process.argv[2];
|
|
276
|
-
async function main() {
|
|
277
|
-
await collector.initialize();
|
|
278
|
-
switch (command) {
|
|
279
|
-
case 'start':
|
|
280
|
-
const variant = process.argv[3];
|
|
281
|
-
const sessionId = await collector.startSession(variant);
|
|
282
|
-
console.log(`Session started: ${sessionId}`);
|
|
283
|
-
break;
|
|
284
|
-
case 'report':
|
|
285
|
-
await collector.generateReport();
|
|
286
|
-
break;
|
|
287
|
-
default:
|
|
288
|
-
console.log('Usage: collect-metrics.ts [start|report] [with_stackmemory|without_stackmemory]');
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
main().catch(console.error);
|
|
292
|
-
}
|
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Context Bridge - Automatic synchronization between sessions and shared context
|
|
3
|
-
*
|
|
4
|
-
* This bridge automatically:
|
|
5
|
-
* - Syncs important frames to shared context
|
|
6
|
-
* - Loads relevant context on session start
|
|
7
|
-
* - Maintains consistency across sessions
|
|
8
|
-
*/
|
|
9
|
-
import { sharedContextLayer } from './shared-context-layer.js';
|
|
10
|
-
import { sessionManager } from '../session/session-manager.js';
|
|
11
|
-
import { logger } from '../monitoring/logger.js';
|
|
12
|
-
export class ContextBridge {
|
|
13
|
-
constructor() {
|
|
14
|
-
this.frameManager = null;
|
|
15
|
-
this.syncTimer = null;
|
|
16
|
-
this.lastSyncTime = 0;
|
|
17
|
-
this.options = {
|
|
18
|
-
autoSync: true,
|
|
19
|
-
syncInterval: 60000, // 1 minute
|
|
20
|
-
minFrameScore: 0.5, // Include frames with score above 0.5
|
|
21
|
-
importantTags: ['decision', 'error', 'milestone', 'learning'],
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
static getInstance() {
|
|
25
|
-
if (!ContextBridge.instance) {
|
|
26
|
-
ContextBridge.instance = new ContextBridge();
|
|
27
|
-
}
|
|
28
|
-
return ContextBridge.instance;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Initialize the bridge with a frame manager
|
|
32
|
-
*/
|
|
33
|
-
async initialize(frameManager, options) {
|
|
34
|
-
this.frameManager = frameManager;
|
|
35
|
-
this.options = { ...this.options, ...options };
|
|
36
|
-
// Load shared context on initialization
|
|
37
|
-
await this.loadSharedContext();
|
|
38
|
-
// Start auto-sync if enabled
|
|
39
|
-
if (this.options.autoSync) {
|
|
40
|
-
this.startAutoSync();
|
|
41
|
-
}
|
|
42
|
-
logger.info('Context bridge initialized', {
|
|
43
|
-
autoSync: this.options.autoSync,
|
|
44
|
-
syncInterval: this.options.syncInterval,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Load relevant shared context into current session
|
|
49
|
-
*/
|
|
50
|
-
async loadSharedContext() {
|
|
51
|
-
try {
|
|
52
|
-
const session = sessionManager.getCurrentSession();
|
|
53
|
-
if (!session)
|
|
54
|
-
return;
|
|
55
|
-
// Get context discovery
|
|
56
|
-
const discovery = await sharedContextLayer.autoDiscoverContext();
|
|
57
|
-
if (!discovery.hasSharedContext) {
|
|
58
|
-
logger.info('No shared context available to load');
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
// Load recent patterns as metadata
|
|
62
|
-
if (discovery.recentPatterns.length > 0) {
|
|
63
|
-
logger.info('Loaded recent patterns from shared context', {
|
|
64
|
-
patternCount: discovery.recentPatterns.length,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
// Load last decisions for reference
|
|
68
|
-
if (discovery.lastDecisions.length > 0) {
|
|
69
|
-
logger.info('Loaded recent decisions from shared context', {
|
|
70
|
-
decisionCount: discovery.lastDecisions.length,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
// Store suggested frames in metadata for quick reference
|
|
74
|
-
if (discovery.suggestedFrames.length > 0) {
|
|
75
|
-
const metadata = {
|
|
76
|
-
suggestedFrames: discovery.suggestedFrames,
|
|
77
|
-
loadedAt: Date.now(),
|
|
78
|
-
};
|
|
79
|
-
// Store in frame manager's context
|
|
80
|
-
if (this.frameManager) {
|
|
81
|
-
await this.frameManager.addContext('shared-context-suggestions', metadata);
|
|
82
|
-
}
|
|
83
|
-
logger.info('Loaded suggested frames from shared context', {
|
|
84
|
-
frameCount: discovery.suggestedFrames.length,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
catch (error) {
|
|
89
|
-
logger.error('Failed to load shared context', error);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Sync current session's important frames to shared context
|
|
94
|
-
*/
|
|
95
|
-
async syncToSharedContext() {
|
|
96
|
-
try {
|
|
97
|
-
if (!this.frameManager)
|
|
98
|
-
return;
|
|
99
|
-
const session = sessionManager.getCurrentSession();
|
|
100
|
-
if (!session)
|
|
101
|
-
return;
|
|
102
|
-
// Get all active frames (filter out any nulls from missing frames)
|
|
103
|
-
const activeFrames = this.frameManager.getActiveFramePath().filter(Boolean);
|
|
104
|
-
// Get recent closed frames (last 100)
|
|
105
|
-
const recentFrames = await this.frameManager.getRecentFrames(100);
|
|
106
|
-
// Combine and filter important frames
|
|
107
|
-
const allFrames = [...activeFrames, ...recentFrames].filter(Boolean);
|
|
108
|
-
const importantFrames = this.filterImportantFrames(allFrames);
|
|
109
|
-
if (importantFrames.length === 0) {
|
|
110
|
-
logger.debug('No important frames to sync');
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
// Add to shared context
|
|
114
|
-
await sharedContextLayer.addToSharedContext(importantFrames, {
|
|
115
|
-
minScore: this.options.minFrameScore,
|
|
116
|
-
tags: this.options.importantTags,
|
|
117
|
-
});
|
|
118
|
-
this.lastSyncTime = Date.now();
|
|
119
|
-
logger.info('Synced frames to shared context', {
|
|
120
|
-
frameCount: importantFrames.length,
|
|
121
|
-
sessionId: session.sessionId,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
catch (error) {
|
|
125
|
-
logger.error('Failed to sync to shared context', error);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Query shared context for relevant frames
|
|
130
|
-
*/
|
|
131
|
-
async querySharedFrames(query) {
|
|
132
|
-
try {
|
|
133
|
-
const results = await sharedContextLayer.querySharedContext({
|
|
134
|
-
...query,
|
|
135
|
-
minScore: this.options.minFrameScore,
|
|
136
|
-
});
|
|
137
|
-
logger.info('Queried shared context', {
|
|
138
|
-
query,
|
|
139
|
-
resultCount: results.length,
|
|
140
|
-
});
|
|
141
|
-
return results;
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
logger.error('Failed to query shared context', error);
|
|
145
|
-
return [];
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Add a decision to shared context
|
|
150
|
-
*/
|
|
151
|
-
async addDecision(decision, reasoning) {
|
|
152
|
-
try {
|
|
153
|
-
await sharedContextLayer.addDecision({
|
|
154
|
-
decision,
|
|
155
|
-
reasoning,
|
|
156
|
-
outcome: 'pending',
|
|
157
|
-
});
|
|
158
|
-
logger.info('Added decision to shared context', { decision });
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
logger.error('Failed to add decision', error);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Start automatic synchronization
|
|
166
|
-
*/
|
|
167
|
-
startAutoSync() {
|
|
168
|
-
if (this.syncTimer) {
|
|
169
|
-
clearInterval(this.syncTimer);
|
|
170
|
-
}
|
|
171
|
-
this.syncTimer = setInterval(() => {
|
|
172
|
-
this.syncToSharedContext().catch((error) => {
|
|
173
|
-
logger.error('Auto-sync failed', error);
|
|
174
|
-
});
|
|
175
|
-
}, this.options.syncInterval);
|
|
176
|
-
// Also sync on important events
|
|
177
|
-
this.setupEventListeners();
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Stop automatic synchronization
|
|
181
|
-
*/
|
|
182
|
-
stopAutoSync() {
|
|
183
|
-
if (this.syncTimer) {
|
|
184
|
-
clearInterval(this.syncTimer);
|
|
185
|
-
this.syncTimer = null;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Filter frames that are important enough to share
|
|
190
|
-
*/
|
|
191
|
-
filterImportantFrames(frames) {
|
|
192
|
-
return frames.filter((frame) => {
|
|
193
|
-
// Check if frame has important tags
|
|
194
|
-
const hasImportantTag = this.options.importantTags.some((tag) => frame.metadata?.tags?.includes(tag));
|
|
195
|
-
// Check frame type importance
|
|
196
|
-
const isImportantType = [
|
|
197
|
-
'task',
|
|
198
|
-
'milestone',
|
|
199
|
-
'error',
|
|
200
|
-
'resolution',
|
|
201
|
-
'decision',
|
|
202
|
-
].includes(frame.type);
|
|
203
|
-
// Check metadata importance flag
|
|
204
|
-
const markedImportant = frame.metadata?.importance === 'high';
|
|
205
|
-
return hasImportantTag || isImportantType || markedImportant;
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Setup event listeners for automatic syncing
|
|
210
|
-
*/
|
|
211
|
-
setupEventListeners() {
|
|
212
|
-
if (!this.frameManager)
|
|
213
|
-
return;
|
|
214
|
-
// Sync when a frame is closed
|
|
215
|
-
const originalClose = this.frameManager.closeFrame.bind(this.frameManager);
|
|
216
|
-
this.frameManager.closeFrame = async (frameId, metadata) => {
|
|
217
|
-
const result = await originalClose(frameId, metadata);
|
|
218
|
-
// Sync if it was an important frame
|
|
219
|
-
const frame = await this.frameManager.getFrame(frameId);
|
|
220
|
-
if (frame && this.filterImportantFrames([frame]).length > 0) {
|
|
221
|
-
await this.syncToSharedContext();
|
|
222
|
-
}
|
|
223
|
-
return result;
|
|
224
|
-
};
|
|
225
|
-
// Sync when a milestone is reached
|
|
226
|
-
const originalMilestone = this.frameManager.createFrame.bind(this.frameManager);
|
|
227
|
-
this.frameManager.createFrame = async (params) => {
|
|
228
|
-
const result = await originalMilestone(params);
|
|
229
|
-
if (params.type === 'milestone') {
|
|
230
|
-
await this.syncToSharedContext();
|
|
231
|
-
}
|
|
232
|
-
return result;
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Get sync statistics
|
|
237
|
-
*/
|
|
238
|
-
getSyncStats() {
|
|
239
|
-
return {
|
|
240
|
-
lastSyncTime: this.lastSyncTime,
|
|
241
|
-
autoSyncEnabled: this.options.autoSync,
|
|
242
|
-
syncInterval: this.options.syncInterval,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Manual trigger for immediate sync
|
|
247
|
-
*/
|
|
248
|
-
async forceSyncNow() {
|
|
249
|
-
logger.info('Force sync triggered');
|
|
250
|
-
await this.syncToSharedContext();
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
export const contextBridge = ContextBridge.getInstance();
|