@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.
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +1 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts +1 -0
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +1 -0
- package/dist/i18n/zh.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/merge/anomaly-detector.d.ts +63 -0
- package/dist/merge/anomaly-detector.d.ts.map +1 -0
- package/dist/merge/anomaly-detector.js +128 -0
- package/dist/merge/anomaly-detector.js.map +1 -0
- package/dist/merge/arbitration.d.ts +18 -0
- package/dist/merge/arbitration.d.ts.map +1 -0
- package/dist/merge/arbitration.js +27 -0
- package/dist/merge/arbitration.js.map +1 -0
- package/dist/merge/auto-onboard.d.ts +56 -0
- package/dist/merge/auto-onboard.d.ts.map +1 -0
- package/dist/merge/auto-onboard.js +81 -0
- package/dist/merge/auto-onboard.js.map +1 -0
- package/dist/merge/backup.d.ts +27 -0
- package/dist/merge/backup.d.ts.map +1 -0
- package/dist/merge/backup.js +49 -0
- package/dist/merge/backup.js.map +1 -0
- package/dist/merge/content.d.ts +50 -0
- package/dist/merge/content.d.ts.map +1 -0
- package/dist/merge/content.js +137 -0
- package/dist/merge/content.js.map +1 -0
- package/dist/merge/cross-file-coordinator.d.ts +52 -0
- package/dist/merge/cross-file-coordinator.d.ts.map +1 -0
- package/dist/merge/cross-file-coordinator.js +222 -0
- package/dist/merge/cross-file-coordinator.js.map +1 -0
- package/dist/merge/data-root.d.ts +9 -0
- package/dist/merge/data-root.d.ts.map +1 -0
- package/dist/merge/data-root.js +42 -0
- package/dist/merge/data-root.js.map +1 -0
- package/dist/merge/driver-llm.d.ts +92 -0
- package/dist/merge/driver-llm.d.ts.map +1 -0
- package/dist/merge/driver-llm.js +174 -0
- package/dist/merge/driver-llm.js.map +1 -0
- package/dist/merge/driver-main.d.ts +29 -0
- package/dist/merge/driver-main.d.ts.map +1 -0
- package/dist/merge/driver-main.js +220 -0
- package/dist/merge/driver-main.js.map +1 -0
- package/dist/merge/evidence-union.d.ts +35 -0
- package/dist/merge/evidence-union.d.ts.map +1 -0
- package/dist/merge/evidence-union.js +88 -0
- package/dist/merge/evidence-union.js.map +1 -0
- package/dist/merge/frontmatter-rules.d.ts +38 -0
- package/dist/merge/frontmatter-rules.d.ts.map +1 -0
- package/dist/merge/frontmatter-rules.js +77 -0
- package/dist/merge/frontmatter-rules.js.map +1 -0
- package/dist/merge/frontmatter.d.ts +52 -0
- package/dist/merge/frontmatter.d.ts.map +1 -0
- package/dist/merge/frontmatter.js +211 -0
- package/dist/merge/frontmatter.js.map +1 -0
- package/dist/merge/index.d.ts +31 -0
- package/dist/merge/index.d.ts.map +1 -0
- package/dist/merge/index.js +31 -0
- package/dist/merge/index.js.map +1 -0
- package/dist/merge/llm-arbiter.d.ts +85 -0
- package/dist/merge/llm-arbiter.d.ts.map +1 -0
- package/dist/merge/llm-arbiter.js +177 -0
- package/dist/merge/llm-arbiter.js.map +1 -0
- package/dist/merge/llm-contract.d.ts +86 -0
- package/dist/merge/llm-contract.d.ts.map +1 -0
- package/dist/merge/llm-contract.js +134 -0
- package/dist/merge/llm-contract.js.map +1 -0
- package/dist/merge/llm-prompt.d.ts +25 -0
- package/dist/merge/llm-prompt.d.ts.map +1 -0
- package/dist/merge/llm-prompt.js +73 -0
- package/dist/merge/llm-prompt.js.map +1 -0
- package/dist/merge/merge-engram.d.ts +33 -0
- package/dist/merge/merge-engram.d.ts.map +1 -0
- package/dist/merge/merge-engram.js +164 -0
- package/dist/merge/merge-engram.js.map +1 -0
- package/dist/merge/merge-stats.d.ts +68 -0
- package/dist/merge/merge-stats.d.ts.map +1 -0
- package/dist/merge/merge-stats.js +152 -0
- package/dist/merge/merge-stats.js.map +1 -0
- package/dist/merge/onboard.d.ts +72 -0
- package/dist/merge/onboard.d.ts.map +1 -0
- package/dist/merge/onboard.js +146 -0
- package/dist/merge/onboard.js.map +1 -0
- package/dist/merge/post-merge-hook.d.ts +77 -0
- package/dist/merge/post-merge-hook.d.ts.map +1 -0
- package/dist/merge/post-merge-hook.js +211 -0
- package/dist/merge/post-merge-hook.js.map +1 -0
- package/dist/merge/resolution-state.d.ts +41 -0
- package/dist/merge/resolution-state.d.ts.map +1 -0
- package/dist/merge/resolution-state.js +100 -0
- package/dist/merge/resolution-state.js.map +1 -0
- package/dist/merge/synapse-merger.d.ts +63 -0
- package/dist/merge/synapse-merger.d.ts.map +1 -0
- package/dist/merge/synapse-merger.js +277 -0
- package/dist/merge/synapse-merger.js.map +1 -0
- package/dist/merge/synapse-rules.d.ts +66 -0
- package/dist/merge/synapse-rules.d.ts.map +1 -0
- package/dist/merge/synapse-rules.js +112 -0
- package/dist/merge/synapse-rules.js.map +1 -0
- package/dist/merge/version.d.ts +10 -0
- package/dist/merge/version.d.ts.map +1 -0
- package/dist/merge/version.js +10 -0
- package/dist/merge/version.js.map +1 -0
- package/dist/merge-driver.cjs +9371 -0
- package/dist/observability/audit-log.d.ts +4 -1
- package/dist/observability/audit-log.d.ts.map +1 -1
- package/dist/observability/audit-log.js +3 -0
- package/dist/observability/audit-log.js.map +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content body 段落级 3-way 合并
|
|
3
|
+
*
|
|
4
|
+
* 调 `git merge-file -p --diff3` 让 git 做机械合并。
|
|
5
|
+
* 干净合并 → 直接用;有 marker → fallback updatedAt 取赢家整段;
|
|
6
|
+
* updatedAt 一致 → 尝试 LLM 兜底(若提供 arbiter);失败 → escalate,
|
|
7
|
+
* 把带 marker 的输出原样返回(由 driver 写入 %A + exit 1)。
|
|
8
|
+
*
|
|
9
|
+
* @module @co-engram/core/merge
|
|
10
|
+
*/
|
|
11
|
+
import type { LlmArbiter } from "./llm-arbiter.js";
|
|
12
|
+
export interface ContentMergeOutcome {
|
|
13
|
+
readonly merged: string;
|
|
14
|
+
readonly strategy: "git-3way-clean" | "updatedAt-fallback" | "llm-resolved" | "escalate";
|
|
15
|
+
readonly conflictMarkersPresent: boolean;
|
|
16
|
+
readonly winner: "ours" | "theirs" | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function mergeContent(params: {
|
|
19
|
+
base: string;
|
|
20
|
+
ours: string;
|
|
21
|
+
theirs: string;
|
|
22
|
+
oursUpdatedAt: string;
|
|
23
|
+
theirsUpdatedAt: string;
|
|
24
|
+
markerSize?: number;
|
|
25
|
+
}): ContentMergeOutcome;
|
|
26
|
+
/**
|
|
27
|
+
* Async 版 content 合并,在 updatedAt collision 时追加 LLM 兜底(spec §5.6)。
|
|
28
|
+
*
|
|
29
|
+
* 注意:content 冲突对 LLM 是困难场景(段落级语义合并),所以仅当:
|
|
30
|
+
* 1. 提供 arbiter
|
|
31
|
+
* 2. updatedAt 完全一致(机械规则无解)
|
|
32
|
+
*
|
|
33
|
+
* 才调 LLM。LLM 必须返回 verdict=merge + mergedValue 才会被采用;
|
|
34
|
+
* verdict=ours/theirs 也接受,采用对应整段。
|
|
35
|
+
*
|
|
36
|
+
* 调用方约束:path 必须是 engram 相对路径(写 audit + prompt 用)。
|
|
37
|
+
*/
|
|
38
|
+
export declare function mergeContentAsync(params: {
|
|
39
|
+
base: string;
|
|
40
|
+
ours: string;
|
|
41
|
+
theirs: string;
|
|
42
|
+
oursUpdatedAt: string;
|
|
43
|
+
theirsUpdatedAt: string;
|
|
44
|
+
oursUpdatedBy?: string;
|
|
45
|
+
theirsUpdatedBy?: string;
|
|
46
|
+
markerSize?: number;
|
|
47
|
+
arbiter: LlmArbiter;
|
|
48
|
+
path: string;
|
|
49
|
+
}): Promise<ContentMergeOutcome>;
|
|
50
|
+
//# sourceMappingURL=content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/merge/content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EACb,gBAAgB,GAChB,oBAAoB,GACpB,cAAc,GACd,UAAU,CAAC;IACf,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;CAC3C;AAID,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,mBAAmB,CA0EtB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAqD/B"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content body 段落级 3-way 合并
|
|
3
|
+
*
|
|
4
|
+
* 调 `git merge-file -p --diff3` 让 git 做机械合并。
|
|
5
|
+
* 干净合并 → 直接用;有 marker → fallback updatedAt 取赢家整段;
|
|
6
|
+
* updatedAt 一致 → 尝试 LLM 兜底(若提供 arbiter);失败 → escalate,
|
|
7
|
+
* 把带 marker 的输出原样返回(由 driver 写入 %A + exit 1)。
|
|
8
|
+
*
|
|
9
|
+
* @module @co-engram/core/merge
|
|
10
|
+
*/
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
const CONFLICT_MARKER_RE = /^(<{7}|={7}|>{7})/m;
|
|
16
|
+
export function mergeContent(params) {
|
|
17
|
+
const { base, ours, theirs, oursUpdatedAt, theirsUpdatedAt, markerSize = 7, } = params;
|
|
18
|
+
const dir = mkdtempSync(join(tmpdir(), "co-engram-merge-"));
|
|
19
|
+
const oursPath = join(dir, "ours.tmp");
|
|
20
|
+
const basePath = join(dir, "base.tmp");
|
|
21
|
+
const theirsPath = join(dir, "theirs.tmp");
|
|
22
|
+
try {
|
|
23
|
+
writeFileSync(oursPath, ours, "utf8");
|
|
24
|
+
writeFileSync(basePath, base, "utf8");
|
|
25
|
+
writeFileSync(theirsPath, theirs, "utf8");
|
|
26
|
+
const result = spawnSync("git", [
|
|
27
|
+
"merge-file",
|
|
28
|
+
"-p",
|
|
29
|
+
"--diff3",
|
|
30
|
+
`--marker-size=${markerSize}`,
|
|
31
|
+
oursPath,
|
|
32
|
+
basePath,
|
|
33
|
+
theirsPath,
|
|
34
|
+
], { encoding: "utf8" });
|
|
35
|
+
// git merge-file: exit 0 = clean; exit >0 = conflict count; null = error
|
|
36
|
+
const stdout = result.stdout ?? "";
|
|
37
|
+
const hasMarkers = CONFLICT_MARKER_RE.test(stdout);
|
|
38
|
+
if (!hasMarkers && result.status === 0) {
|
|
39
|
+
return {
|
|
40
|
+
merged: stdout,
|
|
41
|
+
strategy: "git-3way-clean",
|
|
42
|
+
conflictMarkersPresent: false,
|
|
43
|
+
winner: null,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Conflict — try updatedAt fallback
|
|
47
|
+
if (oursUpdatedAt > theirsUpdatedAt) {
|
|
48
|
+
return {
|
|
49
|
+
merged: ours,
|
|
50
|
+
strategy: "updatedAt-fallback",
|
|
51
|
+
conflictMarkersPresent: false,
|
|
52
|
+
winner: "ours",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (theirsUpdatedAt > oursUpdatedAt) {
|
|
56
|
+
return {
|
|
57
|
+
merged: theirs,
|
|
58
|
+
strategy: "updatedAt-fallback",
|
|
59
|
+
conflictMarkersPresent: false,
|
|
60
|
+
winner: "theirs",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// updatedAt collision — escalate, pass through git's marked output
|
|
64
|
+
return {
|
|
65
|
+
merged: stdout,
|
|
66
|
+
strategy: "escalate",
|
|
67
|
+
conflictMarkersPresent: true,
|
|
68
|
+
winner: null,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
rmSync(dir, { recursive: true, force: true });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Async 版 content 合并,在 updatedAt collision 时追加 LLM 兜底(spec §5.6)。
|
|
77
|
+
*
|
|
78
|
+
* 注意:content 冲突对 LLM 是困难场景(段落级语义合并),所以仅当:
|
|
79
|
+
* 1. 提供 arbiter
|
|
80
|
+
* 2. updatedAt 完全一致(机械规则无解)
|
|
81
|
+
*
|
|
82
|
+
* 才调 LLM。LLM 必须返回 verdict=merge + mergedValue 才会被采用;
|
|
83
|
+
* verdict=ours/theirs 也接受,采用对应整段。
|
|
84
|
+
*
|
|
85
|
+
* 调用方约束:path 必须是 engram 相对路径(写 audit + prompt 用)。
|
|
86
|
+
*/
|
|
87
|
+
export async function mergeContentAsync(params) {
|
|
88
|
+
const layerA = mergeContent(params);
|
|
89
|
+
if (layerA.strategy !== "escalate") {
|
|
90
|
+
return layerA;
|
|
91
|
+
}
|
|
92
|
+
// Layer B: LLM 兜底
|
|
93
|
+
const llmInput = {
|
|
94
|
+
conflictType: "engram_content",
|
|
95
|
+
path: params.path,
|
|
96
|
+
base: params.base,
|
|
97
|
+
ours: params.ours,
|
|
98
|
+
theirs: params.theirs,
|
|
99
|
+
meta: {
|
|
100
|
+
oursUpdatedAt: params.oursUpdatedAt,
|
|
101
|
+
theirsUpdatedAt: params.theirsUpdatedAt,
|
|
102
|
+
oursUpdatedBy: params.oursUpdatedBy ?? "unknown",
|
|
103
|
+
theirsUpdatedBy: params.theirsUpdatedBy ?? "unknown",
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
const result = await params.arbiter.arbitrate(llmInput);
|
|
107
|
+
if (result.verdict.kind === "resolved") {
|
|
108
|
+
const output = result.verdict.output;
|
|
109
|
+
if (output.verdict === "merge" && typeof output.mergedValue === "string") {
|
|
110
|
+
return {
|
|
111
|
+
merged: output.mergedValue,
|
|
112
|
+
strategy: "llm-resolved",
|
|
113
|
+
conflictMarkersPresent: false,
|
|
114
|
+
winner: null, // merge 综合,无单边 winner
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (output.verdict === "ours") {
|
|
118
|
+
return {
|
|
119
|
+
merged: params.ours,
|
|
120
|
+
strategy: "llm-resolved",
|
|
121
|
+
conflictMarkersPresent: false,
|
|
122
|
+
winner: "ours",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (output.verdict === "theirs") {
|
|
126
|
+
return {
|
|
127
|
+
merged: params.theirs,
|
|
128
|
+
strategy: "llm-resolved",
|
|
129
|
+
conflictMarkersPresent: false,
|
|
130
|
+
winner: "theirs",
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
// verdict=escalate: fall through to layerA
|
|
134
|
+
}
|
|
135
|
+
return layerA;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/merge/content.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CrossFileCoordinator (spec §7) — post-merge 跨文件一致性 check。
|
|
3
|
+
*
|
|
4
|
+
* git merge driver 只看单文件冲突,**看不到跨文件状态**。当 engram 被
|
|
5
|
+
* refuted/forgotten 但另一方在其上加 synapse 时,driver 不知道 — 这类
|
|
6
|
+
* 不一致通过本模块在 post-merge hook / maintenance light stage 检测。
|
|
7
|
+
*
|
|
8
|
+
* 设计:
|
|
9
|
+
* - 不重试 / 不阻塞(失败只 audit,等待下次 deep stage)
|
|
10
|
+
* - 明确语义的 check 自动修复;需要语义判断的 check 调 LLM
|
|
11
|
+
* - 报告 spec §7.4 统计影响
|
|
12
|
+
*
|
|
13
|
+
* @module @co-engram/core/merge
|
|
14
|
+
*/
|
|
15
|
+
import type { EngramRepository } from "../storage/repository.js";
|
|
16
|
+
import type { AuditLog } from "../observability/audit-log.js";
|
|
17
|
+
import type { LlmArbiter } from "./llm-arbiter.js";
|
|
18
|
+
/** spec §7.3 不一致类型 */
|
|
19
|
+
export type InconsistencyKind = "refuted_engram_has_active_synapse" | "supersedes_target_not_archived" | "contradicts_resolution_state_drift" | "disjoint_domain_tags";
|
|
20
|
+
export interface Inconsistency {
|
|
21
|
+
readonly kind: InconsistencyKind;
|
|
22
|
+
readonly path?: string;
|
|
23
|
+
readonly engramId?: string;
|
|
24
|
+
readonly synapseId?: string;
|
|
25
|
+
readonly detail: string;
|
|
26
|
+
/** 是否被自动修复;false 表示需要 LLM/人工 */
|
|
27
|
+
readonly autoFixed: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface CrossFileConsistencyReport {
|
|
30
|
+
readonly startedAt: number;
|
|
31
|
+
readonly finishedAt: number;
|
|
32
|
+
readonly durationMs: number;
|
|
33
|
+
readonly inconsistencies: readonly Inconsistency[];
|
|
34
|
+
readonly autoFixedCount: number;
|
|
35
|
+
readonly llmEscalatedCount: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 跑一遍跨文件一致性 check(spec §7.3)。
|
|
39
|
+
*
|
|
40
|
+
* 不抛错(失败 → 写 inconsistency,继续)。
|
|
41
|
+
* 不阻塞调用方(post-merge hook 必须 fast-fail)。
|
|
42
|
+
*/
|
|
43
|
+
export declare function runCrossFileConsistency(params: {
|
|
44
|
+
repository: EngramRepository;
|
|
45
|
+
auditLog?: AuditLog;
|
|
46
|
+
/**
|
|
47
|
+
* 可选 LLM 仲裁器。提供时用于需要语义判断的 check(domainTags union
|
|
48
|
+
* vs 二选一 / contradicts resolutionState)。不提供 → 这两类只记录 inconsistency。
|
|
49
|
+
*/
|
|
50
|
+
llmArbiter?: LlmArbiter;
|
|
51
|
+
}): Promise<CrossFileConsistencyReport>;
|
|
52
|
+
//# sourceMappingURL=cross-file-coordinator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-file-coordinator.d.ts","sourceRoot":"","sources":["../../src/merge/cross-file-coordinator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,sBAAsB;AACtB,MAAM,MAAM,iBAAiB,GACzB,mCAAmC,GACnC,gCAAgC,GAChC,oCAAoC,GACpC,sBAAsB,CAAC;AAE3B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;IACnD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AAQD;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,UAAU,EAAE,gBAAgB,CAAC;IAC7B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAyDtC"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CrossFileCoordinator (spec §7) — post-merge 跨文件一致性 check。
|
|
3
|
+
*
|
|
4
|
+
* git merge driver 只看单文件冲突,**看不到跨文件状态**。当 engram 被
|
|
5
|
+
* refuted/forgotten 但另一方在其上加 synapse 时,driver 不知道 — 这类
|
|
6
|
+
* 不一致通过本模块在 post-merge hook / maintenance light stage 检测。
|
|
7
|
+
*
|
|
8
|
+
* 设计:
|
|
9
|
+
* - 不重试 / 不阻塞(失败只 audit,等待下次 deep stage)
|
|
10
|
+
* - 明确语义的 check 自动修复;需要语义判断的 check 调 LLM
|
|
11
|
+
* - 报告 spec §7.4 统计影响
|
|
12
|
+
*
|
|
13
|
+
* @module @co-engram/core/merge
|
|
14
|
+
*/
|
|
15
|
+
/** spec §7.3 中"refuted/forgotten"对应的 engram status 集合 */
|
|
16
|
+
const INACTIVE_STATUSES = ["archived", "forgotten"];
|
|
17
|
+
/** spec §7.3 中"应被 archived"的 verification status */
|
|
18
|
+
const REFUTED_VERIFICATION = "refuted";
|
|
19
|
+
/**
|
|
20
|
+
* 跑一遍跨文件一致性 check(spec §7.3)。
|
|
21
|
+
*
|
|
22
|
+
* 不抛错(失败 → 写 inconsistency,继续)。
|
|
23
|
+
* 不阻塞调用方(post-merge hook 必须 fast-fail)。
|
|
24
|
+
*/
|
|
25
|
+
export async function runCrossFileConsistency(params) {
|
|
26
|
+
const { repository, auditLog, llmArbiter } = params;
|
|
27
|
+
const startedAt = Date.now();
|
|
28
|
+
const inconsistencies = [];
|
|
29
|
+
// Check 1: refuted/forgotten engram 仍有 active outgoing synapse (spec §7.3 行 1)
|
|
30
|
+
inconsistencies.push(...checkRefutedEngramsHaveActiveSynapses(repository));
|
|
31
|
+
// Check 2: supersedes 关系破裂 (spec §7.3 行 2)
|
|
32
|
+
inconsistencies.push(...checkSupersedesTargets(repository));
|
|
33
|
+
// Check 3 + 4: 需要 LLM 介入的不一致(可选)
|
|
34
|
+
if (llmArbiter) {
|
|
35
|
+
inconsistencies.push(...(await checkContradictsDrift(repository, llmArbiter)));
|
|
36
|
+
inconsistencies.push(...(await checkDisjointDomainTags(repository, llmArbiter)));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// 无 LLM — 仍扫描并 audit,但不尝试修复
|
|
40
|
+
inconsistencies.push(...scanContradictsDriftNoLlm(repository));
|
|
41
|
+
inconsistencies.push(...scanDisjointDomainTagsNoLlm(repository));
|
|
42
|
+
}
|
|
43
|
+
// 自动修复的 inconsistency 标 autoFixed=true(已在 check 内部完成)
|
|
44
|
+
// 写 audit log
|
|
45
|
+
if (auditLog) {
|
|
46
|
+
for (const inc of inconsistencies) {
|
|
47
|
+
auditLog.append({
|
|
48
|
+
actor: "system",
|
|
49
|
+
action: "merge_conflict_escalated",
|
|
50
|
+
metadata: {
|
|
51
|
+
kind: inc.kind,
|
|
52
|
+
path: inc.path,
|
|
53
|
+
engramId: inc.engramId,
|
|
54
|
+
synapseId: inc.synapseId,
|
|
55
|
+
detail: inc.detail,
|
|
56
|
+
autoFixed: inc.autoFixed,
|
|
57
|
+
source: "cross-file-coordinator",
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const finishedAt = Date.now();
|
|
63
|
+
const autoFixedCount = inconsistencies.filter((i) => i.autoFixed).length;
|
|
64
|
+
const llmEscalatedCount = inconsistencies.filter((i) => !i.autoFixed).length;
|
|
65
|
+
return {
|
|
66
|
+
startedAt,
|
|
67
|
+
finishedAt,
|
|
68
|
+
durationMs: finishedAt - startedAt,
|
|
69
|
+
inconsistencies,
|
|
70
|
+
autoFixedCount,
|
|
71
|
+
llmEscalatedCount,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// ============================================================
|
|
75
|
+
// Check 1: refuted/forgotten engram 仍有 active outgoing synapse
|
|
76
|
+
// ============================================================
|
|
77
|
+
function checkRefutedEngramsHaveActiveSynapses(repository) {
|
|
78
|
+
const out = [];
|
|
79
|
+
for (const entry of repository.listEngrams()) {
|
|
80
|
+
let engram;
|
|
81
|
+
try {
|
|
82
|
+
engram = repository.readEngram(entry.id);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
continue; // 索引过期 / 文件已被删
|
|
86
|
+
}
|
|
87
|
+
const isInactive = INACTIVE_STATUSES.includes(engram.status) ||
|
|
88
|
+
engram.verificationStatus === REFUTED_VERIFICATION;
|
|
89
|
+
if (!isInactive)
|
|
90
|
+
continue;
|
|
91
|
+
// 查该 engram 的 outgoing synapses
|
|
92
|
+
const { outgoing } = repository.readSynapses(engram.id);
|
|
93
|
+
if (outgoing.length === 0)
|
|
94
|
+
continue;
|
|
95
|
+
out.push({
|
|
96
|
+
kind: "refuted_engram_has_active_synapse",
|
|
97
|
+
engramId: engram.id,
|
|
98
|
+
detail: `engram status=${engram.status}/verificationStatus=${engram.verificationStatus ?? "unverified"} but has ${outgoing.length} outgoing synapse(s)`,
|
|
99
|
+
autoFixed: false,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
// ============================================================
|
|
105
|
+
// Check 2: supersedes 关系破裂 — A supersedes B,B 应 archived
|
|
106
|
+
// ============================================================
|
|
107
|
+
function checkSupersedesTargets(repository) {
|
|
108
|
+
const out = [];
|
|
109
|
+
// 遍历所有 engram,查 kind=supersedes 的 outgoing synapse
|
|
110
|
+
for (const entry of repository.listEngrams()) {
|
|
111
|
+
let engram;
|
|
112
|
+
try {
|
|
113
|
+
engram = repository.readEngram(entry.id);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const { outgoing } = repository.readSynapses(engram.id);
|
|
119
|
+
for (const syn of outgoing) {
|
|
120
|
+
if (syn.kind !== "supersedes")
|
|
121
|
+
continue;
|
|
122
|
+
let target;
|
|
123
|
+
try {
|
|
124
|
+
target = repository.readEngram(syn.to);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
continue; // target 不存在 — dangling synapse,doctor 已处理
|
|
128
|
+
}
|
|
129
|
+
if (target.status === "archived")
|
|
130
|
+
continue;
|
|
131
|
+
// 自动修复:把 target.status 设为 archived(spec §7.3 行 2:明确语义)
|
|
132
|
+
try {
|
|
133
|
+
repository.updateLifecycle(target.id, "archived");
|
|
134
|
+
out.push({
|
|
135
|
+
kind: "supersedes_target_not_archived",
|
|
136
|
+
engramId: target.id,
|
|
137
|
+
synapseId: syn.id,
|
|
138
|
+
detail: `auto-archived ${target.id} (superseded by ${engram.id})`,
|
|
139
|
+
autoFixed: true,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
out.push({
|
|
144
|
+
kind: "supersedes_target_not_archived",
|
|
145
|
+
engramId: target.id,
|
|
146
|
+
synapseId: syn.id,
|
|
147
|
+
detail: `failed to auto-archive ${target.id}`,
|
|
148
|
+
autoFixed: false,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
// ============================================================
|
|
156
|
+
// Check 3: contradicts synapse resolutionState drift (LLM-assisted)
|
|
157
|
+
// ============================================================
|
|
158
|
+
async function checkContradictsDrift(repository, _arbiter) {
|
|
159
|
+
// 完整 LLM 仲裁涉及大量 LLM 调用,Phase 3 先扫描 + 记录,实际修复留 P4
|
|
160
|
+
return scanContradictsDriftNoLlm(repository);
|
|
161
|
+
}
|
|
162
|
+
function scanContradictsDriftNoLlm(repository) {
|
|
163
|
+
const out = [];
|
|
164
|
+
for (const entry of repository.listEngrams()) {
|
|
165
|
+
let engram;
|
|
166
|
+
try {
|
|
167
|
+
engram = repository.readEngram(entry.id);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const { outgoing } = repository.readSynapses(engram.id);
|
|
173
|
+
for (const syn of outgoing) {
|
|
174
|
+
if (syn.kind !== "contradicts")
|
|
175
|
+
continue;
|
|
176
|
+
const rs = syn.resolutionState;
|
|
177
|
+
if (!rs)
|
|
178
|
+
continue;
|
|
179
|
+
// pending / auto_resolved 状态长期停留 → 可能 drift
|
|
180
|
+
if (rs.status === "pending" || rs.status === "auto_resolved") {
|
|
181
|
+
out.push({
|
|
182
|
+
kind: "contradicts_resolution_state_drift",
|
|
183
|
+
engramId: engram.id,
|
|
184
|
+
synapseId: syn.id,
|
|
185
|
+
detail: `contradicts synapse stuck in ${rs.status} phase ${rs.phase}`,
|
|
186
|
+
autoFixed: false,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
// ============================================================
|
|
194
|
+
// Check 4: disjoint domainTags (LLM-assisted)
|
|
195
|
+
// ============================================================
|
|
196
|
+
async function checkDisjointDomainTags(repository, _arbiter) {
|
|
197
|
+
// 完整 LLM 仲裁涉及版本对比,Phase 3 先扫描空 tags / 单元素 tags 异常
|
|
198
|
+
return scanDisjointDomainTagsNoLlm(repository);
|
|
199
|
+
}
|
|
200
|
+
function scanDisjointDomainTagsNoLlm(repository) {
|
|
201
|
+
const out = [];
|
|
202
|
+
for (const entry of repository.listEngrams()) {
|
|
203
|
+
let engram;
|
|
204
|
+
try {
|
|
205
|
+
engram = repository.readEngram(entry.id);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const tags = engram.domainTags ?? [];
|
|
211
|
+
if (tags.length === 0) {
|
|
212
|
+
out.push({
|
|
213
|
+
kind: "disjoint_domain_tags",
|
|
214
|
+
engramId: engram.id,
|
|
215
|
+
detail: `engram has empty domainTags after merge`,
|
|
216
|
+
autoFixed: false,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=cross-file-coordinator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-file-coordinator.js","sourceRoot":"","sources":["../../src/merge/cross-file-coordinator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk up from a file path to find the team memory data root.
|
|
3
|
+
*
|
|
4
|
+
* Data root = the directory containing the `.co-engram/` subdir.
|
|
5
|
+
*
|
|
6
|
+
* @module @co-engram/core/merge
|
|
7
|
+
*/
|
|
8
|
+
export declare function findDataRoot(startPath: string): string | null;
|
|
9
|
+
//# sourceMappingURL=data-root.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-root.d.ts","sourceRoot":"","sources":["../../src/merge/data-root.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B7D"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk up from a file path to find the team memory data root.
|
|
3
|
+
*
|
|
4
|
+
* Data root = the directory containing the `.co-engram/` subdir.
|
|
5
|
+
*
|
|
6
|
+
* @module @co-engram/core/merge
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, statSync } from "node:fs";
|
|
9
|
+
import { dirname, join, resolve } from "node:path";
|
|
10
|
+
const MARKER_DIR = ".co-engram";
|
|
11
|
+
export function findDataRoot(startPath) {
|
|
12
|
+
let current = startPath;
|
|
13
|
+
// If startPath is a file, begin from its directory
|
|
14
|
+
try {
|
|
15
|
+
const stat = statSync(current);
|
|
16
|
+
if (stat.isFile())
|
|
17
|
+
current = dirname(current);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// path may not exist yet (e.g. %A in some git versions); assume it's a file path
|
|
21
|
+
current = dirname(current);
|
|
22
|
+
}
|
|
23
|
+
current = resolve(current);
|
|
24
|
+
// Walk up
|
|
25
|
+
while (true) {
|
|
26
|
+
if (existsSync(join(current, MARKER_DIR))) {
|
|
27
|
+
try {
|
|
28
|
+
const stat = statSync(join(current, MARKER_DIR));
|
|
29
|
+
if (stat.isDirectory())
|
|
30
|
+
return current;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// ignore stat errors
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const parent = dirname(current);
|
|
37
|
+
if (parent === current)
|
|
38
|
+
return null; // filesystem root
|
|
39
|
+
current = parent;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=data-root.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-root.js","sourceRoot":"","sources":["../../src/merge/data-root.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,IAAI,OAAO,GAAG,SAAS,CAAC;IACxB,mDAAmD;IACnD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,IAAI,CAAC,MAAM,EAAE;YAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,iFAAiF;QACjF,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B,UAAU;IACV,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC;YAC1C,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;gBACjD,IAAI,IAAI,CAAC,WAAW,EAAE;oBAAE,OAAO,OAAO,CAAC;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,qBAAqB;YACvB,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,MAAM,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC,CAAC,kBAAkB;QACvD,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Driver-side LLM client bootstrap (spec §5.1).
|
|
3
|
+
*
|
|
4
|
+
* 问题:host (claude-code-mcp / openclaw-plugin) 启动时已经构造了 LlmClient 实例,
|
|
5
|
+
* 但 git merge driver 是 git 直接 spawn 的独立进程,host 无法注入对象引用。
|
|
6
|
+
*
|
|
7
|
+
* 解决:host 把 LLM 配置(endpoint / apiKey / model)序列化到
|
|
8
|
+
* `~/.co-engram/llm-config.json`,driver 启动时读出来用 fetch 构造 HTTP client。
|
|
9
|
+
*
|
|
10
|
+
* 协议选 OpenAI-compatible(/chat/completions) —— 最通用:
|
|
11
|
+
* - OpenAI / Azure / 通义 / 智谱 / DeepSeek / 月之暗面 / ollama 原生支持
|
|
12
|
+
* - Anthropic 也有 OpenAI 兼容端点(https://api.anthropic.com/v1/openai/v1)
|
|
13
|
+
*
|
|
14
|
+
* @module @co-engram/core/merge
|
|
15
|
+
*/
|
|
16
|
+
import type { LlmClient } from "../observability/necessity-evaluator.js";
|
|
17
|
+
/**
|
|
18
|
+
* LLM 配置(序列化形式)。
|
|
19
|
+
*
|
|
20
|
+
* host 启动时写入,driver 启动时读出。
|
|
21
|
+
* 字段命名与 host 端 NecessityLlmConfig 对齐,便于 host 直接序列化。
|
|
22
|
+
*/
|
|
23
|
+
export interface LlmClientConfig {
|
|
24
|
+
/** OpenAI-compatible chat completions endpoint(完整 URL) */
|
|
25
|
+
readonly endpoint: string;
|
|
26
|
+
/** API key(Bearer token) */
|
|
27
|
+
readonly apiKey: string;
|
|
28
|
+
/** 模型名(如 'glm-5.2' / 'claude-sonnet-4-6' / 'gpt-5.5') */
|
|
29
|
+
readonly model: string;
|
|
30
|
+
/** 额外 headers(可选) */
|
|
31
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
32
|
+
/** 写入时间(ISO)——driver 用于 staleness 检查 */
|
|
33
|
+
readonly writtenAt: string;
|
|
34
|
+
/** 写入者( host 名,用于诊断) */
|
|
35
|
+
readonly writtenBy?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 解析 config 文件路径。
|
|
39
|
+
*
|
|
40
|
+
* 优先级:
|
|
41
|
+
* 1. `$CO_ENGRAM_LLM_CONFIG`(绝对路径,测试用)
|
|
42
|
+
* 2. `$HOME/.co-engram/llm-config.json`(默认)
|
|
43
|
+
* 3. 退化:OS tmpdir(不推荐,仅当 HOME 缺失时)
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveLlmConfigPath(): string;
|
|
46
|
+
/**
|
|
47
|
+
* 写 LLM 配置到磁盘(host 启动时调用)。
|
|
48
|
+
*
|
|
49
|
+
* 不抛错 —— 写失败只警告,host 不应因 LLM 配置写盘失败而崩溃。
|
|
50
|
+
*/
|
|
51
|
+
export declare function writeLlmClientConfig(config: Omit<LlmClientConfig, "writtenAt">): {
|
|
52
|
+
ok: true;
|
|
53
|
+
path: string;
|
|
54
|
+
} | {
|
|
55
|
+
ok: false;
|
|
56
|
+
error: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* 读 LLM 配置(driver 启动时调用)。
|
|
60
|
+
*
|
|
61
|
+
* 不抛错 —— 读失败/文件缺失都返回 undefined,driver 退化为 no-LLM 模式。
|
|
62
|
+
*/
|
|
63
|
+
export declare function readLlmClientConfig(): LlmClientConfig | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* 删除 LLM 配置(uninstall 时调用)。
|
|
66
|
+
*
|
|
67
|
+
* 用于 host 完全卸载 merge driver 时清理敏感数据。
|
|
68
|
+
* 失败不抛错,只返回 status。
|
|
69
|
+
*/
|
|
70
|
+
export declare function clearLlmClientConfig(): {
|
|
71
|
+
ok: true;
|
|
72
|
+
path: string;
|
|
73
|
+
} | {
|
|
74
|
+
ok: false;
|
|
75
|
+
error: string;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* 创建 OpenAI-compatible HTTP LlmClient(基于 fetch)。
|
|
79
|
+
*
|
|
80
|
+
* 任何抛错都让调用方(LlmArbiter)捕获并降级到 escalate。
|
|
81
|
+
*/
|
|
82
|
+
export declare function createHttpLlmClient(config: LlmClientConfig): LlmClient;
|
|
83
|
+
/**
|
|
84
|
+
* Driver 启动时的便捷入口:读 config + 构造 client。
|
|
85
|
+
*
|
|
86
|
+
* 失败返回 null,driver 退化为 no-LLM 模式(机械规则解决不了的冲突 → marker)。
|
|
87
|
+
*/
|
|
88
|
+
export declare function createDriverLlmClient(): {
|
|
89
|
+
client: LlmClient;
|
|
90
|
+
config: LlmClientConfig;
|
|
91
|
+
} | null;
|
|
92
|
+
//# sourceMappingURL=driver-llm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver-llm.d.ts","sourceRoot":"","sources":["../../src/merge/driver-llm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAWH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4BAA4B;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qBAAqB;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,wCAAwC;IACxC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wBAAwB;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAKD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAM7C;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,GACzC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAgB3D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,eAAe,GAAG,SAAS,CAiBjE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAChC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAY/B;AAcD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAiDtE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI;IACvC,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,eAAe,CAAC;CACzB,GAAG,IAAI,CAQP"}
|