@stackmemoryai/stackmemory 1.10.4 → 1.12.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/README.md +104 -23
- package/dist/src/cli/claude-sm.js +266 -84
- package/dist/src/cli/codex-sm.js +185 -33
- package/dist/src/cli/commands/bench.js +209 -2
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/daemon.js +41 -0
- package/dist/src/cli/commands/handoff.js +40 -9
- package/dist/src/cli/commands/onboard.js +70 -3
- package/dist/src/cli/commands/optimize.js +117 -0
- package/dist/src/cli/commands/orchestrate.js +230 -5
- package/dist/src/cli/commands/orchestrator.js +312 -24
- package/dist/src/cli/commands/pack.js +322 -0
- package/dist/src/cli/commands/search.js +40 -1
- package/dist/src/cli/commands/setup.js +177 -7
- package/dist/src/cli/commands/skills.js +10 -1
- package/dist/src/cli/commands/state.js +265 -0
- package/dist/src/cli/commands/wiki.js +33 -0
- package/dist/src/cli/gemini-sm.js +19 -29
- package/dist/src/cli/index.js +90 -29
- package/dist/src/cli/opencode-sm.js +38 -21
- package/dist/src/cli/utils/determinism-watcher.js +66 -0
- package/dist/src/cli/utils/real-cli-bin.js +44 -0
- package/dist/src/core/cache/content-cache.js +238 -0
- package/dist/src/core/cache/index.js +11 -0
- package/dist/src/core/cache/token-estimator.js +16 -0
- package/dist/src/core/context/frame-database.js +38 -30
- package/dist/src/core/cross-search/cross-project-search.js +269 -0
- package/dist/src/core/{merge → cross-search}/index.js +6 -4
- package/dist/src/core/database/sqlite-adapter.js +0 -83
- package/dist/src/core/extensions/provider-adapter.js +5 -0
- package/dist/src/core/models/model-router.js +22 -2
- package/dist/src/core/monitoring/logger.js +2 -1
- package/dist/src/core/optimization/trace-optimizer.js +413 -0
- package/dist/src/core/provenance/confidence-scorer.js +128 -0
- package/dist/src/core/provenance/index.js +40 -0
- package/dist/src/core/provenance/provenance-store.js +194 -0
- package/dist/src/core/provenance/types.js +82 -0
- package/dist/src/core/session/project-handoff.js +64 -0
- package/dist/src/core/session/session-manager.js +28 -0
- package/dist/src/core/shared-state/canonical-store.js +564 -0
- package/dist/src/core/skill-packs/index.js +18 -0
- package/dist/src/core/skill-packs/parser.js +42 -0
- package/dist/src/core/skill-packs/registry.js +224 -0
- package/dist/src/core/skill-packs/types.js +66 -0
- package/dist/src/core/trace/trace-event-store.js +282 -0
- package/dist/src/core/trace/trace-event.js +4 -0
- package/dist/src/core/wiki/wiki-compiler.js +219 -0
- package/dist/src/daemon/daemon-config.js +7 -0
- package/dist/src/daemon/services/github-service.js +126 -0
- package/dist/src/daemon/unified-daemon.js +30 -0
- package/dist/src/features/sweep/pty-wrapper.js +13 -5
- package/dist/src/hooks/schemas.js +2 -0
- package/dist/src/integrations/claude-code/subagent-client.js +89 -0
- package/dist/src/integrations/github/pr-state.js +158 -0
- package/dist/src/integrations/linear/client.js +4 -1
- package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +188 -0
- package/dist/src/integrations/mcp/handlers/index.js +40 -59
- package/dist/src/integrations/mcp/server.js +425 -311
- package/dist/src/integrations/mcp/tool-alias-registry.js +370 -0
- package/dist/src/integrations/mcp/tool-definitions.js +98 -229
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +3 -40
- package/dist/src/integrations/ralph/learning/pattern-learner.js +1 -20
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -2
- package/dist/src/mcp/stackmemory-mcp-server.js +315 -0
- package/dist/src/orchestrators/multimodal/determinism.js +243 -0
- package/dist/src/orchestrators/multimodal/harness.js +147 -77
- package/dist/src/orchestrators/multimodal/providers.js +44 -3
- package/dist/src/utils/hook-installer.js +8 -8
- package/package.json +10 -1
- package/packs/coding/python-fastapi/instructions.md +60 -0
- package/packs/coding/python-fastapi/pack.yaml +28 -0
- package/packs/coding/typescript-react/instructions.md +47 -0
- package/packs/coding/typescript-react/pack.yaml +28 -0
- package/packs/core/commands/capture.md +32 -0
- package/packs/core/commands/learn.md +73 -0
- package/packs/core/commands/next.md +36 -0
- package/packs/core/commands/restart.md +58 -0
- package/packs/core/commands/restore.md +29 -0
- package/packs/core/commands/start.md +57 -0
- package/packs/core/commands/stop.md +65 -0
- package/packs/core/commands/summary.md +40 -0
- package/packs/core/manifest.json +24 -0
- package/packs/ops/decision-recovery/instructions.md +65 -0
- package/packs/ops/decision-recovery/pack.yaml +89 -0
- package/templates/claude-hooks/doc-ingest.js +76 -0
- package/dist/src/cli/commands/team.js +0 -168
- package/dist/src/core/context/shared-context-layer.js +0 -620
- package/dist/src/core/context/stack-merge-resolver.js +0 -748
- package/dist/src/core/merge/conflict-detector.js +0 -430
- package/dist/src/core/merge/resolution-engine.js +0 -557
- package/dist/src/core/merge/stack-diff.js +0 -531
- package/dist/src/core/merge/unified-merge-resolver.js +0 -302
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
- package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
- /package/dist/src/core/{merge → cache}/types.js +0 -0
|
@@ -1,620 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
-
import { dirname as __pathDirname } from 'path';
|
|
3
|
-
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
-
const __dirname = __pathDirname(__filename);
|
|
5
|
-
import { v4 as uuidv4 } from "uuid";
|
|
6
|
-
import * as fs from "fs/promises";
|
|
7
|
-
import * as path from "path";
|
|
8
|
-
import { sessionManager } from "../session/session-manager.js";
|
|
9
|
-
import { logger } from "../monitoring/logger.js";
|
|
10
|
-
class SharedContextLayer {
|
|
11
|
-
static instance;
|
|
12
|
-
contextDir;
|
|
13
|
-
cache = /* @__PURE__ */ new Map();
|
|
14
|
-
MAX_CACHE_SIZE = 100;
|
|
15
|
-
CACHE_TTL = 5 * 60 * 1e3;
|
|
16
|
-
// 5 minutes
|
|
17
|
-
lastCacheClean = Date.now();
|
|
18
|
-
constructor() {
|
|
19
|
-
const homeDir = process.env["HOME"] || process.env["USERPROFILE"] || "";
|
|
20
|
-
this.contextDir = path.join(homeDir, ".stackmemory", "shared-context");
|
|
21
|
-
}
|
|
22
|
-
static getInstance() {
|
|
23
|
-
if (!SharedContextLayer.instance) {
|
|
24
|
-
SharedContextLayer.instance = new SharedContextLayer();
|
|
25
|
-
}
|
|
26
|
-
return SharedContextLayer.instance;
|
|
27
|
-
}
|
|
28
|
-
async initialize() {
|
|
29
|
-
await fs.mkdir(this.contextDir, { recursive: true });
|
|
30
|
-
await fs.mkdir(path.join(this.contextDir, "projects"), { recursive: true });
|
|
31
|
-
await fs.mkdir(path.join(this.contextDir, "patterns"), { recursive: true });
|
|
32
|
-
await fs.mkdir(path.join(this.contextDir, "decisions"), {
|
|
33
|
-
recursive: true
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Get or create shared context for current project/branch
|
|
38
|
-
*/
|
|
39
|
-
async getSharedContext(options) {
|
|
40
|
-
const session = sessionManager.getCurrentSession();
|
|
41
|
-
const projectId = options?.projectId || session?.projectId || "global";
|
|
42
|
-
const branch = options?.branch || session?.branch;
|
|
43
|
-
const cacheKey = `${projectId}:${branch || "main"}`;
|
|
44
|
-
if (this.cache.has(cacheKey)) {
|
|
45
|
-
const cached = this.cache.get(cacheKey);
|
|
46
|
-
if (Date.now() - cached.lastUpdated < this.CACHE_TTL) {
|
|
47
|
-
return cached;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
const context = await this.loadProjectContext(projectId, branch);
|
|
51
|
-
if (options?.includeOtherBranches) {
|
|
52
|
-
const otherBranches = await this.loadOtherBranchContexts(
|
|
53
|
-
projectId,
|
|
54
|
-
branch
|
|
55
|
-
);
|
|
56
|
-
context.sessions.push(...otherBranches);
|
|
57
|
-
}
|
|
58
|
-
this.cache.set(cacheKey, context);
|
|
59
|
-
this.cleanCache();
|
|
60
|
-
return context;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Add current session's important frames to shared context
|
|
64
|
-
*/
|
|
65
|
-
async addToSharedContext(frames, options) {
|
|
66
|
-
const session = sessionManager.getCurrentSession();
|
|
67
|
-
if (!session) return;
|
|
68
|
-
const context = await this.getSharedContext();
|
|
69
|
-
const minScore = options?.minScore || 0.7;
|
|
70
|
-
const importantFrames = frames.filter((f) => {
|
|
71
|
-
const score = this.calculateFrameScore(f);
|
|
72
|
-
return score >= minScore;
|
|
73
|
-
});
|
|
74
|
-
const sessionContext = {
|
|
75
|
-
sessionId: session.sessionId,
|
|
76
|
-
runId: session.runId,
|
|
77
|
-
summary: this.generateSessionSummary(importantFrames),
|
|
78
|
-
keyFrames: importantFrames.map((f) => this.summarizeFrame(f)),
|
|
79
|
-
createdAt: session.startedAt,
|
|
80
|
-
lastActiveAt: Date.now(),
|
|
81
|
-
metadata: session.metadata
|
|
82
|
-
};
|
|
83
|
-
const existingIndex = context.sessions.findIndex(
|
|
84
|
-
(s) => s.sessionId === session.sessionId
|
|
85
|
-
);
|
|
86
|
-
if (existingIndex >= 0) {
|
|
87
|
-
context.sessions[existingIndex] = sessionContext;
|
|
88
|
-
} else {
|
|
89
|
-
context.sessions.push(sessionContext);
|
|
90
|
-
}
|
|
91
|
-
this.updatePatterns(context, importantFrames);
|
|
92
|
-
this.updateReferenceIndex(context, importantFrames);
|
|
93
|
-
await this.saveProjectContext(context);
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Query shared context for relevant frames
|
|
97
|
-
*/
|
|
98
|
-
async querySharedContext(query) {
|
|
99
|
-
const context = await this.getSharedContext({ includeOtherBranches: true });
|
|
100
|
-
let results = [];
|
|
101
|
-
for (const session of context.sessions) {
|
|
102
|
-
if (query.sessionId && session.sessionId !== query.sessionId) continue;
|
|
103
|
-
if (!session.keyFrames || !Array.isArray(session.keyFrames)) continue;
|
|
104
|
-
const filtered = session.keyFrames.filter((f) => {
|
|
105
|
-
if (query.tags && !query.tags.some((tag) => f.tags.includes(tag)))
|
|
106
|
-
return false;
|
|
107
|
-
if (query.type && f.type !== query.type) return false;
|
|
108
|
-
if (query.minScore && f.score < query.minScore) return false;
|
|
109
|
-
return true;
|
|
110
|
-
});
|
|
111
|
-
results.push(...filtered);
|
|
112
|
-
}
|
|
113
|
-
results.sort((a, b) => {
|
|
114
|
-
const scoreWeight = 0.7;
|
|
115
|
-
const recencyWeight = 0.3;
|
|
116
|
-
const aScore = a.score * scoreWeight + (1 - (Date.now() - a.createdAt) / (30 * 24 * 60 * 60 * 1e3)) * recencyWeight;
|
|
117
|
-
const bScore = b.score * scoreWeight + (1 - (Date.now() - b.createdAt) / (30 * 24 * 60 * 60 * 1e3)) * recencyWeight;
|
|
118
|
-
return bScore - aScore;
|
|
119
|
-
});
|
|
120
|
-
if (query.limit) {
|
|
121
|
-
results = results.slice(0, query.limit);
|
|
122
|
-
}
|
|
123
|
-
const index = context.referenceIndex;
|
|
124
|
-
if (!index.recentlyAccessed) {
|
|
125
|
-
index.recentlyAccessed = [];
|
|
126
|
-
}
|
|
127
|
-
if (results.length > 0) {
|
|
128
|
-
const frameIds = results.map((r) => r.frameId);
|
|
129
|
-
index.recentlyAccessed = [
|
|
130
|
-
...frameIds,
|
|
131
|
-
...index.recentlyAccessed.filter((id) => !frameIds.includes(id))
|
|
132
|
-
].slice(0, 100);
|
|
133
|
-
await this.saveProjectContext(context);
|
|
134
|
-
}
|
|
135
|
-
return results;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Get relevant patterns from shared context
|
|
139
|
-
*/
|
|
140
|
-
async getPatterns(type) {
|
|
141
|
-
const context = await this.getSharedContext();
|
|
142
|
-
if (type) {
|
|
143
|
-
return context.globalPatterns.filter((p) => p.type === type);
|
|
144
|
-
}
|
|
145
|
-
return context.globalPatterns;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Add a decision to the shared context
|
|
149
|
-
*/
|
|
150
|
-
async addDecision(decision) {
|
|
151
|
-
const session = sessionManager.getCurrentSession();
|
|
152
|
-
if (!session) return;
|
|
153
|
-
const context = await this.getSharedContext();
|
|
154
|
-
const newDecision = {
|
|
155
|
-
id: uuidv4(),
|
|
156
|
-
timestamp: Date.now(),
|
|
157
|
-
sessionId: session.sessionId,
|
|
158
|
-
outcome: "pending",
|
|
159
|
-
...decision
|
|
160
|
-
};
|
|
161
|
-
context.decisionLog.push(newDecision);
|
|
162
|
-
if (context.decisionLog.length > 100) {
|
|
163
|
-
context.decisionLog = context.decisionLog.slice(-100);
|
|
164
|
-
}
|
|
165
|
-
await this.saveProjectContext(context);
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Get recent decisions from shared context
|
|
169
|
-
*/
|
|
170
|
-
async getDecisions(limit = 10) {
|
|
171
|
-
const context = await this.getSharedContext();
|
|
172
|
-
return context.decisionLog.slice(-limit);
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Automatic context discovery on CLI startup
|
|
176
|
-
*/
|
|
177
|
-
async autoDiscoverContext() {
|
|
178
|
-
const context = await this.getSharedContext({
|
|
179
|
-
includeOtherBranches: false
|
|
180
|
-
});
|
|
181
|
-
const recentPatterns = context.globalPatterns.filter((p) => Date.now() - p.lastSeen < 7 * 24 * 60 * 60 * 1e3).sort((a, b) => b.frequency - a.frequency).slice(0, 5);
|
|
182
|
-
const lastDecisions = context.decisionLog.slice(-5);
|
|
183
|
-
const suggestedFrames = await this.querySharedContext({
|
|
184
|
-
minScore: 0.8,
|
|
185
|
-
limit: 5
|
|
186
|
-
});
|
|
187
|
-
return {
|
|
188
|
-
hasSharedContext: context.sessions.length > 0,
|
|
189
|
-
sessionCount: context.sessions.length,
|
|
190
|
-
recentPatterns,
|
|
191
|
-
lastDecisions,
|
|
192
|
-
suggestedFrames
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
async loadProjectContext(projectId, branch) {
|
|
196
|
-
const contextFile = path.join(
|
|
197
|
-
this.contextDir,
|
|
198
|
-
"projects",
|
|
199
|
-
`${projectId}_${branch || "main"}.json`
|
|
200
|
-
);
|
|
201
|
-
try {
|
|
202
|
-
const data = await fs.readFile(contextFile, "utf-8");
|
|
203
|
-
const context = JSON.parse(data);
|
|
204
|
-
context.referenceIndex.byTag = new Map(
|
|
205
|
-
Object.entries(context.referenceIndex.byTag || {})
|
|
206
|
-
);
|
|
207
|
-
context.referenceIndex.byType = new Map(
|
|
208
|
-
Object.entries(context.referenceIndex.byType || {})
|
|
209
|
-
);
|
|
210
|
-
return context;
|
|
211
|
-
} catch {
|
|
212
|
-
return {
|
|
213
|
-
projectId,
|
|
214
|
-
branch,
|
|
215
|
-
lastUpdated: Date.now(),
|
|
216
|
-
sessions: [],
|
|
217
|
-
globalPatterns: [],
|
|
218
|
-
decisionLog: [],
|
|
219
|
-
referenceIndex: {
|
|
220
|
-
byTag: /* @__PURE__ */ new Map(),
|
|
221
|
-
byType: /* @__PURE__ */ new Map(),
|
|
222
|
-
byScore: [],
|
|
223
|
-
recentlyAccessed: []
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
async saveProjectContext(context) {
|
|
229
|
-
const contextFile = path.join(
|
|
230
|
-
this.contextDir,
|
|
231
|
-
"projects",
|
|
232
|
-
`${context.projectId}_${context.branch || "main"}.json`
|
|
233
|
-
);
|
|
234
|
-
const serializable = {
|
|
235
|
-
...context,
|
|
236
|
-
lastUpdated: Date.now(),
|
|
237
|
-
referenceIndex: {
|
|
238
|
-
...context.referenceIndex,
|
|
239
|
-
byTag: Object.fromEntries(context.referenceIndex.byTag),
|
|
240
|
-
byType: Object.fromEntries(context.referenceIndex.byType)
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
await fs.writeFile(contextFile, JSON.stringify(serializable, null, 2));
|
|
244
|
-
}
|
|
245
|
-
async loadOtherBranchContexts(projectId, currentBranch) {
|
|
246
|
-
const projectsDir = path.join(this.contextDir, "projects");
|
|
247
|
-
const files = await fs.readdir(projectsDir);
|
|
248
|
-
const sessions = [];
|
|
249
|
-
for (const file of files) {
|
|
250
|
-
if (file.startsWith(`${projectId}_`) && !file.includes(currentBranch || "main")) {
|
|
251
|
-
try {
|
|
252
|
-
const data = await fs.readFile(path.join(projectsDir, file), "utf-8");
|
|
253
|
-
const context = JSON.parse(data);
|
|
254
|
-
sessions.push(...context.sessions);
|
|
255
|
-
} catch {
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return sessions;
|
|
260
|
-
}
|
|
261
|
-
calculateFrameScore(frame) {
|
|
262
|
-
let score = 0.5;
|
|
263
|
-
if (frame.type === "task" || frame.type === "review") score += 0.2;
|
|
264
|
-
if (frame.type === "debug" || frame.type === "write") score += 0.15;
|
|
265
|
-
if (frame.type === "error") score += 0.15;
|
|
266
|
-
const frameWithData = frame;
|
|
267
|
-
if (frameWithData.data) score += 0.2;
|
|
268
|
-
if (frame.outputs && Object.keys(frame.outputs).length > 0) score += 0.2;
|
|
269
|
-
if (frame.digest_text || frame.digest_json && Object.keys(frame.digest_json).length > 0)
|
|
270
|
-
score += 0.1;
|
|
271
|
-
if (frame.created_at) {
|
|
272
|
-
const age = Date.now() - frame.created_at;
|
|
273
|
-
const daysSinceCreation = age / (24 * 60 * 60 * 1e3);
|
|
274
|
-
score *= Math.max(0.3, 1 - daysSinceCreation / 30);
|
|
275
|
-
}
|
|
276
|
-
return Math.min(1, score);
|
|
277
|
-
}
|
|
278
|
-
summarizeFrame(frame) {
|
|
279
|
-
return {
|
|
280
|
-
frameId: frame.frame_id,
|
|
281
|
-
title: frame.name,
|
|
282
|
-
type: frame.type,
|
|
283
|
-
score: this.calculateFrameScore(frame),
|
|
284
|
-
tags: [],
|
|
285
|
-
summary: this.generateFrameSummary(frame),
|
|
286
|
-
createdAt: frame.created_at
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
generateFrameSummary(frame) {
|
|
290
|
-
const parts = [];
|
|
291
|
-
const frameWithData = frame;
|
|
292
|
-
if (frame.type) parts.push(`[${frame.type}]`);
|
|
293
|
-
if (frame.name) parts.push(frame.name);
|
|
294
|
-
if (frameWithData.title) parts.push(frameWithData.title);
|
|
295
|
-
if (frameWithData.data?.error)
|
|
296
|
-
parts.push(`Error: ${frameWithData.data.error}`);
|
|
297
|
-
if (frameWithData.data?.resolution)
|
|
298
|
-
parts.push(`Resolution: ${frameWithData.data.resolution}`);
|
|
299
|
-
return parts.join(" - ").slice(0, 200);
|
|
300
|
-
}
|
|
301
|
-
generateSessionSummary(frames) {
|
|
302
|
-
const types = [...new Set(frames.map((f) => f.type))];
|
|
303
|
-
return `Session with ${frames.length} key frames: ${types.join(", ")}`;
|
|
304
|
-
}
|
|
305
|
-
updatePatterns(context, frames) {
|
|
306
|
-
for (const frame of frames) {
|
|
307
|
-
const frameWithData = frame;
|
|
308
|
-
if (frameWithData.data?.error) {
|
|
309
|
-
this.addPattern(
|
|
310
|
-
context,
|
|
311
|
-
frameWithData.data.error,
|
|
312
|
-
"error",
|
|
313
|
-
frameWithData.data?.resolution
|
|
314
|
-
);
|
|
315
|
-
} else if (frame.type === "error" && frame.name) {
|
|
316
|
-
const errorText = frame.outputs?.error || frame.name;
|
|
317
|
-
const resolution = frame.outputs?.resolution;
|
|
318
|
-
if (errorText) {
|
|
319
|
-
this.addPattern(context, errorText, "error", resolution);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
if (frame.type === "decision" && frameWithData.data?.decision) {
|
|
323
|
-
this.addPattern(context, frameWithData.data.decision, "decision");
|
|
324
|
-
} else if (frame.digest_json?.decision) {
|
|
325
|
-
this.addPattern(context, frame.digest_json.decision, "decision");
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
addPattern(context, pattern, type, resolution) {
|
|
330
|
-
const existing = context.globalPatterns.find(
|
|
331
|
-
(p) => p.pattern === pattern && p.type === type
|
|
332
|
-
);
|
|
333
|
-
if (existing) {
|
|
334
|
-
existing.frequency++;
|
|
335
|
-
existing.lastSeen = Date.now();
|
|
336
|
-
if (resolution) existing.resolution = resolution;
|
|
337
|
-
} else {
|
|
338
|
-
context.globalPatterns.push({
|
|
339
|
-
pattern,
|
|
340
|
-
type,
|
|
341
|
-
frequency: 1,
|
|
342
|
-
lastSeen: Date.now(),
|
|
343
|
-
resolution
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
if (context.globalPatterns.length > 100) {
|
|
347
|
-
context.globalPatterns.sort((a, b) => b.frequency - a.frequency);
|
|
348
|
-
context.globalPatterns = context.globalPatterns.slice(0, 100);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
updateReferenceIndex(context, frames) {
|
|
352
|
-
for (const frame of frames) {
|
|
353
|
-
const summary = this.summarizeFrame(frame);
|
|
354
|
-
for (const tag of summary.tags) {
|
|
355
|
-
if (!context.referenceIndex.byTag.has(tag)) {
|
|
356
|
-
context.referenceIndex.byTag.set(tag, []);
|
|
357
|
-
}
|
|
358
|
-
context.referenceIndex.byTag.get(tag).push(frame.frameId);
|
|
359
|
-
}
|
|
360
|
-
if (!context.referenceIndex.byType.has(frame.type)) {
|
|
361
|
-
context.referenceIndex.byType.set(frame.type, []);
|
|
362
|
-
}
|
|
363
|
-
context.referenceIndex.byType.get(frame.type).push(frame.frameId);
|
|
364
|
-
const scoreIndex = context.referenceIndex.byScore;
|
|
365
|
-
const insertIndex = scoreIndex.findIndex((id) => {
|
|
366
|
-
const otherFrame = context.sessions.flatMap((s) => s.keyFrames).find((f) => f.frameId === id);
|
|
367
|
-
return otherFrame && otherFrame.score < summary.score;
|
|
368
|
-
});
|
|
369
|
-
if (insertIndex >= 0) {
|
|
370
|
-
scoreIndex.splice(insertIndex, 0, frame.frameId);
|
|
371
|
-
} else {
|
|
372
|
-
scoreIndex.push(frame.frameId);
|
|
373
|
-
}
|
|
374
|
-
context.referenceIndex.byScore = scoreIndex.slice(0, 1e3);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
cleanCache() {
|
|
378
|
-
if (Date.now() - this.lastCacheClean < 6e4) return;
|
|
379
|
-
if (this.cache.size > this.MAX_CACHE_SIZE) {
|
|
380
|
-
const entries = Array.from(this.cache.entries()).sort(
|
|
381
|
-
(a, b) => b[1].lastUpdated - a[1].lastUpdated
|
|
382
|
-
);
|
|
383
|
-
this.cache = new Map(entries.slice(0, this.MAX_CACHE_SIZE / 2));
|
|
384
|
-
}
|
|
385
|
-
this.lastCacheClean = Date.now();
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
const sharedContextLayer = SharedContextLayer.getInstance();
|
|
389
|
-
class ContextBridge {
|
|
390
|
-
static instance;
|
|
391
|
-
frameManager = null;
|
|
392
|
-
syncTimer = null;
|
|
393
|
-
lastSyncTime = 0;
|
|
394
|
-
options = {
|
|
395
|
-
autoSync: true,
|
|
396
|
-
syncInterval: 6e4,
|
|
397
|
-
// 1 minute
|
|
398
|
-
minFrameScore: 0.5,
|
|
399
|
-
// Include frames with score above 0.5
|
|
400
|
-
importantTags: ["decision", "error", "milestone", "learning"]
|
|
401
|
-
};
|
|
402
|
-
constructor() {
|
|
403
|
-
}
|
|
404
|
-
static getInstance() {
|
|
405
|
-
if (!ContextBridge.instance) {
|
|
406
|
-
ContextBridge.instance = new ContextBridge();
|
|
407
|
-
}
|
|
408
|
-
return ContextBridge.instance;
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Initialize the bridge with a frame manager
|
|
412
|
-
*/
|
|
413
|
-
async initialize(frameManager, options) {
|
|
414
|
-
this.frameManager = frameManager;
|
|
415
|
-
this.options = { ...this.options, ...options };
|
|
416
|
-
await this.loadSharedContext();
|
|
417
|
-
if (this.options.autoSync) {
|
|
418
|
-
this.startAutoSync();
|
|
419
|
-
}
|
|
420
|
-
logger.info("Context bridge initialized", {
|
|
421
|
-
autoSync: this.options.autoSync,
|
|
422
|
-
syncInterval: this.options.syncInterval
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* Load relevant shared context into current session
|
|
427
|
-
*/
|
|
428
|
-
async loadSharedContext() {
|
|
429
|
-
try {
|
|
430
|
-
const session = sessionManager.getCurrentSession();
|
|
431
|
-
if (!session) return;
|
|
432
|
-
const discovery = await sharedContextLayer.autoDiscoverContext();
|
|
433
|
-
if (!discovery.hasSharedContext) {
|
|
434
|
-
logger.info("No shared context available to load");
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
if (discovery.recentPatterns.length > 0) {
|
|
438
|
-
logger.info("Loaded recent patterns from shared context", {
|
|
439
|
-
patternCount: discovery.recentPatterns.length
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
if (discovery.lastDecisions.length > 0) {
|
|
443
|
-
logger.info("Loaded recent decisions from shared context", {
|
|
444
|
-
decisionCount: discovery.lastDecisions.length
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
if (discovery.suggestedFrames.length > 0) {
|
|
448
|
-
const metadata = {
|
|
449
|
-
suggestedFrames: discovery.suggestedFrames,
|
|
450
|
-
loadedAt: Date.now()
|
|
451
|
-
};
|
|
452
|
-
if (this.frameManager) {
|
|
453
|
-
await this.frameManager.addContext(
|
|
454
|
-
"shared-context-suggestions",
|
|
455
|
-
metadata
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
logger.info("Loaded suggested frames from shared context", {
|
|
459
|
-
frameCount: discovery.suggestedFrames.length
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
} catch (error) {
|
|
463
|
-
logger.error("Failed to load shared context", error);
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Sync current session's important frames to shared context
|
|
468
|
-
*/
|
|
469
|
-
async syncToSharedContext() {
|
|
470
|
-
try {
|
|
471
|
-
if (!this.frameManager) return;
|
|
472
|
-
const session = sessionManager.getCurrentSession();
|
|
473
|
-
if (!session) return;
|
|
474
|
-
const activeFrames = this.frameManager.getActiveFramePath().filter(Boolean);
|
|
475
|
-
const recentFrames = await this.frameManager.getRecentFrames(100);
|
|
476
|
-
const allFrames = [...activeFrames, ...recentFrames].filter(Boolean);
|
|
477
|
-
const importantFrames = this.filterImportantFrames(allFrames);
|
|
478
|
-
if (importantFrames.length === 0) {
|
|
479
|
-
logger.debug("No important frames to sync");
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
await sharedContextLayer.addToSharedContext(importantFrames, {
|
|
483
|
-
minScore: this.options.minFrameScore,
|
|
484
|
-
tags: this.options.importantTags
|
|
485
|
-
});
|
|
486
|
-
this.lastSyncTime = Date.now();
|
|
487
|
-
logger.info("Synced frames to shared context", {
|
|
488
|
-
frameCount: importantFrames.length,
|
|
489
|
-
sessionId: session.sessionId
|
|
490
|
-
});
|
|
491
|
-
} catch (error) {
|
|
492
|
-
logger.error("Failed to sync to shared context", error);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
/**
|
|
496
|
-
* Query shared context for relevant frames
|
|
497
|
-
*/
|
|
498
|
-
async querySharedFrames(query) {
|
|
499
|
-
try {
|
|
500
|
-
const results = await sharedContextLayer.querySharedContext({
|
|
501
|
-
...query,
|
|
502
|
-
minScore: this.options.minFrameScore
|
|
503
|
-
});
|
|
504
|
-
logger.info("Queried shared context", {
|
|
505
|
-
query,
|
|
506
|
-
resultCount: results.length
|
|
507
|
-
});
|
|
508
|
-
return results;
|
|
509
|
-
} catch (error) {
|
|
510
|
-
logger.error("Failed to query shared context", error);
|
|
511
|
-
return [];
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
/**
|
|
515
|
-
* Add a decision to shared context
|
|
516
|
-
*/
|
|
517
|
-
async addDecision(decision, reasoning) {
|
|
518
|
-
try {
|
|
519
|
-
await sharedContextLayer.addDecision({
|
|
520
|
-
decision,
|
|
521
|
-
reasoning,
|
|
522
|
-
outcome: "pending"
|
|
523
|
-
});
|
|
524
|
-
logger.info("Added decision to shared context", { decision });
|
|
525
|
-
} catch (error) {
|
|
526
|
-
logger.error("Failed to add decision", error);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
|
-
* Start automatic synchronization
|
|
531
|
-
*/
|
|
532
|
-
startAutoSync() {
|
|
533
|
-
if (this.syncTimer) {
|
|
534
|
-
clearInterval(this.syncTimer);
|
|
535
|
-
}
|
|
536
|
-
this.syncTimer = setInterval(() => {
|
|
537
|
-
this.syncToSharedContext().catch((error) => {
|
|
538
|
-
logger.error("Auto-sync failed", error);
|
|
539
|
-
});
|
|
540
|
-
}, this.options.syncInterval);
|
|
541
|
-
this.setupEventListeners();
|
|
542
|
-
}
|
|
543
|
-
/**
|
|
544
|
-
* Stop automatic synchronization
|
|
545
|
-
*/
|
|
546
|
-
stopAutoSync() {
|
|
547
|
-
if (this.syncTimer) {
|
|
548
|
-
clearInterval(this.syncTimer);
|
|
549
|
-
this.syncTimer = null;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
/**
|
|
553
|
-
* Filter frames that are important enough to share
|
|
554
|
-
*/
|
|
555
|
-
filterImportantFrames(frames) {
|
|
556
|
-
return frames.filter((frame) => {
|
|
557
|
-
const hasImportantTag = this.options.importantTags.some(
|
|
558
|
-
(tag) => frame.metadata?.tags?.includes(tag)
|
|
559
|
-
);
|
|
560
|
-
const isImportantType = [
|
|
561
|
-
"task",
|
|
562
|
-
"milestone",
|
|
563
|
-
"error",
|
|
564
|
-
"resolution",
|
|
565
|
-
"decision"
|
|
566
|
-
].includes(frame.type);
|
|
567
|
-
const markedImportant = frame.metadata?.importance === "high";
|
|
568
|
-
return hasImportantTag || isImportantType || markedImportant;
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* Setup event listeners for automatic syncing
|
|
573
|
-
*/
|
|
574
|
-
setupEventListeners() {
|
|
575
|
-
if (!this.frameManager) return;
|
|
576
|
-
const originalClose = this.frameManager.closeFrame.bind(this.frameManager);
|
|
577
|
-
this.frameManager.closeFrame = async (frameId, metadata) => {
|
|
578
|
-
const result = await originalClose(frameId, metadata);
|
|
579
|
-
const frame = await this.frameManager.getFrame(frameId);
|
|
580
|
-
if (frame && this.filterImportantFrames([frame]).length > 0) {
|
|
581
|
-
await this.syncToSharedContext();
|
|
582
|
-
}
|
|
583
|
-
return result;
|
|
584
|
-
};
|
|
585
|
-
const originalMilestone = this.frameManager.createFrame.bind(
|
|
586
|
-
this.frameManager
|
|
587
|
-
);
|
|
588
|
-
this.frameManager.createFrame = async (params) => {
|
|
589
|
-
const result = await originalMilestone(params);
|
|
590
|
-
if (params.type === "milestone") {
|
|
591
|
-
await this.syncToSharedContext();
|
|
592
|
-
}
|
|
593
|
-
return result;
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
/**
|
|
597
|
-
* Get sync statistics
|
|
598
|
-
*/
|
|
599
|
-
getSyncStats() {
|
|
600
|
-
return {
|
|
601
|
-
lastSyncTime: this.lastSyncTime,
|
|
602
|
-
autoSyncEnabled: this.options.autoSync,
|
|
603
|
-
syncInterval: this.options.syncInterval
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
/**
|
|
607
|
-
* Manual trigger for immediate sync
|
|
608
|
-
*/
|
|
609
|
-
async forceSyncNow() {
|
|
610
|
-
logger.info("Force sync triggered");
|
|
611
|
-
await this.syncToSharedContext();
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
const contextBridge = ContextBridge.getInstance();
|
|
615
|
-
export {
|
|
616
|
-
ContextBridge,
|
|
617
|
-
SharedContextLayer,
|
|
618
|
-
contextBridge,
|
|
619
|
-
sharedContextLayer
|
|
620
|
-
};
|