import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);

// 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";

// src/gateway-models.ts
var DEFAULT_GATEWAY_MODEL = "claude-opus-4-8";
var CACHE_TTL = 10 * 60 * 1e3;

// src/utils/acp-content.ts
function isEmptyContentBlock(block) {
  const candidate = block;
  if (candidate?.type === "text") return !candidate.text;
  if (candidate?.type === "thinking") return !candidate.thinking;
  return false;
}

// ../shared/dist/index.js
import { z } from "zod";
import { z as z2 } from "zod";
import { z as z3 } from "zod";
import { z as z4 } from "zod";
var ANALYTICS_EVENTS = {
  // App lifecycle
  APP_STARTED: "App started",
  APP_QUIT: "App quit",
  // Authentication
  USER_LOGGED_IN: "User logged in",
  USER_LOGGED_OUT: "User logged out",
  // Task management
  TASK_LIST_VIEWED: "Task list viewed",
  TASK_CREATED: "Task created",
  TASK_VIEWED: "Task viewed",
  TASK_RUN: "Task run",
  TASK_RUN_STARTED: "Task run started",
  TASK_RUN_COMPLETED: "Task run completed",
  TASK_RUN_CANCELLED: "Task run cancelled",
  TASK_RUN_STOPPED: "Task run stopped",
  PROMPT_SENT: "Prompt sent",
  // Claude Code session import
  CLAUDE_SESSIONS_SHOWN: "Claude Code sessions shown",
  CLAUDE_SESSION_IMPORTED: "Claude Code session imported",
  CLAUDE_SESSION_IMPORT_FAILED: "Claude Code session import failed",
  // Repository
  REPOSITORY_SELECTED: "Repository selected",
  // Git operations
  GIT_ACTION_EXECUTED: "Git action executed",
  PR_CREATED: "PR created",
  AGENT_FILE_ACTIVITY: "Agent file activity",
  BRANCH_LINKED: "Branch linked",
  BRANCH_UNLINKED: "Branch unlinked",
  BRANCH_LINK_DEFAULT_BRANCH_UNKNOWN: "Branch link default branch unknown",
  // File interactions
  FILE_OPENED: "File opened",
  FILE_DIFF_VIEWED: "File diff viewed",
  REVIEW_PANEL_VIEWED: "Review panel viewed",
  DIFF_VIEW_MODE_CHANGED: "Diff view mode changed",
  // Workspace events
  WORKSPACE_CREATED: "Workspace created",
  WORKSPACE_SCRIPTS_STARTED: "Workspace scripts started",
  FOLDER_REGISTERED: "Folder registered",
  // Navigation events
  SETTINGS_VIEWED: "Settings viewed",
  COMMAND_MENU_OPENED: "Command menu opened",
  COMMAND_MENU_ACTION: "Command menu action",
  COMMAND_CENTER_VIEWED: "Command center viewed",
  BRAINROT_ACTIVATED: "Brainrot activated",
  SKILL_BUTTON_TRIGGERED: "Skill button triggered",
  POSTHOG_WEB_OPENED: "PostHog web opened",
  // Permission events
  PERMISSION_RESPONDED: "Permission responded",
  PERMISSION_CANCELLED: "Permission cancelled",
  // Session config events
  SESSION_CONFIG_CHANGED: "Session config changed",
  // Settings events
  SETTING_CHANGED: "Setting changed",
  CUSTOM_SOUND_ADDED: "Custom sound added",
  CUSTOM_SOUND_RECORDING_SILENT: "Custom sound recording silent",
  // Feedback events
  TASK_FEEDBACK: "Task feedback",
  // Branch mismatch events
  BRANCH_MISMATCH_WARNING_SHOWN: "Branch mismatch warning shown",
  BRANCH_MISMATCH_ACTION: "Branch mismatch action",
  // Tour events
  TOUR_EVENT: "Tour event",
  // Onboarding events
  ONBOARDING_STARTED: "Onboarding started",
  ONBOARDING_STEP_VIEWED: "Onboarding step viewed",
  ONBOARDING_STEP_COMPLETED: "Onboarding step completed",
  ONBOARDING_STEP_SKIPPED: "Onboarding step skipped",
  ONBOARDING_SIGN_IN_INITIATED: "Onboarding sign in initiated",
  ONBOARDING_PROJECT_SELECTED: "Onboarding project selected",
  ONBOARDING_INVITE_CODE_SUBMITTED: "Onboarding invite code submitted",
  ONBOARDING_FOLDER_SELECTED: "Onboarding folder selected",
  ONBOARDING_GITHUB_CONNECT_STARTED: "Onboarding github connect started",
  ONBOARDING_GITHUB_CONNECT_FAILED: "Onboarding github connect failed",
  ONBOARDING_GITHUB_CONNECTED: "Onboarding github connected",
  ONBOARDING_CLI_CHECK_COMPLETED: "Onboarding cli check completed",
  ONBOARDING_CLI_RUN_COMPLETED: "Onboarding cli run completed",
  ONBOARDING_COMPLETED: "Onboarding completed",
  ONBOARDING_ABANDONED: "Onboarding abandoned",
  AI_CONSENT_GATE_SHOWN: "Ai consent gate shown",
  AI_CONSENT_APPROVED: "Ai consent approved",
  AI_CONSENT_GRANTED_INAPP: "Ai consent granted in-app",
  // Setup / onboarding events
  SETUP_DISCOVERY_STARTED: "Setup discovery started",
  SETUP_DISCOVERY_COMPLETED: "Setup discovery completed",
  SETUP_DISCOVERY_FAILED: "Setup discovery failed",
  SETUP_TASK_SELECTED: "Setup task selected",
  SETUP_TASK_DISMISSED: "Setup task dismissed",
  // Deep link events
  DEEP_LINK_NEW_TASK: "Deep link new task",
  DEEP_LINK_PLAN: "Deep link plan",
  DEEP_LINK_ISSUE: "Deep link issue",
  DEEP_LINK_ISSUE_FAILED: "Deep link issue failed",
  DEEP_LINK_CANVAS: "Deep link canvas",
  DEEP_LINK_CHANNEL: "Deep link channel",
  // Error events
  TASK_CREATION_FAILED: "Task creation failed",
  AGENT_SESSION_ERROR: "Agent session error",
  CLOUD_STREAM_DISCONNECTED: "Cloud stream disconnected",
  // Inbox events
  INBOX_VIEWED: "Inbox viewed",
  INBOX_REPORT_OPENED: "Inbox report opened",
  INBOX_REPORT_CLOSED: "Inbox report closed",
  INBOX_REPORT_ACTION: "Inbox report action",
  INBOX_REPORT_SCROLLED: "Inbox report scrolled",
  SIGNAL_SOURCE_CONNECTED: "Signal source connected",
  // Agents page events
  AGENTS_VIEWED: "Agents viewed",
  AGENTS_ACTION: "Agents action",
  // Scout events
  SCOUT_FLEET_VIEWED: "Scout fleet viewed",
  SCOUT_DETAIL_VIEWED: "Scout detail viewed",
  SCOUT_CONFIG_CHANGED: "Scout config changed",
  SCOUT_CHAT_STARTED: "Scout chat started",
  SCOUT_ACTION: "Scout action",
  // Usage and spend analysis events
  USAGE_VIEWED: "Usage viewed",
  SPEND_ANALYSIS_TASK_OPENED: "Spend analysis task opened",
  // Prompt history events
  PROMPT_HISTORY_OPENED: "Prompt history opened",
  PROMPT_HISTORY_SELECTED: "Prompt history selected",
  // Subscription events
  UPGRADE_PROMPT_SHOWN: "Upgrade prompt shown",
  UPGRADE_PROMPT_CLICKED: "Upgrade prompt clicked",
  CLOUD_TASK_USAGE_BLOCKED: "Cloud task usage blocked",
  SUBSCRIPTION_STARTED: "Subscription started",
  SUBSCRIPTION_CANCELLED: "Subscription cancelled",
  USAGE_BILLING_ANNOUNCEMENT_ACKNOWLEDGED: "Usage billing announcement acknowledged",
  // Project Bluebird (Channels) events
  CHANNELS_SPACE_VIEWED: "Channels space viewed",
  CHANNEL_ACTION: "Channel action",
  DASHBOARD_ACTION: "Dashboard action",
  CANVAS_PROMPT_SENT: "Canvas prompt sent",
  CONTEXT_ACTION: "Context action",
  // Autoresearch events
  AUTORESEARCH_ARMED: "Autoresearch armed",
  AUTORESEARCH_RUN_STARTED: "Autoresearch run started"
};
var INBOX_ANALYTICS_EVENT_NAMES = /* @__PURE__ */ new Set([
  ANALYTICS_EVENTS.INBOX_VIEWED,
  ANALYTICS_EVENTS.INBOX_REPORT_OPENED,
  ANALYTICS_EVENTS.INBOX_REPORT_CLOSED,
  ANALYTICS_EVENTS.INBOX_REPORT_ACTION,
  ANALYTICS_EVENTS.INBOX_REPORT_SCROLLED,
  ANALYTICS_EVENTS.SIGNAL_SOURCE_CONNECTED
]);
var archivedTaskSchema = z.object({
  taskId: z.string(),
  archivedAt: z.string(),
  folderId: z.string(),
  mode: z.enum(["worktree", "local", "cloud"]),
  worktreeName: z.string().nullable(),
  branchName: z.string().nullable(),
  checkpointId: z.string().nullable()
});
var IMAGE_MIME_TYPES = {
  png: "image/png",
  jpg: "image/jpeg",
  jpeg: "image/jpeg",
  gif: "image/gif",
  webp: "image/webp",
  bmp: "image/bmp",
  ico: "image/x-icon",
  tiff: "image/tiff",
  tif: "image/tiff",
  svg: "image/svg+xml",
  heic: "image/heic",
  heif: "image/heif",
  avif: "image/avif"
};
var IMAGE_EXTENSIONS = new Set(
  Object.keys(IMAGE_MIME_TYPES)
);
var CLAUDE_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
  "image/jpeg",
  "image/png",
  "image/gif",
  "image/webp"
]);
function isClaudeImageMimeType(mimeType) {
  return CLAUDE_IMAGE_MIME_TYPES.has(mimeType.toLowerCase());
}
var MAX_DATA_URL_LENGTH = 20 * 1024 * 1024;
var MAX_IMAGE_BASE64_LENGTH = 15 * 1024 * 1024;
var MAX_CLAUDE_IMAGE_BYTES = 5 * 1024 * 1024;
function estimateBase64Bytes(base64) {
  const padding = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
  return Math.floor(base64.length * 3 / 4) - padding;
}
var AUDIO_VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
  "mp3",
  "mp4",
  "wav",
  "avi",
  "mov",
  "mkv",
  "webm",
  "mpg",
  "mpeg",
  "flac",
  "ogg",
  "ogv",
  "m4a",
  "m4v",
  "aac"
]);
var ARCHIVE_EXTENSIONS = /* @__PURE__ */ new Set([
  "zip",
  "tar",
  "gz",
  "tgz",
  "bz2",
  "xz",
  "rar",
  "7z"
]);
var EXECUTABLE_EXTENSIONS = /* @__PURE__ */ new Set([
  "exe",
  "dll",
  "so",
  "dylib",
  "wasm",
  "bin",
  "o"
]);
var FONT_EXTENSIONS = /* @__PURE__ */ new Set([
  "ttf",
  "otf",
  "woff",
  "woff2",
  "eot"
]);
var DOCUMENT_BINARY_EXTENSIONS = /* @__PURE__ */ new Set(["pdf"]);
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
  ...Object.keys(IMAGE_MIME_TYPES).filter((ext) => ext !== "svg"),
  ...AUDIO_VIDEO_EXTENSIONS,
  ...ARCHIVE_EXTENSIONS,
  ...EXECUTABLE_EXTENSIONS,
  ...FONT_EXTENSIONS,
  ...DOCUMENT_BINARY_EXTENSIONS
]);
var browserTabSchema = z2.object({
  id: z2.string(),
  windowId: z2.string(),
  /** Canvas this tab shows. Null for a task tab or a blank tab. */
  dashboardId: z2.string().nullable(),
  /** Task this tab shows. Null for a canvas tab or a blank tab. */
  taskId: z2.string().nullable().default(null),
  channelId: z2.string().nullable().default(null),
  /**
   * Channel sub-section this tab fronts (`artifacts` / `history` /
   * `context`). Null = the channel home, or a non-channel tab (canvas / task /
   * blank). Pairs with `channelId`: the two together identify a channel tab.
   */
  channelSection: z2.string().nullable().default(null),
  /**
   * Top-level app page this tab shows (`inbox` / `agents` / `skills` /
   * `mcp-servers` / `command-center` / `home`). Null for a canvas / task /
   * channel / blank tab. These pages have no channel, task, or dashboard id, so
   * this is what lets them be a real tab target (label + restore-on-refocus).
   */
  appView: z2.string().nullable().default(null),
  /** Gap-spaced ordering key within a window. Reindexed on collision. */
  position: z2.number(),
  /**
   * Reserved/unwired. Opaque per-tab state for future scroll restoration etc.
   * Plain `z.unknown()` (not `.default(null)`) so the inferred shape matches
   * the tRPC-wire inference on the client — keeps the renderer facade type and
   * the transport type identical.
   */
  scrollState: z2.unknown().optional(),
  createdAt: z2.number(),
  lastActiveAt: z2.number()
});
var windowBoundsSchema = z2.object({
  x: z2.number(),
  y: z2.number(),
  width: z2.number(),
  height: z2.number()
});
var browserWindowSchema = z2.object({
  id: z2.string(),
  isPrimary: z2.boolean(),
  /** Saved geometry for session restore. Null on web / before first persist. */
  bounds: windowBoundsSchema.nullable().default(null),
  /** Which tab is focused in this window. Null = channels landing. */
  activeTabId: z2.string().nullable().default(null)
});
var tabsSnapshotSchema = z2.object({
  windows: z2.array(browserWindowSchema),
  tabs: z2.array(browserTabSchema)
});
var prReviewCommentUserSchema = z3.object({
  login: z3.string(),
  avatar_url: z3.string()
});
var prReviewCommentSchema = z3.object({
  id: z3.number(),
  body: z3.string(),
  path: z3.string(),
  line: z3.number().nullable(),
  original_line: z3.number().nullable(),
  side: z3.enum(["LEFT", "RIGHT"]),
  start_line: z3.number().nullable(),
  start_side: z3.enum(["LEFT", "RIGHT"]).nullable(),
  diff_hunk: z3.string(),
  in_reply_to_id: z3.number().nullish(),
  user: prReviewCommentUserSchema,
  created_at: z3.string(),
  updated_at: z3.string(),
  subject_type: z3.enum(["line", "file"]).nullable()
});
var prReviewThreadSchema = z3.object({
  nodeId: z3.string(),
  isResolved: z3.boolean(),
  rootId: z3.number(),
  filePath: z3.string(),
  comments: z3.array(prReviewCommentSchema)
});
var githubRefKindSchema = z3.enum(["issue", "pr"]);
var githubRefStateSchema = z3.enum(["OPEN", "CLOSED", "MERGED"]);
var githubRefSchema = z3.object({
  kind: githubRefKindSchema,
  number: z3.number(),
  title: z3.string(),
  state: githubRefStateSchema,
  labels: z3.array(z3.string()),
  url: z3.string(),
  repo: z3.string(),
  isDraft: z3.boolean().optional()
});
var prActionTypeSchema = z3.enum(["close", "reopen", "ready", "draft"]);
var getPrInfoByUrlInput = z3.object({ prUrl: z3.string() });
var getPrInfoByUrlOutput = z3.object({
  number: z3.number(),
  title: z3.string(),
  body: z3.string(),
  author: z3.string().nullable(),
  state: z3.string(),
  merged: z3.boolean(),
  draft: z3.boolean(),
  /** GitHub computes mergeability asynchronously; null until it settles. */
  mergeable: z3.boolean().nullable(),
  /**
   * GitHub's `mergeable_state`: "clean" | "unstable" | "blocked" | "dirty" |
   * "behind" | "draft" | "unknown". "blocked" means branch protection forbids
   * the merge for this viewer — e.g. a required approving review is missing
   * (authors can't approve their own PRs) or required checks are failing.
   * Kept as a plain string so an undocumented value can't fail the parse.
   */
  mergeStateStatus: z3.string().catch("unknown"),
  baseRefName: z3.string().nullable(),
  headRefName: z3.string().nullable(),
  additions: z3.number(),
  deletions: z3.number(),
  changedFiles: z3.number()
});
var approvePrInput = z3.object({ prUrl: z3.string() });
var approvePrOutput = z3.object({
  success: z3.boolean(),
  message: z3.string()
});
var prMergeMethodSchema = z3.enum(["merge", "squash", "rebase"]);
var mergePrInput = z3.object({
  prUrl: z3.string(),
  method: prMergeMethodSchema
});
var mergePrOutput = z3.object({
  success: z3.boolean(),
  message: z3.string()
});
var prCheckBucketSchema = z3.enum([
  "fail",
  "cancel",
  "pending",
  "pass",
  "skipping"
]);
var prCheckSchema = z3.object({
  name: z3.string(),
  bucket: prCheckBucketSchema,
  link: z3.string().nullable(),
  workflow: z3.string().nullable(),
  description: z3.string().nullable()
});
var getPrChecksInput = z3.object({ prUrl: z3.string() });
var getPrChecksOutput = z3.array(prCheckSchema).nullable();
var prConversationCommentSchema = z3.object({
  id: z3.number(),
  author: z3.string(),
  avatarUrl: z3.string().nullable(),
  body: z3.string(),
  createdAt: z3.string(),
  url: z3.string().nullable()
});
var getPrCommentsInput = z3.object({ prUrl: z3.string() });
var getPrCommentsOutput = z3.array(prConversationCommentSchema).nullable();
var TOKEN_REFRESH_BUFFER_MS = 30 * 60 * 1e3;
var TOKEN_REFRESH_FORCE_MS = 60 * 1e3;
var VIDEO_MIME_TYPES = {
  mp4: "video/mp4",
  m4v: "video/mp4",
  mov: "video/quicktime",
  webm: "video/webm",
  ogv: "video/ogg"
};
var PLAYABLE_VIDEO_EXTENSIONS = new Set(
  Object.keys(VIDEO_MIME_TYPES)
);
var ALLOWED_VIDEO_MIME_TYPES = new Set(
  Object.values(VIDEO_MIME_TYPES)
);
var MAX_VIDEO_BASE64_LENGTH = 50 * 1024 * 1024;
var workspaceModeSchema = z4.enum(["worktree", "local", "cloud", "root"]).transform((val) => val === "root" ? "local" : val);
var worktreeInfoSchema = z4.object({
  worktreePath: z4.string(),
  worktreeName: z4.string(),
  branchName: z4.string().nullable(),
  baseBranch: z4.string(),
  createdAt: z4.string(),
  output: z4.string().optional()
});
var workspaceInfoSchema = z4.object({
  taskId: z4.string(),
  mode: workspaceModeSchema,
  worktree: worktreeInfoSchema.nullable(),
  branchName: z4.string().nullable(),
  linkedBranch: z4.string().nullable()
});
var workspaceSchema = z4.object({
  taskId: z4.string(),
  folderId: z4.string(),
  folderPath: z4.string(),
  mode: workspaceModeSchema,
  worktreePath: z4.string().nullable(),
  worktreeName: z4.string().nullable(),
  branchName: z4.string().nullable(),
  baseBranch: z4.string().nullable(),
  linkedBranch: z4.string().nullable(),
  createdAt: z4.string(),
  /**
   * Synthetic workspace for a repo-less channel task: its folderPath is a
   * scratch dir, not a registered folder. Marks it so callers (e.g. the
   * navigation task binder) don't try to register it as a folder or git-init it.
   */
  isScratch: z4.boolean().optional()
});

// src/adapters/claude/image-sanitization.ts
function unprocessableImageReason(value) {
  if (!value || typeof value !== "object") return null;
  const block = value;
  if (block.type !== "image") return null;
  const data = typeof block.data === "string" ? block.data : typeof block.source?.data === "string" ? block.source.data : null;
  const mimeType = typeof block.mimeType === "string" ? block.mimeType : typeof block.source?.media_type === "string" ? block.source.media_type : null;
  if (data == null) return null;
  if (data.trim().length === 0) return "image data is empty";
  if (mimeType != null && !isClaudeImageMimeType(mimeType)) {
    return `unsupported image type ${mimeType}`;
  }
  if (estimateBase64Bytes(data) > MAX_CLAUDE_IMAGE_BYTES) {
    return "image exceeds the 5 MB per-image limit";
  }
  return null;
}
function neutralizeUnprocessableImages(value) {
  const reason = unprocessableImageReason(value);
  if (reason) {
    return {
      changed: true,
      value: {
        type: "text",
        text: `[Removed unprocessable image: ${reason}]`
      }
    };
  }
  if (Array.isArray(value)) {
    let changed = false;
    const items = value.map((item) => {
      const result2 = neutralizeUnprocessableImages(item);
      changed ||= result2.changed;
      return result2.value;
    });
    return { changed, value: changed ? items : value };
  }
  if (!value || typeof value !== "object") {
    return { changed: false, value };
  }
  const record = value;
  if (!("content" in record)) {
    return { changed: false, value };
  }
  const result = neutralizeUnprocessableImages(record.content);
  return result.changed ? { changed: true, value: { ...record, content: result.value } } : { changed: false, value };
}

// src/adapters/claude/session/models.ts
var MODELS_WITH_1M_CONTEXT = /* @__PURE__ */ new Set([
  "claude-opus-4-7",
  "claude-opus-4-8",
  "claude-sonnet-4-6",
  "claude-sonnet-5",
  "claude-fable-5"
]);
function supports1MContext(modelId) {
  return MODELS_WITH_1M_CONTEXT.has(modelId);
}

// src/adapters/claude/session/jsonl-hydration.ts
var MAX_TOOL_PAYLOAD_CHARS = 1e4;
function capToolPayload(value) {
  const text = typeof value === "string" ? value : JSON.stringify(value);
  if (typeof text !== "string" || text.length <= MAX_TOOL_PAYLOAD_CHARS) {
    return value;
  }
  const preview = `${text.slice(0, MAX_TOOL_PAYLOAD_CHARS)}\u2026 [truncated ${text.length - MAX_TOOL_PAYLOAD_CHARS} chars]`;
  return typeof value === "string" ? preview : { _truncated: true, preview, originalSize: text.length };
}
function isEmptyRecord(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).length === 0;
}
var MAX_PROJECT_KEY_LENGTH = 200;
function hashString(s) {
  let hash = 0;
  for (let i = 0; i < s.length; i++) {
    hash = (hash << 5) - hash + s.charCodeAt(i);
    hash |= 0;
  }
  return Math.abs(hash).toString(36);
}
function encodeCwdToProjectKey(cwd) {
  let projectKey = cwd.replace(/[^a-zA-Z0-9]/g, "-");
  if (projectKey.length > MAX_PROJECT_KEY_LENGTH) {
    projectKey = `${projectKey.slice(0, MAX_PROJECT_KEY_LENGTH)}-${hashString(cwd)}`;
  }
  return projectKey;
}
function getSessionJsonlPath(sessionId, cwd) {
  const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude");
  return path.join(
    configDir,
    "projects",
    encodeCwdToProjectKey(cwd),
    `${sessionId}.jsonl`
  );
}
function 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;
      switch (update.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 ? [content] : [];
          const lastTurn = turns[turns.length - 1];
          if (lastTurn?.role === "user") {
            lastTurn.content.push(...contentArray);
          } else {
            turns.push({ role: "user", content: contentArray });
          }
          break;
        }
        case "agent_message":
        case "agent_message_chunk":
        case "agent_thought_chunk": {
          const content = update.content;
          if (content && !Array.isArray(content) && !isEmptyContentBlock(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;
          const toolCallId = update.toolCallId ?? meta?.toolCallId;
          if (!toolCallId) break;
          let toolCall = currentToolCalls.find(
            (tc) => tc.toolCallId === toolCallId
          );
          if (!toolCall) {
            const toolName = meta?.toolName;
            if (!toolName) break;
            toolCall = { toolCallId, toolName, input: {} };
            currentToolCalls.push(toolCall);
          }
          const input = update.rawInput ?? meta?.toolInput;
          if (input !== void 0 && !isEmptyRecord(input)) {
            toolCall.input = capToolPayload(input);
          }
          const result = update.rawOutput ?? meta?.toolResponse;
          if (result !== void 0) {
            toolCall.result = capToolPayload(result);
          }
          break;
        }
        case "tool_result": {
          const meta = update._meta?.claudeCode;
          const toolCallId = update.toolCallId ?? meta?.toolCallId;
          if (toolCallId) {
            const toolCall = currentToolCalls.find(
              (tc) => tc.toolCallId === toolCallId
            );
            const result = update.rawOutput ?? meta?.toolResponse;
            if (toolCall && result !== void 0) {
              toolCall.result = capToolPayload(result);
            }
          }
          break;
        }
      }
    }
  }
  if (currentAssistantContent.length > 0 || currentToolCalls.length > 0) {
    turns.push({
      role: "assistant",
      content: currentAssistantContent,
      toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0
    });
  }
  return turns;
}
var CHARS_PER_TOKEN = 3;
var DEFAULT_MAX_TOKENS = 8e4;
var LARGE_CONTEXT_MAX_TOKENS = 4e5;
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;
  }
  if (startIndex === turns.length && turns.length > 0) {
    return selectOversizedTailFallback(turns, maxTokens);
  }
  while (startIndex < turns.length && turns[startIndex].role !== "user") {
    startIndex++;
  }
  return turns.slice(startIndex);
}
function selectOversizedTailFallback(turns, maxTokens) {
  const last = turns[turns.length - 1];
  let userIndex = turns.length - 1;
  while (userIndex >= 0 && turns[userIndex].role !== "user") {
    userIndex--;
  }
  const selected = [];
  let budget = maxTokens;
  if (userIndex >= 0) {
    selected.push(turns[userIndex]);
    budget -= estimateTurnTokens(turns[userIndex]);
  }
  if (userIndex !== turns.length - 1) {
    selected.push(dropOldestToolCalls(last, Math.max(budget, 0)));
  }
  return selected;
}
function dropOldestToolCalls(turn, budget) {
  if (!turn.toolCalls?.length) return turn;
  const toolCalls = [...turn.toolCalls];
  const trimmed = { ...turn, toolCalls };
  while (toolCalls.length > 0 && estimateTurnTokens(trimmed) > budget) {
    toolCalls.shift();
  }
  if (toolCalls.length === 0) {
    trimmed.toolCalls = void 0;
  }
  return trimmed;
}
var BASE62 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
function generateMessageId() {
  const bytes = new Uint8Array(24);
  crypto.getRandomValues(bytes);
  let id = "msg_01";
  for (const b of bytes) {
    id += BASE62[b % 62];
  }
  return id;
}
var ADJECTIVES = [
  "bright",
  "calm",
  "daring",
  "eager",
  "fair",
  "gentle",
  "happy",
  "keen",
  "lively",
  "merry",
  "noble",
  "polite",
  "quick",
  "sharp",
  "warm",
  "witty"
];
var VERBS = [
  "blazing",
  "crafting",
  "dashing",
  "flowing",
  "gliding",
  "humming",
  "jumping",
  "linking",
  "melting",
  "nesting",
  "pacing",
  "roaming",
  "sailing",
  "turning",
  "waving",
  "zoning"
];
var NOUNS = [
  "aurora",
  "breeze",
  "cedar",
  "delta",
  "ember",
  "frost",
  "grove",
  "haven",
  "inlet",
  "jewel",
  "knoll",
  "lotus",
  "maple",
  "nexus",
  "oasis",
  "prism"
];
function generateSlug() {
  const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
  return `${pick(ADJECTIVES)}-${pick(VERBS)}-${pick(NOUNS)}`;
}
function conversationTurnsToJsonlEntries(turns, config) {
  const lines = [];
  let parentUuid = null;
  const model = config.model ?? DEFAULT_GATEWAY_MODEL;
  const version = config.version ?? "2.1.63";
  const gitBranch = config.gitBranch ?? "";
  const slug = config.slug ?? generateSlug();
  const permissionMode = config.permissionMode ?? "default";
  const baseTime = Date.now() - turns.length * 3e3;
  let turnIndex = 0;
  for (const turn of turns) {
    const timestamp = new Date(baseTime + turnIndex * 3e3).toISOString();
    turnIndex++;
    if (turn.role === "user") {
      lines.push(
        JSON.stringify({
          type: "queue-operation",
          operation: "enqueue",
          timestamp,
          sessionId: config.sessionId
        })
      );
      lines.push(
        JSON.stringify({
          type: "queue-operation",
          operation: "dequeue",
          timestamp,
          sessionId: config.sessionId
        })
      );
      const uuid = randomUUID();
      const textParts = turn.content.filter(
        (block) => "text" in block && typeof block.text === "string" && block.text
      ).map((block) => block.text);
      const userText = textParts.length > 0 ? textParts.join("") : " ";
      lines.push(
        JSON.stringify({
          parentUuid,
          isSidechain: false,
          userType: "external",
          cwd: config.cwd,
          sessionId: config.sessionId,
          version,
          gitBranch,
          slug,
          type: "user",
          message: {
            role: "user",
            content: [{ type: "text", text: userText }]
          },
          uuid,
          timestamp,
          permissionMode
        })
      );
      parentUuid = uuid;
    } else {
      const allBlocks = [];
      for (const block of turn.content) {
        const blockType = block.type;
        if ((blockType === "thinking" || blockType === "text") && !isEmptyContentBlock(block)) {
          allBlocks.push(block);
        }
      }
      if (turn.toolCalls) {
        for (const tc of turn.toolCalls) {
          allBlocks.push({
            type: "tool_use",
            id: tc.toolCallId,
            name: tc.toolName,
            // undefined would be dropped on stringify; the API requires input
            input: tc.input ?? {}
          });
        }
      }
      const msgId = generateMessageId();
      const hasToolUse = allBlocks.some(
        (b) => b.type === "tool_use"
      );
      const lastStopReason = hasToolUse ? "tool_use" : "end_turn";
      for (let i = 0; i < allBlocks.length; i++) {
        const block = allBlocks[i];
        const isLast = i === allBlocks.length - 1;
        const uuid = randomUUID();
        lines.push(
          JSON.stringify({
            parentUuid,
            isSidechain: false,
            userType: "external",
            cwd: config.cwd,
            sessionId: config.sessionId,
            version,
            gitBranch,
            slug,
            type: "assistant",
            message: {
              model,
              id: msgId,
              type: "message",
              role: "assistant",
              content: [block],
              stop_reason: isLast ? lastStopReason : null,
              stop_sequence: null,
              usage: {
                input_tokens: 0,
                cache_creation_input_tokens: 0,
                cache_read_input_tokens: 0,
                output_tokens: 0
              }
            },
            uuid,
            timestamp
          })
        );
        parentUuid = uuid;
      }
      if (turn.toolCalls) {
        for (const tc of turn.toolCalls) {
          if (tc.result === void 0) continue;
          const uuid = randomUUID();
          const resultText = typeof tc.result === "string" ? tc.result : JSON.stringify(tc.result);
          lines.push(
            JSON.stringify({
              parentUuid,
              isSidechain: false,
              userType: "external",
              cwd: config.cwd,
              sessionId: config.sessionId,
              version,
              gitBranch,
              slug,
              type: "user",
              message: {
                role: "user",
                content: [
                  {
                    type: "tool_result",
                    tool_use_id: tc.toolCallId,
                    content: resultText
                  }
                ]
              },
              uuid,
              timestamp
            })
          );
          parentUuid = uuid;
        }
      }
    }
  }
  return lines;
}
async function sanitizeSessionJsonl(jsonlPath) {
  let raw;
  let statBefore;
  try {
    statBefore = await fs.stat(jsonlPath);
    raw = await fs.readFile(jsonlPath, "utf8");
  } catch {
    return false;
  }
  let changed = false;
  const sanitized = raw.split("\n").map((line) => {
    if (!line.trim()) return line;
    let parsed;
    try {
      parsed = JSON.parse(line);
    } catch {
      return line;
    }
    const message = parsed.message;
    if (!message || !Array.isArray(message.content)) return line;
    let lineChanged = false;
    const kept = message.content.filter((block) => !isEmptyContentBlock(block));
    if (kept.length !== message.content.length) {
      lineChanged = true;
      message.content = kept.length > 0 ? kept : [{ type: "text", text: " " }];
    }
    for (const block of message.content) {
      if (block?.type === "tool_use" && block.input == null) {
        block.input = {};
        lineChanged = true;
      }
    }
    const imageResult = neutralizeUnprocessableImages(message.content);
    if (imageResult.changed) {
      message.content = imageResult.value;
      lineChanged = true;
    }
    if (!lineChanged) return line;
    changed = true;
    return JSON.stringify(parsed);
  });
  if (!changed) return false;
  const tmpPath = `${jsonlPath}.tmp.${Date.now()}`;
  let renamed = false;
  try {
    await fs.writeFile(tmpPath, sanitized.join("\n"));
    const statNow = await fs.stat(jsonlPath);
    if (statNow.mtimeMs !== statBefore.mtimeMs || statNow.size !== statBefore.size) {
      return false;
    }
    await fs.rename(tmpPath, jsonlPath);
    renamed = true;
    return true;
  } finally {
    if (!renamed) {
      await fs.unlink(tmpPath).catch(() => {
      });
    }
  }
}
async function hydrateSessionJsonl(params) {
  const { posthogAPI, log } = params;
  try {
    const jsonlPath = getSessionJsonlPath(params.sessionId, params.cwd);
    try {
      await fs.access(jsonlPath);
      try {
        if (await sanitizeSessionJsonl(jsonlPath)) {
          log.info(
            "Healed existing session JSONL (empty and/or unprocessable-image blocks)",
            { jsonlPath }
          );
        }
      } catch (err) {
        log.warn("Failed to sanitize existing session JSONL", {
          jsonlPath,
          error: err instanceof Error ? err.message : String(err)
        });
      }
      return true;
    } catch {
    }
    const taskRun = await posthogAPI.getTaskRun(params.taskId, params.runId);
    if (!taskRun.log_url) {
      log.info("No log URL, skipping JSONL hydration");
      return false;
    }
    const entries = await posthogAPI.fetchTaskRunLogs(taskRun);
    if (entries.length === 0) {
      log.info("No S3 log entries, skipping JSONL hydration");
      return false;
    }
    const entryCounts = {};
    for (const entry of entries) {
      const method = entry.notification?.method ?? "unknown";
      const entryParams = entry.notification?.params;
      const update = entryParams?.update;
      const key = update?.sessionUpdate ? `${method}:${update.sessionUpdate}` : method;
      entryCounts[key] = (entryCounts[key] ?? 0) + 1;
    }
    log.info("S3 log entry breakdown", {
      totalEntries: entries.length,
      types: entryCounts
    });
    const allTurns = rebuildConversation(entries);
    if (allTurns.length === 0) {
      log.info("No conversation to hydrate, skipping JSONL hydration");
      return false;
    }
    const maxTokens = supports1MContext(params.model ?? "") ? LARGE_CONTEXT_MAX_TOKENS : DEFAULT_MAX_TOKENS;
    const conversation = selectRecentTurns(allTurns, maxTokens);
    log.info("Selected recent turns for hydration", {
      totalTurns: allTurns.length,
      selectedTurns: conversation.length,
      turnRoles: conversation.map((t) => t.role)
    });
    const jsonlLines = conversationTurnsToJsonlEntries(conversation, {
      sessionId: params.sessionId,
      cwd: params.cwd,
      model: params.model,
      gitBranch: params.gitBranch,
      permissionMode: params.permissionMode
    });
    await fs.mkdir(path.dirname(jsonlPath), { recursive: true });
    const tmpPath = `${jsonlPath}.tmp.${Date.now()}`;
    await fs.writeFile(tmpPath, `${jsonlLines.join("\n")}
`);
    await fs.rename(tmpPath, jsonlPath);
    log.info("Hydrated session JSONL from S3", {
      sessionId: params.sessionId,
      turns: conversation.length,
      lines: jsonlLines.length
    });
    return true;
  } catch (err) {
    log.warn("Failed to hydrate session JSONL, continuing", {
      sessionId: params.sessionId,
      error: err instanceof Error ? err.message : String(err)
    });
    return false;
  }
}
export {
  conversationTurnsToJsonlEntries,
  encodeCwdToProjectKey,
  getSessionJsonlPath,
  hydrateSessionJsonl,
  rebuildConversation,
  sanitizeSessionJsonl,
  selectRecentTurns
};
//# sourceMappingURL=jsonl-hydration.js.map