@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.
Files changed (33) hide show
  1. package/dist/cli/commands/handoff.js +215 -59
  2. package/dist/cli/commands/handoff.js.map +2 -2
  3. package/dist/cli/index.js +8 -2
  4. package/dist/cli/index.js.map +2 -2
  5. package/dist/integrations/claude-code/lifecycle-hooks.js +3 -3
  6. package/dist/integrations/claude-code/lifecycle-hooks.js.map +1 -1
  7. package/package.json +1 -1
  8. package/scripts/auto-handoff.sh +1 -1
  9. package/scripts/claude-sm-autostart.js +174 -132
  10. package/scripts/setup-claude-integration.js +14 -10
  11. package/scripts/stackmemory-auto-handoff.sh +3 -3
  12. package/scripts/test-session-handoff.sh +2 -2
  13. package/dist/core/context/compaction-handler.js +0 -330
  14. package/dist/core/context/compaction-handler.js.map +0 -7
  15. package/dist/core/context/context-bridge.js +0 -238
  16. package/dist/core/context/context-bridge.js.map +0 -7
  17. package/scripts/testing/scripts/testing/ab-test-runner.js +0 -363
  18. package/scripts/testing/scripts/testing/collect-metrics.js +0 -292
  19. package/scripts/testing/src/core/context/context-bridge.js +0 -253
  20. package/scripts/testing/src/core/context/frame-manager.js +0 -746
  21. package/scripts/testing/src/core/context/shared-context-layer.js +0 -437
  22. package/scripts/testing/src/core/database/database-adapter.js +0 -54
  23. package/scripts/testing/src/core/errors/index.js +0 -291
  24. package/scripts/testing/src/core/errors/recovery.js +0 -268
  25. package/scripts/testing/src/core/monitoring/logger.js +0 -145
  26. package/scripts/testing/src/core/retrieval/context-retriever.js +0 -516
  27. package/scripts/testing/src/core/session/index.js +0 -1
  28. package/scripts/testing/src/core/session/session-manager.js +0 -323
  29. package/scripts/testing/src/core/trace/cli-trace-wrapper.js +0 -140
  30. package/scripts/testing/src/core/trace/db-trace-wrapper.js +0 -251
  31. package/scripts/testing/src/core/trace/debug-trace.js +0 -398
  32. package/scripts/testing/src/core/trace/index.js +0 -120
  33. package/scripts/testing/src/core/trace/linear-api-wrapper.js +0 -204
@@ -32,10 +32,10 @@ capture_handoff() {
32
32
  echo -e "${YELLOW}📸 Capturing handoff context...${NC}"
33
33
  log "Capturing handoff: reason=$reason, exit_code=$exit_code"
34
34
 
35
- # Run stackmemory handoff command
35
+ # Run stackmemory capture command
36
36
  if command -v "$STACKMEMORY_BIN" &> /dev/null; then
37
37
  # Capture the handoff
38
- "$STACKMEMORY_BIN" handoff --no-commit 2>&1 | tee -a "$LOG_FILE"
38
+ "$STACKMEMORY_BIN" capture --no-commit 2>&1 | tee -a "$LOG_FILE"
39
39
 
40
40
  # Save additional metadata
41
41
  local metadata_file="${HANDOFF_DIR}/last-handoff-meta.json"
@@ -75,7 +75,7 @@ EOF
75
75
  done
76
76
  fi
77
77
 
78
- echo -e "${GREEN}✨ Run 'stackmemory handoff restore' in your next session${NC}"
78
+ echo -e "${GREEN}✨ Run 'stackmemory restore' in your next session${NC}"
79
79
  else
80
80
  echo -e "${RED}❌ StackMemory not found${NC}"
81
81
  log "ERROR: StackMemory binary not found"
@@ -73,7 +73,7 @@ echo
73
73
 
74
74
  # 3. Generate handoff
75
75
  echo -e "${BLUE}3. Generating handoff summary...${NC}"
76
- stackmemory handoff > /tmp/handoff-test.md
76
+ stackmemory capture > /tmp/handoff-test.md
77
77
  echo -e "${GREEN}✓ Handoff generated${NC}"
78
78
  echo " Saved to: /tmp/handoff-test.md"
79
79
  echo
@@ -159,7 +159,7 @@ echo " • Last handoff: ./.stackmemory/last-handoff.md"
159
159
  echo " • Session data: ~/.stackmemory/sessions/"
160
160
  echo
161
161
  echo -e "${YELLOW}💡 To use in new Claude session:${NC}"
162
- echo " 1. Run: stackmemory handoff"
162
+ echo " 1. Run: stackmemory capture"
163
163
  echo " 2. Copy the handoff summary"
164
164
  echo " 3. Paste at start of new session"
165
165
  echo " 4. Context will be automatically loaded"
@@ -1,330 +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 "../monitoring/logger.js";
6
- class CompactionHandler {
7
- frameManager;
8
- metrics;
9
- tokenAccumulator = 0;
10
- preservedAnchors = /* @__PURE__ */ new Map();
11
- constructor(frameManager) {
12
- this.frameManager = frameManager;
13
- this.metrics = {
14
- estimatedTokens: 0,
15
- warningThreshold: 15e4,
16
- // 150K tokens
17
- criticalThreshold: 17e4,
18
- // 170K tokens
19
- anchorsPreserved: 0
20
- };
21
- }
22
- /**
23
- * Track token usage from a message
24
- */
25
- trackTokens(content) {
26
- const estimatedTokens = Math.ceil(content.length / 4);
27
- this.tokenAccumulator += estimatedTokens;
28
- this.metrics.estimatedTokens += estimatedTokens;
29
- if (this.isApproachingCompaction()) {
30
- this.preserveCriticalContext();
31
- }
32
- }
33
- /**
34
- * Check if approaching compaction threshold
35
- */
36
- isApproachingCompaction() {
37
- return this.metrics.estimatedTokens >= this.metrics.warningThreshold;
38
- }
39
- /**
40
- * Check if past critical threshold
41
- */
42
- isPastCriticalThreshold() {
43
- return this.metrics.estimatedTokens >= this.metrics.criticalThreshold;
44
- }
45
- /**
46
- * Detect if compaction likely occurred
47
- */
48
- detectCompactionEvent(content) {
49
- const compactionIndicators = [
50
- "earlier in this conversation",
51
- "previously discussed",
52
- "as mentioned before",
53
- "summarized for brevity",
54
- "[conversation compressed]",
55
- "[context truncated]"
56
- ];
57
- const lowerContent = content.toLowerCase();
58
- return compactionIndicators.some(
59
- (indicator) => lowerContent.includes(indicator)
60
- );
61
- }
62
- /**
63
- * Preserve critical context before compaction
64
- */
65
- async preserveCriticalContext() {
66
- try {
67
- const currentFrameId = this.frameManager.getCurrentFrameId();
68
- if (!currentFrameId) {
69
- logger.warn("No active frame to preserve context from");
70
- return;
71
- }
72
- const events = this.frameManager.getFrameEvents(currentFrameId);
73
- const toolCalls = this.extractToolCalls(events);
74
- const fileOps = this.extractFileOperations(events);
75
- const decisions = this.extractDecisions(events);
76
- const errorPatterns = this.extractErrorPatterns(events);
77
- const anchor = {
78
- anchor_id: `compact_${Date.now()}`,
79
- type: "COMPACTION_PRESERVE",
80
- priority: 10,
81
- content: {
82
- tool_calls: toolCalls,
83
- file_operations: fileOps,
84
- decisions,
85
- error_resolutions: errorPatterns
86
- },
87
- created_at: Date.now(),
88
- token_estimate: this.metrics.estimatedTokens
89
- };
90
- this.frameManager.addAnchor(
91
- "CONSTRAINT",
92
- // Using CONSTRAINT type for now
93
- JSON.stringify(anchor),
94
- 10,
95
- {
96
- compaction_preserve: true,
97
- token_count: this.metrics.estimatedTokens
98
- },
99
- currentFrameId
100
- );
101
- this.preservedAnchors.set(anchor.anchor_id, anchor);
102
- this.metrics.anchorsPreserved++;
103
- logger.info(
104
- `Preserved critical context at ${this.metrics.estimatedTokens} tokens`
105
- );
106
- } catch (error) {
107
- logger.error(
108
- "Failed to preserve critical context:",
109
- error instanceof Error ? error : void 0
110
- );
111
- }
112
- }
113
- /**
114
- * Extract tool calls from events
115
- */
116
- extractToolCalls(events) {
117
- const toolCalls = [];
118
- const toolEvents = events.filter((e) => e.event_type === "tool_call");
119
- for (const event of toolEvents) {
120
- const resultEvent = events.find(
121
- (e) => e.event_type === "tool_result" && e.seq > event.seq && e.payload.tool_name === event.payload.tool_name
122
- );
123
- toolCalls.push({
124
- tool: event.payload.tool_name || "unknown",
125
- timestamp: event.ts,
126
- key_inputs: this.extractKeyInputs(event.payload),
127
- key_outputs: resultEvent ? this.extractKeyOutputs(resultEvent.payload) : {},
128
- files_affected: this.extractAffectedFiles(
129
- event.payload,
130
- resultEvent?.payload
131
- ),
132
- success: resultEvent ? !resultEvent.payload.error : false,
133
- error: resultEvent?.payload.error
134
- });
135
- }
136
- return toolCalls;
137
- }
138
- /**
139
- * Extract key inputs from tool call
140
- */
141
- extractKeyInputs(payload) {
142
- const keys = [
143
- "file_path",
144
- "command",
145
- "query",
146
- "path",
147
- "pattern",
148
- "content"
149
- ];
150
- const result = {};
151
- for (const key of keys) {
152
- if (payload.arguments?.[key]) {
153
- result[key] = payload.arguments[key];
154
- }
155
- }
156
- return result;
157
- }
158
- /**
159
- * Extract key outputs from tool result
160
- */
161
- extractKeyOutputs(payload) {
162
- return {
163
- success: !payload.error,
164
- error: payload.error,
165
- result_type: payload.result_type,
166
- files_created: payload.files_created,
167
- files_modified: payload.files_modified
168
- };
169
- }
170
- /**
171
- * Extract affected files from tool events
172
- */
173
- extractAffectedFiles(callPayload, resultPayload) {
174
- const files = /* @__PURE__ */ new Set();
175
- if (callPayload?.arguments?.file_path) {
176
- files.add(callPayload.arguments.file_path);
177
- }
178
- if (callPayload?.arguments?.path) {
179
- files.add(callPayload.arguments.path);
180
- }
181
- if (resultPayload?.files_created) {
182
- resultPayload.files_created.forEach((f) => files.add(f));
183
- }
184
- if (resultPayload?.files_modified) {
185
- resultPayload.files_modified.forEach((f) => files.add(f));
186
- }
187
- return Array.from(files);
188
- }
189
- /**
190
- * Extract file operations from events
191
- */
192
- extractFileOperations(events) {
193
- const fileOps = [];
194
- const fileTools = ["Read", "Write", "Edit", "MultiEdit", "Delete"];
195
- const toolEvents = events.filter(
196
- (e) => e.event_type === "tool_call" && fileTools.includes(e.payload.tool_name)
197
- );
198
- for (const event of toolEvents) {
199
- const operation = this.mapToolToOperation(event.payload.tool_name);
200
- const path = event.payload.arguments?.file_path || event.payload.arguments?.path || "unknown";
201
- fileOps.push({
202
- type: operation,
203
- path,
204
- timestamp: event.ts,
205
- success: true,
206
- // Will be updated from result
207
- error: void 0
208
- });
209
- }
210
- return fileOps;
211
- }
212
- /**
213
- * Map tool name to file operation type
214
- */
215
- mapToolToOperation(toolName) {
216
- const mapping = {
217
- Read: "read",
218
- Write: "write",
219
- Edit: "edit",
220
- MultiEdit: "edit",
221
- Delete: "delete"
222
- };
223
- return mapping[toolName] || "read";
224
- }
225
- /**
226
- * Extract decisions from events
227
- */
228
- extractDecisions(events) {
229
- const decisions = [];
230
- const decisionEvents = events.filter((e) => e.event_type === "decision");
231
- for (const event of decisionEvents) {
232
- if (event.payload.text) {
233
- decisions.push(event.payload.text);
234
- }
235
- }
236
- return decisions;
237
- }
238
- /**
239
- * Extract error patterns and resolutions
240
- */
241
- extractErrorPatterns(events) {
242
- const patterns = [];
243
- const errorEvents = events.filter(
244
- (e) => e.event_type === "tool_result" && e.payload.error
245
- );
246
- for (const errorEvent of errorEvents) {
247
- const subsequentTools = events.filter((e) => e.event_type === "tool_call" && e.seq > errorEvent.seq).slice(0, 3);
248
- if (subsequentTools.length > 0) {
249
- patterns.push({
250
- error: errorEvent.payload.error,
251
- resolution: `Attempted resolution with ${subsequentTools.map((t) => t.payload.tool_name).join(", ")}`,
252
- tool_sequence: subsequentTools.map((t) => t.payload.tool_name),
253
- timestamp: errorEvent.ts
254
- });
255
- }
256
- }
257
- return patterns;
258
- }
259
- /**
260
- * Restore context after compaction detected
261
- */
262
- async restoreContext() {
263
- if (this.preservedAnchors.size === 0) {
264
- logger.warn("No preserved anchors to restore from");
265
- return;
266
- }
267
- const anchors = Array.from(this.preservedAnchors.values());
268
- anchors.sort((a, b) => b.created_at - a.created_at);
269
- const latestAnchor = anchors[0];
270
- const restorationFrame = this.frameManager.createFrame({
271
- type: "review",
272
- name: "Context Restoration After Compaction",
273
- inputs: { reason: "autocompaction_detected" }
274
- });
275
- this.frameManager.addAnchor(
276
- "FACT",
277
- `Context restored from token position ${latestAnchor.token_estimate}`,
278
- 10,
279
- { restoration: true },
280
- restorationFrame
281
- );
282
- const toolSequence = latestAnchor.content.tool_calls.map((t) => t.tool).join(" \u2192 ");
283
- this.frameManager.addAnchor(
284
- "FACT",
285
- `Tool sequence: ${toolSequence}`,
286
- 9,
287
- {},
288
- restorationFrame
289
- );
290
- const files = /* @__PURE__ */ new Set();
291
- latestAnchor.content.file_operations.forEach((op) => files.add(op.path));
292
- if (files.size > 0) {
293
- this.frameManager.addAnchor(
294
- "FACT",
295
- `Files touched: ${Array.from(files).join(", ")}`,
296
- 8,
297
- {},
298
- restorationFrame
299
- );
300
- }
301
- for (const decision of latestAnchor.content.decisions) {
302
- this.frameManager.addAnchor(
303
- "DECISION",
304
- decision,
305
- 7,
306
- {},
307
- restorationFrame
308
- );
309
- }
310
- logger.info("Context restored after compaction detection");
311
- }
312
- /**
313
- * Get current metrics
314
- */
315
- getMetrics() {
316
- return { ...this.metrics };
317
- }
318
- /**
319
- * Reset token counter (e.g., at session start)
320
- */
321
- resetTokenCount() {
322
- this.metrics.estimatedTokens = 0;
323
- this.tokenAccumulator = 0;
324
- this.metrics.lastCompactionAt = void 0;
325
- }
326
- }
327
- export {
328
- CompactionHandler
329
- };
330
- //# sourceMappingURL=compaction-handler.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/core/context/compaction-handler.ts"],
4
- "sourcesContent": ["/**\n * Compaction Handler for Claude Code Autocompaction\n * Preserves critical context across token limit boundaries\n */\n\nimport { FrameManager } from './index.js';\nimport type { Anchor, Event } from './index.js';\nimport { logger } from '../monitoring/logger.js';\n\nexport interface CompactionMetrics {\n estimatedTokens: number;\n warningThreshold: number;\n criticalThreshold: number;\n lastCompactionAt?: number;\n anchorsPreserved: number;\n}\n\nexport interface ToolCallSummary {\n tool: string;\n timestamp: number;\n key_inputs: Record<string, any>;\n key_outputs: Record<string, any>;\n files_affected: string[];\n success: boolean;\n error?: string;\n}\n\nexport interface CriticalContextAnchor {\n anchor_id: string;\n type: 'COMPACTION_PRESERVE';\n priority: 10; // Highest priority\n content: {\n tool_calls: ToolCallSummary[];\n decisions: string[];\n file_operations: FileOperation[];\n error_resolutions: ErrorPattern[];\n };\n created_at: number;\n token_estimate: number;\n}\n\nexport interface FileOperation {\n type: 'read' | 'write' | 'edit' | 'delete' | 'create';\n path: string;\n timestamp: number;\n success: boolean;\n error?: string;\n}\n\nexport interface ErrorPattern {\n error: string;\n resolution: string;\n tool_sequence: string[];\n timestamp: number;\n}\n\nexport class CompactionHandler {\n private frameManager: FrameManager;\n private metrics: CompactionMetrics;\n private tokenAccumulator: number = 0;\n private preservedAnchors: Map<string, CriticalContextAnchor> = new Map();\n\n constructor(frameManager: FrameManager) {\n this.frameManager = frameManager;\n this.metrics = {\n estimatedTokens: 0,\n warningThreshold: 150000, // 150K tokens\n criticalThreshold: 170000, // 170K tokens\n anchorsPreserved: 0,\n };\n }\n\n /**\n * Track token usage from a message\n */\n trackTokens(content: string): void {\n // Rough estimation: 1 token \u2248 4 characters\n const estimatedTokens = Math.ceil(content.length / 4);\n this.tokenAccumulator += estimatedTokens;\n this.metrics.estimatedTokens += estimatedTokens;\n\n // Check thresholds\n if (this.isApproachingCompaction()) {\n this.preserveCriticalContext();\n }\n }\n\n /**\n * Check if approaching compaction threshold\n */\n isApproachingCompaction(): boolean {\n return this.metrics.estimatedTokens >= this.metrics.warningThreshold;\n }\n\n /**\n * Check if past critical threshold\n */\n isPastCriticalThreshold(): boolean {\n return this.metrics.estimatedTokens >= this.metrics.criticalThreshold;\n }\n\n /**\n * Detect if compaction likely occurred\n */\n detectCompactionEvent(content: string): boolean {\n const compactionIndicators = [\n 'earlier in this conversation',\n 'previously discussed',\n 'as mentioned before',\n 'summarized for brevity',\n '[conversation compressed]',\n '[context truncated]',\n ];\n\n const lowerContent = content.toLowerCase();\n return compactionIndicators.some((indicator) =>\n lowerContent.includes(indicator)\n );\n }\n\n /**\n * Preserve critical context before compaction\n */\n async preserveCriticalContext(): Promise<void> {\n try {\n const currentFrameId = this.frameManager.getCurrentFrameId();\n if (!currentFrameId) {\n logger.warn('No active frame to preserve context from');\n return;\n }\n\n // Get events from current frame\n const events = this.frameManager.getFrameEvents(currentFrameId);\n\n // Extract critical information\n const toolCalls = this.extractToolCalls(events);\n const fileOps = this.extractFileOperations(events);\n const decisions = this.extractDecisions(events);\n const errorPatterns = this.extractErrorPatterns(events);\n\n // Create preservation anchor\n const anchor: CriticalContextAnchor = {\n anchor_id: `compact_${Date.now()}`,\n type: 'COMPACTION_PRESERVE',\n priority: 10,\n content: {\n tool_calls: toolCalls,\n file_operations: fileOps,\n decisions: decisions,\n error_resolutions: errorPatterns,\n },\n created_at: Date.now(),\n token_estimate: this.metrics.estimatedTokens,\n };\n\n // Store in frame manager as high-priority anchor\n this.frameManager.addAnchor(\n 'CONSTRAINT' as any, // Using CONSTRAINT type for now\n JSON.stringify(anchor),\n 10,\n {\n compaction_preserve: true,\n token_count: this.metrics.estimatedTokens,\n },\n currentFrameId\n );\n\n // Store locally for quick access\n this.preservedAnchors.set(anchor.anchor_id, anchor);\n this.metrics.anchorsPreserved++;\n\n logger.info(\n `Preserved critical context at ${this.metrics.estimatedTokens} tokens`\n );\n } catch (error: unknown) {\n logger.error(\n 'Failed to preserve critical context:',\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Extract tool calls from events\n */\n private extractToolCalls(events: Event[]): ToolCallSummary[] {\n const toolCalls: ToolCallSummary[] = [];\n const toolEvents = events.filter((e) => e.event_type === 'tool_call');\n\n for (const event of toolEvents) {\n const resultEvent = events.find(\n (e) =>\n e.event_type === 'tool_result' &&\n e.seq > event.seq &&\n e.payload.tool_name === event.payload.tool_name\n );\n\n toolCalls.push({\n tool: event.payload.tool_name || 'unknown',\n timestamp: event.ts,\n key_inputs: this.extractKeyInputs(event.payload),\n key_outputs: resultEvent\n ? this.extractKeyOutputs(resultEvent.payload)\n : {},\n files_affected: this.extractAffectedFiles(\n event.payload,\n resultEvent?.payload\n ),\n success: resultEvent ? !resultEvent.payload.error : false,\n error: resultEvent?.payload.error,\n });\n }\n\n return toolCalls;\n }\n\n /**\n * Extract key inputs from tool call\n */\n private extractKeyInputs(payload: any): Record<string, any> {\n const keys = [\n 'file_path',\n 'command',\n 'query',\n 'path',\n 'pattern',\n 'content',\n ];\n const result: Record<string, any> = {};\n\n for (const key of keys) {\n if (payload.arguments?.[key]) {\n result[key] = payload.arguments[key];\n }\n }\n\n return result;\n }\n\n /**\n * Extract key outputs from tool result\n */\n private extractKeyOutputs(payload: any): Record<string, any> {\n return {\n success: !payload.error,\n error: payload.error,\n result_type: payload.result_type,\n files_created: payload.files_created,\n files_modified: payload.files_modified,\n };\n }\n\n /**\n * Extract affected files from tool events\n */\n private extractAffectedFiles(callPayload: any, resultPayload: any): string[] {\n const files = new Set<string>();\n\n // From tool call\n if (callPayload?.arguments?.file_path) {\n files.add(callPayload.arguments.file_path);\n }\n if (callPayload?.arguments?.path) {\n files.add(callPayload.arguments.path);\n }\n\n // From tool result\n if (resultPayload?.files_created) {\n resultPayload.files_created.forEach((f: string) => files.add(f));\n }\n if (resultPayload?.files_modified) {\n resultPayload.files_modified.forEach((f: string) => files.add(f));\n }\n\n return Array.from(files);\n }\n\n /**\n * Extract file operations from events\n */\n private extractFileOperations(events: Event[]): FileOperation[] {\n const fileOps: FileOperation[] = [];\n const fileTools = ['Read', 'Write', 'Edit', 'MultiEdit', 'Delete'];\n\n const toolEvents = events.filter(\n (e) =>\n e.event_type === 'tool_call' && fileTools.includes(e.payload.tool_name)\n );\n\n for (const event of toolEvents) {\n const operation = this.mapToolToOperation(event.payload.tool_name);\n const path =\n event.payload.arguments?.file_path ||\n event.payload.arguments?.path ||\n 'unknown';\n\n fileOps.push({\n type: operation,\n path: path,\n timestamp: event.ts,\n success: true, // Will be updated from result\n error: undefined,\n });\n }\n\n return fileOps;\n }\n\n /**\n * Map tool name to file operation type\n */\n private mapToolToOperation(toolName: string): FileOperation['type'] {\n const mapping: Record<string, FileOperation['type']> = {\n Read: 'read',\n Write: 'write',\n Edit: 'edit',\n MultiEdit: 'edit',\n Delete: 'delete',\n };\n\n return mapping[toolName] || 'read';\n }\n\n /**\n * Extract decisions from events\n */\n private extractDecisions(events: Event[]): string[] {\n const decisions: string[] = [];\n\n const decisionEvents = events.filter((e) => e.event_type === 'decision');\n for (const event of decisionEvents) {\n if (event.payload.text) {\n decisions.push(event.payload.text);\n }\n }\n\n return decisions;\n }\n\n /**\n * Extract error patterns and resolutions\n */\n private extractErrorPatterns(events: Event[]): ErrorPattern[] {\n const patterns: ErrorPattern[] = [];\n\n // Find tool results with errors\n const errorEvents = events.filter(\n (e) => e.event_type === 'tool_result' && e.payload.error\n );\n\n for (const errorEvent of errorEvents) {\n // Look for subsequent successful tool calls that might be resolutions\n const subsequentTools = events\n .filter((e) => e.event_type === 'tool_call' && e.seq > errorEvent.seq)\n .slice(0, 3); // Next 3 tools might be resolution attempts\n\n if (subsequentTools.length > 0) {\n patterns.push({\n error: errorEvent.payload.error,\n resolution: `Attempted resolution with ${subsequentTools.map((t) => t.payload.tool_name).join(', ')}`,\n tool_sequence: subsequentTools.map((t) => t.payload.tool_name),\n timestamp: errorEvent.ts,\n });\n }\n }\n\n return patterns;\n }\n\n /**\n * Restore context after compaction detected\n */\n async restoreContext(): Promise<void> {\n if (this.preservedAnchors.size === 0) {\n logger.warn('No preserved anchors to restore from');\n return;\n }\n\n // Get the most recent anchor\n const anchors = Array.from(this.preservedAnchors.values());\n anchors.sort((a, b) => b.created_at - a.created_at);\n const latestAnchor = anchors[0];\n\n // Create restoration frame\n const restorationFrame = this.frameManager.createFrame({\n type: 'review',\n name: 'Context Restoration After Compaction',\n inputs: { reason: 'autocompaction_detected' },\n });\n\n // Add restoration anchor\n this.frameManager.addAnchor(\n 'FACT',\n `Context restored from token position ${latestAnchor.token_estimate}`,\n 10,\n { restoration: true },\n restorationFrame\n );\n\n // Add tool sequence summary\n const toolSequence = latestAnchor.content.tool_calls\n .map((t) => t.tool)\n .join(' \u2192 ');\n this.frameManager.addAnchor(\n 'FACT',\n `Tool sequence: ${toolSequence}`,\n 9,\n {},\n restorationFrame\n );\n\n // Add file operations summary\n const files = new Set<string>();\n latestAnchor.content.file_operations.forEach((op) => files.add(op.path));\n if (files.size > 0) {\n this.frameManager.addAnchor(\n 'FACT',\n `Files touched: ${Array.from(files).join(', ')}`,\n 8,\n {},\n restorationFrame\n );\n }\n\n // Add decisions\n for (const decision of latestAnchor.content.decisions) {\n this.frameManager.addAnchor(\n 'DECISION',\n decision,\n 7,\n {},\n restorationFrame\n );\n }\n\n logger.info('Context restored after compaction detection');\n }\n\n /**\n * Get current metrics\n */\n getMetrics(): CompactionMetrics {\n return { ...this.metrics };\n }\n\n /**\n * Reset token counter (e.g., at session start)\n */\n resetTokenCount(): void {\n this.metrics.estimatedTokens = 0;\n this.tokenAccumulator = 0;\n this.metrics.lastCompactionAt = undefined;\n }\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,238 +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 { sharedContextLayer } from "./shared-context-layer.js";
6
- import { sessionManager } from "../session/session-manager.js";
7
- import { logger } from "../monitoring/logger.js";
8
- class ContextBridge {
9
- static instance;
10
- frameManager = null;
11
- syncTimer = null;
12
- lastSyncTime = 0;
13
- options = {
14
- autoSync: true,
15
- syncInterval: 6e4,
16
- // 1 minute
17
- minFrameScore: 0.5,
18
- // Include frames with score above 0.5
19
- importantTags: ["decision", "error", "milestone", "learning"]
20
- };
21
- constructor() {
22
- }
23
- static getInstance() {
24
- if (!ContextBridge.instance) {
25
- ContextBridge.instance = new ContextBridge();
26
- }
27
- return ContextBridge.instance;
28
- }
29
- /**
30
- * Initialize the bridge with a frame manager
31
- */
32
- async initialize(frameManager, options) {
33
- this.frameManager = frameManager;
34
- this.options = { ...this.options, ...options };
35
- await this.loadSharedContext();
36
- if (this.options.autoSync) {
37
- this.startAutoSync();
38
- }
39
- logger.info("Context bridge initialized", {
40
- autoSync: this.options.autoSync,
41
- syncInterval: this.options.syncInterval
42
- });
43
- }
44
- /**
45
- * Load relevant shared context into current session
46
- */
47
- async loadSharedContext() {
48
- try {
49
- const session = sessionManager.getCurrentSession();
50
- if (!session) return;
51
- const discovery = await sharedContextLayer.autoDiscoverContext();
52
- if (!discovery.hasSharedContext) {
53
- logger.info("No shared context available to load");
54
- return;
55
- }
56
- if (discovery.recentPatterns.length > 0) {
57
- logger.info("Loaded recent patterns from shared context", {
58
- patternCount: discovery.recentPatterns.length
59
- });
60
- }
61
- if (discovery.lastDecisions.length > 0) {
62
- logger.info("Loaded recent decisions from shared context", {
63
- decisionCount: discovery.lastDecisions.length
64
- });
65
- }
66
- if (discovery.suggestedFrames.length > 0) {
67
- const metadata = {
68
- suggestedFrames: discovery.suggestedFrames,
69
- loadedAt: Date.now()
70
- };
71
- if (this.frameManager) {
72
- await this.frameManager.addContext(
73
- "shared-context-suggestions",
74
- metadata
75
- );
76
- }
77
- logger.info("Loaded suggested frames from shared context", {
78
- frameCount: discovery.suggestedFrames.length
79
- });
80
- }
81
- } catch (error) {
82
- logger.error("Failed to load shared context", error);
83
- }
84
- }
85
- /**
86
- * Sync current session's important frames to shared context
87
- */
88
- async syncToSharedContext() {
89
- try {
90
- if (!this.frameManager) return;
91
- const session = sessionManager.getCurrentSession();
92
- if (!session) return;
93
- const activeFrames = this.frameManager.getActiveFramePath().filter(Boolean);
94
- const recentFrames = await this.frameManager.getRecentFrames(100);
95
- const allFrames = [...activeFrames, ...recentFrames].filter(Boolean);
96
- const importantFrames = this.filterImportantFrames(allFrames);
97
- if (importantFrames.length === 0) {
98
- logger.debug("No important frames to sync");
99
- return;
100
- }
101
- await sharedContextLayer.addToSharedContext(importantFrames, {
102
- minScore: this.options.minFrameScore,
103
- tags: this.options.importantTags
104
- });
105
- this.lastSyncTime = Date.now();
106
- logger.info("Synced frames to shared context", {
107
- frameCount: importantFrames.length,
108
- sessionId: session.sessionId
109
- });
110
- } catch (error) {
111
- logger.error("Failed to sync to shared context", error);
112
- }
113
- }
114
- /**
115
- * Query shared context for relevant frames
116
- */
117
- async querySharedFrames(query) {
118
- try {
119
- const results = await sharedContextLayer.querySharedContext({
120
- ...query,
121
- minScore: this.options.minFrameScore
122
- });
123
- logger.info("Queried shared context", {
124
- query,
125
- resultCount: results.length
126
- });
127
- return results;
128
- } catch (error) {
129
- logger.error("Failed to query shared context", error);
130
- return [];
131
- }
132
- }
133
- /**
134
- * Add a decision to shared context
135
- */
136
- async addDecision(decision, reasoning) {
137
- try {
138
- await sharedContextLayer.addDecision({
139
- decision,
140
- reasoning,
141
- outcome: "pending"
142
- });
143
- logger.info("Added decision to shared context", { decision });
144
- } catch (error) {
145
- logger.error("Failed to add decision", error);
146
- }
147
- }
148
- /**
149
- * Start automatic synchronization
150
- */
151
- startAutoSync() {
152
- if (this.syncTimer) {
153
- clearInterval(this.syncTimer);
154
- }
155
- this.syncTimer = setInterval(() => {
156
- this.syncToSharedContext().catch((error) => {
157
- logger.error("Auto-sync failed", error);
158
- });
159
- }, this.options.syncInterval);
160
- this.setupEventListeners();
161
- }
162
- /**
163
- * Stop automatic synchronization
164
- */
165
- stopAutoSync() {
166
- if (this.syncTimer) {
167
- clearInterval(this.syncTimer);
168
- this.syncTimer = null;
169
- }
170
- }
171
- /**
172
- * Filter frames that are important enough to share
173
- */
174
- filterImportantFrames(frames) {
175
- return frames.filter((frame) => {
176
- const hasImportantTag = this.options.importantTags.some(
177
- (tag) => frame.metadata?.tags?.includes(tag)
178
- );
179
- const isImportantType = [
180
- "task",
181
- "milestone",
182
- "error",
183
- "resolution",
184
- "decision"
185
- ].includes(frame.type);
186
- const markedImportant = frame.metadata?.importance === "high";
187
- return hasImportantTag || isImportantType || markedImportant;
188
- });
189
- }
190
- /**
191
- * Setup event listeners for automatic syncing
192
- */
193
- setupEventListeners() {
194
- if (!this.frameManager) return;
195
- const originalClose = this.frameManager.closeFrame.bind(this.frameManager);
196
- this.frameManager.closeFrame = async (frameId, metadata) => {
197
- const result = await originalClose(frameId, metadata);
198
- const frame = await this.frameManager.getFrame(frameId);
199
- if (frame && this.filterImportantFrames([frame]).length > 0) {
200
- await this.syncToSharedContext();
201
- }
202
- return result;
203
- };
204
- const originalMilestone = this.frameManager.createFrame.bind(
205
- this.frameManager
206
- );
207
- this.frameManager.createFrame = async (params) => {
208
- const result = await originalMilestone(params);
209
- if (params.type === "milestone") {
210
- await this.syncToSharedContext();
211
- }
212
- return result;
213
- };
214
- }
215
- /**
216
- * Get sync statistics
217
- */
218
- getSyncStats() {
219
- return {
220
- lastSyncTime: this.lastSyncTime,
221
- autoSyncEnabled: this.options.autoSync,
222
- syncInterval: this.options.syncInterval
223
- };
224
- }
225
- /**
226
- * Manual trigger for immediate sync
227
- */
228
- async forceSyncNow() {
229
- logger.info("Force sync triggered");
230
- await this.syncToSharedContext();
231
- }
232
- }
233
- const contextBridge = ContextBridge.getInstance();
234
- export {
235
- ContextBridge,
236
- contextBridge
237
- };
238
- //# sourceMappingURL=context-bridge.js.map