// src/evaluation/content.ts
var CONTENT_TYPES = /* @__PURE__ */ new Set(["text", "image", "file"]);
function isContent(value) {
  if (!value || typeof value !== "object") return false;
  const v = value;
  return typeof v.type === "string" && CONTENT_TYPES.has(v.type);
}
function isContentArray(value) {
  return Array.isArray(value) && value.length > 0 && value.every(isContent);
}
function getTextContent(content) {
  if (content == null) return "";
  if (typeof content === "string") return content;
  if (!Array.isArray(content)) return "";
  const parts = [];
  for (const block of content) {
    if (block.type === "text") {
      parts.push(block.text);
    }
  }
  return parts.join("\n");
}

// src/evaluation/types.ts
var TEST_MESSAGE_ROLE_VALUES = ["system", "user", "assistant", "tool"];
var TEST_MESSAGE_ROLES = TEST_MESSAGE_ROLE_VALUES;
var TEST_MESSAGE_ROLE_SET = new Set(TEST_MESSAGE_ROLE_VALUES);
function isTestMessageRole(value) {
  return typeof value === "string" && TEST_MESSAGE_ROLE_SET.has(value);
}
function isJsonObject(value) {
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
    return false;
  }
  return Object.values(value).every(isJsonValue);
}
function isJsonValue(value) {
  if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
    return true;
  }
  if (Array.isArray(value)) {
    return value.every(isJsonValue);
  }
  if (typeof value === "object") {
    return isJsonObject(value);
  }
  return false;
}
function isTestMessage(value) {
  if (typeof value !== "object" || value === null) {
    return false;
  }
  const candidate = value;
  if (!isTestMessageRole(candidate.role)) {
    return false;
  }
  if (typeof candidate.content === "string") {
    return true;
  }
  if (Array.isArray(candidate.content) && candidate.content.every(isJsonObject)) {
    return true;
  }
  if (Array.isArray(candidate.tool_calls) && candidate.tool_calls.length > 0) {
    return true;
  }
  if (isJsonObject(candidate.content)) {
    return true;
  }
  return false;
}
var EVALUATOR_KIND_VALUES = [
  "code-grader",
  "llm-grader",
  "rubric",
  "composite",
  "tool-trajectory",
  "field-accuracy",
  "latency",
  "cost",
  "token-usage",
  "execution-metrics",
  "skill-trigger",
  "contains",
  "contains-any",
  "contains-all",
  "icontains",
  "icontains-any",
  "icontains-all",
  "starts-with",
  "ends-with",
  "regex",
  "is-json",
  "equals",
  "rubrics",
  "inline-assert"
];
var EVALUATOR_KIND_SET = new Set(EVALUATOR_KIND_VALUES);
function isEvaluatorKind(value) {
  return typeof value === "string" && EVALUATOR_KIND_SET.has(value);
}

// src/evaluation/file-utils.ts
import { constants } from "node:fs";
import { access, readFile } from "node:fs/promises";
import path from "node:path";
async function fileExists(filePath) {
  try {
    await access(filePath, constants.F_OK);
    return true;
  } catch {
    return false;
  }
}
function normalizeLineEndings(content) {
  return content.replace(/\r\n/g, "\n");
}
async function readTextFile(filePath) {
  const content = await readFile(filePath, "utf8");
  return normalizeLineEndings(content);
}
async function readJsonFile(filePath) {
  const content = await readFile(filePath, "utf8");
  return JSON.parse(content);
}
async function findGitRoot(startPath) {
  let currentDir = path.dirname(path.resolve(startPath));
  const root = path.parse(currentDir).root;
  while (currentDir !== root) {
    const gitPath = path.join(currentDir, ".git");
    if (await fileExists(gitPath)) {
      return currentDir;
    }
    const parentDir = path.dirname(currentDir);
    if (parentDir === currentDir) {
      break;
    }
    currentDir = parentDir;
  }
  return null;
}
function buildDirectoryChain(filePath, repoRoot) {
  const directories = [];
  const seen = /* @__PURE__ */ new Set();
  const boundary = path.resolve(repoRoot);
  let current = path.resolve(path.dirname(filePath));
  while (current !== void 0) {
    if (!seen.has(current)) {
      directories.push(current);
      seen.add(current);
    }
    if (current === boundary) {
      break;
    }
    const parent = path.dirname(current);
    if (parent === current) {
      break;
    }
    current = parent;
  }
  if (!seen.has(boundary)) {
    directories.push(boundary);
  }
  return directories;
}
function buildSearchRoots(evalPath, repoRoot) {
  const uniqueRoots = [];
  const addRoot = (root) => {
    const normalized = path.resolve(root);
    if (!uniqueRoots.includes(normalized)) {
      uniqueRoots.push(normalized);
    }
  };
  let currentDir = path.dirname(evalPath);
  let reachedBoundary = false;
  while (!reachedBoundary) {
    addRoot(currentDir);
    const parentDir = path.dirname(currentDir);
    if (currentDir === repoRoot || parentDir === currentDir) {
      reachedBoundary = true;
    } else {
      currentDir = parentDir;
    }
  }
  addRoot(repoRoot);
  addRoot(process.cwd());
  return uniqueRoots;
}
function trimLeadingSeparators(value) {
  const trimmed = value.replace(/^[/\\]+/, "");
  return trimmed.length > 0 ? trimmed : value;
}
async function resolveFileReference(rawValue, searchRoots) {
  const displayPath = trimLeadingSeparators(rawValue);
  const potentialPaths = [];
  if (path.isAbsolute(rawValue)) {
    potentialPaths.push(path.normalize(rawValue));
  }
  for (const base of searchRoots) {
    potentialPaths.push(path.resolve(base, displayPath));
  }
  const attempted = [];
  const seen = /* @__PURE__ */ new Set();
  for (const candidate of potentialPaths) {
    const absoluteCandidate = path.resolve(candidate);
    if (seen.has(absoluteCandidate)) {
      continue;
    }
    seen.add(absoluteCandidate);
    attempted.push(absoluteCandidate);
    if (await fileExists(absoluteCandidate)) {
      return { displayPath, resolvedPath: absoluteCandidate, attempted };
    }
  }
  return { displayPath, attempted };
}

// src/evaluation/providers/targets.ts
import path2 from "node:path";
import { z } from "zod";
var CliHealthcheckHttpInputSchema = z.object({
  url: z.string().min(1, "healthcheck URL is required"),
  timeout_seconds: z.number().positive().optional(),
  timeoutSeconds: z.number().positive().optional()
});
var CliHealthcheckCommandInputSchema = z.object({
  command: z.string().min(1, "healthcheck command is required"),
  cwd: z.string().optional(),
  timeout_seconds: z.number().positive().optional(),
  timeoutSeconds: z.number().positive().optional()
});
var CliHealthcheckInputSchema = z.union([
  CliHealthcheckHttpInputSchema,
  CliHealthcheckCommandInputSchema
]);
var CliTargetInputSchema = z.object({
  name: z.string().min(1, "target name is required"),
  provider: z.string().refine((p) => p.toLowerCase() === "cli", { message: "provider must be 'cli'" }),
  // Command - required
  command: z.string(),
  // Files format - optional
  files_format: z.string().optional(),
  filesFormat: z.string().optional(),
  attachments_format: z.string().optional(),
  attachmentsFormat: z.string().optional(),
  // Working directory - optional
  cwd: z.string().optional(),
  // Workspace template directory - optional (mutually exclusive with cwd)
  workspace_template: z.string().optional(),
  workspaceTemplate: z.string().optional(),
  // Timeout in seconds - optional
  timeout_seconds: z.number().positive().optional(),
  timeoutSeconds: z.number().positive().optional(),
  // Healthcheck configuration - optional
  healthcheck: CliHealthcheckInputSchema.optional(),
  // Verbose mode - optional
  verbose: z.boolean().optional(),
  cli_verbose: z.boolean().optional(),
  cliVerbose: z.boolean().optional(),
  // Keep temp files - optional
  keep_temp_files: z.boolean().optional(),
  keepTempFiles: z.boolean().optional(),
  keep_output_files: z.boolean().optional(),
  keepOutputFiles: z.boolean().optional(),
  // Common target fields
  grader_target: z.string().optional(),
  judge_target: z.string().optional(),
  // backward compat
  workers: z.number().int().min(1).optional(),
  provider_batching: z.boolean().optional(),
  providerBatching: z.boolean().optional()
});
var CliHealthcheckHttpSchema = z.object({
  url: z.string().min(1),
  timeoutMs: z.number().positive().optional()
}).strict();
var CliHealthcheckCommandSchema = z.object({
  command: z.string().min(1),
  cwd: z.string().optional(),
  timeoutMs: z.number().positive().optional()
}).strict();
var CliHealthcheckSchema = z.union([
  CliHealthcheckHttpSchema,
  CliHealthcheckCommandSchema
]);
var CliTargetConfigSchema = z.object({
  command: z.string().min(1),
  filesFormat: z.string().optional(),
  cwd: z.string().optional(),
  workspaceTemplate: z.string().optional(),
  timeoutMs: z.number().positive().optional(),
  healthcheck: CliHealthcheckSchema.optional(),
  verbose: z.boolean().optional(),
  keepTempFiles: z.boolean().optional()
}).strict();
function normalizeCliHealthcheck(input, env, targetName, evalFilePath) {
  const timeoutSeconds = input.timeout_seconds ?? input.timeoutSeconds;
  const timeoutMs = timeoutSeconds !== void 0 ? Math.floor(timeoutSeconds * 1e3) : void 0;
  if ("url" in input && input.url) {
    const url = resolveString(input.url, env, `${targetName} healthcheck URL`);
    return {
      url,
      timeoutMs
    };
  }
  if (!("command" in input) || !input.command) {
    throw new Error(
      `${targetName} healthcheck: Either 'command' or 'url' is required for healthcheck`
    );
  }
  const command = resolveString(input.command, env, `${targetName} healthcheck command`, true);
  let cwd = resolveOptionalString(input.cwd, env, `${targetName} healthcheck cwd`, {
    allowLiteral: true,
    optionalEnv: true
  });
  if (cwd && evalFilePath && !path2.isAbsolute(cwd)) {
    cwd = path2.resolve(path2.dirname(path2.resolve(evalFilePath)), cwd);
  }
  if (!cwd && evalFilePath) {
    cwd = path2.dirname(path2.resolve(evalFilePath));
  }
  return {
    command,
    cwd,
    timeoutMs
  };
}
function normalizeCliTargetInput(input, env, evalFilePath) {
  const targetName = input.name;
  const command = resolveString(input.command, env, `${targetName} CLI command`, true);
  const filesFormatSource = input.files_format ?? input.filesFormat ?? input.attachments_format ?? input.attachmentsFormat;
  const filesFormat = resolveOptionalLiteralString(filesFormatSource);
  const workspaceTemplateSource = input.workspace_template ?? input.workspaceTemplate;
  let workspaceTemplate = resolveOptionalString(
    workspaceTemplateSource,
    env,
    `${targetName} workspace template`,
    {
      allowLiteral: true,
      optionalEnv: true
    }
  );
  if (workspaceTemplate && evalFilePath && !path2.isAbsolute(workspaceTemplate)) {
    workspaceTemplate = path2.resolve(path2.dirname(path2.resolve(evalFilePath)), workspaceTemplate);
  }
  let cwd = resolveOptionalString(input.cwd, env, `${targetName} working directory`, {
    allowLiteral: true,
    optionalEnv: true
  });
  if (cwd && evalFilePath && !path2.isAbsolute(cwd)) {
    cwd = path2.resolve(path2.dirname(path2.resolve(evalFilePath)), cwd);
  }
  if (cwd && workspaceTemplate) {
    throw new Error(
      `${targetName}: 'cwd' and 'workspace_template' are mutually exclusive. Use 'cwd' to run in an existing directory, or 'workspace_template' to copy a template to a temp location.`
    );
  }
  if (!cwd && !workspaceTemplate && evalFilePath) {
    cwd = path2.dirname(path2.resolve(evalFilePath));
  }
  const timeoutSeconds = input.timeout_seconds ?? input.timeoutSeconds;
  const timeoutMs = timeoutSeconds !== void 0 ? Math.floor(timeoutSeconds * 1e3) : void 0;
  const verbose = resolveOptionalBoolean(input.verbose ?? input.cli_verbose ?? input.cliVerbose);
  const keepTempFiles = resolveOptionalBoolean(
    input.keep_temp_files ?? input.keepTempFiles ?? input.keep_output_files ?? input.keepOutputFiles
  );
  const healthcheck = input.healthcheck ? normalizeCliHealthcheck(input.healthcheck, env, targetName, evalFilePath) : void 0;
  return {
    command,
    filesFormat,
    cwd,
    workspaceTemplate,
    timeoutMs,
    healthcheck,
    verbose,
    keepTempFiles
  };
}
var CLI_PLACEHOLDERS = /* @__PURE__ */ new Set([
  "PROMPT",
  "PROMPT_FILE",
  "EVAL_ID",
  "ATTEMPT",
  "FILES",
  "OUTPUT_FILE"
]);
var COMMON_TARGET_SETTINGS = [
  "provider_batching",
  "providerBatching",
  "subagent_mode_allowed",
  "subagentModeAllowed"
];
var BASE_TARGET_SCHEMA = z.object({
  name: z.string().min(1, "target name is required"),
  provider: z.string().min(1, "provider is required"),
  grader_target: z.string().optional(),
  judge_target: z.string().optional(),
  // backward compat
  workers: z.number().int().min(1).optional(),
  workspace_template: z.string().optional(),
  workspaceTemplate: z.string().optional(),
  subagent_mode_allowed: z.boolean().optional()
}).passthrough();
var DEFAULT_AZURE_API_VERSION = "2024-12-01-preview";
var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
function normalizeAzureApiVersion(value) {
  if (!value) {
    return DEFAULT_AZURE_API_VERSION;
  }
  const trimmed = value.trim();
  if (trimmed.length === 0) {
    return DEFAULT_AZURE_API_VERSION;
  }
  const withoutPrefix = trimmed.replace(/^api[-_]?version\s*=\s*/i, "").trim();
  return withoutPrefix.length > 0 ? withoutPrefix : DEFAULT_AZURE_API_VERSION;
}
function resolveRetryConfig(target) {
  const maxRetries = resolveOptionalNumber(
    target.max_retries ?? target.maxRetries,
    `${target.name} max retries`
  );
  const initialDelayMs = resolveOptionalNumber(
    target.retry_initial_delay_ms ?? target.retryInitialDelayMs,
    `${target.name} retry initial delay`
  );
  const maxDelayMs = resolveOptionalNumber(
    target.retry_max_delay_ms ?? target.retryMaxDelayMs,
    `${target.name} retry max delay`
  );
  const backoffFactor = resolveOptionalNumber(
    target.retry_backoff_factor ?? target.retryBackoffFactor,
    `${target.name} retry backoff factor`
  );
  const retryableStatusCodes = resolveOptionalNumberArray(
    target.retry_status_codes ?? target.retryStatusCodes,
    `${target.name} retry status codes`
  );
  if (maxRetries === void 0 && initialDelayMs === void 0 && maxDelayMs === void 0 && backoffFactor === void 0 && retryableStatusCodes === void 0) {
    return void 0;
  }
  return {
    maxRetries,
    initialDelayMs,
    maxDelayMs,
    backoffFactor,
    retryableStatusCodes
  };
}
function resolveTargetDefinition(definition, env = process.env, evalFilePath) {
  const parsed = BASE_TARGET_SCHEMA.parse(definition);
  if (parsed.workspace_template !== void 0 || parsed.workspaceTemplate !== void 0) {
    throw new Error(
      `${parsed.name}: target-level workspace_template has been removed. Use eval-level workspace.template.`
    );
  }
  const provider = resolveString(
    parsed.provider,
    env,
    `${parsed.name} provider`,
    true
  ).toLowerCase();
  const providerBatching = resolveOptionalBoolean(
    parsed.provider_batching ?? parsed.providerBatching
  );
  const subagentModeAllowed = resolveOptionalBoolean(
    parsed.subagent_mode_allowed ?? parsed.subagentModeAllowed
  );
  const base = {
    name: parsed.name,
    graderTarget: parsed.grader_target ?? parsed.judge_target,
    workers: parsed.workers,
    providerBatching,
    subagentModeAllowed
  };
  switch (provider) {
    case "openai":
      return {
        kind: "openai",
        ...base,
        config: resolveOpenAIConfig(parsed, env)
      };
    case "openrouter":
      return {
        kind: "openrouter",
        ...base,
        config: resolveOpenRouterConfig(parsed, env)
      };
    case "azure":
    case "azure-openai":
      return {
        kind: "azure",
        ...base,
        config: resolveAzureConfig(parsed, env)
      };
    case "anthropic":
      return {
        kind: "anthropic",
        ...base,
        config: resolveAnthropicConfig(parsed, env)
      };
    case "gemini":
    case "google":
    case "google-gemini":
      return {
        kind: "gemini",
        ...base,
        config: resolveGeminiConfig(parsed, env)
      };
    case "codex":
    case "codex-cli":
      return {
        kind: "codex",
        ...base,
        config: resolveCodexConfig(parsed, env, evalFilePath)
      };
    case "copilot-sdk":
    case "copilot_sdk":
      return {
        kind: "copilot-sdk",
        ...base,
        config: resolveCopilotSdkConfig(parsed, env, evalFilePath)
      };
    case "copilot":
    case "copilot-cli":
      return {
        kind: "copilot-cli",
        ...base,
        config: resolveCopilotCliConfig(parsed, env, evalFilePath)
      };
    case "copilot-log":
      return {
        kind: "copilot-log",
        ...base,
        config: resolveCopilotLogConfig(parsed, env)
      };
    case "pi":
    case "pi-coding-agent":
      return {
        kind: "pi-coding-agent",
        ...base,
        config: resolvePiCodingAgentConfig(parsed, env, evalFilePath)
      };
    case "pi-cli":
      return {
        kind: "pi-cli",
        ...base,
        config: resolvePiCliConfig(parsed, env, evalFilePath)
      };
    case "claude":
    case "claude-code":
    case "claude-cli":
      return {
        kind: "claude-cli",
        ...base,
        config: resolveClaudeConfig(parsed, env, evalFilePath)
      };
    case "claude-sdk":
      return {
        kind: "claude-sdk",
        ...base,
        config: resolveClaudeConfig(parsed, env, evalFilePath)
      };
    case "mock":
      return {
        kind: "mock",
        ...base,
        config: resolveMockConfig(parsed)
      };
    case "vscode":
    case "vscode-insiders":
      return {
        kind: provider,
        ...base,
        config: resolveVSCodeConfig(parsed, env, provider === "vscode-insiders", evalFilePath)
      };
    case "agentv": {
      const model = typeof parsed.model === "string" ? parsed.model : void 0;
      if (!model) {
        throw new Error(
          `Target "${parsed.name}" (provider: agentv) requires a "model" field (e.g., "openai:gpt-5-mini")`
        );
      }
      const temperature = typeof parsed.temperature === "number" ? parsed.temperature : 0;
      return {
        kind: "agentv",
        ...base,
        workers: typeof parsed.workers === "number" ? parsed.workers : void 0,
        config: { model, temperature }
      };
    }
    case "cli":
      return {
        kind: "cli",
        ...base,
        config: resolveCliConfig(parsed, env, evalFilePath)
      };
    default:
      return {
        kind: "cli",
        ...base,
        config: resolveDiscoveredProviderConfig(parsed, provider, env, evalFilePath)
      };
  }
}
function normalizeOpenAIBaseUrl(value) {
  if (!value) {
    return DEFAULT_OPENAI_BASE_URL;
  }
  const trimmed = value.trim().replace(/\/+$/, "");
  if (trimmed.length === 0) {
    return DEFAULT_OPENAI_BASE_URL;
  }
  return trimmed.endsWith("/v1") ? trimmed : `${trimmed}/v1`;
}
function resolveAzureConfig(target, env) {
  const endpointSource = target.endpoint ?? target.resource ?? target.resourceName;
  const apiKeySource = target.api_key ?? target.apiKey;
  const deploymentSource = target.deployment ?? target.deploymentName ?? target.model;
  const versionSource = target.version ?? target.api_version;
  const temperatureSource = target.temperature;
  const maxTokensSource = target.max_output_tokens ?? target.maxTokens;
  const resourceName = resolveString(endpointSource, env, `${target.name} endpoint`);
  const apiKey = resolveString(apiKeySource, env, `${target.name} api key`);
  const deploymentName = resolveString(deploymentSource, env, `${target.name} deployment`);
  const version = normalizeAzureApiVersion(
    resolveOptionalString(versionSource, env, `${target.name} api version`, {
      allowLiteral: true,
      optionalEnv: true
    })
  );
  const temperature = resolveOptionalNumber(temperatureSource, `${target.name} temperature`);
  const maxOutputTokens = resolveOptionalNumber(
    maxTokensSource,
    `${target.name} max output tokens`
  );
  const retry = resolveRetryConfig(target);
  return {
    resourceName,
    deploymentName,
    apiKey,
    version,
    temperature,
    maxOutputTokens,
    retry
  };
}
function resolveOpenAIConfig(target, env) {
  const endpointSource = target.endpoint ?? target.base_url ?? target.baseUrl;
  const apiKeySource = target.api_key ?? target.apiKey;
  const modelSource = target.model ?? target.deployment ?? target.variant;
  const temperatureSource = target.temperature;
  const maxTokensSource = target.max_output_tokens ?? target.maxTokens;
  const baseURL = normalizeOpenAIBaseUrl(
    resolveOptionalString(endpointSource, env, `${target.name} endpoint`, {
      allowLiteral: true,
      optionalEnv: true
    })
  );
  const apiKey = resolveString(apiKeySource, env, `${target.name} api key`);
  const model = resolveString(modelSource, env, `${target.name} model`);
  const retry = resolveRetryConfig(target);
  return {
    baseURL,
    apiKey,
    model,
    temperature: resolveOptionalNumber(temperatureSource, `${target.name} temperature`),
    maxOutputTokens: resolveOptionalNumber(maxTokensSource, `${target.name} max output tokens`),
    retry
  };
}
function resolveOpenRouterConfig(target, env) {
  const apiKeySource = target.api_key ?? target.apiKey;
  const modelSource = target.model ?? target.deployment ?? target.variant;
  const temperatureSource = target.temperature;
  const maxTokensSource = target.max_output_tokens ?? target.maxTokens;
  const retry = resolveRetryConfig(target);
  return {
    apiKey: resolveString(apiKeySource, env, `${target.name} OpenRouter api key`),
    model: resolveString(modelSource, env, `${target.name} OpenRouter model`),
    temperature: resolveOptionalNumber(temperatureSource, `${target.name} temperature`),
    maxOutputTokens: resolveOptionalNumber(maxTokensSource, `${target.name} max output tokens`),
    retry
  };
}
function resolveAnthropicConfig(target, env) {
  const apiKeySource = target.api_key ?? target.apiKey;
  const modelSource = target.model ?? target.deployment ?? target.variant;
  const temperatureSource = target.temperature;
  const maxTokensSource = target.max_output_tokens ?? target.maxTokens;
  const thinkingBudgetSource = target.thinking_budget ?? target.thinkingBudget;
  const apiKey = resolveString(apiKeySource, env, `${target.name} Anthropic api key`);
  const model = resolveString(modelSource, env, `${target.name} Anthropic model`);
  const retry = resolveRetryConfig(target);
  return {
    apiKey,
    model,
    temperature: resolveOptionalNumber(temperatureSource, `${target.name} temperature`),
    maxOutputTokens: resolveOptionalNumber(maxTokensSource, `${target.name} max output tokens`),
    thinkingBudget: resolveOptionalNumber(thinkingBudgetSource, `${target.name} thinking budget`),
    retry
  };
}
function resolveGeminiConfig(target, env) {
  const apiKeySource = target.api_key ?? target.apiKey;
  const modelSource = target.model ?? target.deployment ?? target.variant;
  const temperatureSource = target.temperature;
  const maxTokensSource = target.max_output_tokens ?? target.maxTokens;
  const apiKey = resolveString(apiKeySource, env, `${target.name} Google API key`);
  const model = resolveOptionalString(modelSource, env, `${target.name} Gemini model`, {
    allowLiteral: true,
    optionalEnv: true
  }) ?? "gemini-2.5-flash";
  const retry = resolveRetryConfig(target);
  return {
    apiKey,
    model,
    temperature: resolveOptionalNumber(temperatureSource, `${target.name} temperature`),
    maxOutputTokens: resolveOptionalNumber(maxTokensSource, `${target.name} max output tokens`),
    retry
  };
}
function resolveCodexConfig(target, env, evalFilePath) {
  const modelSource = target.model;
  const executableSource = target.executable ?? target.command ?? target.binary;
  const argsSource = target.args ?? target.arguments;
  const cwdSource = target.cwd;
  const workspaceTemplateSource = target.workspace_template ?? target.workspaceTemplate;
  const timeoutSource = target.timeout_seconds ?? target.timeoutSeconds;
  const logDirSource = target.log_dir ?? target.logDir ?? target.log_directory ?? target.logDirectory;
  const logFormatSource = target.log_format ?? target.logFormat ?? target.log_output_format ?? target.logOutputFormat ?? env.AGENTV_CODEX_LOG_FORMAT;
  const systemPromptSource = target.system_prompt ?? target.systemPrompt;
  const model = resolveOptionalString(modelSource, env, `${target.name} codex model`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const executable = resolveOptionalString(executableSource, env, `${target.name} codex executable`, {
    allowLiteral: true,
    optionalEnv: true
  }) ?? "codex";
  const args = resolveOptionalStringArray(argsSource, env, `${target.name} codex args`);
  const cwd = resolveOptionalString(cwdSource, env, `${target.name} codex cwd`, {
    allowLiteral: true,
    optionalEnv: true
  });
  let workspaceTemplate = resolveOptionalString(
    workspaceTemplateSource,
    env,
    `${target.name} codex workspace template`,
    {
      allowLiteral: true,
      optionalEnv: true
    }
  );
  if (workspaceTemplate && evalFilePath && !path2.isAbsolute(workspaceTemplate)) {
    workspaceTemplate = path2.resolve(path2.dirname(path2.resolve(evalFilePath)), workspaceTemplate);
  }
  if (cwd && workspaceTemplate) {
    throw new Error(
      `${target.name}: 'cwd' and 'workspace_template' are mutually exclusive. Use 'cwd' to run in an existing directory, or 'workspace_template' to copy a template to a temp location.`
    );
  }
  const timeoutMs = resolveTimeoutMs(timeoutSource, `${target.name} codex timeout`);
  const logDir = resolveOptionalString(logDirSource, env, `${target.name} codex log directory`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const logFormat = normalizeCodexLogFormat(logFormatSource);
  const systemPrompt = typeof systemPromptSource === "string" && systemPromptSource.trim().length > 0 ? systemPromptSource.trim() : void 0;
  return {
    model,
    executable,
    args,
    cwd,
    workspaceTemplate,
    timeoutMs,
    logDir,
    logFormat,
    systemPrompt
  };
}
function normalizeCodexLogFormat(value) {
  if (value === void 0 || value === null) {
    return void 0;
  }
  if (typeof value !== "string") {
    throw new Error("codex log format must be 'summary' or 'json'");
  }
  const normalized = value.trim().toLowerCase();
  if (normalized === "json" || normalized === "summary") {
    return normalized;
  }
  throw new Error("codex log format must be 'summary' or 'json'");
}
function resolveCopilotSdkConfig(target, env, evalFilePath) {
  const cliUrlSource = target.cli_url ?? target.cliUrl;
  const cliPathSource = target.cli_path ?? target.cliPath;
  const githubTokenSource = target.github_token ?? target.githubToken;
  const modelSource = target.model;
  const cwdSource = target.cwd;
  const workspaceTemplateSource = target.workspace_template ?? target.workspaceTemplate;
  const timeoutSource = target.timeout_seconds ?? target.timeoutSeconds;
  const logDirSource = target.log_dir ?? target.logDir ?? target.log_directory ?? target.logDirectory;
  const logFormatSource = target.log_format ?? target.logFormat;
  const systemPromptSource = target.system_prompt ?? target.systemPrompt;
  const cliUrl = resolveOptionalString(cliUrlSource, env, `${target.name} copilot-sdk cli URL`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const cliPath = resolveOptionalString(cliPathSource, env, `${target.name} copilot-sdk cli path`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const githubToken = resolveOptionalString(
    githubTokenSource,
    env,
    `${target.name} copilot-sdk github token`,
    {
      allowLiteral: false,
      optionalEnv: true
    }
  );
  const model = resolveOptionalString(modelSource, env, `${target.name} copilot-sdk model`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const cwd = resolveOptionalString(cwdSource, env, `${target.name} copilot-sdk cwd`, {
    allowLiteral: true,
    optionalEnv: true
  });
  let workspaceTemplate = resolveOptionalString(
    workspaceTemplateSource,
    env,
    `${target.name} copilot-sdk workspace template`,
    {
      allowLiteral: true,
      optionalEnv: true
    }
  );
  if (workspaceTemplate && evalFilePath && !path2.isAbsolute(workspaceTemplate)) {
    workspaceTemplate = path2.resolve(path2.dirname(path2.resolve(evalFilePath)), workspaceTemplate);
  }
  if (cwd && workspaceTemplate) {
    throw new Error(
      `${target.name}: 'cwd' and 'workspace_template' are mutually exclusive. Use 'cwd' to run in an existing directory, or 'workspace_template' to copy a template to a temp location.`
    );
  }
  const timeoutMs = resolveTimeoutMs(timeoutSource, `${target.name} copilot-sdk timeout`);
  const logDir = resolveOptionalString(
    logDirSource,
    env,
    `${target.name} copilot-sdk log directory`,
    {
      allowLiteral: true,
      optionalEnv: true
    }
  );
  const logFormat = normalizeCopilotLogFormat(logFormatSource);
  const systemPrompt = typeof systemPromptSource === "string" && systemPromptSource.trim().length > 0 ? systemPromptSource.trim() : void 0;
  return {
    cliUrl,
    cliPath,
    githubToken,
    model,
    cwd,
    workspaceTemplate,
    timeoutMs,
    logDir,
    logFormat,
    systemPrompt
  };
}
function resolveCopilotCliConfig(target, env, evalFilePath) {
  const executableSource = target.executable ?? target.command ?? target.binary;
  const modelSource = target.model;
  const argsSource = target.args ?? target.arguments;
  const cwdSource = target.cwd;
  const workspaceTemplateSource = target.workspace_template ?? target.workspaceTemplate;
  const timeoutSource = target.timeout_seconds ?? target.timeoutSeconds;
  const logDirSource = target.log_dir ?? target.logDir ?? target.log_directory ?? target.logDirectory;
  const logFormatSource = target.log_format ?? target.logFormat;
  const systemPromptSource = target.system_prompt ?? target.systemPrompt;
  const executable = resolveOptionalString(executableSource, env, `${target.name} copilot-cli executable`, {
    allowLiteral: true,
    optionalEnv: true
  }) ?? "copilot";
  const model = resolveOptionalString(modelSource, env, `${target.name} copilot-cli model`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const args = resolveOptionalStringArray(argsSource, env, `${target.name} copilot-cli args`);
  const cwd = resolveOptionalString(cwdSource, env, `${target.name} copilot-cli cwd`, {
    allowLiteral: true,
    optionalEnv: true
  });
  let workspaceTemplate = resolveOptionalString(
    workspaceTemplateSource,
    env,
    `${target.name} copilot-cli workspace template`,
    {
      allowLiteral: true,
      optionalEnv: true
    }
  );
  if (workspaceTemplate && evalFilePath && !path2.isAbsolute(workspaceTemplate)) {
    workspaceTemplate = path2.resolve(path2.dirname(path2.resolve(evalFilePath)), workspaceTemplate);
  }
  if (cwd && workspaceTemplate) {
    throw new Error(
      `${target.name}: 'cwd' and 'workspace_template' are mutually exclusive. Use 'cwd' to run in an existing directory, or 'workspace_template' to copy a template to a temp location.`
    );
  }
  const timeoutMs = resolveTimeoutMs(timeoutSource, `${target.name} copilot-cli timeout`);
  const logDir = resolveOptionalString(
    logDirSource,
    env,
    `${target.name} copilot-cli log directory`,
    {
      allowLiteral: true,
      optionalEnv: true
    }
  );
  const logFormat = normalizeCopilotLogFormat(logFormatSource);
  const systemPrompt = typeof systemPromptSource === "string" && systemPromptSource.trim().length > 0 ? systemPromptSource.trim() : void 0;
  return {
    executable,
    model,
    args,
    cwd,
    workspaceTemplate,
    timeoutMs,
    logDir,
    logFormat,
    systemPrompt
  };
}
function normalizeCopilotLogFormat(value) {
  if (value === void 0 || value === null) return void 0;
  if (typeof value !== "string") throw new Error("copilot log format must be 'summary' or 'json'");
  const normalized = value.trim().toLowerCase();
  if (normalized === "json" || normalized === "summary") return normalized;
  throw new Error("copilot log format must be 'summary' or 'json'");
}
function resolvePiCodingAgentConfig(target, env, evalFilePath) {
  const subproviderSource = target.subprovider;
  const modelSource = target.model ?? target.pi_model ?? target.piModel;
  const apiKeySource = target.api_key ?? target.apiKey;
  const toolsSource = target.tools ?? target.pi_tools ?? target.piTools;
  const thinkingSource = target.thinking ?? target.pi_thinking ?? target.piThinking;
  const cwdSource = target.cwd;
  const workspaceTemplateSource = target.workspace_template ?? target.workspaceTemplate;
  const timeoutSource = target.timeout_seconds ?? target.timeoutSeconds;
  const logDirSource = target.log_dir ?? target.logDir ?? target.log_directory ?? target.logDirectory;
  const logFormatSource = target.log_format ?? target.logFormat;
  const systemPromptSource = target.system_prompt ?? target.systemPrompt;
  const subprovider = resolveOptionalString(
    subproviderSource,
    env,
    `${target.name} pi subprovider`,
    {
      allowLiteral: true,
      optionalEnv: true
    }
  );
  const model = resolveOptionalString(modelSource, env, `${target.name} pi model`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const apiKey = resolveOptionalString(apiKeySource, env, `${target.name} pi api key`, {
    allowLiteral: false,
    optionalEnv: true
  });
  const tools = resolveOptionalString(toolsSource, env, `${target.name} pi tools`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const thinking = resolveOptionalString(thinkingSource, env, `${target.name} pi thinking`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const cwd = resolveOptionalString(cwdSource, env, `${target.name} pi cwd`, {
    allowLiteral: true,
    optionalEnv: true
  });
  let workspaceTemplate = resolveOptionalString(
    workspaceTemplateSource,
    env,
    `${target.name} pi workspace template`,
    {
      allowLiteral: true,
      optionalEnv: true
    }
  );
  if (workspaceTemplate && evalFilePath && !path2.isAbsolute(workspaceTemplate)) {
    workspaceTemplate = path2.resolve(path2.dirname(path2.resolve(evalFilePath)), workspaceTemplate);
  }
  if (cwd && workspaceTemplate) {
    throw new Error(
      `${target.name}: 'cwd' and 'workspace_template' are mutually exclusive. Use 'cwd' to run in an existing directory, or 'workspace_template' to copy a template to a temp location.`
    );
  }
  const timeoutMs = resolveTimeoutMs(timeoutSource, `${target.name} pi timeout`);
  const logDir = resolveOptionalString(logDirSource, env, `${target.name} pi log directory`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const logFormat = logFormatSource === "json" || logFormatSource === "summary" ? logFormatSource : void 0;
  const systemPrompt = typeof systemPromptSource === "string" && systemPromptSource.trim().length > 0 ? systemPromptSource.trim() : void 0;
  return {
    subprovider,
    model,
    apiKey,
    tools,
    thinking,
    cwd,
    workspaceTemplate,
    timeoutMs,
    logDir,
    logFormat,
    systemPrompt
  };
}
function resolvePiCliConfig(target, env, evalFilePath) {
  const executableSource = target.executable ?? target.command ?? target.binary;
  const subproviderSource = target.subprovider;
  const modelSource = target.model ?? target.pi_model ?? target.piModel;
  const apiKeySource = target.api_key ?? target.apiKey;
  const toolsSource = target.tools ?? target.pi_tools ?? target.piTools;
  const thinkingSource = target.thinking ?? target.pi_thinking ?? target.piThinking;
  const cwdSource = target.cwd;
  const workspaceTemplateSource = target.workspace_template ?? target.workspaceTemplate;
  const timeoutSource = target.timeout_seconds ?? target.timeoutSeconds;
  const logDirSource = target.log_dir ?? target.logDir ?? target.log_directory ?? target.logDirectory;
  const logFormatSource = target.log_format ?? target.logFormat;
  const systemPromptSource = target.system_prompt ?? target.systemPrompt;
  const executable = resolveOptionalString(executableSource, env, `${target.name} pi-cli executable`, {
    allowLiteral: true,
    optionalEnv: true
  }) ?? "pi";
  const subprovider = resolveOptionalString(
    subproviderSource,
    env,
    `${target.name} pi-cli subprovider`,
    { allowLiteral: true, optionalEnv: true }
  );
  const model = resolveOptionalString(modelSource, env, `${target.name} pi-cli model`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const apiKey = resolveOptionalString(apiKeySource, env, `${target.name} pi-cli api key`, {
    allowLiteral: false,
    optionalEnv: true
  });
  const tools = resolveOptionalString(toolsSource, env, `${target.name} pi-cli tools`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const thinking = resolveOptionalString(thinkingSource, env, `${target.name} pi-cli thinking`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const rawArgs = target.args ?? target.arguments;
  const args = resolveOptionalStringArray(rawArgs, env, `${target.name} pi-cli args`);
  const cwd = resolveOptionalString(cwdSource, env, `${target.name} pi-cli cwd`, {
    allowLiteral: true,
    optionalEnv: true
  });
  let workspaceTemplate = resolveOptionalString(
    workspaceTemplateSource,
    env,
    `${target.name} pi-cli workspace template`,
    { allowLiteral: true, optionalEnv: true }
  );
  if (workspaceTemplate && evalFilePath && !path2.isAbsolute(workspaceTemplate)) {
    workspaceTemplate = path2.resolve(path2.dirname(path2.resolve(evalFilePath)), workspaceTemplate);
  }
  if (cwd && workspaceTemplate) {
    throw new Error(`${target.name}: 'cwd' and 'workspace_template' are mutually exclusive.`);
  }
  const timeoutMs = resolveTimeoutMs(timeoutSource, `${target.name} pi-cli timeout`);
  const logDir = resolveOptionalString(logDirSource, env, `${target.name} pi-cli log directory`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const logFormat = logFormatSource === "json" || logFormatSource === "summary" ? logFormatSource : void 0;
  const systemPrompt = typeof systemPromptSource === "string" && systemPromptSource.trim().length > 0 ? systemPromptSource.trim() : void 0;
  return {
    executable,
    subprovider,
    model,
    apiKey,
    tools,
    thinking,
    args,
    cwd,
    workspaceTemplate,
    timeoutMs,
    logDir,
    logFormat,
    systemPrompt
  };
}
function resolveClaudeConfig(target, env, evalFilePath) {
  const modelSource = target.model;
  const cwdSource = target.cwd;
  const workspaceTemplateSource = target.workspace_template ?? target.workspaceTemplate;
  const timeoutSource = target.timeout_seconds ?? target.timeoutSeconds;
  const logDirSource = target.log_dir ?? target.logDir ?? target.log_directory ?? target.logDirectory;
  const logFormatSource = target.log_format ?? target.logFormat ?? target.log_output_format ?? target.logOutputFormat ?? env.AGENTV_CLAUDE_LOG_FORMAT;
  const systemPromptSource = target.system_prompt ?? target.systemPrompt;
  const model = resolveOptionalString(modelSource, env, `${target.name} claude model`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const cwd = resolveOptionalString(cwdSource, env, `${target.name} claude cwd`, {
    allowLiteral: true,
    optionalEnv: true
  });
  let workspaceTemplate = resolveOptionalString(
    workspaceTemplateSource,
    env,
    `${target.name} claude workspace template`,
    {
      allowLiteral: true,
      optionalEnv: true
    }
  );
  if (workspaceTemplate && evalFilePath && !path2.isAbsolute(workspaceTemplate)) {
    workspaceTemplate = path2.resolve(path2.dirname(path2.resolve(evalFilePath)), workspaceTemplate);
  }
  if (cwd && workspaceTemplate) {
    throw new Error(
      `${target.name}: 'cwd' and 'workspace_template' are mutually exclusive. Use 'cwd' to run in an existing directory, or 'workspace_template' to copy a template to a temp location.`
    );
  }
  const timeoutMs = resolveTimeoutMs(timeoutSource, `${target.name} claude timeout`);
  const logDir = resolveOptionalString(logDirSource, env, `${target.name} claude log directory`, {
    allowLiteral: true,
    optionalEnv: true
  });
  const logFormat = normalizeClaudeLogFormat(logFormatSource);
  const systemPrompt = typeof systemPromptSource === "string" && systemPromptSource.trim().length > 0 ? systemPromptSource.trim() : void 0;
  const maxTurns = typeof target.max_turns === "number" ? target.max_turns : typeof target.maxTurns === "number" ? target.maxTurns : void 0;
  const maxBudgetUsd = typeof target.max_budget_usd === "number" ? target.max_budget_usd : typeof target.maxBudgetUsd === "number" ? target.maxBudgetUsd : void 0;
  return {
    model,
    systemPrompt,
    cwd,
    workspaceTemplate,
    timeoutMs,
    maxTurns,
    maxBudgetUsd,
    logDir,
    logFormat
  };
}
function normalizeClaudeLogFormat(value) {
  if (value === void 0 || value === null) {
    return void 0;
  }
  if (typeof value !== "string") {
    throw new Error("claude log format must be 'summary' or 'json'");
  }
  const normalized = value.trim().toLowerCase();
  if (normalized === "json" || normalized === "summary") {
    return normalized;
  }
  throw new Error("claude log format must be 'summary' or 'json'");
}
function resolveMockConfig(target) {
  const response = typeof target.response === "string" ? target.response : void 0;
  return { response };
}
function resolveVSCodeConfig(target, env, insiders, evalFilePath) {
  const workspaceTemplateEnvVar = resolveOptionalLiteralString(
    target.workspace_template ?? target.workspaceTemplate
  );
  let workspaceTemplate = workspaceTemplateEnvVar ? resolveOptionalString(
    workspaceTemplateEnvVar,
    env,
    `${target.name} workspace template path`,
    {
      allowLiteral: true,
      optionalEnv: true
    }
  ) : void 0;
  if (workspaceTemplate && evalFilePath && !path2.isAbsolute(workspaceTemplate)) {
    workspaceTemplate = path2.resolve(path2.dirname(path2.resolve(evalFilePath)), workspaceTemplate);
  }
  const executableSource = target.executable;
  const waitSource = target.wait;
  const dryRunSource = target.dry_run ?? target.dryRun;
  const subagentRootSource = target.subagent_root ?? target.subagentRoot;
  const timeoutSource = target.timeout_seconds ?? target.timeoutSeconds;
  const defaultCommand = insiders ? "code-insiders" : "code";
  const executable = resolveOptionalString(executableSource, env, `${target.name} vscode executable`, {
    allowLiteral: true,
    optionalEnv: true
  }) ?? defaultCommand;
  const timeoutMs = resolveTimeoutMs(timeoutSource, `${target.name} vscode timeout`);
  return {
    executable,
    waitForResponse: resolveOptionalBoolean(waitSource) ?? true,
    dryRun: resolveOptionalBoolean(dryRunSource) ?? false,
    subagentRoot: resolveOptionalString(subagentRootSource, env, `${target.name} subagent root`, {
      allowLiteral: true,
      optionalEnv: true
    }),
    workspaceTemplate,
    timeoutMs
  };
}
var cliErrorMap = (issue, ctx) => {
  if (issue.code === z.ZodIssueCode.unrecognized_keys) {
    return { message: `Unknown CLI provider settings: ${issue.keys.join(", ")}` };
  }
  if (issue.code === z.ZodIssueCode.invalid_union) {
    return { message: "healthcheck must have either 'url' (HTTP) or 'command' (command)" };
  }
  if (issue.code === z.ZodIssueCode.invalid_type && issue.expected === "string") {
    return { message: `${ctx.defaultError} (expected a string value)` };
  }
  return { message: ctx.defaultError };
};
function resolveCliConfig(target, env, evalFilePath) {
  const parseResult = CliTargetInputSchema.safeParse(target, { errorMap: cliErrorMap });
  if (!parseResult.success) {
    const firstError = parseResult.error.errors[0];
    const path4 = firstError?.path.join(".") || "";
    const prefix = path4 ? `${target.name} ${path4}: ` : `${target.name}: `;
    throw new Error(`${prefix}${firstError?.message}`);
  }
  const normalized = normalizeCliTargetInput(parseResult.data, env, evalFilePath);
  assertSupportedCliPlaceholders(normalized.command, `${target.name} CLI command`);
  if ("command" in (normalized.healthcheck ?? {}) && normalized.healthcheck.command) {
    assertSupportedCliPlaceholders(
      normalized.healthcheck.command,
      `${target.name} healthcheck command`
    );
  }
  return normalized;
}
function resolveDiscoveredProviderConfig(target, providerKind, env, evalFilePath) {
  const command = target.command ? resolveString(target.command, env, `${target.name} command`, true) : `bun run .agentv/providers/${providerKind}.ts {PROMPT}`;
  const timeoutSeconds = target.timeout_seconds ?? target.timeoutSeconds;
  const timeoutMs = resolveTimeoutMs(timeoutSeconds, `${target.name} timeout`);
  let cwd = resolveOptionalString(target.cwd, env, `${target.name} working directory`, {
    allowLiteral: true,
    optionalEnv: true
  });
  if (cwd && evalFilePath && !path2.isAbsolute(cwd)) {
    cwd = path2.resolve(path2.dirname(path2.resolve(evalFilePath)), cwd);
  }
  if (!cwd && evalFilePath) {
    cwd = path2.dirname(path2.resolve(evalFilePath));
  }
  return {
    command,
    cwd,
    timeoutMs
  };
}
function resolveTimeoutMs(source, description) {
  const seconds = resolveOptionalNumber(source, `${description} (seconds)`);
  if (seconds === void 0) {
    return void 0;
  }
  if (seconds <= 0) {
    throw new Error(`${description} must be greater than zero seconds`);
  }
  return Math.floor(seconds * 1e3);
}
function assertSupportedCliPlaceholders(template, description) {
  const placeholders = extractCliPlaceholders(template);
  for (const placeholder of placeholders) {
    if (!CLI_PLACEHOLDERS.has(placeholder)) {
      throw new Error(
        `${description} includes unsupported placeholder '{${placeholder}}'. Supported placeholders: ${Array.from(CLI_PLACEHOLDERS).join(", ")}`
      );
    }
  }
}
function extractCliPlaceholders(template) {
  const matches = template.matchAll(/\{([A-Z_]+)\}/g);
  const results = [];
  for (const match of matches) {
    if (match[1]) {
      results.push(match[1]);
    }
  }
  return results;
}
function resolveString(source, env, description, allowLiteral = false) {
  const value = resolveOptionalString(source, env, description, {
    allowLiteral,
    optionalEnv: false
  });
  if (value === void 0) {
    throw new Error(`${description} is required`);
  }
  return value;
}
function resolveDiscover(value, targetName) {
  if (value === void 0 || value === null) return void 0;
  if (value === "latest") return "latest";
  throw new Error(`Target "${targetName}": discover must be "latest" (got "${String(value)}")`);
}
function resolveCopilotLogConfig(target, env) {
  const sessionDirSource = target.session_dir ?? target.sessionDir;
  const sessionIdSource = target.session_id ?? target.sessionId;
  const discoverSource = target.discover;
  const sessionStateDirSource = target.session_state_dir ?? target.sessionStateDir;
  const cwdSource = target.cwd;
  return {
    sessionDir: resolveOptionalString(
      sessionDirSource,
      env,
      `${target.name} copilot-log session_dir`,
      { allowLiteral: true, optionalEnv: true }
    ),
    sessionId: resolveOptionalString(
      sessionIdSource,
      env,
      `${target.name} copilot-log session_id`,
      { allowLiteral: true, optionalEnv: true }
    ),
    discover: resolveDiscover(discoverSource, target.name),
    sessionStateDir: resolveOptionalString(
      sessionStateDirSource,
      env,
      `${target.name} copilot-log session_state_dir`,
      { allowLiteral: true, optionalEnv: true }
    ),
    cwd: resolveOptionalString(cwdSource, env, `${target.name} copilot-log cwd`, {
      allowLiteral: true,
      optionalEnv: true
    })
  };
}
function resolveOptionalString(source, env, description, options) {
  if (source === void 0 || source === null) {
    return void 0;
  }
  if (typeof source !== "string") {
    throw new Error(`${description} must be a string`);
  }
  const trimmed = source.trim();
  if (trimmed.length === 0) {
    return void 0;
  }
  const envVarMatch = trimmed.match(/^\$\{\{\s*([A-Z0-9_]+)\s*\}\}$/i);
  if (envVarMatch) {
    const varName = envVarMatch[1];
    const envValue = env[varName];
    const optionalEnv = options?.optionalEnv ?? false;
    if (envValue === void 0 || envValue.trim().length === 0) {
      if (optionalEnv) {
        return void 0;
      }
      const status = envValue === void 0 ? "is not set" : "is empty";
      throw new Error(`Environment variable '${varName}' required for ${description} ${status}`);
    }
    return envValue;
  }
  const allowLiteral = options?.allowLiteral ?? false;
  if (!allowLiteral) {
    throw new Error(
      `${description} must use \${{ VARIABLE_NAME }} syntax for environment variables or be marked as allowing literals`
    );
  }
  return trimmed;
}
function resolveOptionalLiteralString(source) {
  if (source === void 0 || source === null) {
    return void 0;
  }
  if (typeof source !== "string") {
    throw new Error("expected string value");
  }
  const trimmed = source.trim();
  return trimmed.length > 0 ? trimmed : void 0;
}
function resolveOptionalNumber(source, description) {
  if (source === void 0 || source === null || source === "") {
    return void 0;
  }
  if (typeof source === "number") {
    return Number.isFinite(source) ? source : void 0;
  }
  if (typeof source === "string") {
    const numeric = Number(source);
    if (Number.isFinite(numeric)) {
      return numeric;
    }
  }
  throw new Error(`${description} must be a number`);
}
function resolveOptionalBoolean(source) {
  if (source === void 0 || source === null || source === "") {
    return void 0;
  }
  if (typeof source === "boolean") {
    return source;
  }
  if (typeof source === "string") {
    const lowered = source.trim().toLowerCase();
    if (lowered === "true" || lowered === "1") {
      return true;
    }
    if (lowered === "false" || lowered === "0") {
      return false;
    }
  }
  throw new Error("expected boolean value");
}
function resolveOptionalStringArray(source, env, description) {
  if (source === void 0 || source === null) {
    return void 0;
  }
  if (!Array.isArray(source)) {
    throw new Error(`${description} must be an array of strings`);
  }
  if (source.length === 0) {
    return void 0;
  }
  const resolved = [];
  for (let i = 0; i < source.length; i++) {
    const item = source[i];
    if (typeof item !== "string") {
      throw new Error(`${description}[${i}] must be a string`);
    }
    const trimmed = item.trim();
    if (trimmed.length === 0) {
      throw new Error(`${description}[${i}] cannot be empty`);
    }
    const envVarMatch = trimmed.match(/^\$\{\{\s*([A-Z0-9_]+)\s*\}\}$/i);
    if (envVarMatch) {
      const varName = envVarMatch[1];
      const envValue = env[varName];
      if (envValue !== void 0) {
        if (envValue.trim().length === 0) {
          throw new Error(`Environment variable '${varName}' for ${description}[${i}] is empty`);
        }
        resolved.push(envValue);
        continue;
      }
      throw new Error(`Environment variable '${varName}' for ${description}[${i}] is not set`);
    }
    resolved.push(trimmed);
  }
  return resolved.length > 0 ? resolved : void 0;
}
function resolveOptionalNumberArray(source, description) {
  if (source === void 0 || source === null) {
    return void 0;
  }
  if (!Array.isArray(source)) {
    throw new Error(`${description} must be an array of numbers`);
  }
  if (source.length === 0) {
    return void 0;
  }
  const resolved = [];
  for (let i = 0; i < source.length; i++) {
    const item = source[i];
    if (typeof item !== "number" || !Number.isFinite(item)) {
      throw new Error(`${description}[${i}] must be a number`);
    }
    resolved.push(item);
  }
  return resolved.length > 0 ? resolved : void 0;
}

// src/evaluation/providers/types.ts
var AGENT_PROVIDER_KINDS = [
  "codex",
  "copilot-sdk",
  "copilot-cli",
  "pi-coding-agent",
  "pi-cli",
  "claude",
  "claude-cli",
  "claude-sdk",
  "vscode",
  "vscode-insiders"
];
var KNOWN_PROVIDERS = [
  "openai",
  "openrouter",
  "azure",
  "anthropic",
  "gemini",
  "codex",
  "copilot-sdk",
  "copilot-cli",
  "copilot-log",
  "pi-coding-agent",
  "pi-cli",
  "claude",
  "claude-cli",
  "claude-sdk",
  "cli",
  "mock",
  "vscode",
  "vscode-insiders",
  "agentv"
];
var PROVIDER_ALIASES = [
  "azure-openai",
  // alias for "azure"
  "google",
  // alias for "gemini"
  "google-gemini",
  // alias for "gemini"
  "codex-cli",
  // alias for "codex"
  "copilot",
  // alias for "copilot-cli" (default copilot experience)
  "copilot_sdk",
  // alias for "copilot-sdk" (underscore variant)
  "pi",
  // alias for "pi-coding-agent"
  "claude-code",
  // alias for "claude" (legacy)
  "bedrock",
  // legacy/future support
  "vertex"
  // legacy/future support
];
function extractLastAssistantContent(messages) {
  if (!messages || messages.length === 0) {
    return "";
  }
  for (let i = messages.length - 1; i >= 0; i--) {
    const msg = messages[i];
    if (msg.role === "assistant" && msg.content !== void 0) {
      if (typeof msg.content === "string") {
        return msg.content;
      }
      if (isContentArray(msg.content)) {
        return getTextContent(msg.content);
      }
      return JSON.stringify(msg.content);
    }
  }
  return "";
}
function isAgentProvider(provider) {
  return provider ? AGENT_PROVIDER_KINDS.includes(provider.kind) : false;
}

// src/evaluation/interpolation.ts
var ENV_VAR_PATTERN = /\$\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
function interpolateEnv(value, env) {
  if (typeof value === "string") {
    return value.replace(ENV_VAR_PATTERN, (_, varName) => env[varName] ?? "");
  }
  if (Array.isArray(value)) {
    return value.map((item) => interpolateEnv(item, env));
  }
  if (value !== null && typeof value === "object") {
    const result = {};
    for (const [key, val] of Object.entries(value)) {
      result[key] = interpolateEnv(val, env);
    }
    return result;
  }
  return value;
}

// src/evaluation/loaders/case-file-loader.ts
import { readFile as readFile2 } from "node:fs/promises";
import path3 from "node:path";
import fg from "fast-glob";
import { parse as parseYaml } from "yaml";
var ANSI_YELLOW = "\x1B[33m";
var ANSI_RESET = "\x1B[0m";
var FILE_PROTOCOL = "file://";
function isFileReference(value) {
  return typeof value === "string" && value.startsWith(FILE_PROTOCOL);
}
function extractFilePath(ref) {
  return ref.slice(FILE_PROTOCOL.length);
}
function isGlobPattern(filePath) {
  return filePath.includes("*") || filePath.includes("?") || filePath.includes("{");
}
function parseYamlCases(content, filePath) {
  const raw = parseYaml(content);
  const parsed = interpolateEnv(raw, process.env);
  if (!Array.isArray(parsed)) {
    throw new Error(
      `External test file must contain a YAML array, got ${typeof parsed}: ${filePath}`
    );
  }
  const results = [];
  for (const item of parsed) {
    if (!isJsonObject(item)) {
      throw new Error(`External test file contains non-object entry: ${filePath}`);
    }
    results.push(item);
  }
  return results;
}
function parseJsonlCases(content, filePath) {
  const lines = content.split("\n");
  const results = [];
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i].trim();
    if (line === "") continue;
    try {
      const raw = JSON.parse(line);
      const parsed = interpolateEnv(raw, process.env);
      if (!isJsonObject(parsed)) {
        throw new Error("Expected JSON object");
      }
      results.push(parsed);
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      throw new Error(`Malformed JSONL at line ${i + 1}: ${message}
  File: ${filePath}`);
    }
  }
  return results;
}
async function loadCasesFromFile(filePath) {
  const ext = path3.extname(filePath).toLowerCase();
  let content;
  try {
    content = await readFile2(filePath, "utf8");
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    throw new Error(`Cannot read external test file: ${filePath}
  ${message}`);
  }
  if (content.trim() === "") {
    console.warn(
      `${ANSI_YELLOW}Warning: External test file is empty, skipping: ${filePath}${ANSI_RESET}`
    );
    return [];
  }
  if (ext === ".yaml" || ext === ".yml") {
    return parseYamlCases(content, filePath);
  }
  if (ext === ".jsonl") {
    return parseJsonlCases(content, filePath);
  }
  throw new Error(
    `Unsupported external test file format '${ext}': ${filePath}. Supported: .yaml, .yml, .jsonl`
  );
}
async function resolveFileReference2(ref, evalFileDir) {
  const rawPath = extractFilePath(ref);
  const absolutePattern = path3.resolve(evalFileDir, rawPath);
  if (isGlobPattern(rawPath)) {
    const matches = await fg(absolutePattern.replaceAll("\\", "/"), {
      onlyFiles: true,
      absolute: true
    });
    if (matches.length === 0) {
      console.warn(
        `${ANSI_YELLOW}Warning: Glob pattern matched no files: ${ref} (resolved to ${absolutePattern})${ANSI_RESET}`
      );
      return [];
    }
    matches.sort();
    const allCases = [];
    for (const match of matches) {
      const cases = await loadCasesFromFile(match);
      allCases.push(...cases);
    }
    return allCases;
  }
  return loadCasesFromFile(absolutePattern);
}
async function expandFileReferences(tests, evalFileDir) {
  const expanded = [];
  for (const entry of tests) {
    if (isFileReference(entry)) {
      const cases = await resolveFileReference2(entry, evalFileDir);
      expanded.push(...cases);
    } else {
      expanded.push(entry);
    }
  }
  return expanded;
}

export {
  isContent,
  isContentArray,
  getTextContent,
  TEST_MESSAGE_ROLES,
  isTestMessageRole,
  isJsonObject,
  isJsonValue,
  isTestMessage,
  isEvaluatorKind,
  interpolateEnv,
  loadCasesFromFile,
  expandFileReferences,
  fileExists,
  normalizeLineEndings,
  readTextFile,
  readJsonFile,
  findGitRoot,
  buildDirectoryChain,
  buildSearchRoots,
  resolveFileReference,
  CLI_PLACEHOLDERS,
  COMMON_TARGET_SETTINGS,
  resolveTargetDefinition,
  KNOWN_PROVIDERS,
  PROVIDER_ALIASES,
  extractLastAssistantContent,
  isAgentProvider
};
//# sourceMappingURL=chunk-PXYYRDHH.js.map