import {
  FallbackLlmClient
} from "./chunk-CRU27Q4J.js";
import "./chunk-Q7FJ5ZHM.js";
import "./chunk-ODWDQNRE.js";
import "./chunk-Y27UJK6V.js";
import "./chunk-UZB5KHKX.js";
import "./chunk-MARWOCVP.js";
import {
  log
} from "./chunk-2ODBA7MQ.js";
import {
  listJsonFiles
} from "./chunk-LPSF4OQH.js";
import "./chunk-PZ5AY32C.js";

// src/calibration.ts
import { createHash } from "crypto";
import path from "path";
import { mkdir, readFile, writeFile } from "fs/promises";
function calibrationDir(memoryDir) {
  return path.join(memoryDir, "state", "calibration");
}
function calibrationIndexPath(memoryDir) {
  return path.join(calibrationDir(memoryDir), "calibration-index.json");
}
async function readCalibrationIndex(memoryDir) {
  try {
    const raw = JSON.parse(await readFile(calibrationIndexPath(memoryDir), "utf8"));
    return {
      rules: Array.isArray(raw.rules) ? raw.rules : [],
      updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : (/* @__PURE__ */ new Date()).toISOString(),
      totalCorrectionsAnalyzed: typeof raw.totalCorrectionsAnalyzed === "number" ? raw.totalCorrectionsAnalyzed : 0
    };
  } catch {
    return { rules: [], updatedAt: (/* @__PURE__ */ new Date()).toISOString(), totalCorrectionsAnalyzed: 0 };
  }
}
async function writeCalibrationIndex(memoryDir, index) {
  const dir = calibrationDir(memoryDir);
  await mkdir(dir, { recursive: true });
  index.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
  await writeFile(calibrationIndexPath(memoryDir), JSON.stringify(index, null, 2), "utf8");
}
async function readCalibrationCorrections(memoryDir) {
  return readCorrectionsImpl(memoryDir);
}
async function readCorrections(memoryDir) {
  return readCorrectionsImpl(memoryDir);
}
async function readCorrectionsImpl(memoryDir) {
  const correctionsDir = path.join(memoryDir, "corrections");
  const files = await listJsonFiles(correctionsDir).catch(() => {
    return [];
  });
  const factsDir = path.join(memoryDir, "facts");
  try {
    const { readdir } = await import("fs/promises");
    const dayDirs = (await readdir(factsDir)).filter((d) => /^\d{4}-\d{2}-\d{2}$/.test(d));
    for (const day of dayDirs) {
      const dayPath = path.join(factsDir, day);
      const dayFiles = (await readdir(dayPath)).filter((f) => f.startsWith("correction-") && f.endsWith(".md")).map((f) => path.join(dayPath, f));
      files.push(...dayFiles);
    }
  } catch {
  }
  try {
    const { readdir } = await import("fs/promises");
    const corrFiles = (await readdir(correctionsDir)).filter((f) => f.endsWith(".md")).map((f) => path.join(correctionsDir, f));
    files.push(...corrFiles);
  } catch {
  }
  const corrections = [];
  const seen = /* @__PURE__ */ new Set();
  for (const filePath of files) {
    try {
      const raw = await readFile(filePath, "utf8");
      const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
      if (!fmMatch) continue;
      const content = fmMatch[2].trim();
      if (!content || content.length < 10) continue;
      const idMatch = fmMatch[1].match(/^id:\s*(.+)$/m);
      const id = idMatch?.[1]?.trim() ?? path.basename(filePath, ".md");
      if (seen.has(id)) continue;
      seen.add(id);
      const confMatch = fmMatch[1].match(/^confidence:\s*(.+)$/m);
      const confidence = confMatch ? parseFloat(confMatch[1]) : 0.9;
      const entityMatch = fmMatch[1].match(/^entityRef:\s*(.+)$/m);
      const entityRefs = entityMatch ? [entityMatch[1].trim()] : [];
      corrections.push({ id, content, created: "", confidence, entityRefs, tags: [] });
    } catch {
    }
  }
  return corrections;
}
var CLUSTER_PROMPT = `You are analyzing user corrections to an AI assistant. Each correction represents a moment where the assistant's prediction of what the user wanted was WRONG.

Your job: Group these corrections into clusters where the SAME TYPE of misunderstanding is happening. Then for each cluster, synthesize a CalibrationRule.

A CalibrationRule describes:
- condition: When does this type of mistake happen?
- modelTendency: What does the model tend to assume or do wrong?
- userExpectation: What does the user actually want instead?
- calibration: How should the model adjust its behavior?
- ruleType: One of "model_tendency", "user_expectation", "scope_boundary", "verification_required"

Focus on PATTERNS, not individual corrections. A cluster needs at least 2 corrections to be worth a rule.

Output valid JSON only:
{
  "rules": [
    {
      "ruleType": "model_tendency",
      "condition": "When discussing project scope or task boundaries",
      "modelTendency": "The model tends to assume broader scope than the user intends",
      "userExpectation": "The user prefers narrow, specific task definitions and wants to be asked before scope expansion",
      "calibration": "When uncertain about scope, ask for clarification rather than assuming. Default to the narrower interpretation.",
      "confidence": 0.85,
      "evidenceIds": ["correction-id-1", "correction-id-2"]
    }
  ]
}`;
async function synthesizeCalibrationRules(corrections, llm, existingRules, agentId) {
  if (corrections.length < 2) return [];
  const correctionText = corrections.slice(0, 50).map((c, i) => `[${c.id}] ${c.content}`).join("\n\n");
  const existingRulesText = existingRules.length > 0 ? `

Existing calibration rules (avoid duplicating these):
${existingRules.map((r) => `- ${r.condition}: ${r.calibration}`).join("\n")}` : "";
  const response = await llm.chatCompletion(
    [
      { role: "system", content: CLUSTER_PROMPT },
      { role: "user", content: `Here are ${corrections.length} corrections from this user:

${correctionText}${existingRulesText}` }
    ],
    { temperature: 0.3, maxTokens: 3e3, agentId }
  );
  if (!response?.content) return [];
  try {
    let jsonStr = response.content.trim();
    const fenceMatch = jsonStr.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
    if (fenceMatch) jsonStr = fenceMatch[1];
    const parsed = JSON.parse(jsonStr);
    if (!Array.isArray(parsed.rules)) return [];
    const now = (/* @__PURE__ */ new Date()).toISOString();
    return parsed.rules.filter((r) => r.condition && r.calibration && r.modelTendency).map((r) => ({
      id: `cal-${createHash("sha256").update(r.condition + r.calibration).digest("hex").slice(0, 12)}`,
      ruleType: r.ruleType ?? "model_tendency",
      condition: String(r.condition),
      modelTendency: String(r.modelTendency),
      userExpectation: String(r.userExpectation ?? ""),
      calibration: String(r.calibration),
      confidence: typeof r.confidence === "number" ? r.confidence : 0.7,
      evidenceCount: Array.isArray(r.evidenceIds) ? r.evidenceIds.length : 1,
      evidenceCorrectionIds: Array.isArray(r.evidenceIds) ? r.evidenceIds : [],
      createdAt: now,
      lastReinforcedAt: now
    }));
  } catch {
    log.warn("[calibration] failed to parse LLM response");
    return [];
  }
}
function buildCalibrationRecallSection(rules, query, maxChars = 1200) {
  if (rules.length === 0) return null;
  const lines = [
    "## Model Calibration (learned from past corrections)",
    "",
    "Adjustments for this specific user, learned from patterns in their corrections:",
    ""
  ];
  let totalChars = lines.join("\n").length;
  for (const rule of rules) {
    const line = `- **${rule.condition}**: ${rule.modelTendency} \u2192 Instead: ${rule.calibration}`;
    if (totalChars + line.length + 1 > maxChars) break;
    lines.push(line);
    totalChars += line.length + 1;
  }
  if (lines.length <= 4) return null;
  lines.push("");
  return lines.join("\n");
}
async function runCalibrationConsolidation(options) {
  try {
    const llm = new FallbackLlmClient(options.gatewayConfig);
    if (!llm.isAvailable(options.gatewayAgentId)) {
      log.debug("[calibration] no LLM available \u2014 skipping consolidation");
      return [];
    }
    const corrections = await readCorrections(options.memoryDir);
    if (corrections.length < 3) {
      log.debug(`[calibration] only ${corrections.length} corrections \u2014 need at least 3`);
      return [];
    }
    const existingIndex = await readCalibrationIndex(options.memoryDir);
    const newRules = await synthesizeCalibrationRules(corrections, llm, existingIndex.rules, options.gatewayAgentId);
    if (newRules.length === 0) {
      log.debug("[calibration] no new calibration rules synthesized");
      return existingIndex.rules;
    }
    const ruleMap = new Map(existingIndex.rules.map((r) => [r.id, r]));
    for (const rule of newRules) {
      if (ruleMap.has(rule.id)) {
        const existing = ruleMap.get(rule.id);
        existing.lastReinforcedAt = (/* @__PURE__ */ new Date()).toISOString();
        existing.evidenceCount += rule.evidenceCount;
        existing.confidence = Math.min(1, existing.confidence + 0.05);
      } else {
        ruleMap.set(rule.id, rule);
      }
    }
    const allRules = [...ruleMap.values()];
    await writeCalibrationIndex(options.memoryDir, {
      rules: allRules,
      updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
      totalCorrectionsAnalyzed: corrections.length
    });
    log.debug(`[calibration] synthesized ${newRules.length} new rule(s), ${allRules.length} total`);
    return allRules;
  } catch (error) {
    log.warn(`[calibration] consolidation failed (non-fatal): ${error instanceof Error ? error.message : String(error)}`);
    return [];
  }
}
async function runCalibrationIfEnabled(options) {
  if (!options.calibrationEnabled) {
    return [];
  }
  return runCalibrationConsolidation({
    memoryDir: options.memoryDir,
    gatewayConfig: options.gatewayConfig
  });
}
async function getCalibrationRulesForRecall(memoryDir) {
  const index = await readCalibrationIndex(memoryDir);
  return index.rules;
}
export {
  buildCalibrationRecallSection,
  getCalibrationRulesForRecall,
  readCalibrationCorrections,
  readCalibrationIndex,
  runCalibrationConsolidation,
  runCalibrationIfEnabled,
  synthesizeCalibrationRules
};
//# sourceMappingURL=calibration.js.map