// src/adapters/claude/session/jsonl-hydration.ts
import { randomUUID } from "crypto";
import * as fs from "fs/promises";
import * as os from "os";
import * as path from "path";
var CHARS_PER_TOKEN = 4;
var DEFAULT_MAX_TOKENS = 15e4;
function estimateTurnTokens(turn) {
  let chars = 0;
  for (const block of turn.content) {
    if ("text" in block && typeof block.text === "string") {
      chars += block.text.length;
    }
  }
  if (turn.toolCalls) {
    for (const tc of turn.toolCalls) {
      chars += JSON.stringify(tc.input ?? "").length;
      if (tc.result !== void 0) {
        chars += typeof tc.result === "string" ? tc.result.length : JSON.stringify(tc.result).length;
      }
    }
  }
  return Math.ceil(chars / CHARS_PER_TOKEN);
}
function selectRecentTurns(turns, maxTokens = DEFAULT_MAX_TOKENS) {
  let budget = maxTokens;
  let startIndex = turns.length;
  for (let i = turns.length - 1; i >= 0; i--) {
    const cost = estimateTurnTokens(turns[i]);
    if (cost > budget) break;
    budget -= cost;
    startIndex = i;
  }
  while (startIndex < turns.length && turns[startIndex].role !== "user") {
    startIndex++;
  }
  return turns.slice(startIndex);
}

// ../shared/dist/index.js
var consoleLogger = {
  info: (_message, _data) => {
  },
  debug: (_message, _data) => {
  },
  error: (_message, _data) => {
  },
  warn: (_message, _data) => {
  }
};
var Saga = class {
  completedSteps = [];
  currentStepName = "unknown";
  stepTimings = [];
  log;
  constructor(logger) {
    this.log = logger ?? consoleLogger;
  }
  /**
   * Run the saga with the given input.
   * Returns a discriminated union result - either success with data or failure with error details.
   */
  async run(input) {
    this.completedSteps = [];
    this.currentStepName = "unknown";
    this.stepTimings = [];
    const sagaStart = performance.now();
    try {
      const result = await this.execute(input);
      const totalDuration = performance.now() - sagaStart;
      this.log.debug("Saga completed successfully", {
        sagaName: this.sagaName,
        stepsCompleted: this.completedSteps.length,
        totalDurationMs: Math.round(totalDuration),
        stepTimings: this.stepTimings
      });
      return { success: true, data: result };
    } catch (error) {
      this.log.error("Saga failed, initiating rollback", {
        sagaName: this.sagaName,
        failedStep: this.currentStepName,
        error: error instanceof Error ? error.message : String(error),
        completedStepTimings: this.stepTimings
      });
      await this.rollback();
      return {
        success: false,
        error: error instanceof Error ? error.message : String(error),
        failedStep: this.currentStepName
      };
    }
  }
  /**
   * Execute a step with its rollback action.
   * If the step succeeds, its rollback action is stored for potential rollback.
   * The step name is automatically tracked for error reporting.
   *
   * @param config - Step configuration with name, execute, and rollback functions
   * @returns The result of the execute function
   * @throws Re-throws any error from the execute function (triggers rollback)
   */
  async step(config) {
    this.currentStepName = config.name;
    const stepStart = performance.now();
    const result = await config.execute();
    const durationMs = Math.round(performance.now() - stepStart);
    this.stepTimings.push({ name: config.name, durationMs });
    this.completedSteps.push({
      name: config.name,
      rollback: () => config.rollback(result)
    });
    return result;
  }
  /**
   * Execute a step that doesn't need rollback.
   * Useful for read-only operations or operations that are idempotent.
   * The step name is automatically tracked for error reporting.
   *
   * @param name - Step name for logging and error tracking
   * @param execute - The action to execute
   * @returns The result of the execute function
   */
  async readOnlyStep(name, execute) {
    this.currentStepName = name;
    const stepStart = performance.now();
    const result = await execute();
    const durationMs = Math.round(performance.now() - stepStart);
    this.stepTimings.push({ name, durationMs });
    return result;
  }
  /**
   * Roll back all completed steps in reverse order.
   * Rollback errors are logged but don't stop the rollback of other steps.
   */
  async rollback() {
    this.log.info("Rolling back saga", {
      stepsToRollback: this.completedSteps.length
    });
    const stepsReversed = [...this.completedSteps].reverse();
    for (const step of stepsReversed) {
      try {
        await step.rollback();
      } catch (error) {
        this.log.error(`Failed to rollback step: ${step.name}`, {
          error: error instanceof Error ? error.message : String(error)
        });
      }
    }
    this.log.info("Rollback completed", {
      stepsAttempted: this.completedSteps.length
    });
  }
  /**
   * Get the number of completed steps (useful for testing)
   */
  getCompletedStepCount() {
    return this.completedSteps.length;
  }
};

// src/acp-extensions.ts
var POSTHOG_NOTIFICATIONS = {
  /** Git branch was created for a task */
  BRANCH_CREATED: "_posthog/branch_created",
  /** Task run has started execution */
  RUN_STARTED: "_posthog/run_started",
  /** Task has completed (success or failure) */
  TASK_COMPLETE: "_posthog/task_complete",
  /** Agent finished processing a turn (prompt returned, waiting for next input) */
  TURN_COMPLETE: "_posthog/turn_complete",
  /** Error occurred during task execution */
  ERROR: "_posthog/error",
  /** Console/log output from the agent */
  CONSOLE: "_posthog/console",
  /** Maps taskRunId to agent's sessionId and adapter type (for resumption) */
  SDK_SESSION: "_posthog/sdk_session",
  /** Git checkpoint captured for handoff */
  GIT_CHECKPOINT: "_posthog/git_checkpoint",
  /** Agent mode changed (interactive/background) */
  MODE_CHANGE: "_posthog/mode_change",
  /** Request to resume a session from previous state */
  SESSION_RESUME: "_posthog/session/resume",
  /** User message sent from client to agent */
  USER_MESSAGE: "_posthog/user_message",
  /** Request to cancel current operation */
  CANCEL: "_posthog/cancel",
  /** Request to close the session */
  CLOSE: "_posthog/close",
  /** Agent status update (thinking, working, etc.) */
  STATUS: "_posthog/status",
  /** Structured backend progress notification; events in the same turn group into one card on the client */
  PROGRESS: "_posthog/progress",
  /** Task-level notification (progress, milestones) */
  TASK_NOTIFICATION: "_posthog/task_notification",
  /** Marks a boundary for log compaction */
  COMPACT_BOUNDARY: "_posthog/compact_boundary",
  /** Token usage update for a session turn */
  USAGE_UPDATE: "_posthog/usage_update",
  /** Response to a relayed permission request (plan approval, question) */
  PERMISSION_RESPONSE: "_posthog/permission_response"
};

// src/sagas/resume-saga.ts
var ResumeSaga = class extends Saga {
  sagaName = "ResumeSaga";
  async execute(input) {
    const { taskId, runId, apiClient } = input;
    const taskRun = await this.readOnlyStep(
      "fetch_task_run",
      () => apiClient.getTaskRun(taskId, runId)
    );
    if (!taskRun.log_url) {
      this.log.info("No log URL found, starting fresh");
      return this.emptyResult();
    }
    const entries = await this.readOnlyStep(
      "fetch_logs",
      () => apiClient.fetchTaskRunLogs(taskRun)
    );
    if (entries.length === 0) {
      this.log.info("No log entries found, starting fresh");
      return this.emptyResult();
    }
    this.log.info("Fetched log entries", { count: entries.length });
    const latestGitCheckpoint = await this.readOnlyStep(
      "find_git_checkpoint",
      () => Promise.resolve(this.findLatestGitCheckpoint(entries))
    );
    if (latestGitCheckpoint) {
      this.log.info("Found git checkpoint", {
        checkpointId: latestGitCheckpoint.checkpointId,
        branch: latestGitCheckpoint.branch
      });
    }
    const conversation = await this.readOnlyStep(
      "rebuild_conversation",
      () => Promise.resolve(this.rebuildConversation(entries))
    );
    const lastDevice = await this.readOnlyStep(
      "find_device",
      () => Promise.resolve(this.findLastDeviceInfo(entries))
    );
    this.log.info("Resume state rebuilt", {
      turns: conversation.length,
      hasGitCheckpoint: !!latestGitCheckpoint,
      interrupted: false
    });
    return {
      conversation,
      latestGitCheckpoint,
      interrupted: false,
      lastDevice,
      logEntryCount: entries.length
    };
  }
  emptyResult() {
    return {
      conversation: [],
      latestGitCheckpoint: null,
      interrupted: false,
      logEntryCount: 0
    };
  }
  findLatestGitCheckpoint(entries) {
    const sdkPrefixedMethod = `_${POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT}`;
    for (let i = entries.length - 1; i >= 0; i--) {
      const entry = entries[i];
      const method = entry.notification?.method;
      if (method === sdkPrefixedMethod || method === POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT) {
        const params = entry.notification?.params;
        if (params?.checkpointId && params?.checkpointRef) {
          return params;
        }
      }
    }
    return null;
  }
  findLastDeviceInfo(entries) {
    for (let i = entries.length - 1; i >= 0; i--) {
      const entry = entries[i];
      const params = entry.notification?.params;
      if (params?.device) {
        return params.device;
      }
    }
    return void 0;
  }
  rebuildConversation(entries) {
    const turns = [];
    let currentAssistantContent = [];
    let currentToolCalls = [];
    for (const entry of entries) {
      const method = entry.notification?.method;
      const params = entry.notification?.params;
      if (method === "session/update" && params?.update) {
        const update = params.update;
        const sessionUpdate = update.sessionUpdate;
        switch (sessionUpdate) {
          case "user_message":
          case "user_message_chunk": {
            if (currentAssistantContent.length > 0 || currentToolCalls.length > 0) {
              turns.push({
                role: "assistant",
                content: currentAssistantContent,
                toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0
              });
              currentAssistantContent = [];
              currentToolCalls = [];
            }
            const content = update.content;
            const contentArray = Array.isArray(content) ? content : [content];
            turns.push({
              role: "user",
              content: contentArray
            });
            break;
          }
          case "agent_message": {
            const content = update.content;
            if (content) {
              if (content.type === "text" && currentAssistantContent.length > 0 && currentAssistantContent[currentAssistantContent.length - 1].type === "text") {
                const lastBlock = currentAssistantContent[currentAssistantContent.length - 1];
                lastBlock.text += content.text;
              } else {
                currentAssistantContent.push(content);
              }
            }
            break;
          }
          case "agent_message_chunk": {
            const content = update.content;
            if (content) {
              if (content.type === "text" && currentAssistantContent.length > 0 && currentAssistantContent[currentAssistantContent.length - 1].type === "text") {
                const lastBlock = currentAssistantContent[currentAssistantContent.length - 1];
                lastBlock.text += content.text;
              } else {
                currentAssistantContent.push(content);
              }
            }
            break;
          }
          case "tool_call":
          case "tool_call_update": {
            const meta = update._meta?.claudeCode;
            if (meta) {
              const toolCallId = meta.toolCallId;
              const toolName = meta.toolName;
              const toolInput = meta.toolInput;
              const toolResponse = meta.toolResponse;
              if (toolCallId && toolName) {
                let toolCall = currentToolCalls.find(
                  (tc) => tc.toolCallId === toolCallId
                );
                if (!toolCall) {
                  toolCall = {
                    toolCallId,
                    toolName,
                    input: toolInput
                  };
                  currentToolCalls.push(toolCall);
                }
                if (toolResponse !== void 0) {
                  toolCall.result = toolResponse;
                }
              }
            }
            break;
          }
          case "tool_result": {
            const meta = update._meta?.claudeCode;
            if (meta) {
              const toolCallId = meta.toolCallId;
              const toolResponse = meta.toolResponse;
              if (toolCallId) {
                const toolCall = currentToolCalls.find(
                  (tc) => tc.toolCallId === toolCallId
                );
                if (toolCall && toolResponse !== void 0) {
                  toolCall.result = toolResponse;
                }
              }
            }
            break;
          }
        }
      }
    }
    if (currentAssistantContent.length > 0 || currentToolCalls.length > 0) {
      turns.push({
        role: "assistant",
        content: currentAssistantContent,
        toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0
      });
    }
    return turns;
  }
};

// src/utils/logger.ts
var Logger = class _Logger {
  debugEnabled;
  prefix;
  scope;
  onLog;
  constructor(config = {}) {
    this.debugEnabled = config.debug ?? false;
    this.prefix = config.prefix ?? "[PostHog Agent]";
    this.scope = config.scope ?? "agent";
    this.onLog = config.onLog;
  }
  formatMessage(level, message, data) {
    const timestamp = (/* @__PURE__ */ new Date()).toISOString();
    const base = `${timestamp} ${this.prefix} [${level}] ${message}`;
    if (data !== void 0) {
      return `${base} ${JSON.stringify(data, null, 2)}`;
    }
    return base;
  }
  emitLog(level, message, data) {
    if (this.onLog) {
      this.onLog(level, this.scope, message, data);
      return;
    }
    const shouldLog = this.debugEnabled || level === "error";
    if (shouldLog) {
      console[level](this.formatMessage(level.toLowerCase(), message, data));
    }
  }
  error(message, error) {
    const data = error instanceof Error ? { message: error.message, stack: error.stack } : error;
    this.emitLog("error", message, data);
  }
  warn(message, data) {
    this.emitLog("warn", message, data);
  }
  info(message, data) {
    this.emitLog("info", message, data);
  }
  debug(message, data) {
    this.emitLog("debug", message, data);
  }
  child(childPrefix) {
    return new _Logger({
      debug: this.debugEnabled,
      prefix: `${this.prefix} [${childPrefix}]`,
      scope: `${this.scope}:${childPrefix}`,
      onLog: this.onLog
    });
  }
};

// src/resume.ts
async function resumeFromLog(config) {
  const logger = config.logger || new Logger({ debug: false, prefix: "[Resume]" });
  logger.info("Resuming from log", {
    taskId: config.taskId,
    runId: config.runId
  });
  const saga = new ResumeSaga(logger);
  const result = await saga.run({
    taskId: config.taskId,
    runId: config.runId,
    repositoryPath: config.repositoryPath,
    apiClient: config.apiClient,
    logger
  });
  if (!result.success) {
    logger.error("Failed to resume from log", {
      error: result.error,
      failedStep: result.failedStep
    });
    throw new Error(
      `Failed to resume at step '${result.failedStep}': ${result.error}`
    );
  }
  return {
    conversation: result.data.conversation,
    latestGitCheckpoint: result.data.latestGitCheckpoint,
    interrupted: result.data.interrupted,
    lastDevice: result.data.lastDevice,
    logEntryCount: result.data.logEntryCount
  };
}
function conversationToPromptHistory(conversation) {
  return conversation.map((turn) => ({
    role: turn.role,
    content: turn.content
  }));
}
var RESUME_HISTORY_TOKEN_BUDGET = 5e4;
var TOOL_RESULT_MAX_CHARS = 2e3;
var RESUME_CONTEXT_MARKERS = [
  "You are resuming a previous conversation",
  "Here is the conversation history from the",
  "Continue from where you left off"
];
function isResumeContextTurn(turn) {
  if (turn.role !== "user") return false;
  const text = turn.content.filter((b) => b.type === "text").map((b) => b.text).join("");
  return RESUME_CONTEXT_MARKERS.some((marker) => text.includes(marker));
}
function formatConversationForResume(conversation) {
  const filtered = conversation.filter((turn) => !isResumeContextTurn(turn));
  const selected = selectRecentTurns(filtered, RESUME_HISTORY_TOKEN_BUDGET);
  const parts = [];
  if (selected.length < filtered.length) {
    parts.push(
      `*(${filtered.length - selected.length} earlier turns omitted)*`
    );
  }
  for (const turn of selected) {
    const role = turn.role === "user" ? "User" : "Assistant";
    const textParts = turn.content.filter((block) => block.type === "text").map((block) => block.text);
    if (textParts.length > 0) {
      parts.push(`**${role}**: ${textParts.join("\n")}`);
    }
    if (turn.toolCalls?.length) {
      const toolSummary = turn.toolCalls.map((tc) => {
        let resultStr = "";
        if (tc.result !== void 0) {
          const raw = typeof tc.result === "string" ? tc.result : JSON.stringify(tc.result);
          resultStr = raw.length > TOOL_RESULT_MAX_CHARS ? ` \u2192 ${raw.substring(0, TOOL_RESULT_MAX_CHARS)}...(truncated)` : ` \u2192 ${raw}`;
        }
        return `  - ${tc.toolName}${resultStr}`;
      }).join("\n");
      parts.push(`**${role} (tools)**:
${toolSummary}`);
    }
  }
  return parts.join("\n\n");
}
export {
  conversationToPromptHistory,
  formatConversationForResume,
  resumeFromLog
};
//# sourceMappingURL=resume.js.map