// 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/copilot.ts
var _sdkMock = null;
async function loadSDK() {
  if (_sdkMock) return _sdkMock;
  try {
    return await import('@github/copilot-sdk');
  } catch {
    throw new SubprocessError(
      "@github/copilot-sdk is not installed. Install it: npm install @github/copilot-sdk"
    );
  }
}
function _injectSDK(mock) {
  _sdkMock = mock;
}
function _resetSDK() {
  _sdkMock = null;
}
function mapToolsToSDK(tools) {
  return tools.map((tool) => ({
    name: tool.name,
    description: tool.description,
    parameters: convertParameters(tool.parameters),
    handler: async (args) => {
      const result = await tool.execute(args);
      return typeof result === "string" ? result : JSON.stringify(result);
    }
  }));
}
function convertParameters(params) {
  if (!params) return void 0;
  if (params && typeof params === "object" && "_def" in params) {
    return zodToJsonSchema(params);
  }
  return params;
}
async function mapToolsToSDKAsync(tools) {
  return tools.map((tool) => ({
    name: tool.name,
    description: tool.description,
    parameters: convertParameters(tool.parameters),
    handler: async (args) => {
      const result = await tool.execute(args);
      return typeof result === "string" ? result : JSON.stringify(result);
    }
  }));
}
function buildPermissionHandler(config) {
  const onPermission = config.supervisor?.onPermission;
  if (!onPermission) {
    return async () => ({ kind: "approved" });
  }
  const permissionStore = config.permissionStore;
  return async (request) => {
    const toolName = String(request.kind);
    if (permissionStore && await permissionStore.isApproved(toolName)) {
      return { kind: "approved" };
    }
    const unifiedRequest = {
      toolName,
      toolArgs: { ...request },
      toolCallId: request.toolCallId,
      rawSDKRequest: request
    };
    const ac = new AbortController();
    const decision = await onPermission(unifiedRequest, ac.signal);
    if (decision.allowed) {
      if (permissionStore && decision.scope) {
        await permissionStore.approve(toolName, decision.scope);
      }
      return { kind: "approved" };
    }
    return { kind: "denied-interactively-by-user" };
  };
}
function buildUserInputHandler(config) {
  const onAskUser = config.supervisor?.onAskUser;
  if (!onAskUser) {
    return async () => ({
      answer: "Complete the task autonomously without asking questions.",
      wasFreeform: true
    });
  }
  return async (request) => {
    const ac = new AbortController();
    const response = await onAskUser(
      {
        question: request.question,
        choices: request.choices,
        allowFreeform: request.allowFreeform
      },
      ac.signal
    );
    return { answer: response.answer, wasFreeform: response.wasFreeform };
  };
}
var ToolCallTracker = class {
  map = /* @__PURE__ */ new Map();
  trackStart(toolCallId, toolName, args) {
    this.map.set(toolCallId, { toolName, args });
  }
  getInfo(toolCallId) {
    return this.map.get(toolCallId);
  }
  clear() {
    this.map.clear();
  }
};
var ThinkingTracker = class {
  active = false;
  completed = false;
  isActive() {
    return this.active;
  }
  /** Returns true if thinking already completed (should ignore further reasoning events). */
  isCompleted() {
    return this.completed;
  }
  startThinking() {
    this.active = true;
  }
  /** Returns true if thinking was active and is now ended. */
  endThinking() {
    if (!this.active) return false;
    this.active = false;
    this.completed = true;
    return true;
  }
  /** Reset for next turn (e.g. after done event). */
  reset() {
    this.active = false;
    this.completed = false;
  }
};
function mapSessionEvent(event, tracker, thinkingTracker) {
  const data = event.data;
  switch (event.type) {
    case "assistant.message_delta": {
      const textEvent = {
        type: "text_delta",
        text: String(data.deltaContent ?? "")
      };
      if (thinkingTracker.endThinking()) {
        return [{ type: "thinking_end" }, textEvent];
      }
      return textEvent;
    }
    case "assistant.reasoning":
    case "assistant.reasoning_delta": {
      if (thinkingTracker.isCompleted()) return null;
      const events = [];
      if (!thinkingTracker.isActive()) {
        thinkingTracker.startThinking();
        events.push({ type: "thinking_start" });
      }
      const reasoningText = String(data.deltaContent ?? data.content ?? "");
      if (reasoningText) {
        events.push({ type: "thinking_delta", text: reasoningText });
      }
      return events.length === 1 ? events[0] : events.length > 1 ? events : null;
    }
    case "tool.execution_start": {
      const toolCallId = String(data.toolCallId ?? "");
      const toolName = String(data.toolName ?? "unknown");
      let args = {};
      if (typeof data.arguments === "string") {
        try {
          args = JSON.parse(data.arguments);
        } catch {
          args = data.arguments;
        }
      } else if (data.arguments != null) {
        args = data.arguments;
      }
      tracker.trackStart(toolCallId, toolName, args);
      const toolStartEvent = { type: "tool_call_start", toolCallId, toolName, args };
      const events = [];
      if (thinkingTracker.endThinking()) {
        events.push({ type: "thinking_end" });
      }
      thinkingTracker.reset();
      events.push(toolStartEvent);
      return events.length === 1 ? events[0] : events;
    }
    case "tool.execution_complete": {
      const toolCallId = String(data.toolCallId ?? "");
      const info = tracker.getInfo(toolCallId);
      const rawResult = data.result;
      const result = (rawResult && typeof rawResult === "object" && "content" in rawResult ? rawResult.content : rawResult) ?? null;
      return {
        type: "tool_call_end",
        toolCallId,
        toolName: info?.toolName ?? "unknown",
        result
      };
    }
    case "assistant.usage":
      return {
        type: "usage_update",
        promptTokens: Number(data.inputTokens ?? 0),
        completionTokens: Number(data.outputTokens ?? 0)
      };
    case "session.error":
      console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
      {
        const errorMsg = String(data.message ?? "Unknown error");
        const code = classifyAgentError(errorMsg);
        return {
          type: "error",
          error: errorMsg,
          recoverable: isRecoverableErrorCode(code),
          code
        };
      }
    case "assistant.message": {
      const doneEvent = {
        type: "done",
        finalOutput: null,
        streamed: true
      };
      if (thinkingTracker.endThinking()) {
        return [{ type: "thinking_end" }, doneEvent];
      }
      return doneEvent;
    }
    default:
      return null;
  }
}
var CopilotAgent = class extends BaseAgent {
  backendName = "copilot";
  getClient;
  sdkTools;
  sessionConfig;
  sendAndWaitTimeout;
  isPersistent;
  persistentSession = null;
  _sessionId;
  _persistentModel;
  activeSession = null;
  _resumeSessionId;
  _toolsReady = null;
  constructor(config, getClient, sendAndWaitTimeout, resumeSessionId) {
    super(config);
    this.getClient = getClient;
    this.sendAndWaitTimeout = sendAndWaitTimeout;
    this.isPersistent = config.sessionMode === "persistent";
    this.sdkTools = mapToolsToSDK(config.tools ?? []);
    this.sessionConfig = {
      model: config.model,
      tools: this.sdkTools,
      systemMessage: {
        mode: config.systemMessageMode ?? "append",
        content: config.systemPrompt
      },
      onPermissionRequest: buildPermissionHandler(config),
      onUserInputRequest: buildUserInputHandler(config),
      ...config.availableTools?.length ? { availableTools: config.availableTools } : {}
    };
    this._toolsReady = this._initToolsAsync(config);
    this._resumeSessionId = resumeSessionId;
  }
  /** Pre-convert Zod schemas to JSON Schema asynchronously.
   *  Updates sdkTools and sessionConfig.tools before first session creation. */
  async _initToolsAsync(config) {
    this.sdkTools = await mapToolsToSDKAsync(config.tools ?? []);
    this.sessionConfig.tools = this.sdkTools;
  }
  get sessionId() {
    return this._sessionId;
  }
  async interrupt() {
    if (this.activeSession) {
      this.activeSession.abort().catch(() => {
      });
    }
    this.abort();
  }
  emitSessionInfo(sessionId) {
    const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
    const transcriptPath = home ? `${home}/.copilot/session-state/${sessionId}/events.jsonl` : void 0;
    return { type: "session_info", sessionId, transcriptPath, backend: "copilot" };
  }
  clearPersistentSession() {
    if (this.isPersistent) {
      this.persistentSession?.destroy().catch(() => {
      });
      this.persistentSession = null;
      this._sessionId = void 0;
      this._persistentModel = void 0;
    }
  }
  async getOrCreateSession(streaming, options) {
    if (this.isPersistent && this.persistentSession) {
      if (options.model !== this._persistentModel) {
        this.persistentSession.destroy().catch(() => {
        });
        this.persistentSession = null;
        this._sessionId = void 0;
      } else {
        return { session: this.persistentSession, isNew: false };
      }
    }
    if (this._toolsReady) {
      await this._toolsReady;
      this._toolsReady = null;
    }
    const sessionConfig = { ...this.sessionConfig };
    sessionConfig.model = options.model;
    const resolvedTools = this.resolveTools(options);
    if (options?.tools) {
      sessionConfig.tools = mapToolsToSDK(resolvedTools);
    }
    const client = await this.getClient();
    if (this._resumeSessionId) {
      const storedId = this._resumeSessionId;
      this._resumeSessionId = void 0;
      try {
        const session2 = await client.resumeSession(storedId, {
          ...sessionConfig,
          streaming: this.isPersistent ? true : streaming
        });
        if (this.isPersistent) {
          this.persistentSession = session2;
          this._sessionId = session2.sessionId;
          this._persistentModel = options.model;
        }
        return { session: session2, isNew: false };
      } catch {
      }
    }
    const session = await client.createSession({
      ...sessionConfig,
      streaming: this.isPersistent ? true : streaming
    });
    if (this.isPersistent) {
      this.persistentSession = session;
      this._sessionId = session.sessionId;
      this._persistentModel = options.model;
    }
    return { session, isNew: true };
  }
  // ─── executeRun ─────────────────────────────────────────────────
  async executeRun(messages, options, signal) {
    this.checkAbort(signal);
    const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
    this.activeSession = session;
    const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
    const tracker = new ToolCallTracker();
    const toolCalls = [];
    let usage;
    const unsubscribe = session.on((event) => {
      if (event.type === "tool.execution_start") {
        tracker.trackStart(
          String(event.data.toolCallId ?? ""),
          String(event.data.toolName ?? "unknown"),
          event.data.arguments ?? {}
        );
      }
      if (event.type === "tool.execution_complete") {
        const info = tracker.getInfo(String(event.data.toolCallId ?? ""));
        const resultContent = event.data.result?.content;
        toolCalls.push({
          toolName: info?.toolName ?? "unknown",
          args: info?.args ?? {},
          result: resultContent ?? null,
          approved: Boolean(event.data.success ?? true)
        });
      }
      if (event.type === "assistant.usage") {
        usage = {
          promptTokens: Number(event.data.inputTokens ?? 0),
          completionTokens: Number(event.data.outputTokens ?? 0)
        };
      }
    });
    const onAbort = () => {
      session.abort().catch(() => {
      });
    };
    signal.addEventListener("abort", onAbort, { once: true });
    try {
      const response = this.sendAndWaitTimeout !== void 0 ? await session.sendAndWait({ prompt }, this.sendAndWaitTimeout) : await session.sendAndWait({ prompt });
      const output = response?.data?.content ?? null;
      return {
        output,
        structuredOutput: void 0,
        toolCalls,
        messages: [
          ...messages,
          ...output !== null ? [{ role: "assistant", content: output }] : []
        ],
        usage
      };
    } catch (error) {
      this.clearPersistentSession();
      throw error;
    } finally {
      this.activeSession = null;
      signal.removeEventListener("abort", onAbort);
      unsubscribe();
      tracker.clear();
      if (!this.isPersistent) {
        session.destroy().catch(() => {
        });
      }
    }
  }
  // ─── executeRunStructured ───────────────────────────────────────
  async executeRunStructured(messages, schema, options, signal) {
    const jsonSchema = zodToJsonSchema(schema.schema);
    const instruction = `

You MUST respond with ONLY valid JSON matching this schema:
` + JSON.stringify(jsonSchema, null, 2);
    const augmented = [...messages];
    const lastIdx = augmented.length - 1;
    if (lastIdx >= 0 && augmented[lastIdx].role === "user") {
      const orig = augmented[lastIdx];
      augmented[lastIdx] = {
        role: "user",
        content: getTextContent(orig.content) + instruction
      };
    }
    const result = await this.executeRun(augmented, options, signal);
    let structuredOutput;
    if (result.output) {
      try {
        const jsonMatch = result.output.match(
          /```(?:json)?\s*([\s\S]*?)```/
        );
        const raw = jsonMatch ? jsonMatch[1].trim() : result.output.trim();
        structuredOutput = schema.schema.parse(JSON.parse(raw));
      } catch {
      }
    }
    return {
      ...result,
      structuredOutput
    };
  }
  // ─── executeStream ──────────────────────────────────────────────
  async *executeStream(messages, options, signal) {
    this.checkAbort(signal);
    const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
    this.activeSession = session;
    if (isNewSession) {
      yield this.emitSessionInfo(session.sessionId);
    }
    const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
    const tracker = new ToolCallTracker();
    const thinkingTracker = new ThinkingTracker();
    const queue = [];
    let notify = null;
    const push = (item) => {
      queue.push(item);
      if (notify) {
        notify();
        notify = null;
      }
    };
    const waitForItem = () => new Promise((resolve) => {
      notify = resolve;
    });
    const unsubscribe = session.on((event) => {
      const mapped = mapSessionEvent(event, tracker, thinkingTracker);
      if (mapped) {
        if (Array.isArray(mapped)) {
          for (const e of mapped) push({ event: e });
        } else {
          push({ event: mapped });
        }
      }
      if (event.type === "session.idle") {
        if (thinkingTracker.endThinking()) {
          push({ event: { type: "thinking_end" } });
        }
        push({ done: true });
      } else if (event.type === "session.error") {
        console.error("[copilot] session.error:", JSON.stringify(event.data));
        push({
          error: new Error(
            String(event.data.message ?? "Session error")
          )
        });
      }
    });
    const onAbort = () => {
      session.abort().catch(() => {
      });
      push({ error: new AbortError() });
    };
    signal.addEventListener("abort", onAbort, { once: true });
    try {
      await session.send({ prompt });
      while (true) {
        while (queue.length === 0) await waitForItem();
        const item = queue.shift();
        if ("done" in item) break;
        if ("error" in item) {
          this.clearPersistentSession();
          throw item.error;
        }
        yield item.event;
      }
    } catch (error) {
      this.clearPersistentSession();
      throw error;
    } finally {
      this.activeSession = null;
      signal.removeEventListener("abort", onAbort);
      unsubscribe();
      tracker.clear();
      if (!this.isPersistent) {
        session.destroy().catch(() => {
        });
      }
    }
  }
  // ─── dispose ────────────────────────────────────────────────────
  dispose() {
    if (this.persistentSession) {
      this.persistentSession.destroy().catch(() => {
      });
      this.persistentSession = null;
      this._sessionId = void 0;
    }
    super.dispose();
  }
};
function withTimeout(promise, ms, message) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
    promise.then(
      (val) => {
        clearTimeout(timer);
        resolve(val);
      },
      (err) => {
        clearTimeout(timer);
        reject(err);
      }
    );
  });
}
var CopilotAgentService = class {
  name = "copilot";
  client = null;
  clientPromise = null;
  disposed = false;
  options;
  constructor(options) {
    this.options = options;
  }
  async ensureClient() {
    if (this.disposed) throw new DisposedError("CopilotAgentService");
    if (this.client) return this.client;
    if (this.clientPromise) return this.clientPromise;
    this.clientPromise = (async () => {
      try {
        const sdk = await loadSDK();
        const client = new sdk.CopilotClient({
          cliPath: this.options.cliPath,
          cwd: this.options.workingDirectory,
          useStdio: true,
          autoStart: false,
          autoRestart: true,
          logLevel: "error",
          githubToken: this.options.githubToken,
          useLoggedInUser: this.options.useLoggedInUser ?? !this.options.githubToken,
          ...this.options.cliArgs ? { cliArgs: this.options.cliArgs } : {},
          ...this.options.env ? { env: { ...process.env, ...this.options.env } } : {}
        });
        const startupTimeout = this.options.startupTimeoutMs ?? 3e4;
        await withTimeout(client.start(), startupTimeout, "CLI startup timed out");
        const auth = await withTimeout(
          client.getAuthStatus(),
          startupTimeout,
          "Auth status check timed out \u2014 token may be expired"
        );
        if (!auth.isAuthenticated) {
          await client.stop();
          throw new SubprocessError(
            "Not authenticated with GitHub Copilot. Run 'copilot auth login' or set GITHUB_TOKEN."
          );
        }
        this.client = client;
        return client;
      } catch (e) {
        this.clientPromise = null;
        throw e;
      }
    })();
    return this.clientPromise;
  }
  createAgent(config) {
    if (this.disposed) throw new DisposedError("CopilotAgentService");
    return new CopilotAgent(config, () => this.ensureClient(), this.options.timeout, this.options.resumeSessionId);
  }
  async listModels() {
    const client = await this.ensureClient();
    const models = await client.listModels();
    return models.map((m) => ({
      id: m.id,
      name: m.name,
      provider: "copilot",
      ...m.capabilities?.limits?.max_context_window_tokens != null && {
        contextWindow: m.capabilities.limits.max_context_window_tokens
      }
    }));
  }
  async validate() {
    const errors = [];
    try {
      const client = await this.ensureClient();
      const auth = await client.getAuthStatus();
      if (!auth.isAuthenticated) {
        errors.push(
          "Not authenticated with GitHub Copilot. Run 'copilot auth login'."
        );
      }
    } catch (e) {
      errors.push(
        `Failed to connect to Copilot CLI: ${e instanceof Error ? e.message : String(e)}`
      );
    }
    return { valid: errors.length === 0, errors };
  }
  async dispose() {
    if (this.disposed) return;
    this.disposed = true;
    if (this.clientPromise) {
      try {
        await this.clientPromise;
      } catch {
      }
    }
    if (this.client) {
      await this.client.stop();
      this.client = null;
    }
    this.clientPromise = null;
  }
};
function createCopilotService(options) {
  return new CopilotAgentService(options);
}

export { _injectSDK, _resetSDK, createCopilotService };
//# sourceMappingURL=copilot.js.map
//# sourceMappingURL=copilot.js.map