@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,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/core/context/context-bridge.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Context Bridge - Automatic synchronization between sessions and shared context\n *\n * This bridge automatically:\n * - Syncs important frames to shared context\n * - Loads relevant context on session start\n * - Maintains consistency across sessions\n */\n\nimport { FrameManager, type Frame } from './index.js';\nimport { sharedContextLayer } from './shared-context-layer.js';\nimport { sessionManager } from '../session/session-manager.js';\nimport { logger } from '../monitoring/logger.js';\n\nexport interface BridgeOptions {\n autoSync: boolean;\n syncInterval: number;\n minFrameScore: number;\n importantTags: string[];\n}\n\nexport class ContextBridge {\n private static instance: ContextBridge;\n private frameManager: FrameManager | null = null;\n private syncTimer: NodeJS.Timeout | null = null;\n private lastSyncTime: number = 0;\n private options: BridgeOptions = {\n autoSync: true,\n syncInterval: 60000, // 1 minute\n minFrameScore: 0.5, // Include frames with score above 0.5\n importantTags: ['decision', 'error', 'milestone', 'learning'],\n };\n\n private constructor() {}\n\n static getInstance(): ContextBridge {\n if (!ContextBridge.instance) {\n ContextBridge.instance = new ContextBridge();\n }\n return ContextBridge.instance;\n }\n\n /**\n * Initialize the bridge with a frame manager\n */\n async initialize(\n frameManager: FrameManager,\n options?: Partial<BridgeOptions>\n ): Promise<void> {\n this.frameManager = frameManager;\n this.options = { ...this.options, ...options };\n\n // Load shared context on initialization\n await this.loadSharedContext();\n\n // Start auto-sync if enabled\n if (this.options.autoSync) {\n this.startAutoSync();\n }\n\n logger.info('Context bridge initialized', {\n autoSync: this.options.autoSync,\n syncInterval: this.options.syncInterval,\n });\n }\n\n /**\n * Load relevant shared context into current session\n */\n async loadSharedContext(): Promise<void> {\n try {\n const session = sessionManager.getCurrentSession();\n if (!session) return;\n\n // Get context discovery\n const discovery = await sharedContextLayer.autoDiscoverContext();\n\n if (!discovery.hasSharedContext) {\n logger.info('No shared context available to load');\n return;\n }\n\n // Load recent patterns as metadata\n if (discovery.recentPatterns.length > 0) {\n logger.info('Loaded recent patterns from shared context', {\n patternCount: discovery.recentPatterns.length,\n });\n }\n\n // Load last decisions for reference\n if (discovery.lastDecisions.length > 0) {\n logger.info('Loaded recent decisions from shared context', {\n decisionCount: discovery.lastDecisions.length,\n });\n }\n\n // Store suggested frames in metadata for quick reference\n if (discovery.suggestedFrames.length > 0) {\n const metadata = {\n suggestedFrames: discovery.suggestedFrames,\n loadedAt: Date.now(),\n };\n\n // Store in frame manager's context\n if (this.frameManager) {\n await this.frameManager.addContext(\n 'shared-context-suggestions',\n metadata\n );\n }\n\n logger.info('Loaded suggested frames from shared context', {\n frameCount: discovery.suggestedFrames.length,\n });\n }\n } catch (error: unknown) {\n logger.error('Failed to load shared context', error as Error);\n }\n }\n\n /**\n * Sync current session's important frames to shared context\n */\n async syncToSharedContext(): Promise<void> {\n try {\n if (!this.frameManager) return;\n\n const session = sessionManager.getCurrentSession();\n if (!session) return;\n\n // Get all active frames (filter out any nulls from missing frames)\n const activeFrames = this.frameManager\n .getActiveFramePath()\n .filter(Boolean);\n\n // Get recent closed frames (last 100)\n const recentFrames = await this.frameManager.getRecentFrames(100);\n\n // Combine and filter important frames\n const allFrames = [...activeFrames, ...recentFrames].filter(Boolean);\n const importantFrames = this.filterImportantFrames(allFrames);\n\n if (importantFrames.length === 0) {\n logger.debug('No important frames to sync');\n return;\n }\n\n // Add to shared context\n await sharedContextLayer.addToSharedContext(importantFrames, {\n minScore: this.options.minFrameScore,\n tags: this.options.importantTags,\n });\n\n this.lastSyncTime = Date.now();\n\n logger.info('Synced frames to shared context', {\n frameCount: importantFrames.length,\n sessionId: session.sessionId,\n });\n } catch (error: unknown) {\n logger.error('Failed to sync to shared context', error as Error);\n }\n }\n\n /**\n * Query shared context for relevant frames\n */\n async querySharedFrames(query: {\n tags?: string[];\n type?: string;\n limit?: number;\n }): Promise<any[]> {\n try {\n const results = await sharedContextLayer.querySharedContext({\n ...query,\n minScore: this.options.minFrameScore,\n });\n\n logger.info('Queried shared context', {\n query,\n resultCount: results.length,\n });\n\n return results;\n } catch (error: unknown) {\n logger.error('Failed to query shared context', error as Error);\n return [];\n }\n }\n\n /**\n * Add a decision to shared context\n */\n async addDecision(decision: string, reasoning: string): Promise<void> {\n try {\n await sharedContextLayer.addDecision({\n decision,\n reasoning,\n outcome: 'pending',\n });\n\n logger.info('Added decision to shared context', { decision });\n } catch (error: unknown) {\n logger.error('Failed to add decision', error as Error);\n }\n }\n\n /**\n * Start automatic synchronization\n */\n private startAutoSync(): void {\n if (this.syncTimer) {\n clearInterval(this.syncTimer);\n }\n\n this.syncTimer = setInterval(() => {\n this.syncToSharedContext().catch((error) => {\n logger.error('Auto-sync failed', error as Error);\n });\n }, this.options.syncInterval);\n\n // Also sync on important events\n this.setupEventListeners();\n }\n\n /**\n * Stop automatic synchronization\n */\n stopAutoSync(): void {\n if (this.syncTimer) {\n clearInterval(this.syncTimer);\n this.syncTimer = null;\n }\n }\n\n /**\n * Filter frames that are important enough to share\n */\n private filterImportantFrames(frames: Frame[]): Frame[] {\n return frames.filter((frame) => {\n // Check if frame has important tags\n const hasImportantTag = this.options.importantTags.some((tag) =>\n frame.metadata?.tags?.includes(tag)\n );\n\n // Check frame type importance\n const isImportantType = [\n 'task',\n 'milestone',\n 'error',\n 'resolution',\n 'decision',\n ].includes(frame.type);\n\n // Check metadata importance flag\n const markedImportant = frame.metadata?.importance === 'high';\n\n return hasImportantTag || isImportantType || markedImportant;\n });\n }\n\n /**\n * Setup event listeners for automatic syncing\n */\n private setupEventListeners(): void {\n if (!this.frameManager) return;\n\n // Sync when a frame is closed\n const originalClose = this.frameManager.closeFrame.bind(this.frameManager);\n this.frameManager.closeFrame = async (frameId: string, metadata?: any) => {\n const result = await originalClose(frameId, metadata);\n\n // Sync if it was an important frame\n const frame = await this.frameManager!.getFrame(frameId);\n if (frame && this.filterImportantFrames([frame]).length > 0) {\n await this.syncToSharedContext();\n }\n\n return result;\n };\n\n // Sync when a milestone is reached\n const originalMilestone = this.frameManager.createFrame.bind(\n this.frameManager\n );\n this.frameManager.createFrame = async (params: any) => {\n const result = await originalMilestone(params);\n\n if (params.type === 'milestone') {\n await this.syncToSharedContext();\n }\n\n return result;\n };\n }\n\n /**\n * Get sync statistics\n */\n getSyncStats(): {\n lastSyncTime: number;\n autoSyncEnabled: boolean;\n syncInterval: number;\n } {\n return {\n lastSyncTime: this.lastSyncTime,\n autoSyncEnabled: this.options.autoSync,\n syncInterval: this.options.syncInterval,\n };\n }\n\n /**\n * Manual trigger for immediate sync\n */\n async forceSyncNow(): Promise<void> {\n logger.info('Force sync triggered');\n await this.syncToSharedContext();\n }\n}\n\nexport const contextBridge = ContextBridge.getInstance();\n\n// Export for testing\nexport { BridgeOptions };\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { MetricsCollector } from './collect-metrics.js';
|
|
3
|
-
import { spawn } from 'child_process';
|
|
4
|
-
import * as fs from 'fs/promises';
|
|
5
|
-
import * as path from 'path';
|
|
6
|
-
export class ABTestRunner {
|
|
7
|
-
constructor() {
|
|
8
|
-
this.scenarios = new Map();
|
|
9
|
-
this.runs = [];
|
|
10
|
-
this.stackMemoryEnabled = false;
|
|
11
|
-
this.collector = new MetricsCollector();
|
|
12
|
-
this.loadScenarios();
|
|
13
|
-
}
|
|
14
|
-
loadScenarios() {
|
|
15
|
-
// Define test scenarios
|
|
16
|
-
const scenarios = [
|
|
17
|
-
{
|
|
18
|
-
id: 'multi_session_feature',
|
|
19
|
-
name: 'E-commerce checkout flow',
|
|
20
|
-
type: 'feature_dev',
|
|
21
|
-
description: 'Implement a complete checkout flow with payment integration',
|
|
22
|
-
complexity: 'high',
|
|
23
|
-
expectedDuration: 180,
|
|
24
|
-
steps: [
|
|
25
|
-
{ action: 'Design checkout flow architecture', requiresContext: false },
|
|
26
|
-
{ action: 'Implement cart validation', requiresContext: true },
|
|
27
|
-
{ action: 'Add payment gateway integration', requiresContext: true },
|
|
28
|
-
{ action: 'Create checkout UI components', requiresContext: true },
|
|
29
|
-
{ action: 'Add order confirmation', requiresContext: true },
|
|
30
|
-
{ action: 'Write integration tests', requiresContext: true }
|
|
31
|
-
],
|
|
32
|
-
contextBreaks: [
|
|
33
|
-
{ afterStep: 2, duration: 480, type: 'session_end' }, // Overnight
|
|
34
|
-
{ afterStep: 4, duration: 60, type: 'interruption' } // Lunch break
|
|
35
|
-
]
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
id: 'complex_debugging',
|
|
39
|
-
name: 'Performance issue in production',
|
|
40
|
-
type: 'complex_debug',
|
|
41
|
-
description: 'Debug and fix a memory leak causing performance degradation',
|
|
42
|
-
complexity: 'high',
|
|
43
|
-
expectedDuration: 120,
|
|
44
|
-
steps: [
|
|
45
|
-
{ action: 'Analyze performance metrics', requiresContext: false },
|
|
46
|
-
{ action: 'Profile memory usage', requiresContext: true },
|
|
47
|
-
{ action: 'Identify memory leak source', requiresContext: true },
|
|
48
|
-
{ action: 'Implement fix', requiresContext: true },
|
|
49
|
-
{ action: 'Verify fix with tests', requiresContext: true }
|
|
50
|
-
],
|
|
51
|
-
contextBreaks: [
|
|
52
|
-
{ afterStep: 3, duration: 30, type: 'team_handoff' }
|
|
53
|
-
]
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
id: 'large_refactoring',
|
|
57
|
-
name: 'Migrate authentication system',
|
|
58
|
-
type: 'refactor',
|
|
59
|
-
description: 'Refactor from session-based to JWT authentication',
|
|
60
|
-
complexity: 'very_high',
|
|
61
|
-
expectedDuration: 360,
|
|
62
|
-
steps: [
|
|
63
|
-
{ action: 'Analyze current auth implementation', requiresContext: false },
|
|
64
|
-
{ action: 'Design JWT architecture', requiresContext: true },
|
|
65
|
-
{ action: 'Implement JWT service', requiresContext: true },
|
|
66
|
-
{ action: 'Migrate user sessions', requiresContext: true },
|
|
67
|
-
{ action: 'Update API endpoints', requiresContext: true },
|
|
68
|
-
{ action: 'Migrate frontend auth', requiresContext: true },
|
|
69
|
-
{ action: 'Add refresh token logic', requiresContext: true },
|
|
70
|
-
{ action: 'Update tests', requiresContext: true },
|
|
71
|
-
{ action: 'Performance testing', requiresContext: true }
|
|
72
|
-
],
|
|
73
|
-
contextBreaks: [
|
|
74
|
-
{ afterStep: 2, duration: 480, type: 'session_end' },
|
|
75
|
-
{ afterStep: 4, duration: 480, type: 'session_end' },
|
|
76
|
-
{ afterStep: 6, duration: 60, type: 'interruption' },
|
|
77
|
-
{ afterStep: 7, duration: 480, type: 'session_end' }
|
|
78
|
-
]
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
id: 'rapid_bug_fixes',
|
|
82
|
-
name: 'Fix 5 related bugs',
|
|
83
|
-
type: 'bug_fix',
|
|
84
|
-
description: 'Fix multiple related bugs in the user registration flow',
|
|
85
|
-
complexity: 'medium',
|
|
86
|
-
expectedDuration: 90,
|
|
87
|
-
steps: [
|
|
88
|
-
{ action: 'Fix email validation bug', requiresContext: false },
|
|
89
|
-
{ action: 'Fix password strength checker', requiresContext: true },
|
|
90
|
-
{ action: 'Fix duplicate user check', requiresContext: true },
|
|
91
|
-
{ action: 'Fix confirmation email sending', requiresContext: true },
|
|
92
|
-
{ action: 'Fix redirect after registration', requiresContext: true }
|
|
93
|
-
],
|
|
94
|
-
contextBreaks: [
|
|
95
|
-
{ afterStep: 1, duration: 15, type: 'interruption' },
|
|
96
|
-
{ afterStep: 2, duration: 15, type: 'interruption' },
|
|
97
|
-
{ afterStep: 3, duration: 15, type: 'interruption' },
|
|
98
|
-
{ afterStep: 4, duration: 15, type: 'interruption' }
|
|
99
|
-
]
|
|
100
|
-
}
|
|
101
|
-
];
|
|
102
|
-
scenarios.forEach(scenario => {
|
|
103
|
-
this.scenarios.set(scenario.id, scenario);
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
async initialize() {
|
|
107
|
-
await this.collector.initialize();
|
|
108
|
-
}
|
|
109
|
-
async enableStackMemory() {
|
|
110
|
-
console.log('Enabling StackMemory...');
|
|
111
|
-
this.stackMemoryEnabled = true;
|
|
112
|
-
// Start StackMemory daemon if not running
|
|
113
|
-
try {
|
|
114
|
-
await this.executeCommand('stackmemory-daemon status');
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
await this.executeCommand('stackmemory-daemon start');
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
async disableStackMemory() {
|
|
121
|
-
console.log('Disabling StackMemory...');
|
|
122
|
-
this.stackMemoryEnabled = false;
|
|
123
|
-
// Stop StackMemory daemon
|
|
124
|
-
try {
|
|
125
|
-
await this.executeCommand('stackmemory-daemon stop');
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
// Ignore if already stopped
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
executeCommand(command) {
|
|
132
|
-
return new Promise((resolve, reject) => {
|
|
133
|
-
const child = spawn(command, { shell: true });
|
|
134
|
-
let output = '';
|
|
135
|
-
let error = '';
|
|
136
|
-
child.stdout.on('data', (data) => {
|
|
137
|
-
output += data.toString();
|
|
138
|
-
});
|
|
139
|
-
child.stderr.on('data', (data) => {
|
|
140
|
-
error += data.toString();
|
|
141
|
-
});
|
|
142
|
-
child.on('close', (code) => {
|
|
143
|
-
if (code === 0) {
|
|
144
|
-
resolve(output);
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
reject(new Error(error || `Command failed with code ${code}`));
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
async runScenario(scenarioId, variant) {
|
|
153
|
-
const scenario = this.scenarios.get(scenarioId);
|
|
154
|
-
if (!scenario) {
|
|
155
|
-
throw new Error(`Scenario ${scenarioId} not found`);
|
|
156
|
-
}
|
|
157
|
-
console.log(`\nRunning scenario: ${scenario.name} (${variant})`);
|
|
158
|
-
console.log(`Expected duration: ${scenario.expectedDuration} minutes`);
|
|
159
|
-
console.log(`Complexity: ${scenario.complexity}`);
|
|
160
|
-
console.log(`Context breaks: ${scenario.contextBreaks.length}`);
|
|
161
|
-
// Enable/disable StackMemory based on variant
|
|
162
|
-
if (variant === 'with_stackmemory') {
|
|
163
|
-
await this.enableStackMemory();
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
await this.disableStackMemory();
|
|
167
|
-
}
|
|
168
|
-
const runId = `${scenarioId}-${variant}-${Date.now()}`;
|
|
169
|
-
const sessionId = await this.collector.startSession(variant);
|
|
170
|
-
const run = {
|
|
171
|
-
id: runId,
|
|
172
|
-
scenario,
|
|
173
|
-
variant,
|
|
174
|
-
startTime: new Date(),
|
|
175
|
-
metrics: {},
|
|
176
|
-
recordings: [],
|
|
177
|
-
success: false,
|
|
178
|
-
errors: []
|
|
179
|
-
};
|
|
180
|
-
try {
|
|
181
|
-
// Execute scenario steps
|
|
182
|
-
for (let i = 0; i < scenario.steps.length; i++) {
|
|
183
|
-
const step = scenario.steps[i];
|
|
184
|
-
console.log(`\nStep ${i + 1}/${scenario.steps.length}: ${step.action}`);
|
|
185
|
-
// Simulate step execution
|
|
186
|
-
await this.executeStep(step, sessionId, run);
|
|
187
|
-
// Check for context break
|
|
188
|
-
const contextBreak = scenario.contextBreaks.find(cb => cb.afterStep === i + 1);
|
|
189
|
-
if (contextBreak) {
|
|
190
|
-
console.log(`\nContext break: ${contextBreak.type} for ${contextBreak.duration} minutes`);
|
|
191
|
-
await this.simulateContextBreak(contextBreak, sessionId);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
run.success = true;
|
|
195
|
-
}
|
|
196
|
-
catch (error) {
|
|
197
|
-
console.error(`Scenario failed: ${error.message}`);
|
|
198
|
-
run.errors.push(error.message);
|
|
199
|
-
this.collector.trackError(sessionId, error);
|
|
200
|
-
}
|
|
201
|
-
// Collect final metrics
|
|
202
|
-
run.endTime = new Date();
|
|
203
|
-
run.metrics = await this.collector.endSession(sessionId);
|
|
204
|
-
// Save run results
|
|
205
|
-
this.runs.push(run);
|
|
206
|
-
await this.saveRun(run);
|
|
207
|
-
return run;
|
|
208
|
-
}
|
|
209
|
-
async executeStep(step, sessionId, run) {
|
|
210
|
-
const startTime = Date.now();
|
|
211
|
-
// Track tool call
|
|
212
|
-
this.collector.trackToolCall(sessionId, 'execute_step');
|
|
213
|
-
// If step requires context and we're testing with StackMemory
|
|
214
|
-
if (step.requiresContext && this.stackMemoryEnabled) {
|
|
215
|
-
const contextTime = await this.collector.measureContextReestablishment(sessionId);
|
|
216
|
-
console.log(` Context retrieved in ${(contextTime / 1000).toFixed(2)}s`);
|
|
217
|
-
}
|
|
218
|
-
// Simulate step execution with command if provided
|
|
219
|
-
if (step.command) {
|
|
220
|
-
try {
|
|
221
|
-
const output = await this.executeCommand(step.command);
|
|
222
|
-
// Record tool call
|
|
223
|
-
run.recordings.push({
|
|
224
|
-
timestamp: new Date(),
|
|
225
|
-
tool: 'command',
|
|
226
|
-
parameters: { command: step.command },
|
|
227
|
-
result: output,
|
|
228
|
-
duration: Date.now() - startTime
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
catch (error) {
|
|
232
|
-
this.collector.trackError(sessionId, error);
|
|
233
|
-
throw error;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
// Simulate work being done
|
|
238
|
-
await this.simulateWork(2000 + Math.random() * 3000);
|
|
239
|
-
}
|
|
240
|
-
// Randomly simulate decisions and frame creation
|
|
241
|
-
if (Math.random() > 0.5) {
|
|
242
|
-
this.collector.trackFrameCreation(sessionId, `frame-${Date.now()}`);
|
|
243
|
-
}
|
|
244
|
-
if (Math.random() > 0.7) {
|
|
245
|
-
this.collector.trackDecision(sessionId, `Decision for ${step.action}`);
|
|
246
|
-
}
|
|
247
|
-
console.log(` Step completed in ${((Date.now() - startTime) / 1000).toFixed(2)}s`);
|
|
248
|
-
}
|
|
249
|
-
async simulateContextBreak(contextBreak, sessionId) {
|
|
250
|
-
// Simulate time passing
|
|
251
|
-
console.log(` Simulating ${contextBreak.duration} minute break...`);
|
|
252
|
-
if (contextBreak.type === 'session_end' && this.stackMemoryEnabled) {
|
|
253
|
-
// Simulate session end with StackMemory
|
|
254
|
-
this.collector.trackFrameClosure(sessionId, 'session-frame', true);
|
|
255
|
-
}
|
|
256
|
-
// In real testing, we would actually wait or simulate the time passing
|
|
257
|
-
await this.simulateWork(1000);
|
|
258
|
-
// After break, measure context reestablishment
|
|
259
|
-
if (this.stackMemoryEnabled) {
|
|
260
|
-
const reestablishTime = await this.collector.measureContextReestablishment(sessionId);
|
|
261
|
-
console.log(` Context reestablished in ${(reestablishTime / 1000).toFixed(2)}s`);
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
// Without StackMemory, simulate manual context reestablishment
|
|
265
|
-
console.log(` Manual context reestablishment required (est. 5 minutes)`);
|
|
266
|
-
this.collector.trackRework(sessionId);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
simulateWork(ms) {
|
|
270
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
271
|
-
}
|
|
272
|
-
async runAllScenarios() {
|
|
273
|
-
console.log('='.repeat(60));
|
|
274
|
-
console.log('Starting A/B Test Suite');
|
|
275
|
-
console.log('='.repeat(60));
|
|
276
|
-
for (const scenario of this.scenarios.values()) {
|
|
277
|
-
// Run without StackMemory
|
|
278
|
-
await this.runScenario(scenario.id, 'without_stackmemory');
|
|
279
|
-
// Run with StackMemory
|
|
280
|
-
await this.runScenario(scenario.id, 'with_stackmemory');
|
|
281
|
-
}
|
|
282
|
-
await this.generateComparison();
|
|
283
|
-
}
|
|
284
|
-
async generateComparison() {
|
|
285
|
-
const withStackMemory = this.runs.filter(r => r.variant === 'with_stackmemory');
|
|
286
|
-
const withoutStackMemory = this.runs.filter(r => r.variant === 'without_stackmemory');
|
|
287
|
-
console.log('\n' + '='.repeat(60));
|
|
288
|
-
console.log('A/B Test Results Summary');
|
|
289
|
-
console.log('='.repeat(60));
|
|
290
|
-
for (const scenario of this.scenarios.values()) {
|
|
291
|
-
const withRun = withStackMemory.find(r => r.scenario.id === scenario.id);
|
|
292
|
-
const withoutRun = withoutStackMemory.find(r => r.scenario.id === scenario.id);
|
|
293
|
-
if (withRun && withoutRun) {
|
|
294
|
-
console.log(`\n${scenario.name}:`);
|
|
295
|
-
console.log(` Without StackMemory: ${((withoutRun.metrics.completionTime || 0) / 1000 / 60).toFixed(2)} min`);
|
|
296
|
-
console.log(` With StackMemory: ${((withRun.metrics.completionTime || 0) / 1000 / 60).toFixed(2)} min`);
|
|
297
|
-
const improvement = ((withoutRun.metrics.completionTime - withRun.metrics.completionTime) /
|
|
298
|
-
withoutRun.metrics.completionTime) * 100;
|
|
299
|
-
console.log(` Improvement: ${improvement.toFixed(1)}%`);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
// Generate detailed report
|
|
303
|
-
await this.collector.generateReport('./test-results/ab-test-report.md');
|
|
304
|
-
}
|
|
305
|
-
async saveRun(run) {
|
|
306
|
-
const outputDir = './test-results/runs';
|
|
307
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
308
|
-
const filename = path.join(outputDir, `${run.id}.json`);
|
|
309
|
-
await fs.writeFile(filename, JSON.stringify(run, null, 2));
|
|
310
|
-
console.log(`Run saved to: ${filename}`);
|
|
311
|
-
}
|
|
312
|
-
async runSpecificScenario(scenarioId) {
|
|
313
|
-
if (!this.scenarios.has(scenarioId)) {
|
|
314
|
-
console.error(`Scenario '${scenarioId}' not found`);
|
|
315
|
-
console.log('Available scenarios:');
|
|
316
|
-
for (const [id, scenario] of this.scenarios) {
|
|
317
|
-
console.log(` - ${id}: ${scenario.name}`);
|
|
318
|
-
}
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
// Run both variants
|
|
322
|
-
await this.runScenario(scenarioId, 'without_stackmemory');
|
|
323
|
-
await this.runScenario(scenarioId, 'with_stackmemory');
|
|
324
|
-
await this.generateComparison();
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
// CLI interface
|
|
328
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
329
|
-
const runner = new ABTestRunner();
|
|
330
|
-
async function main() {
|
|
331
|
-
await runner.initialize();
|
|
332
|
-
const command = process.argv[2];
|
|
333
|
-
const scenarioId = process.argv[3];
|
|
334
|
-
switch (command) {
|
|
335
|
-
case 'all':
|
|
336
|
-
await runner.runAllScenarios();
|
|
337
|
-
break;
|
|
338
|
-
case 'scenario':
|
|
339
|
-
if (!scenarioId) {
|
|
340
|
-
console.error('Please specify a scenario ID');
|
|
341
|
-
process.exit(1);
|
|
342
|
-
}
|
|
343
|
-
await runner.runSpecificScenario(scenarioId);
|
|
344
|
-
break;
|
|
345
|
-
case 'list':
|
|
346
|
-
console.log('Available scenarios:');
|
|
347
|
-
console.log(' - multi_session_feature: E-commerce checkout flow');
|
|
348
|
-
console.log(' - complex_debugging: Performance issue in production');
|
|
349
|
-
console.log(' - large_refactoring: Migrate authentication system');
|
|
350
|
-
console.log(' - rapid_bug_fixes: Fix 5 related bugs');
|
|
351
|
-
break;
|
|
352
|
-
default:
|
|
353
|
-
console.log('Usage: ab-test-runner.ts [all|scenario|list] [scenario-id]');
|
|
354
|
-
console.log('');
|
|
355
|
-
console.log('Commands:');
|
|
356
|
-
console.log(' all - Run all test scenarios');
|
|
357
|
-
console.log(' scenario - Run a specific scenario');
|
|
358
|
-
console.log(' list - List available scenarios');
|
|
359
|
-
}
|
|
360
|
-
process.exit(0);
|
|
361
|
-
}
|
|
362
|
-
main().catch(console.error);
|
|
363
|
-
}
|