// src/interfaces/ILogger.ts
var NoopLogger = class {
  debug() {
  }
  info() {
  }
  warn() {
  }
  error() {
  }
  child() {
    return this;
  }
};

// src/agent/agent-loop.ts
var DEFAULT_AGENT_LOOP_OPTIONS = {
  maxIterations: 10,
  maxTokens: Infinity,
  maxCostUsd: Infinity,
  cacheTtlSeconds: 300,
  cacheKeyPrefix: "agent:tool:"
};
function mapFinishReason(reason) {
  switch (reason) {
    case "stop":
      return "stop";
    case "length":
      return "length";
    case "content_filter":
      return "content_filter";
    case "tool_calls":
      return "stop";
    // shouldn't reach here in normal flow
    case "error":
    default:
      return "error";
  }
}
async function executeToolCall(toolCall, executorMap, cache, cacheKeyPrefix, cacheTtlSeconds) {
  const start = Date.now();
  const executor = executorMap.get(toolCall.function.name);
  if (!executor) {
    return {
      toolCall,
      result: `Error: Unknown tool "${toolCall.function.name}"`,
      success: false,
      error: `Unknown tool: ${toolCall.function.name}`,
      durationMs: Date.now() - start,
      cached: false
    };
  }
  if (cache) {
    const cacheKey = cacheKeyPrefix + toolCall.function.name + ":" + toolCall.function.arguments;
    try {
      const cached = await cache.get(cacheKey);
      if (cached !== null) {
        return {
          toolCall,
          result: cached,
          success: true,
          durationMs: Date.now() - start,
          cached: true
        };
      }
    } catch {
    }
  }
  try {
    const parsed = JSON.parse(toolCall.function.arguments);
    const result = await executor.execute(parsed, toolCall);
    if (cache) {
      const cacheKey = cacheKeyPrefix + toolCall.function.name + ":" + toolCall.function.arguments;
      await cache.set(cacheKey, result, cacheTtlSeconds).catch(() => {
      });
    }
    return {
      toolCall,
      result,
      success: true,
      durationMs: Date.now() - start,
      cached: false
    };
  } catch (err) {
    const errorMsg = err instanceof Error ? err.message : String(err);
    return {
      toolCall,
      result: `Error: ${errorMsg}`,
      success: false,
      error: errorMsg,
      durationMs: Date.now() - start,
      cached: false
    };
  }
}
async function runAgentLoop(options) {
  const opts = { ...DEFAULT_AGENT_LOOP_OPTIONS, ...options };
  const logger = opts.logger ?? new NoopLogger();
  const toolDefs = opts.tools.map((t) => t.definition);
  const executorMap = /* @__PURE__ */ new Map();
  for (const tool of opts.tools) {
    executorMap.set(tool.definition.function.name, tool);
  }
  const messages = [...opts.messages];
  const iterations = [];
  const cumulative = {
    promptTokens: 0,
    completionTokens: 0,
    totalTokens: 0,
    estimatedCostUsd: 0,
    iterations: 0,
    toolCallCount: 0
  };
  let lastResponse;
  let stopReason = "stop";
  for (let i = 1; i <= opts.maxIterations; i++) {
    if (opts.signal?.aborted) {
      stopReason = "aborted";
      break;
    }
    if (cumulative.totalTokens >= opts.maxTokens) {
      stopReason = "max_tokens";
      break;
    }
    if (cumulative.estimatedCostUsd >= opts.maxCostUsd) {
      stopReason = "max_cost";
      break;
    }
    logger.debug("Agent loop iteration", { iteration: i });
    const chatRequest = {
      ...opts.chatRequestOverrides,
      messages,
      tools: toolDefs.length > 0 ? toolDefs : void 0
    };
    const response = await opts.ai.chat(chatRequest);
    lastResponse = response;
    cumulative.promptTokens += response.usage.promptTokens;
    cumulative.completionTokens += response.usage.completionTokens;
    cumulative.totalTokens += response.usage.totalTokens;
    cumulative.estimatedCostUsd += response.usage.estimatedCostUsd;
    cumulative.iterations = i;
    const assistantMessage = response.choices[0]?.message;
    if (!assistantMessage) {
      stopReason = "error";
      break;
    }
    messages.push(assistantMessage);
    if (response.finishReason !== "tool_calls") {
      stopReason = mapFinishReason(response.finishReason);
      const event2 = {
        iteration: i,
        response,
        toolCalls: [],
        cumulativeUsage: { ...cumulative },
        messages: [...messages]
      };
      iterations.push(event2);
      await opts.onIteration?.(event2);
      break;
    }
    const toolCalls = assistantMessage.toolCalls ?? [];
    cumulative.toolCallCount += toolCalls.length;
    const toolResults = await Promise.all(
      toolCalls.map(
        (tc) => executeToolCall(
          tc,
          executorMap,
          opts.cache,
          opts.cacheKeyPrefix,
          opts.cacheTtlSeconds
        )
      )
    );
    for (const tr of toolResults) {
      messages.push({
        role: "tool",
        content: tr.result,
        toolCallId: tr.toolCall.id
      });
    }
    const event = {
      iteration: i,
      response,
      toolCalls: toolResults,
      cumulativeUsage: { ...cumulative },
      messages: [...messages]
    };
    iterations.push(event);
    const callbackResult = await opts.onIteration?.(event);
    if (callbackResult === false) {
      stopReason = "callback_abort";
      break;
    }
    if (i === opts.maxIterations) {
      stopReason = "max_iterations";
    }
  }
  return {
    response: lastResponse,
    messages,
    stopReason,
    iterations,
    usage: cumulative
  };
}

// src/agent/agent-tracer.ts
function createAgentTracer(options) {
  const { tracing, agentId } = options;
  return {
    async traceLoop(fn) {
      return tracing.withSpanAsync(
        "agent.loop",
        async (span) => {
          span.setAttribute("agent.id", agentId);
          return fn(span);
        },
        { kind: "internal" }
      );
    },
    async traceIteration(iteration, fn) {
      return tracing.withSpanAsync(
        "agent.iteration",
        async (span) => {
          span.setAttributes({
            "agent.id": agentId,
            "agent.iteration": iteration
          });
          return fn(span);
        },
        { kind: "internal" }
      );
    },
    async traceToolCall(toolName, fn) {
      return tracing.withSpanAsync(
        `agent.tool.${toolName}`,
        async (span) => {
          span.setAttributes({
            "agent.id": agentId,
            "agent.tool.name": toolName
          });
          const result = await fn(span);
          span.setAttributes({
            "agent.tool.duration_ms": result.durationMs,
            "agent.tool.success": result.success,
            "agent.tool.cached": result.cached
          });
          if (result.error) {
            span.setAttribute("agent.tool.error", result.error);
          }
          return result;
        },
        { kind: "internal" }
      );
    },
    traceDecision(span, attrs) {
      span.setAttributes({
        "agent.decision.finish_reason": attrs.finishReason,
        "agent.decision.tool_count": attrs.toolCallCount,
        "agent.decision.total_tokens": attrs.cumulativeUsage.totalTokens,
        "agent.decision.cost_usd": attrs.cumulativeUsage.estimatedCostUsd,
        "agent.decision.iterations": attrs.cumulativeUsage.iterations
      });
      span.addEvent("agent.decision", {
        finish_reason: attrs.finishReason,
        tool_count: attrs.toolCallCount
      });
    }
  };
}

// src/agent/agent-usage.ts
function createAgentUsageTracker(options) {
  const { usage, agentId, tenantId, userId } = options;
  return {
    async recordIteration(params) {
      return usage.record({
        tenantId,
        userId,
        category: params.category ?? "chat",
        provider: params.provider,
        model: params.model,
        inputTokens: params.usage.promptTokens,
        outputTokens: params.usage.completionTokens,
        totalTokens: params.usage.totalTokens,
        costUsd: params.usage.estimatedCostUsd,
        latencyMs: params.latencyMs ?? 0,
        success: params.success ?? true,
        error: params.error,
        metadata: { agentId }
      });
    },
    createBudgetCallback(budget) {
      return async (event) => {
        const response = event.response;
        await usage.record({
          tenantId,
          userId,
          category: "chat",
          provider: response.provider,
          model: response.model,
          inputTokens: response.usage.promptTokens,
          outputTokens: response.usage.completionTokens,
          totalTokens: response.usage.totalTokens,
          costUsd: response.usage.estimatedCostUsd,
          latencyMs: 0,
          success: true,
          metadata: { agentId, iteration: event.iteration }
        });
        if (budget) {
          if (budget.maxCostUsd !== void 0 && event.cumulativeUsage.estimatedCostUsd >= budget.maxCostUsd) {
            return false;
          }
          if (budget.maxTokens !== void 0 && event.cumulativeUsage.totalTokens >= budget.maxTokens) {
            return false;
          }
        }
      };
    }
  };
}

// src/agent/traced-ai.ts
function createTracedAI(options) {
  const { ai, tracing } = options;
  return {
    async chat(request) {
      return tracing.withSpanAsync(
        "ai.chat",
        async (span) => {
          const start = Date.now();
          if (request.model) {
            span.setAttribute("ai.model", request.model);
          }
          try {
            const response = await ai.chat(request);
            span.setAttributes({
              "ai.provider": response.provider,
              "ai.model": response.model,
              "ai.input_tokens": response.usage.promptTokens,
              "ai.output_tokens": response.usage.completionTokens,
              "ai.total_tokens": response.usage.totalTokens,
              "ai.cost_usd": response.usage.estimatedCostUsd,
              "ai.finish_reason": response.finishReason,
              "ai.latency_ms": Date.now() - start
            });
            span.setStatus({ code: "ok" });
            return response;
          } catch (err) {
            span.setAttributes({
              "ai.latency_ms": Date.now() - start
            });
            if (err instanceof Error) {
              span.recordException(err);
            }
            span.setStatus({
              code: "error",
              message: err instanceof Error ? err.message : String(err)
            });
            throw err;
          }
        },
        { kind: "client" }
      );
    },
    async *chatStream(request) {
      yield* ai.chatStream(request);
    },
    async chatWithCallback(request, callback) {
      return tracing.withSpanAsync(
        "ai.chatWithCallback",
        async (span) => {
          const start = Date.now();
          if (request.model) {
            span.setAttribute("ai.model", request.model);
          }
          try {
            const response = await ai.chatWithCallback(request, callback);
            span.setAttributes({
              "ai.provider": response.provider,
              "ai.model": response.model,
              "ai.input_tokens": response.usage.promptTokens,
              "ai.output_tokens": response.usage.completionTokens,
              "ai.total_tokens": response.usage.totalTokens,
              "ai.cost_usd": response.usage.estimatedCostUsd,
              "ai.finish_reason": response.finishReason,
              "ai.latency_ms": Date.now() - start
            });
            span.setStatus({ code: "ok" });
            return response;
          } catch (err) {
            span.setAttributes({
              "ai.latency_ms": Date.now() - start
            });
            if (err instanceof Error) {
              span.recordException(err);
            }
            span.setStatus({
              code: "error",
              message: err instanceof Error ? err.message : String(err)
            });
            throw err;
          }
        },
        { kind: "client" }
      );
    },
    // Pass-through methods (no tracing needed for non-chat operations in v1)
    async complete(request) {
      return ai.complete(request);
    },
    async *completeStream(request) {
      yield* ai.completeStream(request);
    },
    async embed(request) {
      return ai.embed(request);
    },
    async similarity(text1, text2, model) {
      return ai.similarity(text1, text2, model);
    },
    async listModels() {
      return ai.listModels();
    },
    async getModel(modelId) {
      return ai.getModel(modelId);
    },
    async supportsCapability(modelId, capability) {
      return ai.supportsCapability(modelId, capability);
    },
    async estimateTokens(text, model) {
      return ai.estimateTokens(text, model);
    },
    async estimateCost(request) {
      return ai.estimateCost(request);
    },
    async healthCheck() {
      return ai.healthCheck();
    }
  };
}

// src/interfaces/IAI.ts
var AIErrorMessages = {
  invalid_request: "The request was invalid or malformed",
  authentication_error: "Authentication failed - check your API key",
  rate_limit_exceeded: "Rate limit exceeded - try again later",
  quota_exceeded: "Usage quota exceeded for this billing period",
  model_not_found: "The specified model was not found",
  context_length_exceeded: "Input exceeds the model context length",
  content_filter: "Content was filtered due to policy violations",
  server_error: "The AI provider encountered an internal error",
  timeout: "The request timed out",
  network_error: "Network error connecting to AI provider",
  provider_unavailable: "The AI provider is currently unavailable",
  unknown: "An unknown error occurred"
};
function createAIError(code, message, options) {
  const error = new Error(message || AIErrorMessages[code]);
  error.name = "AIError";
  error.code = code;
  error.provider = options?.provider;
  error.model = options?.model;
  error.statusCode = options?.statusCode;
  error.retryable = options?.retryable ?? [
    "rate_limit_exceeded",
    "server_error",
    "timeout",
    "network_error"
  ].includes(code);
  error.retryAfterMs = options?.retryAfterMs;
  if (options?.cause) {
    error.cause = options.cause;
  }
  return error;
}

// src/adapters/ollama/OllamaAdapter.ts
var OllamaAdapter = class {
  baseUrl;
  defaultModel;
  defaultEmbeddingModel;
  timeoutMs;
  constructor(config) {
    this.baseUrl = (config?.baseUrl ?? "http://localhost:11434").replace(
      /\/$/,
      ""
    );
    this.defaultModel = config?.defaultModel ?? "qwen2.5:3b";
    this.defaultEmbeddingModel = config?.defaultEmbeddingModel ?? "nomic-embed-text";
    this.timeoutMs = config?.timeoutMs ?? 12e4;
  }
  async chat(request) {
    const model = request.model ?? this.defaultModel;
    const ollamaReq = {
      model,
      messages: request.messages.map((m) => ({
        role: m.role === "tool" ? "assistant" : m.role,
        content: m.content
      })),
      stream: false,
      options: {
        temperature: request.temperature,
        num_predict: request.maxTokens,
        top_p: request.topP,
        stop: request.stop ? Array.isArray(request.stop) ? request.stop : [request.stop] : void 0
      }
    };
    const response = await this.fetch(
      "/api/chat",
      ollamaReq
    );
    const usage = {
      promptTokens: response.prompt_eval_count ?? 0,
      completionTokens: response.eval_count ?? 0,
      totalTokens: (response.prompt_eval_count ?? 0) + (response.eval_count ?? 0),
      estimatedCostUsd: 0
      // Local — zero cost
    };
    return {
      id: `ollama-${Date.now()}`,
      model: response.model ?? model,
      provider: "custom",
      choices: [
        {
          index: 0,
          message: {
            role: "assistant",
            content: response.message.content
          },
          finishReason: "stop"
        }
      ],
      usage,
      created: /* @__PURE__ */ new Date(),
      finishReason: "stop"
    };
  }
  async *chatStream(request) {
    const model = request.model ?? this.defaultModel;
    const ollamaReq = {
      model,
      messages: request.messages.map((m) => ({
        role: m.role === "tool" ? "assistant" : m.role,
        content: m.content
      })),
      stream: true,
      options: {
        temperature: request.temperature,
        num_predict: request.maxTokens,
        top_p: request.topP
      }
    };
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), this.timeoutMs);
    try {
      const res = await fetch(`${this.baseUrl}/api/chat`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(ollamaReq),
        signal: controller.signal
      });
      if (!res.ok) {
        throw createAIError("server_error", `Ollama error: ${res.status}`);
      }
      const reader = res.body?.getReader();
      if (!reader) return;
      const decoder = new TextDecoder();
      let buffer = "";
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split("\n");
        buffer = lines.pop() ?? "";
        for (const line of lines) {
          if (!line.trim()) continue;
          const chunk = JSON.parse(line);
          yield {
            id: `ollama-stream-${Date.now()}`,
            model,
            provider: "custom",
            delta: {
              content: chunk.message?.content ?? ""
            },
            finishReason: chunk.done ? "stop" : void 0
          };
        }
      }
    } finally {
      clearTimeout(timer);
    }
  }
  async chatWithCallback(request, callback) {
    let fullContent = "";
    for await (const chunk of this.chatStream(request)) {
      await callback(chunk);
      if (chunk.delta.content) {
        fullContent += chunk.delta.content;
      }
    }
    const model = request.model ?? this.defaultModel;
    return {
      id: `ollama-${Date.now()}`,
      model,
      provider: "custom",
      choices: [
        {
          index: 0,
          message: { role: "assistant", content: fullContent },
          finishReason: "stop"
        }
      ],
      usage: {
        promptTokens: 0,
        completionTokens: 0,
        totalTokens: 0,
        estimatedCostUsd: 0
      },
      created: /* @__PURE__ */ new Date(),
      finishReason: "stop"
    };
  }
  async complete(request) {
    const response = await this.chat({
      messages: [{ role: "user", content: request.prompt }],
      model: request.model,
      temperature: request.temperature,
      maxTokens: request.maxTokens
    });
    return {
      id: response.id,
      model: response.model,
      provider: "custom",
      text: response.choices[0]?.message.content ?? "",
      usage: response.usage,
      created: response.created,
      finishReason: response.finishReason
    };
  }
  async *completeStream(request) {
    yield* this.chatStream({
      messages: [{ role: "user", content: request.prompt }],
      model: request.model,
      temperature: request.temperature,
      maxTokens: request.maxTokens
    });
  }
  async embed(request) {
    const model = request.model ?? this.defaultEmbeddingModel;
    const response = await this.fetch("/api/embed", {
      model,
      input: request.input
    });
    return {
      id: `ollama-emb-${Date.now()}`,
      model,
      provider: "custom",
      embeddings: response.embeddings,
      usage: {
        promptTokens: 0,
        completionTokens: 0,
        totalTokens: 0,
        estimatedCostUsd: 0
      },
      created: /* @__PURE__ */ new Date()
    };
  }
  async similarity(text1, text2, model) {
    const response = await this.embed({ input: [text1, text2], model });
    const [a, b] = response.embeddings;
    if (!a || !b) return 0;
    let dot = 0, normA = 0, normB = 0;
    for (let i = 0; i < a.length; i++) {
      dot += a[i] * b[i];
      normA += a[i] * a[i];
      normB += b[i] * b[i];
    }
    return dot / (Math.sqrt(normA) * Math.sqrt(normB));
  }
  async listModels() {
    try {
      const response = await this.fetch(
        "/api/tags",
        null,
        "GET"
      );
      return response.models.map((m) => ({
        modelId: m.name,
        provider: "custom",
        capabilities: ["chat", "completion"],
        maxContextTokens: 4096,
        maxOutputTokens: 2048,
        inputCostPer1K: 0,
        outputCostPer1K: 0,
        supportsStreaming: true,
        supportsTools: false,
        supportsVision: false
      }));
    } catch {
      return [];
    }
  }
  async getModel(modelId) {
    const models = await this.listModels();
    return models.find((m) => m.modelId === modelId) ?? null;
  }
  async supportsCapability(modelId, capability) {
    const model = await this.getModel(modelId);
    return model?.capabilities.includes(capability) ?? false;
  }
  async estimateTokens(text, _model) {
    return Math.ceil(text.length / 4);
  }
  async estimateCost() {
    return 0;
  }
  async healthCheck() {
    try {
      const start = Date.now();
      await this.fetch("/api/tags", null, "GET");
      const latency = Date.now() - start;
      return {
        healthy: true,
        providers: {
          custom: { available: true, latencyMs: latency },
          openai: { available: false, error: "Not configured" },
          anthropic: { available: false, error: "Not configured" },
          google: { available: false, error: "Not configured" },
          azure: { available: false, error: "Not configured" },
          bedrock: { available: false, error: "Not configured" }
        }
      };
    } catch (err) {
      return {
        healthy: false,
        providers: {
          custom: {
            available: false,
            error: err instanceof Error ? err.message : "Ollama unavailable"
          },
          openai: { available: false, error: "Not configured" },
          anthropic: { available: false, error: "Not configured" },
          google: { available: false, error: "Not configured" },
          azure: { available: false, error: "Not configured" },
          bedrock: { available: false, error: "Not configured" }
        }
      };
    }
  }
  async fetch(path, body, method = "POST") {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), this.timeoutMs);
    try {
      const res = await fetch(`${this.baseUrl}${path}`, {
        method,
        headers: body ? { "Content-Type": "application/json" } : void 0,
        body: body ? JSON.stringify(body) : void 0,
        signal: controller.signal
      });
      if (!res.ok) {
        const text = await res.text().catch(() => "");
        throw createAIError(
          "server_error",
          `Ollama ${method} ${path}: ${res.status} ${text}`
        );
      }
      return await res.json();
    } finally {
      clearTimeout(timer);
    }
  }
};
export {
  DEFAULT_AGENT_LOOP_OPTIONS,
  OllamaAdapter,
  createAgentTracer,
  createAgentUsageTracker,
  createTracedAI,
  runAgentLoop
};
//# sourceMappingURL=agents.mjs.map