import a from './index5.mjs';
import { n as nanoid } from '../shared/ccjk.BoApaI4j.mjs';
import { T as TaskPersistence } from '../shared/ccjk.CNhnT6uQ.mjs';
import { m as matter } from '../shared/ccjk.y-a_1vK4.mjs';
import { writeFile, readFile } from './fs-operations.mjs';
import { j as join, d as dirname } from '../shared/ccjk.bQ7Dh1g4.mjs';
import { existsSync, readFileSync, appendFileSync, writeFileSync, mkdirSync } from 'node:fs';
import { homedir } from 'node:os';
import { e as executeCommand } from '../shared/ccjk.BnsY5WxD.mjs';
import '../shared/ccjk.BAGoDD49.mjs';
import 'node:crypto';
import 'better-sqlite3';
import 'fs';
import '../shared/ccjk.COweQ1RR.mjs';
import 'node:fs/promises';
import 'node:child_process';
import 'node:util';

const DEFAULT_RESEARCH_PROGRAM_PATH = ".ccjk/research/program.md";
const DEFAULT_RESEARCH_MAX_ROUNDS = 10;
const DEFAULT_RESEARCH_MAX_NO_IMPROVE_ROUNDS = 3;
const DEFAULT_RESEARCH_BUDGET_MS = 5 * 60 * 1e3;
function normalizePositiveInteger(value, fallback) {
  if (typeof value === "number" && Number.isInteger(value) && value > 0) {
    return value;
  }
  if (typeof value === "string" && value.trim()) {
    const parsed = Number.parseInt(value.trim(), 10);
    if (Number.isInteger(parsed) && parsed > 0) {
      return parsed;
    }
  }
  return fallback;
}
function normalizeOptionalNumber(value) {
  if (typeof value === "number" && Number.isFinite(value)) {
    return value;
  }
  if (typeof value === "string" && value.trim()) {
    const parsed = Number.parseFloat(value.trim());
    if (Number.isFinite(parsed)) {
      return parsed;
    }
  }
  return void 0;
}
function normalizeObjective(value) {
  return value === "maximize" || value === "minimize" || value === "auto" ? value : "auto";
}
function normalizeString(value) {
  if (typeof value !== "string") {
    return void 0;
  }
  const trimmed = value.trim();
  return trimmed || void 0;
}
function resolveResearchProgramPath(programPath, cwd) {
  return programPath ? join(cwd || process.cwd(), programPath) : join(cwd || process.cwd(), DEFAULT_RESEARCH_PROGRAM_PATH);
}
function createDefaultResearchProgramTemplate(cwd) {
  const workingDirectory = cwd || process.cwd();
  return `---
name: repo-research
metric: test_pass_rate
objective: maximize
baselineCommand: pnpm test:run
candidateCommand: pnpm test:run
cwd: ${workingDirectory}
maxRounds: ${DEFAULT_RESEARCH_MAX_ROUNDS}
maxNoImproveRounds: ${DEFAULT_RESEARCH_MAX_NO_IMPROVE_ROUNDS}
budgetMs: ${DEFAULT_RESEARCH_BUDGET_MS}
targetMetric:
---

# Research Goal

Describe the optimization target for this repository.

## Guardrails

- Keep changes scoped and measurable.
- Prefer one candidate change per round.
- Stop when the target metric is reached or repeated no-improvement rounds occur.

## Notes

Document hypotheses, constraints, and any files or modules that must not change.
`;
}
function parseResearchProgram(content, programPath = DEFAULT_RESEARCH_PROGRAM_PATH) {
  const parsed = matter(content);
  const data = parsed.data || {};
  const baselineCommand = normalizeString(data.baselineCommand);
  if (!baselineCommand) {
    throw new Error(`Missing required 'baselineCommand' in ${programPath}`);
  }
  const candidateCommand = normalizeString(data.candidateCommand);
  if (!candidateCommand) {
    throw new Error(`Missing required 'candidateCommand' in ${programPath}`);
  }
  return {
    name: normalizeString(data.name) || "repo-research",
    metric: normalizeString(data.metric),
    objective: normalizeObjective(data.objective),
    baselineCommand,
    candidateCommand,
    cwd: normalizeString(data.cwd) || process.cwd(),
    maxRounds: normalizePositiveInteger(data.maxRounds, DEFAULT_RESEARCH_MAX_ROUNDS),
    maxNoImproveRounds: normalizePositiveInteger(data.maxNoImproveRounds, DEFAULT_RESEARCH_MAX_NO_IMPROVE_ROUNDS),
    budgetMs: normalizePositiveInteger(data.budgetMs, DEFAULT_RESEARCH_BUDGET_MS),
    targetMetric: normalizeOptionalNumber(data.targetMetric),
    notes: parsed.content.trim(),
    programPath
  };
}
function readResearchProgram(programPath, cwd) {
  const resolvedPath = resolveResearchProgramPath(programPath, cwd);
  const content = readFile(resolvedPath);
  return parseResearchProgram(content, resolvedPath);
}
function initResearchProgram(programPath, cwd) {
  const resolvedPath = resolveResearchProgramPath(programPath, cwd);
  const content = createDefaultResearchProgramTemplate(cwd);
  writeFile(resolvedPath, content);
  return {
    programPath: resolvedPath,
    content
  };
}

class TaskQueue {
  queue = [];
  runningTasks = /* @__PURE__ */ new Set();
  stats;
  options;
  taskIdCounter = 0;
  executionTimes = [];
  processing = false;
  paused = false;
  constructor(options = {}) {
    this.options = {
      concurrency: options.concurrency ?? Number.POSITIVE_INFINITY,
      defaultTimeout: options.defaultTimeout ?? 3e4,
      defaultMaxRetries: options.defaultMaxRetries ?? 0,
      defaultRetryDelay: options.defaultRetryDelay ?? 1e3,
      autoStart: options.autoStart ?? true
    };
    this.stats = {
      totalTasks: 0,
      pendingTasks: 0,
      runningTasks: 0,
      completedTasks: 0,
      failedTasks: 0,
      timeoutTasks: 0,
      averageExecutionTime: 0,
      createdAt: Date.now()
    };
    if (this.options.autoStart) {
      this.start();
    }
  }
  /**
   * Add a task to the queue
   */
  add(execute, options = {}) {
    return new Promise((resolve, reject) => {
      const task = {
        id: this.generateTaskId(),
        execute,
        options: {
          priority: options.priority ?? "normal",
          timeout: options.timeout ?? this.options.defaultTimeout,
          maxRetries: options.maxRetries ?? this.options.defaultMaxRetries,
          retryDelay: options.retryDelay ?? this.options.defaultRetryDelay,
          retryBackoff: options.retryBackoff ?? 1,
          metadata: options.metadata ?? {}
        },
        createdAt: Date.now(),
        retryCount: 0,
        status: "pending",
        resolve,
        reject
      };
      this.queue.push(task);
      this.stats.totalTasks++;
      this.stats.pendingTasks++;
      this.sortQueue();
      if (!this.paused) {
        this.processQueue();
      }
    });
  }
  /**
   * Start queue processing
   */
  start() {
    this.paused = false;
    this.processQueue();
  }
  /**
   * Pause queue processing
   */
  pause() {
    this.paused = true;
  }
  /**
   * Resume queue processing
   */
  resume() {
    this.start();
  }
  /**
   * Clear all pending tasks
   */
  clear() {
    const pendingTasks = this.queue.filter((task) => task.status === "pending");
    pendingTasks.forEach((task) => {
      task.status = "failed";
      task.reject(new Error("Task cancelled: queue cleared"));
    });
    this.queue = this.queue.filter((task) => task.status === "running");
    this.stats.pendingTasks = 0;
  }
  /**
   * Wait for all tasks to complete
   */
  async drain() {
    while (this.queue.length > 0 || this.runningTasks.size > 0) {
      await new Promise((resolve) => setTimeout(resolve, 100));
    }
  }
  /**
   * Get queue statistics
   */
  getStats() {
    return { ...this.stats };
  }
  /**
   * Get current queue size
   */
  size() {
    return this.queue.length;
  }
  /**
   * Check if queue is empty
   */
  isEmpty() {
    return this.queue.length === 0 && this.runningTasks.size === 0;
  }
  /**
   * Check if queue is paused
   */
  isPaused() {
    return this.paused;
  }
  /**
   * Process tasks in the queue
   */
  async processQueue() {
    if (this.processing || this.paused) {
      return;
    }
    this.processing = true;
    while (!this.paused && this.queue.length > 0 && this.runningTasks.size < this.options.concurrency) {
      const task = this.queue.find((t) => t.status === "pending");
      if (!task) {
        break;
      }
      task.status = "running";
      this.stats.pendingTasks--;
      this.stats.runningTasks++;
      this.runningTasks.add(task.id);
      this.executeTask(task).catch(() => {
      });
    }
    this.processing = false;
  }
  /**
   * Execute a single task with timeout and retry support
   */
  async executeTask(task) {
    const startTime = Date.now();
    try {
      const timeoutPromise = new Promise((_, reject) => {
        setTimeout(() => {
          reject(new Error(`Task timeout after ${task.options.timeout}ms`));
        }, task.options.timeout);
      });
      const result = await Promise.race([
        task.execute(),
        timeoutPromise
      ]);
      const executionTime = Date.now() - startTime;
      this.recordExecutionTime(executionTime);
      task.status = "completed";
      this.stats.completedTasks++;
      task.resolve(result);
    } catch (error) {
      const isTimeout = error instanceof Error && error.message.includes("timeout");
      if (isTimeout) {
        task.status = "timeout";
        this.stats.timeoutTasks++;
      }
      if (task.retryCount < task.options.maxRetries) {
        task.retryCount++;
        task.status = "pending";
        const delay = task.options.retryDelay * task.options.retryBackoff ** (task.retryCount - 1);
        setTimeout(() => {
          this.stats.pendingTasks++;
          this.sortQueue();
          this.processQueue();
        }, delay);
      } else {
        task.status = "failed";
        this.stats.failedTasks++;
        task.reject(error instanceof Error ? error : new Error(String(error)));
      }
    } finally {
      this.runningTasks.delete(task.id);
      this.stats.runningTasks--;
      if (task.status === "completed" || task.status === "failed" || task.status === "timeout") {
        this.queue = this.queue.filter((t) => t.id !== task.id);
      }
      this.processQueue();
    }
  }
  /**
   * Sort queue by priority
   */
  sortQueue() {
    const priorityOrder = {
      critical: 0,
      high: 1,
      normal: 2,
      low: 3
    };
    this.queue.sort((a, b) => {
      const priorityDiff = priorityOrder[a.options.priority] - priorityOrder[b.options.priority];
      if (priorityDiff !== 0) {
        return priorityDiff;
      }
      return a.createdAt - b.createdAt;
    });
  }
  /**
   * Generate unique task ID
   */
  generateTaskId() {
    return `task_${++this.taskIdCounter}_${Date.now()}`;
  }
  /**
   * Record task execution time for statistics
   */
  recordExecutionTime(time) {
    this.executionTimes.push(time);
    if (this.executionTimes.length > 100) {
      this.executionTimes.shift();
    }
    const sum = this.executionTimes.reduce((acc, t) => acc + t, 0);
    this.stats.averageExecutionTime = sum / this.executionTimes.length;
  }
}

const DEFAULT_BUDGET_MS = 5 * 60 * 1e3;
const DEFAULT_SESSION_LIMIT = 10;
const RESEARCH_SESSION_KIND = "research";
const RESULTS_LEDGER_FILE = "research-results.tsv";
const RESULTS_LEDGER_HEADERS = [
  "timestamp",
  "sessionId",
  "taskId",
  "name",
  "phase",
  "verdict",
  "status",
  "exitCode",
  "metricName",
  "metricValue",
  "durationMs",
  "cwd",
  "command"
];
class ResearchCommandError extends Error {
  constructor(result) {
    super(result.error || `Command exited with code ${result.exitCode}`);
    this.result = result;
    this.name = "ResearchCommandError";
  }
}
function createPersistence$1(dbPath) {
  return new TaskPersistence(dbPath);
}
function getResearchDataDir(dbPath) {
  const resolvedDbPath = dbPath || join(homedir(), ".ccjk", "brain.db");
  const dir = dirname(resolvedDbPath);
  if (!existsSync(dir)) {
    mkdirSync(dir, { recursive: true });
  }
  return dir;
}
function getResultsLedgerPath(dbPath) {
  return join(getResearchDataDir(dbPath), RESULTS_LEDGER_FILE);
}
function escapeRegex(value) {
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function extractMetricValue(output, metricName) {
  if (!metricName) {
    return void 0;
  }
  const matcher = new RegExp(`(?:^|\\b)${escapeRegex(metricName)}\\s*[:=]\\s*(-?\\d+(?:\\.\\d+)?)`, "im");
  const match = output.match(matcher);
  if (!match) {
    return void 0;
  }
  const value = Number.parseFloat(match[1]);
  return Number.isFinite(value) ? value : void 0;
}
function getFailureStatus(result, error) {
  const message = [result.error, result.stderr, error?.message].filter(Boolean).join(" ");
  return /timed out/i.test(message) ? "timeout" : "failed";
}
function inferResearchPhase(name) {
  return /baseline/i.test(name) ? "baseline" : "experiment";
}
function buildPhaseHistory(runPhase) {
  return ["brief", runPhase, "verify", "report"];
}
function sanitizeLedgerField(value) {
  if (value === void 0 || value === null) {
    return "";
  }
  return String(value).replace(/\t/g, " ").replace(/\r?\n/g, " ").trim();
}
function ensureResultsLedger(dbPath) {
  const ledgerPath = getResultsLedgerPath(dbPath);
  if (!existsSync(ledgerPath)) {
    writeFileSync(ledgerPath, `${RESULTS_LEDGER_HEADERS.join("	")}
`, "utf8");
  }
  return ledgerPath;
}
function serializeResultRow(row) {
  const values = {
    timestamp: sanitizeLedgerField(row.timestamp),
    sessionId: sanitizeLedgerField(row.sessionId),
    taskId: sanitizeLedgerField(row.taskId),
    name: sanitizeLedgerField(row.name),
    phase: sanitizeLedgerField(row.phase),
    verdict: sanitizeLedgerField(row.verdict),
    status: sanitizeLedgerField(row.status),
    exitCode: sanitizeLedgerField(row.exitCode),
    metricName: sanitizeLedgerField(row.metricName),
    metricValue: sanitizeLedgerField(row.metricValue),
    durationMs: sanitizeLedgerField(row.durationMs),
    cwd: sanitizeLedgerField(row.cwd),
    command: sanitizeLedgerField(row.command)
  };
  return RESULTS_LEDGER_HEADERS.map((header) => values[header]).join("	");
}
function parseResultRow(line) {
  if (!line.trim()) {
    return null;
  }
  const columns = line.split("	");
  if (columns.length < RESULTS_LEDGER_HEADERS.length) {
    return null;
  }
  const [
    timestamp,
    sessionId,
    taskId,
    name,
    phase,
    verdict,
    status,
    exitCode,
    metricName,
    metricValue,
    durationMs,
    cwd,
    command
  ] = columns;
  const parsedMetric = metricValue ? Number.parseFloat(metricValue) : void 0;
  return {
    timestamp,
    sessionId,
    taskId,
    name,
    phase: phase || "experiment",
    verdict: verdict || "PARTIAL",
    status,
    exitCode: Number.parseInt(exitCode, 10) || 0,
    metricName: metricName || void 0,
    metricValue: Number.isFinite(parsedMetric) ? parsedMetric : void 0,
    durationMs: Number.parseInt(durationMs, 10) || 0,
    cwd,
    command
  };
}
function appendResultRow(row, dbPath) {
  const ledgerPath = ensureResultsLedger(dbPath);
  appendFileSync(ledgerPath, `${serializeResultRow(row)}
`, "utf8");
}
function readAllResultRows(dbPath) {
  const ledgerPath = getResultsLedgerPath(dbPath);
  if (!existsSync(ledgerPath)) {
    return [];
  }
  const content = readFileSync(ledgerPath, "utf8");
  const lines = content.split(/\r?\n/).filter(Boolean);
  if (lines.length <= 1) {
    return [];
  }
  return lines.slice(1).map(parseResultRow).filter((row) => Boolean(row));
}
function resolveResearchObjective(metricName, objective = "auto") {
  if (objective === "maximize" || objective === "minimize") {
    return objective;
  }
  const normalized = (metricName || "").toLowerCase();
  if (!normalized) {
    return "maximize";
  }
  const lowerIsBetter = ["loss", "error", "bpb", "perplexity", "ppl", "latency", "duration", "time", "cost", "price", "wer", "cer"];
  if (lowerIsBetter.some((keyword) => normalized.includes(keyword))) {
    return "minimize";
  }
  return "maximize";
}
function compareMetricValues(baselineMetricValue, candidateMetricValue, objective) {
  if (baselineMetricValue === void 0 || candidateMetricValue === void 0) {
    return "unknown";
  }
  if (candidateMetricValue === baselineMetricValue) {
    return "equal";
  }
  if (objective === "minimize") {
    return candidateMetricValue < baselineMetricValue ? "better" : "worse";
  }
  return candidateMetricValue > baselineMetricValue ? "better" : "worse";
}
function formatSignedNumber(value) {
  if (value === void 0) {
    return "unknown";
  }
  return `${value > 0 ? "+" : ""}${value}`;
}
function formatPercent(value) {
  if (value === void 0) {
    return "unknown";
  }
  return `${value > 0 ? "+" : ""}${value.toFixed(2)}%`;
}
function buildComparisonSummary(input) {
  const result = compareMetricValues(input.baselineMetricValue, input.candidateMetricValue, input.objective);
  const delta = input.baselineMetricValue !== void 0 && input.candidateMetricValue !== void 0 ? input.candidateMetricValue - input.baselineMetricValue : void 0;
  const percentDelta = input.baselineMetricValue !== void 0 && input.baselineMetricValue !== 0 && delta !== void 0 ? delta / input.baselineMetricValue * 100 : void 0;
  const goal = input.objective === "minimize" ? "lower" : "higher";
  let summary = `Needs ${goal} ${input.objective === "minimize" ? "metric reduction" : "metric lift"} data.`;
  if (result === "better") {
    summary = `Candidate is better than baseline (${input.candidateMetricValue} vs ${input.baselineMetricValue}, \u0394 ${formatSignedNumber(delta)} / ${formatPercent(percentDelta)}).`;
  } else if (result === "worse") {
    summary = `Candidate is worse than baseline (${input.candidateMetricValue} vs ${input.baselineMetricValue}, \u0394 ${formatSignedNumber(delta)} / ${formatPercent(percentDelta)}).`;
  } else if (result === "equal") {
    summary = `Candidate matches baseline (${input.candidateMetricValue}).`;
  }
  return {
    baselineSessionId: input.baselineSessionId,
    baselineName: input.baselineName,
    objective: input.objective,
    result,
    baselineMetricValue: input.baselineMetricValue,
    candidateMetricValue: input.candidateMetricValue,
    delta,
    percentDelta,
    summary
  };
}
function determineVerdict(args) {
  if (!args.success) {
    return {
      verdict: "FAIL",
      reason: "Command execution failed."
    };
  }
  if (args.metricName && args.metricValue === void 0) {
    return {
      verdict: "PARTIAL",
      reason: `Metric ${args.metricName} was configured but not found in output.`
    };
  }
  if (args.comparison?.result === "worse") {
    return {
      verdict: "FAIL",
      reason: args.comparison.summary
    };
  }
  if (args.comparison?.result === "unknown") {
    return {
      verdict: "PARTIAL",
      reason: args.comparison.summary
    };
  }
  if (args.comparison?.result === "equal") {
    return {
      verdict: "PARTIAL",
      reason: args.comparison.summary
    };
  }
  if (args.comparison?.result === "better") {
    return {
      verdict: "PASS",
      reason: args.comparison.summary
    };
  }
  if (args.metricName && args.metricValue !== void 0) {
    return {
      verdict: "PASS",
      reason: `Metric ${args.metricName}=${args.metricValue} was captured successfully.`
    };
  }
  return {
    verdict: "PASS",
    reason: "Command completed successfully."
  };
}
function selectBestByMetric(rows) {
  const newestMetricRow = [...rows].reverse().find((row) => row.metricName && row.metricValue !== void 0);
  if (!newestMetricRow?.metricName) {
    return [...rows].reverse().find((row) => row.verdict === "PASS") || rows[rows.length - 1];
  }
  const candidates = rows.filter((row) => row.metricName === newestMetricRow.metricName && row.metricValue !== void 0);
  if (candidates.length === 0) {
    return newestMetricRow;
  }
  const objective = resolveResearchObjective(newestMetricRow.metricName);
  return candidates.reduce((best, current) => {
    if (best.metricValue === void 0 || current.metricValue === void 0) {
      return best;
    }
    if (objective === "minimize") {
      return current.metricValue < best.metricValue ? current : best;
    }
    return current.metricValue > best.metricValue ? current : best;
  });
}
function buildResearchTask(taskId, options) {
  return {
    id: taskId,
    name: options.name,
    description: `Run research command: ${options.command}`,
    type: "research-run",
    priority: "normal",
    status: "pending",
    requiredCapabilities: [],
    input: {
      parameters: {
        command: options.command,
        cwd: options.cwd,
        metricName: options.metricName,
        budgetMs: options.budgetMs
      },
      instructions: "Execute the configured research command and capture its metric output."
    },
    dependencies: [],
    maxRetries: options.maxRetries,
    retryCount: 0,
    timeout: options.budgetMs,
    metadata: {
      tags: ["research", "experiment"],
      category: "research",
      createdBy: "ccjk research",
      custom: {
        metricName: options.metricName,
        cwd: options.cwd,
        baselineSessionId: options.baselineSessionId
      }
    },
    createdAt: (/* @__PURE__ */ new Date()).toISOString(),
    progress: 0
  };
}
function normalizeRunOptions(options) {
  return {
    command: options.command,
    name: options.name || `research-${Date.now()}`,
    cwd: options.cwd || process.cwd(),
    budgetMs: options.budgetMs || DEFAULT_BUDGET_MS,
    maxRetries: options.maxRetries ?? 0,
    metricName: options.metricName,
    baselineSessionId: options.baselineSessionId,
    objective: options.objective || "auto",
    dbPath: options.dbPath
  };
}
async function runResearchExperiment(options) {
  const normalized = normalizeRunOptions(options);
  const persistence = createPersistence$1(normalized.dbPath);
  const queue = new TaskQueue({
    concurrency: 1,
    defaultTimeout: normalized.budgetMs,
    defaultMaxRetries: normalized.maxRetries
  });
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
  const runPhase = inferResearchPhase(normalized.name);
  const phaseHistory = buildPhaseHistory(runPhase);
  const sessionId = `research-${Date.now()}-${nanoid(6)}`;
  const taskId = `research-task-${nanoid(8)}`;
  const task = buildResearchTask(taskId, normalized);
  const objective = resolveResearchObjective(normalized.metricName, normalized.objective);
  let baselineSummary;
  let comparison;
  let baselineMetricValue;
  if (normalized.baselineSessionId) {
    baselineSummary = listResearchSessions(Number.MAX_SAFE_INTEGER, normalized.dbPath).find((session) => session.id === normalized.baselineSessionId);
    const baselineReport = getResearchReport(normalized.baselineSessionId, normalized.dbPath);
    if (baselineReport) {
      baselineMetricValue = baselineReport.metricValue;
      comparison = buildComparisonSummary({
        baselineSessionId: baselineReport.sessionId,
        baselineName: baselineReport.name,
        baselineMetricValue,
        candidateMetricValue: void 0,
        objective
      });
    }
  }
  persistence.saveSession(sessionId, {
    kind: RESEARCH_SESSION_KIND,
    name: normalized.name,
    command: normalized.command,
    cwd: normalized.cwd,
    metricName: normalized.metricName,
    budgetMs: normalized.budgetMs,
    objective,
    baselineSessionId: normalized.baselineSessionId,
    currentPhase: runPhase,
    phaseHistory: ["brief", runPhase],
    createdAt
  });
  persistence.saveTask(task, sessionId);
  let attempts = 0;
  let commandResult;
  let error;
  const startedAt = Date.now();
  persistence.updateTaskStatus(taskId, "running");
  try {
    commandResult = await queue.add(async () => {
      attempts += 1;
      const result = await executeCommand(normalized.command, [], {
        cwd: normalized.cwd,
        timeout: normalized.budgetMs,
        shell: true
      });
      if (!result.success) {
        throw new ResearchCommandError(result);
      }
      return result;
    }, {
      timeout: normalized.budgetMs,
      maxRetries: normalized.maxRetries,
      metadata: {
        sessionId,
        command: normalized.command
      }
    });
  } catch (caughtError) {
    error = caughtError instanceof Error ? caughtError : new Error(String(caughtError));
    if (caughtError instanceof ResearchCommandError) {
      commandResult = caughtError.result;
    }
  }
  const durationMs = Date.now() - startedAt;
  const finalizedResult = commandResult || {
    success: false,
    stdout: "",
    stderr: "",
    exitCode: 1,
    error: error?.message || "Research command failed"
  };
  const status = finalizedResult.success ? "completed" : getFailureStatus(finalizedResult, error);
  const combinedOutput = [finalizedResult.stdout, finalizedResult.stderr].filter(Boolean).join("\n");
  const metricValue = extractMetricValue(combinedOutput, normalized.metricName);
  if (normalized.baselineSessionId) {
    comparison = buildComparisonSummary({
      baselineSessionId: normalized.baselineSessionId,
      baselineName: baselineSummary?.name || normalized.baselineSessionId,
      baselineMetricValue,
      candidateMetricValue: metricValue,
      objective
    });
  }
  const verdictDecision = determineVerdict({
    success: finalizedResult.success,
    metricName: normalized.metricName,
    metricValue,
    comparison
  });
  const verdict = verdictDecision.verdict;
  const verdictReason = verdictDecision.reason;
  const output = {
    data: {
      command: normalized.command,
      cwd: normalized.cwd,
      status,
      exitCode: finalizedResult.exitCode,
      stdout: finalizedResult.stdout,
      stderr: finalizedResult.stderr,
      metricName: normalized.metricName,
      metricValue,
      durationMs,
      attempts,
      phase: runPhase,
      objective,
      verdict,
      verdictReason,
      baselineSessionId: normalized.baselineSessionId,
      comparison
    },
    logs: combinedOutput ? combinedOutput.split(/\r?\n/) : [],
    metadata: {
      status,
      success: finalizedResult.success,
      verdict,
      objective
    }
  };
  persistence.updateTaskStatus(
    taskId,
    finalizedResult.success ? "completed" : "failed",
    output,
    finalizedResult.success ? void 0 : error
  );
  persistence.recordMetrics({
    taskId,
    sessionId,
    executionTime: durationMs,
    retryCount: Math.max(0, attempts - 1),
    success: finalizedResult.success,
    errorType: finalizedResult.success ? void 0 : status
  });
  persistence.logDecision({
    id: `decision-${nanoid(8)}`,
    sessionId,
    taskId,
    decision: "record research run",
    reasoning: normalized.metricName ? `Executed the research command and evaluated metric ${normalized.metricName} with objective ${objective}.` : "Executed the research command without a configured metric parser.",
    context: JSON.stringify({
      command: normalized.command,
      cwd: normalized.cwd,
      budgetMs: normalized.budgetMs,
      metricName: normalized.metricName,
      phase: runPhase,
      objective,
      baselineSessionId: normalized.baselineSessionId
    }),
    outcome: verdictReason
  });
  persistence.saveSession(sessionId, {
    kind: RESEARCH_SESSION_KIND,
    name: normalized.name,
    command: normalized.command,
    cwd: normalized.cwd,
    metricName: normalized.metricName,
    budgetMs: normalized.budgetMs,
    objective,
    baselineSessionId: normalized.baselineSessionId,
    currentPhase: "report",
    phaseHistory,
    runPhase,
    verdict,
    verdictReason,
    status,
    exitCode: finalizedResult.exitCode,
    metricValue,
    comparison,
    durationMs,
    createdAt
  });
  appendResultRow({
    timestamp: createdAt,
    sessionId,
    taskId,
    name: normalized.name,
    phase: runPhase,
    verdict,
    status,
    exitCode: finalizedResult.exitCode,
    metricName: normalized.metricName,
    metricValue,
    durationMs,
    cwd: normalized.cwd,
    command: normalized.command
  }, normalized.dbPath);
  return {
    sessionId,
    taskId,
    name: normalized.name,
    command: normalized.command,
    cwd: normalized.cwd,
    metricName: normalized.metricName,
    metricValue,
    success: finalizedResult.success,
    status,
    exitCode: finalizedResult.exitCode,
    stdout: finalizedResult.stdout,
    stderr: finalizedResult.stderr,
    durationMs,
    phase: runPhase,
    objective,
    verdict,
    verdictReason,
    baselineSessionId: normalized.baselineSessionId,
    comparison,
    phaseHistory,
    createdAt
  };
}
function listResearchSessions(limit = DEFAULT_SESSION_LIMIT, dbPath) {
  const persistence = createPersistence$1(dbPath);
  return persistence.listSessions(Math.max(limit * 5, 50)).filter((session) => session.metadata?.kind === RESEARCH_SESSION_KIND).slice(0, limit).map((session) => ({
    id: session.id,
    createdAt: session.createdAt,
    name: session.metadata.name || session.id,
    command: session.metadata.command || "",
    cwd: session.metadata.cwd || process.cwd(),
    metricName: session.metadata.metricName,
    budgetMs: session.metadata.budgetMs || DEFAULT_BUDGET_MS,
    currentPhase: session.metadata.currentPhase || "experiment",
    objective: session.metadata.objective,
    verdict: session.metadata.verdict,
    baselineSessionId: session.metadata.baselineSessionId
  }));
}
function getLatestResearchSession(dbPath) {
  return listResearchSessions(1, dbPath)[0];
}
function getResearchSessionStatus(sessionId, dbPath) {
  const persistence = createPersistence$1(dbPath);
  const restored = persistence.restoreContext(sessionId);
  if (!restored) {
    return null;
  }
  return {
    sessionId,
    metadata: restored.metadata,
    tasks: restored.tasks,
    metrics: persistence.getSessionMetrics(sessionId),
    decisions: persistence.getDecisionLog(sessionId),
    recovery: persistence.recoverExecutionState(sessionId)
  };
}
function listResearchResults(limit = DEFAULT_SESSION_LIMIT, dbPath) {
  return readAllResultRows(dbPath).reverse().slice(0, limit);
}
function getBestResearchResult(dbPath) {
  const rows = readAllResultRows(dbPath);
  if (rows.length === 0) {
    return void 0;
  }
  return selectBestByMetric(rows);
}
function getResearchReport(sessionId, dbPath) {
  const resolvedSessionId = sessionId || getLatestResearchSession(dbPath)?.id;
  if (!resolvedSessionId) {
    return null;
  }
  const status = getResearchSessionStatus(resolvedSessionId, dbPath);
  if (!status) {
    return null;
  }
  const latestTask = status.tasks[status.tasks.length - 1];
  const outputData = latestTask?.output?.data || {};
  const phaseHistory = status.metadata.phaseHistory || [];
  const comparison = status.metadata.comparison;
  const verdictDecision = determineVerdict({
    success: status.metrics.failedTasks === 0,
    metricName: status.metadata.metricName,
    metricValue: outputData.metricValue,
    comparison
  });
  const verdict = status.metadata.verdict || verdictDecision.verdict;
  const verdictReason = String(status.metadata.verdictReason || verdictDecision.reason);
  const currentPhase = status.metadata.currentPhase || "report";
  const createdAt = String(status.metadata.createdAt || "");
  const outcome = String(
    status.decisions[status.decisions.length - 1]?.outcome || status.decisions[status.decisions.length - 1]?.decision || outputData.stderr || outputData.status || "unknown"
  ).replace(/\s+/g, " ").trim().slice(0, 160);
  const lines = [];
  lines.push("# CCJK Research Report");
  lines.push("");
  lines.push(`**Session**: ${resolvedSessionId}`);
  lines.push(`**Name**: ${status.metadata.name || resolvedSessionId}`);
  lines.push(`**Created**: ${createdAt || "unknown"}`);
  lines.push(`**Phase**: ${currentPhase}`);
  lines.push(`**Verdict**: ${verdict}`);
  lines.push(`**Command**: ${status.metadata.command || outputData.command || "unknown"}`);
  lines.push(`**CWD**: ${status.metadata.cwd || outputData.cwd || process.cwd()}`);
  lines.push(`**Exit Code**: ${outputData.exitCode ?? "unknown"}`);
  lines.push(`**Status**: ${outputData.status || status.metadata.status || "unknown"}`);
  lines.push(`**Metric**: ${status.metadata.metricName ? `${status.metadata.metricName}=${outputData.metricValue ?? "not found"}` : "not configured"}`);
  lines.push(`**Objective**: ${status.metadata.objective || outputData.objective || "unknown"}`);
  lines.push(`**Duration**: ${outputData.durationMs ?? status.metadata.durationMs ?? 0}ms`);
  lines.push(`**Reason**: ${verdictReason}`);
  lines.push(`**Outcome**: ${outcome}`);
  if (comparison) {
    lines.push("");
    lines.push("## Comparison");
    lines.push("");
    lines.push(`- Baseline: ${comparison.baselineName} (${comparison.baselineSessionId})`);
    lines.push(`- Objective: ${comparison.objective}`);
    lines.push(`- Result: ${comparison.result}`);
    lines.push(`- Summary: ${comparison.summary}`);
  }
  if (phaseHistory.length > 0) {
    lines.push("");
    lines.push("## Phase History");
    lines.push("");
    lines.push(`- ${phaseHistory.join(" \u2192 ")}`);
  }
  return {
    sessionId: resolvedSessionId,
    name: status.metadata.name || resolvedSessionId,
    createdAt,
    currentPhase,
    verdict,
    verdictReason,
    command: status.metadata.command || outputData.command || "unknown",
    cwd: status.metadata.cwd || outputData.cwd || process.cwd(),
    status: String(outputData.status || status.metadata.status || "unknown"),
    exitCode: Number(outputData.exitCode ?? 0),
    metricName: status.metadata.metricName,
    metricValue: typeof outputData.metricValue === "number" ? outputData.metricValue : void 0,
    objective: status.metadata.objective,
    baselineSessionId: status.metadata.baselineSessionId,
    comparison,
    durationMs: Number(outputData.durationMs ?? status.metadata.durationMs ?? 0),
    phaseHistory,
    outcome,
    content: lines.join("\n")
  };
}

const RESEARCH_LOOP_SESSION_KIND = "research-loop";
const DEFAULT_FAILURE_STREAK_LIMIT = 2;
function createPersistence(dbPath) {
  return new TaskPersistence(dbPath);
}
function toPositiveInteger(value, fallback) {
  return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : fallback;
}
function toOptionalFiniteNumber(value) {
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
}
function applyProgramOverrides(program, overrides = {}) {
  return {
    ...program,
    cwd: overrides.cwd || program.cwd,
    budgetMs: toPositiveInteger(overrides.budgetMs, program.budgetMs),
    maxRounds: toPositiveInteger(overrides.maxRounds, program.maxRounds),
    maxNoImproveRounds: toPositiveInteger(overrides.maxNoImproveRounds, program.maxNoImproveRounds),
    targetMetric: toOptionalFiniteNumber(overrides.targetMetric) ?? program.targetMetric
  };
}
function nowIso() {
  return (/* @__PURE__ */ new Date()).toISOString();
}
function toObjective(value, metricName) {
  if (value === "maximize" || value === "minimize") {
    return value;
  }
  const normalized = (metricName || "").toLowerCase();
  if (!normalized) {
    return "maximize";
  }
  const lowerIsBetter = ["loss", "error", "bpb", "perplexity", "ppl", "latency", "duration", "time", "cost", "price", "wer", "cer"];
  return lowerIsBetter.some((keyword) => normalized.includes(keyword)) ? "minimize" : "maximize";
}
function isAcceptedResult(result) {
  return result.verdict === "PASS";
}
function cloneLoopMetadata(metadata, patch = {}) {
  return {
    ...metadata,
    ...patch,
    acceptedRoundSessionIds: patch.acceptedRoundSessionIds || [...metadata.acceptedRoundSessionIds],
    rejectedRoundSessionIds: patch.rejectedRoundSessionIds || [...metadata.rejectedRoundSessionIds],
    roundSessionIds: patch.roundSessionIds || [...metadata.roundSessionIds],
    updatedAt: patch.updatedAt || nowIso()
  };
}
function ensureLoopMetadata(raw, sessionId) {
  const createdAt = typeof raw.createdAt === "string" && raw.createdAt ? raw.createdAt : nowIso();
  return {
    kind: "research-loop",
    name: typeof raw.name === "string" && raw.name ? raw.name : sessionId,
    programPath: typeof raw.programPath === "string" ? raw.programPath : ".ccjk/research/program.md",
    metric: typeof raw.metric === "string" && raw.metric ? raw.metric : void 0,
    objective: raw.objective === "maximize" || raw.objective === "minimize" || raw.objective === "auto" ? raw.objective : "auto",
    cwd: typeof raw.cwd === "string" && raw.cwd ? raw.cwd : process.cwd(),
    budgetMs: toPositiveInteger(raw.budgetMs, 5 * 60 * 1e3),
    maxRounds: toPositiveInteger(raw.maxRounds, 10),
    maxNoImproveRounds: toPositiveInteger(raw.maxNoImproveRounds, 3),
    targetMetric: typeof raw.targetMetric === "number" && Number.isFinite(raw.targetMetric) ? raw.targetMetric : void 0,
    status: raw.status === "running" || raw.status === "stopped" || raw.status === "completed" || raw.status === "failed" ? raw.status : "running",
    baselineSessionId: typeof raw.baselineSessionId === "string" && raw.baselineSessionId ? raw.baselineSessionId : void 0,
    currentRound: toPositiveInteger(raw.currentRound, 0) - 1 >= 0 ? toPositiveInteger(raw.currentRound, 0) : 0,
    bestSessionId: typeof raw.bestSessionId === "string" && raw.bestSessionId ? raw.bestSessionId : void 0,
    bestMetricValue: typeof raw.bestMetricValue === "number" && Number.isFinite(raw.bestMetricValue) ? raw.bestMetricValue : void 0,
    acceptedRoundSessionIds: Array.isArray(raw.acceptedRoundSessionIds) ? raw.acceptedRoundSessionIds.filter((value) => typeof value === "string") : [],
    rejectedRoundSessionIds: Array.isArray(raw.rejectedRoundSessionIds) ? raw.rejectedRoundSessionIds.filter((value) => typeof value === "string") : [],
    roundSessionIds: Array.isArray(raw.roundSessionIds) ? raw.roundSessionIds.filter((value) => typeof value === "string") : [],
    noImproveStreak: typeof raw.noImproveStreak === "number" && raw.noImproveStreak >= 0 ? raw.noImproveStreak : 0,
    failureStreak: typeof raw.failureStreak === "number" && raw.failureStreak >= 0 ? raw.failureStreak : 0,
    stopReason: raw.stopReason,
    createdAt,
    updatedAt: typeof raw.updatedAt === "string" && raw.updatedAt ? raw.updatedAt : createdAt,
    lastRoundSessionId: typeof raw.lastRoundSessionId === "string" && raw.lastRoundSessionId ? raw.lastRoundSessionId : void 0,
    notes: typeof raw.notes === "string" && raw.notes ? raw.notes : void 0
  };
}
function getLoopSessionRecord(sessionId, dbPath) {
  const persistence = createPersistence(dbPath);
  const session = persistence.getSession(sessionId);
  if (!session || session.metadata?.kind !== RESEARCH_LOOP_SESSION_KIND) {
    return null;
  }
  return {
    id: session.id,
    createdAt: session.createdAt,
    updatedAt: session.updatedAt,
    metadata: ensureLoopMetadata(session.metadata, session.id)
  };
}
function getLatestLoopSession(dbPath) {
  const persistence = createPersistence(dbPath);
  const session = persistence.listSessions(100).find((item) => item.metadata?.kind === RESEARCH_LOOP_SESSION_KIND);
  if (!session) {
    return null;
  }
  return {
    id: session.id,
    createdAt: session.createdAt,
    metadata: ensureLoopMetadata(session.metadata, session.id)
  };
}
function getResolvedLoopSessionId(sessionId, dbPath) {
  return sessionId || getLatestLoopSession(dbPath)?.id;
}
function saveLoopMetadata(sessionId, metadata, dbPath) {
  const persistence = createPersistence(dbPath);
  const normalized = ensureLoopMetadata({ ...metadata, updatedAt: nowIso() }, sessionId);
  persistence.saveSession(sessionId, normalized);
  return normalized;
}
function buildRoundRecord(round, result) {
  return {
    round,
    sessionId: result.sessionId,
    name: result.name,
    status: result.status,
    verdict: result.verdict,
    metricName: result.metricName,
    metricValue: result.metricValue,
    accepted: isAcceptedResult(result),
    objective: result.objective,
    command: result.command,
    reason: result.verdictReason,
    baselineSessionId: result.baselineSessionId,
    comparison: result.comparison,
    createdAt: result.createdAt
  };
}
function getRoundRecords(metadata, dbPath) {
  return metadata.roundSessionIds.map((sessionId, index) => {
    const report = getResearchReport(sessionId, dbPath);
    if (!report) {
      return null;
    }
    const record = {
      round: index + 1,
      sessionId,
      name: report.name,
      status: report.status,
      verdict: report.verdict,
      metricName: report.metricName,
      metricValue: report.metricValue,
      accepted: metadata.acceptedRoundSessionIds.includes(sessionId),
      objective: report.objective || toObjective(metadata.objective, metadata.metric),
      command: report.command,
      reason: report.verdictReason,
      baselineSessionId: report.baselineSessionId,
      comparison: report.comparison,
      createdAt: report.createdAt
    };
    return record;
  }).filter((value) => value !== null);
}
function isTargetReached(metricValue, objective, targetMetric) {
  if (metricValue === void 0 || targetMetric === void 0) {
    return false;
  }
  return objective === "minimize" ? metricValue <= targetMetric : metricValue >= targetMetric;
}
function evaluateLoopStopCondition(input) {
  const objective = toObjective(input.metadata.objective, input.metadata.metric);
  if (input.metadata.status === "completed" || input.metadata.status === "failed") {
    return { shouldStop: true, reason: input.metadata.stopReason, status: input.metadata.status };
  }
  if (input.metadata.status === "stopped") {
    return { shouldStop: true, reason: input.metadata.stopReason || "manual-stop", status: "stopped" };
  }
  if (input.phase === "baseline" && input.latestResult && !input.latestResult.success) {
    return { shouldStop: true, reason: "baseline-failed", status: "failed" };
  }
  if (input.metadata.currentRound >= input.program.maxRounds) {
    return { shouldStop: true, reason: "max-rounds-reached", status: "completed" };
  }
  if (input.metadata.noImproveStreak >= input.program.maxNoImproveRounds) {
    return { shouldStop: true, reason: "max-no-improve-rounds-reached", status: "completed" };
  }
  if (isTargetReached(input.metadata.bestMetricValue, objective, input.program.targetMetric)) {
    return { shouldStop: true, reason: "target-metric-reached", status: "completed" };
  }
  if (input.phase === "round" && input.metadata.failureStreak >= DEFAULT_FAILURE_STREAK_LIMIT) {
    return { shouldStop: true, reason: "candidate-failed-repeatedly", status: "failed" };
  }
  return { shouldStop: false };
}
function createLoopSession(program, dbPath) {
  const sessionId = `research-loop-${Date.now()}-${nanoid(6)}`;
  const createdAt = nowIso();
  const metadata = {
    kind: "research-loop",
    name: program.name,
    programPath: program.programPath,
    metric: program.metric,
    objective: program.objective,
    cwd: program.cwd,
    budgetMs: program.budgetMs,
    maxRounds: program.maxRounds,
    maxNoImproveRounds: program.maxNoImproveRounds,
    targetMetric: program.targetMetric,
    status: "running",
    baselineSessionId: void 0,
    currentRound: 0,
    bestSessionId: void 0,
    bestMetricValue: void 0,
    acceptedRoundSessionIds: [],
    rejectedRoundSessionIds: [],
    roundSessionIds: [],
    noImproveStreak: 0,
    failureStreak: 0,
    createdAt,
    updatedAt: createdAt,
    notes: program.notes
  };
  return {
    sessionId,
    metadata: saveLoopMetadata(sessionId, metadata, dbPath)
  };
}
async function ensureBaseline(sessionId, metadata, program, dbPath) {
  if (metadata.baselineSessionId) {
    const report = getResearchReport(metadata.baselineSessionId, dbPath);
    if (report) {
      return {
        metadata,
        baseline: {
          sessionId: report.sessionId,
          taskId: "",
          name: report.name,
          command: report.command,
          cwd: report.cwd,
          metricName: report.metricName,
          metricValue: report.metricValue,
          success: report.status === "completed",
          status: report.status,
          exitCode: report.exitCode,
          stdout: "",
          stderr: "",
          durationMs: report.durationMs,
          phase: "baseline",
          objective: report.objective || toObjective(program.objective, program.metric),
          verdict: report.verdict,
          verdictReason: report.verdictReason,
          baselineSessionId: report.baselineSessionId,
          comparison: report.comparison,
          phaseHistory: report.phaseHistory,
          createdAt: report.createdAt
        }
      };
    }
  }
  const baseline = await runResearchExperiment({
    name: `${program.name}-baseline`,
    command: program.baselineCommand,
    metricName: program.metric,
    budgetMs: program.budgetMs,
    cwd: program.cwd,
    objective: program.objective,
    dbPath
  });
  const nextMetadata = cloneLoopMetadata(metadata, {
    baselineSessionId: baseline.sessionId,
    bestSessionId: baseline.sessionId,
    bestMetricValue: baseline.metricValue,
    failureStreak: baseline.success ? 0 : metadata.failureStreak + 1
  });
  return {
    metadata: saveLoopMetadata(sessionId, nextMetadata, dbPath),
    baseline
  };
}
async function executeResearchRound(sessionId, metadata, program, dbPath) {
  const roundNumber = metadata.currentRound + 1;
  const baselineSessionId = metadata.bestSessionId || metadata.baselineSessionId;
  const result = await runResearchExperiment({
    name: `${program.name}-round-${roundNumber}`,
    command: program.candidateCommand,
    metricName: program.metric,
    budgetMs: program.budgetMs,
    cwd: program.cwd,
    objective: program.objective,
    baselineSessionId,
    dbPath
  });
  const accepted = isAcceptedResult(result);
  const acceptedRoundSessionIds = accepted ? [...metadata.acceptedRoundSessionIds, result.sessionId] : [...metadata.acceptedRoundSessionIds];
  const rejectedRoundSessionIds = accepted ? [...metadata.rejectedRoundSessionIds] : [...metadata.rejectedRoundSessionIds, result.sessionId];
  const bestSessionId = accepted ? result.sessionId : metadata.bestSessionId;
  const bestMetricValue = accepted ? result.metricValue : metadata.bestMetricValue;
  const nextMetadata = cloneLoopMetadata(metadata, {
    currentRound: roundNumber,
    bestSessionId,
    bestMetricValue,
    roundSessionIds: [...metadata.roundSessionIds, result.sessionId],
    acceptedRoundSessionIds,
    rejectedRoundSessionIds,
    noImproveStreak: accepted ? 0 : metadata.noImproveStreak + 1,
    failureStreak: result.success ? 0 : metadata.failureStreak + 1,
    lastRoundSessionId: result.sessionId
  });
  return {
    metadata: saveLoopMetadata(sessionId, nextMetadata, dbPath),
    result,
    round: buildRoundRecord(roundNumber, result)
  };
}
function getResearchLoopStatusInternal(sessionId, dbPath) {
  const loopSession = getLoopSessionRecord(sessionId, dbPath);
  if (!loopSession) {
    return null;
  }
  const baselineReport = loopSession.metadata.baselineSessionId ? getResearchReport(loopSession.metadata.baselineSessionId, dbPath) : null;
  const rounds = getRoundRecords(loopSession.metadata, dbPath);
  return {
    sessionId,
    metadata: loopSession.metadata,
    baseline: baselineReport ? {
      sessionId: baselineReport.sessionId,
      taskId: "",
      name: baselineReport.name,
      command: baselineReport.command,
      cwd: baselineReport.cwd,
      metricName: baselineReport.metricName,
      metricValue: baselineReport.metricValue,
      success: baselineReport.status === "completed",
      status: baselineReport.status,
      exitCode: baselineReport.exitCode,
      stdout: "",
      stderr: "",
      durationMs: baselineReport.durationMs,
      phase: "baseline",
      objective: baselineReport.objective || toObjective(loopSession.metadata.objective, loopSession.metadata.metric),
      verdict: baselineReport.verdict,
      verdictReason: baselineReport.verdictReason,
      baselineSessionId: baselineReport.baselineSessionId,
      comparison: baselineReport.comparison,
      phaseHistory: baselineReport.phaseHistory,
      createdAt: baselineReport.createdAt
    } : null,
    latestRound: rounds[rounds.length - 1],
    rounds
  };
}
async function startResearchLoop(options = {}) {
  const program = applyProgramOverrides(readResearchProgram(options.programPath, options.cwd), options.overrides);
  const { sessionId } = createLoopSession(program, options.dbPath);
  return await resumeResearchLoop({ ...options, sessionId });
}
async function runResearchRound(options = {}) {
  const resolvedSessionId = getResolvedLoopSessionId(options.sessionId, options.dbPath);
  if (!resolvedSessionId) {
    throw new Error("No research loop session found.");
  }
  const loopSession = getLoopSessionRecord(resolvedSessionId, options.dbPath);
  if (!loopSession) {
    throw new Error(`Research loop session not found: ${resolvedSessionId}`);
  }
  const program = applyProgramOverrides(readResearchProgram(loopSession.metadata.programPath, options.cwd), options.overrides);
  let metadata = saveLoopMetadata(resolvedSessionId, cloneLoopMetadata(loopSession.metadata, { status: "running", stopReason: void 0 }), options.dbPath);
  const baselineState = await ensureBaseline(resolvedSessionId, metadata, program, options.dbPath);
  metadata = baselineState.metadata;
  const stopAfterBaseline = evaluateLoopStopCondition({ metadata, latestResult: baselineState.baseline, program, phase: "baseline" });
  if (stopAfterBaseline.shouldStop) {
    metadata = saveLoopMetadata(resolvedSessionId, cloneLoopMetadata(metadata, {
      status: stopAfterBaseline.status || metadata.status,
      stopReason: stopAfterBaseline.reason
    }), options.dbPath);
    return getResearchLoopStatusInternal(resolvedSessionId, options.dbPath);
  }
  const roundState = await executeResearchRound(resolvedSessionId, metadata, program, options.dbPath);
  metadata = roundState.metadata;
  const stopState = evaluateLoopStopCondition({ metadata, latestResult: roundState.result, program, phase: "round" });
  if (stopState.shouldStop) {
    metadata = saveLoopMetadata(resolvedSessionId, cloneLoopMetadata(metadata, {
      status: stopState.status || metadata.status,
      stopReason: stopState.reason
    }), options.dbPath);
  }
  return getResearchLoopStatusInternal(resolvedSessionId, options.dbPath);
}
async function resumeResearchLoop(options = {}) {
  const resolvedSessionId = getResolvedLoopSessionId(options.sessionId, options.dbPath);
  if (!resolvedSessionId) {
    throw new Error("No research loop session found.");
  }
  const loopSession = getLoopSessionRecord(resolvedSessionId, options.dbPath);
  if (!loopSession) {
    throw new Error(`Research loop session not found: ${resolvedSessionId}`);
  }
  const program = applyProgramOverrides(readResearchProgram(loopSession.metadata.programPath, options.cwd), options.overrides);
  let metadata = saveLoopMetadata(resolvedSessionId, cloneLoopMetadata(loopSession.metadata, {
    status: loopSession.metadata.status === "completed" || loopSession.metadata.status === "failed" ? loopSession.metadata.status : "running",
    stopReason: loopSession.metadata.status === "stopped" ? void 0 : loopSession.metadata.stopReason
  }), options.dbPath);
  const baselineState = await ensureBaseline(resolvedSessionId, metadata, program, options.dbPath);
  metadata = baselineState.metadata;
  while (true) {
    const stopBeforeRound = evaluateLoopStopCondition({ metadata, program });
    if (stopBeforeRound.shouldStop) {
      metadata = saveLoopMetadata(resolvedSessionId, cloneLoopMetadata(metadata, {
        status: stopBeforeRound.status || metadata.status,
        stopReason: stopBeforeRound.reason
      }), options.dbPath);
      return getResearchLoopStatusInternal(resolvedSessionId, options.dbPath);
    }
    const roundState = await executeResearchRound(resolvedSessionId, metadata, program, options.dbPath);
    metadata = roundState.metadata;
    const stopAfterRound = evaluateLoopStopCondition({ metadata, latestResult: roundState.result, program, phase: "round" });
    if (stopAfterRound.shouldStop) {
      metadata = saveLoopMetadata(resolvedSessionId, cloneLoopMetadata(metadata, {
        status: stopAfterRound.status || metadata.status,
        stopReason: stopAfterRound.reason || "resume-complete"
      }), options.dbPath);
      return getResearchLoopStatusInternal(resolvedSessionId, options.dbPath);
    }
  }
}
function stopResearchLoop(options = {}) {
  const resolvedSessionId = getResolvedLoopSessionId(options.sessionId, options.dbPath);
  if (!resolvedSessionId) {
    return null;
  }
  const loopSession = getLoopSessionRecord(resolvedSessionId, options.dbPath);
  if (!loopSession) {
    return null;
  }
  saveLoopMetadata(resolvedSessionId, cloneLoopMetadata(loopSession.metadata, {
    status: "stopped",
    stopReason: "manual-stop"
  }), options.dbPath);
  return getResearchLoopStatusInternal(resolvedSessionId, options.dbPath);
}
function getResearchLoopStatus(sessionId, dbPath) {
  const resolvedSessionId = getResolvedLoopSessionId(sessionId, dbPath);
  if (!resolvedSessionId) {
    return null;
  }
  return getResearchLoopStatusInternal(resolvedSessionId, dbPath);
}
function getResearchLoopReport(sessionId, dbPath) {
  const status = getResearchLoopStatus(sessionId, dbPath);
  if (!status) {
    return null;
  }
  const lines = [];
  lines.push("# CCJK Research Loop Report");
  lines.push("");
  lines.push(`**Session**: ${status.sessionId}`);
  lines.push(`**Name**: ${status.metadata.name}`);
  lines.push(`**Status**: ${status.metadata.status}`);
  lines.push(`**Stop Reason**: ${status.metadata.stopReason || "running"}`);
  lines.push(`**Rounds**: ${status.metadata.currentRound}/${status.metadata.maxRounds}`);
  lines.push(`**No-Improve Streak**: ${status.metadata.noImproveStreak}/${status.metadata.maxNoImproveRounds}`);
  lines.push(`**Metric**: ${status.metadata.metric || "not configured"}`);
  lines.push(`**Objective**: ${status.metadata.objective}`);
  lines.push(`**Target Metric**: ${status.metadata.targetMetric ?? "not configured"}`);
  lines.push(`**Best Session**: ${status.metadata.bestSessionId || "none"}`);
  lines.push(`**Best Metric**: ${status.metadata.bestMetricValue ?? "not found"}`);
  lines.push(`**Created**: ${status.metadata.createdAt}`);
  lines.push(`**Updated**: ${status.metadata.updatedAt}`);
  if (status.baseline) {
    lines.push("");
    lines.push("## Baseline");
    lines.push("");
    lines.push(`- Session: ${status.baseline.sessionId}`);
    lines.push(`- Verdict: ${status.baseline.verdict}`);
    lines.push(`- Metric: ${status.baseline.metricName ? `${status.baseline.metricName}=${status.baseline.metricValue ?? "not found"}` : "not configured"}`);
    lines.push(`- Reason: ${status.baseline.verdictReason}`);
  }
  if (status.rounds.length > 0) {
    lines.push("");
    lines.push("## Rounds");
    lines.push("");
    for (const round of status.rounds) {
      lines.push(`- Round ${round.round}: ${round.sessionId} \xB7 ${round.verdict} \xB7 ${round.metricName ? `${round.metricName}=${round.metricValue ?? "not found"}` : "no metric"} \xB7 ${round.accepted ? "accepted" : "rejected"}`);
    }
  }
  return {
    sessionId: status.sessionId,
    name: status.metadata.name,
    status: status.metadata.status,
    stopReason: status.metadata.stopReason,
    currentRound: status.metadata.currentRound,
    maxRounds: status.metadata.maxRounds,
    noImproveStreak: status.metadata.noImproveStreak,
    maxNoImproveRounds: status.metadata.maxNoImproveRounds,
    acceptedRounds: status.metadata.acceptedRoundSessionIds.length,
    rejectedRounds: status.metadata.rejectedRoundSessionIds.length,
    bestSessionId: status.metadata.bestSessionId,
    bestMetricValue: status.metadata.bestMetricValue,
    metric: status.metadata.metric,
    objective: status.metadata.objective,
    targetMetric: status.metadata.targetMetric,
    baselineSessionId: status.metadata.baselineSessionId,
    createdAt: status.metadata.createdAt,
    updatedAt: status.metadata.updatedAt,
    content: lines.join("\n")
  };
}

function printDivider() {
  console.log(a.dim("\u2500".repeat(60)));
}
function formatMetric(metricName, metricValue) {
  if (!metricName) {
    return "not configured";
  }
  if (metricValue === void 0) {
    return `${metricName} (not found)`;
  }
  return `${metricName}=${metricValue}`;
}
function formatTimestamp(value) {
  if (!value) {
    return "unknown";
  }
  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? value : date.toISOString();
}
function previewCommand(command, maxLength = 48) {
  if (command.length <= maxLength) {
    return command;
  }
  return `${command.slice(0, maxLength - 1)}\u2026`;
}
function getLatestTaskData(status) {
  if (!status || status.tasks.length === 0) {
    return {};
  }
  const latestTask = status.tasks[status.tasks.length - 1];
  return latestTask?.output?.data || {};
}
function getLoopOverrides(options) {
  return {
    cwd: options.cwd,
    budgetMs: options.budgetMs,
    maxRounds: options.maxRounds,
    maxNoImproveRounds: options.maxNoImproveRounds,
    targetMetric: options.targetMetric
  };
}
function printResearchLoopStatus(status) {
  console.log("");
  console.log(a.bold.cyan("\u{1F501} Research Loop Status"));
  printDivider();
  console.log(a.gray(`Session:   ${status.sessionId}`));
  console.log(a.gray(`Name:      ${status.metadata.name}`));
  console.log(a.gray(`Status:    ${status.metadata.status}`));
  console.log(a.gray(`Stop:      ${status.metadata.stopReason || "running"}`));
  console.log(a.gray(`Rounds:    ${status.metadata.currentRound}/${status.metadata.maxRounds}`));
  console.log(a.gray(`Objective: ${status.metadata.objective}`));
  console.log(a.gray(`Metric:    ${formatMetric(status.metadata.metric, status.metadata.bestMetricValue)}`));
  console.log(a.gray(`Baseline:  ${status.metadata.baselineSessionId || "none"}`));
  console.log(a.gray(`Best:      ${status.metadata.bestSessionId || "none"}`));
  console.log(a.gray(`Accepted:  ${status.metadata.acceptedRoundSessionIds.length}`));
  console.log(a.gray(`Rejected:  ${status.metadata.rejectedRoundSessionIds.length}`));
  console.log(a.gray(`Streak:    ${status.metadata.noImproveStreak}/${status.metadata.maxNoImproveRounds}`));
  if (status.latestRound) {
    console.log(a.gray(`Latest:    round ${status.latestRound.round} \xB7 ${status.latestRound.verdict} \xB7 ${formatMetric(status.latestRound.metricName, status.latestRound.metricValue)}`));
  }
  console.log("");
}
async function researchCommand(action, args = [], options = {}) {
  switch (action) {
    case "run":
      await runResearchCommand(options);
      return;
    case "init":
      await initResearchCommand(options);
      return;
    case "loop":
      await startResearchLoopCommand(options);
      return;
    case "round":
      await runResearchRoundCommand(args[0] || void 0, options);
      return;
    case "resume":
      await resumeResearchLoopCommand(args[0] || void 0, options);
      return;
    case "stop":
      await stopResearchLoopCommand(args[0] || void 0, options);
      return;
    case "status":
      await showResearchStatus(args[0] || void 0, options);
      return;
    case "sessions":
    case "list":
      await listResearchCommand(options);
      return;
    case "results":
      await showResearchResults(options);
      return;
    case "report":
      await showResearchReport(args[0] || void 0, options);
      return;
    case "help":
    case "":
      showResearchHelp();
      return;
    default:
      console.error(a.red(`Unknown research action: ${action}`));
      showResearchHelp();
  }
}
async function runResearchCommand(options) {
  if (!options.cmd) {
    console.error(a.red("Error: --cmd is required for `ccjk research run`"));
    console.log(a.dim('Example: ccjk research run --name baseline --cmd "python train.py" --metric val_bpb'));
    return;
  }
  const result = await runResearchExperiment({
    name: options.name,
    command: options.cmd,
    metricName: options.metric,
    budgetMs: options.budgetMs,
    cwd: options.cwd,
    baselineSessionId: options.baseline,
    objective: options.objective,
    dbPath: options.dbPath
  });
  console.log("");
  console.log(a.bold.cyan("\u{1F52C} Research Run"));
  printDivider();
  console.log(a.gray(`Session:   ${result.sessionId}`));
  console.log(a.gray(`Task:      ${result.taskId}`));
  console.log(a.gray(`Name:      ${result.name}`));
  console.log(a.gray(`Phase:     ${result.phase}`));
  console.log(a.gray(`Objective: ${result.objective}`));
  console.log(a.gray(`Verdict:   ${result.verdict}`));
  console.log(a.gray(`Reason:    ${result.verdictReason}`));
  console.log(a.gray(`Command:   ${result.command}`));
  console.log(a.gray(`CWD:       ${result.cwd}`));
  console.log(a.gray(`Status:    ${result.status}`));
  console.log(a.gray(`Exit code: ${result.exitCode}`));
  console.log(a.gray(`Metric:    ${formatMetric(result.metricName, result.metricValue)}`));
  if (result.comparison) {
    console.log(a.gray(`Baseline:  ${result.comparison.baselineName} (${result.comparison.baselineSessionId})`));
    console.log(a.gray(`Compare:   ${result.comparison.result}`));
  }
  console.log(a.gray(`Duration:  ${result.durationMs}ms`));
  if (result.stderr) {
    console.log("");
    console.log(a.bold("stderr"));
    console.log(a.dim(result.stderr));
  }
  if (result.stdout) {
    console.log("");
    console.log(a.bold("stdout"));
    console.log(a.dim(result.stdout));
  }
  console.log("");
}
async function initResearchCommand(options) {
  const { programPath } = initResearchProgram(options.program, options.cwd);
  console.log("");
  console.log(a.bold.cyan("\u{1F9ED} Research Program"));
  printDivider();
  console.log(a.gray(`Program:   ${programPath}`));
  console.log("");
}
async function startResearchLoopCommand(options) {
  const status = await startResearchLoop({
    programPath: options.program,
    cwd: options.cwd,
    dbPath: options.dbPath,
    overrides: getLoopOverrides(options)
  });
  printResearchLoopStatus(status);
}
async function runResearchRoundCommand(sessionId, options) {
  const status = await runResearchRound({
    sessionId,
    cwd: options.cwd,
    dbPath: options.dbPath,
    overrides: getLoopOverrides(options)
  });
  printResearchLoopStatus(status);
}
async function resumeResearchLoopCommand(sessionId, options) {
  const status = await resumeResearchLoop({
    sessionId,
    cwd: options.cwd,
    dbPath: options.dbPath,
    overrides: getLoopOverrides(options)
  });
  printResearchLoopStatus(status);
}
async function stopResearchLoopCommand(sessionId, options) {
  const status = stopResearchLoop({ sessionId, dbPath: options.dbPath });
  if (!status) {
    console.log(a.yellow(sessionId ? `Research loop session not found: ${sessionId}` : "No research loop sessions found."));
    console.log("");
    return;
  }
  printResearchLoopStatus(status);
}
async function showResearchStatus(sessionId, options) {
  const loopStatus = getResearchLoopStatus(sessionId, options.dbPath);
  if (loopStatus) {
    printResearchLoopStatus(loopStatus);
    return;
  }
  const resolvedSessionId = sessionId || getLatestResearchSession(options.dbPath)?.id;
  if (!resolvedSessionId) {
    console.log(a.yellow("No research sessions found."));
    console.log("");
    return;
  }
  const status = getResearchSessionStatus(resolvedSessionId, options.dbPath);
  if (!status) {
    console.log(a.yellow(`Research session not found: ${resolvedSessionId}`));
    console.log("");
    return;
  }
  const outputData = getLatestTaskData(status);
  const metricValue = typeof outputData.metricValue === "number" ? outputData.metricValue : typeof status.metadata.metricValue === "number" ? status.metadata.metricValue : void 0;
  console.log("");
  console.log(a.bold.cyan("\u{1F9EA} Research Status"));
  printDivider();
  console.log(a.gray(`Session:   ${status.sessionId}`));
  console.log(a.gray(`Name:      ${status.metadata.name || status.sessionId}`));
  console.log(a.gray(`Phase:     ${status.metadata.currentPhase || "unknown"}`));
  console.log(a.gray(`Objective: ${status.metadata.objective || "unknown"}`));
  console.log(a.gray(`Verdict:   ${status.metadata.verdict || "unknown"}`));
  console.log(a.gray(`Reason:    ${status.metadata.verdictReason || "unknown"}`));
  console.log(a.gray(`Command:   ${status.metadata.command || "unknown"}`));
  console.log(a.gray(`Metric:    ${formatMetric(status.metadata.metricName, metricValue)}`));
  console.log(a.gray(`Status:    ${outputData.status || status.metadata.status || "unknown"}`));
  console.log(a.gray(`Exit code: ${outputData.exitCode ?? status.metadata.exitCode ?? "unknown"}`));
  console.log(a.gray(`Duration:  ${outputData.durationMs ?? status.metadata.durationMs ?? "unknown"}ms`));
  console.log(a.gray(`Tasks:     ${status.metrics.completedTasks}/${status.metrics.totalTasks} completed`));
  console.log(a.gray(`Success:   ${Math.round((status.metrics.successRate || 0) * 100)}%`));
  console.log(a.gray(`Avg ms:    ${status.metrics.avgExecutionTime}`));
  console.log(a.gray(`Next:      ${status.recovery.nextExecutable.length}`));
  if (status.metadata.comparison) {
    const comparison = status.metadata.comparison;
    console.log(a.gray(`Baseline:  ${comparison.baselineName || comparison.baselineSessionId || "unknown"}`));
    console.log(a.gray(`Compare:   ${comparison.result || "unknown"}`));
    console.log(a.gray(`Summary:   ${comparison.summary || "unknown"}`));
  }
  if (status.decisions.length > 0) {
    const lastDecision = status.decisions[status.decisions.length - 1];
    console.log(a.gray(`Decision:  ${lastDecision.outcome || lastDecision.decision}`));
  }
  console.log("");
}
async function listResearchCommand(options) {
  const sessions = listResearchSessions(options.limit || 10, options.dbPath);
  console.log("");
  console.log(a.bold.cyan("\u{1F4DA} Research Sessions"));
  printDivider();
  if (sessions.length === 0) {
    console.log(a.yellow("No research sessions found."));
    console.log("");
    return;
  }
  for (const session of sessions) {
    console.log(a.bold(session.name));
    console.log(a.gray(`  ${session.id}`));
    console.log(a.gray(`  phase: ${session.currentPhase}`));
    console.log(a.gray(`  objective: ${session.objective || "unknown"}`));
    console.log(a.gray(`  verdict: ${session.verdict || "unknown"}`));
    if (session.baselineSessionId) {
      console.log(a.gray(`  baseline: ${session.baselineSessionId}`));
    }
    console.log(a.gray(`  cmd: ${session.command}`));
    console.log(a.gray(`  metric: ${session.metricName || "not configured"}`));
    console.log(a.gray(`  cwd: ${session.cwd}`));
  }
  console.log("");
}
async function showResearchResults(options) {
  const rows = listResearchResults(options.limit || 10, options.dbPath);
  const best = getBestResearchResult(options.dbPath);
  console.log("");
  console.log(a.bold.cyan("\u{1F4C8} Research Results"));
  printDivider();
  if (rows.length === 0) {
    console.log(a.yellow("No research results found."));
    console.log("");
    return;
  }
  if (best) {
    console.log(a.bold("Best"));
    console.log(a.gray(`  ${best.name} \xB7 ${formatMetric(best.metricName, best.metricValue)} \xB7 ${best.status} \xB7 ${best.durationMs}ms`));
    console.log(a.gray(`  objective: ${best.metricName ? ["loss", "error", "bpb", "perplexity", "ppl", "latency", "duration", "time", "cost", "price", "wer", "cer"].some((keyword) => best.metricName?.toLowerCase().includes(keyword)) ? "minimize" : "maximize" : "unknown"}`));
    console.log(a.gray(`  ${previewCommand(best.command)}`));
    console.log("");
  }
  console.log(a.bold("Recent"));
  for (const row of rows) {
    console.log(a.gray(`${formatTimestamp(row.timestamp)} \xB7 ${row.name} \xB7 ${row.status} \xB7 ${formatMetric(row.metricName, row.metricValue)} \xB7 ${row.durationMs}ms \xB7 ${previewCommand(row.command)}`));
  }
  console.log("");
}
async function showResearchReport(sessionId, options) {
  const loopReport = getResearchLoopReport(sessionId, options.dbPath);
  if (loopReport) {
    console.log("");
    console.log(a.bold.cyan("\u{1F4DD} Research Loop Report"));
    printDivider();
    console.log(loopReport.content);
    console.log("");
    return;
  }
  const report = getResearchReport(sessionId, options.dbPath);
  if (!report) {
    console.log(a.yellow(sessionId ? `Research session not found: ${sessionId}` : "No research sessions found."));
    console.log("");
    return;
  }
  console.log("");
  console.log(a.bold.cyan("\u{1F4DD} Research Report"));
  printDivider();
  console.log(a.gray(`Session:   ${report.sessionId}`));
  console.log(a.gray(`Name:      ${report.name}`));
  console.log(a.gray(`Created:   ${formatTimestamp(report.createdAt)}`));
  console.log(a.gray(`Command:   ${report.command}`));
  console.log(a.gray(`CWD:       ${report.cwd}`));
  console.log(a.gray(`Status:    ${report.status}`));
  console.log(a.gray(`Exit code: ${report.exitCode}`));
  console.log(a.gray(`Duration:  ${report.durationMs}ms`));
  console.log(a.gray(`Metric:    ${formatMetric(report.metricName, report.metricValue)}`));
  console.log(a.gray(`Objective: ${report.objective || "unknown"}`));
  console.log(a.gray(`Reason:    ${report.verdictReason}`));
  if (report.comparison) {
    console.log(a.gray(`Baseline:  ${report.comparison.baselineName} (${report.comparison.baselineSessionId})`));
    console.log(a.gray(`Compare:   ${report.comparison.result}`));
  }
  console.log(a.gray(`Outcome:   ${report.outcome}`));
  if (report.phaseHistory.length > 0) {
    console.log(a.gray(`Phases:    ${report.phaseHistory.join(" \u2192 ")}`));
  }
  console.log("");
}
function showResearchHelp() {
  console.log("");
  console.log(a.bold.cyan("ccjk research"));
  printDivider();
  console.log("  init     Create a research program template");
  console.log("  loop     Start a persisted research loop");
  console.log("  round    Run one candidate round for a loop");
  console.log("  resume   Continue a persisted research loop until stop");
  console.log("  stop     Stop a running research loop");
  console.log("  run      Run a persisted research experiment with optional baseline comparison");
  console.log("  status   Show the latest or selected research session status");
  console.log("  sessions List recent research sessions");
  console.log("  results  Show recent result rows and the current best run");
  console.log("  report   Render a compact persisted research report");
  console.log("");
  console.log(a.dim("Example: ccjk research init"));
  console.log(a.dim("Example: ccjk research loop --program .ccjk/research/program.md"));
  console.log(a.dim("Example: ccjk research round research-loop-123"));
  console.log(a.dim('Example: ccjk research run --name baseline --cmd "python train.py" --metric val_bpb --objective minimize'));
  console.log(a.dim("Example: ccjk research report research-123"));
  console.log("");
}

export { researchCommand, showResearchHelp };
