import { splitLines } from "../git/merge3"; import { hunkQuestionId } from "./decide"; import type { ClefRequest, ClefResponse } from "./decide"; import type { MergeFixProvider } from "./plan"; const SECURITY_HINT = /password|secret|token|auth|crypto|permission/i; /** Union of both sides' lines, keeping head order first: a plausible deterministic "AI" resolution. */ function unionLines(ours: string, theirs: string): string { const lines = splitLines(ours); const seen = new Set(lines); for (const line of splitLines(theirs)) if (!seen.has(line)) lines.push(line); return lines.join(""); } function spread(ids: string[], pick: string, top: number): Record { const rest = ids.length > 1 ? (1 - top) / (ids.length - 1) : 0; return Object.fromEntries(ids.map((id) => [id, id === pick ? top : rest])); } /** * Deterministic stand-in for the generative model and Clef, selected with MERGEFIX_PROVIDER=mock. It picks * the first AI candidate with high confidence and flags anything that mentions credentials as security-sensitive. */ export function mockProvider(): MergeFixProvider { return { async generate({ hunk }) { return { text: `\`\`\`merge\n${unionLines(hunk.ours, hunk.theirs)}\`\`\``, usage: { inputTokens: 100, outputTokens: 20 }, }; }, async decide(request: ClefRequest): Promise { const answers: ClefResponse["answers"] = {}; for (const [id, question] of Object.entries(request.questions)) { if (question.type !== "choice") continue; const ids = Object.keys(question.criteria); const pick = ids.find((option) => option.startsWith("ai_")) ?? ids.find((option) => option === "ours_then_theirs") ?? ids[0]; const probabilities = spread(ids, pick, 0.9); answers[id] = { type: "choice", choice: pick, probabilities, confidence: 0.9 }; } const sensitive = SECURITY_HINT.test(JSON.stringify(request.state)); const risk = sensitive ? [0.05, 0.15, 0.8] : [0.9, 0.08, 0.02]; answers.risk = { type: "score", score: risk[1] + 2 * risk[2], probabilities: { "0": risk[0], "1": risk[1], "2": risk[2] }, confidence: Math.max(...risk), }; answers.needs_human = { type: "noul", noul: sensitive ? 0.7 : 0.05 }; if (!answers[hunkQuestionId(0)]) answers.needs_human = { type: "noul", noul: 1 }; return { answers, usage: { input_tokens: 500, output_tokens: 0 } }; }, }; }