import {
  persistedPartToWorkProduct
} from "./chunk-V3HO43PG.js";
import {
  persistedPartToInteraction
} from "./chunk-3ZK5IJSW.js";
import {
  persistedPartToPlan,
  planToPersistedPart
} from "./chunk-YJMCRXQQ.js";

// src/chat-routes/wire.ts
function chatTurnRequestInit(payload) {
  return {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload)
  };
}
var INLINE_PARTS_MAX_BYTES = 95e4;
var DISPATCH_REQUEST_MAX_BYTES = 1024 * 1024;
var DISPATCH_STRUCTURAL_RESERVE_BYTES = 64 * 1024;
var DISPATCH_MAX_PARTS = 64;
var DISPATCH_MAX_MEDIA_PARTS = 24;
function base64WireLen(byteLen) {
  return Math.ceil(byteLen / 3) * 4;
}
function formatBytes(bytes) {
  if (bytes < 1024) return `${bytes}B`;
  if (bytes >= 1024 * 1024) {
    const megabytes = Math.floor(bytes / (1024 * 1024));
    const remainder = bytes % (1024 * 1024);
    return remainder === 0 ? `${megabytes}MB` : `${megabytes}MB ${formatBytes(remainder)}`;
  }
  return `${Math.round(bytes / 1024)}KB`;
}
var ChatTurnInputError = class extends Error {
  constructor(message, status = 400, code = "INVALID_CHAT_TURN") {
    super(message);
    this.status = status;
    this.code = code;
    this.name = "ChatTurnInputError";
  }
  status;
  code;
};
function partByteSize(part) {
  let bytes = 0;
  if (part.type === "text") return part.text.length;
  if (part.url) bytes += part.url.length;
  if (part.content) bytes += part.content.length;
  if (part.path) bytes += part.path.length;
  return bytes;
}
function promptPartsByteSize(parts) {
  return parts.reduce((total, part) => total + partByteSize(part), 0);
}
function assertPromptPartsWithinCap(parts, maxBytes = INLINE_PARTS_MAX_BYTES) {
  const total = promptPartsByteSize(parts);
  if (total <= maxBytes) return;
  const largest = [...parts].sort((a, b) => partByteSize(b) - partByteSize(a))[0];
  const largestName = largest && largest.type !== "text" ? largest.filename ?? largest.path ?? largest.type : "text";
  throw new ChatTurnInputError(
    `Inline prompt parts total ${total}B, over the ${maxBytes}B budget (largest: ${largestName}, ${largest ? partByteSize(largest) : 0}B). Upload large files through the upload route so they travel as sandbox path references.`,
    413,
    "PROMPT_PARTS_TOO_LARGE"
  );
}
var MENTION_IMAGE_MEDIA_TYPES = /* @__PURE__ */ new Map([
  [".png", "image/png"],
  [".jpg", "image/jpeg"],
  [".jpeg", "image/jpeg"],
  [".gif", "image/gif"],
  [".webp", "image/webp"],
  [".svg", "image/svg+xml"],
  [".bmp", "image/bmp"],
  [".heic", "image/heic"],
  [".heif", "image/heif"],
  [".avif", "image/avif"]
]);
function extensionOf(path) {
  const base = path.split("/").filter(Boolean).pop() ?? path;
  const dot = base.lastIndexOf(".");
  return dot > 0 ? base.slice(dot).toLowerCase() : "";
}
function mediaTypeForMentionPath(path) {
  return MENTION_IMAGE_MEDIA_TYPES.get(extensionOf(path));
}
function mentionKindForPath(path) {
  return mediaTypeForMentionPath(path) ? "image" : "file";
}
function fileMentionsToParts(mentions, opts = {}) {
  const resolvePath = opts.resolvePath ?? ((path) => path);
  return mentions.map((mention) => {
    const mediaType = mediaTypeForMentionPath(mention.path);
    const part = {
      type: mediaType ? "image" : "file",
      filename: mention.name,
      path: resolvePath(mention.path)
    };
    if (mediaType) part.mediaType = mediaType;
    return part;
  });
}
function buildMentionPromptBlock(mentions) {
  if (mentions.length === 0) return "";
  const lines = mentions.map((m) => `- ${m.name} (${m.path})`);
  return `

Mentioned files \u2014 read them from these paths:
${lines.join("\n")}`;
}
var MENTION_MAX_COUNT = 16;
var MAX_MENTION_NAME_LENGTH = 256;
var MAX_MENTION_PATH_LENGTH = 1024;
function validateSandboxMentionPath(path) {
  if (typeof path !== "string" || path.length === 0) {
    return { succeeded: false, error: "mention path must be a non-empty string" };
  }
  if (path.length > MAX_MENTION_PATH_LENGTH) {
    return { succeeded: false, error: `mention path must not exceed ${MAX_MENTION_PATH_LENGTH} characters` };
  }
  if (path.includes("\0")) {
    return { succeeded: false, error: "mention path must not contain null bytes" };
  }
  if (path.includes("\\")) {
    return { succeeded: false, error: "mention path must not contain backslashes" };
  }
  if (path.startsWith("/")) {
    return { succeeded: false, error: "mention path must be workspace-relative, not absolute" };
  }
  if (path.split("/").some((segment) => segment === "..")) {
    return { succeeded: false, error: 'mention path must not contain ".." segments' };
  }
  return { succeeded: true };
}
function parseFileMention(value, index) {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new ChatTurnInputError(`mentions[${index}] must be an object`);
  }
  const record = value;
  const pathCheck = validateSandboxMentionPath(record.path);
  if (!pathCheck.succeeded) throw new ChatTurnInputError(`mentions[${index}]: ${pathCheck.error}`);
  const name = record.name;
  if (typeof name !== "string" || !name.trim()) {
    throw new ChatTurnInputError(`mentions[${index}].name must be a non-empty string`);
  }
  if (name.length > MAX_MENTION_NAME_LENGTH) {
    throw new ChatTurnInputError(`mentions[${index}].name must not exceed ${MAX_MENTION_NAME_LENGTH} characters`);
  }
  const size = record.size;
  if (size !== void 0) {
    if (typeof size !== "number" || !Number.isFinite(size)) {
      throw new ChatTurnInputError(`mentions[${index}].size must be a finite number`);
    }
    if (size < 0) {
      throw new ChatTurnInputError(`mentions[${index}].size must not be negative`);
    }
  }
  return { path: record.path, name, ...typeof size === "number" ? { size } : {} };
}
function parseFileMentions(raw) {
  if (raw === void 0 || raw === null) return [];
  if (!Array.isArray(raw)) throw new ChatTurnInputError("mentions must be an array");
  if (raw.length > MENTION_MAX_COUNT) {
    throw new ChatTurnInputError(`mentions must not exceed ${MENTION_MAX_COUNT} entries`);
  }
  const mentions = [];
  const seenPaths = /* @__PURE__ */ new Set();
  for (let index = 0; index < raw.length; index += 1) {
    const mention = parseFileMention(raw[index], index);
    if (seenPaths.has(mention.path)) continue;
    seenPaths.add(mention.path);
    mentions.push(mention);
  }
  return mentions;
}
function parseChatTurnParts(raw) {
  if (raw === void 0 || raw === null) return [];
  if (!Array.isArray(raw)) throw new ChatTurnInputError("parts must be an array");
  return raw.map((entry, index) => {
    const part = entry;
    if (!part || typeof part !== "object") {
      throw new ChatTurnInputError(`parts[${index}] must be an object`);
    }
    if (part.type !== "image" && part.type !== "file") {
      throw new ChatTurnInputError(`parts[${index}].type must be 'image' or 'file'`);
    }
    for (const key of ["filename", "mediaType", "url", "path", "content"]) {
      if (part[key] !== void 0 && typeof part[key] !== "string") {
        throw new ChatTurnInputError(`parts[${index}].${key} must be a string`);
      }
    }
    if (!part.url && !part.path && !part.content) {
      throw new ChatTurnInputError(`parts[${index}] needs a url, path, or content`);
    }
    return {
      type: part.type,
      ...part.filename !== void 0 ? { filename: part.filename } : {},
      ...part.mediaType !== void 0 ? { mediaType: part.mediaType } : {},
      ...part.url !== void 0 ? { url: part.url } : {},
      ...part.path !== void 0 ? { path: part.path } : {},
      ...part.content !== void 0 ? { content: part.content } : {}
    };
  });
}

// src/chat-store/parts.ts
function toChatMessageParts(parts) {
  const out = [];
  for (const part of parts) {
    const typed = toChatMessagePart(part);
    if (typed) out.push(typed);
  }
  return out;
}
var str = (value) => typeof value === "string";
function toChatMessagePart(part) {
  if (!part || typeof part !== "object") return null;
  const type = part.type;
  switch (type) {
    case "text":
    case "reasoning":
      return str(part.text) ? part : null;
    case "tool":
      return str(part.id) && str(part.tool) && part.state && typeof part.state === "object" ? part : null;
    case "file":
    case "image":
      return part;
    case "subtask":
      return str(part.prompt) && str(part.description) && str(part.agent) ? part : null;
    case "step-start":
      return { type: "step-start" };
    case "step-finish":
      return part;
    case "interaction":
      return persistedPartToInteraction(part) ? part : null;
    case "notice":
      return str(part.id) && str(part.noticeKind) && str(part.text) ? part : null;
    case "plan": {
      const plan = persistedPartToPlan(part);
      return plan ? { ...part, ...planToPersistedPart(plan) } : null;
    }
    case "work_product":
      return persistedPartToWorkProduct(part) ? part : null;
    case "mention":
      return isChatMentionPart(part) ? part : null;
    case void 0:
      return null;
    default: {
      const _exhaustive = type;
      void _exhaustive;
      return null;
    }
  }
}
function isChatToolPart(part) {
  return part.type === "tool";
}
function isChatTextPart(part) {
  return part.type === "text";
}
function isChatInteractionPart(part) {
  return part.type === "interaction";
}
function isChatPlanPart(part) {
  return part.type === "plan";
}
function isChatWorkProductPart(part) {
  return part.type === "work_product";
}
function isChatStepFinishPart(part) {
  return part.type === "step-finish";
}
function isChatMentionPart(part) {
  if (!part || typeof part !== "object") return false;
  const record = part;
  if (record.size !== void 0 && (typeof record.size !== "number" || !Number.isFinite(record.size))) {
    return false;
  }
  return record.type === "mention" && typeof record.path === "string" && record.path.length > 0 && typeof record.name === "string" && record.name.trim().length > 0 && (record.mentionKind === "image" || record.mentionKind === "file");
}
function mentionPartsFromMessageParts(parts) {
  if (!parts) return [];
  return parts.filter(isChatMentionPart);
}
function mentionInputToPart(input) {
  const part = {
    type: "mention",
    mentionKind: mentionKindForPath(input.path),
    path: input.path,
    name: input.name
  };
  if (typeof input.size === "number" && Number.isFinite(input.size)) part.size = input.size;
  return part;
}
function attachmentKindForMime(mime) {
  return mime?.startsWith("image/") ? "image" : "file";
}
function isChatAttachmentPart(part) {
  if (!part || typeof part !== "object") return false;
  const record = part;
  return (record.type === "image" || record.type === "file") && typeof record.path === "string" && record.path.length > 0;
}
function attachmentInputToPart(input) {
  const part = { type: input.kind, path: input.path, name: input.name };
  if (typeof input.size === "number" && Number.isFinite(input.size)) part.size = input.size;
  if (input.mediaType) part.mediaType = input.mediaType;
  return part;
}
function attachmentPartsFromMessageParts(parts) {
  if (!parts) return [];
  return parts.filter(isChatAttachmentPart);
}
var DEFAULT_ATTACHMENT_PROMPT_HEADER = "Attached files (already saved to the workspace vault \u2014 read them from these paths):";
var PROMPT_BLOCK_CONTROL_CHARS = /[\x00-\x1F\x7F]/g;
function buildAttachmentPromptBlock(atts, header = DEFAULT_ATTACHMENT_PROMPT_HEADER) {
  if (atts.length === 0) return "";
  const clean = (value) => value.replace(PROMPT_BLOCK_CONTROL_CHARS, " ");
  const lines = atts.map((a) => `- ${clean(a.name)} (vault: ${clean(a.path)})`);
  return `

${header}
${lines.join("\n")}`;
}
function historyContentWithAttachments(message, header) {
  const attachmentParts = attachmentPartsFromMessageParts(message.parts);
  if (attachmentParts.length === 0) return message.content;
  return `${message.content}${buildAttachmentPromptBlock(attachmentParts, header)}`;
}

export {
  chatTurnRequestInit,
  INLINE_PARTS_MAX_BYTES,
  DISPATCH_REQUEST_MAX_BYTES,
  DISPATCH_STRUCTURAL_RESERVE_BYTES,
  DISPATCH_MAX_PARTS,
  DISPATCH_MAX_MEDIA_PARTS,
  base64WireLen,
  formatBytes,
  ChatTurnInputError,
  promptPartsByteSize,
  assertPromptPartsWithinCap,
  mediaTypeForMentionPath,
  mentionKindForPath,
  fileMentionsToParts,
  buildMentionPromptBlock,
  MENTION_MAX_COUNT,
  validateSandboxMentionPath,
  parseFileMentions,
  parseChatTurnParts,
  toChatMessageParts,
  isChatToolPart,
  isChatTextPart,
  isChatInteractionPart,
  isChatPlanPart,
  isChatWorkProductPart,
  isChatStepFinishPart,
  isChatMentionPart,
  mentionPartsFromMessageParts,
  mentionInputToPart,
  attachmentKindForMime,
  isChatAttachmentPart,
  attachmentInputToPart,
  attachmentPartsFromMessageParts,
  DEFAULT_ATTACHMENT_PROMPT_HEADER,
  buildAttachmentPromptBlock,
  historyContentWithAttachments
};
//# sourceMappingURL=chunk-FEUW4G2L.js.map