@co-engram/core 0.1.0 → 0.1.1

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 (113) hide show
  1. package/dist/i18n/en.d.ts.map +1 -1
  2. package/dist/i18n/en.js +1 -0
  3. package/dist/i18n/en.js.map +1 -1
  4. package/dist/i18n/zh.d.ts +1 -0
  5. package/dist/i18n/zh.d.ts.map +1 -1
  6. package/dist/i18n/zh.js +1 -0
  7. package/dist/i18n/zh.js.map +1 -1
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/merge/anomaly-detector.d.ts +63 -0
  13. package/dist/merge/anomaly-detector.d.ts.map +1 -0
  14. package/dist/merge/anomaly-detector.js +128 -0
  15. package/dist/merge/anomaly-detector.js.map +1 -0
  16. package/dist/merge/arbitration.d.ts +18 -0
  17. package/dist/merge/arbitration.d.ts.map +1 -0
  18. package/dist/merge/arbitration.js +27 -0
  19. package/dist/merge/arbitration.js.map +1 -0
  20. package/dist/merge/auto-onboard.d.ts +56 -0
  21. package/dist/merge/auto-onboard.d.ts.map +1 -0
  22. package/dist/merge/auto-onboard.js +81 -0
  23. package/dist/merge/auto-onboard.js.map +1 -0
  24. package/dist/merge/backup.d.ts +27 -0
  25. package/dist/merge/backup.d.ts.map +1 -0
  26. package/dist/merge/backup.js +49 -0
  27. package/dist/merge/backup.js.map +1 -0
  28. package/dist/merge/content.d.ts +50 -0
  29. package/dist/merge/content.d.ts.map +1 -0
  30. package/dist/merge/content.js +137 -0
  31. package/dist/merge/content.js.map +1 -0
  32. package/dist/merge/cross-file-coordinator.d.ts +52 -0
  33. package/dist/merge/cross-file-coordinator.d.ts.map +1 -0
  34. package/dist/merge/cross-file-coordinator.js +222 -0
  35. package/dist/merge/cross-file-coordinator.js.map +1 -0
  36. package/dist/merge/data-root.d.ts +9 -0
  37. package/dist/merge/data-root.d.ts.map +1 -0
  38. package/dist/merge/data-root.js +42 -0
  39. package/dist/merge/data-root.js.map +1 -0
  40. package/dist/merge/driver-llm.d.ts +92 -0
  41. package/dist/merge/driver-llm.d.ts.map +1 -0
  42. package/dist/merge/driver-llm.js +174 -0
  43. package/dist/merge/driver-llm.js.map +1 -0
  44. package/dist/merge/driver-main.d.ts +29 -0
  45. package/dist/merge/driver-main.d.ts.map +1 -0
  46. package/dist/merge/driver-main.js +220 -0
  47. package/dist/merge/driver-main.js.map +1 -0
  48. package/dist/merge/evidence-union.d.ts +35 -0
  49. package/dist/merge/evidence-union.d.ts.map +1 -0
  50. package/dist/merge/evidence-union.js +88 -0
  51. package/dist/merge/evidence-union.js.map +1 -0
  52. package/dist/merge/frontmatter-rules.d.ts +38 -0
  53. package/dist/merge/frontmatter-rules.d.ts.map +1 -0
  54. package/dist/merge/frontmatter-rules.js +77 -0
  55. package/dist/merge/frontmatter-rules.js.map +1 -0
  56. package/dist/merge/frontmatter.d.ts +52 -0
  57. package/dist/merge/frontmatter.d.ts.map +1 -0
  58. package/dist/merge/frontmatter.js +211 -0
  59. package/dist/merge/frontmatter.js.map +1 -0
  60. package/dist/merge/index.d.ts +31 -0
  61. package/dist/merge/index.d.ts.map +1 -0
  62. package/dist/merge/index.js +31 -0
  63. package/dist/merge/index.js.map +1 -0
  64. package/dist/merge/llm-arbiter.d.ts +85 -0
  65. package/dist/merge/llm-arbiter.d.ts.map +1 -0
  66. package/dist/merge/llm-arbiter.js +177 -0
  67. package/dist/merge/llm-arbiter.js.map +1 -0
  68. package/dist/merge/llm-contract.d.ts +86 -0
  69. package/dist/merge/llm-contract.d.ts.map +1 -0
  70. package/dist/merge/llm-contract.js +134 -0
  71. package/dist/merge/llm-contract.js.map +1 -0
  72. package/dist/merge/llm-prompt.d.ts +25 -0
  73. package/dist/merge/llm-prompt.d.ts.map +1 -0
  74. package/dist/merge/llm-prompt.js +73 -0
  75. package/dist/merge/llm-prompt.js.map +1 -0
  76. package/dist/merge/merge-engram.d.ts +33 -0
  77. package/dist/merge/merge-engram.d.ts.map +1 -0
  78. package/dist/merge/merge-engram.js +164 -0
  79. package/dist/merge/merge-engram.js.map +1 -0
  80. package/dist/merge/merge-stats.d.ts +68 -0
  81. package/dist/merge/merge-stats.d.ts.map +1 -0
  82. package/dist/merge/merge-stats.js +152 -0
  83. package/dist/merge/merge-stats.js.map +1 -0
  84. package/dist/merge/onboard.d.ts +72 -0
  85. package/dist/merge/onboard.d.ts.map +1 -0
  86. package/dist/merge/onboard.js +146 -0
  87. package/dist/merge/onboard.js.map +1 -0
  88. package/dist/merge/post-merge-hook.d.ts +77 -0
  89. package/dist/merge/post-merge-hook.d.ts.map +1 -0
  90. package/dist/merge/post-merge-hook.js +211 -0
  91. package/dist/merge/post-merge-hook.js.map +1 -0
  92. package/dist/merge/resolution-state.d.ts +41 -0
  93. package/dist/merge/resolution-state.d.ts.map +1 -0
  94. package/dist/merge/resolution-state.js +100 -0
  95. package/dist/merge/resolution-state.js.map +1 -0
  96. package/dist/merge/synapse-merger.d.ts +63 -0
  97. package/dist/merge/synapse-merger.d.ts.map +1 -0
  98. package/dist/merge/synapse-merger.js +277 -0
  99. package/dist/merge/synapse-merger.js.map +1 -0
  100. package/dist/merge/synapse-rules.d.ts +66 -0
  101. package/dist/merge/synapse-rules.d.ts.map +1 -0
  102. package/dist/merge/synapse-rules.js +112 -0
  103. package/dist/merge/synapse-rules.js.map +1 -0
  104. package/dist/merge/version.d.ts +10 -0
  105. package/dist/merge/version.d.ts.map +1 -0
  106. package/dist/merge/version.js +10 -0
  107. package/dist/merge/version.js.map +1 -0
  108. package/dist/merge-driver.cjs +9371 -0
  109. package/dist/observability/audit-log.d.ts +4 -1
  110. package/dist/observability/audit-log.d.ts.map +1 -1
  111. package/dist/observability/audit-log.js +3 -0
  112. package/dist/observability/audit-log.js.map +1 -1
  113. package/package.json +6 -2
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Engram 文件合并入口
3
+ *
4
+ * 组装 frontmatter 合并 + content 合并 + 收尾(contentHash / contentSize 重算,
5
+ * updatedAt 取 max, version 取 max+1, updatedBy='merge-driver')。
6
+ * 失败模式:任一方解析失败 → throw;frontmatter/content escalate → 用 git marker
7
+ * 形式包装输给 driver 写 %A + exit 1。
8
+ *
9
+ * @module @co-engram/core/merge
10
+ */
11
+ import { createHash } from "node:crypto";
12
+ import { parseEngramFile, serializeEngramFile, detectEngramFileLanguage, } from "../storage/engram-store.js";
13
+ import { mergeFrontmatter, mergeFrontmatterAsync } from "./frontmatter.js";
14
+ import { mergeContent, mergeContentAsync } from "./content.js";
15
+ import { snapshotLoser } from "./backup.js";
16
+ function computeContentHashAndSize(content) {
17
+ const hash = createHash("sha256").update(content, "utf8").digest("hex");
18
+ const size = Buffer.byteLength(content, "utf8");
19
+ return { hash, size };
20
+ }
21
+ function buildEscalatedContent(oursRaw, theirsRaw) {
22
+ return `<<<<<<< ours\n${oursRaw}\n=======\n${theirsRaw}\n>>>>>>> theirs\n`;
23
+ }
24
+ export async function mergeEngramFile(params) {
25
+ const { baseRaw, oursRaw, theirsRaw, relPath, dataRoot, auditLog } = params;
26
+ // Step 1: Parse (may throw — caller exits 1)
27
+ const baseFile = parseEngramFile(baseRaw);
28
+ const oursFile = parseEngramFile(oursRaw);
29
+ const theirsFile = parseEngramFile(theirsRaw);
30
+ // Detect language from base (fallback to ours / theirs) so output preserves input format.
31
+ const language = detectEngramFileLanguage(baseRaw) ??
32
+ detectEngramFileLanguage(oursRaw) ??
33
+ detectEngramFileLanguage(theirsRaw);
34
+ // Step 2: Frontmatter merge (with optional LLM Layer B)
35
+ const fmOutcome = params.llmArbiter
36
+ ? await mergeFrontmatterAsync({
37
+ base: baseFile.frontmatter,
38
+ ours: oursFile.frontmatter,
39
+ theirs: theirsFile.frontmatter,
40
+ arbiter: params.llmArbiter,
41
+ path: relPath,
42
+ meta: {
43
+ oursUpdatedAt: oursFile.frontmatter.updatedAt,
44
+ theirsUpdatedAt: theirsFile.frontmatter.updatedAt,
45
+ oursUpdatedBy: oursFile.frontmatter.updatedBy ?? "unknown",
46
+ theirsUpdatedBy: theirsFile.frontmatter.updatedBy ?? "unknown",
47
+ },
48
+ })
49
+ : mergeFrontmatter({
50
+ base: baseFile.frontmatter,
51
+ ours: oursFile.frontmatter,
52
+ theirs: theirsFile.frontmatter,
53
+ });
54
+ // Step 3: Content merge (with optional LLM Layer B)
55
+ const contentOutcome = params.llmArbiter
56
+ ? await mergeContentAsync({
57
+ base: baseFile.content,
58
+ ours: oursFile.content,
59
+ theirs: theirsFile.content,
60
+ oursUpdatedAt: oursFile.frontmatter.updatedAt,
61
+ theirsUpdatedAt: theirsFile.frontmatter.updatedAt,
62
+ oursUpdatedBy: oursFile.frontmatter.updatedBy,
63
+ theirsUpdatedBy: theirsFile.frontmatter.updatedBy,
64
+ arbiter: params.llmArbiter,
65
+ path: relPath,
66
+ })
67
+ : mergeContent({
68
+ base: baseFile.content,
69
+ ours: oursFile.content,
70
+ theirs: theirsFile.content,
71
+ oursUpdatedAt: oursFile.frontmatter.updatedAt,
72
+ theirsUpdatedAt: theirsFile.frontmatter.updatedAt,
73
+ });
74
+ // Step 4: Decide escalation
75
+ const escalated = fmOutcome.escalatedFields.length > 0 ||
76
+ contentOutcome.strategy === "escalate";
77
+ if (escalated) {
78
+ const strategyParts = [];
79
+ if (fmOutcome.escalatedFields.length > 0) {
80
+ strategyParts.push(`frontmatter-escalate:${fmOutcome.escalatedFields.join(",")}`);
81
+ }
82
+ if (contentOutcome.strategy === "escalate") {
83
+ strategyParts.push("content-escalate:updatedAt-collision");
84
+ }
85
+ return {
86
+ mergedContent: buildEscalatedContent(oursRaw, theirsRaw),
87
+ strategy: `escalate(${strategyParts.join(" + ")})`,
88
+ winner: null,
89
+ escalated: true,
90
+ };
91
+ }
92
+ // Step 5: Recompute contentHash / contentSize + finalize
93
+ // updatedAt / version are 'max' fields — orchestrator already computed max(ours, theirs).
94
+ // Per spec §4.5 we additionally:
95
+ // - bump version by +1 (merge produces a new state)
96
+ // - set updatedBy = 'merge-driver'
97
+ const { hash, size } = computeContentHashAndSize(contentOutcome.merged);
98
+ const oursVersion = oursFile.frontmatter.version ?? 0;
99
+ const theirsVersion = theirsFile.frontmatter.version ?? 0;
100
+ const mergedFm = {
101
+ ...fmOutcome.merged,
102
+ contentHash: hash,
103
+ contentSize: size,
104
+ updatedBy: "merge-driver",
105
+ version: Math.max(oursVersion, theirsVersion) + 1,
106
+ };
107
+ // fmOutcome.merged.updatedAt is already max(ours, theirs) via the 'max' rule; leave as-is.
108
+ // Guard against the case where both sides omitted updatedAt (rare; treat as Date.now).
109
+ if (mergedFm.updatedAt === undefined) {
110
+ mergedFm.updatedAt = new Date().toISOString();
111
+ }
112
+ const mergedFile = {
113
+ frontmatter: mergedFm,
114
+ content: contentOutcome.merged,
115
+ };
116
+ const serialized = serializeEngramFile(mergedFile, language);
117
+ // Step 6: Backup + audit
118
+ let backupPath;
119
+ const loserSide = contentOutcome.winner === "ours"
120
+ ? "theirs"
121
+ : contentOutcome.winner === "theirs"
122
+ ? "ours"
123
+ : null;
124
+ if (loserSide && dataRoot) {
125
+ try {
126
+ const backup = snapshotLoser({
127
+ dataRoot,
128
+ relPath,
129
+ side: loserSide,
130
+ content: loserSide === "ours" ? oursRaw : theirsRaw,
131
+ });
132
+ backupPath = backup.backupPath;
133
+ }
134
+ catch (e) {
135
+ auditLog?.append({
136
+ actor: "system",
137
+ action: "merge_backup_failed",
138
+ metadata: {
139
+ path: relPath,
140
+ reason: e instanceof Error ? e.message : String(e),
141
+ },
142
+ });
143
+ }
144
+ }
145
+ auditLog?.append({
146
+ actor: "system",
147
+ action: "merge_resolved",
148
+ engramId: mergedFm.id,
149
+ metadata: {
150
+ path: relPath,
151
+ strategy: `${fmOutcome.strategy} + ${contentOutcome.strategy}`,
152
+ winner: contentOutcome.winner ?? fmOutcome.arbitratedWinner,
153
+ backupPath,
154
+ },
155
+ });
156
+ return {
157
+ mergedContent: serialized,
158
+ strategy: `${fmOutcome.strategy} + ${contentOutcome.strategy}`,
159
+ winner: contentOutcome.winner ?? fmOutcome.arbitratedWinner,
160
+ escalated: false,
161
+ backupPath,
162
+ };
163
+ }
164
+ //# sourceMappingURL=merge-engram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-engram.js","sourceRoot":"","sources":["../../src/merge/merge-engram.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Merge statistics (spec §7.4, §9) — 从 audit log 聚合 merge driver 指标。
3
+ *
4
+ * 用途:
5
+ * - `co-engram merge stats` CLI 输出
6
+ * - viewer "Merges" tab 数据源
7
+ * - anomaly detection(P4.4)的输入
8
+ *
9
+ * 设计:纯函数 + 注入 AuditLog,便于测试。
10
+ *
11
+ * @module @co-engram/core/merge
12
+ */
13
+ import type { AuditLog } from "../observability/audit-log.js";
14
+ /** 默认统计窗口:7 天(spec §9 operational window)。 */
15
+ export declare const DEFAULT_STATS_WINDOW_DAYS = 7;
16
+ export interface MergeStatsWindow {
17
+ readonly since: string;
18
+ readonly until: string;
19
+ }
20
+ export interface MergeStats {
21
+ readonly window: MergeStatsWindow;
22
+ /** 总 merge 处理数(resolved + escalated)。 */
23
+ readonly totalMerges: number;
24
+ /** 自动解决数(merge_resolved)。 */
25
+ readonly autoResolved: number;
26
+ /** 升级到 markers 的冲突数(merge_conflict_escalated)。 */
27
+ readonly escalatedToMarkers: number;
28
+ /** LLM 介入统计。 */
29
+ readonly llm: LlmStats;
30
+ /** Backup 失败数(spec §9 健康指标)。 */
31
+ readonly backupFailures: number;
32
+ /** 自动解决率 [0,1] = autoResolved / totalMerges。 */
33
+ readonly autoResolveRate: number;
34
+ /** 按策略(strategy)分组的解决计数(metadata.reason)。 */
35
+ readonly byStrategy: Readonly<Record<string, number>>;
36
+ /** 按路径分组的冲突计数(metadata.path)。 */
37
+ readonly byPath: Readonly<Record<string, number>>;
38
+ /** 按天聚合的 merge 总数(YYYY-MM-DD → count),用于趋势分析。 */
39
+ readonly byDay: Readonly<Record<string, number>>;
40
+ }
41
+ export interface LlmStats {
42
+ /** LLM 成功仲裁数(merge_llm_arbitrated)。 */
43
+ readonly arbitrated: number;
44
+ /** LLM 介入但 escalate 的数(置信度不够等)。 */
45
+ readonly escalated: number;
46
+ /** LLM 调用失败数(网络 / parse / timeout)。 */
47
+ readonly failed: number;
48
+ /** LLM 总调用尝试 = arbitrated + escalated + failed。 */
49
+ readonly totalInvocations: number;
50
+ /** 成功率 [0,1] = arbitrated / totalInvocations。 */
51
+ readonly successRate: number;
52
+ }
53
+ /**
54
+ * 聚合 audit log 为 merge 统计。
55
+ *
56
+ * @param auditLog 要读取的 audit log
57
+ * @param windowMs 时间窗口(默认 7 天),从 now 向前回溯
58
+ */
59
+ export declare function computeMergeStats(params: {
60
+ auditLog: AuditLog;
61
+ windowMs?: number;
62
+ now?: () => Date;
63
+ }): MergeStats;
64
+ /**
65
+ * 把 MergeStats 格式化成人类可读的文本(供 CLI 使用)。
66
+ */
67
+ export declare function formatMergeStatsAsText(stats: MergeStats): string;
68
+ //# sourceMappingURL=merge-stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-stats.d.ts","sourceRoot":"","sources":["../../src/merge/merge-stats.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,QAAQ,EAGT,MAAM,+BAA+B,CAAC;AAYvC,8CAA8C;AAC9C,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,yCAAyC;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,6BAA6B;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,gBAAgB;IAChB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;IACvB,gCAAgC;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,gDAAgD;IAChD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,6CAA6C;IAC7C,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,QAAQ;IACvB,uCAAuC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,mCAAmC;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,iDAAiD;IACjD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB,GAAG,UAAU,CAiBb;AA2ED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CA4ChE"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Merge statistics (spec §7.4, §9) — 从 audit log 聚合 merge driver 指标。
3
+ *
4
+ * 用途:
5
+ * - `co-engram merge stats` CLI 输出
6
+ * - viewer "Merges" tab 数据源
7
+ * - anomaly detection(P4.4)的输入
8
+ *
9
+ * 设计:纯函数 + 注入 AuditLog,便于测试。
10
+ *
11
+ * @module @co-engram/core/merge
12
+ */
13
+ /** Merge-related audit actions(spec §5.5 + §9)。 */
14
+ const MERGE_ACTIONS = [
15
+ "merge_resolved",
16
+ "merge_conflict_escalated",
17
+ "merge_backup_failed",
18
+ "merge_llm_arbitrated",
19
+ "merge_llm_arbitrated_escalated",
20
+ "merge_llm_arbitrated_failed",
21
+ ];
22
+ /** 默认统计窗口:7 天(spec §9 operational window)。 */
23
+ export const DEFAULT_STATS_WINDOW_DAYS = 7;
24
+ /**
25
+ * 聚合 audit log 为 merge 统计。
26
+ *
27
+ * @param auditLog 要读取的 audit log
28
+ * @param windowMs 时间窗口(默认 7 天),从 now 向前回溯
29
+ */
30
+ export function computeMergeStats(params) {
31
+ const { auditLog, now = () => new Date() } = params;
32
+ const windowMs = params.windowMs ?? DEFAULT_STATS_WINDOW_DAYS * 24 * 60 * 60 * 1000;
33
+ const until = now();
34
+ const since = new Date(until.getTime() - windowMs);
35
+ const sinceIso = since.toISOString();
36
+ const untilIso = until.toISOString();
37
+ const entries = auditLog.query({
38
+ since: sinceIso,
39
+ until: untilIso,
40
+ action: MERGE_ACTIONS,
41
+ });
42
+ return aggregateEntries(entries, { since: sinceIso, until: untilIso });
43
+ }
44
+ function aggregateEntries(entries, window) {
45
+ let autoResolved = 0;
46
+ let escalatedToMarkers = 0;
47
+ let backupFailures = 0;
48
+ let llmArbitrated = 0;
49
+ let llmEscalated = 0;
50
+ let llmFailed = 0;
51
+ const byStrategy = {};
52
+ const byPath = {};
53
+ const byDay = {};
54
+ for (const entry of entries) {
55
+ // 按天聚合(YYYY-MM-DD)
56
+ const day = entry.ts.slice(0, 10);
57
+ byDay[day] = (byDay[day] ?? 0) + 1;
58
+ switch (entry.action) {
59
+ case "merge_resolved":
60
+ autoResolved++;
61
+ if (entry.metadata?.reason) {
62
+ const reason = String(entry.metadata.reason);
63
+ byStrategy[reason] = (byStrategy[reason] ?? 0) + 1;
64
+ }
65
+ break;
66
+ case "merge_conflict_escalated":
67
+ escalatedToMarkers++;
68
+ if (entry.metadata?.path) {
69
+ const p = String(entry.metadata.path);
70
+ byPath[p] = (byPath[p] ?? 0) + 1;
71
+ }
72
+ break;
73
+ case "merge_backup_failed":
74
+ backupFailures++;
75
+ break;
76
+ case "merge_llm_arbitrated":
77
+ llmArbitrated++;
78
+ break;
79
+ case "merge_llm_arbitrated_escalated":
80
+ llmEscalated++;
81
+ break;
82
+ case "merge_llm_arbitrated_failed":
83
+ llmFailed++;
84
+ break;
85
+ }
86
+ }
87
+ const totalMerges = autoResolved + escalatedToMarkers;
88
+ const llmTotal = llmArbitrated + llmEscalated + llmFailed;
89
+ return {
90
+ window,
91
+ totalMerges,
92
+ autoResolved,
93
+ escalatedToMarkers,
94
+ llm: {
95
+ arbitrated: llmArbitrated,
96
+ escalated: llmEscalated,
97
+ failed: llmFailed,
98
+ totalInvocations: llmTotal,
99
+ successRate: llmTotal === 0 ? 0 : llmArbitrated / llmTotal,
100
+ },
101
+ backupFailures,
102
+ autoResolveRate: totalMerges === 0 ? 0 : autoResolved / totalMerges,
103
+ byStrategy,
104
+ byPath,
105
+ byDay,
106
+ };
107
+ }
108
+ /**
109
+ * 把 MergeStats 格式化成人类可读的文本(供 CLI 使用)。
110
+ */
111
+ export function formatMergeStatsAsText(stats) {
112
+ const lines = [];
113
+ const w = stats.window;
114
+ lines.push(`co-engram merge stats`);
115
+ lines.push(` window: ${w.since} → ${w.until}`);
116
+ lines.push(` `);
117
+ lines.push(` total merges: ${stats.totalMerges}`);
118
+ lines.push(` auto-resolved: ${stats.autoResolved} (${pct(stats.autoResolveRate)})`);
119
+ lines.push(` escalated(markers): ${stats.escalatedToMarkers}`);
120
+ lines.push(` `);
121
+ lines.push(` LLM arbitration:`);
122
+ lines.push(` total: ${stats.llm.totalInvocations}`);
123
+ lines.push(` succeeded: ${stats.llm.arbitrated}`);
124
+ lines.push(` escalated: ${stats.llm.escalated}`);
125
+ lines.push(` failed: ${stats.llm.failed}`);
126
+ lines.push(` success rate:${pct(stats.llm.successRate)}`);
127
+ lines.push(` `);
128
+ lines.push(` backup failures: ${stats.backupFailures}`);
129
+ // Top strategies
130
+ const strategies = Object.entries(stats.byStrategy).sort((a, b) => b[1] - a[1]);
131
+ if (strategies.length > 0) {
132
+ lines.push(` `);
133
+ lines.push(` top strategies:`);
134
+ for (const [s, n] of strategies.slice(0, 5)) {
135
+ lines.push(` ${n.toString().padStart(5)} ${s}`);
136
+ }
137
+ }
138
+ // Hot paths
139
+ const paths = Object.entries(stats.byPath).sort((a, b) => b[1] - a[1]);
140
+ if (paths.length > 0) {
141
+ lines.push(` `);
142
+ lines.push(` hot paths (most conflicts):`);
143
+ for (const [p, n] of paths.slice(0, 5)) {
144
+ lines.push(` ${n.toString().padStart(5)} ${p}`);
145
+ }
146
+ }
147
+ return lines.join("\n") + "\n";
148
+ }
149
+ function pct(r) {
150
+ return `${(r * 100).toFixed(1)}%`;
151
+ }
152
+ //# sourceMappingURL=merge-stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-stats.js","sourceRoot":"","sources":["../../src/merge/merge-stats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Onboard: install/uninstall/status for the merge driver in a git repo.
3
+ *
4
+ * Phase 1: invoked manually by tests.
5
+ * Phase 2: wired into plugin onboarding + `co-engram git enable/disable/status`.
6
+ *
7
+ * installMergeDriver:
8
+ * 1. Copy the esbuild bundle to `~/.co-engram/merge-driver.cjs` (version-checked).
9
+ * 2. Write `[merge "co-engram"]` block into the repo's `.git/config`.
10
+ * 3. Append the merge=co-engram entry to `.gitattributes` (idempotent).
11
+ *
12
+ * uninstallMergeDriver (spec §8.8):
13
+ * - Removes `merge.co-engram.*` keys from `.git/config`.
14
+ * - DOES NOT remove .gitattributes entries (committed; affects whole team).
15
+ * - DOES NOT remove ~/.co-engram/merge-driver.cjs (shared across repos).
16
+ * Use uninstallGlobalBundle() for that.
17
+ *
18
+ * @module @co-engram-core/merge
19
+ */
20
+ export declare const GITATTRIBUTES_ENTRY = "# co-engram structured merge driver\n**/*.md merge=co-engram\nsynapses/**/*.yaml merge=co-engram\n";
21
+ export interface OnboardResult {
22
+ readonly bundlePath: string;
23
+ readonly bundleUpgraded: boolean;
24
+ readonly gitConfigWritten: boolean;
25
+ readonly gitattributesUpdated: boolean;
26
+ }
27
+ export declare function installMergeDriver(params: {
28
+ repoRoot: string;
29
+ bundleSourcePath: string;
30
+ }): OnboardResult;
31
+ export interface OnboardStatus {
32
+ /** True if ~/.co-engram/merge-driver.cjs exists and matches the current version. */
33
+ readonly bundleInstalled: boolean;
34
+ /** The version banner in the installed bundle, or null if missing. */
35
+ readonly installedVersion: string | null;
36
+ /** True if .git/config has merge.co-engram.name. */
37
+ readonly gitConfigPresent: boolean;
38
+ /** True if .gitattributes contains merge=co-engram. */
39
+ readonly gitattributesPresent: boolean;
40
+ /** Absolute path to the bundle. */
41
+ readonly bundlePath: string;
42
+ }
43
+ export interface UninstallResult {
44
+ /** True if .git/config merge.co-engram.* keys were removed. */
45
+ readonly gitConfigRemoved: boolean;
46
+ /** True if ~/.co-engram/merge-driver.cjs was removed. */
47
+ readonly bundleRemoved: boolean;
48
+ }
49
+ /**
50
+ * Read the current onboard status for diagnostic / `co-engram git status` use.
51
+ */
52
+ export declare function getOnboardStatus(params: {
53
+ repoRoot: string;
54
+ }): OnboardStatus;
55
+ /**
56
+ * Uninstall merge driver from the repo (spec §8.8).
57
+ *
58
+ * Removes .git/config merge.co-engram.* keys. Does NOT touch:
59
+ * - .gitattributes (committed; affects whole team — user decides)
60
+ * - ~/.co-engram/merge-driver.cjs (shared across repos — use uninstallGlobalBundle)
61
+ */
62
+ export declare function uninstallMergeDriver(params: {
63
+ repoRoot: string;
64
+ }): UninstallResult;
65
+ /**
66
+ * Remove the global bundle (~/.co-engram/merge-driver.cjs).
67
+ * Spec §8.8: explicit `co-engram git uninstall --global` only.
68
+ */
69
+ export declare function uninstallGlobalBundle(): {
70
+ bundleRemoved: boolean;
71
+ };
72
+ //# sourceMappingURL=onboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../src/merge/onboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAeH,eAAO,MAAM,mBAAmB,kHAG/B,CAAC;AAKF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;CACxC;AAaD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,GAAG,aAAa,CAsChB;AAED,MAAM,WAAW,aAAa;IAC5B,oFAAoF;IACpF,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,oDAAoD;IACpD,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,uDAAuD;IACvD,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;IACvC,mCAAmC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,yDAAyD;IACzD,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CA4B5E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,eAAe,CAclB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI;IAAE,aAAa,EAAE,OAAO,CAAA;CAAE,CAUlE"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Onboard: install/uninstall/status for the merge driver in a git repo.
3
+ *
4
+ * Phase 1: invoked manually by tests.
5
+ * Phase 2: wired into plugin onboarding + `co-engram git enable/disable/status`.
6
+ *
7
+ * installMergeDriver:
8
+ * 1. Copy the esbuild bundle to `~/.co-engram/merge-driver.cjs` (version-checked).
9
+ * 2. Write `[merge "co-engram"]` block into the repo's `.git/config`.
10
+ * 3. Append the merge=co-engram entry to `.gitattributes` (idempotent).
11
+ *
12
+ * uninstallMergeDriver (spec §8.8):
13
+ * - Removes `merge.co-engram.*` keys from `.git/config`.
14
+ * - DOES NOT remove .gitattributes entries (committed; affects whole team).
15
+ * - DOES NOT remove ~/.co-engram/merge-driver.cjs (shared across repos).
16
+ * Use uninstallGlobalBundle() for that.
17
+ *
18
+ * @module @co-engram-core/merge
19
+ */
20
+ import { copyFileSync, existsSync, readFileSync, appendFileSync, mkdirSync, unlinkSync, } from "node:fs";
21
+ import { execSync } from "node:child_process";
22
+ import { join, dirname } from "node:path";
23
+ import { homedir } from "node:os";
24
+ import { DRIVER_BUNDLE_VERSION } from "./version.js";
25
+ export const GITATTRIBUTES_ENTRY = `# co-engram structured merge driver
26
+ **/*.md merge=co-engram
27
+ synapses/**/*.yaml merge=co-engram
28
+ `;
29
+ const BUNDLE_RELATIVE_PATH = ".co-engram/merge-driver.cjs";
30
+ const VERSION_PREFIX = "// co-engram-merge-driver v";
31
+ function readInstalledVersion(bundlePath) {
32
+ if (!existsSync(bundlePath))
33
+ return null;
34
+ try {
35
+ const firstLine = readFileSync(bundlePath, "utf8").split("\n")[0] ?? "";
36
+ if (!firstLine.startsWith(VERSION_PREFIX))
37
+ return null;
38
+ return firstLine.slice(VERSION_PREFIX.length).trim();
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ }
44
+ export function installMergeDriver(params) {
45
+ const { repoRoot, bundleSourcePath } = params;
46
+ // 1. Bundle copy (version-gated)
47
+ const home = homedir();
48
+ const bundleDest = join(home, BUNDLE_RELATIVE_PATH);
49
+ let bundleUpgraded = false;
50
+ const installed = readInstalledVersion(bundleDest);
51
+ if (installed !== DRIVER_BUNDLE_VERSION) {
52
+ mkdirSync(dirname(bundleDest), { recursive: true });
53
+ copyFileSync(bundleSourcePath, bundleDest);
54
+ bundleUpgraded = true;
55
+ }
56
+ // 2. .git/config — register merge driver
57
+ execSync('git config merge.co-engram.name "co-engram structured merge"', {
58
+ cwd: repoRoot,
59
+ });
60
+ execSync(`git config merge.co-engram.driver "node ${bundleDest} %O %A %B %L %P"`, { cwd: repoRoot });
61
+ // 3. .gitattributes (idempotent — only append if the marker is missing)
62
+ const attrsPath = join(repoRoot, ".gitattributes");
63
+ const existing = existsSync(attrsPath) ? readFileSync(attrsPath, "utf8") : "";
64
+ let gitattributesUpdated = false;
65
+ if (!existing.includes("merge=co-engram")) {
66
+ appendFileSync(attrsPath, GITATTRIBUTES_ENTRY, "utf8");
67
+ gitattributesUpdated = true;
68
+ }
69
+ return {
70
+ bundlePath: bundleDest,
71
+ bundleUpgraded,
72
+ gitConfigWritten: true,
73
+ gitattributesUpdated,
74
+ };
75
+ }
76
+ /**
77
+ * Read the current onboard status for diagnostic / `co-engram git status` use.
78
+ */
79
+ export function getOnboardStatus(params) {
80
+ const { repoRoot } = params;
81
+ const home = homedir();
82
+ const bundleDest = join(home, BUNDLE_RELATIVE_PATH);
83
+ const installedVersion = readInstalledVersion(bundleDest);
84
+ let gitConfigPresent = false;
85
+ try {
86
+ execSync("git config merge.co-engram.name", {
87
+ cwd: repoRoot,
88
+ stdio: "pipe",
89
+ });
90
+ gitConfigPresent = true;
91
+ }
92
+ catch {
93
+ gitConfigPresent = false;
94
+ }
95
+ const attrsPath = join(repoRoot, ".gitattributes");
96
+ const existing = existsSync(attrsPath) ? readFileSync(attrsPath, "utf8") : "";
97
+ const gitattributesPresent = existing.includes("merge=co-engram");
98
+ return {
99
+ bundleInstalled: installedVersion === DRIVER_BUNDLE_VERSION,
100
+ installedVersion,
101
+ gitConfigPresent,
102
+ gitattributesPresent,
103
+ bundlePath: bundleDest,
104
+ };
105
+ }
106
+ /**
107
+ * Uninstall merge driver from the repo (spec §8.8).
108
+ *
109
+ * Removes .git/config merge.co-engram.* keys. Does NOT touch:
110
+ * - .gitattributes (committed; affects whole team — user decides)
111
+ * - ~/.co-engram/merge-driver.cjs (shared across repos — use uninstallGlobalBundle)
112
+ */
113
+ export function uninstallMergeDriver(params) {
114
+ const { repoRoot } = params;
115
+ let gitConfigRemoved = false;
116
+ try {
117
+ execSync("git config --remove-section merge.co-engram", {
118
+ cwd: repoRoot,
119
+ stdio: "pipe",
120
+ });
121
+ gitConfigRemoved = true;
122
+ }
123
+ catch {
124
+ // Section already absent — nothing to remove.
125
+ gitConfigRemoved = false;
126
+ }
127
+ return { gitConfigRemoved, bundleRemoved: false };
128
+ }
129
+ /**
130
+ * Remove the global bundle (~/.co-engram/merge-driver.cjs).
131
+ * Spec §8.8: explicit `co-engram git uninstall --global` only.
132
+ */
133
+ export function uninstallGlobalBundle() {
134
+ const home = homedir();
135
+ const bundleDest = join(home, BUNDLE_RELATIVE_PATH);
136
+ if (!existsSync(bundleDest))
137
+ return { bundleRemoved: false };
138
+ try {
139
+ unlinkSync(bundleDest);
140
+ return { bundleRemoved: true };
141
+ }
142
+ catch {
143
+ return { bundleRemoved: false };
144
+ }
145
+ }
146
+ //# sourceMappingURL=onboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboard.js","sourceRoot":"","sources":["../../src/merge/onboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,cAAc,EACd,SAAS,EACT,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;CAGlC,CAAC;AAEF,MAAM,oBAAoB,GAAG,6BAA6B,CAAC;AAC3D,MAAM,cAAc,GAAG,6BAA6B,CAAC;AASrD,SAAS,oBAAoB,CAAC,UAAkB;IAC9C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACxE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,cAAc,CAAC;YAAE,OAAO,IAAI,CAAC;QACvD,OAAO,SAAS,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAGlC;IACC,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAAC;IAE9C,iCAAiC;IACjC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;IACpD,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,MAAM,SAAS,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;IACnD,IAAI,SAAS,KAAK,qBAAqB,EAAE,CAAC;QACxC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,YAAY,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;QAC3C,cAAc,GAAG,IAAI,CAAC;IACxB,CAAC;IAED,yCAAyC;IACzC,QAAQ,CAAC,8DAA8D,EAAE;QACvE,GAAG,EAAE,QAAQ;KACd,CAAC,CAAC;IACH,QAAQ,CACN,2CAA2C,UAAU,kBAAkB,EACvE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAClB,CAAC;IAEF,wEAAwE;IACxE,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,IAAI,oBAAoB,GAAG,KAAK,CAAC;IACjC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;QAC1C,cAAc,CAAC,SAAS,EAAE,mBAAmB,EAAE,MAAM,CAAC,CAAC;QACvD,oBAAoB,GAAG,IAAI,CAAC;IAC9B,CAAC;IAED,OAAO;QACL,UAAU,EAAE,UAAU;QACtB,cAAc;QACd,gBAAgB,EAAE,IAAI;QACtB,oBAAoB;KACrB,CAAC;AACJ,CAAC;AAsBD;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA4B;IAC3D,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAC5B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;IACpD,MAAM,gBAAgB,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;IAE1D,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,CAAC;QACH,QAAQ,CAAC,iCAAiC,EAAE;YAC1C,GAAG,EAAE,QAAQ;YACb,KAAK,EAAE,MAAM;SACd,CAAC,CAAC;QACH,gBAAgB,GAAG,IAAI,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,gBAAgB,GAAG,KAAK,CAAC;IAC3B,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,MAAM,oBAAoB,GAAG,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC;IAElE,OAAO;QACL,eAAe,EAAE,gBAAgB,KAAK,qBAAqB;QAC3D,gBAAgB;QAChB,gBAAgB;QAChB,oBAAoB;QACpB,UAAU,EAAE,UAAU;KACvB,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAEpC;IACC,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAC5B,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,CAAC;QACH,QAAQ,CAAC,6CAA6C,EAAE;YACtD,GAAG,EAAE,QAAQ;YACb,KAAK,EAAE,MAAM;SACd,CAAC,CAAC;QACH,gBAAgB,GAAG,IAAI,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;QAC9C,gBAAgB,GAAG,KAAK,CAAC;IAC3B,CAAC;IACD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB;IACnC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;IACpD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;IAC7D,IAAI,CAAC;QACH,UAAU,CAAC,UAAU,CAAC,CAAC;QACvB,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;IAClC,CAAC;AACH,CAAC"}