@stackmemoryai/stackmemory 1.10.5 → 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/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/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 +0 -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/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,531 +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 { ConflictDetector } from "./conflict-detector.js";
|
|
6
|
-
class StackDiffVisualizer {
|
|
7
|
-
conflictDetector;
|
|
8
|
-
constructor() {
|
|
9
|
-
this.conflictDetector = new ConflictDetector();
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Visualize divergence between two frame stacks
|
|
13
|
-
*/
|
|
14
|
-
visualizeDivergence(baseFrame, branch1, branch2) {
|
|
15
|
-
const nodes = [];
|
|
16
|
-
const edges = [];
|
|
17
|
-
nodes.push({
|
|
18
|
-
id: baseFrame.frame_id,
|
|
19
|
-
type: "common",
|
|
20
|
-
frame: baseFrame,
|
|
21
|
-
position: { x: 0, y: 0 },
|
|
22
|
-
metadata: { label: "Common Ancestor" }
|
|
23
|
-
});
|
|
24
|
-
const branch1Nodes = this.processBranch(
|
|
25
|
-
branch1,
|
|
26
|
-
baseFrame.frame_id,
|
|
27
|
-
-100,
|
|
28
|
-
// Left side
|
|
29
|
-
100
|
|
30
|
-
);
|
|
31
|
-
nodes.push(...branch1Nodes.nodes);
|
|
32
|
-
edges.push(...branch1Nodes.edges);
|
|
33
|
-
const branch2Nodes = this.processBranch(
|
|
34
|
-
branch2,
|
|
35
|
-
baseFrame.frame_id,
|
|
36
|
-
100,
|
|
37
|
-
// Right side
|
|
38
|
-
100
|
|
39
|
-
);
|
|
40
|
-
nodes.push(...branch2Nodes.nodes);
|
|
41
|
-
edges.push(...branch2Nodes.edges);
|
|
42
|
-
const conflicts = this.conflictDetector.detectConflicts(branch1, branch2);
|
|
43
|
-
this.markConflicts(nodes, edges, conflicts);
|
|
44
|
-
return {
|
|
45
|
-
nodes,
|
|
46
|
-
edges,
|
|
47
|
-
layout: "tree"
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Render conflict markers for visualization
|
|
52
|
-
*/
|
|
53
|
-
renderConflictMarkers(conflicts) {
|
|
54
|
-
const markers = [];
|
|
55
|
-
for (const conflict of conflicts) {
|
|
56
|
-
markers.push({
|
|
57
|
-
frameId: conflict.frameId1,
|
|
58
|
-
type: "conflict",
|
|
59
|
-
color: this.getSeverityColor(conflict.severity),
|
|
60
|
-
symbol: this.getConflictSymbol(conflict.type),
|
|
61
|
-
description: conflict.description
|
|
62
|
-
});
|
|
63
|
-
markers.push({
|
|
64
|
-
frameId: conflict.frameId2,
|
|
65
|
-
type: "conflict",
|
|
66
|
-
color: this.getSeverityColor(conflict.severity),
|
|
67
|
-
symbol: this.getConflictSymbol(conflict.type),
|
|
68
|
-
description: conflict.description
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
return markers;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Generate a merge preview based on resolution strategy
|
|
75
|
-
*/
|
|
76
|
-
generateMergePreview(stack1, stack2, strategy) {
|
|
77
|
-
const mergedFrames = [];
|
|
78
|
-
const keptFromStack1 = [];
|
|
79
|
-
const keptFromStack2 = [];
|
|
80
|
-
const conflicts = this.conflictDetector.detectConflicts(stack1, stack2);
|
|
81
|
-
switch (strategy) {
|
|
82
|
-
case "keep_both":
|
|
83
|
-
return this.previewKeepBoth(stack1, stack2, conflicts);
|
|
84
|
-
case "team_vote":
|
|
85
|
-
return this.previewTeamVote(stack1, stack2, conflicts);
|
|
86
|
-
case "senior_override":
|
|
87
|
-
return this.previewSeniorOverride(stack1, stack2, conflicts);
|
|
88
|
-
case "ai_suggest":
|
|
89
|
-
return this.previewAISuggest(stack1, stack2, conflicts);
|
|
90
|
-
case "hybrid":
|
|
91
|
-
return this.previewHybrid(stack1, stack2, conflicts);
|
|
92
|
-
default:
|
|
93
|
-
return {
|
|
94
|
-
mergedFrames,
|
|
95
|
-
keptFromStack1,
|
|
96
|
-
keptFromStack2,
|
|
97
|
-
conflicts,
|
|
98
|
-
estimatedSuccess: 0
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Create a stack diff comparison
|
|
104
|
-
*/
|
|
105
|
-
createStackDiff(baseFrameId, stack1, stack2) {
|
|
106
|
-
const conflicts = this.conflictDetector.detectConflicts(stack1, stack2);
|
|
107
|
-
const divergencePoint = this.findDivergencePoint(stack1, stack2);
|
|
108
|
-
const commonAncestor = this.findCommonAncestor(stack1, stack2);
|
|
109
|
-
const baseFrame = stack1.frames.find((f) => f.frame_id === baseFrameId) || stack2.frames.find((f) => f.frame_id === baseFrameId);
|
|
110
|
-
const visualRepresentation = baseFrame ? this.visualizeDivergence(baseFrame, stack1, stack2) : void 0;
|
|
111
|
-
return {
|
|
112
|
-
baseFrame: baseFrameId,
|
|
113
|
-
branch1: stack1,
|
|
114
|
-
branch2: stack2,
|
|
115
|
-
divergencePoint,
|
|
116
|
-
conflicts,
|
|
117
|
-
commonAncestor,
|
|
118
|
-
visualRepresentation
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Process a branch for visualization
|
|
123
|
-
*/
|
|
124
|
-
processBranch(stack, parentId, xOffset, yStart) {
|
|
125
|
-
const nodes = [];
|
|
126
|
-
const edges = [];
|
|
127
|
-
let yPos = yStart;
|
|
128
|
-
const children = stack.frames.filter((f) => f.parent_frame_id === parentId);
|
|
129
|
-
for (const frame of children) {
|
|
130
|
-
nodes.push({
|
|
131
|
-
id: frame.frame_id,
|
|
132
|
-
type: xOffset < 0 ? "branch1" : "branch2",
|
|
133
|
-
frame,
|
|
134
|
-
position: { x: xOffset, y: yPos },
|
|
135
|
-
metadata: {
|
|
136
|
-
branch: xOffset < 0 ? "left" : "right",
|
|
137
|
-
depth: frame.depth
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
edges.push({
|
|
141
|
-
source: parentId,
|
|
142
|
-
target: frame.frame_id,
|
|
143
|
-
type: "parent",
|
|
144
|
-
weight: 1
|
|
145
|
-
});
|
|
146
|
-
const childResults = this.processBranch(
|
|
147
|
-
stack,
|
|
148
|
-
frame.frame_id,
|
|
149
|
-
xOffset + (xOffset < 0 ? -50 : 50),
|
|
150
|
-
yPos + 100
|
|
151
|
-
);
|
|
152
|
-
nodes.push(...childResults.nodes);
|
|
153
|
-
edges.push(...childResults.edges);
|
|
154
|
-
yPos += 150;
|
|
155
|
-
}
|
|
156
|
-
return { nodes, edges };
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Mark conflicts in the visualization
|
|
160
|
-
*/
|
|
161
|
-
markConflicts(nodes, edges, conflicts) {
|
|
162
|
-
for (const conflict of conflicts) {
|
|
163
|
-
const node1 = nodes.find((n) => n.id === conflict.frameId1);
|
|
164
|
-
const node2 = nodes.find((n) => n.id === conflict.frameId2);
|
|
165
|
-
if (node1) {
|
|
166
|
-
node1.type = "conflict";
|
|
167
|
-
node1.metadata = {
|
|
168
|
-
...node1.metadata,
|
|
169
|
-
conflictType: conflict.type,
|
|
170
|
-
severity: conflict.severity
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
if (node2) {
|
|
174
|
-
node2.type = "conflict";
|
|
175
|
-
node2.metadata = {
|
|
176
|
-
...node2.metadata,
|
|
177
|
-
conflictType: conflict.type,
|
|
178
|
-
severity: conflict.severity
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
if (node1 && node2) {
|
|
182
|
-
edges.push({
|
|
183
|
-
source: conflict.frameId1,
|
|
184
|
-
target: conflict.frameId2,
|
|
185
|
-
type: "conflict",
|
|
186
|
-
weight: this.getSeverityWeight(conflict.severity)
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Get color for severity level
|
|
193
|
-
*/
|
|
194
|
-
getSeverityColor(severity) {
|
|
195
|
-
switch (severity) {
|
|
196
|
-
case "critical":
|
|
197
|
-
return "#ff0000";
|
|
198
|
-
case "high":
|
|
199
|
-
return "#ff6600";
|
|
200
|
-
case "medium":
|
|
201
|
-
return "#ffaa00";
|
|
202
|
-
case "low":
|
|
203
|
-
return "#ffdd00";
|
|
204
|
-
default:
|
|
205
|
-
return "#888888";
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Get symbol for conflict type
|
|
210
|
-
*/
|
|
211
|
-
getConflictSymbol(type) {
|
|
212
|
-
switch (type) {
|
|
213
|
-
case "parallel_solution":
|
|
214
|
-
return "\u26A1";
|
|
215
|
-
case "conflicting_decision":
|
|
216
|
-
return "\u26A0\uFE0F";
|
|
217
|
-
case "structural_divergence":
|
|
218
|
-
return "\u{1F500}";
|
|
219
|
-
default:
|
|
220
|
-
return "\u2753";
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Get weight for severity level
|
|
225
|
-
*/
|
|
226
|
-
getSeverityWeight(severity) {
|
|
227
|
-
switch (severity) {
|
|
228
|
-
case "critical":
|
|
229
|
-
return 4;
|
|
230
|
-
case "high":
|
|
231
|
-
return 3;
|
|
232
|
-
case "medium":
|
|
233
|
-
return 2;
|
|
234
|
-
case "low":
|
|
235
|
-
return 1;
|
|
236
|
-
default:
|
|
237
|
-
return 0;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Preview keep both strategy
|
|
242
|
-
*/
|
|
243
|
-
previewKeepBoth(stack1, stack2, conflicts) {
|
|
244
|
-
const mergedFrames = [];
|
|
245
|
-
const keptFromStack1 = [];
|
|
246
|
-
const keptFromStack2 = [];
|
|
247
|
-
for (const frame of stack1.frames) {
|
|
248
|
-
mergedFrames.push(frame);
|
|
249
|
-
keptFromStack1.push(frame.frame_id);
|
|
250
|
-
}
|
|
251
|
-
for (const frame of stack2.frames) {
|
|
252
|
-
if (!mergedFrames.find((f) => f.frame_id === frame.frame_id)) {
|
|
253
|
-
mergedFrames.push(frame);
|
|
254
|
-
keptFromStack2.push(frame.frame_id);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
const criticalConflicts = conflicts.filter(
|
|
258
|
-
(c) => c.severity === "critical"
|
|
259
|
-
).length;
|
|
260
|
-
const estimatedSuccess = Math.max(0, 1 - criticalConflicts * 0.2);
|
|
261
|
-
return {
|
|
262
|
-
mergedFrames,
|
|
263
|
-
keptFromStack1,
|
|
264
|
-
keptFromStack2,
|
|
265
|
-
conflicts,
|
|
266
|
-
estimatedSuccess
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Preview team vote strategy
|
|
271
|
-
*/
|
|
272
|
-
previewTeamVote(stack1, stack2, conflicts) {
|
|
273
|
-
const mergedFrames = [];
|
|
274
|
-
const keptFromStack1 = [];
|
|
275
|
-
const keptFromStack2 = [];
|
|
276
|
-
const conflictingFrameIds = /* @__PURE__ */ new Set();
|
|
277
|
-
for (const conflict of conflicts) {
|
|
278
|
-
conflictingFrameIds.add(conflict.frameId1);
|
|
279
|
-
conflictingFrameIds.add(conflict.frameId2);
|
|
280
|
-
}
|
|
281
|
-
for (const frame of stack1.frames) {
|
|
282
|
-
if (!conflictingFrameIds.has(frame.frame_id)) {
|
|
283
|
-
mergedFrames.push(frame);
|
|
284
|
-
keptFromStack1.push(frame.frame_id);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
for (const frame of stack2.frames) {
|
|
288
|
-
if (!conflictingFrameIds.has(frame.frame_id) && !mergedFrames.find((f) => f.frame_id === frame.frame_id)) {
|
|
289
|
-
mergedFrames.push(frame);
|
|
290
|
-
keptFromStack2.push(frame.frame_id);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
let useStack1 = true;
|
|
294
|
-
for (const conflict of conflicts) {
|
|
295
|
-
if (useStack1) {
|
|
296
|
-
const frame = stack1.frames.find(
|
|
297
|
-
(f) => f.frame_id === conflict.frameId1
|
|
298
|
-
);
|
|
299
|
-
if (frame) {
|
|
300
|
-
mergedFrames.push(frame);
|
|
301
|
-
keptFromStack1.push(frame.frame_id);
|
|
302
|
-
}
|
|
303
|
-
} else {
|
|
304
|
-
const frame = stack2.frames.find(
|
|
305
|
-
(f) => f.frame_id === conflict.frameId2
|
|
306
|
-
);
|
|
307
|
-
if (frame) {
|
|
308
|
-
mergedFrames.push(frame);
|
|
309
|
-
keptFromStack2.push(frame.frame_id);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
useStack1 = !useStack1;
|
|
313
|
-
}
|
|
314
|
-
return {
|
|
315
|
-
mergedFrames,
|
|
316
|
-
keptFromStack1,
|
|
317
|
-
keptFromStack2,
|
|
318
|
-
conflicts,
|
|
319
|
-
estimatedSuccess: 0.75
|
|
320
|
-
// Team consensus usually works well
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Preview senior override strategy
|
|
325
|
-
*/
|
|
326
|
-
previewSeniorOverride(stack1, stack2, conflicts) {
|
|
327
|
-
const mergedFrames = [...stack1.frames];
|
|
328
|
-
const keptFromStack1 = stack1.frames.map((f) => f.frame_id);
|
|
329
|
-
const keptFromStack2 = [];
|
|
330
|
-
const stack1Ids = new Set(keptFromStack1);
|
|
331
|
-
for (const frame of stack2.frames) {
|
|
332
|
-
const hasConflict = conflicts.some((c) => c.frameId2 === frame.frame_id);
|
|
333
|
-
if (!hasConflict && !stack1Ids.has(frame.frame_id)) {
|
|
334
|
-
mergedFrames.push(frame);
|
|
335
|
-
keptFromStack2.push(frame.frame_id);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
return {
|
|
339
|
-
mergedFrames,
|
|
340
|
-
keptFromStack1,
|
|
341
|
-
keptFromStack2,
|
|
342
|
-
conflicts,
|
|
343
|
-
estimatedSuccess: 0.85
|
|
344
|
-
// Senior override is usually reliable
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* Preview AI suggest strategy
|
|
349
|
-
*/
|
|
350
|
-
previewAISuggest(stack1, stack2, conflicts) {
|
|
351
|
-
const mergedFrames = [];
|
|
352
|
-
const keptFromStack1 = [];
|
|
353
|
-
const keptFromStack2 = [];
|
|
354
|
-
for (const conflict of conflicts) {
|
|
355
|
-
const frame1 = stack1.frames.find(
|
|
356
|
-
(f) => f.frame_id === conflict.frameId1
|
|
357
|
-
);
|
|
358
|
-
const frame2 = stack2.frames.find(
|
|
359
|
-
(f) => f.frame_id === conflict.frameId2
|
|
360
|
-
);
|
|
361
|
-
if (frame1 && frame2) {
|
|
362
|
-
const score1 = this.scoreFrame(frame1);
|
|
363
|
-
const score2 = this.scoreFrame(frame2);
|
|
364
|
-
if (score1 >= score2) {
|
|
365
|
-
mergedFrames.push(frame1);
|
|
366
|
-
keptFromStack1.push(frame1.frame_id);
|
|
367
|
-
} else {
|
|
368
|
-
mergedFrames.push(frame2);
|
|
369
|
-
keptFromStack2.push(frame2.frame_id);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
this.addNonConflictingFrames(
|
|
374
|
-
stack1,
|
|
375
|
-
stack2,
|
|
376
|
-
conflicts,
|
|
377
|
-
mergedFrames,
|
|
378
|
-
keptFromStack1,
|
|
379
|
-
keptFromStack2
|
|
380
|
-
);
|
|
381
|
-
return {
|
|
382
|
-
mergedFrames,
|
|
383
|
-
keptFromStack1,
|
|
384
|
-
keptFromStack2,
|
|
385
|
-
conflicts,
|
|
386
|
-
estimatedSuccess: 0.9
|
|
387
|
-
// AI suggestions are usually optimal
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Preview hybrid strategy
|
|
392
|
-
*/
|
|
393
|
-
previewHybrid(stack1, stack2, conflicts) {
|
|
394
|
-
const mergedFrames = [];
|
|
395
|
-
const keptFromStack1 = [];
|
|
396
|
-
const keptFromStack2 = [];
|
|
397
|
-
for (const conflict of conflicts) {
|
|
398
|
-
if (conflict.type === "parallel_solution") {
|
|
399
|
-
const frame1 = stack1.frames.find(
|
|
400
|
-
(f) => f.frame_id === conflict.frameId1
|
|
401
|
-
);
|
|
402
|
-
const frame2 = stack2.frames.find(
|
|
403
|
-
(f) => f.frame_id === conflict.frameId2
|
|
404
|
-
);
|
|
405
|
-
if (frame1) {
|
|
406
|
-
mergedFrames.push(frame1);
|
|
407
|
-
keptFromStack1.push(frame1.frame_id);
|
|
408
|
-
}
|
|
409
|
-
if (frame2) {
|
|
410
|
-
mergedFrames.push(frame2);
|
|
411
|
-
keptFromStack2.push(frame2.frame_id);
|
|
412
|
-
}
|
|
413
|
-
} else if (conflict.type === "conflicting_decision") {
|
|
414
|
-
const frame1 = stack1.frames.find(
|
|
415
|
-
(f) => f.frame_id === conflict.frameId1
|
|
416
|
-
);
|
|
417
|
-
const frame2 = stack2.frames.find(
|
|
418
|
-
(f) => f.frame_id === conflict.frameId2
|
|
419
|
-
);
|
|
420
|
-
if (frame1 && frame2) {
|
|
421
|
-
const score1 = this.scoreFrame(frame1);
|
|
422
|
-
const score2 = this.scoreFrame(frame2);
|
|
423
|
-
if (score1 >= score2) {
|
|
424
|
-
mergedFrames.push(frame1);
|
|
425
|
-
keptFromStack1.push(frame1.frame_id);
|
|
426
|
-
} else {
|
|
427
|
-
mergedFrames.push(frame2);
|
|
428
|
-
keptFromStack2.push(frame2.frame_id);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
} else {
|
|
432
|
-
const frame1 = stack1.frames.find(
|
|
433
|
-
(f) => f.frame_id === conflict.frameId1
|
|
434
|
-
);
|
|
435
|
-
if (frame1) {
|
|
436
|
-
mergedFrames.push(frame1);
|
|
437
|
-
keptFromStack1.push(frame1.frame_id);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
this.addNonConflictingFrames(
|
|
442
|
-
stack1,
|
|
443
|
-
stack2,
|
|
444
|
-
conflicts,
|
|
445
|
-
mergedFrames,
|
|
446
|
-
keptFromStack1,
|
|
447
|
-
keptFromStack2
|
|
448
|
-
);
|
|
449
|
-
return {
|
|
450
|
-
mergedFrames,
|
|
451
|
-
keptFromStack1,
|
|
452
|
-
keptFromStack2,
|
|
453
|
-
conflicts,
|
|
454
|
-
estimatedSuccess: 0.88
|
|
455
|
-
// Hybrid is very effective
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Score a frame for quality
|
|
460
|
-
*/
|
|
461
|
-
scoreFrame(frame) {
|
|
462
|
-
let score = 0;
|
|
463
|
-
if (frame.state === "closed") score += 0.3;
|
|
464
|
-
if (frame.outputs && Object.keys(frame.outputs).length > 0) score += 0.2;
|
|
465
|
-
if (frame.digest_text) score += 0.2;
|
|
466
|
-
if (frame.closed_at && frame.created_at) {
|
|
467
|
-
const duration = frame.closed_at - frame.created_at;
|
|
468
|
-
if (duration < 6e5) score += 0.3;
|
|
469
|
-
}
|
|
470
|
-
return score;
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Add non-conflicting frames to merge
|
|
474
|
-
*/
|
|
475
|
-
addNonConflictingFrames(stack1, stack2, conflicts, mergedFrames, keptFromStack1, keptFromStack2) {
|
|
476
|
-
const conflictingIds = /* @__PURE__ */ new Set();
|
|
477
|
-
const mergedIds = new Set(mergedFrames.map((f) => f.frame_id));
|
|
478
|
-
for (const conflict of conflicts) {
|
|
479
|
-
conflictingIds.add(conflict.frameId1);
|
|
480
|
-
conflictingIds.add(conflict.frameId2);
|
|
481
|
-
}
|
|
482
|
-
for (const frame of stack1.frames) {
|
|
483
|
-
if (!conflictingIds.has(frame.frame_id) && !mergedIds.has(frame.frame_id)) {
|
|
484
|
-
mergedFrames.push(frame);
|
|
485
|
-
keptFromStack1.push(frame.frame_id);
|
|
486
|
-
mergedIds.add(frame.frame_id);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
for (const frame of stack2.frames) {
|
|
490
|
-
if (!conflictingIds.has(frame.frame_id) && !mergedIds.has(frame.frame_id)) {
|
|
491
|
-
mergedFrames.push(frame);
|
|
492
|
-
keptFromStack2.push(frame.frame_id);
|
|
493
|
-
mergedIds.add(frame.frame_id);
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Find divergence point between stacks
|
|
499
|
-
*/
|
|
500
|
-
findDivergencePoint(stack1, stack2) {
|
|
501
|
-
const events1 = stack1.events.sort((a, b) => a.ts - b.ts);
|
|
502
|
-
const events2 = stack2.events.sort((a, b) => a.ts - b.ts);
|
|
503
|
-
for (let i = 0; i < Math.min(events1.length, events2.length); i++) {
|
|
504
|
-
if (events1[i].event_id !== events2[i].event_id) {
|
|
505
|
-
return events1[i].ts;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return Math.min(
|
|
509
|
-
events1[events1.length - 1]?.ts || 0,
|
|
510
|
-
events2[events2.length - 1]?.ts || 0
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
/**
|
|
514
|
-
* Find common ancestor frame
|
|
515
|
-
*/
|
|
516
|
-
findCommonAncestor(stack1, stack2) {
|
|
517
|
-
const frames1 = new Set(stack1.frames.map((f) => f.frame_id));
|
|
518
|
-
let deepestCommon;
|
|
519
|
-
let maxDepth = -1;
|
|
520
|
-
for (const frame of stack2.frames) {
|
|
521
|
-
if (frames1.has(frame.frame_id) && frame.depth > maxDepth) {
|
|
522
|
-
deepestCommon = frame;
|
|
523
|
-
maxDepth = frame.depth;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return deepestCommon?.frame_id;
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
export {
|
|
530
|
-
StackDiffVisualizer
|
|
531
|
-
};
|