@stackmemoryai/stackmemory 1.10.5 → 1.14.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.
Files changed (225) hide show
  1. package/LICENSE +131 -64
  2. package/README.md +107 -24
  3. package/bin/claude-sm +16 -1
  4. package/bin/claude-smd +16 -1
  5. package/bin/codex-smd +16 -1
  6. package/bin/gemini-sm +16 -1
  7. package/bin/hermes-sm +21 -0
  8. package/bin/hermes-smd +21 -0
  9. package/bin/opencode-sm +16 -1
  10. package/dist/src/cli/claude-sm.js +266 -84
  11. package/dist/src/cli/codex-sm.js +225 -33
  12. package/dist/src/cli/commands/bench.js +209 -2
  13. package/dist/src/cli/commands/brain.js +206 -0
  14. package/dist/src/cli/commands/cache.js +126 -0
  15. package/dist/src/cli/commands/company-os.js +184 -0
  16. package/dist/src/cli/commands/context.js +5 -0
  17. package/dist/src/cli/commands/daemon.js +41 -0
  18. package/dist/src/cli/commands/handoff.js +40 -9
  19. package/dist/src/cli/commands/onboard.js +70 -3
  20. package/dist/src/cli/commands/operator.js +127 -0
  21. package/dist/src/cli/commands/optimize.js +117 -0
  22. package/dist/src/cli/commands/orchestrate.js +232 -5
  23. package/dist/src/cli/commands/orchestrator.js +315 -26
  24. package/dist/src/cli/commands/pack.js +322 -0
  25. package/dist/src/cli/commands/patterns.js +254 -0
  26. package/dist/src/cli/commands/portal.js +161 -0
  27. package/dist/src/cli/commands/scaffold.js +92 -0
  28. package/dist/src/cli/commands/search.js +40 -1
  29. package/dist/src/cli/commands/setup.js +178 -11
  30. package/dist/src/cli/commands/skills.js +10 -1
  31. package/dist/src/cli/commands/state.js +265 -0
  32. package/dist/src/cli/commands/sync.js +253 -0
  33. package/dist/src/cli/commands/tasks.js +130 -1
  34. package/dist/src/cli/commands/vision.js +221 -0
  35. package/dist/src/cli/gemini-sm.js +19 -29
  36. package/dist/src/cli/hermes-sm.js +224 -0
  37. package/dist/src/cli/index.js +105 -39
  38. package/dist/src/cli/opencode-sm.js +38 -21
  39. package/dist/src/cli/utils/determinism-watcher.js +66 -0
  40. package/dist/src/cli/utils/real-cli-bin.js +116 -0
  41. package/dist/src/core/brain/brain-store.js +187 -0
  42. package/dist/src/core/brain/brain-sync.js +193 -0
  43. package/dist/src/core/brain/index.js +78 -0
  44. package/dist/src/core/brain/types.js +10 -0
  45. package/dist/src/core/cache/content-cache.js +238 -0
  46. package/dist/src/{integrations/diffmem → core/cache}/index.js +5 -5
  47. package/dist/src/core/cache/token-estimator.js +39 -0
  48. package/dist/src/core/config/feature-flags.js +2 -6
  49. package/dist/src/core/context/frame-database.js +79 -27
  50. package/dist/src/core/context/recursive-context-manager.js +1 -1
  51. package/dist/src/core/context/rehydration.js +2 -1
  52. package/dist/src/core/cross-search/cross-project-search.js +269 -0
  53. package/dist/src/core/cross-search/index.js +10 -0
  54. package/dist/src/core/database/sqlite-adapter.js +14 -84
  55. package/dist/src/core/extensions/provider-adapter.js +5 -0
  56. package/dist/src/core/models/model-router.js +54 -2
  57. package/dist/src/core/models/provider-pricing.js +58 -4
  58. package/dist/src/core/monitoring/logger.js +2 -1
  59. package/dist/src/core/optimization/trace-optimizer.js +413 -0
  60. package/dist/src/core/patterns/index.js +22 -0
  61. package/dist/src/core/patterns/pattern-applier.js +39 -0
  62. package/dist/src/core/patterns/pattern-observer.js +157 -0
  63. package/dist/src/core/patterns/pattern-store.js +259 -0
  64. package/dist/src/core/patterns/types.js +19 -0
  65. package/dist/src/core/provenance/confidence-scorer.js +128 -0
  66. package/dist/src/core/provenance/index.js +40 -0
  67. package/dist/src/core/provenance/provenance-store.js +194 -0
  68. package/dist/src/core/provenance/types.js +82 -0
  69. package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
  70. package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
  71. package/dist/src/core/session/project-handoff.js +64 -0
  72. package/dist/src/core/session/session-manager.js +28 -0
  73. package/dist/src/core/shared-state/canonical-store.js +564 -0
  74. package/dist/src/core/skill-packs/index.js +18 -0
  75. package/dist/src/core/skill-packs/parser.js +42 -0
  76. package/dist/src/core/skill-packs/registry.js +224 -0
  77. package/dist/src/core/skill-packs/types.js +79 -0
  78. package/dist/src/core/storage/cloud-sync-manager.js +116 -0
  79. package/dist/src/core/storage/cloud-sync.js +574 -0
  80. package/dist/src/core/storage/two-tier-storage.js +5 -1
  81. package/dist/src/core/tasks/master-tasks-template.js +43 -0
  82. package/dist/src/core/tasks/md-task-parser.js +138 -0
  83. package/dist/src/core/trace/trace-event-store.js +282 -0
  84. package/dist/src/core/vision/index.js +27 -0
  85. package/dist/src/core/vision/signals.js +79 -0
  86. package/dist/src/core/vision/types.js +22 -0
  87. package/dist/src/core/vision/vision-file.js +146 -0
  88. package/dist/src/core/vision/vision-loop.js +220 -0
  89. package/dist/src/core/wiki/wiki-compiler.js +103 -1
  90. package/dist/src/daemon/daemon-config.js +52 -0
  91. package/dist/src/daemon/services/desire-path-service.js +566 -0
  92. package/dist/src/daemon/services/github-service.js +126 -0
  93. package/dist/src/daemon/services/research-stream-service.js +320 -0
  94. package/dist/src/daemon/services/telemetry-service.js +192 -0
  95. package/dist/src/daemon/unified-daemon.js +58 -1
  96. package/dist/src/features/browser/cli-browser-agent.js +417 -0
  97. package/dist/src/features/browser/stagehand-workflows.js +578 -0
  98. package/dist/src/features/operator/adapter-factory.js +62 -0
  99. package/dist/src/features/operator/browser-adapter.js +109 -0
  100. package/dist/src/features/operator/desktop-adapter.js +125 -0
  101. package/dist/src/features/operator/index.js +39 -0
  102. package/dist/src/features/operator/llm-decision.js +137 -0
  103. package/dist/src/features/operator/operator-logger.js +92 -0
  104. package/dist/src/features/operator/overnight-runner.js +327 -0
  105. package/dist/src/features/operator/screen-adapter.js +91 -0
  106. package/dist/src/features/operator/session-manager.js +127 -0
  107. package/dist/src/features/operator/state-machine.js +227 -0
  108. package/dist/src/features/operator/task-queue.js +81 -0
  109. package/dist/src/features/portal/index.js +26 -0
  110. package/dist/src/features/portal/server.js +240 -0
  111. package/dist/src/features/portal/types.js +14 -0
  112. package/dist/src/features/portal/ui.js +195 -0
  113. package/dist/src/features/sweep/pty-wrapper.js +13 -5
  114. package/dist/src/features/tasks/task-aware-context.js +2 -1
  115. package/dist/src/features/tui/simple-monitor.js +0 -23
  116. package/dist/src/features/tui/swarm-monitor.js +8 -66
  117. package/dist/src/features/web/client/hooks/use-socket.js +12 -0
  118. package/dist/src/{core/merge/index.js → features/web/client/lib/utils.js} +8 -4
  119. package/dist/src/features/web/client/next-env.d.js +4 -0
  120. package/dist/src/features/web/client/stores/session-store.js +12 -0
  121. package/dist/src/features/web/server/gcp-billing.js +76 -0
  122. package/dist/src/features/web/server/index.js +10 -0
  123. package/dist/src/features/web/server/spend-calculator.js +228 -0
  124. package/dist/src/hooks/schemas.js +6 -1
  125. package/dist/src/integrations/anthropic/client.js +3 -2
  126. package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
  127. package/dist/src/integrations/claude-code/subagent-client.js +307 -11
  128. package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
  129. package/dist/src/integrations/github/pr-state.js +158 -0
  130. package/dist/src/integrations/linear/client.js +4 -1
  131. package/dist/src/integrations/linear/webhook-retry.js +196 -0
  132. package/dist/src/integrations/linear/webhook-server.js +18 -22
  133. package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
  134. package/dist/src/integrations/mcp/handlers/index.js +40 -84
  135. package/dist/src/integrations/mcp/server.js +542 -641
  136. package/dist/src/integrations/mcp/tool-alias-registry.js +297 -0
  137. package/dist/src/integrations/mcp/tool-definitions.js +152 -682
  138. package/dist/src/mcp/stackmemory-mcp-server.js +571 -231
  139. package/dist/src/orchestrators/multimodal/determinism.js +244 -0
  140. package/dist/src/orchestrators/multimodal/harness.js +149 -78
  141. package/dist/src/orchestrators/multimodal/providers.js +44 -3
  142. package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
  143. package/dist/src/utils/hook-installer.js +0 -8
  144. package/dist/src/utils/process-cleanup.js +1 -7
  145. package/docs/README.md +42 -0
  146. package/docs/guides/README_INSTALL.md +208 -0
  147. package/package.json +27 -9
  148. package/packs/coding/python-fastapi/instructions.md +60 -0
  149. package/packs/coding/python-fastapi/pack.yaml +28 -0
  150. package/packs/coding/typescript-react/instructions.md +47 -0
  151. package/packs/coding/typescript-react/pack.yaml +28 -0
  152. package/packs/core/commands/capture.md +32 -0
  153. package/packs/core/commands/learn.md +73 -0
  154. package/packs/core/commands/next.md +36 -0
  155. package/packs/core/commands/restart.md +58 -0
  156. package/packs/core/commands/restore.md +29 -0
  157. package/packs/core/commands/start.md +57 -0
  158. package/packs/core/commands/stop.md +65 -0
  159. package/packs/core/commands/summary.md +40 -0
  160. package/packs/core/manifest.json +24 -0
  161. package/packs/ops/decision-recovery/instructions.md +65 -0
  162. package/packs/ops/decision-recovery/pack.yaml +89 -0
  163. package/scripts/claude-code-wrapper.sh +11 -0
  164. package/scripts/claude-sm-setup.sh +12 -1
  165. package/scripts/codex-wrapper.sh +11 -0
  166. package/scripts/git-hooks/branch-context-manager.sh +11 -0
  167. package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
  168. package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
  169. package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
  170. package/scripts/hooks/cleanup-shell.sh +12 -1
  171. package/scripts/hooks/task-complete.sh +12 -1
  172. package/scripts/install-code-execution-hooks.sh +12 -1
  173. package/scripts/install-sweep-hook.sh +12 -0
  174. package/scripts/install.sh +11 -0
  175. package/scripts/opencode-wrapper.sh +11 -0
  176. package/scripts/portal/cloud-init.yaml +69 -0
  177. package/scripts/portal/setup.sh +69 -0
  178. package/scripts/portal/stackmemory-portal.service +34 -0
  179. package/scripts/setup-claude-integration.sh +12 -1
  180. package/scripts/smoke-init-db.sh +23 -0
  181. package/scripts/stackmemory-daemon.sh +11 -0
  182. package/scripts/verify-dist.cjs +11 -4
  183. package/dist/src/cli/commands/ralph.js +0 -1053
  184. package/dist/src/cli/commands/team.js +0 -168
  185. package/dist/src/core/context/shared-context-layer.js +0 -620
  186. package/dist/src/core/context/stack-merge-resolver.js +0 -748
  187. package/dist/src/core/merge/conflict-detector.js +0 -430
  188. package/dist/src/core/merge/resolution-engine.js +0 -557
  189. package/dist/src/core/merge/stack-diff.js +0 -531
  190. package/dist/src/core/merge/unified-merge-resolver.js +0 -302
  191. package/dist/src/hooks/diffmem-hooks.js +0 -376
  192. package/dist/src/integrations/diffmem/client.js +0 -208
  193. package/dist/src/integrations/diffmem/config.js +0 -14
  194. package/dist/src/integrations/greptile/client.js +0 -101
  195. package/dist/src/integrations/greptile/config.js +0 -14
  196. package/dist/src/integrations/greptile/index.js +0 -11
  197. package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
  198. package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
  199. package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
  200. package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
  201. package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
  202. package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
  203. package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
  204. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -391
  205. package/dist/src/integrations/ralph/index.js +0 -17
  206. package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -435
  207. package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
  208. package/dist/src/integrations/ralph/loopmax.js +0 -488
  209. package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
  210. package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
  211. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
  212. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
  213. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
  214. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
  215. package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
  216. package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
  217. package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
  218. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
  219. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1007
  220. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
  221. package/scripts/ralph-loop-implementation.js +0 -404
  222. /package/dist/src/core/{merge → cache}/types.js +0 -0
  223. /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
  224. /package/dist/src/{integrations/greptile/types.js → core/trace/trace-event.js} +0 -0
  225. /package/dist/src/{integrations/ralph → features/operator}/types.js +0 -0
@@ -1,748 +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
- import { ValidationError, DatabaseError, ErrorCode } from "../errors/index.js";
7
- import {
8
- validateInput,
9
- StartMergeSessionSchema,
10
- CreateMergePolicySchema,
11
- ConflictResolutionSchema
12
- } from "./validation.js";
13
- class StackMergeResolver {
14
- dualStackManager;
15
- activeSessions = /* @__PURE__ */ new Map();
16
- mergePolicies = /* @__PURE__ */ new Map();
17
- constructor(dualStackManager) {
18
- this.dualStackManager = dualStackManager;
19
- this.initializeDefaultPolicies();
20
- logger.debug("StackMergeResolver initialized", {
21
- policies: Array.from(this.mergePolicies.keys())
22
- });
23
- }
24
- /**
25
- * Start a merge session with conflict analysis
26
- */
27
- async startMergeSession(sourceStackId, targetStackId, frameIds, policyName = "default") {
28
- const input = validateInput(StartMergeSessionSchema, {
29
- sourceStackId,
30
- targetStackId,
31
- frameIds,
32
- policyName
33
- });
34
- const sessionId = `merge-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
35
- logger.debug("Looking for merge policy", {
36
- policyName: input.policyName,
37
- availablePolicies: Array.from(this.mergePolicies.keys())
38
- });
39
- const policy = this.mergePolicies.get(input.policyName);
40
- if (!policy) {
41
- logger.error("Merge policy not found", {
42
- requested: input.policyName,
43
- available: Array.from(this.mergePolicies.keys())
44
- });
45
- throw new ValidationError(
46
- `Merge policy not found: ${input.policyName}`,
47
- ErrorCode.RESOURCE_NOT_FOUND
48
- );
49
- }
50
- try {
51
- const currentUserId = this.dualStackManager.getCurrentContext().ownerId || "unknown";
52
- await this.dualStackManager.getPermissionManager().enforcePermission(
53
- this.dualStackManager.getPermissionManager().createContext(currentUserId, "merge", "stack", input.sourceStackId)
54
- );
55
- await this.dualStackManager.getPermissionManager().enforcePermission(
56
- this.dualStackManager.getPermissionManager().createContext(currentUserId, "merge", "stack", input.targetStackId)
57
- );
58
- const session = {
59
- sessionId,
60
- sourceStackId: input.sourceStackId,
61
- targetStackId: input.targetStackId,
62
- conflicts: [],
63
- resolutions: [],
64
- policy,
65
- status: "analyzing",
66
- startedAt: /* @__PURE__ */ new Date(),
67
- metadata: {
68
- totalFrames: 0,
69
- conflictFrames: 0,
70
- autoResolvedConflicts: 0,
71
- manualResolvedConflicts: 0
72
- }
73
- };
74
- this.activeSessions.set(sessionId, session);
75
- await this.analyzeConflicts(sessionId, frameIds);
76
- await this.autoResolveConflicts(sessionId);
77
- logger.info(`Merge session started: ${sessionId}`, {
78
- sourceStack: sourceStackId,
79
- targetStack: targetStackId,
80
- conflicts: session.conflicts.length,
81
- policy: policyName
82
- });
83
- return sessionId;
84
- } catch (error) {
85
- logger.error("Failed to start merge session", {
86
- error: error instanceof Error ? error.message : error,
87
- sourceStackId: input.sourceStackId,
88
- targetStackId: input.targetStackId,
89
- policyName: input.policyName
90
- });
91
- throw new DatabaseError(
92
- "Failed to start merge session",
93
- ErrorCode.OPERATION_FAILED,
94
- { sourceStackId, targetStackId },
95
- error instanceof Error ? error : void 0
96
- );
97
- }
98
- }
99
- /**
100
- * Analyze conflicts between source and target stacks
101
- */
102
- async analyzeConflicts(sessionId, frameIds) {
103
- const session = this.activeSessions.get(sessionId);
104
- if (!session) {
105
- throw new DatabaseError(
106
- `Merge session not found: ${sessionId}`,
107
- ErrorCode.RESOURCE_NOT_FOUND
108
- );
109
- }
110
- try {
111
- const sourceStack = this.getStackManager(session.sourceStackId);
112
- const targetStack = this.getStackManager(session.targetStackId);
113
- const framesToAnalyze = frameIds || (await sourceStack.getActiveFrames()).map((f) => f.frame_id);
114
- session.metadata.totalFrames = framesToAnalyze.length;
115
- for (const frameId of framesToAnalyze) {
116
- const sourceFrame = await sourceStack.getFrame(frameId);
117
- if (!sourceFrame) continue;
118
- const targetFrame = await targetStack.getFrame(frameId);
119
- if (!targetFrame) continue;
120
- const conflicts = await this.analyzeFrameConflicts(
121
- sourceFrame,
122
- targetFrame
123
- );
124
- session.conflicts.push(...conflicts);
125
- }
126
- session.metadata.conflictFrames = new Set(
127
- session.conflicts.map((c) => c.frameId)
128
- ).size;
129
- session.status = "resolving";
130
- this.activeSessions.set(sessionId, session);
131
- logger.info(`Conflict analysis completed: ${sessionId}`, {
132
- totalConflicts: session.conflicts.length,
133
- conflictFrames: session.metadata.conflictFrames
134
- });
135
- } catch (error) {
136
- session.status = "failed";
137
- this.activeSessions.set(sessionId, session);
138
- throw error;
139
- }
140
- }
141
- /**
142
- * Analyze conflicts within a single frame
143
- */
144
- async analyzeFrameConflicts(sourceFrame, targetFrame) {
145
- const conflicts = [];
146
- if (sourceFrame.name !== targetFrame.name) {
147
- conflicts.push({
148
- frameId: sourceFrame.frame_id,
149
- conflictType: "content",
150
- sourceFrame,
151
- targetFrame,
152
- conflictDetails: [
153
- {
154
- field: "name",
155
- sourceValue: sourceFrame.name,
156
- targetValue: targetFrame.name,
157
- lastModified: {
158
- source: new Date(sourceFrame.created_at * 1e3),
159
- target: new Date(targetFrame.created_at * 1e3)
160
- }
161
- }
162
- ],
163
- severity: "medium",
164
- autoResolvable: false
165
- });
166
- }
167
- if (sourceFrame.state !== targetFrame.state) {
168
- conflicts.push({
169
- frameId: sourceFrame.frame_id,
170
- conflictType: "metadata",
171
- sourceFrame,
172
- targetFrame,
173
- conflictDetails: [
174
- {
175
- field: "state",
176
- sourceValue: sourceFrame.state,
177
- targetValue: targetFrame.state,
178
- lastModified: {
179
- source: new Date(sourceFrame.created_at * 1e3),
180
- target: new Date(targetFrame.created_at * 1e3)
181
- }
182
- }
183
- ],
184
- severity: "high",
185
- autoResolvable: true
186
- // Can auto-resolve based on timestamps
187
- });
188
- }
189
- if (JSON.stringify(sourceFrame.inputs) !== JSON.stringify(targetFrame.inputs)) {
190
- conflicts.push({
191
- frameId: sourceFrame.frame_id,
192
- conflictType: "content",
193
- sourceFrame,
194
- targetFrame,
195
- conflictDetails: [
196
- {
197
- field: "inputs",
198
- sourceValue: sourceFrame.inputs,
199
- targetValue: targetFrame.inputs,
200
- lastModified: {
201
- source: new Date(sourceFrame.created_at * 1e3),
202
- target: new Date(targetFrame.created_at * 1e3)
203
- }
204
- }
205
- ],
206
- severity: "medium",
207
- autoResolvable: false
208
- });
209
- }
210
- const eventConflicts = await this.analyzeEventConflicts(
211
- sourceFrame,
212
- targetFrame
213
- );
214
- conflicts.push(...eventConflicts);
215
- const anchorConflicts = await this.analyzeAnchorConflicts(
216
- sourceFrame,
217
- targetFrame
218
- );
219
- conflicts.push(...anchorConflicts);
220
- return conflicts;
221
- }
222
- /**
223
- * Analyze conflicts in frame events
224
- */
225
- async analyzeEventConflicts(sourceFrame, targetFrame) {
226
- const conflicts = [];
227
- try {
228
- const sourceStack = this.getStackManager(sourceFrame.project_id);
229
- const targetStack = this.getStackManager(targetFrame.project_id);
230
- const sourceEvents = await sourceStack.getFrameEvents(
231
- sourceFrame.frame_id
232
- );
233
- const targetEvents = await targetStack.getFrameEvents(
234
- targetFrame.frame_id
235
- );
236
- if (sourceEvents.length !== targetEvents.length) {
237
- conflicts.push({
238
- frameId: sourceFrame.frame_id,
239
- conflictType: "sequence",
240
- sourceFrame,
241
- targetFrame,
242
- conflictDetails: [
243
- {
244
- field: "event_count",
245
- sourceValue: sourceEvents.length,
246
- targetValue: targetEvents.length,
247
- lastModified: {
248
- source: /* @__PURE__ */ new Date(),
249
- target: /* @__PURE__ */ new Date()
250
- }
251
- }
252
- ],
253
- severity: "high",
254
- autoResolvable: true
255
- // Can merge events
256
- });
257
- }
258
- const minLength = Math.min(sourceEvents.length, targetEvents.length);
259
- for (let i = 0; i < minLength; i++) {
260
- const sourceEvent = sourceEvents[i];
261
- const targetEvent = targetEvents[i];
262
- if (sourceEvent.text !== targetEvent.text || JSON.stringify(sourceEvent.metadata) !== JSON.stringify(targetEvent.metadata)) {
263
- conflicts.push({
264
- frameId: sourceFrame.frame_id,
265
- conflictType: "content",
266
- sourceFrame,
267
- targetFrame,
268
- conflictDetails: [
269
- {
270
- field: `event_${i}`,
271
- sourceValue: {
272
- text: sourceEvent.text,
273
- metadata: sourceEvent.metadata
274
- },
275
- targetValue: {
276
- text: targetEvent.text,
277
- metadata: targetEvent.metadata
278
- },
279
- lastModified: {
280
- source: /* @__PURE__ */ new Date(),
281
- target: /* @__PURE__ */ new Date()
282
- }
283
- }
284
- ],
285
- severity: "medium",
286
- autoResolvable: false
287
- });
288
- }
289
- }
290
- } catch (error) {
291
- logger.warn(
292
- `Failed to analyze event conflicts for frame: ${sourceFrame.frame_id}`,
293
- error
294
- );
295
- }
296
- return conflicts;
297
- }
298
- /**
299
- * Analyze conflicts in frame anchors
300
- */
301
- async analyzeAnchorConflicts(sourceFrame, targetFrame) {
302
- const conflicts = [];
303
- try {
304
- const sourceStack = this.getStackManager(sourceFrame.project_id);
305
- const targetStack = this.getStackManager(targetFrame.project_id);
306
- const sourceAnchors = await sourceStack.getFrameAnchors(
307
- sourceFrame.frame_id
308
- );
309
- const targetAnchors = await targetStack.getFrameAnchors(
310
- targetFrame.frame_id
311
- );
312
- const sourceAnchorsByType = this.groupAnchorsByType(sourceAnchors);
313
- const targetAnchorsByType = this.groupAnchorsByType(targetAnchors);
314
- const allTypes = /* @__PURE__ */ new Set([
315
- ...Object.keys(sourceAnchorsByType),
316
- ...Object.keys(targetAnchorsByType)
317
- ]);
318
- for (const type of allTypes) {
319
- const sourceTypeAnchors = sourceAnchorsByType[type] || [];
320
- const targetTypeAnchors = targetAnchorsByType[type] || [];
321
- if (sourceTypeAnchors.length !== targetTypeAnchors.length || !this.anchorsEqual(sourceTypeAnchors, targetTypeAnchors)) {
322
- conflicts.push({
323
- frameId: sourceFrame.frame_id,
324
- conflictType: "content",
325
- sourceFrame,
326
- targetFrame,
327
- conflictDetails: [
328
- {
329
- field: `anchors_${type}`,
330
- sourceValue: sourceTypeAnchors,
331
- targetValue: targetTypeAnchors,
332
- lastModified: {
333
- source: /* @__PURE__ */ new Date(),
334
- target: /* @__PURE__ */ new Date()
335
- }
336
- }
337
- ],
338
- severity: "low",
339
- autoResolvable: true
340
- // Can merge anchors
341
- });
342
- }
343
- }
344
- } catch (error) {
345
- logger.warn(
346
- `Failed to analyze anchor conflicts for frame: ${sourceFrame.frame_id}`,
347
- error
348
- );
349
- }
350
- return conflicts;
351
- }
352
- /**
353
- * Auto-resolve conflicts based on merge policy
354
- */
355
- async autoResolveConflicts(sessionId) {
356
- const session = this.activeSessions.get(sessionId);
357
- if (!session) return;
358
- const autoResolvableConflicts = session.conflicts.filter(
359
- (c) => c.autoResolvable
360
- );
361
- for (const conflict of autoResolvableConflicts) {
362
- const resolution = await this.applyMergePolicy(conflict, session.policy);
363
- if (resolution) {
364
- session.resolutions.push(resolution);
365
- session.metadata.autoResolvedConflicts++;
366
- logger.debug(`Auto-resolved conflict: ${conflict.frameId}`, {
367
- type: conflict.conflictType,
368
- strategy: resolution.strategy
369
- });
370
- }
371
- }
372
- const remainingConflicts = session.conflicts.filter(
373
- (c) => !session.resolutions.find((r) => r.conflictId === c.frameId)
374
- );
375
- if (remainingConflicts.length === 0) {
376
- session.status = "completed";
377
- session.completedAt = /* @__PURE__ */ new Date();
378
- } else if (remainingConflicts.every((c) => !c.autoResolvable)) {
379
- session.status = "manual_review";
380
- }
381
- this.activeSessions.set(sessionId, session);
382
- }
383
- /**
384
- * Apply merge policy to resolve conflicts automatically
385
- */
386
- async applyMergePolicy(conflict, policy) {
387
- const sortedRules = policy.rules.sort((a, b) => b.priority - a.priority);
388
- for (const rule of sortedRules) {
389
- if (this.evaluateRuleCondition(conflict, rule.condition)) {
390
- return {
391
- conflictId: conflict.frameId,
392
- strategy: rule.action === "require_manual" ? "manual" : rule.action,
393
- resolvedBy: "system",
394
- resolvedAt: /* @__PURE__ */ new Date(),
395
- notes: `Auto-resolved by policy: ${policy.name}`
396
- };
397
- }
398
- }
399
- return null;
400
- }
401
- /**
402
- * Manually resolve a specific conflict
403
- */
404
- async resolveConflict(sessionId, conflictId, resolution) {
405
- const input = validateInput(ConflictResolutionSchema, {
406
- strategy: resolution.strategy,
407
- resolvedBy: resolution.resolvedBy,
408
- notes: resolution.notes
409
- });
410
- const session = this.activeSessions.get(sessionId);
411
- if (!session) {
412
- throw new ValidationError(
413
- `Merge session not found: ${sessionId}`,
414
- ErrorCode.MERGE_SESSION_INVALID
415
- );
416
- }
417
- const conflict = session.conflicts.find((c) => c.frameId === conflictId);
418
- if (!conflict) {
419
- throw new ValidationError(
420
- `Conflict not found: ${conflictId}`,
421
- ErrorCode.MERGE_CONFLICT_UNRESOLVABLE
422
- );
423
- }
424
- const fullResolution = {
425
- ...input,
426
- conflictId,
427
- resolvedAt: /* @__PURE__ */ new Date()
428
- };
429
- session.resolutions.push(fullResolution);
430
- session.metadata.manualResolvedConflicts++;
431
- const resolvedConflictIds = new Set(
432
- session.resolutions.map((r) => r.conflictId)
433
- );
434
- const allResolved = session.conflicts.every(
435
- (c) => resolvedConflictIds.has(c.frameId)
436
- );
437
- if (allResolved) {
438
- session.status = "completed";
439
- session.completedAt = /* @__PURE__ */ new Date();
440
- }
441
- this.activeSessions.set(sessionId, session);
442
- logger.info(`Conflict manually resolved: ${conflictId}`, {
443
- strategy: resolution.strategy,
444
- resolvedBy: resolution.resolvedBy
445
- });
446
- }
447
- /**
448
- * Execute merge with resolved conflicts
449
- */
450
- async executeMerge(sessionId) {
451
- const session = this.activeSessions.get(sessionId);
452
- if (!session) {
453
- throw new DatabaseError(
454
- `Merge session not found: ${sessionId}`,
455
- ErrorCode.RESOURCE_NOT_FOUND
456
- );
457
- }
458
- if (session.status !== "completed") {
459
- throw new DatabaseError(
460
- `Merge session not ready for execution: ${session.status}`,
461
- ErrorCode.INVALID_STATE
462
- );
463
- }
464
- try {
465
- const resolutionMap = new Map(
466
- session.resolutions.map((r) => [r.conflictId, r])
467
- );
468
- const result = {
469
- success: true,
470
- conflictFrames: [],
471
- mergedFrames: [],
472
- errors: []
473
- };
474
- const sourceStack = this.getStackManager(session.sourceStackId);
475
- const targetStack = this.getStackManager(session.targetStackId);
476
- const conflictsByFrame = /* @__PURE__ */ new Map();
477
- for (const conflict of session.conflicts) {
478
- const existing = conflictsByFrame.get(conflict.frameId) || [];
479
- existing.push(conflict);
480
- conflictsByFrame.set(conflict.frameId, existing);
481
- }
482
- for (const [frameId] of conflictsByFrame) {
483
- try {
484
- const resolution = resolutionMap.get(frameId);
485
- if (!resolution) {
486
- result.errors.push({
487
- frameId,
488
- error: "No resolution found",
489
- resolution: "skipped"
490
- });
491
- result.conflictFrames.push(frameId);
492
- continue;
493
- }
494
- const sourceFrame = await sourceStack.getFrame(frameId);
495
- const targetFrame = await targetStack.getFrame(frameId);
496
- if (!sourceFrame) {
497
- result.errors.push({
498
- frameId,
499
- error: "Source frame not found",
500
- resolution: "skipped"
501
- });
502
- continue;
503
- }
504
- switch (resolution.strategy) {
505
- case "source_wins":
506
- if (targetFrame) await targetStack.deleteFrame(frameId);
507
- await this.copyFrameToStack(
508
- sourceFrame,
509
- sourceStack,
510
- targetStack
511
- );
512
- result.mergedFrames.push(frameId);
513
- break;
514
- case "target_wins":
515
- result.mergedFrames.push(frameId);
516
- break;
517
- case "merge_both":
518
- if (targetFrame) {
519
- await this.mergeFrameContents(
520
- sourceFrame,
521
- targetFrame,
522
- sourceStack,
523
- targetStack
524
- );
525
- } else {
526
- await this.copyFrameToStack(
527
- sourceFrame,
528
- sourceStack,
529
- targetStack
530
- );
531
- }
532
- result.mergedFrames.push(frameId);
533
- break;
534
- case "skip":
535
- result.conflictFrames.push(frameId);
536
- break;
537
- case "manual":
538
- result.errors.push({
539
- frameId,
540
- error: "Manual resolution not applied",
541
- resolution: "skipped"
542
- });
543
- result.conflictFrames.push(frameId);
544
- break;
545
- }
546
- } catch (error) {
547
- result.errors.push({
548
- frameId,
549
- error: error instanceof Error ? error.message : String(error),
550
- resolution: "skipped"
551
- });
552
- result.success = false;
553
- }
554
- }
555
- logger.info(`Merge executed: ${sessionId}`, {
556
- mergedFrames: result.mergedFrames.length,
557
- conflicts: result.conflictFrames.length,
558
- errors: result.errors.length
559
- });
560
- return result;
561
- } catch (error) {
562
- throw new DatabaseError(
563
- "Failed to execute merge",
564
- ErrorCode.OPERATION_FAILED,
565
- { sessionId },
566
- error instanceof Error ? error : void 0
567
- );
568
- }
569
- }
570
- async copyFrameToStack(frame, sourceStack, targetStack) {
571
- await targetStack.createFrame({
572
- frame_id: frame.frame_id,
573
- name: frame.name,
574
- type: frame.type,
575
- parent_frame_id: frame.parent_frame_id,
576
- inputs: frame.inputs,
577
- outputs: frame.outputs
578
- });
579
- const events = await sourceStack.getFrameEvents(frame.frame_id);
580
- for (const event of events) {
581
- await targetStack.addEvent(frame.frame_id, {
582
- type: event.type,
583
- text: event.text,
584
- metadata: event.metadata
585
- });
586
- }
587
- const anchors = await sourceStack.getFrameAnchors(frame.frame_id);
588
- for (const anchor of anchors) {
589
- await targetStack.addAnchor(frame.frame_id, {
590
- type: anchor.type,
591
- text: anchor.text,
592
- priority: anchor.priority,
593
- metadata: anchor.metadata
594
- });
595
- }
596
- }
597
- async mergeFrameContents(sourceFrame, targetFrame, sourceStack, targetStack) {
598
- const sourceEvents = await sourceStack.getFrameEvents(sourceFrame.frame_id);
599
- const targetEvents = await targetStack.getFrameEvents(targetFrame.frame_id);
600
- const existingSignatures = new Set(
601
- targetEvents.map((e) => `${e.type}:${e.text}`)
602
- );
603
- for (const event of sourceEvents) {
604
- const sig = `${event.type}:${event.text}`;
605
- if (!existingSignatures.has(sig)) {
606
- await targetStack.addEvent(targetFrame.frame_id, {
607
- type: event.type,
608
- text: event.text,
609
- metadata: { ...event.metadata, merged: true }
610
- });
611
- existingSignatures.add(sig);
612
- }
613
- }
614
- const sourceAnchors = await sourceStack.getFrameAnchors(
615
- sourceFrame.frame_id
616
- );
617
- const targetAnchors = await targetStack.getFrameAnchors(
618
- targetFrame.frame_id
619
- );
620
- const existingAnchorSigs = new Set(
621
- targetAnchors.map((a) => `${a.type}:${a.text}:${a.priority}`)
622
- );
623
- for (const anchor of sourceAnchors) {
624
- const sig = `${anchor.type}:${anchor.text}:${anchor.priority}`;
625
- if (!existingAnchorSigs.has(sig)) {
626
- await targetStack.addAnchor(targetFrame.frame_id, {
627
- type: anchor.type,
628
- text: anchor.text,
629
- priority: anchor.priority,
630
- metadata: { ...anchor.metadata, merged: true }
631
- });
632
- existingAnchorSigs.add(sig);
633
- }
634
- }
635
- logger.debug(`Merged frame contents: ${sourceFrame.frame_id}`);
636
- }
637
- /**
638
- * Get merge session details
639
- */
640
- async getMergeSession(sessionId) {
641
- return this.activeSessions.get(sessionId) || null;
642
- }
643
- /**
644
- * Create custom merge policy
645
- */
646
- async createMergePolicy(policy) {
647
- const input = validateInput(CreateMergePolicySchema, policy);
648
- this.mergePolicies.set(input.name, input);
649
- logger.info(`Created merge policy: ${input.name}`, {
650
- rules: input.rules.length,
651
- autoApplyThreshold: input.autoApplyThreshold
652
- });
653
- }
654
- /**
655
- * Initialize default merge policies
656
- */
657
- initializeDefaultPolicies() {
658
- this.mergePolicies.set("conservative", {
659
- name: "conservative",
660
- description: "Prefer manual resolution for most conflicts",
661
- rules: [
662
- {
663
- condition: '$.conflictType == "metadata" && $.severity == "low"',
664
- action: "target_wins",
665
- priority: 1
666
- },
667
- {
668
- condition: '$.severity == "critical"',
669
- action: "require_manual",
670
- priority: 10
671
- }
672
- ],
673
- autoApplyThreshold: "never"
674
- });
675
- this.mergePolicies.set("aggressive", {
676
- name: "aggressive",
677
- description: "Auto-resolve conflicts when safe",
678
- rules: [
679
- {
680
- condition: '$.conflictType == "sequence"',
681
- action: "merge_both",
682
- priority: 5
683
- },
684
- {
685
- condition: '$.severity == "low"',
686
- action: "source_wins",
687
- priority: 2
688
- },
689
- {
690
- condition: '$.severity == "medium" && $.autoResolvable',
691
- action: "merge_both",
692
- priority: 4
693
- }
694
- ],
695
- autoApplyThreshold: "medium"
696
- });
697
- this.mergePolicies.set("default", {
698
- name: "default",
699
- description: "Balanced conflict resolution",
700
- rules: [
701
- {
702
- condition: '$.conflictType == "sequence" && $.severity == "low"',
703
- action: "merge_both",
704
- priority: 3
705
- },
706
- {
707
- condition: '$.conflictType == "metadata" && $.autoResolvable',
708
- action: "target_wins",
709
- priority: 2
710
- },
711
- {
712
- condition: '$.severity == "critical"',
713
- action: "require_manual",
714
- priority: 10
715
- }
716
- ],
717
- autoApplyThreshold: "low"
718
- });
719
- }
720
- // Helper methods
721
- getStackManager(stackId) {
722
- return this.dualStackManager.getStackManager(stackId);
723
- }
724
- groupAnchorsByType(anchors) {
725
- return anchors.reduce(
726
- (groups, anchor) => {
727
- if (!groups[anchor.type]) groups[anchor.type] = [];
728
- groups[anchor.type].push(anchor);
729
- return groups;
730
- },
731
- {}
732
- );
733
- }
734
- anchorsEqual(anchors1, anchors2) {
735
- if (anchors1.length !== anchors2.length) return false;
736
- const sorted1 = [...anchors1].sort((a, b) => a.text.localeCompare(b.text));
737
- const sorted2 = [...anchors2].sort((a, b) => a.text.localeCompare(b.text));
738
- return sorted1.every(
739
- (anchor, i) => anchor.text === sorted2[i].text && anchor.priority === sorted2[i].priority
740
- );
741
- }
742
- evaluateRuleCondition(conflict, condition) {
743
- return condition.includes(conflict.conflictType) || condition.includes(conflict.severity);
744
- }
745
- }
746
- export {
747
- StackMergeResolver
748
- };