// src/hooks/results.ts
function hookNext(updatedInput, opts) {
  return {
    type: "next",
    ...updatedInput !== undefined ? { updatedInput } : {},
    ...opts?.updateContextWindow ? { updateContextWindow: true } : {},
    ...opts?.notifyModel ? { notifyModel: true } : {}
  };
}
function hookDeny(reason) {
  return { type: "deny", ...reason !== undefined ? { reason } : {} };
}
function hookAsk(approval) {
  return { type: "ask", approval };
}
function hookToolResult(output, opts) {
  return { type: "toolResult", output, isError: opts?.isError ?? false };
}
function hookStop(options) {
  return { type: "stop", ...options };
}
function hookDone(mutatedResult) {
  return { type: "done", ...mutatedResult !== undefined ? { mutatedResult } : {} };
}

// src/hooks/approval.ts
async function runApprovalHooks(hooks, baseCtx) {
  for (const hook of hooks) {
    const ctx = {
      toolName: baseCtx.toolName,
      toolCallId: baseCtx.toolCallId,
      input: baseCtx.input,
      tool: baseCtx.tool,
      getContextWindow: baseCtx.getContextWindow,
      next() {
        return hookNext();
      },
      deny(reason) {
        return hookDeny(reason);
      },
      ask(data) {
        const approval = {
          id: data.id ?? baseCtx.toolCallId,
          toolName: baseCtx.toolName,
          toolCallId: baseCtx.toolCallId,
          input: baseCtx.input,
          ...data.metadata !== undefined ? { metadata: data.metadata } : {},
          ...data.message !== undefined ? { message: data.message } : {}
        };
        return hookAsk(approval);
      }
    };
    const result = await hook(ctx);
    if (result.type === "next") {
      continue;
    }
    return result;
  }
  return hookNext();
}
// src/hooks/deduplicate-reads.ts
function deduplicateReads(opts) {
  const persist = opts?.persist ?? false;
  const readToolName = "read";
  return (ctx) => {
    const lastReadByFile = new Map;
    const toolCallToFile = new Map;
    for (const msg of ctx.messages) {
      if (msg.role !== "assistant" || !Array.isArray(msg.content))
        continue;
      for (const part of msg.content) {
        if (part.type !== "tool-call" || part.toolName !== readToolName)
          continue;
        const args = part.args ?? part.input;
        const filePath = args?.filePath ?? args?.file_path ?? args?.path;
        if (filePath) {
          lastReadByFile.set(filePath, part.toolCallId);
          toolCallToFile.set(part.toolCallId, filePath);
        }
      }
    }
    const staleToolCallIds = new Set;
    const seenByFile = new Map;
    for (const msg of ctx.messages) {
      if (msg.role !== "assistant" || !Array.isArray(msg.content))
        continue;
      for (const part of msg.content) {
        if (part.type !== "tool-call" || part.toolName !== readToolName)
          continue;
        const filePath = toolCallToFile.get(part.toolCallId);
        if (!filePath)
          continue;
        const existing = seenByFile.get(filePath) ?? [];
        existing.push(part.toolCallId);
        seenByFile.set(filePath, existing);
      }
    }
    for (const [_filePath, toolCallIds] of seenByFile) {
      if (toolCallIds.length <= 1)
        continue;
      const lastId = lastReadByFile.get(_filePath);
      for (const id of toolCallIds) {
        if (id !== lastId)
          staleToolCallIds.add(id);
      }
    }
    if (staleToolCallIds.size === 0)
      return ctx.next();
    const transformed = ctx.messages.map((msg) => {
      if (msg.role !== "tool" || !Array.isArray(msg.content))
        return msg;
      let changed = false;
      const newContent = msg.content.map((part) => {
        if (part.type !== "tool-result")
          return part;
        if (!staleToolCallIds.has(part.toolCallId))
          return part;
        changed = true;
        const filePath = toolCallToFile.get(part.toolCallId) ?? "unknown";
        const placeholder = `[File ${filePath} was read again later — see most recent read.]`;
        return {
          ...part,
          output: typeof part.output === "string" ? placeholder : { type: "text", value: placeholder }
        };
      });
      if (!changed)
        return msg;
      return { ...msg, content: newContent };
    });
    return ctx.transform(transformed, { persist });
  };
}
// src/interfaces/read.ts
import { z as z2 } from "zod";

// src/define-tool.ts
import { z } from "zod";
function defineTool(config) {
  return config;
}
function defineToolInterface(config) {
  const outputSchema = config.output ?? z.string();
  return {
    ...config,
    output: outputSchema,
    define(executor, overrides) {
      return defineTool({
        name: config.name,
        description: overrides?.description ?? config.description,
        input: config.input,
        output: outputSchema,
        ...config.stateKey ? { stateKey: config.stateKey, stateSchema: config.stateSchema } : {},
        serialize: config.serialize ? (raw, input) => config.serialize(raw, input, {
          getContextWindow: () => [],
          updateContextWindow: () => {},
          signal: new AbortController().signal,
          stop: (opts) => ({ type: "stop", ...opts }),
          getContextWindowTokens: () => 0,
          getContextWindowLimit: () => {
            return;
          }
        }) : undefined,
        execute: async (input, ctx) => {
          const transformedInput = config.beforeExecutionTransform ? config.beforeExecutionTransform(input, ctx) : input;
          const raw = await executor(transformedInput, ctx);
          return raw;
        }
      });
    }
  };
}

// src/interfaces/read.ts
var readInput = z2.object({
  file_path: z2.string().describe("Path to the file to read"),
  offset: z2.number().optional().describe("Line number to start from (1-indexed)"),
  limit: z2.number().optional().describe("Max lines to read").default(2000)
});
var ReadTool = defineToolInterface({
  name: "read",
  description: "Read a file with line numbers",
  input: readInput,
  output: z2.string(),
  serialize: (raw, input) => {
    const lines = raw.split(`
`);
    const offset = input.offset ?? 1;
    const limit = input.limit ?? 2000;
    const slice = lines.slice(offset - 1, offset - 1 + limit);
    const totalLines = lines.length;
    const width = String(offset + slice.length - 1).length;
    const numbered = slice.map((line, i) => {
      const lineNum = String(offset + i).padStart(width, " ");
      return `${lineNum}→${line}`;
    }).join(`
`);
    if (slice.length < totalLines) {
      return `${numbered}

(Showing lines ${offset}-${offset + slice.length - 1} of ${totalLines}. Use offset=${offset + slice.length} to continue.)`;
    }
    return `${numbered}

(End of file - total ${totalLines} lines)`;
  }
});

// src/hooks/typed.ts
function createPreToolUseHook(toolOrTools, hook) {
  const tools = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools];
  const toolNames = tools.map((tool) => tool.name);
  return (ctx) => {
    if (!toolNames.includes(ctx.toolName)) {
      return ctx.next();
    }
    return hook(ctx);
  };
}
function createApprovalHook(toolOrTools, hook) {
  const tools = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools];
  const toolNames = tools.map((tool) => tool.name);
  return (ctx) => {
    if (!toolNames.includes(ctx.toolName)) {
      return ctx.next();
    }
    return hook(ctx);
  };
}
function createPostToolUseHook(toolOrTools, hook) {
  const tools = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools];
  const toolNames = tools.map((tool) => tool.name);
  return (ctx) => {
    if (!toolNames.includes(ctx.toolName)) {
      return ctx.done();
    }
    return hook(ctx);
  };
}
function isToolCall(ctx, tool) {
  return ctx.toolName === tool.name;
}

// src/hooks/output-truncation.ts
function truncateWithOptions(output, opts) {
  const maxLines = opts?.maxLines ?? 2000;
  const maxBytes = opts?.maxBytes ?? 50 * 1024;
  const maxLineWidth = opts?.maxLineWidth;
  const direction = opts?.direction ?? "head";
  let lines = output.split(`
`);
  if (maxLineWidth !== undefined) {
    lines = lines.map((line) => line.length > maxLineWidth ? `${line.slice(0, maxLineWidth)}...` : line);
  }
  const totalLines = lines.length;
  const originalByteLength = Buffer.byteLength(output, "utf8");
  const workingLines = direction === "tail" ? [...lines].reverse() : lines;
  const kept = [];
  let keptBytes = 0;
  let hitBytes = false;
  for (const line of workingLines) {
    const lineBytes = Buffer.byteLength(line, "utf8") + 1;
    if (kept.length >= maxLines) {
      break;
    }
    if (keptBytes + lineBytes > maxBytes) {
      hitBytes = true;
      break;
    }
    kept.push(line);
    keptBytes += lineBytes;
  }
  if (direction === "tail") {
    kept.reverse();
  }
  const truncated = kept.length < totalLines;
  const truncatedLines = totalLines - kept.length;
  const content = kept.join(`
`);
  const keptByteLength = Buffer.byteLength(content, "utf8");
  const truncatedBytes = originalByteLength - keptByteLength;
  return {
    content,
    truncated,
    truncatedLines,
    truncatedBytes,
    hitBytes
  };
}
function createReadTruncationHook(opts) {
  const maxLines = opts?.maxLines ?? 2000;
  const maxBytes = opts?.maxBytes ?? 50 * 1024;
  const maxLineWidth = opts?.maxLineWidth ?? 2000;
  const customHint = opts?.hint;
  return createPostToolUseHook(ReadTool, (ctx) => {
    if (typeof ctx.output !== "string") {
      return ctx.done();
    }
    const input = ctx.input;
    const result = truncateWithOptions(ctx.output, {
      maxLines,
      maxBytes,
      maxLineWidth,
      direction: "head"
    });
    const contentChanged = result.content !== ctx.output;
    if (!contentChanged) {
      return ctx.done();
    }
    if (!result.truncated) {
      return ctx.done(result.content);
    }
    const startLine = input.offset ?? 1;
    const keptLineCount = result.content.split(`
`).length;
    const endLine = startLine + keptLineCount - 1;
    const nextOffset = endLine + 1;
    let hint;
    if (customHint) {
      hint = customHint({
        toolName: ctx.toolName,
        truncatedLines: result.truncatedLines,
        truncatedBytes: result.truncatedBytes,
        hitBytes: result.hitBytes,
        startLine,
        endLine,
        nextOffset
      });
    } else if (result.hitBytes) {
      hint = `(Output capped at ${Math.round(maxBytes / 1024)} KB. Showing lines ${startLine}-${endLine}. Use offset=${nextOffset} to continue.)`;
    } else {
      hint = `(Showing lines ${startLine}-${endLine}. Use offset=${nextOffset} to continue.)`;
    }
    return ctx.done(`${result.content}

${hint}`);
  });
}
var readTruncationHook = createReadTruncationHook();
// src/hooks/post-tool-use.ts
async function runPostToolUseHooks(hooks, baseCtx) {
  let currentOutput = baseCtx.output;
  const localState = { ...baseCtx.state ?? {} };
  const stateUpdates = [];
  for (const hook of hooks) {
    const ctx = {
      toolName: baseCtx.toolName,
      toolCallId: baseCtx.toolCallId,
      input: baseCtx.input,
      output: currentOutput,
      rawOutput: baseCtx.rawOutput,
      tool: baseCtx.tool,
      getContextWindow: baseCtx.getContextWindow,
      getState(key) {
        return localState[key];
      },
      updateState(key, updater) {
        const nextValue = updater(localState[key]);
        localState[key] = nextValue;
        stateUpdates.push({
          key,
          apply: (current) => updater(current)
        });
      },
      done(mutatedResult) {
        return hookDone(mutatedResult);
      }
    };
    const result = await hook(ctx);
    if (typeof result.mutatedResult === "string" && typeof currentOutput === "string") {
      currentOutput = result.mutatedResult;
    }
  }
  const hasUpdatedOutput = currentOutput !== baseCtx.output;
  return {
    result: hookDone(hasUpdatedOutput ? currentOutput : undefined),
    stateUpdates
  };
}
// src/hooks/pre-request.ts
function createPreRequestHook(hook) {
  return hook;
}
async function runPreRequestHooks(hooks, input) {
  let currentMessages = input.messages;
  let persist = false;
  let transformed = false;
  for (const hook of hooks) {
    const ctx = {
      messages: Object.freeze([...currentMessages]),
      contextWindowTokens: input.contextWindowTokens ?? 0,
      contextWindowLimit: input.contextWindowLimit,
      next() {
        return { type: "preRequestNext" };
      },
      transform(messages, opts) {
        return {
          type: "preRequestTransform",
          messages,
          persist: opts?.persist ?? false
        };
      }
    };
    const result = await hook(ctx);
    if (result.type === "preRequestTransform") {
      currentMessages = result.messages;
      transformed = true;
      if (result.persist) {
        persist = true;
      }
    }
  }
  return { messages: currentMessages, persist, transformed };
}
// src/hooks/pre-tool-use.ts
async function runPreToolUseHooks(hooks, baseCtx) {
  let currentInput = baseCtx.input;
  let aggregatedUpdateContextWindow = false;
  let aggregatedNotifyModel = false;
  const localState = { ...baseCtx.state ?? {} };
  const stateUpdates = [];
  for (const hook of hooks) {
    const ctx = {
      toolName: baseCtx.toolName,
      toolCallId: baseCtx.toolCallId,
      input: currentInput,
      tool: baseCtx.tool,
      getContextWindow: baseCtx.getContextWindow,
      getState(key) {
        return localState[key];
      },
      updateState(key, updater) {
        const nextValue = updater(localState[key]);
        localState[key] = nextValue;
        stateUpdates.push({
          key,
          apply: (current) => updater(current)
        });
      },
      next(updatedInput, opts2) {
        return hookNext(updatedInput, opts2);
      },
      toolResult(output, opts2) {
        return hookToolResult(output, opts2);
      },
      stop(options) {
        return hookStop(options);
      }
    };
    const result = await hook(ctx);
    if (result.type === "next") {
      if (result.updatedInput !== undefined) {
        currentInput = result.updatedInput;
      }
      if (result.updateContextWindow)
        aggregatedUpdateContextWindow = true;
      if (result.notifyModel)
        aggregatedNotifyModel = true;
      continue;
    }
    return {
      result,
      stateUpdates
    };
  }
  const hasUpdatedInput = currentInput !== baseCtx.input;
  const opts = aggregatedUpdateContextWindow || aggregatedNotifyModel ? {
    ...aggregatedUpdateContextWindow ? { updateContextWindow: true } : {},
    ...aggregatedNotifyModel ? { notifyModel: true } : {}
  } : undefined;
  return {
    result: hookNext(hasUpdatedInput ? currentInput : undefined, opts),
    stateUpdates
  };
}
// src/hooks/strip-thinking-tokens.ts
function stripThinkingTokens(opts) {
  const textPatterns = opts?.textPatterns;
  return (ctx) => {
    let anyChanged = false;
    const transformed = ctx.messages.map((msg) => {
      if (msg.role !== "assistant")
        return msg;
      if (typeof msg.content === "string") {
        if (!textPatterns?.length)
          return msg;
        let text = msg.content;
        for (const pattern of textPatterns) {
          const before = text;
          text = text.replace(new RegExp(pattern.source, pattern.flags), "");
          if (text !== before)
            anyChanged = true;
        }
        if (text === msg.content)
          return msg;
        return { ...msg, content: text.trim() };
      }
      if (Array.isArray(msg.content)) {
        let msgChanged = false;
        const filtered = msg.content.filter((part) => {
          if (part.type === "reasoning") {
            msgChanged = true;
            return false;
          }
          return true;
        });
        const newContent = textPatterns?.length ? filtered.map((part) => {
          if (part.type !== "text")
            return part;
          let text = part.text;
          for (const pattern of textPatterns) {
            const before = text;
            text = text.replace(new RegExp(pattern.source, pattern.flags), "");
            if (text !== before)
              msgChanged = true;
          }
          if (text === part.text)
            return part;
          return { ...part, text: text.trim() };
        }) : filtered;
        if (msgChanged) {
          anyChanged = true;
          return { ...msg, content: newContent };
        }
        return msg;
      }
      return msg;
    });
    if (!anyChanged)
      return ctx.next();
    return ctx.transform(transformed, { persist: opts?.persist });
  };
}
// src/hooks/truncate-bash-results.ts
function truncateOldBashResults(opts) {
  const keep = opts?.keep ?? 3;
  const summaryLines = opts?.summaryLines ?? 5;
  const persist = opts?.persist ?? false;
  const bashToolName = "bash";
  return (ctx) => {
    const bashToolCallIds = [];
    for (const msg of ctx.messages) {
      if (msg.role !== "tool" || !Array.isArray(msg.content))
        continue;
      for (const part of msg.content) {
        if (part.type !== "tool-result")
          continue;
        if (part.toolName !== bashToolName)
          continue;
        bashToolCallIds.push(part.toolCallId);
      }
    }
    if (bashToolCallIds.length <= keep)
      return ctx.next();
    const preserveSet = new Set(bashToolCallIds.slice(-keep));
    const transformed = ctx.messages.map((msg) => {
      if (msg.role !== "tool" || !Array.isArray(msg.content))
        return msg;
      let changed = false;
      const newContent = msg.content.map((part) => {
        if (part.type !== "tool-result")
          return part;
        if (part.toolName !== bashToolName)
          return part;
        if (preserveSet.has(part.toolCallId))
          return part;
        changed = true;
        const output = typeof part.output === "string" ? part.output : part.output?.value ?? "";
        const lines = output.split(`
`);
        const totalLines = lines.length;
        let truncatedOutput;
        if (totalLines > summaryLines) {
          const keptLines = lines.slice(0, summaryLines).join(`
`);
          truncatedOutput = `${keptLines}
[... ${totalLines} total lines — truncated]`;
        } else {
          truncatedOutput = output;
        }
        return {
          ...part,
          output: typeof part.output === "string" ? truncatedOutput : { type: "text", value: truncatedOutput }
        };
      });
      if (!changed)
        return msg;
      return { ...msg, content: newContent };
    });
    return ctx.transform(transformed, { persist });
  };
}
export {
  truncateWithOptions,
  truncateOldBashResults,
  stripThinkingTokens,
  runPreToolUseHooks,
  runPreRequestHooks,
  runPostToolUseHooks,
  runApprovalHooks,
  readTruncationHook,
  isToolCall,
  hookToolResult,
  hookStop,
  hookNext,
  hookDone,
  hookDeny,
  hookAsk,
  deduplicateReads,
  createReadTruncationHook,
  createPreToolUseHook,
  createPreRequestHook,
  createPostToolUseHook,
  createApprovalHook
};

//# debugId=2AACF9315D338FDA64756E2164756E21
