agentv 0.5.0 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5039,6 +5039,14 @@ var ba = new Error("Agent definition is the prompt you give to the LLM for the a
5039
5039
  import { exec as execWithCallback } from "node:child_process";
5040
5040
  import path22 from "node:path";
5041
5041
  import { promisify as promisify2 } from "node:util";
5042
+ import { exec as execCallback, spawn as spawn2 } from "node:child_process";
5043
+ import { randomUUID } from "node:crypto";
5044
+ import { constants as constants22, createWriteStream } from "node:fs";
5045
+ import { access as access22, copyFile as copyFile2, mkdtemp, mkdir as mkdir3, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
5046
+ import { tmpdir } from "node:os";
5047
+ import path42 from "node:path";
5048
+ import { promisify as promisify22 } from "node:util";
5049
+ import path32 from "node:path";
5042
5050
 
5043
5051
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
5044
5052
  var external_exports = {};
@@ -9083,7 +9091,7 @@ var NEVER = INVALID;
9083
9091
 
9084
9092
  // ../../packages/core/dist/index.js
9085
9093
  import { readFile as readFile22 } from "node:fs/promises";
9086
- import path32 from "node:path";
9094
+ import path52 from "node:path";
9087
9095
 
9088
9096
  // ../../node_modules/.pnpm/subagent@0.4.6/node_modules/subagent/dist/vscode/agentDispatch.js
9089
9097
  import { exec, spawn } from "child_process";
@@ -11021,19 +11029,12 @@ async function provisionSubagents(options) {
11021
11029
  }
11022
11030
 
11023
11031
  // ../../packages/core/dist/index.js
11024
- import { exec as execCallback, spawn as spawn2 } from "node:child_process";
11025
- import { constants as constants22 } from "node:fs";
11026
- import { access as access22, copyFile as copyFile2, mkdtemp, mkdir as mkdir3, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
11027
- import { tmpdir } from "node:os";
11028
- import path52 from "node:path";
11029
- import { promisify as promisify22 } from "node:util";
11030
- import path42 from "node:path";
11031
11032
  import { constants as constants32 } from "node:fs";
11032
11033
  import { access as access32, readFile as readFile3 } from "node:fs/promises";
11033
11034
  import path62 from "node:path";
11034
11035
  import { parse as parse22 } from "yaml";
11035
- import { randomUUID } from "node:crypto";
11036
- import { createHash, randomUUID as randomUUID2 } from "node:crypto";
11036
+ import { randomUUID as randomUUID2 } from "node:crypto";
11037
+ import { createHash, randomUUID as randomUUID3 } from "node:crypto";
11037
11038
  import { mkdir as mkdir22, readFile as readFile4, writeFile as writeFile22 } from "node:fs/promises";
11038
11039
  import path72 from "node:path";
11039
11040
  var TEST_MESSAGE_ROLE_VALUES = ["system", "user", "assistant", "tool"];
@@ -11960,1301 +11961,1558 @@ function formatTimeoutSuffix(timeoutMs) {
11960
11961
  const seconds = Math.ceil(timeoutMs / 1e3);
11961
11962
  return ` after ${seconds}s`;
11962
11963
  }
11963
- var DEFAULT_MOCK_RESPONSE = '{"answer":"Mock provider response. Configure targets.yaml to supply a custom value."}';
11964
- var MockProvider = class {
11964
+ function buildPromptDocument(request, inputFiles, options) {
11965
+ const parts = [];
11966
+ const guidelineFiles = collectGuidelineFiles(
11967
+ inputFiles,
11968
+ options?.guidelinePatterns ?? request.guideline_patterns,
11969
+ options?.guidelineOverrides
11970
+ );
11971
+ const inputFilesList = collectInputFiles(inputFiles);
11972
+ const nonGuidelineInputFiles = inputFilesList.filter(
11973
+ (file) => !guidelineFiles.includes(file)
11974
+ );
11975
+ const prereadBlock = buildMandatoryPrereadBlock(guidelineFiles, nonGuidelineInputFiles);
11976
+ if (prereadBlock.length > 0) {
11977
+ parts.push("\n", prereadBlock);
11978
+ }
11979
+ parts.push("\n[[ ## user_query ## ]]\n", request.prompt.trim());
11980
+ return parts.join("\n").trim();
11981
+ }
11982
+ function normalizeInputFiles2(inputFiles) {
11983
+ if (!inputFiles || inputFiles.length === 0) {
11984
+ return void 0;
11985
+ }
11986
+ const deduped = /* @__PURE__ */ new Map();
11987
+ for (const inputFile of inputFiles) {
11988
+ const absolutePath = path32.resolve(inputFile);
11989
+ if (!deduped.has(absolutePath)) {
11990
+ deduped.set(absolutePath, absolutePath);
11991
+ }
11992
+ }
11993
+ return Array.from(deduped.values());
11994
+ }
11995
+ function collectGuidelineFiles(inputFiles, guidelinePatterns, overrides) {
11996
+ if (!inputFiles || inputFiles.length === 0) {
11997
+ return [];
11998
+ }
11999
+ const unique = /* @__PURE__ */ new Map();
12000
+ for (const inputFile of inputFiles) {
12001
+ const absolutePath = path32.resolve(inputFile);
12002
+ if (overrides?.has(absolutePath)) {
12003
+ if (!unique.has(absolutePath)) {
12004
+ unique.set(absolutePath, absolutePath);
12005
+ }
12006
+ continue;
12007
+ }
12008
+ const normalized = absolutePath.split(path32.sep).join("/");
12009
+ if (isGuidelineFile(normalized, guidelinePatterns)) {
12010
+ if (!unique.has(absolutePath)) {
12011
+ unique.set(absolutePath, absolutePath);
12012
+ }
12013
+ }
12014
+ }
12015
+ return Array.from(unique.values());
12016
+ }
12017
+ function collectInputFiles(inputFiles) {
12018
+ if (!inputFiles || inputFiles.length === 0) {
12019
+ return [];
12020
+ }
12021
+ const unique = /* @__PURE__ */ new Map();
12022
+ for (const inputFile of inputFiles) {
12023
+ const absolutePath = path32.resolve(inputFile);
12024
+ if (!unique.has(absolutePath)) {
12025
+ unique.set(absolutePath, absolutePath);
12026
+ }
12027
+ }
12028
+ return Array.from(unique.values());
12029
+ }
12030
+ function buildMandatoryPrereadBlock(guidelineFiles, inputFiles) {
12031
+ if (guidelineFiles.length === 0 && inputFiles.length === 0) {
12032
+ return "";
12033
+ }
12034
+ const buildList = (files) => files.map((absolutePath) => {
12035
+ const fileName = path32.basename(absolutePath);
12036
+ const fileUri = pathToFileUri2(absolutePath);
12037
+ return `* [${fileName}](${fileUri})`;
12038
+ });
12039
+ const sections = [];
12040
+ if (guidelineFiles.length > 0) {
12041
+ sections.push(`Read all guideline files:
12042
+ ${buildList(guidelineFiles).join("\n")}.`);
12043
+ }
12044
+ if (inputFiles.length > 0) {
12045
+ sections.push(`Read all input files:
12046
+ ${buildList(inputFiles).join("\n")}.`);
12047
+ }
12048
+ sections.push(
12049
+ "If any file is missing, fail with ERROR: missing-file <filename> and stop.",
12050
+ "Then apply system_instructions on the user query below."
12051
+ );
12052
+ return sections.join("\n");
12053
+ }
12054
+ function pathToFileUri2(filePath) {
12055
+ const absolutePath = path32.isAbsolute(filePath) ? filePath : path32.resolve(filePath);
12056
+ const normalizedPath = absolutePath.replace(/\\/g, "/");
12057
+ if (/^[a-zA-Z]:\//.test(normalizedPath)) {
12058
+ return `file:///${normalizedPath}`;
12059
+ }
12060
+ return `file://${normalizedPath}`;
12061
+ }
12062
+ var execAsync22 = promisify22(execCallback);
12063
+ var WORKSPACE_PREFIX = "agentv-codex-";
12064
+ var PROMPT_FILENAME = "prompt.md";
12065
+ var FILES_DIR = "files";
12066
+ var JSONL_TYPE_ITEM_COMPLETED = "item.completed";
12067
+ var CodexProvider = class {
11965
12068
  id;
11966
- kind = "mock";
12069
+ kind = "codex";
11967
12070
  targetName;
11968
- cannedResponse;
11969
- delayMs;
11970
- delayMinMs;
11971
- delayMaxMs;
11972
- constructor(targetName, config) {
11973
- this.id = `mock:${targetName}`;
12071
+ supportsBatch = false;
12072
+ config;
12073
+ runCodex;
12074
+ environmentCheck;
12075
+ resolvedExecutable;
12076
+ constructor(targetName, config, runner = defaultCodexRunner) {
12077
+ this.id = `codex:${targetName}`;
11974
12078
  this.targetName = targetName;
11975
- this.cannedResponse = config.response ?? DEFAULT_MOCK_RESPONSE;
11976
- this.delayMs = config.delayMs ?? 0;
11977
- this.delayMinMs = config.delayMinMs ?? 0;
11978
- this.delayMaxMs = config.delayMaxMs ?? 0;
12079
+ this.config = config;
12080
+ this.runCodex = runner;
11979
12081
  }
11980
12082
  async invoke(request) {
11981
- const delay = this.calculateDelay();
11982
- if (delay > 0) {
11983
- await new Promise((resolve) => setTimeout(resolve, delay));
12083
+ if (request.signal?.aborted) {
12084
+ throw new Error("Codex provider request was aborted before execution");
11984
12085
  }
11985
- return {
11986
- text: this.cannedResponse,
11987
- raw: {
11988
- prompt: request.prompt,
11989
- guidelines: request.guidelines
12086
+ await this.ensureEnvironmentReady();
12087
+ const inputFiles = normalizeInputFiles2(request.inputFiles);
12088
+ const originalGuidelines = new Set(
12089
+ collectGuidelineFiles(inputFiles, request.guideline_patterns).map((file) => path42.resolve(file))
12090
+ );
12091
+ const workspaceRoot = await this.createWorkspace();
12092
+ const logger = await this.createStreamLogger(request).catch(() => void 0);
12093
+ try {
12094
+ const { mirroredInputFiles, guidelineMirrors } = await this.mirrorInputFiles(
12095
+ inputFiles,
12096
+ workspaceRoot,
12097
+ originalGuidelines
12098
+ );
12099
+ const promptContent = buildPromptDocument(request, mirroredInputFiles, {
12100
+ guidelinePatterns: request.guideline_patterns,
12101
+ guidelineOverrides: guidelineMirrors
12102
+ });
12103
+ const promptFile = path42.join(workspaceRoot, PROMPT_FILENAME);
12104
+ await writeFile3(promptFile, promptContent, "utf8");
12105
+ const args = this.buildCodexArgs();
12106
+ const cwd = this.resolveCwd(workspaceRoot);
12107
+ const result = await this.executeCodex(args, cwd, promptContent, request.signal, logger);
12108
+ if (result.timedOut) {
12109
+ throw new Error(
12110
+ `Codex CLI timed out${formatTimeoutSuffix2(this.config.timeoutMs ?? void 0)}`
12111
+ );
11990
12112
  }
11991
- };
12113
+ if (result.exitCode !== 0) {
12114
+ const detail = pickDetail(result.stderr, result.stdout);
12115
+ const prefix = `Codex CLI exited with code ${result.exitCode}`;
12116
+ throw new Error(detail ? `${prefix}: ${detail}` : prefix);
12117
+ }
12118
+ const parsed = parseCodexJson(result.stdout);
12119
+ const assistantText = extractAssistantText(parsed);
12120
+ return {
12121
+ text: assistantText,
12122
+ raw: {
12123
+ response: parsed,
12124
+ stdout: result.stdout,
12125
+ stderr: result.stderr,
12126
+ exitCode: result.exitCode,
12127
+ args,
12128
+ executable: this.resolvedExecutable ?? this.config.executable,
12129
+ promptFile,
12130
+ workspace: workspaceRoot,
12131
+ inputFiles: mirroredInputFiles,
12132
+ logFile: logger?.filePath
12133
+ }
12134
+ };
12135
+ } finally {
12136
+ await logger?.close();
12137
+ await this.cleanupWorkspace(workspaceRoot);
12138
+ }
11992
12139
  }
11993
- calculateDelay() {
11994
- if (this.delayMinMs > 0 || this.delayMaxMs > 0) {
11995
- const min = Math.max(0, this.delayMinMs);
11996
- const max = Math.max(min, this.delayMaxMs);
11997
- return Math.floor(Math.random() * (max - min + 1)) + min;
12140
+ async ensureEnvironmentReady() {
12141
+ if (!this.environmentCheck) {
12142
+ this.environmentCheck = this.validateEnvironment();
11998
12143
  }
11999
- return this.delayMs;
12144
+ await this.environmentCheck;
12000
12145
  }
12001
- };
12002
- var CLI_PLACEHOLDERS = /* @__PURE__ */ new Set(["PROMPT", "GUIDELINES", "EVAL_ID", "ATTEMPT", "FILES"]);
12003
- var BASE_TARGET_SCHEMA = external_exports.object({
12004
- name: external_exports.string().min(1, "target name is required"),
12005
- provider: external_exports.string().min(1, "provider is required"),
12006
- settings: external_exports.record(external_exports.unknown()).optional(),
12007
- judge_target: external_exports.string().optional(),
12008
- workers: external_exports.number().int().min(1).optional()
12009
- });
12010
- var DEFAULT_AZURE_API_VERSION = "2024-10-01-preview";
12011
- function normalizeAzureApiVersion(value) {
12012
- if (!value) {
12013
- return DEFAULT_AZURE_API_VERSION;
12146
+ async validateEnvironment() {
12147
+ this.resolvedExecutable = await locateExecutable(this.config.executable);
12014
12148
  }
12015
- const trimmed = value.trim();
12016
- if (trimmed.length === 0) {
12017
- return DEFAULT_AZURE_API_VERSION;
12149
+ resolveCwd(workspaceRoot) {
12150
+ if (!this.config.cwd) {
12151
+ return workspaceRoot;
12152
+ }
12153
+ return path42.resolve(this.config.cwd);
12018
12154
  }
12019
- const withoutPrefix = trimmed.replace(/^api[-_]?version\s*=\s*/i, "").trim();
12020
- return withoutPrefix.length > 0 ? withoutPrefix : DEFAULT_AZURE_API_VERSION;
12021
- }
12022
- function resolveTargetDefinition(definition, env = process.env) {
12023
- const parsed = BASE_TARGET_SCHEMA.parse(definition);
12024
- const provider = parsed.provider.toLowerCase();
12025
- const providerBatching = resolveOptionalBoolean(
12026
- parsed.settings?.provider_batching ?? parsed.settings?.providerBatching
12027
- );
12028
- switch (provider) {
12029
- case "azure":
12030
- case "azure-openai":
12031
- return {
12032
- kind: "azure",
12033
- name: parsed.name,
12034
- judgeTarget: parsed.judge_target,
12035
- workers: parsed.workers,
12036
- providerBatching,
12037
- config: resolveAzureConfig(parsed, env)
12038
- };
12039
- case "anthropic":
12040
- return {
12041
- kind: "anthropic",
12042
- name: parsed.name,
12043
- judgeTarget: parsed.judge_target,
12044
- workers: parsed.workers,
12045
- providerBatching,
12046
- config: resolveAnthropicConfig(parsed, env)
12047
- };
12048
- case "gemini":
12049
- case "google":
12050
- case "google-gemini":
12051
- return {
12052
- kind: "gemini",
12053
- name: parsed.name,
12054
- judgeTarget: parsed.judge_target,
12055
- workers: parsed.workers,
12056
- providerBatching,
12057
- config: resolveGeminiConfig(parsed, env)
12058
- };
12059
- case "codex":
12060
- case "codex-cli":
12061
- return {
12062
- kind: "codex",
12063
- name: parsed.name,
12064
- judgeTarget: parsed.judge_target,
12065
- workers: parsed.workers,
12066
- providerBatching,
12067
- config: resolveCodexConfig(parsed, env)
12068
- };
12069
- case "mock":
12070
- return {
12071
- kind: "mock",
12072
- name: parsed.name,
12073
- judgeTarget: parsed.judge_target,
12074
- workers: parsed.workers,
12075
- providerBatching,
12076
- config: resolveMockConfig(parsed)
12077
- };
12078
- case "vscode":
12079
- case "vscode-insiders":
12080
- return {
12081
- kind: provider,
12082
- name: parsed.name,
12083
- judgeTarget: parsed.judge_target,
12084
- workers: parsed.workers,
12085
- providerBatching,
12086
- config: resolveVSCodeConfig(parsed, env, provider === "vscode-insiders")
12087
- };
12088
- case "cli":
12155
+ buildCodexArgs() {
12156
+ const args = ["--ask-for-approval", "never", "exec", "--json", "--color", "never", "--skip-git-repo-check"];
12157
+ if (this.config.args && this.config.args.length > 0) {
12158
+ args.push(...this.config.args);
12159
+ }
12160
+ args.push("-");
12161
+ return args;
12162
+ }
12163
+ async executeCodex(args, cwd, promptContent, signal, logger) {
12164
+ try {
12165
+ return await this.runCodex({
12166
+ executable: this.resolvedExecutable ?? this.config.executable,
12167
+ args,
12168
+ cwd,
12169
+ prompt: promptContent,
12170
+ timeoutMs: this.config.timeoutMs,
12171
+ env: process.env,
12172
+ signal,
12173
+ onStdoutChunk: logger ? (chunk) => logger.handleStdoutChunk(chunk) : void 0,
12174
+ onStderrChunk: logger ? (chunk) => logger.handleStderrChunk(chunk) : void 0
12175
+ });
12176
+ } catch (error) {
12177
+ const err = error;
12178
+ if (err.code === "ENOENT") {
12179
+ throw new Error(
12180
+ `Codex executable '${this.config.executable}' was not found. Update the target settings.executable or add it to PATH.`
12181
+ );
12182
+ }
12183
+ throw error;
12184
+ }
12185
+ }
12186
+ async mirrorInputFiles(inputFiles, workspaceRoot, guidelineOriginals) {
12187
+ if (!inputFiles || inputFiles.length === 0) {
12089
12188
  return {
12090
- kind: "cli",
12091
- name: parsed.name,
12092
- judgeTarget: parsed.judge_target,
12093
- workers: parsed.workers,
12094
- providerBatching,
12095
- config: resolveCliConfig(parsed, env)
12189
+ mirroredInputFiles: void 0,
12190
+ guidelineMirrors: /* @__PURE__ */ new Set()
12096
12191
  };
12097
- default:
12098
- throw new Error(`Unsupported provider '${parsed.provider}' in target '${parsed.name}'`);
12192
+ }
12193
+ const filesRoot = path42.join(workspaceRoot, FILES_DIR);
12194
+ await mkdir3(filesRoot, { recursive: true });
12195
+ const mirrored = [];
12196
+ const guidelineMirrors = /* @__PURE__ */ new Set();
12197
+ const nameCounts = /* @__PURE__ */ new Map();
12198
+ for (const inputFile of inputFiles) {
12199
+ const absoluteSource = path42.resolve(inputFile);
12200
+ const baseName = path42.basename(absoluteSource);
12201
+ const count = nameCounts.get(baseName) ?? 0;
12202
+ nameCounts.set(baseName, count + 1);
12203
+ const finalName = count === 0 ? baseName : `${baseName}.${count}`;
12204
+ const destination = path42.join(filesRoot, finalName);
12205
+ await copyFile2(absoluteSource, destination);
12206
+ const resolvedDestination = path42.resolve(destination);
12207
+ mirrored.push(resolvedDestination);
12208
+ if (guidelineOriginals.has(absoluteSource)) {
12209
+ guidelineMirrors.add(resolvedDestination);
12210
+ }
12211
+ }
12212
+ return {
12213
+ mirroredInputFiles: mirrored,
12214
+ guidelineMirrors
12215
+ };
12099
12216
  }
12100
- }
12101
- function resolveAzureConfig(target, env) {
12102
- const settings = target.settings ?? {};
12103
- const endpointSource = settings.endpoint ?? settings.resource ?? settings.resourceName;
12104
- const apiKeySource = settings.api_key ?? settings.apiKey;
12105
- const deploymentSource = settings.deployment ?? settings.deploymentName ?? settings.model;
12106
- const versionSource = settings.version ?? settings.api_version;
12107
- const temperatureSource = settings.temperature;
12108
- const maxTokensSource = settings.max_output_tokens ?? settings.maxTokens;
12109
- const resourceName = resolveString(endpointSource, env, `${target.name} endpoint`);
12110
- const apiKey = resolveString(apiKeySource, env, `${target.name} api key`);
12111
- const deploymentName = resolveString(deploymentSource, env, `${target.name} deployment`);
12112
- const version = normalizeAzureApiVersion(
12113
- resolveOptionalString(versionSource, env, `${target.name} api version`)
12114
- );
12115
- const temperature = resolveOptionalNumber(temperatureSource, `${target.name} temperature`);
12116
- const maxOutputTokens = resolveOptionalNumber(
12117
- maxTokensSource,
12118
- `${target.name} max output tokens`
12119
- );
12120
- return {
12121
- resourceName,
12122
- deploymentName,
12123
- apiKey,
12124
- version,
12125
- temperature,
12126
- maxOutputTokens
12127
- };
12128
- }
12129
- function resolveAnthropicConfig(target, env) {
12130
- const settings = target.settings ?? {};
12131
- const apiKeySource = settings.api_key ?? settings.apiKey;
12132
- const modelSource = settings.model ?? settings.deployment ?? settings.variant;
12133
- const temperatureSource = settings.temperature;
12134
- const maxTokensSource = settings.max_output_tokens ?? settings.maxTokens;
12135
- const thinkingBudgetSource = settings.thinking_budget ?? settings.thinkingBudget;
12136
- const apiKey = resolveString(apiKeySource, env, `${target.name} Anthropic api key`);
12137
- const model = resolveString(modelSource, env, `${target.name} Anthropic model`);
12138
- return {
12139
- apiKey,
12140
- model,
12141
- temperature: resolveOptionalNumber(temperatureSource, `${target.name} temperature`),
12142
- maxOutputTokens: resolveOptionalNumber(maxTokensSource, `${target.name} max output tokens`),
12143
- thinkingBudget: resolveOptionalNumber(thinkingBudgetSource, `${target.name} thinking budget`)
12144
- };
12145
- }
12146
- function resolveGeminiConfig(target, env) {
12147
- const settings = target.settings ?? {};
12148
- const apiKeySource = settings.api_key ?? settings.apiKey;
12149
- const modelSource = settings.model ?? settings.deployment ?? settings.variant;
12150
- const temperatureSource = settings.temperature;
12151
- const maxTokensSource = settings.max_output_tokens ?? settings.maxTokens;
12152
- const apiKey = resolveString(apiKeySource, env, `${target.name} Google API key`);
12153
- const model = resolveOptionalString(modelSource, env, `${target.name} Gemini model`, {
12154
- allowLiteral: true,
12155
- optionalEnv: true
12156
- }) ?? "gemini-2.5-flash";
12157
- return {
12158
- apiKey,
12159
- model,
12160
- temperature: resolveOptionalNumber(temperatureSource, `${target.name} temperature`),
12161
- maxOutputTokens: resolveOptionalNumber(maxTokensSource, `${target.name} max output tokens`)
12162
- };
12163
- }
12164
- function resolveCodexConfig(target, env) {
12165
- const settings = target.settings ?? {};
12166
- const executableSource = settings.executable ?? settings.command ?? settings.binary;
12167
- const argsSource = settings.args ?? settings.arguments;
12168
- const cwdSource = settings.cwd;
12169
- const timeoutSource = settings.timeout_seconds ?? settings.timeoutSeconds;
12170
- const executable = resolveOptionalString(executableSource, env, `${target.name} codex executable`, {
12171
- allowLiteral: true,
12172
- optionalEnv: true
12173
- }) ?? "codex";
12174
- const args = resolveOptionalStringArray(argsSource, env, `${target.name} codex args`);
12175
- const cwd = resolveOptionalString(cwdSource, env, `${target.name} codex cwd`, {
12176
- allowLiteral: true,
12177
- optionalEnv: true
12178
- });
12179
- const timeoutMs = resolveTimeoutMs(timeoutSource, `${target.name} codex timeout`);
12180
- return {
12181
- executable,
12182
- args,
12183
- cwd,
12184
- timeoutMs
12185
- };
12186
- }
12187
- function resolveMockConfig(target) {
12188
- const settings = target.settings ?? {};
12189
- const response = typeof settings.response === "string" ? settings.response : void 0;
12190
- return { response };
12191
- }
12192
- function resolveVSCodeConfig(target, env, insiders) {
12193
- const settings = target.settings ?? {};
12194
- const workspaceTemplateEnvVar = resolveOptionalLiteralString(settings.workspace_template ?? settings.workspaceTemplate);
12195
- const workspaceTemplate = workspaceTemplateEnvVar ? resolveOptionalString(workspaceTemplateEnvVar, env, `${target.name} workspace template path`, {
12196
- allowLiteral: false,
12197
- optionalEnv: true
12198
- }) : void 0;
12199
- const commandSource = settings.vscode_cmd ?? settings.command;
12200
- const waitSource = settings.wait;
12201
- const dryRunSource = settings.dry_run ?? settings.dryRun;
12202
- const subagentRootSource = settings.subagent_root ?? settings.subagentRoot;
12203
- const defaultCommand = insiders ? "code-insiders" : "code";
12204
- const command = resolveOptionalLiteralString(commandSource) ?? defaultCommand;
12205
- return {
12206
- command,
12207
- waitForResponse: resolveOptionalBoolean(waitSource) ?? true,
12208
- dryRun: resolveOptionalBoolean(dryRunSource) ?? false,
12209
- subagentRoot: resolveOptionalString(subagentRootSource, env, `${target.name} subagent root`, {
12210
- allowLiteral: true,
12211
- optionalEnv: true
12212
- }),
12213
- workspaceTemplate
12214
- };
12215
- }
12216
- function resolveCliConfig(target, env) {
12217
- const settings = target.settings ?? {};
12218
- const commandTemplateSource = settings.command_template ?? settings.commandTemplate;
12219
- const filesFormat = resolveOptionalLiteralString(
12220
- settings.files_format ?? settings.filesFormat ?? settings.attachments_format ?? settings.attachmentsFormat
12221
- );
12222
- const cwd = resolveOptionalString(settings.cwd, env, `${target.name} working directory`, {
12223
- allowLiteral: true,
12224
- optionalEnv: true
12225
- });
12226
- const envOverrides = resolveEnvOverrides(settings.env, env, target.name);
12227
- const timeoutMs = resolveTimeoutMs(settings.timeout_seconds ?? settings.timeoutSeconds, `${target.name} timeout`);
12228
- const healthcheck = resolveCliHealthcheck(settings.healthcheck, env, target.name);
12229
- const commandTemplate = resolveString(
12230
- commandTemplateSource,
12231
- env,
12232
- `${target.name} CLI command template`,
12233
- true
12234
- );
12235
- assertSupportedCliPlaceholders(commandTemplate, `${target.name} CLI command template`);
12236
- return {
12237
- commandTemplate,
12238
- filesFormat,
12239
- cwd,
12240
- env: envOverrides,
12241
- timeoutMs,
12242
- healthcheck
12243
- };
12244
- }
12245
- function resolveEnvOverrides(source2, env, targetName) {
12246
- if (source2 === void 0 || source2 === null) {
12247
- return void 0;
12217
+ async createWorkspace() {
12218
+ return await mkdtemp(path42.join(tmpdir(), WORKSPACE_PREFIX));
12248
12219
  }
12249
- if (typeof source2 !== "object" || Array.isArray(source2)) {
12250
- throw new Error(`${targetName} env overrides must be an object map of strings`);
12220
+ async cleanupWorkspace(workspaceRoot) {
12221
+ try {
12222
+ await rm2(workspaceRoot, { recursive: true, force: true });
12223
+ } catch {
12224
+ }
12251
12225
  }
12252
- const entries = Object.entries(source2);
12253
- const resolved = {};
12254
- for (const [key2, value] of entries) {
12255
- if (typeof value !== "string") {
12256
- throw new Error(`${targetName} env override '${key2}' must be a string`);
12226
+ resolveLogDirectory() {
12227
+ const disabled = isCodexLogStreamingDisabled();
12228
+ if (disabled) {
12229
+ return void 0;
12230
+ }
12231
+ if (this.config.logDir) {
12232
+ return path42.resolve(this.config.logDir);
12233
+ }
12234
+ return path42.join(process.cwd(), ".agentv", "logs", "codex");
12235
+ }
12236
+ async createStreamLogger(request) {
12237
+ const logDir = this.resolveLogDirectory();
12238
+ if (!logDir) {
12239
+ return void 0;
12240
+ }
12241
+ try {
12242
+ await mkdir3(logDir, { recursive: true });
12243
+ } catch (error) {
12244
+ const message = error instanceof Error ? error.message : String(error);
12245
+ console.warn(`Skipping Codex stream logging (could not create ${logDir}): ${message}`);
12246
+ return void 0;
12247
+ }
12248
+ const filePath = path42.join(logDir, buildLogFilename(request, this.targetName));
12249
+ try {
12250
+ const logger = await CodexStreamLogger.create({
12251
+ filePath,
12252
+ targetName: this.targetName,
12253
+ evalCaseId: request.evalCaseId,
12254
+ attempt: request.attempt,
12255
+ format: this.config.logFormat ?? "summary"
12256
+ });
12257
+ console.log(`Streaming Codex CLI output to ${filePath}`);
12258
+ return logger;
12259
+ } catch (error) {
12260
+ const message = error instanceof Error ? error.message : String(error);
12261
+ console.warn(`Skipping Codex stream logging for ${filePath}: ${message}`);
12262
+ return void 0;
12257
12263
  }
12258
- const resolvedValue = resolveString(value, env, `${targetName} env override '${key2}'`);
12259
- resolved[key2] = resolvedValue;
12260
12264
  }
12261
- return Object.keys(resolved).length > 0 ? resolved : void 0;
12262
- }
12263
- function resolveTimeoutMs(source2, description) {
12264
- const seconds = resolveOptionalNumber(source2, `${description} (seconds)`);
12265
- if (seconds === void 0) {
12266
- return void 0;
12265
+ };
12266
+ var CodexStreamLogger = class _CodexStreamLogger {
12267
+ filePath;
12268
+ stream;
12269
+ startedAt = Date.now();
12270
+ stdoutBuffer = "";
12271
+ stderrBuffer = "";
12272
+ format;
12273
+ constructor(filePath, format) {
12274
+ this.filePath = filePath;
12275
+ this.format = format;
12276
+ this.stream = createWriteStream(filePath, { flags: "a" });
12277
+ }
12278
+ static async create(options) {
12279
+ const logger = new _CodexStreamLogger(options.filePath, options.format);
12280
+ const header = [
12281
+ "# Codex CLI stream log",
12282
+ `# target: ${options.targetName}`,
12283
+ options.evalCaseId ? `# eval: ${options.evalCaseId}` : void 0,
12284
+ options.attempt !== void 0 ? `# attempt: ${options.attempt + 1}` : void 0,
12285
+ `# started: ${(/* @__PURE__ */ new Date()).toISOString()}`,
12286
+ ""
12287
+ ].filter((line2) => Boolean(line2));
12288
+ logger.writeLines(header);
12289
+ return logger;
12290
+ }
12291
+ handleStdoutChunk(chunk) {
12292
+ this.stdoutBuffer += chunk;
12293
+ this.flushBuffer("stdout");
12294
+ }
12295
+ handleStderrChunk(chunk) {
12296
+ this.stderrBuffer += chunk;
12297
+ this.flushBuffer("stderr");
12267
12298
  }
12268
- if (seconds <= 0) {
12269
- throw new Error(`${description} must be greater than zero seconds`);
12299
+ async close() {
12300
+ this.flushBuffer("stdout");
12301
+ this.flushBuffer("stderr");
12302
+ this.flushRemainder();
12303
+ await new Promise((resolve, reject) => {
12304
+ this.stream.once("error", reject);
12305
+ this.stream.end(() => resolve());
12306
+ });
12270
12307
  }
12271
- return Math.floor(seconds * 1e3);
12272
- }
12273
- function resolveCliHealthcheck(source2, env, targetName) {
12274
- if (source2 === void 0 || source2 === null) {
12275
- return void 0;
12308
+ writeLines(lines) {
12309
+ for (const line2 of lines) {
12310
+ this.stream.write(`${line2}
12311
+ `);
12312
+ }
12276
12313
  }
12277
- if (typeof source2 !== "object" || Array.isArray(source2)) {
12278
- throw new Error(`${targetName} healthcheck must be an object`);
12314
+ flushBuffer(source2) {
12315
+ const buffer2 = source2 === "stdout" ? this.stdoutBuffer : this.stderrBuffer;
12316
+ const lines = buffer2.split(/\r?\n/);
12317
+ const remainder = lines.pop() ?? "";
12318
+ if (source2 === "stdout") {
12319
+ this.stdoutBuffer = remainder;
12320
+ } else {
12321
+ this.stderrBuffer = remainder;
12322
+ }
12323
+ for (const line2 of lines) {
12324
+ const formatted = this.formatLine(line2, source2);
12325
+ if (formatted) {
12326
+ this.stream.write(formatted);
12327
+ this.stream.write("\n");
12328
+ }
12329
+ }
12279
12330
  }
12280
- const candidate = source2;
12281
- const type = candidate.type;
12282
- const timeoutMs = resolveTimeoutMs(
12283
- candidate.timeout_seconds ?? candidate.timeoutSeconds,
12284
- `${targetName} healthcheck timeout`
12285
- );
12286
- if (type === "http") {
12287
- const url = resolveString(candidate.url, env, `${targetName} healthcheck URL`);
12288
- return {
12289
- type: "http",
12290
- url,
12291
- timeoutMs
12292
- };
12331
+ formatLine(rawLine, source2) {
12332
+ const trimmed = rawLine.trim();
12333
+ if (trimmed.length === 0) {
12334
+ return void 0;
12335
+ }
12336
+ const message = this.format === "json" ? formatCodexJsonLog(trimmed) : formatCodexLogMessage(trimmed, source2);
12337
+ return `[+${formatElapsed(this.startedAt)}] [${source2}] ${message}`;
12293
12338
  }
12294
- if (type === "command") {
12295
- const commandTemplate = resolveString(
12296
- candidate.command_template ?? candidate.commandTemplate,
12297
- env,
12298
- `${targetName} healthcheck command template`,
12299
- true
12300
- );
12301
- assertSupportedCliPlaceholders(commandTemplate, `${targetName} healthcheck command template`);
12302
- const cwd = resolveOptionalString(candidate.cwd, env, `${targetName} healthcheck cwd`, {
12303
- allowLiteral: true,
12304
- optionalEnv: true
12305
- });
12306
- return {
12307
- type: "command",
12308
- commandTemplate,
12309
- timeoutMs,
12310
- cwd
12311
- };
12339
+ flushRemainder() {
12340
+ const stdoutRemainder = this.stdoutBuffer.trim();
12341
+ if (stdoutRemainder.length > 0) {
12342
+ const formatted = this.formatLine(stdoutRemainder, "stdout");
12343
+ if (formatted) {
12344
+ this.stream.write(formatted);
12345
+ this.stream.write("\n");
12346
+ }
12347
+ }
12348
+ const stderrRemainder = this.stderrBuffer.trim();
12349
+ if (stderrRemainder.length > 0) {
12350
+ const formatted = this.formatLine(stderrRemainder, "stderr");
12351
+ if (formatted) {
12352
+ this.stream.write(formatted);
12353
+ this.stream.write("\n");
12354
+ }
12355
+ }
12356
+ this.stdoutBuffer = "";
12357
+ this.stderrBuffer = "";
12312
12358
  }
12313
- throw new Error(`${targetName} healthcheck type must be 'http' or 'command'`);
12359
+ };
12360
+ function isCodexLogStreamingDisabled() {
12361
+ const envValue = process.env.AGENTV_CODEX_STREAM_LOGS;
12362
+ if (!envValue) {
12363
+ return false;
12364
+ }
12365
+ const normalized = envValue.trim().toLowerCase();
12366
+ return normalized === "false" || normalized === "0" || normalized === "off";
12314
12367
  }
12315
- function assertSupportedCliPlaceholders(template, description) {
12316
- const placeholders = extractCliPlaceholders(template);
12317
- for (const placeholder of placeholders) {
12318
- if (!CLI_PLACEHOLDERS.has(placeholder)) {
12319
- throw new Error(
12320
- `${description} includes unsupported placeholder '{${placeholder}}'. Supported placeholders: ${Array.from(CLI_PLACEHOLDERS).join(", ")}`
12321
- );
12322
- }
12368
+ function buildLogFilename(request, targetName) {
12369
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12370
+ const evalId = sanitizeForFilename(request.evalCaseId ?? "codex");
12371
+ const attemptSuffix = request.attempt !== void 0 ? `_attempt-${request.attempt + 1}` : "";
12372
+ const target = sanitizeForFilename(targetName);
12373
+ return `${timestamp}_${target}_${evalId}${attemptSuffix}_${randomUUID().slice(0, 8)}.log`;
12374
+ }
12375
+ function sanitizeForFilename(value) {
12376
+ const sanitized = value.replace(/[^A-Za-z0-9._-]+/g, "_");
12377
+ return sanitized.length > 0 ? sanitized : "codex";
12378
+ }
12379
+ function formatElapsed(startedAt) {
12380
+ const elapsedSeconds = Math.floor((Date.now() - startedAt) / 1e3);
12381
+ const hours = Math.floor(elapsedSeconds / 3600);
12382
+ const minutes = Math.floor(elapsedSeconds % 3600 / 60);
12383
+ const seconds = elapsedSeconds % 60;
12384
+ if (hours > 0) {
12385
+ return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
12323
12386
  }
12387
+ return `${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
12324
12388
  }
12325
- function extractCliPlaceholders(template) {
12326
- const matches = template.matchAll(/\{([A-Z_]+)\}/g);
12327
- const results = [];
12328
- for (const match of matches) {
12329
- if (match[1]) {
12330
- results.push(match[1]);
12389
+ function formatCodexLogMessage(rawLine, source2) {
12390
+ const parsed = tryParseJsonValue(rawLine);
12391
+ if (parsed) {
12392
+ const summary = summarizeCodexEvent(parsed);
12393
+ if (summary) {
12394
+ return summary;
12331
12395
  }
12332
12396
  }
12333
- return results;
12334
- }
12335
- function resolveString(source2, env, description, allowLiteral = false) {
12336
- const value = resolveOptionalString(source2, env, description, {
12337
- allowLiteral,
12338
- optionalEnv: false
12339
- });
12340
- if (value === void 0) {
12341
- throw new Error(`${description} is required`);
12397
+ if (source2 === "stderr") {
12398
+ return `stderr: ${rawLine}`;
12342
12399
  }
12343
- return value;
12400
+ return rawLine;
12344
12401
  }
12345
- function resolveOptionalString(source2, env, description, options) {
12346
- if (source2 === void 0 || source2 === null) {
12347
- return void 0;
12402
+ function formatCodexJsonLog(rawLine) {
12403
+ const parsed = tryParseJsonValue(rawLine);
12404
+ if (!parsed) {
12405
+ return rawLine;
12348
12406
  }
12349
- if (typeof source2 !== "string") {
12350
- throw new Error(`${description} must be a string`);
12407
+ try {
12408
+ return JSON.stringify(parsed, null, 2);
12409
+ } catch {
12410
+ return rawLine;
12351
12411
  }
12352
- const trimmed = source2.trim();
12353
- if (trimmed.length === 0) {
12412
+ }
12413
+ function summarizeCodexEvent(event) {
12414
+ if (!event || typeof event !== "object") {
12354
12415
  return void 0;
12355
12416
  }
12356
- const envValue = env[trimmed];
12357
- if (envValue !== void 0) {
12358
- if (envValue.trim().length === 0) {
12359
- throw new Error(`Environment variable '${trimmed}' for ${description} is empty`);
12417
+ const record = event;
12418
+ const type = typeof record.type === "string" ? record.type : void 0;
12419
+ let message = extractFromEvent(event) ?? extractFromItem(record.item) ?? flattenContent(record.output ?? record.content);
12420
+ if (!message && type === JSONL_TYPE_ITEM_COMPLETED) {
12421
+ const item = record.item;
12422
+ if (item && typeof item === "object") {
12423
+ const candidate = flattenContent(
12424
+ item.text ?? item.content ?? item.output
12425
+ );
12426
+ if (candidate) {
12427
+ message = candidate;
12428
+ }
12360
12429
  }
12361
- return envValue;
12362
12430
  }
12363
- const allowLiteral = options?.allowLiteral ?? false;
12364
- const optionalEnv = options?.optionalEnv ?? false;
12365
- const looksLikeEnv = isLikelyEnvReference(trimmed);
12366
- if (looksLikeEnv) {
12367
- if (optionalEnv) {
12368
- return void 0;
12431
+ if (!message) {
12432
+ const itemType = typeof record.item?.type === "string" ? record.item.type : void 0;
12433
+ if (type && itemType) {
12434
+ return `${type}:${itemType}`;
12369
12435
  }
12370
- if (!allowLiteral) {
12371
- throw new Error(`Environment variable '${trimmed}' required for ${description} is not set`);
12436
+ if (type) {
12437
+ return type;
12372
12438
  }
12373
12439
  }
12374
- return trimmed;
12375
- }
12376
- function resolveOptionalLiteralString(source2) {
12377
- if (source2 === void 0 || source2 === null) {
12378
- return void 0;
12440
+ if (type && message) {
12441
+ return `${type}: ${message}`;
12379
12442
  }
12380
- if (typeof source2 !== "string") {
12381
- throw new Error("expected string value");
12443
+ if (message) {
12444
+ return message;
12382
12445
  }
12383
- const trimmed = source2.trim();
12384
- return trimmed.length > 0 ? trimmed : void 0;
12446
+ return type;
12385
12447
  }
12386
- function resolveOptionalNumber(source2, description) {
12387
- if (source2 === void 0 || source2 === null || source2 === "") {
12448
+ function tryParseJsonValue(rawLine) {
12449
+ try {
12450
+ return JSON.parse(rawLine);
12451
+ } catch {
12388
12452
  return void 0;
12389
12453
  }
12390
- if (typeof source2 === "number") {
12391
- return Number.isFinite(source2) ? source2 : void 0;
12454
+ }
12455
+ async function locateExecutable(candidate) {
12456
+ const includesPathSeparator = candidate.includes("/") || candidate.includes("\\");
12457
+ if (includesPathSeparator) {
12458
+ const resolved = path42.isAbsolute(candidate) ? candidate : path42.resolve(candidate);
12459
+ const executablePath = await ensureWindowsExecutableVariant(resolved);
12460
+ await access22(executablePath, constants22.F_OK);
12461
+ return executablePath;
12392
12462
  }
12393
- if (typeof source2 === "string") {
12394
- const numeric = Number(source2);
12395
- if (Number.isFinite(numeric)) {
12396
- return numeric;
12463
+ const locator = process.platform === "win32" ? "where" : "which";
12464
+ try {
12465
+ const { stdout } = await execAsync22(`${locator} ${candidate}`);
12466
+ const lines = stdout.split(/\r?\n/).map((line2) => line2.trim()).filter((line2) => line2.length > 0);
12467
+ const preferred = selectExecutableCandidate(lines);
12468
+ if (preferred) {
12469
+ const executablePath = await ensureWindowsExecutableVariant(preferred);
12470
+ await access22(executablePath, constants22.F_OK);
12471
+ return executablePath;
12397
12472
  }
12473
+ } catch {
12398
12474
  }
12399
- throw new Error(`${description} must be a number`);
12475
+ throw new Error(`Codex executable '${candidate}' was not found on PATH`);
12400
12476
  }
12401
- function resolveOptionalBoolean(source2) {
12402
- if (source2 === void 0 || source2 === null || source2 === "") {
12477
+ function selectExecutableCandidate(candidates) {
12478
+ if (candidates.length === 0) {
12403
12479
  return void 0;
12404
12480
  }
12405
- if (typeof source2 === "boolean") {
12406
- return source2;
12481
+ if (process.platform !== "win32") {
12482
+ return candidates[0];
12407
12483
  }
12408
- if (typeof source2 === "string") {
12409
- const lowered = source2.trim().toLowerCase();
12410
- if (lowered === "true" || lowered === "1") {
12411
- return true;
12484
+ const extensions = getWindowsExecutableExtensions();
12485
+ for (const ext of extensions) {
12486
+ const match = candidates.find((candidate) => candidate.toLowerCase().endsWith(ext));
12487
+ if (match) {
12488
+ return match;
12412
12489
  }
12413
- if (lowered === "false" || lowered === "0") {
12414
- return false;
12490
+ }
12491
+ return candidates[0];
12492
+ }
12493
+ async function ensureWindowsExecutableVariant(candidate) {
12494
+ if (process.platform !== "win32") {
12495
+ return candidate;
12496
+ }
12497
+ if (hasExecutableExtension(candidate)) {
12498
+ return candidate;
12499
+ }
12500
+ const extensions = getWindowsExecutableExtensions();
12501
+ for (const ext of extensions) {
12502
+ const withExtension = `${candidate}${ext}`;
12503
+ try {
12504
+ await access22(withExtension, constants22.F_OK);
12505
+ return withExtension;
12506
+ } catch {
12415
12507
  }
12416
12508
  }
12417
- throw new Error("expected boolean value");
12509
+ return candidate;
12418
12510
  }
12419
- function isLikelyEnvReference(value) {
12420
- return /^[A-Z0-9_]+$/.test(value);
12511
+ function hasExecutableExtension(candidate) {
12512
+ const lower = candidate.toLowerCase();
12513
+ return getWindowsExecutableExtensions().some((ext) => lower.endsWith(ext));
12421
12514
  }
12422
- function resolveOptionalStringArray(source2, env, description) {
12423
- if (source2 === void 0 || source2 === null) {
12424
- return void 0;
12425
- }
12426
- if (!Array.isArray(source2)) {
12427
- throw new Error(`${description} must be an array of strings`);
12515
+ var DEFAULT_WINDOWS_EXTENSIONS = [".com", ".exe", ".bat", ".cmd", ".ps1"];
12516
+ function getWindowsExecutableExtensions() {
12517
+ if (process.platform !== "win32") {
12518
+ return [];
12428
12519
  }
12429
- if (source2.length === 0) {
12430
- return void 0;
12520
+ const fromEnv = process.env.PATHEXT?.split(";").map((ext) => ext.trim().toLowerCase()).filter((ext) => ext.length > 0);
12521
+ return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_WINDOWS_EXTENSIONS;
12522
+ }
12523
+ function parseCodexJson(output) {
12524
+ const trimmed = output.trim();
12525
+ if (trimmed.length === 0) {
12526
+ throw new Error("Codex CLI produced no output in --json mode");
12431
12527
  }
12432
- const resolved = [];
12433
- for (let i6 = 0; i6 < source2.length; i6++) {
12434
- const item = source2[i6];
12435
- if (typeof item !== "string") {
12436
- throw new Error(`${description}[${i6}] must be a string`);
12437
- }
12438
- const trimmed = item.trim();
12439
- if (trimmed.length === 0) {
12440
- throw new Error(`${description}[${i6}] cannot be empty`);
12441
- }
12442
- const envValue = env[trimmed];
12443
- if (envValue !== void 0) {
12444
- if (envValue.trim().length === 0) {
12445
- throw new Error(`Environment variable '${trimmed}' for ${description}[${i6}] is empty`);
12528
+ try {
12529
+ return JSON.parse(trimmed);
12530
+ } catch {
12531
+ const lineObjects = parseJsonLines(trimmed);
12532
+ if (lineObjects) {
12533
+ return lineObjects;
12534
+ }
12535
+ const lastBrace = trimmed.lastIndexOf("{");
12536
+ if (lastBrace >= 0) {
12537
+ const candidate = trimmed.slice(lastBrace);
12538
+ try {
12539
+ return JSON.parse(candidate);
12540
+ } catch {
12446
12541
  }
12447
- resolved.push(envValue);
12448
- } else {
12449
- resolved.push(trimmed);
12450
12542
  }
12543
+ const preview = trimmed.slice(0, 200);
12544
+ throw new Error(`Codex CLI emitted invalid JSON: ${preview}${trimmed.length > 200 ? "\u2026" : ""}`);
12451
12545
  }
12452
- return resolved.length > 0 ? resolved : void 0;
12453
12546
  }
12454
- var VSCodeProvider = class {
12455
- id;
12456
- kind;
12457
- targetName;
12458
- supportsBatch = true;
12459
- config;
12460
- constructor(targetName, config, kind) {
12461
- this.id = `${kind}:${targetName}`;
12462
- this.kind = kind;
12463
- this.targetName = targetName;
12464
- this.config = config;
12465
- }
12466
- async invoke(request) {
12467
- if (request.signal?.aborted) {
12468
- throw new Error("VS Code provider request was aborted before dispatch");
12469
- }
12470
- const inputFiles = normalizeAttachments(request.inputFiles);
12471
- const promptContent = buildPromptDocument(request, inputFiles, request.guideline_patterns);
12472
- const session = await dispatchAgentSession({
12473
- userQuery: promptContent,
12474
- extraAttachments: inputFiles,
12475
- wait: this.config.waitForResponse,
12476
- dryRun: this.config.dryRun,
12477
- vscodeCmd: this.config.command,
12478
- subagentRoot: this.config.subagentRoot,
12479
- workspaceTemplate: this.config.workspaceTemplate,
12480
- silent: true
12481
- });
12482
- if (session.exitCode !== 0 || !session.responseFile) {
12483
- const failure = session.error ?? "VS Code subagent did not produce a response";
12484
- throw new Error(failure);
12485
- }
12486
- if (this.config.dryRun) {
12487
- return {
12488
- text: "",
12489
- raw: {
12490
- session,
12491
- inputFiles
12492
- }
12493
- };
12547
+ function extractAssistantText(parsed) {
12548
+ if (Array.isArray(parsed)) {
12549
+ const text = extractFromEventStream(parsed);
12550
+ if (text) {
12551
+ return text;
12494
12552
  }
12495
- const responseText = await readFile22(session.responseFile, "utf8");
12496
- return {
12497
- text: responseText,
12498
- raw: {
12499
- session,
12500
- inputFiles
12501
- }
12502
- };
12503
12553
  }
12504
- async invokeBatch(requests) {
12505
- if (requests.length === 0) {
12506
- return [];
12507
- }
12508
- const normalizedRequests = requests.map((req) => ({
12509
- request: req,
12510
- inputFiles: normalizeAttachments(req.inputFiles)
12511
- }));
12512
- const combinedInputFiles = mergeAttachments(
12513
- normalizedRequests.map(({ inputFiles }) => inputFiles)
12514
- );
12515
- const userQueries = normalizedRequests.map(
12516
- ({ request, inputFiles }) => buildPromptDocument(request, inputFiles, request.guideline_patterns)
12517
- );
12518
- const session = await dispatchBatchAgent({
12519
- userQueries,
12520
- extraAttachments: combinedInputFiles,
12521
- wait: this.config.waitForResponse,
12522
- dryRun: this.config.dryRun,
12523
- vscodeCmd: this.config.command,
12524
- subagentRoot: this.config.subagentRoot,
12525
- workspaceTemplate: this.config.workspaceTemplate,
12526
- silent: true
12527
- });
12528
- if (session.exitCode !== 0 || !session.responseFiles) {
12529
- const failure = session.error ?? "VS Code subagent did not produce batch responses";
12530
- throw new Error(failure);
12554
+ if (!parsed || typeof parsed !== "object") {
12555
+ throw new Error("Codex CLI JSON response did not include an assistant message");
12556
+ }
12557
+ const record = parsed;
12558
+ const eventText = extractFromEvent(record);
12559
+ if (eventText) {
12560
+ return eventText;
12561
+ }
12562
+ const messages = Array.isArray(record.messages) ? record.messages : void 0;
12563
+ if (messages) {
12564
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
12565
+ const entry = messages[index];
12566
+ if (!entry || typeof entry !== "object") {
12567
+ continue;
12568
+ }
12569
+ const role = entry.role;
12570
+ if (role !== "assistant") {
12571
+ continue;
12572
+ }
12573
+ const content = entry.content;
12574
+ const flattened = flattenContent(content);
12575
+ if (flattened) {
12576
+ return flattened;
12577
+ }
12531
12578
  }
12532
- if (this.config.dryRun) {
12533
- return normalizedRequests.map(({ inputFiles }) => ({
12534
- text: "",
12535
- raw: {
12536
- session,
12537
- inputFiles,
12538
- allInputFiles: combinedInputFiles
12539
- }
12540
- }));
12579
+ }
12580
+ const response = record.response;
12581
+ if (response && typeof response === "object") {
12582
+ const content = response.content;
12583
+ const flattened = flattenContent(content);
12584
+ if (flattened) {
12585
+ return flattened;
12541
12586
  }
12542
- if (session.responseFiles.length !== requests.length) {
12543
- throw new Error(
12544
- `VS Code batch returned ${session.responseFiles.length} responses for ${requests.length} requests`
12545
- );
12587
+ }
12588
+ const output = record.output;
12589
+ const flattenedOutput = flattenContent(output);
12590
+ if (flattenedOutput) {
12591
+ return flattenedOutput;
12592
+ }
12593
+ throw new Error("Codex CLI JSON response did not include an assistant message");
12594
+ }
12595
+ function extractFromEventStream(events) {
12596
+ for (let index = events.length - 1; index >= 0; index -= 1) {
12597
+ const candidate = events[index];
12598
+ const text = extractFromEvent(candidate);
12599
+ if (text) {
12600
+ return text;
12546
12601
  }
12547
- const responses = [];
12548
- for (const [index, responseFile] of session.responseFiles.entries()) {
12549
- const responseText = await readFile22(responseFile, "utf8");
12550
- responses.push({
12551
- text: responseText,
12552
- raw: {
12553
- session,
12554
- inputFiles: normalizedRequests[index]?.inputFiles,
12555
- allInputFiles: combinedInputFiles,
12556
- responseFile
12557
- }
12558
- });
12602
+ }
12603
+ return void 0;
12604
+ }
12605
+ function extractFromEvent(event) {
12606
+ if (!event || typeof event !== "object") {
12607
+ return void 0;
12608
+ }
12609
+ const record = event;
12610
+ const type = typeof record.type === "string" ? record.type : void 0;
12611
+ if (type === JSONL_TYPE_ITEM_COMPLETED) {
12612
+ const item = record.item;
12613
+ const text = extractFromItem(item);
12614
+ if (text) {
12615
+ return text;
12559
12616
  }
12560
- return responses;
12561
12617
  }
12562
- };
12563
- function buildPromptDocument(request, attachments, guidelinePatterns) {
12564
- const parts = [];
12565
- const guidelineFiles = collectGuidelineFiles(attachments, guidelinePatterns);
12566
- const attachmentFiles = collectAttachmentFiles(attachments);
12567
- const nonGuidelineAttachments = attachmentFiles.filter(
12568
- (file) => !guidelineFiles.includes(file)
12569
- );
12570
- const prereadBlock = buildMandatoryPrereadBlock(guidelineFiles, nonGuidelineAttachments);
12571
- if (prereadBlock.length > 0) {
12572
- parts.push("\n", prereadBlock);
12618
+ const output = record.output ?? record.content;
12619
+ const flattened = flattenContent(output);
12620
+ if (flattened) {
12621
+ return flattened;
12573
12622
  }
12574
- parts.push("\n[[ ## user_query ## ]]\n", request.prompt.trim());
12575
- return parts.join("\n").trim();
12623
+ return void 0;
12576
12624
  }
12577
- function buildMandatoryPrereadBlock(guidelineFiles, attachmentFiles) {
12578
- if (guidelineFiles.length === 0 && attachmentFiles.length === 0) {
12579
- return "";
12580
- }
12581
- const buildList = (files) => files.map((absolutePath) => {
12582
- const fileName = path32.basename(absolutePath);
12583
- const fileUri = pathToFileUri2(absolutePath);
12584
- return `* [${fileName}](${fileUri})`;
12585
- });
12586
- const sections = [];
12587
- if (guidelineFiles.length > 0) {
12588
- sections.push(`Read all guideline files:
12589
- ${buildList(guidelineFiles).join("\n")}.`);
12625
+ function extractFromItem(item) {
12626
+ if (!item || typeof item !== "object") {
12627
+ return void 0;
12590
12628
  }
12591
- if (attachmentFiles.length > 0) {
12592
- sections.push(`Read all attachment files:
12593
- ${buildList(attachmentFiles).join("\n")}.`);
12629
+ const record = item;
12630
+ const itemType = typeof record.type === "string" ? record.type : void 0;
12631
+ if (itemType === "agent_message" || itemType === "response" || itemType === "output") {
12632
+ const text = flattenContent(record.text ?? record.content ?? record.output);
12633
+ if (text) {
12634
+ return text;
12635
+ }
12594
12636
  }
12595
- sections.push(
12596
- "If any file is missing, fail with ERROR: missing-file <filename> and stop.",
12597
- "Then apply system_instructions on the user query below."
12598
- );
12599
- return sections.join("\n");
12637
+ return void 0;
12600
12638
  }
12601
- function collectGuidelineFiles(attachments, guidelinePatterns) {
12602
- if (!attachments || attachments.length === 0) {
12603
- return [];
12639
+ function flattenContent(value) {
12640
+ if (typeof value === "string") {
12641
+ return value;
12604
12642
  }
12605
- const unique = /* @__PURE__ */ new Map();
12606
- for (const attachment of attachments) {
12607
- const absolutePath = path32.resolve(attachment);
12608
- const normalized = absolutePath.split(path32.sep).join("/");
12609
- if (isGuidelineFile(normalized, guidelinePatterns)) {
12610
- if (!unique.has(absolutePath)) {
12611
- unique.set(absolutePath, absolutePath);
12643
+ if (Array.isArray(value)) {
12644
+ const parts = value.map((segment) => {
12645
+ if (typeof segment === "string") {
12646
+ return segment;
12612
12647
  }
12613
- }
12648
+ if (segment && typeof segment === "object" && "text" in segment) {
12649
+ const text = segment.text;
12650
+ return typeof text === "string" ? text : void 0;
12651
+ }
12652
+ return void 0;
12653
+ }).filter((part) => typeof part === "string" && part.length > 0);
12654
+ return parts.length > 0 ? parts.join(" \n") : void 0;
12614
12655
  }
12615
- return Array.from(unique.values());
12656
+ if (value && typeof value === "object" && "text" in value) {
12657
+ const text = value.text;
12658
+ return typeof text === "string" ? text : void 0;
12659
+ }
12660
+ return void 0;
12616
12661
  }
12617
- function collectAttachmentFiles(attachments) {
12618
- if (!attachments || attachments.length === 0) {
12619
- return [];
12662
+ function parseJsonLines(output) {
12663
+ const lines = output.split(/\r?\n/).map((line2) => line2.trim()).filter((line2) => line2.length > 0);
12664
+ if (lines.length <= 1) {
12665
+ return void 0;
12620
12666
  }
12621
- const unique = /* @__PURE__ */ new Map();
12622
- for (const attachment of attachments) {
12623
- const absolutePath = path32.resolve(attachment);
12624
- if (!unique.has(absolutePath)) {
12625
- unique.set(absolutePath, absolutePath);
12667
+ const parsed = [];
12668
+ for (const line2 of lines) {
12669
+ try {
12670
+ parsed.push(JSON.parse(line2));
12671
+ } catch {
12672
+ return void 0;
12626
12673
  }
12627
12674
  }
12628
- return Array.from(unique.values());
12675
+ return parsed;
12629
12676
  }
12630
- function pathToFileUri2(filePath) {
12631
- const absolutePath = path32.isAbsolute(filePath) ? filePath : path32.resolve(filePath);
12632
- const normalizedPath = absolutePath.replace(/\\/g, "/");
12633
- if (/^[a-zA-Z]:\//.test(normalizedPath)) {
12634
- return `file:///${normalizedPath}`;
12677
+ function pickDetail(stderr, stdout) {
12678
+ const errorText = stderr.trim();
12679
+ if (errorText.length > 0) {
12680
+ return errorText;
12635
12681
  }
12636
- return `file://${normalizedPath}`;
12682
+ const stdoutText = stdout.trim();
12683
+ return stdoutText.length > 0 ? stdoutText : void 0;
12637
12684
  }
12638
- function normalizeAttachments(attachments) {
12639
- if (!attachments || attachments.length === 0) {
12640
- return void 0;
12641
- }
12642
- const deduped = /* @__PURE__ */ new Set();
12643
- for (const attachment of attachments) {
12644
- deduped.add(path32.resolve(attachment));
12685
+ function formatTimeoutSuffix2(timeoutMs) {
12686
+ if (!timeoutMs || timeoutMs <= 0) {
12687
+ return "";
12645
12688
  }
12646
- return Array.from(deduped);
12689
+ const seconds = Math.ceil(timeoutMs / 1e3);
12690
+ return ` after ${seconds}s`;
12647
12691
  }
12648
- function mergeAttachments(all) {
12649
- const deduped = /* @__PURE__ */ new Set();
12650
- for (const list of all) {
12651
- if (!list) continue;
12652
- for (const inputFile of list) {
12653
- deduped.add(path32.resolve(inputFile));
12692
+ async function defaultCodexRunner(options) {
12693
+ return await new Promise((resolve, reject) => {
12694
+ const child = spawn2(options.executable, options.args, {
12695
+ cwd: options.cwd,
12696
+ env: options.env,
12697
+ stdio: ["pipe", "pipe", "pipe"],
12698
+ shell: shouldShellExecute(options.executable)
12699
+ });
12700
+ let stdout = "";
12701
+ let stderr = "";
12702
+ let timedOut = false;
12703
+ const onAbort = () => {
12704
+ child.kill("SIGTERM");
12705
+ };
12706
+ if (options.signal) {
12707
+ if (options.signal.aborted) {
12708
+ onAbort();
12709
+ } else {
12710
+ options.signal.addEventListener("abort", onAbort, { once: true });
12711
+ }
12654
12712
  }
12655
- }
12656
- return deduped.size > 0 ? Array.from(deduped) : void 0;
12657
- }
12658
- async function ensureVSCodeSubagents(options) {
12659
- const { kind, count, verbose = false } = options;
12660
- const vscodeCmd = kind === "vscode-insiders" ? "code-insiders" : "code";
12661
- const subagentRoot = getSubagentRoot(vscodeCmd);
12662
- try {
12663
- if (verbose) {
12664
- console.log(`Provisioning ${count} subagent(s) via: subagent ${vscodeCmd} provision`);
12713
+ let timeoutHandle;
12714
+ if (options.timeoutMs && options.timeoutMs > 0) {
12715
+ timeoutHandle = setTimeout(() => {
12716
+ timedOut = true;
12717
+ child.kill("SIGTERM");
12718
+ }, options.timeoutMs);
12719
+ timeoutHandle.unref?.();
12665
12720
  }
12666
- const result = await provisionSubagents({
12667
- targetRoot: subagentRoot,
12668
- subagents: count,
12669
- dryRun: false
12721
+ child.stdout.setEncoding("utf8");
12722
+ child.stdout.on("data", (chunk) => {
12723
+ stdout += chunk;
12724
+ options.onStdoutChunk?.(chunk);
12670
12725
  });
12671
- if (verbose) {
12672
- if (result.created.length > 0) {
12673
- console.log(`Created ${result.created.length} new subagent(s)`);
12726
+ child.stderr.setEncoding("utf8");
12727
+ child.stderr.on("data", (chunk) => {
12728
+ stderr += chunk;
12729
+ options.onStderrChunk?.(chunk);
12730
+ });
12731
+ child.stdin.end(options.prompt);
12732
+ const cleanup = () => {
12733
+ if (timeoutHandle) {
12734
+ clearTimeout(timeoutHandle);
12674
12735
  }
12675
- if (result.skippedExisting.length > 0) {
12676
- console.log(`Reusing ${result.skippedExisting.length} existing unlocked subagent(s)`);
12736
+ if (options.signal) {
12737
+ options.signal.removeEventListener("abort", onAbort);
12677
12738
  }
12678
- console.log(`
12679
- total unlocked subagents available: ${result.created.length + result.skippedExisting.length}`);
12680
- }
12681
- return {
12682
- provisioned: true,
12683
- message: `Provisioned ${count} subagent(s): ${result.created.length} created, ${result.skippedExisting.length} reused`
12684
12739
  };
12685
- } catch (error) {
12686
- const errorMessage = error instanceof Error ? error.message : String(error);
12687
- if (verbose) {
12688
- console.warn(`Provisioning failed (continuing anyway): ${errorMessage}`);
12740
+ child.on("error", (error) => {
12741
+ cleanup();
12742
+ reject(error);
12743
+ });
12744
+ child.on("close", (code) => {
12745
+ cleanup();
12746
+ resolve({
12747
+ stdout,
12748
+ stderr,
12749
+ exitCode: typeof code === "number" ? code : -1,
12750
+ timedOut
12751
+ });
12752
+ });
12753
+ });
12754
+ }
12755
+ function shouldShellExecute(executable) {
12756
+ if (process.platform !== "win32") {
12757
+ return false;
12758
+ }
12759
+ const lower = executable.toLowerCase();
12760
+ return lower.endsWith(".cmd") || lower.endsWith(".bat") || lower.endsWith(".ps1");
12761
+ }
12762
+ var DEFAULT_MOCK_RESPONSE = '{"answer":"Mock provider response. Configure targets.yaml to supply a custom value."}';
12763
+ var MockProvider = class {
12764
+ id;
12765
+ kind = "mock";
12766
+ targetName;
12767
+ cannedResponse;
12768
+ delayMs;
12769
+ delayMinMs;
12770
+ delayMaxMs;
12771
+ constructor(targetName, config) {
12772
+ this.id = `mock:${targetName}`;
12773
+ this.targetName = targetName;
12774
+ this.cannedResponse = config.response ?? DEFAULT_MOCK_RESPONSE;
12775
+ this.delayMs = config.delayMs ?? 0;
12776
+ this.delayMinMs = config.delayMinMs ?? 0;
12777
+ this.delayMaxMs = config.delayMaxMs ?? 0;
12778
+ }
12779
+ async invoke(request) {
12780
+ const delay = this.calculateDelay();
12781
+ if (delay > 0) {
12782
+ await new Promise((resolve) => setTimeout(resolve, delay));
12689
12783
  }
12690
12784
  return {
12691
- provisioned: false,
12692
- message: `Provisioning failed: ${errorMessage}`
12785
+ text: this.cannedResponse,
12786
+ raw: {
12787
+ prompt: request.prompt,
12788
+ guidelines: request.guidelines
12789
+ }
12693
12790
  };
12694
12791
  }
12792
+ calculateDelay() {
12793
+ if (this.delayMinMs > 0 || this.delayMaxMs > 0) {
12794
+ const min = Math.max(0, this.delayMinMs);
12795
+ const max = Math.max(min, this.delayMaxMs);
12796
+ return Math.floor(Math.random() * (max - min + 1)) + min;
12797
+ }
12798
+ return this.delayMs;
12799
+ }
12800
+ };
12801
+ var CLI_PLACEHOLDERS = /* @__PURE__ */ new Set(["PROMPT", "GUIDELINES", "EVAL_ID", "ATTEMPT", "FILES"]);
12802
+ var BASE_TARGET_SCHEMA = external_exports.object({
12803
+ name: external_exports.string().min(1, "target name is required"),
12804
+ provider: external_exports.string().min(1, "provider is required"),
12805
+ settings: external_exports.record(external_exports.unknown()).optional(),
12806
+ judge_target: external_exports.string().optional(),
12807
+ workers: external_exports.number().int().min(1).optional()
12808
+ });
12809
+ var DEFAULT_AZURE_API_VERSION = "2024-10-01-preview";
12810
+ function normalizeAzureApiVersion(value) {
12811
+ if (!value) {
12812
+ return DEFAULT_AZURE_API_VERSION;
12813
+ }
12814
+ const trimmed = value.trim();
12815
+ if (trimmed.length === 0) {
12816
+ return DEFAULT_AZURE_API_VERSION;
12817
+ }
12818
+ const withoutPrefix = trimmed.replace(/^api[-_]?version\s*=\s*/i, "").trim();
12819
+ return withoutPrefix.length > 0 ? withoutPrefix : DEFAULT_AZURE_API_VERSION;
12820
+ }
12821
+ function resolveTargetDefinition(definition, env = process.env) {
12822
+ const parsed = BASE_TARGET_SCHEMA.parse(definition);
12823
+ const provider = parsed.provider.toLowerCase();
12824
+ const providerBatching = resolveOptionalBoolean(
12825
+ parsed.settings?.provider_batching ?? parsed.settings?.providerBatching
12826
+ );
12827
+ switch (provider) {
12828
+ case "azure":
12829
+ case "azure-openai":
12830
+ return {
12831
+ kind: "azure",
12832
+ name: parsed.name,
12833
+ judgeTarget: parsed.judge_target,
12834
+ workers: parsed.workers,
12835
+ providerBatching,
12836
+ config: resolveAzureConfig(parsed, env)
12837
+ };
12838
+ case "anthropic":
12839
+ return {
12840
+ kind: "anthropic",
12841
+ name: parsed.name,
12842
+ judgeTarget: parsed.judge_target,
12843
+ workers: parsed.workers,
12844
+ providerBatching,
12845
+ config: resolveAnthropicConfig(parsed, env)
12846
+ };
12847
+ case "gemini":
12848
+ case "google":
12849
+ case "google-gemini":
12850
+ return {
12851
+ kind: "gemini",
12852
+ name: parsed.name,
12853
+ judgeTarget: parsed.judge_target,
12854
+ workers: parsed.workers,
12855
+ providerBatching,
12856
+ config: resolveGeminiConfig(parsed, env)
12857
+ };
12858
+ case "codex":
12859
+ case "codex-cli":
12860
+ return {
12861
+ kind: "codex",
12862
+ name: parsed.name,
12863
+ judgeTarget: parsed.judge_target,
12864
+ workers: parsed.workers,
12865
+ providerBatching,
12866
+ config: resolveCodexConfig(parsed, env)
12867
+ };
12868
+ case "mock":
12869
+ return {
12870
+ kind: "mock",
12871
+ name: parsed.name,
12872
+ judgeTarget: parsed.judge_target,
12873
+ workers: parsed.workers,
12874
+ providerBatching,
12875
+ config: resolveMockConfig(parsed)
12876
+ };
12877
+ case "vscode":
12878
+ case "vscode-insiders":
12879
+ return {
12880
+ kind: provider,
12881
+ name: parsed.name,
12882
+ judgeTarget: parsed.judge_target,
12883
+ workers: parsed.workers,
12884
+ providerBatching,
12885
+ config: resolveVSCodeConfig(parsed, env, provider === "vscode-insiders")
12886
+ };
12887
+ case "cli":
12888
+ return {
12889
+ kind: "cli",
12890
+ name: parsed.name,
12891
+ judgeTarget: parsed.judge_target,
12892
+ workers: parsed.workers,
12893
+ providerBatching,
12894
+ config: resolveCliConfig(parsed, env)
12895
+ };
12896
+ default:
12897
+ throw new Error(`Unsupported provider '${parsed.provider}' in target '${parsed.name}'`);
12898
+ }
12695
12899
  }
12696
- function buildPromptDocument2(request, inputFiles, options) {
12697
- const parts = [];
12698
- const guidelineFiles = collectGuidelineFiles2(
12699
- inputFiles,
12700
- options?.guidelinePatterns ?? request.guideline_patterns,
12701
- options?.guidelineOverrides
12900
+ function resolveAzureConfig(target, env) {
12901
+ const settings = target.settings ?? {};
12902
+ const endpointSource = settings.endpoint ?? settings.resource ?? settings.resourceName;
12903
+ const apiKeySource = settings.api_key ?? settings.apiKey;
12904
+ const deploymentSource = settings.deployment ?? settings.deploymentName ?? settings.model;
12905
+ const versionSource = settings.version ?? settings.api_version;
12906
+ const temperatureSource = settings.temperature;
12907
+ const maxTokensSource = settings.max_output_tokens ?? settings.maxTokens;
12908
+ const resourceName = resolveString(endpointSource, env, `${target.name} endpoint`);
12909
+ const apiKey = resolveString(apiKeySource, env, `${target.name} api key`);
12910
+ const deploymentName = resolveString(deploymentSource, env, `${target.name} deployment`);
12911
+ const version = normalizeAzureApiVersion(
12912
+ resolveOptionalString(versionSource, env, `${target.name} api version`)
12702
12913
  );
12703
- const inputFilesList = collectInputFiles(inputFiles);
12704
- const nonGuidelineInputFiles = inputFilesList.filter(
12705
- (file) => !guidelineFiles.includes(file)
12914
+ const temperature = resolveOptionalNumber(temperatureSource, `${target.name} temperature`);
12915
+ const maxOutputTokens = resolveOptionalNumber(
12916
+ maxTokensSource,
12917
+ `${target.name} max output tokens`
12706
12918
  );
12707
- const prereadBlock = buildMandatoryPrereadBlock2(guidelineFiles, nonGuidelineInputFiles);
12708
- if (prereadBlock.length > 0) {
12709
- parts.push("\n", prereadBlock);
12710
- }
12711
- parts.push("\n[[ ## user_query ## ]]\n", request.prompt.trim());
12712
- return parts.join("\n").trim();
12919
+ return {
12920
+ resourceName,
12921
+ deploymentName,
12922
+ apiKey,
12923
+ version,
12924
+ temperature,
12925
+ maxOutputTokens
12926
+ };
12713
12927
  }
12714
- function normalizeInputFiles2(inputFiles) {
12715
- if (!inputFiles || inputFiles.length === 0) {
12716
- return void 0;
12717
- }
12718
- const deduped = /* @__PURE__ */ new Map();
12719
- for (const inputFile of inputFiles) {
12720
- const absolutePath = path42.resolve(inputFile);
12721
- if (!deduped.has(absolutePath)) {
12722
- deduped.set(absolutePath, absolutePath);
12723
- }
12724
- }
12725
- return Array.from(deduped.values());
12928
+ function resolveAnthropicConfig(target, env) {
12929
+ const settings = target.settings ?? {};
12930
+ const apiKeySource = settings.api_key ?? settings.apiKey;
12931
+ const modelSource = settings.model ?? settings.deployment ?? settings.variant;
12932
+ const temperatureSource = settings.temperature;
12933
+ const maxTokensSource = settings.max_output_tokens ?? settings.maxTokens;
12934
+ const thinkingBudgetSource = settings.thinking_budget ?? settings.thinkingBudget;
12935
+ const apiKey = resolveString(apiKeySource, env, `${target.name} Anthropic api key`);
12936
+ const model = resolveString(modelSource, env, `${target.name} Anthropic model`);
12937
+ return {
12938
+ apiKey,
12939
+ model,
12940
+ temperature: resolveOptionalNumber(temperatureSource, `${target.name} temperature`),
12941
+ maxOutputTokens: resolveOptionalNumber(maxTokensSource, `${target.name} max output tokens`),
12942
+ thinkingBudget: resolveOptionalNumber(thinkingBudgetSource, `${target.name} thinking budget`)
12943
+ };
12726
12944
  }
12727
- function collectGuidelineFiles2(inputFiles, guidelinePatterns, overrides) {
12728
- if (!inputFiles || inputFiles.length === 0) {
12729
- return [];
12730
- }
12731
- const unique = /* @__PURE__ */ new Map();
12732
- for (const inputFile of inputFiles) {
12733
- const absolutePath = path42.resolve(inputFile);
12734
- if (overrides?.has(absolutePath)) {
12735
- if (!unique.has(absolutePath)) {
12736
- unique.set(absolutePath, absolutePath);
12737
- }
12738
- continue;
12739
- }
12740
- const normalized = absolutePath.split(path42.sep).join("/");
12741
- if (isGuidelineFile(normalized, guidelinePatterns)) {
12742
- if (!unique.has(absolutePath)) {
12743
- unique.set(absolutePath, absolutePath);
12744
- }
12745
- }
12746
- }
12747
- return Array.from(unique.values());
12945
+ function resolveGeminiConfig(target, env) {
12946
+ const settings = target.settings ?? {};
12947
+ const apiKeySource = settings.api_key ?? settings.apiKey;
12948
+ const modelSource = settings.model ?? settings.deployment ?? settings.variant;
12949
+ const temperatureSource = settings.temperature;
12950
+ const maxTokensSource = settings.max_output_tokens ?? settings.maxTokens;
12951
+ const apiKey = resolveString(apiKeySource, env, `${target.name} Google API key`);
12952
+ const model = resolveOptionalString(modelSource, env, `${target.name} Gemini model`, {
12953
+ allowLiteral: true,
12954
+ optionalEnv: true
12955
+ }) ?? "gemini-2.5-flash";
12956
+ return {
12957
+ apiKey,
12958
+ model,
12959
+ temperature: resolveOptionalNumber(temperatureSource, `${target.name} temperature`),
12960
+ maxOutputTokens: resolveOptionalNumber(maxTokensSource, `${target.name} max output tokens`)
12961
+ };
12748
12962
  }
12749
- function collectInputFiles(inputFiles) {
12750
- if (!inputFiles || inputFiles.length === 0) {
12751
- return [];
12752
- }
12753
- const unique = /* @__PURE__ */ new Map();
12754
- for (const inputFile of inputFiles) {
12755
- const absolutePath = path42.resolve(inputFile);
12756
- if (!unique.has(absolutePath)) {
12757
- unique.set(absolutePath, absolutePath);
12758
- }
12759
- }
12760
- return Array.from(unique.values());
12963
+ function resolveCodexConfig(target, env) {
12964
+ const settings = target.settings ?? {};
12965
+ const executableSource = settings.executable ?? settings.command ?? settings.binary;
12966
+ const argsSource = settings.args ?? settings.arguments;
12967
+ const cwdSource = settings.cwd;
12968
+ const timeoutSource = settings.timeout_seconds ?? settings.timeoutSeconds;
12969
+ const logDirSource = settings.log_dir ?? settings.logDir ?? settings.log_directory ?? settings.logDirectory;
12970
+ const logFormatSource = settings.log_format ?? settings.logFormat ?? settings.log_output_format ?? settings.logOutputFormat ?? env.AGENTV_CODEX_LOG_FORMAT;
12971
+ const executable = resolveOptionalString(executableSource, env, `${target.name} codex executable`, {
12972
+ allowLiteral: true,
12973
+ optionalEnv: true
12974
+ }) ?? "codex";
12975
+ const args = resolveOptionalStringArray(argsSource, env, `${target.name} codex args`);
12976
+ const cwd = resolveOptionalString(cwdSource, env, `${target.name} codex cwd`, {
12977
+ allowLiteral: true,
12978
+ optionalEnv: true
12979
+ });
12980
+ const timeoutMs = resolveTimeoutMs(timeoutSource, `${target.name} codex timeout`);
12981
+ const logDir = resolveOptionalString(logDirSource, env, `${target.name} codex log directory`, {
12982
+ allowLiteral: true,
12983
+ optionalEnv: true
12984
+ });
12985
+ const logFormat = normalizeCodexLogFormat(logFormatSource);
12986
+ return {
12987
+ executable,
12988
+ args,
12989
+ cwd,
12990
+ timeoutMs,
12991
+ logDir,
12992
+ logFormat
12993
+ };
12761
12994
  }
12762
- function buildMandatoryPrereadBlock2(guidelineFiles, inputFiles) {
12763
- if (guidelineFiles.length === 0 && inputFiles.length === 0) {
12764
- return "";
12995
+ function normalizeCodexLogFormat(value) {
12996
+ if (value === void 0 || value === null) {
12997
+ return void 0;
12765
12998
  }
12766
- const buildList = (files) => files.map((absolutePath) => {
12767
- const fileName = path42.basename(absolutePath);
12768
- const fileUri = pathToFileUri22(absolutePath);
12769
- return `* [${fileName}](${fileUri})`;
12770
- });
12771
- const sections = [];
12772
- if (guidelineFiles.length > 0) {
12773
- sections.push(`Read all guideline files:
12774
- ${buildList(guidelineFiles).join("\n")}.`);
12999
+ if (typeof value !== "string") {
13000
+ throw new Error("codex log format must be 'summary' or 'json'");
12775
13001
  }
12776
- if (inputFiles.length > 0) {
12777
- sections.push(`Read all input files:
12778
- ${buildList(inputFiles).join("\n")}.`);
13002
+ const normalized = value.trim().toLowerCase();
13003
+ if (normalized === "json" || normalized === "summary") {
13004
+ return normalized;
12779
13005
  }
12780
- sections.push(
12781
- "If any file is missing, fail with ERROR: missing-file <filename> and stop.",
12782
- "Then apply system_instructions on the user query below."
12783
- );
12784
- return sections.join("\n");
13006
+ throw new Error("codex log format must be 'summary' or 'json'");
12785
13007
  }
12786
- function pathToFileUri22(filePath) {
12787
- const absolutePath = path42.isAbsolute(filePath) ? filePath : path42.resolve(filePath);
12788
- const normalizedPath = absolutePath.replace(/\\/g, "/");
12789
- if (/^[a-zA-Z]:\//.test(normalizedPath)) {
12790
- return `file:///${normalizedPath}`;
12791
- }
12792
- return `file://${normalizedPath}`;
13008
+ function resolveMockConfig(target) {
13009
+ const settings = target.settings ?? {};
13010
+ const response = typeof settings.response === "string" ? settings.response : void 0;
13011
+ return { response };
12793
13012
  }
12794
- var execAsync22 = promisify22(execCallback);
12795
- var WORKSPACE_PREFIX = "agentv-codex-";
12796
- var PROMPT_FILENAME = "prompt.md";
12797
- var FILES_DIR = "files";
12798
- var JSONL_TYPE_ITEM_COMPLETED = "item.completed";
12799
- var CodexProvider = class {
12800
- id;
12801
- kind = "codex";
12802
- targetName;
12803
- supportsBatch = false;
12804
- config;
12805
- runCodex;
12806
- environmentCheck;
12807
- resolvedExecutable;
12808
- constructor(targetName, config, runner = defaultCodexRunner) {
12809
- this.id = `codex:${targetName}`;
12810
- this.targetName = targetName;
12811
- this.config = config;
12812
- this.runCodex = runner;
12813
- }
12814
- async invoke(request) {
12815
- if (request.signal?.aborted) {
12816
- throw new Error("Codex provider request was aborted before execution");
12817
- }
12818
- await this.ensureEnvironmentReady();
12819
- const inputFiles = normalizeInputFiles2(request.inputFiles);
12820
- const originalGuidelines = new Set(
12821
- collectGuidelineFiles2(inputFiles, request.guideline_patterns).map((file) => path52.resolve(file))
12822
- );
12823
- const workspaceRoot = await this.createWorkspace();
12824
- try {
12825
- const { mirroredInputFiles, guidelineMirrors } = await this.mirrorInputFiles(
12826
- inputFiles,
12827
- workspaceRoot,
12828
- originalGuidelines
12829
- );
12830
- const promptContent = buildPromptDocument2(request, mirroredInputFiles, {
12831
- guidelinePatterns: request.guideline_patterns,
12832
- guidelineOverrides: guidelineMirrors
12833
- });
12834
- const promptFile = path52.join(workspaceRoot, PROMPT_FILENAME);
12835
- await writeFile3(promptFile, promptContent, "utf8");
12836
- const args = this.buildCodexArgs();
12837
- const cwd = this.resolveCwd(workspaceRoot);
12838
- const result = await this.executeCodex(args, cwd, promptContent, request.signal);
12839
- if (result.timedOut) {
12840
- throw new Error(
12841
- `Codex CLI timed out${formatTimeoutSuffix2(this.config.timeoutMs ?? void 0)}`
12842
- );
12843
- }
12844
- if (result.exitCode !== 0) {
12845
- const detail = pickDetail(result.stderr, result.stdout);
12846
- const prefix = `Codex CLI exited with code ${result.exitCode}`;
12847
- throw new Error(detail ? `${prefix}: ${detail}` : prefix);
12848
- }
12849
- const parsed = parseCodexJson(result.stdout);
12850
- const assistantText = extractAssistantText(parsed);
12851
- return {
12852
- text: assistantText,
12853
- raw: {
12854
- response: parsed,
12855
- stdout: result.stdout,
12856
- stderr: result.stderr,
12857
- exitCode: result.exitCode,
12858
- args,
12859
- executable: this.resolvedExecutable ?? this.config.executable,
12860
- promptFile,
12861
- workspace: workspaceRoot,
12862
- inputFiles: mirroredInputFiles
12863
- }
12864
- };
12865
- } finally {
12866
- await this.cleanupWorkspace(workspaceRoot);
12867
- }
13013
+ function resolveVSCodeConfig(target, env, insiders) {
13014
+ const settings = target.settings ?? {};
13015
+ const workspaceTemplateEnvVar = resolveOptionalLiteralString(settings.workspace_template ?? settings.workspaceTemplate);
13016
+ const workspaceTemplate = workspaceTemplateEnvVar ? resolveOptionalString(workspaceTemplateEnvVar, env, `${target.name} workspace template path`, {
13017
+ allowLiteral: false,
13018
+ optionalEnv: true
13019
+ }) : void 0;
13020
+ const commandSource = settings.vscode_cmd ?? settings.command;
13021
+ const waitSource = settings.wait;
13022
+ const dryRunSource = settings.dry_run ?? settings.dryRun;
13023
+ const subagentRootSource = settings.subagent_root ?? settings.subagentRoot;
13024
+ const defaultCommand = insiders ? "code-insiders" : "code";
13025
+ const command = resolveOptionalLiteralString(commandSource) ?? defaultCommand;
13026
+ return {
13027
+ command,
13028
+ waitForResponse: resolveOptionalBoolean(waitSource) ?? true,
13029
+ dryRun: resolveOptionalBoolean(dryRunSource) ?? false,
13030
+ subagentRoot: resolveOptionalString(subagentRootSource, env, `${target.name} subagent root`, {
13031
+ allowLiteral: true,
13032
+ optionalEnv: true
13033
+ }),
13034
+ workspaceTemplate
13035
+ };
13036
+ }
13037
+ function resolveCliConfig(target, env) {
13038
+ const settings = target.settings ?? {};
13039
+ const commandTemplateSource = settings.command_template ?? settings.commandTemplate;
13040
+ const filesFormat = resolveOptionalLiteralString(
13041
+ settings.files_format ?? settings.filesFormat ?? settings.attachments_format ?? settings.attachmentsFormat
13042
+ );
13043
+ const cwd = resolveOptionalString(settings.cwd, env, `${target.name} working directory`, {
13044
+ allowLiteral: true,
13045
+ optionalEnv: true
13046
+ });
13047
+ const envOverrides = resolveEnvOverrides(settings.env, env, target.name);
13048
+ const timeoutMs = resolveTimeoutMs(settings.timeout_seconds ?? settings.timeoutSeconds, `${target.name} timeout`);
13049
+ const healthcheck = resolveCliHealthcheck(settings.healthcheck, env, target.name);
13050
+ const commandTemplate = resolveString(
13051
+ commandTemplateSource,
13052
+ env,
13053
+ `${target.name} CLI command template`,
13054
+ true
13055
+ );
13056
+ assertSupportedCliPlaceholders(commandTemplate, `${target.name} CLI command template`);
13057
+ return {
13058
+ commandTemplate,
13059
+ filesFormat,
13060
+ cwd,
13061
+ env: envOverrides,
13062
+ timeoutMs,
13063
+ healthcheck
13064
+ };
13065
+ }
13066
+ function resolveEnvOverrides(source2, env, targetName) {
13067
+ if (source2 === void 0 || source2 === null) {
13068
+ return void 0;
12868
13069
  }
12869
- async ensureEnvironmentReady() {
12870
- if (!this.environmentCheck) {
12871
- this.environmentCheck = this.validateEnvironment();
13070
+ if (typeof source2 !== "object" || Array.isArray(source2)) {
13071
+ throw new Error(`${targetName} env overrides must be an object map of strings`);
13072
+ }
13073
+ const entries = Object.entries(source2);
13074
+ const resolved = {};
13075
+ for (const [key2, value] of entries) {
13076
+ if (typeof value !== "string") {
13077
+ throw new Error(`${targetName} env override '${key2}' must be a string`);
12872
13078
  }
12873
- await this.environmentCheck;
13079
+ const resolvedValue = resolveString(value, env, `${targetName} env override '${key2}'`);
13080
+ resolved[key2] = resolvedValue;
12874
13081
  }
12875
- async validateEnvironment() {
12876
- this.resolvedExecutable = await locateExecutable(this.config.executable);
13082
+ return Object.keys(resolved).length > 0 ? resolved : void 0;
13083
+ }
13084
+ function resolveTimeoutMs(source2, description) {
13085
+ const seconds = resolveOptionalNumber(source2, `${description} (seconds)`);
13086
+ if (seconds === void 0) {
13087
+ return void 0;
12877
13088
  }
12878
- resolveCwd(workspaceRoot) {
12879
- if (!this.config.cwd) {
12880
- return workspaceRoot;
12881
- }
12882
- return path52.resolve(this.config.cwd);
13089
+ if (seconds <= 0) {
13090
+ throw new Error(`${description} must be greater than zero seconds`);
12883
13091
  }
12884
- buildCodexArgs() {
12885
- const args = ["--ask-for-approval", "never", "exec", "--json", "--color", "never", "--skip-git-repo-check"];
12886
- if (this.config.args && this.config.args.length > 0) {
12887
- args.push(...this.config.args);
12888
- }
12889
- args.push("-");
12890
- return args;
13092
+ return Math.floor(seconds * 1e3);
13093
+ }
13094
+ function resolveCliHealthcheck(source2, env, targetName) {
13095
+ if (source2 === void 0 || source2 === null) {
13096
+ return void 0;
12891
13097
  }
12892
- async executeCodex(args, cwd, promptContent, signal) {
12893
- try {
12894
- return await this.runCodex({
12895
- executable: this.resolvedExecutable ?? this.config.executable,
12896
- args,
12897
- cwd,
12898
- prompt: promptContent,
12899
- timeoutMs: this.config.timeoutMs,
12900
- env: process.env,
12901
- signal
12902
- });
12903
- } catch (error) {
12904
- const err = error;
12905
- if (err.code === "ENOENT") {
12906
- throw new Error(
12907
- `Codex executable '${this.config.executable}' was not found. Update the target settings.executable or add it to PATH.`
12908
- );
12909
- }
12910
- throw error;
12911
- }
13098
+ if (typeof source2 !== "object" || Array.isArray(source2)) {
13099
+ throw new Error(`${targetName} healthcheck must be an object`);
12912
13100
  }
12913
- async mirrorInputFiles(inputFiles, workspaceRoot, guidelineOriginals) {
12914
- if (!inputFiles || inputFiles.length === 0) {
12915
- return {
12916
- mirroredInputFiles: void 0,
12917
- guidelineMirrors: /* @__PURE__ */ new Set()
12918
- };
12919
- }
12920
- const filesRoot = path52.join(workspaceRoot, FILES_DIR);
12921
- await mkdir3(filesRoot, { recursive: true });
12922
- const mirrored = [];
12923
- const guidelineMirrors = /* @__PURE__ */ new Set();
12924
- const nameCounts = /* @__PURE__ */ new Map();
12925
- for (const inputFile of inputFiles) {
12926
- const absoluteSource = path52.resolve(inputFile);
12927
- const baseName = path52.basename(absoluteSource);
12928
- const count = nameCounts.get(baseName) ?? 0;
12929
- nameCounts.set(baseName, count + 1);
12930
- const finalName = count === 0 ? baseName : `${baseName}.${count}`;
12931
- const destination = path52.join(filesRoot, finalName);
12932
- await copyFile2(absoluteSource, destination);
12933
- const resolvedDestination = path52.resolve(destination);
12934
- mirrored.push(resolvedDestination);
12935
- if (guidelineOriginals.has(absoluteSource)) {
12936
- guidelineMirrors.add(resolvedDestination);
12937
- }
12938
- }
13101
+ const candidate = source2;
13102
+ const type = candidate.type;
13103
+ const timeoutMs = resolveTimeoutMs(
13104
+ candidate.timeout_seconds ?? candidate.timeoutSeconds,
13105
+ `${targetName} healthcheck timeout`
13106
+ );
13107
+ if (type === "http") {
13108
+ const url = resolveString(candidate.url, env, `${targetName} healthcheck URL`);
12939
13109
  return {
12940
- mirroredInputFiles: mirrored,
12941
- guidelineMirrors
13110
+ type: "http",
13111
+ url,
13112
+ timeoutMs
12942
13113
  };
12943
13114
  }
12944
- async createWorkspace() {
12945
- return await mkdtemp(path52.join(tmpdir(), WORKSPACE_PREFIX));
13115
+ if (type === "command") {
13116
+ const commandTemplate = resolveString(
13117
+ candidate.command_template ?? candidate.commandTemplate,
13118
+ env,
13119
+ `${targetName} healthcheck command template`,
13120
+ true
13121
+ );
13122
+ assertSupportedCliPlaceholders(commandTemplate, `${targetName} healthcheck command template`);
13123
+ const cwd = resolveOptionalString(candidate.cwd, env, `${targetName} healthcheck cwd`, {
13124
+ allowLiteral: true,
13125
+ optionalEnv: true
13126
+ });
13127
+ return {
13128
+ type: "command",
13129
+ commandTemplate,
13130
+ timeoutMs,
13131
+ cwd
13132
+ };
12946
13133
  }
12947
- async cleanupWorkspace(workspaceRoot) {
12948
- try {
12949
- await rm2(workspaceRoot, { recursive: true, force: true });
12950
- } catch {
13134
+ throw new Error(`${targetName} healthcheck type must be 'http' or 'command'`);
13135
+ }
13136
+ function assertSupportedCliPlaceholders(template, description) {
13137
+ const placeholders = extractCliPlaceholders(template);
13138
+ for (const placeholder of placeholders) {
13139
+ if (!CLI_PLACEHOLDERS.has(placeholder)) {
13140
+ throw new Error(
13141
+ `${description} includes unsupported placeholder '{${placeholder}}'. Supported placeholders: ${Array.from(CLI_PLACEHOLDERS).join(", ")}`
13142
+ );
12951
13143
  }
12952
13144
  }
12953
- };
12954
- async function locateExecutable(candidate) {
12955
- const includesPathSeparator = candidate.includes("/") || candidate.includes("\\");
12956
- if (includesPathSeparator) {
12957
- const resolved = path52.isAbsolute(candidate) ? candidate : path52.resolve(candidate);
12958
- const executablePath = await ensureWindowsExecutableVariant(resolved);
12959
- await access22(executablePath, constants22.F_OK);
12960
- return executablePath;
12961
- }
12962
- const locator = process.platform === "win32" ? "where" : "which";
12963
- try {
12964
- const { stdout } = await execAsync22(`${locator} ${candidate}`);
12965
- const lines = stdout.split(/\r?\n/).map((line2) => line2.trim()).filter((line2) => line2.length > 0);
12966
- const preferred = selectExecutableCandidate(lines);
12967
- if (preferred) {
12968
- const executablePath = await ensureWindowsExecutableVariant(preferred);
12969
- await access22(executablePath, constants22.F_OK);
12970
- return executablePath;
13145
+ }
13146
+ function extractCliPlaceholders(template) {
13147
+ const matches = template.matchAll(/\{([A-Z_]+)\}/g);
13148
+ const results = [];
13149
+ for (const match of matches) {
13150
+ if (match[1]) {
13151
+ results.push(match[1]);
12971
13152
  }
12972
- } catch {
12973
13153
  }
12974
- throw new Error(`Codex executable '${candidate}' was not found on PATH`);
13154
+ return results;
12975
13155
  }
12976
- function selectExecutableCandidate(candidates) {
12977
- if (candidates.length === 0) {
13156
+ function resolveString(source2, env, description, allowLiteral = false) {
13157
+ const value = resolveOptionalString(source2, env, description, {
13158
+ allowLiteral,
13159
+ optionalEnv: false
13160
+ });
13161
+ if (value === void 0) {
13162
+ throw new Error(`${description} is required`);
13163
+ }
13164
+ return value;
13165
+ }
13166
+ function resolveOptionalString(source2, env, description, options) {
13167
+ if (source2 === void 0 || source2 === null) {
12978
13168
  return void 0;
12979
13169
  }
12980
- if (process.platform !== "win32") {
12981
- return candidates[0];
13170
+ if (typeof source2 !== "string") {
13171
+ throw new Error(`${description} must be a string`);
12982
13172
  }
12983
- const extensions = getWindowsExecutableExtensions();
12984
- for (const ext of extensions) {
12985
- const match = candidates.find((candidate) => candidate.toLowerCase().endsWith(ext));
12986
- if (match) {
12987
- return match;
13173
+ const trimmed = source2.trim();
13174
+ if (trimmed.length === 0) {
13175
+ return void 0;
13176
+ }
13177
+ const envValue = env[trimmed];
13178
+ if (envValue !== void 0) {
13179
+ if (envValue.trim().length === 0) {
13180
+ throw new Error(`Environment variable '${trimmed}' for ${description} is empty`);
12988
13181
  }
13182
+ return envValue;
12989
13183
  }
12990
- return candidates[0];
13184
+ const allowLiteral = options?.allowLiteral ?? false;
13185
+ const optionalEnv = options?.optionalEnv ?? false;
13186
+ const looksLikeEnv = isLikelyEnvReference(trimmed);
13187
+ if (looksLikeEnv) {
13188
+ if (optionalEnv) {
13189
+ return void 0;
13190
+ }
13191
+ if (!allowLiteral) {
13192
+ throw new Error(`Environment variable '${trimmed}' required for ${description} is not set`);
13193
+ }
13194
+ }
13195
+ return trimmed;
13196
+ }
13197
+ function resolveOptionalLiteralString(source2) {
13198
+ if (source2 === void 0 || source2 === null) {
13199
+ return void 0;
13200
+ }
13201
+ if (typeof source2 !== "string") {
13202
+ throw new Error("expected string value");
13203
+ }
13204
+ const trimmed = source2.trim();
13205
+ return trimmed.length > 0 ? trimmed : void 0;
12991
13206
  }
12992
- async function ensureWindowsExecutableVariant(candidate) {
12993
- if (process.platform !== "win32") {
12994
- return candidate;
13207
+ function resolveOptionalNumber(source2, description) {
13208
+ if (source2 === void 0 || source2 === null || source2 === "") {
13209
+ return void 0;
12995
13210
  }
12996
- if (hasExecutableExtension(candidate)) {
12997
- return candidate;
13211
+ if (typeof source2 === "number") {
13212
+ return Number.isFinite(source2) ? source2 : void 0;
12998
13213
  }
12999
- const extensions = getWindowsExecutableExtensions();
13000
- for (const ext of extensions) {
13001
- const withExtension = `${candidate}${ext}`;
13002
- try {
13003
- await access22(withExtension, constants22.F_OK);
13004
- return withExtension;
13005
- } catch {
13214
+ if (typeof source2 === "string") {
13215
+ const numeric = Number(source2);
13216
+ if (Number.isFinite(numeric)) {
13217
+ return numeric;
13006
13218
  }
13007
13219
  }
13008
- return candidate;
13009
- }
13010
- function hasExecutableExtension(candidate) {
13011
- const lower = candidate.toLowerCase();
13012
- return getWindowsExecutableExtensions().some((ext) => lower.endsWith(ext));
13220
+ throw new Error(`${description} must be a number`);
13013
13221
  }
13014
- var DEFAULT_WINDOWS_EXTENSIONS = [".com", ".exe", ".bat", ".cmd", ".ps1"];
13015
- function getWindowsExecutableExtensions() {
13016
- if (process.platform !== "win32") {
13017
- return [];
13222
+ function resolveOptionalBoolean(source2) {
13223
+ if (source2 === void 0 || source2 === null || source2 === "") {
13224
+ return void 0;
13018
13225
  }
13019
- const fromEnv = process.env.PATHEXT?.split(";").map((ext) => ext.trim().toLowerCase()).filter((ext) => ext.length > 0);
13020
- return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_WINDOWS_EXTENSIONS;
13021
- }
13022
- function parseCodexJson(output) {
13023
- const trimmed = output.trim();
13024
- if (trimmed.length === 0) {
13025
- throw new Error("Codex CLI produced no output in --json mode");
13226
+ if (typeof source2 === "boolean") {
13227
+ return source2;
13026
13228
  }
13027
- try {
13028
- return JSON.parse(trimmed);
13029
- } catch {
13030
- const lineObjects = parseJsonLines(trimmed);
13031
- if (lineObjects) {
13032
- return lineObjects;
13229
+ if (typeof source2 === "string") {
13230
+ const lowered = source2.trim().toLowerCase();
13231
+ if (lowered === "true" || lowered === "1") {
13232
+ return true;
13033
13233
  }
13034
- const lastBrace = trimmed.lastIndexOf("{");
13035
- if (lastBrace >= 0) {
13036
- const candidate = trimmed.slice(lastBrace);
13037
- try {
13038
- return JSON.parse(candidate);
13039
- } catch {
13040
- }
13234
+ if (lowered === "false" || lowered === "0") {
13235
+ return false;
13041
13236
  }
13042
- const preview = trimmed.slice(0, 200);
13043
- throw new Error(`Codex CLI emitted invalid JSON: ${preview}${trimmed.length > 200 ? "\u2026" : ""}`);
13044
13237
  }
13238
+ throw new Error("expected boolean value");
13045
13239
  }
13046
- function extractAssistantText(parsed) {
13047
- if (Array.isArray(parsed)) {
13048
- const text = extractFromEventStream(parsed);
13049
- if (text) {
13050
- return text;
13051
- }
13240
+ function isLikelyEnvReference(value) {
13241
+ return /^[A-Z0-9_]+$/.test(value);
13242
+ }
13243
+ function resolveOptionalStringArray(source2, env, description) {
13244
+ if (source2 === void 0 || source2 === null) {
13245
+ return void 0;
13052
13246
  }
13053
- if (!parsed || typeof parsed !== "object") {
13054
- throw new Error("Codex CLI JSON response did not include an assistant message");
13247
+ if (!Array.isArray(source2)) {
13248
+ throw new Error(`${description} must be an array of strings`);
13055
13249
  }
13056
- const record = parsed;
13057
- const eventText = extractFromEvent(record);
13058
- if (eventText) {
13059
- return eventText;
13250
+ if (source2.length === 0) {
13251
+ return void 0;
13060
13252
  }
13061
- const messages = Array.isArray(record.messages) ? record.messages : void 0;
13062
- if (messages) {
13063
- for (let index = messages.length - 1; index >= 0; index -= 1) {
13064
- const entry = messages[index];
13065
- if (!entry || typeof entry !== "object") {
13066
- continue;
13067
- }
13068
- const role = entry.role;
13069
- if (role !== "assistant") {
13070
- continue;
13071
- }
13072
- const content = entry.content;
13073
- const flattened = flattenContent(content);
13074
- if (flattened) {
13075
- return flattened;
13076
- }
13253
+ const resolved = [];
13254
+ for (let i6 = 0; i6 < source2.length; i6++) {
13255
+ const item = source2[i6];
13256
+ if (typeof item !== "string") {
13257
+ throw new Error(`${description}[${i6}] must be a string`);
13077
13258
  }
13078
- }
13079
- const response = record.response;
13080
- if (response && typeof response === "object") {
13081
- const content = response.content;
13082
- const flattened = flattenContent(content);
13083
- if (flattened) {
13084
- return flattened;
13259
+ const trimmed = item.trim();
13260
+ if (trimmed.length === 0) {
13261
+ throw new Error(`${description}[${i6}] cannot be empty`);
13085
13262
  }
13086
- }
13087
- const output = record.output;
13088
- const flattenedOutput = flattenContent(output);
13089
- if (flattenedOutput) {
13090
- return flattenedOutput;
13091
- }
13092
- throw new Error("Codex CLI JSON response did not include an assistant message");
13093
- }
13094
- function extractFromEventStream(events) {
13095
- for (let index = events.length - 1; index >= 0; index -= 1) {
13096
- const candidate = events[index];
13097
- const text = extractFromEvent(candidate);
13098
- if (text) {
13099
- return text;
13263
+ const envValue = env[trimmed];
13264
+ if (envValue !== void 0) {
13265
+ if (envValue.trim().length === 0) {
13266
+ throw new Error(`Environment variable '${trimmed}' for ${description}[${i6}] is empty`);
13267
+ }
13268
+ resolved.push(envValue);
13269
+ } else {
13270
+ resolved.push(trimmed);
13100
13271
  }
13101
13272
  }
13102
- return void 0;
13273
+ return resolved.length > 0 ? resolved : void 0;
13103
13274
  }
13104
- function extractFromEvent(event) {
13105
- if (!event || typeof event !== "object") {
13106
- return void 0;
13275
+ var VSCodeProvider = class {
13276
+ id;
13277
+ kind;
13278
+ targetName;
13279
+ supportsBatch = true;
13280
+ config;
13281
+ constructor(targetName, config, kind) {
13282
+ this.id = `${kind}:${targetName}`;
13283
+ this.kind = kind;
13284
+ this.targetName = targetName;
13285
+ this.config = config;
13107
13286
  }
13108
- const record = event;
13109
- const type = typeof record.type === "string" ? record.type : void 0;
13110
- if (type === JSONL_TYPE_ITEM_COMPLETED) {
13111
- const item = record.item;
13112
- const text = extractFromItem(item);
13113
- if (text) {
13114
- return text;
13287
+ async invoke(request) {
13288
+ if (request.signal?.aborted) {
13289
+ throw new Error("VS Code provider request was aborted before dispatch");
13290
+ }
13291
+ const inputFiles = normalizeAttachments(request.inputFiles);
13292
+ const promptContent = buildPromptDocument2(request, inputFiles, request.guideline_patterns);
13293
+ const session = await dispatchAgentSession({
13294
+ userQuery: promptContent,
13295
+ extraAttachments: inputFiles,
13296
+ wait: this.config.waitForResponse,
13297
+ dryRun: this.config.dryRun,
13298
+ vscodeCmd: this.config.command,
13299
+ subagentRoot: this.config.subagentRoot,
13300
+ workspaceTemplate: this.config.workspaceTemplate,
13301
+ silent: true
13302
+ });
13303
+ if (session.exitCode !== 0 || !session.responseFile) {
13304
+ const failure = session.error ?? "VS Code subagent did not produce a response";
13305
+ throw new Error(failure);
13306
+ }
13307
+ if (this.config.dryRun) {
13308
+ return {
13309
+ text: "",
13310
+ raw: {
13311
+ session,
13312
+ inputFiles
13313
+ }
13314
+ };
13115
13315
  }
13316
+ const responseText = await readFile22(session.responseFile, "utf8");
13317
+ return {
13318
+ text: responseText,
13319
+ raw: {
13320
+ session,
13321
+ inputFiles
13322
+ }
13323
+ };
13116
13324
  }
13117
- const output = record.output ?? record.content;
13118
- const flattened = flattenContent(output);
13119
- if (flattened) {
13120
- return flattened;
13325
+ async invokeBatch(requests) {
13326
+ if (requests.length === 0) {
13327
+ return [];
13328
+ }
13329
+ const normalizedRequests = requests.map((req) => ({
13330
+ request: req,
13331
+ inputFiles: normalizeAttachments(req.inputFiles)
13332
+ }));
13333
+ const combinedInputFiles = mergeAttachments(
13334
+ normalizedRequests.map(({ inputFiles }) => inputFiles)
13335
+ );
13336
+ const userQueries = normalizedRequests.map(
13337
+ ({ request, inputFiles }) => buildPromptDocument2(request, inputFiles, request.guideline_patterns)
13338
+ );
13339
+ const session = await dispatchBatchAgent({
13340
+ userQueries,
13341
+ extraAttachments: combinedInputFiles,
13342
+ wait: this.config.waitForResponse,
13343
+ dryRun: this.config.dryRun,
13344
+ vscodeCmd: this.config.command,
13345
+ subagentRoot: this.config.subagentRoot,
13346
+ workspaceTemplate: this.config.workspaceTemplate,
13347
+ silent: true
13348
+ });
13349
+ if (session.exitCode !== 0 || !session.responseFiles) {
13350
+ const failure = session.error ?? "VS Code subagent did not produce batch responses";
13351
+ throw new Error(failure);
13352
+ }
13353
+ if (this.config.dryRun) {
13354
+ return normalizedRequests.map(({ inputFiles }) => ({
13355
+ text: "",
13356
+ raw: {
13357
+ session,
13358
+ inputFiles,
13359
+ allInputFiles: combinedInputFiles
13360
+ }
13361
+ }));
13362
+ }
13363
+ if (session.responseFiles.length !== requests.length) {
13364
+ throw new Error(
13365
+ `VS Code batch returned ${session.responseFiles.length} responses for ${requests.length} requests`
13366
+ );
13367
+ }
13368
+ const responses = [];
13369
+ for (const [index, responseFile] of session.responseFiles.entries()) {
13370
+ const responseText = await readFile22(responseFile, "utf8");
13371
+ responses.push({
13372
+ text: responseText,
13373
+ raw: {
13374
+ session,
13375
+ inputFiles: normalizedRequests[index]?.inputFiles,
13376
+ allInputFiles: combinedInputFiles,
13377
+ responseFile
13378
+ }
13379
+ });
13380
+ }
13381
+ return responses;
13121
13382
  }
13122
- return void 0;
13383
+ };
13384
+ function buildPromptDocument2(request, attachments, guidelinePatterns) {
13385
+ const parts = [];
13386
+ const guidelineFiles = collectGuidelineFiles2(attachments, guidelinePatterns);
13387
+ const attachmentFiles = collectAttachmentFiles(attachments);
13388
+ const nonGuidelineAttachments = attachmentFiles.filter(
13389
+ (file) => !guidelineFiles.includes(file)
13390
+ );
13391
+ const prereadBlock = buildMandatoryPrereadBlock2(guidelineFiles, nonGuidelineAttachments);
13392
+ if (prereadBlock.length > 0) {
13393
+ parts.push("\n", prereadBlock);
13394
+ }
13395
+ parts.push("\n[[ ## user_query ## ]]\n", request.prompt.trim());
13396
+ return parts.join("\n").trim();
13123
13397
  }
13124
- function extractFromItem(item) {
13125
- if (!item || typeof item !== "object") {
13126
- return void 0;
13398
+ function buildMandatoryPrereadBlock2(guidelineFiles, attachmentFiles) {
13399
+ if (guidelineFiles.length === 0 && attachmentFiles.length === 0) {
13400
+ return "";
13127
13401
  }
13128
- const record = item;
13129
- const itemType = typeof record.type === "string" ? record.type : void 0;
13130
- if (itemType === "agent_message" || itemType === "response" || itemType === "output") {
13131
- const text = flattenContent(record.text ?? record.content ?? record.output);
13132
- if (text) {
13133
- return text;
13134
- }
13402
+ const buildList = (files) => files.map((absolutePath) => {
13403
+ const fileName = path52.basename(absolutePath);
13404
+ const fileUri = pathToFileUri22(absolutePath);
13405
+ return `* [${fileName}](${fileUri})`;
13406
+ });
13407
+ const sections = [];
13408
+ if (guidelineFiles.length > 0) {
13409
+ sections.push(`Read all guideline files:
13410
+ ${buildList(guidelineFiles).join("\n")}.`);
13135
13411
  }
13136
- return void 0;
13412
+ if (attachmentFiles.length > 0) {
13413
+ sections.push(`Read all attachment files:
13414
+ ${buildList(attachmentFiles).join("\n")}.`);
13415
+ }
13416
+ sections.push(
13417
+ "If any file is missing, fail with ERROR: missing-file <filename> and stop.",
13418
+ "Then apply system_instructions on the user query below."
13419
+ );
13420
+ return sections.join("\n");
13137
13421
  }
13138
- function flattenContent(value) {
13139
- if (typeof value === "string") {
13140
- return value;
13422
+ function collectGuidelineFiles2(attachments, guidelinePatterns) {
13423
+ if (!attachments || attachments.length === 0) {
13424
+ return [];
13141
13425
  }
13142
- if (Array.isArray(value)) {
13143
- const parts = value.map((segment) => {
13144
- if (typeof segment === "string") {
13145
- return segment;
13146
- }
13147
- if (segment && typeof segment === "object" && "text" in segment) {
13148
- const text = segment.text;
13149
- return typeof text === "string" ? text : void 0;
13426
+ const unique = /* @__PURE__ */ new Map();
13427
+ for (const attachment of attachments) {
13428
+ const absolutePath = path52.resolve(attachment);
13429
+ const normalized = absolutePath.split(path52.sep).join("/");
13430
+ if (isGuidelineFile(normalized, guidelinePatterns)) {
13431
+ if (!unique.has(absolutePath)) {
13432
+ unique.set(absolutePath, absolutePath);
13150
13433
  }
13151
- return void 0;
13152
- }).filter((part) => typeof part === "string" && part.length > 0);
13153
- return parts.length > 0 ? parts.join(" \n") : void 0;
13154
- }
13155
- if (value && typeof value === "object" && "text" in value) {
13156
- const text = value.text;
13157
- return typeof text === "string" ? text : void 0;
13434
+ }
13158
13435
  }
13159
- return void 0;
13436
+ return Array.from(unique.values());
13160
13437
  }
13161
- function parseJsonLines(output) {
13162
- const lines = output.split(/\r?\n/).map((line2) => line2.trim()).filter((line2) => line2.length > 0);
13163
- if (lines.length <= 1) {
13164
- return void 0;
13438
+ function collectAttachmentFiles(attachments) {
13439
+ if (!attachments || attachments.length === 0) {
13440
+ return [];
13165
13441
  }
13166
- const parsed = [];
13167
- for (const line2 of lines) {
13168
- try {
13169
- parsed.push(JSON.parse(line2));
13170
- } catch {
13171
- return void 0;
13442
+ const unique = /* @__PURE__ */ new Map();
13443
+ for (const attachment of attachments) {
13444
+ const absolutePath = path52.resolve(attachment);
13445
+ if (!unique.has(absolutePath)) {
13446
+ unique.set(absolutePath, absolutePath);
13172
13447
  }
13173
13448
  }
13174
- return parsed;
13449
+ return Array.from(unique.values());
13175
13450
  }
13176
- function pickDetail(stderr, stdout) {
13177
- const errorText = stderr.trim();
13178
- if (errorText.length > 0) {
13179
- return errorText;
13451
+ function pathToFileUri22(filePath) {
13452
+ const absolutePath = path52.isAbsolute(filePath) ? filePath : path52.resolve(filePath);
13453
+ const normalizedPath = absolutePath.replace(/\\/g, "/");
13454
+ if (/^[a-zA-Z]:\//.test(normalizedPath)) {
13455
+ return `file:///${normalizedPath}`;
13180
13456
  }
13181
- const stdoutText = stdout.trim();
13182
- return stdoutText.length > 0 ? stdoutText : void 0;
13457
+ return `file://${normalizedPath}`;
13183
13458
  }
13184
- function formatTimeoutSuffix2(timeoutMs) {
13185
- if (!timeoutMs || timeoutMs <= 0) {
13186
- return "";
13459
+ function normalizeAttachments(attachments) {
13460
+ if (!attachments || attachments.length === 0) {
13461
+ return void 0;
13187
13462
  }
13188
- const seconds = Math.ceil(timeoutMs / 1e3);
13189
- return ` after ${seconds}s`;
13463
+ const deduped = /* @__PURE__ */ new Set();
13464
+ for (const attachment of attachments) {
13465
+ deduped.add(path52.resolve(attachment));
13466
+ }
13467
+ return Array.from(deduped);
13190
13468
  }
13191
- async function defaultCodexRunner(options) {
13192
- return await new Promise((resolve, reject) => {
13193
- const child = spawn2(options.executable, options.args, {
13194
- cwd: options.cwd,
13195
- env: options.env,
13196
- stdio: ["pipe", "pipe", "pipe"],
13197
- shell: shouldShellExecute(options.executable)
13198
- });
13199
- let stdout = "";
13200
- let stderr = "";
13201
- let timedOut = false;
13202
- const onAbort = () => {
13203
- child.kill("SIGTERM");
13204
- };
13205
- if (options.signal) {
13206
- if (options.signal.aborted) {
13207
- onAbort();
13208
- } else {
13209
- options.signal.addEventListener("abort", onAbort, { once: true });
13210
- }
13469
+ function mergeAttachments(all) {
13470
+ const deduped = /* @__PURE__ */ new Set();
13471
+ for (const list of all) {
13472
+ if (!list) continue;
13473
+ for (const inputFile of list) {
13474
+ deduped.add(path52.resolve(inputFile));
13211
13475
  }
13212
- let timeoutHandle;
13213
- if (options.timeoutMs && options.timeoutMs > 0) {
13214
- timeoutHandle = setTimeout(() => {
13215
- timedOut = true;
13216
- child.kill("SIGTERM");
13217
- }, options.timeoutMs);
13218
- timeoutHandle.unref?.();
13476
+ }
13477
+ return deduped.size > 0 ? Array.from(deduped) : void 0;
13478
+ }
13479
+ async function ensureVSCodeSubagents(options) {
13480
+ const { kind, count, verbose = false } = options;
13481
+ const vscodeCmd = kind === "vscode-insiders" ? "code-insiders" : "code";
13482
+ const subagentRoot = getSubagentRoot(vscodeCmd);
13483
+ try {
13484
+ if (verbose) {
13485
+ console.log(`Provisioning ${count} subagent(s) via: subagent ${vscodeCmd} provision`);
13219
13486
  }
13220
- child.stdout.setEncoding("utf8");
13221
- child.stdout.on("data", (chunk) => {
13222
- stdout += chunk;
13223
- });
13224
- child.stderr.setEncoding("utf8");
13225
- child.stderr.on("data", (chunk) => {
13226
- stderr += chunk;
13487
+ const result = await provisionSubagents({
13488
+ targetRoot: subagentRoot,
13489
+ subagents: count,
13490
+ dryRun: false
13227
13491
  });
13228
- child.stdin.end(options.prompt);
13229
- const cleanup = () => {
13230
- if (timeoutHandle) {
13231
- clearTimeout(timeoutHandle);
13492
+ if (verbose) {
13493
+ if (result.created.length > 0) {
13494
+ console.log(`Created ${result.created.length} new subagent(s)`);
13232
13495
  }
13233
- if (options.signal) {
13234
- options.signal.removeEventListener("abort", onAbort);
13496
+ if (result.skippedExisting.length > 0) {
13497
+ console.log(`Reusing ${result.skippedExisting.length} existing unlocked subagent(s)`);
13235
13498
  }
13499
+ console.log(`
13500
+ total unlocked subagents available: ${result.created.length + result.skippedExisting.length}`);
13501
+ }
13502
+ return {
13503
+ provisioned: true,
13504
+ message: `Provisioned ${count} subagent(s): ${result.created.length} created, ${result.skippedExisting.length} reused`
13505
+ };
13506
+ } catch (error) {
13507
+ const errorMessage = error instanceof Error ? error.message : String(error);
13508
+ if (verbose) {
13509
+ console.warn(`Provisioning failed (continuing anyway): ${errorMessage}`);
13510
+ }
13511
+ return {
13512
+ provisioned: false,
13513
+ message: `Provisioning failed: ${errorMessage}`
13236
13514
  };
13237
- child.on("error", (error) => {
13238
- cleanup();
13239
- reject(error);
13240
- });
13241
- child.on("close", (code) => {
13242
- cleanup();
13243
- resolve({
13244
- stdout,
13245
- stderr,
13246
- exitCode: typeof code === "number" ? code : -1,
13247
- timedOut
13248
- });
13249
- });
13250
- });
13251
- }
13252
- function shouldShellExecute(executable) {
13253
- if (process.platform !== "win32") {
13254
- return false;
13255
13515
  }
13256
- const lower = executable.toLowerCase();
13257
- return lower.endsWith(".cmd") || lower.endsWith(".bat") || lower.endsWith(".ps1");
13258
13516
  }
13259
13517
  function isRecord(value) {
13260
13518
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -13394,7 +13652,7 @@ var LlmJudgeEvaluator = class {
13394
13652
  const misses = Array.isArray(parsed.misses) ? parsed.misses.filter(isNonEmptyString).slice(0, 4) : [];
13395
13653
  const reasoning = parsed.reasoning ?? response.reasoning;
13396
13654
  const evaluatorRawRequest = {
13397
- id: randomUUID(),
13655
+ id: randomUUID2(),
13398
13656
  provider: judgeProvider.id,
13399
13657
  prompt,
13400
13658
  target: context2.target.name,
@@ -14395,7 +14653,7 @@ function sanitizeFilename(value) {
14395
14653
  return "prompt";
14396
14654
  }
14397
14655
  const sanitized = value.replace(/[^A-Za-z0-9._-]+/g, "_");
14398
- return sanitized.length > 0 ? sanitized : randomUUID2();
14656
+ return sanitized.length > 0 ? sanitized : randomUUID3();
14399
14657
  }
14400
14658
  async function invokeProvider(provider, options) {
14401
14659
  const { evalCase, promptInputs, attempt, agentTimeoutMs, signal } = options;
@@ -14756,7 +15014,7 @@ var Mutex = class {
14756
15014
  };
14757
15015
 
14758
15016
  // src/commands/eval/jsonl-writer.ts
14759
- import { createWriteStream } from "node:fs";
15017
+ import { createWriteStream as createWriteStream2 } from "node:fs";
14760
15018
  import { mkdir as mkdir4 } from "node:fs/promises";
14761
15019
  import path10 from "node:path";
14762
15020
  import { finished } from "node:stream/promises";
@@ -14769,7 +15027,7 @@ var JsonlWriter = class _JsonlWriter {
14769
15027
  }
14770
15028
  static async open(filePath) {
14771
15029
  await mkdir4(path10.dirname(filePath), { recursive: true });
14772
- const stream = createWriteStream(filePath, { flags: "w", encoding: "utf8" });
15030
+ const stream = createWriteStream2(filePath, { flags: "w", encoding: "utf8" });
14773
15031
  return new _JsonlWriter(stream);
14774
15032
  }
14775
15033
  async append(record) {
@@ -14798,7 +15056,7 @@ var JsonlWriter = class _JsonlWriter {
14798
15056
  };
14799
15057
 
14800
15058
  // src/commands/eval/yaml-writer.ts
14801
- import { createWriteStream as createWriteStream2 } from "node:fs";
15059
+ import { createWriteStream as createWriteStream3 } from "node:fs";
14802
15060
  import { mkdir as mkdir5 } from "node:fs/promises";
14803
15061
  import path11 from "node:path";
14804
15062
  import { finished as finished2 } from "node:stream/promises";
@@ -14813,7 +15071,7 @@ var YamlWriter = class _YamlWriter {
14813
15071
  }
14814
15072
  static async open(filePath) {
14815
15073
  await mkdir5(path11.dirname(filePath), { recursive: true });
14816
- const stream = createWriteStream2(filePath, { flags: "w", encoding: "utf8" });
15074
+ const stream = createWriteStream3(filePath, { flags: "w", encoding: "utf8" });
14817
15075
  return new _YamlWriter(stream);
14818
15076
  }
14819
15077
  async append(record) {
@@ -15269,7 +15527,7 @@ function normalizeNumber(value, fallback) {
15269
15527
  return fallback;
15270
15528
  }
15271
15529
  function normalizeOptions(rawOptions) {
15272
- const formatStr = normalizeString(rawOptions.format) ?? "jsonl";
15530
+ const formatStr = normalizeString(rawOptions.outputFormat) ?? "jsonl";
15273
15531
  const format = formatStr === "yaml" ? "yaml" : "jsonl";
15274
15532
  const workers = normalizeNumber(rawOptions.workers, 0);
15275
15533
  return {
@@ -15489,7 +15747,11 @@ function registerEvalCommand(program) {
15489
15747
  "--workers <count>",
15490
15748
  "Number of parallel workers (default: 1, max: 50). Can also be set per-target in targets.yaml",
15491
15749
  (value) => parseInteger(value, 1)
15492
- ).option("--out <path>", "Write results to the specified path").option("--format <format>", "Output format: 'jsonl' or 'yaml' (default: jsonl)", "jsonl").option("--dry-run", "Use mock provider responses instead of real LLM calls", false).option(
15750
+ ).option("--out <path>", "Write results to the specified path").option(
15751
+ "--output-format <format>",
15752
+ "Output format: 'jsonl' or 'yaml' (default: jsonl)",
15753
+ "jsonl"
15754
+ ).option("--dry-run", "Use mock provider responses instead of real LLM calls", false).option(
15493
15755
  "--dry-run-delay <ms>",
15494
15756
  "Fixed delay in milliseconds for dry-run mode (overridden by delay range if specified)",
15495
15757
  (value) => parseInteger(value, 0),
@@ -15526,58 +15788,105 @@ function registerEvalCommand(program) {
15526
15788
  // src/commands/init/index.ts
15527
15789
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
15528
15790
  import path15 from "node:path";
15791
+ import * as readline from "node:readline/promises";
15529
15792
 
15530
15793
  // src/templates/index.ts
15531
- import { readFileSync } from "node:fs";
15794
+ import { readFileSync, readdirSync, statSync } from "node:fs";
15532
15795
  import path14 from "node:path";
15533
15796
  import { fileURLToPath as fileURLToPath2 } from "node:url";
15534
15797
  var TemplateManager = class {
15535
- static getTemplates() {
15798
+ static getGithubTemplates() {
15799
+ return this.getTemplatesFromDir("github");
15800
+ }
15801
+ static getAgentvTemplates() {
15802
+ return this.getTemplatesFromDir("agentv");
15803
+ }
15804
+ static getTemplatesFromDir(subdir) {
15536
15805
  const currentDir = path14.dirname(fileURLToPath2(import.meta.url));
15537
15806
  let templatesDir;
15538
15807
  if (currentDir.includes(path14.sep + "dist")) {
15539
- templatesDir = path14.join(currentDir, "templates");
15808
+ templatesDir = path14.join(currentDir, "templates", subdir);
15540
15809
  } else {
15541
- templatesDir = currentDir;
15810
+ templatesDir = path14.join(currentDir, subdir);
15542
15811
  }
15543
- const evalBuildPrompt = readFileSync(
15544
- path14.join(templatesDir, "eval-build.prompt.md"),
15545
- "utf-8"
15546
- );
15547
- const evalSchema = readFileSync(
15548
- path14.join(templatesDir, "eval-schema.json"),
15549
- "utf-8"
15550
- );
15551
- const configSchema = readFileSync(
15552
- path14.join(templatesDir, "config-schema.json"),
15553
- "utf-8"
15554
- );
15555
- return [
15556
- {
15557
- path: "prompts/eval-build.prompt.md",
15558
- content: evalBuildPrompt
15559
- },
15560
- {
15561
- path: "contexts/eval-schema.json",
15562
- content: evalSchema
15563
- },
15564
- {
15565
- path: "contexts/config-schema.json",
15566
- content: configSchema
15812
+ return this.readTemplatesRecursively(templatesDir, "");
15813
+ }
15814
+ static readTemplatesRecursively(dir, relativePath) {
15815
+ const templates = [];
15816
+ const entries = readdirSync(dir);
15817
+ for (const entry of entries) {
15818
+ const fullPath = path14.join(dir, entry);
15819
+ const stat4 = statSync(fullPath);
15820
+ const entryRelativePath = relativePath ? path14.join(relativePath, entry) : entry;
15821
+ if (stat4.isDirectory()) {
15822
+ templates.push(...this.readTemplatesRecursively(fullPath, entryRelativePath));
15823
+ } else {
15824
+ const content = readFileSync(fullPath, "utf-8");
15825
+ templates.push({
15826
+ path: entryRelativePath.split(path14.sep).join("/"),
15827
+ // Normalize to forward slashes
15828
+ content
15829
+ });
15567
15830
  }
15568
- ];
15831
+ }
15832
+ return templates;
15569
15833
  }
15570
15834
  };
15571
15835
 
15572
15836
  // src/commands/init/index.ts
15837
+ async function promptYesNo(message) {
15838
+ const rl = readline.createInterface({
15839
+ input: process.stdin,
15840
+ output: process.stdout
15841
+ });
15842
+ try {
15843
+ const answer = await rl.question(`${message} (y/N): `);
15844
+ return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
15845
+ } finally {
15846
+ rl.close();
15847
+ }
15848
+ }
15573
15849
  async function initCommand(options = {}) {
15574
15850
  const targetPath = path15.resolve(options.targetPath ?? ".");
15575
15851
  const githubDir = path15.join(targetPath, ".github");
15852
+ const agentvDir = path15.join(targetPath, ".agentv");
15853
+ const githubTemplates = TemplateManager.getGithubTemplates();
15854
+ const agentvTemplates = TemplateManager.getAgentvTemplates();
15855
+ const existingFiles = [];
15856
+ if (existsSync(githubDir)) {
15857
+ for (const template of githubTemplates) {
15858
+ const targetFilePath = path15.join(githubDir, template.path);
15859
+ if (existsSync(targetFilePath)) {
15860
+ existingFiles.push(path15.relative(targetPath, targetFilePath));
15861
+ }
15862
+ }
15863
+ }
15864
+ if (existsSync(agentvDir)) {
15865
+ for (const template of agentvTemplates) {
15866
+ const targetFilePath = path15.join(agentvDir, template.path);
15867
+ if (existsSync(targetFilePath)) {
15868
+ existingFiles.push(path15.relative(targetPath, targetFilePath));
15869
+ }
15870
+ }
15871
+ }
15872
+ if (existingFiles.length > 0) {
15873
+ console.log("We detected an existing setup:");
15874
+ existingFiles.forEach((file) => console.log(` - ${file}`));
15875
+ console.log();
15876
+ const shouldReplace = await promptYesNo("Do you want to replace these files?");
15877
+ if (!shouldReplace) {
15878
+ console.log("\nInit cancelled. No files were changed.");
15879
+ return;
15880
+ }
15881
+ console.log();
15882
+ }
15576
15883
  if (!existsSync(githubDir)) {
15577
15884
  mkdirSync(githubDir, { recursive: true });
15578
15885
  }
15579
- const templates = TemplateManager.getTemplates();
15580
- for (const template of templates) {
15886
+ if (!existsSync(agentvDir)) {
15887
+ mkdirSync(agentvDir, { recursive: true });
15888
+ }
15889
+ for (const template of githubTemplates) {
15581
15890
  const targetFilePath = path15.join(githubDir, template.path);
15582
15891
  const targetDirPath = path15.dirname(targetFilePath);
15583
15892
  if (!existsSync(targetDirPath)) {
@@ -15586,11 +15895,26 @@ async function initCommand(options = {}) {
15586
15895
  writeFileSync(targetFilePath, template.content, "utf-8");
15587
15896
  console.log(`Created ${path15.relative(targetPath, targetFilePath)}`);
15588
15897
  }
15898
+ for (const template of agentvTemplates) {
15899
+ const targetFilePath = path15.join(agentvDir, template.path);
15900
+ const targetDirPath = path15.dirname(targetFilePath);
15901
+ if (!existsSync(targetDirPath)) {
15902
+ mkdirSync(targetDirPath, { recursive: true });
15903
+ }
15904
+ writeFileSync(targetFilePath, template.content, "utf-8");
15905
+ console.log(`Created ${path15.relative(targetPath, targetFilePath)}`);
15906
+ }
15589
15907
  console.log("\nAgentV initialized successfully!");
15590
15908
  console.log(`
15591
15909
  Files installed to ${path15.relative(targetPath, githubDir)}:`);
15592
- templates.forEach((t) => console.log(` - ${t.path}`));
15593
- console.log("\nYou can now create eval files using the schema and prompt templates.");
15910
+ githubTemplates.forEach((t) => console.log(` - ${t.path}`));
15911
+ console.log(`
15912
+ Files installed to ${path15.relative(targetPath, agentvDir)}:`);
15913
+ agentvTemplates.forEach((t) => console.log(` - ${t.path}`));
15914
+ console.log("\nYou can now:");
15915
+ console.log(" 1. Edit .agentv/.env with your API credentials");
15916
+ console.log(" 2. Configure targets in .agentv/targets.yaml");
15917
+ console.log(" 3. Create eval files using the schema and prompt templates");
15594
15918
  }
15595
15919
 
15596
15920
  // src/commands/status.ts
@@ -16556,4 +16880,4 @@ export {
16556
16880
  createProgram,
16557
16881
  runCli
16558
16882
  };
16559
- //# sourceMappingURL=chunk-THVRLL37.js.map
16883
+ //# sourceMappingURL=chunk-5WBKOCCW.js.map