{"version":3,"file":"index.cjs","names":["resizeImage","formatDimensionNote","resolveReadInputPath","readFile","displayPath","applyImageLimits","imageLimits","isWritableFile","decodeUtf8","splitLines","formatFileHeader","computeFileTag","formatTaggedLine","DEFAULT_MAX_BYTES","truncateLine","truncateHead","formatSize","ReadParamsSchema"],"sources":["../../../../src/services/readTool/index.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\n\nimport type {\n  DoomHeadlessExecutionContext,\n  DoomHeadlessTool,\n  DoomHeadlessToolResult,\n} from '@agimon-ai/doompi-core/headless';\nimport { formatFileHeader, formatTaggedLine, splitLines } from '@agimon-ai/doompi-hashline';\nimport {\n  computeFileTag,\n  decodeUtf8,\n  displayPath,\n  isWritableFile,\n  resolveReadInputPath,\n} from '@agimon-ai/doompi-hashline/files';\nimport {\n  DEFAULT_MAX_BYTES,\n  formatDimensionNote,\n  formatSize,\n  resizeImage,\n  truncateHead,\n  truncateLine,\n  type ReadToolDetails,\n} from '@earendil-works/pi-coding-agent';\n\nimport { ReadParamsSchema, type ReadParams } from '../../schemas/readTool';\nimport {\n  applyImageLimits,\n  imageLimits,\n  type ReadContentPart,\n  type ReadImageResizeOperations,\n  type ReadTextPart,\n} from '../readImage';\n\nconst resizeOperations: ReadImageResizeOperations = {\n  resize: resizeImage,\n  formatDimensionNote: (result) => formatDimensionNote(result),\n};\n\nexport interface ReadToolResult {\n  content: ReadContentPart[];\n  details?: ReadToolDetails;\n}\n\ninterface ReadTextToolResult {\n  content: ReadTextPart[];\n  details?: ReadToolDetails;\n}\n\nexport function assertNotAborted(signal: AbortSignal | undefined): void {\n  if (signal?.aborted) throw new Error('Operation aborted');\n}\n\nexport function isImagePath(filePath: string): string | undefined {\n  const extension = filePath.toLowerCase().match(/\\.([a-z0-9]+)$/u)?.[1];\n  if (!extension) return undefined;\n  const mimeTypes: Record<string, string> = {\n    avif: 'image/avif',\n    gif: 'image/gif',\n    jpeg: 'image/jpeg',\n    jpg: 'image/jpeg',\n    png: 'image/png',\n    webp: 'image/webp',\n  };\n  return mimeTypes[extension];\n}\n\nexport async function executeHeadlessRead(\n  params: ReadParams,\n  cwd: string,\n  signal: AbortSignal | undefined,\n): Promise<ReadToolResult> {\n  assertNotAborted(signal);\n  const absolutePath = await resolveReadInputPath(params.path, cwd);\n  const bytes = await readFile(absolutePath);\n  assertNotAborted(signal);\n  const mimeType = isImagePath(absolutePath);\n  if (mimeType) {\n    const content: ReadContentPart[] = [\n      { type: 'text', text: `Read image file ${displayPath(absolutePath, cwd)} [${mimeType}]` },\n      { type: 'image', data: bytes.toString('base64'), mimeType },\n    ];\n    return { content: await applyImageLimits(content, imageLimits(), resizeOperations) };\n  }\n  if (!(await isWritableFile(absolutePath))) {\n    return { content: [{ type: 'text', text: decodeUtf8(bytes, displayPath(absolutePath, cwd)) }] };\n  }\n  return createTaggedReadResult(bytes, displayPath(absolutePath, cwd), params);\n}\n\nexport function createTaggedReadResult(bytes: Buffer, path: string, params: ReadParams): ReadTextToolResult {\n  const lines = splitLines(decodeUtf8(bytes, path));\n  const startIndex = params.offset === undefined ? 0 : params.offset - 1;\n  if (startIndex >= lines.length) {\n    throw new Error(`Offset ${params.offset} is beyond end of file (${lines.length} lines total).`);\n  }\n\n  const endIndex = params.limit === undefined ? lines.length : Math.min(lines.length, startIndex + params.limit);\n  const selected = lines.slice(startIndex, endIndex);\n  const header = formatFileHeader(path, computeFileTag(bytes));\n  const compactedLines: number[] = [];\n  const headerBytes = Buffer.byteLength(header, 'utf8') + 1;\n  const tagged = selected.map((line, index) => {\n    const lineNumber = startIndex + index + 1;\n    const full = formatTaggedLine(line, lineNumber);\n    if (Buffer.byteLength(full, 'utf8') <= DEFAULT_MAX_BYTES - headerBytes) return full;\n    compactedLines.push(lineNumber);\n    return formatTaggedLine(truncateLine(line).text, lineNumber, '', line);\n  });\n  const truncation = truncateHead([header, ...tagged].join('\\n'));\n  let text = truncation.content;\n  let details: ReadToolDetails | undefined;\n\n  if (truncation.truncated) {\n    const shownLines = Math.max(0, truncation.outputLines - 1);\n    const nextOffset = startIndex + shownLines + 1;\n    const reason = truncation.truncatedBy === 'bytes' ? `, ${formatSize(DEFAULT_MAX_BYTES)} limit` : '';\n    text += `\\n\\n[Showing ${shownLines} anchored lines${reason}. Use offset=${nextOffset} to continue.]`;\n    details = { truncation };\n  } else {\n    const notices: string[] = [];\n    if (compactedLines.length > 0) {\n      notices.push(`Lines ${compactedLines.join(', ')} shown compactly. Their anchors hash the full original lines`);\n    }\n    if (endIndex < lines.length) {\n      notices.push(`${lines.length - endIndex} more lines in file. Use offset=${endIndex + 1} to continue`);\n    }\n    if (notices.length > 0) text += `\\n\\n[${notices.join('. ')}.]`;\n  }\n\n  return { content: [{ type: 'text', text }], details };\n}\n\nexport function isImageRead(content: readonly { readonly type: string; readonly text?: string }[]): boolean {\n  return content.some((part) => part.type === 'image' || part.text?.startsWith('Read image file') === true);\n}\n\nexport function createHeadlessReadTool(): DoomHeadlessTool<typeof ReadParamsSchema> {\n  return {\n    name: 'read',\n    label: 'read',\n    description:\n      'Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images return native-compatible content. Text is truncated to 50.0KB.',\n    promptSnippet: 'Read file contents with snapshot-bound line anchors',\n    promptGuidelines: [\n      'Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.',\n      'Continue large reads with offset until the required anchored lines are visible.',\n    ],\n    parameters: ReadParamsSchema,\n    executionMode: 'parallel',\n    execute: async (\n      _toolCallId: string,\n      params: ReadParams,\n      signal: AbortSignal | undefined,\n      _onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n      context: DoomHeadlessExecutionContext,\n    ) => executeHeadlessRead(params, context.cwd, signal),\n  };\n}\n"],"mappings":"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"}