// src/rollout/readers/claude-jsonl.ts
import { readdir, readFile } from "fs/promises";
import { homedir } from "os";
import { join } from "path";
var DEFAULT_CLAUDE_PROJECTS_DIR = join(homedir(), ".claude", "projects");
function claudeProjectSlug(cwd) {
  return cwd.replace(/[^a-zA-Z0-9-]/g, "-");
}
async function findClaudeTranscripts(cwd, projectsDir = DEFAULT_CLAUDE_PROJECTS_DIR) {
  const dir = join(projectsDir, claudeProjectSlug(cwd));
  const names = await readdir(dir).catch(() => []);
  return names.filter((n) => n.endsWith(".jsonl")).sort().map((n) => ({ sessionId: n.replace(/\.jsonl$/, ""), path: join(dir, n) }));
}
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
function parseClaudeEntries(raw) {
  const out = [];
  for (const line of raw.split("\n")) {
    if (!line.trim()) continue;
    let entry;
    try {
      const parsed = JSON.parse(line);
      if (!isRecord(parsed)) continue;
      entry = parsed;
    } catch {
      continue;
    }
    if (entry.type !== "user" && entry.type !== "assistant") continue;
    const message = entry.message;
    if (!isRecord(message)) continue;
    out.push({
      type: entry.type,
      timestamp: typeof entry.timestamp === "string" ? entry.timestamp : null,
      message,
      toolUseResult: entry.toolUseResult,
      isSidechain: entry.isSidechain === true,
      agentId: typeof entry.agentId === "string" ? entry.agentId : null
    });
  }
  return out;
}
function blockText(content) {
  if (typeof content === "string") return content;
  if (!Array.isArray(content)) return "";
  return content.filter(
    (b) => isRecord(b) && b.type === "text" && typeof b.text === "string"
  ).map((b) => b.text).join("\n");
}
async function readClaudeTranscript(path, options = {}) {
  return transcriptFromEntries(parseClaudeEntries(await readFile(path, "utf8")), options);
}
function transcriptFromEntries(entries, options = {}) {
  const wantSidechain = options.includeSidechain === true;
  const messages = [];
  const usage = { tokensIn: 0, tokensOut: 0, cacheRead: 0, cacheWrite: 0 };
  let startedAt = null;
  let endedAt = null;
  let model = null;
  let lastAssistantApiId = null;
  let lastAssistantIndex = -1;
  for (const entry of entries) {
    if (entry.isSidechain !== wantSidechain) continue;
    const message = entry.message;
    if (entry.timestamp !== null) {
      if (startedAt === null) startedAt = entry.timestamp;
      endedAt = entry.timestamp;
    }
    if (entry.type === "user") {
      lastAssistantApiId = null;
      lastAssistantIndex = -1;
      const content2 = message.content;
      if (typeof content2 === "string") {
        messages.push({ role: "user", content: content2 });
        continue;
      }
      if (!Array.isArray(content2)) continue;
      let userText = "";
      for (const block of content2) {
        if (!isRecord(block)) continue;
        if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
          messages.push({
            role: "tool",
            tool_call_id: block.tool_use_id,
            content: blockText(block.content) || (typeof block.content === "string" ? block.content : "")
          });
        } else if (block.type === "text" && typeof block.text === "string") {
          userText += (userText.length > 0 ? "\n" : "") + block.text;
        }
      }
      if (userText.length > 0) messages.push({ role: "user", content: userText });
      continue;
    }
    if (typeof message.model === "string") model = message.model;
    const apiId = typeof message.id === "string" ? message.id : null;
    const continuesTurn = apiId !== null && apiId === lastAssistantApiId && lastAssistantIndex >= 0;
    const msgUsage = message.usage;
    if (isRecord(msgUsage) && !continuesTurn) {
      usage.tokensIn += typeof msgUsage.input_tokens === "number" ? msgUsage.input_tokens : 0;
      usage.tokensOut += typeof msgUsage.output_tokens === "number" ? msgUsage.output_tokens : 0;
      usage.cacheRead += typeof msgUsage.cache_read_input_tokens === "number" ? msgUsage.cache_read_input_tokens : 0;
      usage.cacheWrite += typeof msgUsage.cache_creation_input_tokens === "number" ? msgUsage.cache_creation_input_tokens : 0;
    }
    const content = message.content;
    if (!Array.isArray(content)) continue;
    let reasoning = "";
    let text = "";
    const toolCalls = [];
    for (const block of content) {
      if (!isRecord(block)) continue;
      if (block.type === "thinking" && typeof block.thinking === "string" && block.thinking.length > 0) {
        reasoning += (reasoning.length > 0 ? "\n" : "") + block.thinking;
      } else if (block.type === "text" && typeof block.text === "string") {
        text += (text.length > 0 ? "\n" : "") + block.text;
      } else if (block.type === "tool_use" && typeof block.id === "string") {
        toolCalls.push({
          id: block.id,
          type: "function",
          function: {
            name: typeof block.name === "string" ? block.name : "unknown",
            arguments: JSON.stringify(block.input ?? {})
          }
        });
      }
    }
    if (reasoning.length === 0 && text.length === 0 && toolCalls.length === 0) continue;
    if (continuesTurn) {
      const prev = messages[lastAssistantIndex];
      if (text.length > 0) prev.content = prev.content === null ? text : `${prev.content}
${text}`;
      if (reasoning.length > 0) {
        prev.reasoning_content = prev.reasoning_content === void 0 ? reasoning : `${prev.reasoning_content}
${reasoning}`;
      }
      if (toolCalls.length > 0) prev.tool_calls = [...prev.tool_calls ?? [], ...toolCalls];
      continue;
    }
    messages.push({
      role: "assistant",
      content: text.length > 0 ? text : null,
      ...reasoning.length > 0 ? { reasoning_content: reasoning } : {},
      ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
    });
    lastAssistantApiId = apiId;
    lastAssistantIndex = messages.length - 1;
  }
  return { messages, usage, startedAt, endedAt, model };
}

// src/rollout/readers/opencode-sqlite.ts
import { homedir as homedir2 } from "os";
import { join as join2 } from "path";
var DEFAULT_OPENCODE_DB = join2(homedir2(), ".local", "share", "opencode", "opencode.db");
var NODE_SQLITE_SPECIFIER = ["node", "sqlite"].join(":");
async function openOpencodeDb(path = DEFAULT_OPENCODE_DB) {
  try {
    const { DatabaseSync } = await import(
      /* @vite-ignore */
      NODE_SQLITE_SPECIFIER
    );
    const db = new DatabaseSync(path, { readOnly: true });
    db.prepare("SELECT id FROM session LIMIT 1").get();
    return db;
  } catch {
    return null;
  }
}
var isRecord2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
function parseSessionRow(row) {
  let model = null;
  if (typeof row.model === "string" && row.model.length > 0) {
    try {
      const parsed = JSON.parse(row.model);
      if (isRecord2(parsed)) model = parsed;
    } catch {
      model = null;
    }
  }
  return {
    id: String(row.id),
    parentId: row.parent_id === null || row.parent_id === void 0 ? null : String(row.parent_id),
    directory: String(row.directory),
    agent: row.agent === null || row.agent === void 0 ? null : String(row.agent),
    model,
    costUsd: Number(row.cost ?? 0),
    tokensInput: Number(row.tokens_input ?? 0),
    tokensOutput: Number(row.tokens_output ?? 0),
    tokensReasoning: Number(row.tokens_reasoning ?? 0),
    tokensCacheRead: Number(row.tokens_cache_read ?? 0),
    tokensCacheWrite: Number(row.tokens_cache_write ?? 0),
    timeCreated: Number(row.time_created ?? 0),
    timeUpdated: Number(row.time_updated ?? 0)
  };
}
var SESSION_COLUMNS = "id, parent_id, directory, agent, model, cost, tokens_input, tokens_output, tokens_reasoning, tokens_cache_read, tokens_cache_write, time_created, time_updated";
function findOpencodeSessionsByDirectory(db, directory) {
  const rows = db.prepare(`SELECT ${SESSION_COLUMNS} FROM session WHERE directory = ? ORDER BY time_created`).all(directory);
  return rows.map(parseSessionRow);
}
function findOpencodeSessionById(db, sessionId) {
  const row = db.prepare(`SELECT ${SESSION_COLUMNS} FROM session WHERE id = ?`).get(sessionId);
  return row === void 0 ? null : parseSessionRow(row);
}
function toolResultContent(output) {
  if (typeof output === "string") return output;
  if (output === null || output === void 0) return "";
  return JSON.stringify(output);
}
function readOpencodeSessionMessages(db, sessionId) {
  const messageRows = db.prepare("SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created, id").all(sessionId);
  const partsStmt = db.prepare("SELECT data FROM part WHERE message_id = ? ORDER BY id");
  const messages = [];
  for (const messageRow of messageRows) {
    let data;
    try {
      const parsed = JSON.parse(messageRow.data);
      if (!isRecord2(parsed)) continue;
      data = parsed;
    } catch {
      continue;
    }
    const parts = [];
    for (const row of partsStmt.all(messageRow.id)) {
      try {
        const parsed = JSON.parse(row.data);
        if (isRecord2(parsed)) parts.push(parsed);
      } catch {
      }
    }
    if (data.role === "user") {
      const text = parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("\n");
      messages.push({ role: "user", content: text });
      continue;
    }
    if (data.role !== "assistant") continue;
    const steps = [];
    let current = [];
    for (const part of parts) {
      if (part.type === "step-start") {
        if (current.length > 0) steps.push(current);
        current = [];
        continue;
      }
      if (part.type === "step-finish" || part.type === "snapshot" || part.type === "patch") continue;
      current.push(part);
    }
    if (current.length > 0) steps.push(current);
    for (const step of steps) {
      const reasoning = step.filter((p) => p.type === "reasoning" && typeof p.text === "string" && p.text.length > 0).map((p) => p.text).join("\n");
      const text = step.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("\n");
      const toolParts = step.filter((p) => p.type === "tool" && typeof p.callID === "string");
      const toolCalls = toolParts.map((p) => ({
        id: p.callID,
        type: "function",
        function: {
          name: p.tool ?? "unknown",
          arguments: JSON.stringify(p.state?.input ?? {})
        }
      }));
      if (reasoning.length === 0 && text.length === 0 && toolCalls.length === 0) continue;
      messages.push({
        role: "assistant",
        content: text.length > 0 ? text : null,
        ...reasoning.length > 0 ? { reasoning_content: reasoning } : {},
        ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
      });
      for (const p of toolParts) {
        messages.push({
          role: "tool",
          tool_call_id: p.callID,
          name: p.tool ?? "unknown",
          content: toolResultContent(p.state?.output)
        });
      }
    }
  }
  return messages;
}

export {
  DEFAULT_CLAUDE_PROJECTS_DIR,
  claudeProjectSlug,
  findClaudeTranscripts,
  parseClaudeEntries,
  readClaudeTranscript,
  transcriptFromEntries,
  DEFAULT_OPENCODE_DB,
  openOpencodeDb,
  findOpencodeSessionsByDirectory,
  findOpencodeSessionById,
  readOpencodeSessionMessages
};
//# sourceMappingURL=chunk-VBQ3CRKH.js.map