// src/types/errors.ts
var RECOVERABLE_CODES = /* @__PURE__ */ new Set([
  "TIMEOUT" /* TIMEOUT */,
  "RATE_LIMIT" /* RATE_LIMIT */,
  "NETWORK" /* NETWORK */,
  "TOOL_EXECUTION" /* TOOL_EXECUTION */,
  "MODEL_OVERLOADED" /* MODEL_OVERLOADED */,
  "PROVIDER_ERROR" /* PROVIDER_ERROR */
]);
function isRecoverableErrorCode(code) {
  return RECOVERABLE_CODES.has(code);
}
function classifyAgentError(error) {
  const msg = (error instanceof Error ? error.message : error).toLowerCase();
  if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("timedout") || msg.includes("etimedout")) {
    return "TIMEOUT" /* TIMEOUT */;
  }
  if (msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("429") || msg.includes("too many requests")) {
    return "RATE_LIMIT" /* RATE_LIMIT */;
  }
  if (msg.includes("unauthorized") || msg.includes("401") || msg.includes("auth") && (msg.includes("expired") || msg.includes("invalid") || msg.includes("denied") || msg.includes("failed"))) {
    return "AUTH_EXPIRED" /* AUTH_EXPIRED */;
  }
  if (msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("enotfound") || msg.includes("network") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
    return "NETWORK" /* NETWORK */;
  }
  if (msg.includes("subprocess") || msg.includes("process exited") || msg.includes("spawn") || msg.includes("enoent") || msg.includes("killed")) {
    return "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */;
  }
  if (msg.includes("abort") || msg.includes("cancel")) {
    return "ABORTED" /* ABORTED */;
  }
  if (msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("internal server error") || msg.includes("service unavailable") || msg.includes("bad gateway") || msg.includes("overloaded")) {
    return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
  }
  return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
}

// src/types/guards.ts
function getTextContent(content) {
  if (typeof content === "string") return content;
  return content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
}

// src/errors.ts
var AgentSDKError = class extends Error {
  /** @internal Marker for cross-bundle identity checks */
  _agentSDKError = true;
  /** Machine-readable error code. Prefer values from the ErrorCode enum. */
  code;
  /** Whether this error is safe to retry */
  retryable;
  /** HTTP status code hint for error classification */
  httpStatus;
  constructor(message, options) {
    super(message, options);
    this.name = "AgentSDKError";
    this.code = options?.code;
    this.retryable = options?.retryable ?? false;
    this.httpStatus = options?.httpStatus;
  }
  /** Check if an error is an AgentSDKError (works across bundled copies) */
  static is(error) {
    return error instanceof Error && "_agentSDKError" in error && error._agentSDKError === true;
  }
};
var ReentrancyError = class extends AgentSDKError {
  constructor() {
    super("Agent is already running. Await the current run before starting another.", {
      code: "REENTRANCY" /* REENTRANCY */
    });
    this.name = "ReentrancyError";
  }
};
var DisposedError = class extends AgentSDKError {
  constructor(entity) {
    super(`${entity} has been disposed and cannot be used.`, {
      code: "DISPOSED" /* DISPOSED */
    });
    this.name = "DisposedError";
  }
};
var SubprocessError = class extends AgentSDKError {
  constructor(message, options) {
    super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
    this.name = "SubprocessError";
  }
};
var AbortError = class extends AgentSDKError {
  constructor() {
    super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
    this.name = "AbortError";
  }
};
var ActivityTimeoutError = class extends AgentSDKError {
  constructor(timeoutMs) {
    super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
      code: "TIMEOUT" /* TIMEOUT */,
      retryable: true
    });
    this.name = "ActivityTimeoutError";
  }
};

// src/base-agent.ts
var BaseAgent = class {
  state = "idle";
  abortController = null;
  config;
  _cleanupExternalSignal = null;
  _streamMiddleware = [];
  /** CLI session ID for persistent mode. Override in backends that support it. */
  get sessionId() {
    return void 0;
  }
  constructor(config) {
    this.config = Object.freeze({ ...config });
  }
  // ─── Public Interface ─────────────────────────────────────────
  async run(prompt, options) {
    this.guardReentrancy();
    this.guardDisposed();
    const ac = this.createAbortController(options?.signal);
    this.state = "running";
    try {
      const messages = [{ role: "user", content: prompt }];
      const result = await this.withRetry(
        () => this.executeRun(messages, options, ac.signal),
        options
      );
      this.enrichAndNotifyUsage(result, options);
      return result;
    } finally {
      this.cleanupRun();
    }
  }
  async runWithContext(messages, options) {
    this.guardReentrancy();
    this.guardDisposed();
    const ac = this.createAbortController(options?.signal);
    this.state = "running";
    try {
      const result = await this.withRetry(
        () => this.executeRun(messages, options, ac.signal),
        options
      );
      this.enrichAndNotifyUsage(result, options);
      return result;
    } finally {
      this.cleanupRun();
    }
  }
  async runStructured(prompt, schema, options) {
    this.guardReentrancy();
    this.guardDisposed();
    const ac = this.createAbortController(options?.signal);
    this.state = "running";
    try {
      const messages = [{ role: "user", content: prompt }];
      const result = await this.withRetry(
        () => this.executeRunStructured(messages, schema, options, ac.signal),
        options
      );
      this.enrichAndNotifyUsage(result, options);
      return result;
    } finally {
      this.cleanupRun();
    }
  }
  async *stream(prompt, options) {
    this.guardReentrancy();
    this.guardDisposed();
    const ac = this.createAbortController(options?.signal);
    this.state = "streaming";
    try {
      const messages = [{ role: "user", content: prompt }];
      yield* this.streamWithRetry(
        () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
        options
      );
    } finally {
      this.cleanupRun();
    }
  }
  async *streamWithContext(messages, options) {
    this.guardReentrancy();
    this.guardDisposed();
    const ac = this.createAbortController(options?.signal);
    this.state = "streaming";
    try {
      yield* this.streamWithRetry(
        () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
        options
      );
    } finally {
      this.cleanupRun();
    }
  }
  /** Register a stream middleware. Applied in registration order after built-in transforms. */
  addStreamMiddleware(middleware) {
    this.guardDisposed();
    this._streamMiddleware.push(middleware);
  }
  /** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
  async *applyStreamPipeline(source, options, ac) {
    let stream = this.enrichStream(source, options);
    stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
    stream = this.heartbeatStream(stream);
    if (this._streamMiddleware.length > 0) {
      const ctx = {
        model: options.model,
        backend: this.backendName,
        abortController: ac,
        config: Object.freeze({ ...this.config })
      };
      for (const mw of this._streamMiddleware) {
        stream = mw(stream, ctx);
      }
    }
    yield* stream;
  }
  abort() {
    if (this.abortController) {
      this.abortController.abort();
    }
  }
  /** Default interrupt — falls back to abort(). Backends may override with graceful shutdown. */
  async interrupt() {
    this.abort();
  }
  getState() {
    return this.state;
  }
  getConfig() {
    return this.config;
  }
  /** Mark agent as disposed. Override to add cleanup. */
  dispose() {
    this._cleanupExternalSignal?.();
    this._cleanupExternalSignal = null;
    this.abort();
    this.state = "disposed";
  }
  // ─── Retry Logic ─────────────────────────────────────────────
  /** Check if an error should be retried given the retry configuration. */
  isRetryableError(error, retry) {
    if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
      return false;
    }
    if (AgentSDKError.is(error)) {
      if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
        return retry.retryableErrors.includes(error.code);
      }
      if (error.retryable) return true;
      if (error.code) return isRecoverableErrorCode(error.code);
    }
    return false;
  }
  /** Execute a function with retry logic per RetryConfig. */
  async withRetry(fn, options) {
    const retry = options?.retry;
    if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
      return fn();
    }
    const maxRetries = retry.maxRetries;
    const initialDelay = retry.initialDelayMs ?? 1e3;
    const multiplier = retry.backoffMultiplier ?? 2;
    let lastError;
    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      try {
        return await fn();
      } catch (err) {
        lastError = err;
        if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
          throw err;
        }
        const delay = initialDelay * Math.pow(multiplier, attempt);
        await new Promise((resolve) => setTimeout(resolve, delay));
        if (options?.signal?.aborted || this.abortController?.signal.aborted) {
          throw err;
        }
      }
    }
    throw lastError;
  }
  /** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
  async *streamWithRetry(factory, options) {
    const retry = options?.retry;
    if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
      yield* factory();
      return;
    }
    const maxRetries = retry.maxRetries;
    const initialDelay = retry.initialDelayMs ?? 1e3;
    const multiplier = retry.backoffMultiplier ?? 2;
    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      try {
        const stream = factory();
        const iterator = stream[Symbol.asyncIterator]();
        const first = await iterator.next();
        if (first.done) return;
        yield first.value;
        while (true) {
          const next = await iterator.next();
          if (next.done) break;
          yield next.value;
        }
        return;
      } catch (err) {
        if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
          throw err;
        }
        const delay = initialDelay * Math.pow(multiplier, attempt);
        await new Promise((resolve) => setTimeout(resolve, delay));
        if (options?.signal?.aborted || this.abortController?.signal.aborted) {
          throw err;
        }
      }
    }
  }
  // ─── CallOptions Resolution ──────────────────────────────────
  /** Resolve tools to use for this call (per-call override > config default) */
  resolveTools(options) {
    return options?.tools ?? this.config.tools ?? [];
  }
  // ─── Usage Enrichment ───────────────────────────────────────────
  /** Enrich result usage with model/backend and fire onUsage callback */
  enrichAndNotifyUsage(result, options) {
    if (result.usage) {
      result.usage = {
        ...result.usage,
        model: options.model,
        backend: this.backendName
      };
      this.callOnUsage(result.usage);
    }
  }
  /** Wrap a stream to enrich usage_update events and fire onUsage callback */
  async *enrichStream(source, options) {
    const model = options.model;
    for await (const event of source) {
      if (event.type === "usage_update") {
        const usage = {
          promptTokens: event.promptTokens,
          completionTokens: event.completionTokens,
          model,
          backend: this.backendName
        };
        this.callOnUsage(usage);
        yield { type: "usage_update", ...usage };
      } else {
        yield event;
      }
    }
  }
  /** Fire onUsage callback (fire-and-forget: errors logged, not propagated) */
  callOnUsage(usage) {
    if (!this.config.onUsage) return;
    try {
      this.config.onUsage(usage);
    } catch (e) {
      console.warn(
        "[agent-sdk] onUsage callback error:",
        e instanceof Error ? e.message : String(e)
      );
    }
  }
  // ─── Heartbeat ───────────────────────────────────────────────
  /** Wrap a stream to emit heartbeat events at configured intervals.
   *  When heartbeatInterval is not set, passes through directly. */
  async *heartbeatStream(source) {
    const interval = this.config.heartbeatInterval;
    if (!interval || interval <= 0) {
      yield* source;
      return;
    }
    const iterator = source[Symbol.asyncIterator]();
    let pendingEvent = null;
    let heartbeatResolve = null;
    const timer = setInterval(() => {
      if (heartbeatResolve) {
        const resolve = heartbeatResolve;
        heartbeatResolve = null;
        resolve();
      }
    }, interval);
    try {
      while (true) {
        if (!pendingEvent) {
          pendingEvent = iterator.next();
        }
        const heartbeatPromise = new Promise((resolve) => {
          heartbeatResolve = resolve;
        });
        const eventDone = pendingEvent.then(
          (r) => ({ kind: "event", result: r })
        );
        const heartbeatDone = heartbeatPromise.then(
          () => ({ kind: "heartbeat" })
        );
        const winner = await Promise.race([eventDone, heartbeatDone]);
        if (winner.kind === "heartbeat") {
          yield { type: "heartbeat" };
        } else {
          pendingEvent = null;
          heartbeatResolve = null;
          if (winner.result.done) break;
          yield winner.result.value;
        }
      }
    } finally {
      clearInterval(timer);
      heartbeatResolve = null;
    }
  }
  // ─── Activity Timeout ────────────────────────────────────────
  /** Wrap a stream to abort on inactivity. Resets timer on every event.
   *  When timeoutMs is not set, passes through directly. */
  async *activityTimeoutStream(source, timeoutMs, ac) {
    if (!timeoutMs || timeoutMs <= 0) {
      yield* source;
      return;
    }
    const iterator = source[Symbol.asyncIterator]();
    let timerId;
    try {
      while (true) {
        const timeoutPromise = new Promise((_, reject) => {
          timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
        });
        const result = await Promise.race([iterator.next(), timeoutPromise]);
        clearTimeout(timerId);
        if (result.done) break;
        yield result.value;
      }
    } catch (err) {
      if (err instanceof ActivityTimeoutError) {
        ac.abort(err);
      }
      throw err;
    } finally {
      clearTimeout(timerId);
    }
  }
  // ─── Guards ───────────────────────────────────────────────────
  guardReentrancy() {
    if (this.state === "running" || this.state === "streaming") {
      throw new ReentrancyError();
    }
  }
  guardDisposed() {
    if (this.state === "disposed") {
      throw new DisposedError("Agent");
    }
  }
  /** Throw AbortError if signal is already aborted */
  checkAbort(signal) {
    if (signal.aborted) {
      throw new AbortError();
    }
  }
  // ─── Internal Helpers ─────────────────────────────────────────
  /** Clean up after a run completes (success, error, or abort). */
  cleanupRun() {
    this._cleanupExternalSignal?.();
    this._cleanupExternalSignal = null;
    this.state = "idle";
    this.abortController = null;
  }
  createAbortController(externalSignal) {
    const ac = new AbortController();
    this.abortController = ac;
    this._cleanupExternalSignal = null;
    if (externalSignal) {
      if (externalSignal.aborted) {
        ac.abort();
      } else {
        const listener = () => ac.abort();
        externalSignal.addEventListener("abort", listener, { once: true });
        this._cleanupExternalSignal = () => externalSignal.removeEventListener("abort", listener);
      }
    }
    return ac;
  }
};

// src/utils/schema.ts
function zodToJsonSchema(schema) {
  const schemaAny = schema;
  if ("toJSONSchema" in schema && typeof schemaAny.toJSONSchema === "function") {
    return schemaAny.toJSONSchema();
  }
  if ("jsonSchema" in schema && typeof schemaAny.jsonSchema === "function") {
    return schemaAny.jsonSchema();
  }
  return extractSchemaFromDef(schema);
}
function extractSchemaFromDef(schema) {
  const def = schema._def;
  const typeName = def.typeName;
  switch (typeName) {
    case "ZodString":
      return { type: "string" };
    case "ZodNumber":
      return { type: "number" };
    case "ZodBoolean":
      return { type: "boolean" };
    case "ZodNull":
      return { type: "null" };
    case "ZodArray":
      return {
        type: "array",
        items: extractSchemaFromDef(def.type)
      };
    case "ZodObject": {
      const shape = schema.shape;
      const properties = {};
      const required = [];
      for (const [key, value] of Object.entries(shape)) {
        const valueDef = value._def;
        if (valueDef.typeName === "ZodOptional") {
          properties[key] = extractSchemaFromDef(valueDef.innerType);
        } else {
          properties[key] = extractSchemaFromDef(value);
          required.push(key);
        }
      }
      return {
        type: "object",
        properties,
        ...required.length > 0 ? { required } : {}
      };
    }
    case "ZodOptional":
      return extractSchemaFromDef(def.innerType);
    case "ZodEnum":
      return { type: "string", enum: def.values };
    default:
      return {};
  }
}

// src/backends/shared.ts
function extractLastUserPrompt(messages) {
  for (let i = messages.length - 1; i >= 0; i--) {
    const msg = messages[i];
    if (msg.role === "user") {
      return getTextContent(msg.content);
    }
  }
  return "";
}
function serializeToolCall(tc) {
  const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
  return `  Tool call: ${tc.name}(${args})`;
}
function serializeToolResult(tr) {
  const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
  const prefix = tr.isError ? "[ERROR] " : "";
  return `  ${tr.name} \u2192 ${prefix}${result}`;
}
function buildContextualPrompt(messages) {
  if (messages.length <= 1) {
    return extractLastUserPrompt(messages);
  }
  const history = messages.slice(0, -1).map((msg) => {
    if (msg.role === "user") {
      return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
    }
    if (msg.role === "tool" && msg.toolResults) {
      const results = msg.toolResults.map(serializeToolResult).join("\n");
      return `Tool results:
${results}`;
    }
    if (msg.role === "assistant") {
      const parts = [];
      const thinking = msg.thinking;
      if (thinking) {
        parts.push(`[reasoning: ${thinking}]`);
      }
      const text2 = msg.content ? getTextContent(msg.content) : "";
      if (text2) parts.push(text2);
      if (msg.toolCalls && msg.toolCalls.length > 0) {
        parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
      }
      return `Assistant: ${parts.join("\n")}`;
    }
    const text = msg.content ? getTextContent(msg.content) : "";
    return `${msg.role}: ${text}`;
  }).join("\n");
  const lastPrompt = extractLastUserPrompt(messages);
  return `Conversation history:
${history}

User: ${lastPrompt}`;
}

// src/backends/claude.ts
var MCP_SERVER_NAME = "agent-sdk-tools";
var MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
function mcpToolName(toolName) {
  return `${MCP_TOOL_PREFIX}${toolName}`;
}
function stripMcpPrefix(name) {
  return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
}
var CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
var _sdkMock = null;
async function loadSDK() {
  if (_sdkMock) return _sdkMock;
  try {
    return await import('@anthropic-ai/claude-agent-sdk');
  } catch {
    throw new SubprocessError(
      "@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
    );
  }
}
function _injectSDK(mock) {
  _sdkMock = mock;
}
function _resetSDK() {
  _sdkMock = null;
}
var ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
var ANTHROPIC_API_VERSION = "2023-06-01";
var ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
function normalizeAskUserInput(args) {
  if (typeof args.question === "string") {
    return {
      question: args.question,
      choices: Array.isArray(args.choices) ? args.choices : void 0,
      allowFreeform: args.allowFreeform !== false
    };
  }
  const questions = args.questions;
  if (questions && questions.length > 0) {
    const first = questions[0];
    return {
      question: first.question,
      choices: first.options?.map((o) => o.label),
      allowFreeform: true
    };
  }
  return { question: JSON.stringify(args), allowFreeform: true };
}
function buildMcpServer(sdk, tools, toolResultCapture, onAskUser) {
  if (tools.length === 0 && !onAskUser) return void 0;
  const mcpTools = tools.map((tool) => {
    const zodSchema = tool.parameters;
    const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
    return sdk.tool(
      tool.name,
      tool.description ?? "",
      inputSchema,
      async (args) => {
        const result = await tool.execute(args);
        if (toolResultCapture) {
          toolResultCapture.set(tool.name, result);
        }
        return {
          content: [
            {
              type: "text",
              text: typeof result === "string" ? result : JSON.stringify(result)
            }
          ]
        };
      }
    );
  });
  if (onAskUser) {
    const askUserTool = sdk.tool(
      "ask_user",
      "Ask the user a question and wait for their response",
      {
        question: { type: "string", description: "The question to ask the user" },
        choices: {
          type: "array",
          items: { type: "string" },
          description: "Optional list of choices for multiple choice"
        },
        questions: {
          type: "array",
          items: {
            type: "object",
            properties: {
              question: { type: "string" },
              options: { type: "array", items: { type: "object", properties: { label: { type: "string" } } } }
            }
          },
          description: "Alternative nested question format"
        }
      },
      async (args) => {
        const normalized = normalizeAskUserInput(args);
        const response = await onAskUser(normalized, AbortSignal.timeout(3e5));
        return {
          content: [{ type: "text", text: response.answer }]
        };
      }
    );
    mcpTools.push(askUserTool);
  }
  return sdk.createSdkMcpServer({
    name: MCP_SERVER_NAME,
    version: "1.0.0",
    tools: mcpTools
  });
}
function scopeToDestination(scope) {
  switch (scope) {
    case "once":
      return "session";
    case "session":
      return "session";
    case "project":
      return "projectSettings";
    case "always":
      return "userSettings";
  }
}
function destinationToScope(dest) {
  switch (dest) {
    case "session":
    case "cliArg":
      return "session";
    case "projectSettings":
    case "localSettings":
      return "project";
    case "userSettings":
      return "always";
  }
}
function extractSuggestedScope(suggestions) {
  if (!suggestions || suggestions.length === 0) return void 0;
  return destinationToScope(suggestions[0].destination);
}
function buildCanUseTool(config) {
  const onPermission = config.supervisor?.onPermission;
  if (!onPermission) return void 0;
  const permissionStore = config.permissionStore;
  return async (rawToolName, input, options) => {
    const toolName = stripMcpPrefix(rawToolName);
    if (permissionStore && await permissionStore.isApproved(toolName)) {
      return {
        behavior: "allow",
        toolUseID: options.toolUseID
      };
    }
    const unifiedRequest = {
      toolName,
      toolArgs: input,
      toolCallId: options.toolUseID,
      suggestedScope: extractSuggestedScope(options.suggestions),
      rawSDKRequest: { toolName, input, ...options }
    };
    const decision = await onPermission(
      unifiedRequest,
      options.signal
    );
    if (decision.allowed) {
      if (permissionStore && decision.scope) {
        await permissionStore.approve(toolName, decision.scope);
      }
      const result = {
        behavior: "allow",
        toolUseID: options.toolUseID
      };
      if (decision.modifiedInput) {
        result.updatedInput = decision.modifiedInput;
      }
      if (decision.scope && decision.scope !== "once" && options.suggestions) {
        result.updatedPermissions = options.suggestions.map((s) => ({
          ...s,
          destination: scopeToDestination(decision.scope)
        }));
      }
      return result;
    }
    return {
      behavior: "deny",
      message: decision.reason ?? "Permission denied",
      toolUseID: options.toolUseID
    };
  };
}
function aggregateUsage(modelUsage) {
  let promptTokens = 0;
  let completionTokens = 0;
  for (const u of Object.values(modelUsage ?? {})) {
    promptTokens += u.inputTokens ?? 0;
    completionTokens += u.outputTokens ?? 0;
  }
  return { promptTokens, completionTokens };
}
var ClaudeToolCallTracker = class {
  queues = /* @__PURE__ */ new Map();
  trackStart(toolCallId, toolName) {
    if (!this.queues.has(toolName)) {
      this.queues.set(toolName, []);
    }
    this.queues.get(toolName).push(toolCallId);
  }
  /** Peek at the current tool call ID for a tool name (does not consume) */
  peekToolCallId(toolName) {
    const queue = this.queues.get(toolName);
    if (!queue || queue.length === 0) return "";
    return queue[0];
  }
  /** Consume and return the first tool call ID for a tool name */
  consumeToolCallId(toolName) {
    const queue = this.queues.get(toolName);
    if (!queue || queue.length === 0) return "";
    return queue.shift();
  }
  clear() {
    this.queues.clear();
  }
};
function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
  switch (msg.type) {
    case "assistant": {
      const betaMessage = msg.message;
      if (!betaMessage?.content) return null;
      const events = [];
      for (const block of betaMessage.content) {
        if (block.type === "tool_use") {
          const toolCallId = String(block.id ?? "");
          const toolName = stripMcpPrefix(block.name ?? "unknown");
          if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
          if (toolCallTracker) {
            toolCallTracker.trackStart(toolCallId, toolName);
          }
          events.push({
            type: "tool_call_start",
            toolCallId,
            toolName,
            args: block.input ?? {}
          });
        }
      }
      return events.length > 0 ? events : null;
    }
    case "user": {
      const toolResult = msg.tool_use_result;
      if (toolResult !== void 0) {
        return null;
      }
      return null;
    }
    case "tool_use_summary": {
      const summary = msg.summary;
      const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
      if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
      const precedingIds = msg.preceding_tool_use_ids;
      let toolCallId = "";
      if (precedingIds && precedingIds.length > 0) {
        toolCallId = precedingIds[0];
        if (toolCallTracker) toolCallTracker.consumeToolCallId(toolName);
      } else if (toolCallTracker) {
        toolCallId = toolCallTracker.consumeToolCallId(toolName);
      }
      return {
        type: "tool_call_end",
        toolCallId,
        toolName,
        result: summary ?? null
      };
    }
    case "stream_event": {
      const event = msg.event;
      if (!event) return null;
      if (event.type === "content_block_delta" && event.index !== void 0 && thinkingBlockIndices?.has(event.index)) {
        const thinkingText = String(event.delta?.thinking ?? event.delta?.text ?? "");
        if (thinkingText) {
          return { type: "thinking_delta", text: thinkingText };
        }
        return null;
      }
      if (event.type === "content_block_delta" && event.delta?.type === "text_delta" && event.delta.text) {
        return { type: "text_delta", text: event.delta.text };
      }
      if (event.type === "content_block_start" && event.content_block?.type === "thinking") {
        if (thinkingBlockIndices && event.index !== void 0) {
          thinkingBlockIndices.add(event.index);
        }
        return { type: "thinking_start" };
      }
      if (event.type === "content_block_stop" && event.index !== void 0 && thinkingBlockIndices?.has(event.index)) {
        thinkingBlockIndices.delete(event.index);
        return { type: "thinking_end" };
      }
      return null;
    }
    case "tool_progress": {
      return null;
    }
    case "result": {
      if (msg.subtype === "success") {
        const r = msg;
        return {
          type: "usage_update",
          ...aggregateUsage(r.modelUsage)
        };
      }
      if (msg.is_error) {
        const r = msg;
        const errorMsg = r.errors?.join("; ") ?? "Unknown error";
        const code = classifyAgentError(errorMsg);
        return {
          type: "error",
          error: errorMsg,
          recoverable: isRecoverableErrorCode(code),
          code
        };
      }
      return null;
    }
    default:
      return null;
  }
}
var ClaudeAgent = class extends BaseAgent {
  backendName = "claude";
  options;
  tools;
  canUseTool;
  isPersistent;
  _sessionId;
  activeQuery = null;
  constructor(config, options) {
    super(config);
    this.options = options;
    this.tools = config.tools ?? [];
    this.canUseTool = buildCanUseTool(config);
    this.isPersistent = config.sessionMode === "persistent";
    if (options.resumeSessionId) {
      this._sessionId = options.resumeSessionId;
    }
  }
  get sessionId() {
    return this._sessionId;
  }
  async interrupt() {
    try {
      if (this.activeQuery) {
        await this.activeQuery.interrupt();
      }
    } catch {
    } finally {
      this.abort();
    }
  }
  /** Clear persistent session state after an error so next call starts fresh */
  clearPersistentSession() {
    this._sessionId = void 0;
  }
  emitSessionInfo(sessionId) {
    const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
    const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
    return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
  }
  buildQueryOptions(signal, options) {
    const ac = new AbortController();
    signal.addEventListener("abort", () => ac.abort(), { once: true });
    const opts = {
      abortController: ac,
      model: options.model,
      maxTurns: this.options.maxTurns,
      cwd: this.options.workingDirectory,
      pathToClaudeCodeExecutable: this.options.cliPath,
      persistSession: this.isPersistent,
      includePartialMessages: true,
      canUseTool: this.canUseTool
    };
    if (this.isPersistent && this._sessionId) {
      opts.resume = this._sessionId;
    }
    if (this.config.systemPrompt) {
      opts.systemPrompt = this.config.systemPrompt;
    }
    if (this.options.oauthToken || this.options.env) {
      opts.env = {
        ...process.env,
        ...this.options.env ?? {},
        ...this.options.oauthToken ? { CLAUDE_CODE_OAUTH_TOKEN: this.options.oauthToken } : {}
      };
    }
    if (opts.canUseTool && !opts.permissionMode) {
      opts.permissionMode = "default";
    }
    if (this.config.availableTools) {
      opts.tools = [...this.config.availableTools];
    }
    return opts;
  }
  async buildMcpConfig(opts, toolResultCapture) {
    const onAskUser = this.config.supervisor?.onAskUser;
    if (this.tools.length === 0 && !onAskUser) return opts;
    const sdk = await loadSDK();
    const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
    if (mcpServer) {
      opts.mcpServers = {
        [MCP_SERVER_NAME]: mcpServer
      };
      const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
      if (onAskUser) {
        mcpToolNames.push(mcpToolName("ask_user"));
      }
      opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
    }
    if (onAskUser) {
      opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
    }
    return opts;
  }
  // ─── Retry Helpers (shared across executeRun/RunStructured/Stream) ──
  /** Setup a retry query: clear session, rebuild with full history */
  async prepareRetryQuery(sdk, messages, signal, options, toolResultCapture, modifyOpts) {
    this.clearPersistentSession();
    const retryPrompt = buildContextualPrompt(messages);
    let retryOpts = this.buildQueryOptions(signal, options);
    toolResultCapture.clear();
    retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
    modifyOpts?.(retryOpts);
    const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
    this.activeQuery = retryQ;
    return retryQ;
  }
  /** Extract tool_use blocks from an assistant SDK message into toolCalls array */
  collectToolCallsFromMessage(msg, toolCalls, toolResultCapture) {
    if (msg.type !== "assistant") return;
    const betaMessage = msg.message;
    if (!betaMessage?.content) return;
    for (const block of betaMessage.content) {
      if (block.type === "tool_use") {
        const toolName = stripMcpPrefix(block.name ?? "unknown");
        if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
        toolCalls.push({
          toolName,
          args: block.input ?? {},
          result: toolResultCapture.get(toolName) ?? null,
          approved: true
        });
      }
    }
  }
  /** Back-fill tool results from capture map on summary/result messages */
  backfillToolResults(msg, toolCalls, toolResultCapture) {
    if (msg.type !== "tool_use_summary" && msg.type !== "result") return;
    for (const tc of toolCalls) {
      if (tc.result === null) {
        const captured = toolResultCapture.get(tc.toolName);
        if (captured !== void 0) tc.result = captured;
      }
    }
  }
  /** Wrap retry inner loop with shared error handling */
  async withRetryErrorHandling(signal, fn) {
    try {
      return await fn();
    } catch (retryError) {
      if (this.isPersistent) this.clearPersistentSession();
      if (signal.aborted) throw new AbortError();
      throw retryError;
    } finally {
      this.activeQuery = null;
    }
  }
  // ─── executeRun ─────────────────────────────────────────────────
  async executeRun(messages, options, signal) {
    this.checkAbort(signal);
    const sdk = await loadSDK();
    const isResuming = this.isPersistent && this._sessionId !== void 0;
    const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
    let opts = this.buildQueryOptions(signal, options);
    const toolResultCapture = /* @__PURE__ */ new Map();
    opts = await this.buildMcpConfig(opts, toolResultCapture);
    const q = sdk.query({ prompt, options: opts });
    this.activeQuery = q;
    const toolCalls = [];
    let output = null;
    let usage;
    try {
      for await (const msg of q) {
        this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
        this.backfillToolResults(msg, toolCalls, toolResultCapture);
        if (msg.type === "result") {
          if (msg.subtype === "success") {
            const r = msg;
            output = r.result;
            usage = aggregateUsage(r.modelUsage);
            if (this.isPersistent && r.session_id) {
              this._sessionId = r.session_id;
            }
          } else if (msg.is_error) {
            const r = msg;
            throw new Error(
              `Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
            );
          }
        }
      }
    } catch (e) {
      if (signal.aborted) throw new AbortError();
      if (isResuming && this.isPersistent) {
        const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
        toolCalls.length = 0;
        output = null;
        return this.withRetryErrorHandling(signal, async () => {
          for await (const msg of retryQ) {
            this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
            this.backfillToolResults(msg, toolCalls, toolResultCapture);
            if (msg.type === "result") {
              if (msg.subtype === "success") {
                const r = msg;
                output = r.result;
                usage = aggregateUsage(r.modelUsage);
                if (this.isPersistent && r.session_id) {
                  this._sessionId = r.session_id;
                }
              } else if (msg.is_error) {
                const r = msg;
                throw new Error(
                  `Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
                );
              }
            }
          }
          return {
            output,
            structuredOutput: void 0,
            toolCalls,
            messages: [
              ...messages,
              ...output !== null ? [{ role: "assistant", content: output }] : []
            ],
            usage
          };
        });
      }
      if (this.isPersistent) this.clearPersistentSession();
      throw e;
    } finally {
      this.activeQuery = null;
    }
    return {
      output,
      structuredOutput: void 0,
      toolCalls,
      messages: [
        ...messages,
        ...output !== null ? [{ role: "assistant", content: output }] : []
      ],
      usage
    };
  }
  // ─── executeRunStructured ───────────────────────────────────────
  async executeRunStructured(messages, schema, options, signal) {
    this.checkAbort(signal);
    const sdk = await loadSDK();
    const isResuming = this.isPersistent && this._sessionId !== void 0;
    const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
    let opts = this.buildQueryOptions(signal, options);
    const toolResultCapture = /* @__PURE__ */ new Map();
    opts = await this.buildMcpConfig(opts, toolResultCapture);
    const jsonSchema = zodToJsonSchema(schema.schema);
    opts.outputFormat = {
      type: "json_schema",
      schema: jsonSchema
    };
    const q = sdk.query({ prompt, options: opts });
    this.activeQuery = q;
    const toolCalls = [];
    let output = null;
    let structuredOutput;
    let usage;
    try {
      for await (const msg of q) {
        this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
        this.backfillToolResults(msg, toolCalls, toolResultCapture);
        if (msg.type === "result" && msg.subtype === "success") {
          const r = msg;
          output = r.result;
          if (r.structured_output !== void 0) {
            try {
              structuredOutput = schema.schema.parse(r.structured_output);
            } catch {
              try {
                structuredOutput = schema.schema.parse(JSON.parse(r.result));
              } catch {
              }
            }
          } else if (r.result) {
            try {
              const jsonMatch = r.result.match(/```(?:json)?\s*([\s\S]*?)```/);
              const raw = jsonMatch ? jsonMatch[1].trim() : r.result.trim();
              structuredOutput = schema.schema.parse(JSON.parse(raw));
            } catch {
            }
          }
          usage = aggregateUsage(r.modelUsage);
          if (this.isPersistent && r.session_id) {
            this._sessionId = r.session_id;
          }
        } else if (msg.type === "result" && msg.is_error) {
          const r = msg;
          throw new Error(
            `Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
          );
        }
      }
    } catch (e) {
      if (signal.aborted) throw new AbortError();
      if (isResuming && this.isPersistent) {
        const retryQ = await this.prepareRetryQuery(
          sdk,
          messages,
          signal,
          options,
          toolResultCapture,
          (opts2) => {
            opts2.outputFormat = { type: "json_schema", schema: jsonSchema };
          }
        );
        toolCalls.length = 0;
        output = null;
        structuredOutput = void 0;
        return this.withRetryErrorHandling(signal, async () => {
          for await (const msg of retryQ) {
            this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
            this.backfillToolResults(msg, toolCalls, toolResultCapture);
            if (msg.type === "result" && msg.subtype === "success") {
              const r = msg;
              output = r.result;
              if (r.structured_output !== void 0) {
                try {
                  structuredOutput = schema.schema.parse(r.structured_output);
                } catch {
                  try {
                    structuredOutput = schema.schema.parse(JSON.parse(r.result));
                  } catch {
                  }
                }
              } else if (r.result) {
                try {
                  const jsonMatch = r.result.match(/```(?:json)?\s*([\s\S]*?)```/);
                  const raw = jsonMatch ? jsonMatch[1].trim() : r.result.trim();
                  structuredOutput = schema.schema.parse(JSON.parse(raw));
                } catch {
                }
              }
              usage = aggregateUsage(r.modelUsage);
              if (this.isPersistent && r.session_id) {
                this._sessionId = r.session_id;
              }
            } else if (msg.type === "result" && msg.is_error) {
              const r = msg;
              throw new Error(
                `Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
              );
            }
          }
          return {
            output,
            structuredOutput,
            toolCalls,
            messages: [
              ...messages,
              ...output !== null ? [{ role: "assistant", content: output }] : []
            ],
            usage
          };
        });
      }
      if (this.isPersistent) this.clearPersistentSession();
      throw e;
    } finally {
      this.activeQuery = null;
    }
    return {
      output,
      structuredOutput,
      toolCalls,
      messages: [
        ...messages,
        ...output !== null ? [{ role: "assistant", content: output }] : []
      ],
      usage
    };
  }
  // ─── executeStream ──────────────────────────────────────────────
  async *executeStream(messages, options, signal) {
    this.checkAbort(signal);
    const sdk = await loadSDK();
    const isResuming = this.isPersistent && this._sessionId !== void 0;
    const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
    let opts = this.buildQueryOptions(signal, options);
    const toolResultCapture = /* @__PURE__ */ new Map();
    opts = await this.buildMcpConfig(opts, toolResultCapture);
    const q = sdk.query({ prompt, options: opts });
    this.activeQuery = q;
    const thinkingBlockIndices = /* @__PURE__ */ new Set();
    const toolCallTracker = new ClaudeToolCallTracker();
    const pendingStreamToolCalls = /* @__PURE__ */ new Map();
    let hasStreamedText = false;
    try {
      for await (const msg of q) {
        if (signal.aborted) throw new AbortError();
        const events = mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker);
        if (events) {
          const mapped = Array.isArray(events) ? events : [events];
          for (const e of mapped) {
            if (e.type === "tool_call_start") {
              pendingStreamToolCalls.set(e.toolCallId, e.toolName);
            }
            if (e.type === "tool_call_end" && toolResultCapture.has(e.toolName)) {
              e.result = toolResultCapture.get(e.toolName);
              toolResultCapture.delete(e.toolName);
              pendingStreamToolCalls.delete(e.toolCallId);
            } else if (e.type === "tool_call_end") {
              pendingStreamToolCalls.delete(e.toolCallId);
            }
            if (e.type === "text_delta") hasStreamedText = true;
            yield e;
          }
        }
        if (msg.type === "result" && msg.subtype === "success") {
          const r = msg;
          for (const [toolCallId, toolName] of pendingStreamToolCalls) {
            if (toolResultCapture.has(toolName)) {
              yield {
                type: "tool_call_end",
                toolCallId,
                toolName,
                result: toolResultCapture.get(toolName)
              };
              toolResultCapture.delete(toolName);
            }
          }
          pendingStreamToolCalls.clear();
          if (r.session_id) {
            if (this.isPersistent) {
              this._sessionId = r.session_id;
            }
            yield this.emitSessionInfo(r.session_id);
          }
          yield {
            type: "done",
            finalOutput: hasStreamedText ? null : r.result,
            ...hasStreamedText ? { streamed: true } : {}
          };
        }
      }
    } catch (e) {
      if (signal.aborted) throw new AbortError();
      if (isResuming && this.isPersistent) {
        const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
        const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
        const retryToolCallTracker = new ClaudeToolCallTracker();
        const retryPendingToolCalls = /* @__PURE__ */ new Map();
        let retryHasStreamedText = false;
        try {
          for await (const msg of retryQ) {
            if (signal.aborted) throw new AbortError();
            const retryEvents = mapSDKMessage(msg, retryThinkingBlockIndices, retryToolCallTracker);
            if (retryEvents) {
              const mapped = Array.isArray(retryEvents) ? retryEvents : [retryEvents];
              for (const ev of mapped) {
                if (ev.type === "tool_call_start") {
                  retryPendingToolCalls.set(ev.toolCallId, ev.toolName);
                }
                if (ev.type === "tool_call_end" && toolResultCapture.has(ev.toolName)) {
                  ev.result = toolResultCapture.get(ev.toolName);
                  toolResultCapture.delete(ev.toolName);
                  retryPendingToolCalls.delete(ev.toolCallId);
                } else if (ev.type === "tool_call_end") {
                  retryPendingToolCalls.delete(ev.toolCallId);
                }
                if (ev.type === "text_delta") retryHasStreamedText = true;
                yield ev;
              }
            }
            if (msg.type === "result" && msg.subtype === "success") {
              const r = msg;
              for (const [toolCallId, toolName] of retryPendingToolCalls) {
                if (toolResultCapture.has(toolName)) {
                  yield {
                    type: "tool_call_end",
                    toolCallId,
                    toolName,
                    result: toolResultCapture.get(toolName)
                  };
                  toolResultCapture.delete(toolName);
                }
              }
              retryPendingToolCalls.clear();
              if (r.session_id) {
                if (this.isPersistent) {
                  this._sessionId = r.session_id;
                }
                yield this.emitSessionInfo(r.session_id);
              }
              yield {
                type: "done",
                finalOutput: retryHasStreamedText ? null : r.result,
                ...retryHasStreamedText ? { streamed: true } : {}
              };
            }
          }
        } catch (retryError) {
          if (this.isPersistent) this.clearPersistentSession();
          if (signal.aborted) throw new AbortError();
          throw retryError;
        } finally {
          this.activeQuery = null;
        }
        return;
      }
      if (this.isPersistent) this.clearPersistentSession();
      throw e;
    } finally {
      this.activeQuery = null;
    }
  }
  dispose() {
    this._sessionId = void 0;
    super.dispose();
  }
};
var ClaudeAgentService = class {
  name = "claude";
  disposed = false;
  options;
  cachedModels = null;
  constructor(options) {
    this.options = options;
  }
  createAgent(config) {
    if (this.disposed) throw new DisposedError("ClaudeAgentService");
    return new ClaudeAgent(config, this.options);
  }
  async listModels() {
    if (this.disposed) throw new DisposedError("ClaudeAgentService");
    if (this.cachedModels) return this.cachedModels;
    const token = this.options.oauthToken;
    if (!token) {
      return [];
    }
    const res = await globalThis.fetch(
      `${ANTHROPIC_MODELS_URL}?limit=100`,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          "anthropic-version": ANTHROPIC_API_VERSION,
          "anthropic-beta": ANTHROPIC_OAUTH_BETA
        }
      }
    );
    if (!res.ok) {
      return [];
    }
    const body = await res.json();
    if (!body.data || body.data.length === 0) {
      return [];
    }
    this.cachedModels = body.data.map((m) => ({
      id: m.id,
      name: m.display_name,
      provider: "claude",
      ...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
    }));
    return this.cachedModels;
  }
  async validate() {
    if (this.disposed) throw new DisposedError("ClaudeAgentService");
    const errors = [];
    try {
      await loadSDK();
    } catch (e) {
      errors.push(
        e instanceof Error ? e.message : String(e)
      );
      return { valid: false, errors };
    }
    try {
      const sdk = await loadSDK();
      const q = sdk.query({
        prompt: "echo test",
        options: {
          model: "claude-sonnet-4-20250514",
          pathToClaudeCodeExecutable: this.options.cliPath,
          cwd: this.options.workingDirectory,
          persistSession: false,
          maxTurns: 1,
          permissionMode: "plan"
        }
      });
      const first = await q.next();
      q.close();
      if (first.done) {
        errors.push("Claude CLI returned no messages \u2014 may not be authenticated.");
      }
    } catch (e) {
      errors.push(
        `Failed to connect to Claude CLI: ${e instanceof Error ? e.message : String(e)}`
      );
    }
    return { valid: errors.length === 0, errors };
  }
  async dispose() {
    if (this.disposed) return;
    this.disposed = true;
    this.cachedModels = null;
  }
};
function createClaudeService(options) {
  return new ClaudeAgentService(options);
}

export { _injectSDK, _resetSDK, createClaudeService };
//# sourceMappingURL=claude.js.map
//# sourceMappingURL=claude.js.map