@narumitw/pi-sync 0.57.0 → 0.57.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 (30) hide show
  1. package/README.md +1 -1
  2. package/dist/chunks/{agent-candidate-3JN6DJKT.ts → agent-candidate-GNEYR225.ts} +3 -3
  3. package/dist/chunks/{agent-plan-QCUJWBP4.ts → agent-plan-K3CRYVXV.ts} +3 -3
  4. package/dist/chunks/{chunk-FQOSHGJ7.ts → chunk-5QAE3G3K.ts} +11 -6
  5. package/dist/chunks/chunk-5QAE3G3K.ts.map +7 -0
  6. package/dist/chunks/{chunk-S2FMH5FT.ts → chunk-QOC47PBC.ts} +2 -2
  7. package/dist/chunks/{chunk-5Q5SSKNG.ts → chunk-S7QS4AWK.ts} +89 -2
  8. package/dist/chunks/chunk-S7QS4AWK.ts.map +7 -0
  9. package/dist/chunks/{chunk-OTZRCKYP.ts → chunk-T3NC77TU.ts} +29 -16
  10. package/dist/chunks/chunk-T3NC77TU.ts.map +7 -0
  11. package/dist/chunks/{conflict-review-6WIEQLXW.ts → conflict-review-ZCGWFKEK.ts} +3 -3
  12. package/dist/chunks/{continuous-sync-AWAAOXK2.ts → continuous-sync-LAIMSW7D.ts} +2 -2
  13. package/dist/chunks/{sync-operations-HDUDT5PD.ts → sync-operations-W5HFFMTP.ts} +3 -3
  14. package/dist/index.ts +4 -4
  15. package/package.json +4 -2
  16. package/skills/configure-pi-sync/references/configuration.md +1 -1
  17. package/src/sync/agent-candidate.ts +8 -3
  18. package/src/sync/merged-sync.ts +1 -1
  19. package/src/sync/package-merge.ts +87 -0
  20. package/src/sync/settings-conflicts.ts +25 -13
  21. package/src/sync/settings-merge.ts +48 -0
  22. package/dist/chunks/chunk-5Q5SSKNG.ts.map +0 -7
  23. package/dist/chunks/chunk-FQOSHGJ7.ts.map +0 -7
  24. package/dist/chunks/chunk-OTZRCKYP.ts.map +0 -7
  25. /package/dist/chunks/{agent-candidate-3JN6DJKT.ts.map → agent-candidate-GNEYR225.ts.map} +0 -0
  26. /package/dist/chunks/{agent-plan-QCUJWBP4.ts.map → agent-plan-K3CRYVXV.ts.map} +0 -0
  27. /package/dist/chunks/{chunk-S2FMH5FT.ts.map → chunk-QOC47PBC.ts.map} +0 -0
  28. /package/dist/chunks/{conflict-review-6WIEQLXW.ts.map → conflict-review-ZCGWFKEK.ts.map} +0 -0
  29. /package/dist/chunks/{continuous-sync-AWAAOXK2.ts.map → continuous-sync-LAIMSW7D.ts.map} +0 -0
  30. /package/dist/chunks/{sync-operations-HDUDT5PD.ts.map → sync-operations-W5HFFMTP.ts.map} +0 -0
@@ -0,0 +1,87 @@
1
+ import { compare, valid } from "semver";
2
+ import { type JsonValue, jsonEqual } from "./json-document.js";
3
+
4
+ interface Entry {
5
+ value: JsonValue;
6
+ version?: string;
7
+ options: JsonValue;
8
+ }
9
+
10
+ function entries(value: JsonValue | undefined): Map<string, Entry> | undefined {
11
+ if (!Array.isArray(value)) return undefined;
12
+ const result = new Map<string, Entry>();
13
+ for (const item of value) {
14
+ if (typeof item !== "string" && (!item || Array.isArray(item) || typeof item !== "object")) return undefined;
15
+ const source = typeof item === "string" ? item : item.source;
16
+ if (typeof source !== "string") return undefined;
17
+ const spec = source.startsWith("npm:") ? source.slice(4).trim() : undefined;
18
+ const match = spec?.match(/^(@?[^@]+(?:\/[^@]+)?)(?:@(.+))?$/u);
19
+ const key = match ? `npm:${match[1]}` : source;
20
+ if (result.has(key)) return undefined;
21
+ const options =
22
+ typeof item === "string" ? null : Object.fromEntries(Object.entries(item).filter(([key]) => key !== "source"));
23
+ result.set(key, { value: item, options, version: match?.[2] });
24
+ }
25
+ return result;
26
+ }
27
+
28
+ /** Compare only explicit npm versions; do not resolve ranges, tags, Git refs or registry state. */
29
+ export function mergePackageVersions(
30
+ base: JsonValue | undefined,
31
+ local: JsonValue | undefined,
32
+ remote: JsonValue | undefined,
33
+ ): JsonValue[] | undefined {
34
+ const ancestor = entries(base ?? []);
35
+ const left = entries(local);
36
+ const right = entries(remote);
37
+ if (!ancestor || !left || !right) return undefined;
38
+ // Raw Git refs and local path aliases do not prove distinct package identities.
39
+ // Leave changes to those sources under the existing atomic field policy.
40
+ const otherEntries = (items: Map<string, Entry>) =>
41
+ [...items].filter(([key]) => !key.startsWith("npm:")).map(([, entry]) => entry.value);
42
+ if (!jsonEqual(otherEntries(ancestor), otherEntries(left)) || !jsonEqual(otherEntries(ancestor), otherEntries(right)))
43
+ return undefined;
44
+
45
+ // Package order affects resource precedence. Merge the ordered identity list
46
+ // atomically, then select versions without changing that order.
47
+ const baseKeys = [...ancestor.keys()];
48
+ const leftKeys = [...left.keys()];
49
+ const rightKeys = [...right.keys()];
50
+ const order = jsonEqual(leftKeys, rightKeys)
51
+ ? leftKeys
52
+ : jsonEqual(leftKeys, baseKeys)
53
+ ? rightKeys
54
+ : jsonEqual(rightKeys, baseKeys)
55
+ ? leftKeys
56
+ : undefined;
57
+ if (!order) return undefined;
58
+ const output: JsonValue[] = [];
59
+ for (const key of new Set([...order, ...baseKeys])) {
60
+ const a = ancestor.get(key);
61
+ const l = left.get(key);
62
+ const r = right.get(key);
63
+ let chosen: Entry | undefined;
64
+ if (
65
+ l &&
66
+ r &&
67
+ key.startsWith("npm:") &&
68
+ l.version &&
69
+ r.version &&
70
+ valid(l.version) &&
71
+ valid(r.version) &&
72
+ jsonEqual(l.options, r.options)
73
+ ) {
74
+ const precedence = compare(l.version, r.version);
75
+ if (precedence !== 0) chosen = precedence > 0 ? l : r;
76
+ }
77
+ // Build metadata and equivalent version spelling do not establish a higher version.
78
+ if (!chosen) {
79
+ if (jsonEqual(l?.value, r?.value)) chosen = l;
80
+ else if (jsonEqual(l?.value, a?.value)) chosen = r;
81
+ else if (jsonEqual(r?.value, a?.value)) chosen = l;
82
+ else return undefined;
83
+ }
84
+ if (chosen) output.push(chosen.value);
85
+ }
86
+ return output;
87
+ }
@@ -4,7 +4,7 @@ import type { Snapshot } from "../snapshot/snapshot-types.js";
4
4
  import { readMergeAncestor } from "../state/merge-baseline-store.js";
5
5
  import type { SyncState } from "../state/state-types.js";
6
6
  import type { FileMergePlan } from "./file-merge-planner.js";
7
- import { mergeSettingsJson } from "./settings-merge.js";
7
+ import { mergeAcceptedSettingsPackages, mergeSettingsJson } from "./settings-merge.js";
8
8
 
9
9
  export async function resolveSettingsConflicts(
10
10
  config: AnySyncConfig,
@@ -18,21 +18,33 @@ export async function resolveSettingsConflicts(
18
18
  const decisions = [...plan.decisions];
19
19
  for (let index = 0; index < decisions.length; index++) {
20
20
  const decision = decisions[index];
21
- if (decision?.kind !== "conflict" || decision.reason !== "both-changed" || decision.path !== "settings.json")
22
- continue;
21
+ if (decision?.path !== "settings.json") continue;
22
+ if (decision.kind === "conflict" ? decision.reason !== "both-changed" : !config.agentMode) continue;
23
23
  const left = local.files.find((file) => file.path === decision.path);
24
24
  const right = remote.files.find((file) => file.path === decision.path);
25
25
  if (!left || !right) continue; // File deletion/addition is not a JSON field merge.
26
26
  const ancestor = await readMergeAncestor(config, state, decision.path);
27
27
  if (!ancestor) continue;
28
- const merged = mergeSettingsJson(
29
- ancestor,
30
- Buffer.from(left.contentBase64, "base64"),
31
- Buffer.from(right.contentBase64, "base64"),
32
- );
33
- if (merged.kind === "review") {
34
- fields.push(...merged.fields);
35
- continue;
28
+ const leftBytes = Buffer.from(left.contentBase64, "base64");
29
+ const rightBytes = Buffer.from(right.contentBase64, "base64");
30
+ let content: Buffer;
31
+ if (decision.kind === "accepted") {
32
+ if (!decision.file) continue;
33
+ const patched = mergeAcceptedSettingsPackages(
34
+ ancestor,
35
+ leftBytes,
36
+ rightBytes,
37
+ Buffer.from(decision.file.contentBase64, "base64"),
38
+ );
39
+ if (!patched) continue;
40
+ content = patched;
41
+ } else {
42
+ const merged = mergeSettingsJson(ancestor, leftBytes, rightBytes, undefined, Boolean(config.agentMode));
43
+ if (merged.kind === "review") {
44
+ fields.push(...merged.fields);
45
+ continue;
46
+ }
47
+ content = merged.content;
36
48
  }
37
49
  decisions[index] = {
38
50
  kind: "accepted",
@@ -40,8 +52,8 @@ export async function resolveSettingsConflicts(
40
52
  source: "merged",
41
53
  file: {
42
54
  path: decision.path,
43
- contentBase64: merged.content.toString("base64"),
44
- sha256: createHash("sha256").update(merged.content).digest("hex"),
55
+ contentBase64: content.toString("base64"),
56
+ sha256: createHash("sha256").update(content).digest("hex"),
45
57
  },
46
58
  };
47
59
  }
@@ -6,6 +6,8 @@ import {
6
6
  renderSettingsMembers,
7
7
  } from "./json-document.js";
8
8
 
9
+ import { mergePackageVersions } from "./package-merge.js";
10
+
9
11
  export type SettingsMergeResult =
10
12
  | { kind: "merged"; content: Buffer }
11
13
  | { kind: "review"; reason: "unsupported-format" | "field-conflict"; fields: string[] };
@@ -18,12 +20,51 @@ export const COUPLED_FIELDS = [
18
20
  ["enableAnalytics", "trackingId"],
19
21
  ] as const;
20
22
 
23
+ function selectPackages(base: JsonDocument, left: JsonDocument, right: JsonDocument) {
24
+ const packages = mergePackageVersions(
25
+ base.root.members.get("packages")?.node.value,
26
+ left.root.members.get("packages")?.node.value,
27
+ right.root.members.get("packages")?.node.value,
28
+ );
29
+ if (!packages) return undefined;
30
+ const document =
31
+ [left, right].find((source) => jsonEqual(source.root.members.get("packages")?.node.value, packages)) ??
32
+ parseSettingsDocument(Buffer.from(JSON.stringify({ packages })));
33
+ const node = document.root.members.get("packages")?.node;
34
+ return node ? { document, node } : undefined;
35
+ }
36
+
37
+ /** Accepted files need only a package override, not another full-document field merge. */
38
+ export function mergeAcceptedSettingsPackages(
39
+ ancestor: Buffer,
40
+ local: Buffer,
41
+ remote: Buffer,
42
+ accepted: Buffer,
43
+ ): Buffer | undefined {
44
+ try {
45
+ const base = parseSettingsDocument(ancestor);
46
+ const left = parseSettingsDocument(local);
47
+ const right = parseSettingsDocument(remote);
48
+ const document = parseSettingsDocument(accepted);
49
+ const packages = selectPackages(base, left, right);
50
+ if (!packages || jsonEqual(packages.node.value, document.root.members.get("packages")?.node.value))
51
+ return undefined;
52
+ const selected = new Map([...document.root.members].map(([key, member]) => [key, { document, node: member.node }]));
53
+ selected.set("packages", packages);
54
+ return renderSettingsMembers(document, selected);
55
+ } catch {
56
+ // Unsupported settings must not replace an accepted file with a reconstructed document.
57
+ return undefined;
58
+ }
59
+ }
60
+
21
61
  /** Only global settings.json; objects/arrays are atomic units until their invariants are proven. */
22
62
  export function mergeSettingsJson(
23
63
  ancestor: Buffer,
24
64
  local: Buffer,
25
65
  remote: Buffer,
26
66
  selections: ReadonlyMap<string, "local" | "remote"> = new Map(),
67
+ preferHigherPackageVersions = false,
27
68
  ): SettingsMergeResult {
28
69
  let documents: JsonDocument[];
29
70
  try {
@@ -39,6 +80,13 @@ export function mergeSettingsJson(
39
80
  units.push(...[...fields].filter((field) => !coupled.has(field)).map((field) => [field]));
40
81
  const conflicts: string[] = [];
41
82
  for (const unit of units) {
83
+ if (preferHigherPackageVersions && unit[0] === "packages") {
84
+ const packages = selectPackages(base, left, right);
85
+ if (packages) {
86
+ selected.set("packages", packages);
87
+ continue;
88
+ }
89
+ }
42
90
  const equal = (first: JsonDocument, second: JsonDocument) =>
43
91
  unit.every((field) =>
44
92
  jsonEqual(first.root.members.get(field)?.node.value, second.root.members.get(field)?.node.value),
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/sync/content-conflicts.ts", "../../src/sync/settings-merge.ts"],
4
- "sourcesContent": ["import { createHash } from \"node:crypto\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport type { Snapshot, SnapshotFile } from \"../snapshot/snapshot-types.js\";\nimport { readMergeAncestors } from \"../state/merge-baseline-store.js\";\nimport type { SyncState } from \"../state/state-types.js\";\nimport { type FileMergePlan, mergePathIdentity } from \"./file-merge-planner.js\";\nimport { mergeSession } from \"./session-merge.js\";\nimport { isMergeTextPath, mergeText, TEXT_MERGE_WORK_LIMIT, type TextMergeBudget } from \"./text-merge.js\";\nexport function snapshotFile(filePath: string, bytes: Buffer): SnapshotFile {\n return {\n path: filePath,\n contentBase64: bytes.toString(\"base64\"),\n sha256: createHash(\"sha256\").update(bytes).digest(\"hex\"),\n };\n}\nexport async function resolveContentConflicts(\n config: AnySyncConfig,\n state: SyncState,\n local: Snapshot,\n remote: Snapshot,\n plan: FileMergePlan,\n protectedPaths: ReadonlySet<string>,\n options: { textBudget?: TextMergeBudget; signal?: AbortSignal } = {},\n): Promise<FileMergePlan> {\n if (plan.kind !== \"planned\" || !config.mergeContent) return plan;\n options.signal?.throwIfAborted();\n const budget = options.textBudget ?? { remaining: TEXT_MERGE_WORK_LIMIT };\n const decisions = [...plan.decisions];\n let ancestors: SnapshotFile[] = [];\n try {\n ancestors = (await readMergeAncestors(config, state)) ?? [];\n } catch {\n options.signal?.throwIfAborted();\n return plan;\n }\n options.signal?.throwIfAborted();\n const oursByPath = new Map(local.files.map((file) => [file.path, file]));\n const theirsByPath = new Map(remote.files.map((file) => [file.path, file]));\n const ancestorsByPath = new Map(ancestors.map((file) => [file.path, file]));\n const decisionIndex = new Map(decisions.map((decision, index) => [decision.path, index]));\n const protectedKeys = new Set([...protectedPaths].map(mergePathIdentity));\n for (const conflict of plan.conflicts) {\n options.signal?.throwIfAborted();\n if (conflict.reason !== \"both-changed\" || protectedKeys.has(mergePathIdentity(conflict.path))) continue;\n const session = conflict.path.startsWith(\"sessions/\") && conflict.path.endsWith(\".jsonl\");\n if (!session && (!isMergeTextPath(conflict.path) || budget.remaining === 0)) continue;\n const ours = oursByPath.get(conflict.path);\n const theirs = theirsByPath.get(conflict.path);\n if (!ours || !theirs) continue;\n const ancestor = ancestorsByPath.get(conflict.path);\n const base = ancestor ? Buffer.from(ancestor.contentBase64, \"base64\") : undefined;\n if (!base) continue;\n const localBytes = Buffer.from(ours.contentBase64, \"base64\");\n const remoteBytes = Buffer.from(theirs.contentBase64, \"base64\");\n const bytes = session\n ? mergeSession(base, localBytes, remoteBytes)\n : mergeText(base, localBytes, remoteBytes, { budget, signal: options.signal });\n if (!bytes) continue;\n const index = decisionIndex.get(conflict.path);\n if (index === undefined) continue;\n decisions[index] = {\n kind: \"accepted\",\n path: conflict.path,\n source: \"merged\",\n file: snapshotFile(conflict.path, bytes),\n };\n }\n return {\n kind: \"planned\",\n decisions,\n conflicts: decisions.filter((item): item is Extract<typeof item, { kind: \"conflict\" }> => item.kind === \"conflict\"),\n };\n}\n", "import {\n type JsonDocument,\n type JsonNode,\n jsonEqual,\n parseSettingsDocument,\n renderSettingsMembers,\n} from \"./json-document.js\";\n\nexport type SettingsMergeResult =\n | { kind: \"merged\"; content: Buffer }\n | { kind: \"review\"; reason: \"unsupported-format\" | \"field-conflict\"; fields: string[] };\n\nexport const COUPLED_FIELDS = [\n [\"defaultProvider\", \"defaultModel\"],\n [\"queueMode\", \"steeringMode\"],\n [\"websockets\", \"transport\"],\n [\"skills\", \"enableSkillCommands\"],\n [\"enableAnalytics\", \"trackingId\"],\n] as const;\n\n/** Only global settings.json; objects/arrays are atomic units until their invariants are proven. */\nexport function mergeSettingsJson(\n ancestor: Buffer,\n local: Buffer,\n remote: Buffer,\n selections: ReadonlyMap<string, \"local\" | \"remote\"> = new Map(),\n): SettingsMergeResult {\n let documents: JsonDocument[];\n try {\n documents = [ancestor, local, remote].map(parseSettingsDocument);\n } catch {\n return { kind: \"review\", reason: \"unsupported-format\", fields: [] };\n }\n const [base, left, right] = documents as [JsonDocument, JsonDocument, JsonDocument];\n const selected = new Map<string, { document: JsonDocument; node: JsonNode }>();\n const fields = new Set(documents.flatMap((document) => [...document.root.members.keys()]));\n const units: string[][] = COUPLED_FIELDS.map((group) => [...group]);\n const coupled = new Set(units.flat());\n units.push(...[...fields].filter((field) => !coupled.has(field)).map((field) => [field]));\n const conflicts: string[] = [];\n for (const unit of units) {\n const equal = (first: JsonDocument, second: JsonDocument) =>\n unit.every((field) =>\n jsonEqual(first.root.members.get(field)?.node.value, second.root.members.get(field)?.node.value),\n );\n const choice = selections.get(unit[0] ?? \"\");\n const source = equal(left, right)\n ? left\n : equal(left, base)\n ? right\n : equal(right, base)\n ? left\n : choice === \"local\"\n ? left\n : choice === \"remote\"\n ? right\n : undefined;\n if (!source) {\n conflicts.push(...unit.filter((field) => fields.has(field)));\n continue;\n }\n for (const field of unit) {\n const member = source.root.members.get(field);\n if (member) selected.set(field, { document: source, node: member.node });\n }\n }\n if (conflicts.length) return { kind: \"review\", reason: \"field-conflict\", fields: conflicts };\n try {\n return { kind: \"merged\", content: renderSettingsMembers(left, selected) };\n } catch {\n return { kind: \"review\", reason: \"unsupported-format\", fields: [] };\n }\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/sync/agent-candidate.ts"],
4
- "sourcesContent": ["import { createHash } from \"node:crypto\";\nimport { scanSnapshot } from \"../snapshot/snapshot.js\";\nimport type { Snapshot, SnapshotFile } from \"../snapshot/snapshot-types.js\";\nimport { AGENT_INPUT_BYTES, AGENT_MAX_UNITS, AGENT_OUTPUT_BYTES } from \"./agent-policy.js\";\nimport { snapshotFile } from \"./content-conflicts.js\";\nimport type { FileMergePlan } from \"./file-merge-planner.js\";\nimport { parseJsonObjectDocument, parseSettingsDocument } from \"./json-document.js\";\nimport { COUPLED_FIELDS, mergeSettingsJson } from \"./settings-merge.js\";\nimport { edits, type Hunk, isMergeTextPath, lines, text } from \"./text-merge.js\";\n\nexport interface AgentUnit {\n id: string;\n path: string;\n kind: \"text\" | \"settings\";\n base: string;\n local: string;\n remote: string;\n}\ninterface CandidateFile {\n path: string;\n base: string;\n local: string;\n remote: string;\n parts?: (string | AgentUnit)[];\n fields?: { id: string; key: string }[];\n}\nexport interface AgentEvidence {\n units: AgentUnit[];\n files: CandidateFile[];\n unsupported: string[];\n}\nexport interface AgentCandidate {\n version: 1;\n results: (\n | { id: string; content: string }\n | { id: string; source: \"local\" | \"remote\" }\n | { id: string; refuse: true }\n )[];\n}\nexport const digest = (value: unknown) => createHash(\"sha256\").update(JSON.stringify(value)).digest(\"hex\");\n\nfunction overlaps(a: Hunk, b: Hunk) {\n return a.start === a.end || b.start === b.end\n ? a.start <= b.end && b.start <= a.end\n : a.start < b.end && b.start < a.end;\n}\n\n/** Only overlapping edit regions are model-writable; all other bytes are assembled by code. */\nfunction textParts(filePath: string, baseText: string, localText: string, remoteText: string): (string | AgentUnit)[] {\n const base = lines(baseText);\n const changes = [\n ...edits(base, lines(localText)).map((hunk) => ({ ...hunk, side: \"local\" as const })),\n ...edits(base, lines(remoteText)).map((hunk) => ({ ...hunk, side: \"remote\" as const })),\n ].sort((a, b) => a.start - b.start || a.end - b.end);\n const clusters: (typeof changes)[] = [];\n for (const change of changes) {\n const last = clusters.at(-1);\n if (last?.some((item) => overlaps(item, change))) last.push(change);\n else clusters.push([change]);\n }\n const result: (string | AgentUnit)[] = [];\n let cursor = 0;\n for (const cluster of clusters) {\n const start = Math.min(...cluster.map((item) => item.start));\n const end = Math.max(...cluster.map((item) => item.end));\n result.push(base.slice(cursor, start).join(\"\"));\n const image = (side: \"local\" | \"remote\") => {\n let position = start;\n const output: string[] = [];\n for (const hunk of cluster.filter((item) => item.side === side)) {\n output.push(...base.slice(position, hunk.start), ...hunk.lines);\n position = hunk.end;\n }\n return [...output, ...base.slice(position, end)].join(\"\");\n };\n const local = image(\"local\");\n const remote = image(\"remote\");\n const original = base.slice(start, end).join(\"\");\n if (local === remote || local === original || remote === original) result.push(local === original ? remote : local);\n else result.push({ id: \"\", path: filePath, kind: \"text\", base: original, local, remote });\n cursor = end;\n }\n result.push(base.slice(cursor).join(\"\"));\n return result;\n}\n\nexport function buildAgentEvidence(\n plan: FileMergePlan,\n local: Snapshot,\n remote: Snapshot,\n ancestors: SnapshotFile[],\n): AgentEvidence {\n const evidence: AgentEvidence = { units: [], files: [], unsupported: [] };\n if (plan.kind !== \"planned\") return evidence;\n if (plan.conflicts.length > AGENT_MAX_UNITS)\n throw new Error(\"Agent mode paused: too many conflict paths for bounded analysis.\");\n for (const conflict of plan.conflicts) {\n try {\n if (conflict.reason !== \"both-changed\") throw new Error(\"unsupported conflict\");\n const versions = [ancestors, local.files, remote.files].map((files) => {\n const file = files.find((item) => item.path === conflict.path);\n if (!file) throw new Error(\"missing ancestor/version\");\n const bytes = Buffer.from(file.contentBase64, \"base64\");\n if (bytes.length > 8192) throw new Error(\"file bound\");\n return text(bytes);\n }) as [string, string, string];\n const [base, left, right] = versions;\n const file: CandidateFile = { path: conflict.path, base, local: left, remote: right };\n let units: AgentUnit[];\n if (conflict.path === \"settings.json\") {\n const documents = versions.map((value) => parseSettingsDocument(Buffer.from(value)));\n const merge = mergeSettingsJson(...(versions.map((value) => Buffer.from(value)) as [Buffer, Buffer, Buffer]));\n if (merge.kind !== \"review\" || merge.reason !== \"field-conflict\") throw new Error(\"unsupported settings\");\n const fields = new Set(merge.fields);\n const groups = COUPLED_FIELDS.map((group) => [...group] as string[]);\n const coupled = new Set(groups.flat());\n groups.push(...[...fields].filter((key) => !coupled.has(key)).map((key) => [key]));\n file.fields = [];\n units = groups\n .filter((group) => group.some((key) => fields.has(key)))\n .map((group) => {\n const values = documents.map((document) =>\n JSON.stringify(\n Object.fromEntries(\n group.flatMap((key) => {\n const node = document.root.members.get(key)?.node;\n return node ? [[key, node.value]] : [];\n }),\n ),\n ),\n );\n const unit: AgentUnit = {\n id: \"\",\n path: file.path,\n kind: \"settings\",\n base: values[0] ?? \"{}\",\n local: values[1] ?? \"{}\",\n remote: values[2] ?? \"{}\",\n };\n file.fields?.push({ id: String(evidence.units.length + file.fields.length), key: group[0] ?? \"\" });\n return unit;\n });\n } else {\n if (!isMergeTextPath(conflict.path)) throw new Error(\"unsupported path\");\n file.parts = textParts(file.path, base, left, right);\n units = file.parts.filter((part): part is AgentUnit => typeof part !== \"string\");\n }\n for (const unit of units) {\n unit.id = String(evidence.units.length);\n evidence.units.push(unit);\n }\n evidence.files.push(file);\n } catch {\n evidence.unsupported.push(conflict.path);\n }\n }\n if (evidence.units.length > AGENT_MAX_UNITS || Buffer.byteLength(JSON.stringify(evidence.units)) > AGENT_INPUT_BYTES)\n throw new Error(\"Agent mode paused: conflict evidence exceeds its input bound.\");\n // Scan only the actual disclosure image; skipSecretScan never authorizes model disclosure.\n if (\n scanSnapshot({\n ...local,\n files: evidence.units.flatMap((unit) =>\n [unit.path, unit.base, unit.local, unit.remote].map((value) => snapshotFile(unit.path, Buffer.from(value))),\n ),\n }).length\n )\n throw new Error(\"Agent mode paused: possible secrets in model evidence; nothing was disclosed.\");\n return evidence;\n}\n\nexport function parseAgentCandidate(raw: string, evidence: AgentEvidence): AgentCandidate {\n if (Buffer.byteLength(raw) > AGENT_OUTPUT_BYTES) throw new Error(\"Agent output exceeds its bound.\");\n const parsed = parseJsonObjectDocument(Buffer.from(raw)).root.value as Record<string, unknown>;\n if (\n Object.keys(parsed).sort().join(\",\") !== \"results,version\" ||\n parsed.version !== 1 ||\n !Array.isArray(parsed.results) ||\n parsed.results.length !== evidence.units.length\n )\n throw new Error(\"Invalid agent candidate contract.\");\n const seen = new Set<string>();\n for (const result of parsed.results) {\n if (!result || typeof result !== \"object\" || Array.isArray(result)) throw new Error(\"Invalid agent result.\");\n const row = result as Record<string, unknown>;\n const unit = evidence.units.find((unit) => unit.id === row.id);\n if (!unit || seen.has(unit.id)) throw new Error(\"Unowned or duplicate agent unit.\");\n seen.add(unit.id);\n const keys = Object.keys(row).sort().join(\",\");\n if (keys === \"id,refuse\" && row.refuse === true) continue;\n if (unit.kind === \"settings\" && keys === \"id,source\" && (row.source === \"local\" || row.source === \"remote\"))\n continue;\n if (unit.kind === \"text\" && keys === \"content,id\" && typeof row.content === \"string\" && row.content.length > 0) {\n text(Buffer.from(row.content));\n if (/^(?:<{7}|={7}|>{7}|\\|{7})/mu.test(row.content)) throw new Error(\"Unresolved conflict markers.\");\n continue;\n }\n throw new Error(\"Unsupported agent action.\");\n }\n return parsed as unknown as AgentCandidate;\n}\n\nexport function applyAgentCandidate(\n plan: FileMergePlan,\n evidence: AgentEvidence,\n candidate: AgentCandidate,\n): FileMergePlan {\n // Persisted candidates cross a trust boundary too.\n parseAgentCandidate(JSON.stringify(candidate), evidence);\n if (plan.kind !== \"planned\") throw new Error(\"Unverified merge plan.\");\n const results = new Map(candidate.results.map((row) => [row.id, row]));\n const replacements = new Map<string, SnapshotFile>();\n for (const file of evidence.files) {\n const units = evidence.units.filter((unit) => unit.path === file.path);\n if (units.some((unit) => results.get(unit.id) && \"refuse\" in (results.get(unit.id) ?? {}))) continue;\n let bytes: Buffer;\n if (file.parts) {\n bytes = Buffer.from(\n file.parts\n .map((part) => (typeof part === \"string\" ? part : (results.get(part.id) as { content: string }).content))\n .join(\"\"),\n );\n } else {\n const selections = new Map(\n (file.fields ?? []).map(({ id, key }) => [key, (results.get(id) as { source: \"local\" | \"remote\" }).source]),\n );\n const merged = mergeSettingsJson(\n Buffer.from(file.base),\n Buffer.from(file.local),\n Buffer.from(file.remote),\n selections,\n );\n if (merged.kind !== \"merged\") throw new Error(\"Agent settings units remain unresolved.\");\n // The existing renderer/parser retains atomic values, unknown members and exact numeric policy.\n parseSettingsDocument(merged.content);\n for (const { key } of file.fields ?? []) {\n if (!selections.has(key)) throw new Error(\"Missing atomic settings selection.\");\n }\n bytes = merged.content;\n }\n if (bytes.length > AGENT_OUTPUT_BYTES) throw new Error(\"Agent file exceeds output bound.\");\n replacements.set(file.path, snapshotFile(file.path, bytes));\n }\n const decisions = plan.decisions.map((decision) => {\n const file = replacements.get(decision.path);\n return file && decision.kind === \"conflict\"\n ? { kind: \"accepted\" as const, path: file.path, source: \"merged\" as const, file }\n : decision;\n });\n return { kind: \"planned\", decisions, conflicts: decisions.filter((item) => item.kind === \"conflict\") };\n}\n"],
5
- "mappings": "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",
6
- "names": ["file", "unit"]
7
- }