agentv 0.5.0 → 0.5.1

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,13 @@ 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 { constants as constants22 } from "node:fs";
5044
+ import { access as access22, copyFile as copyFile2, mkdtemp, mkdir as mkdir3, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
5045
+ import { tmpdir } from "node:os";
5046
+ import path42 from "node:path";
5047
+ import { promisify as promisify22 } from "node:util";
5048
+ import path32 from "node:path";
5042
5049
 
5043
5050
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
5044
5051
  var external_exports = {};
@@ -9083,7 +9090,7 @@ var NEVER = INVALID;
9083
9090
 
9084
9091
  // ../../packages/core/dist/index.js
9085
9092
  import { readFile as readFile22 } from "node:fs/promises";
9086
- import path32 from "node:path";
9093
+ import path52 from "node:path";
9087
9094
 
9088
9095
  // ../../node_modules/.pnpm/subagent@0.4.6/node_modules/subagent/dist/vscode/agentDispatch.js
9089
9096
  import { exec, spawn } from "child_process";
@@ -11021,13 +11028,6 @@ async function provisionSubagents(options) {
11021
11028
  }
11022
11029
 
11023
11030
  // ../../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
11031
  import { constants as constants32 } from "node:fs";
11032
11032
  import { access as access32, readFile as readFile3 } from "node:fs/promises";
11033
11033
  import path62 from "node:path";
@@ -11960,1301 +11960,1301 @@ function formatTimeoutSuffix(timeoutMs) {
11960
11960
  const seconds = Math.ceil(timeoutMs / 1e3);
11961
11961
  return ` after ${seconds}s`;
11962
11962
  }
11963
- var DEFAULT_MOCK_RESPONSE = '{"answer":"Mock provider response. Configure targets.yaml to supply a custom value."}';
11964
- var MockProvider = class {
11963
+ function buildPromptDocument(request, inputFiles, options) {
11964
+ const parts = [];
11965
+ const guidelineFiles = collectGuidelineFiles(
11966
+ inputFiles,
11967
+ options?.guidelinePatterns ?? request.guideline_patterns,
11968
+ options?.guidelineOverrides
11969
+ );
11970
+ const inputFilesList = collectInputFiles(inputFiles);
11971
+ const nonGuidelineInputFiles = inputFilesList.filter(
11972
+ (file) => !guidelineFiles.includes(file)
11973
+ );
11974
+ const prereadBlock = buildMandatoryPrereadBlock(guidelineFiles, nonGuidelineInputFiles);
11975
+ if (prereadBlock.length > 0) {
11976
+ parts.push("\n", prereadBlock);
11977
+ }
11978
+ parts.push("\n[[ ## user_query ## ]]\n", request.prompt.trim());
11979
+ return parts.join("\n").trim();
11980
+ }
11981
+ function normalizeInputFiles2(inputFiles) {
11982
+ if (!inputFiles || inputFiles.length === 0) {
11983
+ return void 0;
11984
+ }
11985
+ const deduped = /* @__PURE__ */ new Map();
11986
+ for (const inputFile of inputFiles) {
11987
+ const absolutePath = path32.resolve(inputFile);
11988
+ if (!deduped.has(absolutePath)) {
11989
+ deduped.set(absolutePath, absolutePath);
11990
+ }
11991
+ }
11992
+ return Array.from(deduped.values());
11993
+ }
11994
+ function collectGuidelineFiles(inputFiles, guidelinePatterns, overrides) {
11995
+ if (!inputFiles || inputFiles.length === 0) {
11996
+ return [];
11997
+ }
11998
+ const unique = /* @__PURE__ */ new Map();
11999
+ for (const inputFile of inputFiles) {
12000
+ const absolutePath = path32.resolve(inputFile);
12001
+ if (overrides?.has(absolutePath)) {
12002
+ if (!unique.has(absolutePath)) {
12003
+ unique.set(absolutePath, absolutePath);
12004
+ }
12005
+ continue;
12006
+ }
12007
+ const normalized = absolutePath.split(path32.sep).join("/");
12008
+ if (isGuidelineFile(normalized, guidelinePatterns)) {
12009
+ if (!unique.has(absolutePath)) {
12010
+ unique.set(absolutePath, absolutePath);
12011
+ }
12012
+ }
12013
+ }
12014
+ return Array.from(unique.values());
12015
+ }
12016
+ function collectInputFiles(inputFiles) {
12017
+ if (!inputFiles || inputFiles.length === 0) {
12018
+ return [];
12019
+ }
12020
+ const unique = /* @__PURE__ */ new Map();
12021
+ for (const inputFile of inputFiles) {
12022
+ const absolutePath = path32.resolve(inputFile);
12023
+ if (!unique.has(absolutePath)) {
12024
+ unique.set(absolutePath, absolutePath);
12025
+ }
12026
+ }
12027
+ return Array.from(unique.values());
12028
+ }
12029
+ function buildMandatoryPrereadBlock(guidelineFiles, inputFiles) {
12030
+ if (guidelineFiles.length === 0 && inputFiles.length === 0) {
12031
+ return "";
12032
+ }
12033
+ const buildList = (files) => files.map((absolutePath) => {
12034
+ const fileName = path32.basename(absolutePath);
12035
+ const fileUri = pathToFileUri2(absolutePath);
12036
+ return `* [${fileName}](${fileUri})`;
12037
+ });
12038
+ const sections = [];
12039
+ if (guidelineFiles.length > 0) {
12040
+ sections.push(`Read all guideline files:
12041
+ ${buildList(guidelineFiles).join("\n")}.`);
12042
+ }
12043
+ if (inputFiles.length > 0) {
12044
+ sections.push(`Read all input files:
12045
+ ${buildList(inputFiles).join("\n")}.`);
12046
+ }
12047
+ sections.push(
12048
+ "If any file is missing, fail with ERROR: missing-file <filename> and stop.",
12049
+ "Then apply system_instructions on the user query below."
12050
+ );
12051
+ return sections.join("\n");
12052
+ }
12053
+ function pathToFileUri2(filePath) {
12054
+ const absolutePath = path32.isAbsolute(filePath) ? filePath : path32.resolve(filePath);
12055
+ const normalizedPath = absolutePath.replace(/\\/g, "/");
12056
+ if (/^[a-zA-Z]:\//.test(normalizedPath)) {
12057
+ return `file:///${normalizedPath}`;
12058
+ }
12059
+ return `file://${normalizedPath}`;
12060
+ }
12061
+ var execAsync22 = promisify22(execCallback);
12062
+ var WORKSPACE_PREFIX = "agentv-codex-";
12063
+ var PROMPT_FILENAME = "prompt.md";
12064
+ var FILES_DIR = "files";
12065
+ var JSONL_TYPE_ITEM_COMPLETED = "item.completed";
12066
+ var CodexProvider = class {
11965
12067
  id;
11966
- kind = "mock";
12068
+ kind = "codex";
11967
12069
  targetName;
11968
- cannedResponse;
11969
- delayMs;
11970
- delayMinMs;
11971
- delayMaxMs;
11972
- constructor(targetName, config) {
11973
- this.id = `mock:${targetName}`;
12070
+ supportsBatch = false;
12071
+ config;
12072
+ runCodex;
12073
+ environmentCheck;
12074
+ resolvedExecutable;
12075
+ constructor(targetName, config, runner = defaultCodexRunner) {
12076
+ this.id = `codex:${targetName}`;
11974
12077
  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;
12078
+ this.config = config;
12079
+ this.runCodex = runner;
11979
12080
  }
11980
12081
  async invoke(request) {
11981
- const delay = this.calculateDelay();
11982
- if (delay > 0) {
11983
- await new Promise((resolve) => setTimeout(resolve, delay));
12082
+ if (request.signal?.aborted) {
12083
+ throw new Error("Codex provider request was aborted before execution");
11984
12084
  }
11985
- return {
11986
- text: this.cannedResponse,
11987
- raw: {
11988
- prompt: request.prompt,
11989
- guidelines: request.guidelines
12085
+ await this.ensureEnvironmentReady();
12086
+ const inputFiles = normalizeInputFiles2(request.inputFiles);
12087
+ const originalGuidelines = new Set(
12088
+ collectGuidelineFiles(inputFiles, request.guideline_patterns).map((file) => path42.resolve(file))
12089
+ );
12090
+ const workspaceRoot = await this.createWorkspace();
12091
+ try {
12092
+ const { mirroredInputFiles, guidelineMirrors } = await this.mirrorInputFiles(
12093
+ inputFiles,
12094
+ workspaceRoot,
12095
+ originalGuidelines
12096
+ );
12097
+ const promptContent = buildPromptDocument(request, mirroredInputFiles, {
12098
+ guidelinePatterns: request.guideline_patterns,
12099
+ guidelineOverrides: guidelineMirrors
12100
+ });
12101
+ const promptFile = path42.join(workspaceRoot, PROMPT_FILENAME);
12102
+ await writeFile3(promptFile, promptContent, "utf8");
12103
+ const args = this.buildCodexArgs();
12104
+ const cwd = this.resolveCwd(workspaceRoot);
12105
+ const result = await this.executeCodex(args, cwd, promptContent, request.signal);
12106
+ if (result.timedOut) {
12107
+ throw new Error(
12108
+ `Codex CLI timed out${formatTimeoutSuffix2(this.config.timeoutMs ?? void 0)}`
12109
+ );
11990
12110
  }
11991
- };
12111
+ if (result.exitCode !== 0) {
12112
+ const detail = pickDetail(result.stderr, result.stdout);
12113
+ const prefix = `Codex CLI exited with code ${result.exitCode}`;
12114
+ throw new Error(detail ? `${prefix}: ${detail}` : prefix);
12115
+ }
12116
+ const parsed = parseCodexJson(result.stdout);
12117
+ const assistantText = extractAssistantText(parsed);
12118
+ return {
12119
+ text: assistantText,
12120
+ raw: {
12121
+ response: parsed,
12122
+ stdout: result.stdout,
12123
+ stderr: result.stderr,
12124
+ exitCode: result.exitCode,
12125
+ args,
12126
+ executable: this.resolvedExecutable ?? this.config.executable,
12127
+ promptFile,
12128
+ workspace: workspaceRoot,
12129
+ inputFiles: mirroredInputFiles
12130
+ }
12131
+ };
12132
+ } finally {
12133
+ await this.cleanupWorkspace(workspaceRoot);
12134
+ }
11992
12135
  }
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;
12136
+ async ensureEnvironmentReady() {
12137
+ if (!this.environmentCheck) {
12138
+ this.environmentCheck = this.validateEnvironment();
11998
12139
  }
11999
- return this.delayMs;
12140
+ await this.environmentCheck;
12000
12141
  }
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;
12142
+ async validateEnvironment() {
12143
+ this.resolvedExecutable = await locateExecutable(this.config.executable);
12014
12144
  }
12015
- const trimmed = value.trim();
12016
- if (trimmed.length === 0) {
12017
- return DEFAULT_AZURE_API_VERSION;
12145
+ resolveCwd(workspaceRoot) {
12146
+ if (!this.config.cwd) {
12147
+ return workspaceRoot;
12148
+ }
12149
+ return path42.resolve(this.config.cwd);
12018
12150
  }
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":
12151
+ buildCodexArgs() {
12152
+ const args = ["--ask-for-approval", "never", "exec", "--json", "--color", "never", "--skip-git-repo-check"];
12153
+ if (this.config.args && this.config.args.length > 0) {
12154
+ args.push(...this.config.args);
12155
+ }
12156
+ args.push("-");
12157
+ return args;
12158
+ }
12159
+ async executeCodex(args, cwd, promptContent, signal) {
12160
+ try {
12161
+ return await this.runCodex({
12162
+ executable: this.resolvedExecutable ?? this.config.executable,
12163
+ args,
12164
+ cwd,
12165
+ prompt: promptContent,
12166
+ timeoutMs: this.config.timeoutMs,
12167
+ env: process.env,
12168
+ signal
12169
+ });
12170
+ } catch (error) {
12171
+ const err = error;
12172
+ if (err.code === "ENOENT") {
12173
+ throw new Error(
12174
+ `Codex executable '${this.config.executable}' was not found. Update the target settings.executable or add it to PATH.`
12175
+ );
12176
+ }
12177
+ throw error;
12178
+ }
12179
+ }
12180
+ async mirrorInputFiles(inputFiles, workspaceRoot, guidelineOriginals) {
12181
+ if (!inputFiles || inputFiles.length === 0) {
12089
12182
  return {
12090
- kind: "cli",
12091
- name: parsed.name,
12092
- judgeTarget: parsed.judge_target,
12093
- workers: parsed.workers,
12094
- providerBatching,
12095
- config: resolveCliConfig(parsed, env)
12183
+ mirroredInputFiles: void 0,
12184
+ guidelineMirrors: /* @__PURE__ */ new Set()
12096
12185
  };
12097
- default:
12098
- throw new Error(`Unsupported provider '${parsed.provider}' in target '${parsed.name}'`);
12186
+ }
12187
+ const filesRoot = path42.join(workspaceRoot, FILES_DIR);
12188
+ await mkdir3(filesRoot, { recursive: true });
12189
+ const mirrored = [];
12190
+ const guidelineMirrors = /* @__PURE__ */ new Set();
12191
+ const nameCounts = /* @__PURE__ */ new Map();
12192
+ for (const inputFile of inputFiles) {
12193
+ const absoluteSource = path42.resolve(inputFile);
12194
+ const baseName = path42.basename(absoluteSource);
12195
+ const count = nameCounts.get(baseName) ?? 0;
12196
+ nameCounts.set(baseName, count + 1);
12197
+ const finalName = count === 0 ? baseName : `${baseName}.${count}`;
12198
+ const destination = path42.join(filesRoot, finalName);
12199
+ await copyFile2(absoluteSource, destination);
12200
+ const resolvedDestination = path42.resolve(destination);
12201
+ mirrored.push(resolvedDestination);
12202
+ if (guidelineOriginals.has(absoluteSource)) {
12203
+ guidelineMirrors.add(resolvedDestination);
12204
+ }
12205
+ }
12206
+ return {
12207
+ mirroredInputFiles: mirrored,
12208
+ guidelineMirrors
12209
+ };
12099
12210
  }
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;
12211
+ async createWorkspace() {
12212
+ return await mkdtemp(path42.join(tmpdir(), WORKSPACE_PREFIX));
12248
12213
  }
12249
- if (typeof source2 !== "object" || Array.isArray(source2)) {
12250
- throw new Error(`${targetName} env overrides must be an object map of strings`);
12214
+ async cleanupWorkspace(workspaceRoot) {
12215
+ try {
12216
+ await rm2(workspaceRoot, { recursive: true, force: true });
12217
+ } catch {
12218
+ }
12251
12219
  }
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`);
12220
+ };
12221
+ async function locateExecutable(candidate) {
12222
+ const includesPathSeparator = candidate.includes("/") || candidate.includes("\\");
12223
+ if (includesPathSeparator) {
12224
+ const resolved = path42.isAbsolute(candidate) ? candidate : path42.resolve(candidate);
12225
+ const executablePath = await ensureWindowsExecutableVariant(resolved);
12226
+ await access22(executablePath, constants22.F_OK);
12227
+ return executablePath;
12228
+ }
12229
+ const locator = process.platform === "win32" ? "where" : "which";
12230
+ try {
12231
+ const { stdout } = await execAsync22(`${locator} ${candidate}`);
12232
+ const lines = stdout.split(/\r?\n/).map((line2) => line2.trim()).filter((line2) => line2.length > 0);
12233
+ const preferred = selectExecutableCandidate(lines);
12234
+ if (preferred) {
12235
+ const executablePath = await ensureWindowsExecutableVariant(preferred);
12236
+ await access22(executablePath, constants22.F_OK);
12237
+ return executablePath;
12257
12238
  }
12258
- const resolvedValue = resolveString(value, env, `${targetName} env override '${key2}'`);
12259
- resolved[key2] = resolvedValue;
12239
+ } catch {
12260
12240
  }
12261
- return Object.keys(resolved).length > 0 ? resolved : void 0;
12241
+ throw new Error(`Codex executable '${candidate}' was not found on PATH`);
12262
12242
  }
12263
- function resolveTimeoutMs(source2, description) {
12264
- const seconds = resolveOptionalNumber(source2, `${description} (seconds)`);
12265
- if (seconds === void 0) {
12243
+ function selectExecutableCandidate(candidates) {
12244
+ if (candidates.length === 0) {
12266
12245
  return void 0;
12267
12246
  }
12268
- if (seconds <= 0) {
12269
- throw new Error(`${description} must be greater than zero seconds`);
12247
+ if (process.platform !== "win32") {
12248
+ return candidates[0];
12270
12249
  }
12271
- return Math.floor(seconds * 1e3);
12250
+ const extensions = getWindowsExecutableExtensions();
12251
+ for (const ext of extensions) {
12252
+ const match = candidates.find((candidate) => candidate.toLowerCase().endsWith(ext));
12253
+ if (match) {
12254
+ return match;
12255
+ }
12256
+ }
12257
+ return candidates[0];
12272
12258
  }
12273
- function resolveCliHealthcheck(source2, env, targetName) {
12274
- if (source2 === void 0 || source2 === null) {
12275
- return void 0;
12259
+ async function ensureWindowsExecutableVariant(candidate) {
12260
+ if (process.platform !== "win32") {
12261
+ return candidate;
12276
12262
  }
12277
- if (typeof source2 !== "object" || Array.isArray(source2)) {
12278
- throw new Error(`${targetName} healthcheck must be an object`);
12263
+ if (hasExecutableExtension(candidate)) {
12264
+ return candidate;
12279
12265
  }
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
- };
12266
+ const extensions = getWindowsExecutableExtensions();
12267
+ for (const ext of extensions) {
12268
+ const withExtension = `${candidate}${ext}`;
12269
+ try {
12270
+ await access22(withExtension, constants22.F_OK);
12271
+ return withExtension;
12272
+ } catch {
12273
+ }
12293
12274
  }
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
- };
12275
+ return candidate;
12276
+ }
12277
+ function hasExecutableExtension(candidate) {
12278
+ const lower = candidate.toLowerCase();
12279
+ return getWindowsExecutableExtensions().some((ext) => lower.endsWith(ext));
12280
+ }
12281
+ var DEFAULT_WINDOWS_EXTENSIONS = [".com", ".exe", ".bat", ".cmd", ".ps1"];
12282
+ function getWindowsExecutableExtensions() {
12283
+ if (process.platform !== "win32") {
12284
+ return [];
12312
12285
  }
12313
- throw new Error(`${targetName} healthcheck type must be 'http' or 'command'`);
12286
+ const fromEnv = process.env.PATHEXT?.split(";").map((ext) => ext.trim().toLowerCase()).filter((ext) => ext.length > 0);
12287
+ return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_WINDOWS_EXTENSIONS;
12314
12288
  }
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
- );
12289
+ function parseCodexJson(output) {
12290
+ const trimmed = output.trim();
12291
+ if (trimmed.length === 0) {
12292
+ throw new Error("Codex CLI produced no output in --json mode");
12293
+ }
12294
+ try {
12295
+ return JSON.parse(trimmed);
12296
+ } catch {
12297
+ const lineObjects = parseJsonLines(trimmed);
12298
+ if (lineObjects) {
12299
+ return lineObjects;
12300
+ }
12301
+ const lastBrace = trimmed.lastIndexOf("{");
12302
+ if (lastBrace >= 0) {
12303
+ const candidate = trimmed.slice(lastBrace);
12304
+ try {
12305
+ return JSON.parse(candidate);
12306
+ } catch {
12307
+ }
12322
12308
  }
12309
+ const preview = trimmed.slice(0, 200);
12310
+ throw new Error(`Codex CLI emitted invalid JSON: ${preview}${trimmed.length > 200 ? "\u2026" : ""}`);
12323
12311
  }
12324
12312
  }
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]);
12313
+ function extractAssistantText(parsed) {
12314
+ if (Array.isArray(parsed)) {
12315
+ const text = extractFromEventStream(parsed);
12316
+ if (text) {
12317
+ return text;
12331
12318
  }
12332
12319
  }
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`);
12320
+ if (!parsed || typeof parsed !== "object") {
12321
+ throw new Error("Codex CLI JSON response did not include an assistant message");
12342
12322
  }
12343
- return value;
12344
- }
12345
- function resolveOptionalString(source2, env, description, options) {
12346
- if (source2 === void 0 || source2 === null) {
12347
- return void 0;
12323
+ const record = parsed;
12324
+ const eventText = extractFromEvent(record);
12325
+ if (eventText) {
12326
+ return eventText;
12348
12327
  }
12349
- if (typeof source2 !== "string") {
12350
- throw new Error(`${description} must be a string`);
12328
+ const messages = Array.isArray(record.messages) ? record.messages : void 0;
12329
+ if (messages) {
12330
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
12331
+ const entry = messages[index];
12332
+ if (!entry || typeof entry !== "object") {
12333
+ continue;
12334
+ }
12335
+ const role = entry.role;
12336
+ if (role !== "assistant") {
12337
+ continue;
12338
+ }
12339
+ const content = entry.content;
12340
+ const flattened = flattenContent(content);
12341
+ if (flattened) {
12342
+ return flattened;
12343
+ }
12344
+ }
12345
+ }
12346
+ const response = record.response;
12347
+ if (response && typeof response === "object") {
12348
+ const content = response.content;
12349
+ const flattened = flattenContent(content);
12350
+ if (flattened) {
12351
+ return flattened;
12352
+ }
12353
+ }
12354
+ const output = record.output;
12355
+ const flattenedOutput = flattenContent(output);
12356
+ if (flattenedOutput) {
12357
+ return flattenedOutput;
12358
+ }
12359
+ throw new Error("Codex CLI JSON response did not include an assistant message");
12360
+ }
12361
+ function extractFromEventStream(events) {
12362
+ for (let index = events.length - 1; index >= 0; index -= 1) {
12363
+ const candidate = events[index];
12364
+ const text = extractFromEvent(candidate);
12365
+ if (text) {
12366
+ return text;
12367
+ }
12351
12368
  }
12352
- const trimmed = source2.trim();
12353
- if (trimmed.length === 0) {
12369
+ return void 0;
12370
+ }
12371
+ function extractFromEvent(event) {
12372
+ if (!event || typeof event !== "object") {
12354
12373
  return void 0;
12355
12374
  }
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`);
12375
+ const record = event;
12376
+ const type = typeof record.type === "string" ? record.type : void 0;
12377
+ if (type === JSONL_TYPE_ITEM_COMPLETED) {
12378
+ const item = record.item;
12379
+ const text = extractFromItem(item);
12380
+ if (text) {
12381
+ return text;
12360
12382
  }
12361
- return envValue;
12362
12383
  }
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;
12369
- }
12370
- if (!allowLiteral) {
12371
- throw new Error(`Environment variable '${trimmed}' required for ${description} is not set`);
12372
- }
12384
+ const output = record.output ?? record.content;
12385
+ const flattened = flattenContent(output);
12386
+ if (flattened) {
12387
+ return flattened;
12373
12388
  }
12374
- return trimmed;
12389
+ return void 0;
12375
12390
  }
12376
- function resolveOptionalLiteralString(source2) {
12377
- if (source2 === void 0 || source2 === null) {
12391
+ function extractFromItem(item) {
12392
+ if (!item || typeof item !== "object") {
12378
12393
  return void 0;
12379
12394
  }
12380
- if (typeof source2 !== "string") {
12381
- throw new Error("expected string value");
12395
+ const record = item;
12396
+ const itemType = typeof record.type === "string" ? record.type : void 0;
12397
+ if (itemType === "agent_message" || itemType === "response" || itemType === "output") {
12398
+ const text = flattenContent(record.text ?? record.content ?? record.output);
12399
+ if (text) {
12400
+ return text;
12401
+ }
12382
12402
  }
12383
- const trimmed = source2.trim();
12384
- return trimmed.length > 0 ? trimmed : void 0;
12403
+ return void 0;
12385
12404
  }
12386
- function resolveOptionalNumber(source2, description) {
12387
- if (source2 === void 0 || source2 === null || source2 === "") {
12388
- return void 0;
12405
+ function flattenContent(value) {
12406
+ if (typeof value === "string") {
12407
+ return value;
12389
12408
  }
12390
- if (typeof source2 === "number") {
12391
- return Number.isFinite(source2) ? source2 : void 0;
12409
+ if (Array.isArray(value)) {
12410
+ const parts = value.map((segment) => {
12411
+ if (typeof segment === "string") {
12412
+ return segment;
12413
+ }
12414
+ if (segment && typeof segment === "object" && "text" in segment) {
12415
+ const text = segment.text;
12416
+ return typeof text === "string" ? text : void 0;
12417
+ }
12418
+ return void 0;
12419
+ }).filter((part) => typeof part === "string" && part.length > 0);
12420
+ return parts.length > 0 ? parts.join(" \n") : void 0;
12392
12421
  }
12393
- if (typeof source2 === "string") {
12394
- const numeric = Number(source2);
12395
- if (Number.isFinite(numeric)) {
12396
- return numeric;
12397
- }
12422
+ if (value && typeof value === "object" && "text" in value) {
12423
+ const text = value.text;
12424
+ return typeof text === "string" ? text : void 0;
12398
12425
  }
12399
- throw new Error(`${description} must be a number`);
12426
+ return void 0;
12400
12427
  }
12401
- function resolveOptionalBoolean(source2) {
12402
- if (source2 === void 0 || source2 === null || source2 === "") {
12428
+ function parseJsonLines(output) {
12429
+ const lines = output.split(/\r?\n/).map((line2) => line2.trim()).filter((line2) => line2.length > 0);
12430
+ if (lines.length <= 1) {
12403
12431
  return void 0;
12404
12432
  }
12405
- if (typeof source2 === "boolean") {
12406
- return source2;
12407
- }
12408
- if (typeof source2 === "string") {
12409
- const lowered = source2.trim().toLowerCase();
12410
- if (lowered === "true" || lowered === "1") {
12411
- return true;
12412
- }
12413
- if (lowered === "false" || lowered === "0") {
12414
- return false;
12433
+ const parsed = [];
12434
+ for (const line2 of lines) {
12435
+ try {
12436
+ parsed.push(JSON.parse(line2));
12437
+ } catch {
12438
+ return void 0;
12415
12439
  }
12416
12440
  }
12417
- throw new Error("expected boolean value");
12418
- }
12419
- function isLikelyEnvReference(value) {
12420
- return /^[A-Z0-9_]+$/.test(value);
12441
+ return parsed;
12421
12442
  }
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`);
12443
+ function pickDetail(stderr, stdout) {
12444
+ const errorText = stderr.trim();
12445
+ if (errorText.length > 0) {
12446
+ return errorText;
12428
12447
  }
12429
- if (source2.length === 0) {
12430
- return void 0;
12448
+ const stdoutText = stdout.trim();
12449
+ return stdoutText.length > 0 ? stdoutText : void 0;
12450
+ }
12451
+ function formatTimeoutSuffix2(timeoutMs) {
12452
+ if (!timeoutMs || timeoutMs <= 0) {
12453
+ return "";
12431
12454
  }
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`);
12455
+ const seconds = Math.ceil(timeoutMs / 1e3);
12456
+ return ` after ${seconds}s`;
12457
+ }
12458
+ async function defaultCodexRunner(options) {
12459
+ return await new Promise((resolve, reject) => {
12460
+ const child = spawn2(options.executable, options.args, {
12461
+ cwd: options.cwd,
12462
+ env: options.env,
12463
+ stdio: ["pipe", "pipe", "pipe"],
12464
+ shell: shouldShellExecute(options.executable)
12465
+ });
12466
+ let stdout = "";
12467
+ let stderr = "";
12468
+ let timedOut = false;
12469
+ const onAbort = () => {
12470
+ child.kill("SIGTERM");
12471
+ };
12472
+ if (options.signal) {
12473
+ if (options.signal.aborted) {
12474
+ onAbort();
12475
+ } else {
12476
+ options.signal.addEventListener("abort", onAbort, { once: true });
12477
+ }
12437
12478
  }
12438
- const trimmed = item.trim();
12439
- if (trimmed.length === 0) {
12440
- throw new Error(`${description}[${i6}] cannot be empty`);
12479
+ let timeoutHandle;
12480
+ if (options.timeoutMs && options.timeoutMs > 0) {
12481
+ timeoutHandle = setTimeout(() => {
12482
+ timedOut = true;
12483
+ child.kill("SIGTERM");
12484
+ }, options.timeoutMs);
12485
+ timeoutHandle.unref?.();
12441
12486
  }
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`);
12487
+ child.stdout.setEncoding("utf8");
12488
+ child.stdout.on("data", (chunk) => {
12489
+ stdout += chunk;
12490
+ });
12491
+ child.stderr.setEncoding("utf8");
12492
+ child.stderr.on("data", (chunk) => {
12493
+ stderr += chunk;
12494
+ });
12495
+ child.stdin.end(options.prompt);
12496
+ const cleanup = () => {
12497
+ if (timeoutHandle) {
12498
+ clearTimeout(timeoutHandle);
12446
12499
  }
12447
- resolved.push(envValue);
12448
- } else {
12449
- resolved.push(trimmed);
12450
- }
12500
+ if (options.signal) {
12501
+ options.signal.removeEventListener("abort", onAbort);
12502
+ }
12503
+ };
12504
+ child.on("error", (error) => {
12505
+ cleanup();
12506
+ reject(error);
12507
+ });
12508
+ child.on("close", (code) => {
12509
+ cleanup();
12510
+ resolve({
12511
+ stdout,
12512
+ stderr,
12513
+ exitCode: typeof code === "number" ? code : -1,
12514
+ timedOut
12515
+ });
12516
+ });
12517
+ });
12518
+ }
12519
+ function shouldShellExecute(executable) {
12520
+ if (process.platform !== "win32") {
12521
+ return false;
12451
12522
  }
12452
- return resolved.length > 0 ? resolved : void 0;
12523
+ const lower = executable.toLowerCase();
12524
+ return lower.endsWith(".cmd") || lower.endsWith(".bat") || lower.endsWith(".ps1");
12453
12525
  }
12454
- var VSCodeProvider = class {
12526
+ var DEFAULT_MOCK_RESPONSE = '{"answer":"Mock provider response. Configure targets.yaml to supply a custom value."}';
12527
+ var MockProvider = class {
12455
12528
  id;
12456
- kind;
12529
+ kind = "mock";
12457
12530
  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
- };
12531
+ cannedResponse;
12532
+ delayMs;
12533
+ delayMinMs;
12534
+ delayMaxMs;
12535
+ constructor(targetName, config) {
12536
+ this.id = `mock:${targetName}`;
12537
+ this.targetName = targetName;
12538
+ this.cannedResponse = config.response ?? DEFAULT_MOCK_RESPONSE;
12539
+ this.delayMs = config.delayMs ?? 0;
12540
+ this.delayMinMs = config.delayMinMs ?? 0;
12541
+ this.delayMaxMs = config.delayMaxMs ?? 0;
12542
+ }
12543
+ async invoke(request) {
12544
+ const delay = this.calculateDelay();
12545
+ if (delay > 0) {
12546
+ await new Promise((resolve) => setTimeout(resolve, delay));
12494
12547
  }
12495
- const responseText = await readFile22(session.responseFile, "utf8");
12496
12548
  return {
12497
- text: responseText,
12549
+ text: this.cannedResponse,
12498
12550
  raw: {
12499
- session,
12500
- inputFiles
12551
+ prompt: request.prompt,
12552
+ guidelines: request.guidelines
12501
12553
  }
12502
12554
  };
12503
12555
  }
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);
12531
- }
12532
- if (this.config.dryRun) {
12533
- return normalizedRequests.map(({ inputFiles }) => ({
12534
- text: "",
12535
- raw: {
12536
- session,
12537
- inputFiles,
12538
- allInputFiles: combinedInputFiles
12539
- }
12540
- }));
12541
- }
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
- );
12546
- }
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
- });
12556
+ calculateDelay() {
12557
+ if (this.delayMinMs > 0 || this.delayMaxMs > 0) {
12558
+ const min = Math.max(0, this.delayMinMs);
12559
+ const max = Math.max(min, this.delayMaxMs);
12560
+ return Math.floor(Math.random() * (max - min + 1)) + min;
12559
12561
  }
12560
- return responses;
12562
+ return this.delayMs;
12561
12563
  }
12562
12564
  };
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);
12573
- }
12574
- parts.push("\n[[ ## user_query ## ]]\n", request.prompt.trim());
12575
- return parts.join("\n").trim();
12576
- }
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")}.`);
12565
+ var CLI_PLACEHOLDERS = /* @__PURE__ */ new Set(["PROMPT", "GUIDELINES", "EVAL_ID", "ATTEMPT", "FILES"]);
12566
+ var BASE_TARGET_SCHEMA = external_exports.object({
12567
+ name: external_exports.string().min(1, "target name is required"),
12568
+ provider: external_exports.string().min(1, "provider is required"),
12569
+ settings: external_exports.record(external_exports.unknown()).optional(),
12570
+ judge_target: external_exports.string().optional(),
12571
+ workers: external_exports.number().int().min(1).optional()
12572
+ });
12573
+ var DEFAULT_AZURE_API_VERSION = "2024-10-01-preview";
12574
+ function normalizeAzureApiVersion(value) {
12575
+ if (!value) {
12576
+ return DEFAULT_AZURE_API_VERSION;
12590
12577
  }
12591
- if (attachmentFiles.length > 0) {
12592
- sections.push(`Read all attachment files:
12593
- ${buildList(attachmentFiles).join("\n")}.`);
12578
+ const trimmed = value.trim();
12579
+ if (trimmed.length === 0) {
12580
+ return DEFAULT_AZURE_API_VERSION;
12594
12581
  }
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");
12582
+ const withoutPrefix = trimmed.replace(/^api[-_]?version\s*=\s*/i, "").trim();
12583
+ return withoutPrefix.length > 0 ? withoutPrefix : DEFAULT_AZURE_API_VERSION;
12600
12584
  }
12601
- function collectGuidelineFiles(attachments, guidelinePatterns) {
12602
- if (!attachments || attachments.length === 0) {
12603
- return [];
12604
- }
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);
12612
- }
12613
- }
12585
+ function resolveTargetDefinition(definition, env = process.env) {
12586
+ const parsed = BASE_TARGET_SCHEMA.parse(definition);
12587
+ const provider = parsed.provider.toLowerCase();
12588
+ const providerBatching = resolveOptionalBoolean(
12589
+ parsed.settings?.provider_batching ?? parsed.settings?.providerBatching
12590
+ );
12591
+ switch (provider) {
12592
+ case "azure":
12593
+ case "azure-openai":
12594
+ return {
12595
+ kind: "azure",
12596
+ name: parsed.name,
12597
+ judgeTarget: parsed.judge_target,
12598
+ workers: parsed.workers,
12599
+ providerBatching,
12600
+ config: resolveAzureConfig(parsed, env)
12601
+ };
12602
+ case "anthropic":
12603
+ return {
12604
+ kind: "anthropic",
12605
+ name: parsed.name,
12606
+ judgeTarget: parsed.judge_target,
12607
+ workers: parsed.workers,
12608
+ providerBatching,
12609
+ config: resolveAnthropicConfig(parsed, env)
12610
+ };
12611
+ case "gemini":
12612
+ case "google":
12613
+ case "google-gemini":
12614
+ return {
12615
+ kind: "gemini",
12616
+ name: parsed.name,
12617
+ judgeTarget: parsed.judge_target,
12618
+ workers: parsed.workers,
12619
+ providerBatching,
12620
+ config: resolveGeminiConfig(parsed, env)
12621
+ };
12622
+ case "codex":
12623
+ case "codex-cli":
12624
+ return {
12625
+ kind: "codex",
12626
+ name: parsed.name,
12627
+ judgeTarget: parsed.judge_target,
12628
+ workers: parsed.workers,
12629
+ providerBatching,
12630
+ config: resolveCodexConfig(parsed, env)
12631
+ };
12632
+ case "mock":
12633
+ return {
12634
+ kind: "mock",
12635
+ name: parsed.name,
12636
+ judgeTarget: parsed.judge_target,
12637
+ workers: parsed.workers,
12638
+ providerBatching,
12639
+ config: resolveMockConfig(parsed)
12640
+ };
12641
+ case "vscode":
12642
+ case "vscode-insiders":
12643
+ return {
12644
+ kind: provider,
12645
+ name: parsed.name,
12646
+ judgeTarget: parsed.judge_target,
12647
+ workers: parsed.workers,
12648
+ providerBatching,
12649
+ config: resolveVSCodeConfig(parsed, env, provider === "vscode-insiders")
12650
+ };
12651
+ case "cli":
12652
+ return {
12653
+ kind: "cli",
12654
+ name: parsed.name,
12655
+ judgeTarget: parsed.judge_target,
12656
+ workers: parsed.workers,
12657
+ providerBatching,
12658
+ config: resolveCliConfig(parsed, env)
12659
+ };
12660
+ default:
12661
+ throw new Error(`Unsupported provider '${parsed.provider}' in target '${parsed.name}'`);
12614
12662
  }
12615
- return Array.from(unique.values());
12616
12663
  }
12617
- function collectAttachmentFiles(attachments) {
12618
- if (!attachments || attachments.length === 0) {
12619
- return [];
12620
- }
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);
12626
- }
12627
- }
12628
- return Array.from(unique.values());
12664
+ function resolveAzureConfig(target, env) {
12665
+ const settings = target.settings ?? {};
12666
+ const endpointSource = settings.endpoint ?? settings.resource ?? settings.resourceName;
12667
+ const apiKeySource = settings.api_key ?? settings.apiKey;
12668
+ const deploymentSource = settings.deployment ?? settings.deploymentName ?? settings.model;
12669
+ const versionSource = settings.version ?? settings.api_version;
12670
+ const temperatureSource = settings.temperature;
12671
+ const maxTokensSource = settings.max_output_tokens ?? settings.maxTokens;
12672
+ const resourceName = resolveString(endpointSource, env, `${target.name} endpoint`);
12673
+ const apiKey = resolveString(apiKeySource, env, `${target.name} api key`);
12674
+ const deploymentName = resolveString(deploymentSource, env, `${target.name} deployment`);
12675
+ const version = normalizeAzureApiVersion(
12676
+ resolveOptionalString(versionSource, env, `${target.name} api version`)
12677
+ );
12678
+ const temperature = resolveOptionalNumber(temperatureSource, `${target.name} temperature`);
12679
+ const maxOutputTokens = resolveOptionalNumber(
12680
+ maxTokensSource,
12681
+ `${target.name} max output tokens`
12682
+ );
12683
+ return {
12684
+ resourceName,
12685
+ deploymentName,
12686
+ apiKey,
12687
+ version,
12688
+ temperature,
12689
+ maxOutputTokens
12690
+ };
12691
+ }
12692
+ function resolveAnthropicConfig(target, env) {
12693
+ const settings = target.settings ?? {};
12694
+ const apiKeySource = settings.api_key ?? settings.apiKey;
12695
+ const modelSource = settings.model ?? settings.deployment ?? settings.variant;
12696
+ const temperatureSource = settings.temperature;
12697
+ const maxTokensSource = settings.max_output_tokens ?? settings.maxTokens;
12698
+ const thinkingBudgetSource = settings.thinking_budget ?? settings.thinkingBudget;
12699
+ const apiKey = resolveString(apiKeySource, env, `${target.name} Anthropic api key`);
12700
+ const model = resolveString(modelSource, env, `${target.name} Anthropic model`);
12701
+ return {
12702
+ apiKey,
12703
+ model,
12704
+ temperature: resolveOptionalNumber(temperatureSource, `${target.name} temperature`),
12705
+ maxOutputTokens: resolveOptionalNumber(maxTokensSource, `${target.name} max output tokens`),
12706
+ thinkingBudget: resolveOptionalNumber(thinkingBudgetSource, `${target.name} thinking budget`)
12707
+ };
12629
12708
  }
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}`;
12635
- }
12636
- return `file://${normalizedPath}`;
12709
+ function resolveGeminiConfig(target, env) {
12710
+ const settings = target.settings ?? {};
12711
+ const apiKeySource = settings.api_key ?? settings.apiKey;
12712
+ const modelSource = settings.model ?? settings.deployment ?? settings.variant;
12713
+ const temperatureSource = settings.temperature;
12714
+ const maxTokensSource = settings.max_output_tokens ?? settings.maxTokens;
12715
+ const apiKey = resolveString(apiKeySource, env, `${target.name} Google API key`);
12716
+ const model = resolveOptionalString(modelSource, env, `${target.name} Gemini model`, {
12717
+ allowLiteral: true,
12718
+ optionalEnv: true
12719
+ }) ?? "gemini-2.5-flash";
12720
+ return {
12721
+ apiKey,
12722
+ model,
12723
+ temperature: resolveOptionalNumber(temperatureSource, `${target.name} temperature`),
12724
+ maxOutputTokens: resolveOptionalNumber(maxTokensSource, `${target.name} max output tokens`)
12725
+ };
12637
12726
  }
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));
12645
- }
12646
- return Array.from(deduped);
12727
+ function resolveCodexConfig(target, env) {
12728
+ const settings = target.settings ?? {};
12729
+ const executableSource = settings.executable ?? settings.command ?? settings.binary;
12730
+ const argsSource = settings.args ?? settings.arguments;
12731
+ const cwdSource = settings.cwd;
12732
+ const timeoutSource = settings.timeout_seconds ?? settings.timeoutSeconds;
12733
+ const executable = resolveOptionalString(executableSource, env, `${target.name} codex executable`, {
12734
+ allowLiteral: true,
12735
+ optionalEnv: true
12736
+ }) ?? "codex";
12737
+ const args = resolveOptionalStringArray(argsSource, env, `${target.name} codex args`);
12738
+ const cwd = resolveOptionalString(cwdSource, env, `${target.name} codex cwd`, {
12739
+ allowLiteral: true,
12740
+ optionalEnv: true
12741
+ });
12742
+ const timeoutMs = resolveTimeoutMs(timeoutSource, `${target.name} codex timeout`);
12743
+ return {
12744
+ executable,
12745
+ args,
12746
+ cwd,
12747
+ timeoutMs
12748
+ };
12647
12749
  }
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));
12654
- }
12655
- }
12656
- return deduped.size > 0 ? Array.from(deduped) : void 0;
12750
+ function resolveMockConfig(target) {
12751
+ const settings = target.settings ?? {};
12752
+ const response = typeof settings.response === "string" ? settings.response : void 0;
12753
+ return { response };
12657
12754
  }
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`);
12665
- }
12666
- const result = await provisionSubagents({
12667
- targetRoot: subagentRoot,
12668
- subagents: count,
12669
- dryRun: false
12670
- });
12671
- if (verbose) {
12672
- if (result.created.length > 0) {
12673
- console.log(`Created ${result.created.length} new subagent(s)`);
12674
- }
12675
- if (result.skippedExisting.length > 0) {
12676
- console.log(`Reusing ${result.skippedExisting.length} existing unlocked subagent(s)`);
12677
- }
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
- };
12685
- } catch (error) {
12686
- const errorMessage = error instanceof Error ? error.message : String(error);
12687
- if (verbose) {
12688
- console.warn(`Provisioning failed (continuing anyway): ${errorMessage}`);
12689
- }
12690
- return {
12691
- provisioned: false,
12692
- message: `Provisioning failed: ${errorMessage}`
12693
- };
12694
- }
12755
+ function resolveVSCodeConfig(target, env, insiders) {
12756
+ const settings = target.settings ?? {};
12757
+ const workspaceTemplateEnvVar = resolveOptionalLiteralString(settings.workspace_template ?? settings.workspaceTemplate);
12758
+ const workspaceTemplate = workspaceTemplateEnvVar ? resolveOptionalString(workspaceTemplateEnvVar, env, `${target.name} workspace template path`, {
12759
+ allowLiteral: false,
12760
+ optionalEnv: true
12761
+ }) : void 0;
12762
+ const commandSource = settings.vscode_cmd ?? settings.command;
12763
+ const waitSource = settings.wait;
12764
+ const dryRunSource = settings.dry_run ?? settings.dryRun;
12765
+ const subagentRootSource = settings.subagent_root ?? settings.subagentRoot;
12766
+ const defaultCommand = insiders ? "code-insiders" : "code";
12767
+ const command = resolveOptionalLiteralString(commandSource) ?? defaultCommand;
12768
+ return {
12769
+ command,
12770
+ waitForResponse: resolveOptionalBoolean(waitSource) ?? true,
12771
+ dryRun: resolveOptionalBoolean(dryRunSource) ?? false,
12772
+ subagentRoot: resolveOptionalString(subagentRootSource, env, `${target.name} subagent root`, {
12773
+ allowLiteral: true,
12774
+ optionalEnv: true
12775
+ }),
12776
+ workspaceTemplate
12777
+ };
12695
12778
  }
12696
- function buildPromptDocument2(request, inputFiles, options) {
12697
- const parts = [];
12698
- const guidelineFiles = collectGuidelineFiles2(
12699
- inputFiles,
12700
- options?.guidelinePatterns ?? request.guideline_patterns,
12701
- options?.guidelineOverrides
12779
+ function resolveCliConfig(target, env) {
12780
+ const settings = target.settings ?? {};
12781
+ const commandTemplateSource = settings.command_template ?? settings.commandTemplate;
12782
+ const filesFormat = resolveOptionalLiteralString(
12783
+ settings.files_format ?? settings.filesFormat ?? settings.attachments_format ?? settings.attachmentsFormat
12702
12784
  );
12703
- const inputFilesList = collectInputFiles(inputFiles);
12704
- const nonGuidelineInputFiles = inputFilesList.filter(
12705
- (file) => !guidelineFiles.includes(file)
12785
+ const cwd = resolveOptionalString(settings.cwd, env, `${target.name} working directory`, {
12786
+ allowLiteral: true,
12787
+ optionalEnv: true
12788
+ });
12789
+ const envOverrides = resolveEnvOverrides(settings.env, env, target.name);
12790
+ const timeoutMs = resolveTimeoutMs(settings.timeout_seconds ?? settings.timeoutSeconds, `${target.name} timeout`);
12791
+ const healthcheck = resolveCliHealthcheck(settings.healthcheck, env, target.name);
12792
+ const commandTemplate = resolveString(
12793
+ commandTemplateSource,
12794
+ env,
12795
+ `${target.name} CLI command template`,
12796
+ true
12706
12797
  );
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();
12798
+ assertSupportedCliPlaceholders(commandTemplate, `${target.name} CLI command template`);
12799
+ return {
12800
+ commandTemplate,
12801
+ filesFormat,
12802
+ cwd,
12803
+ env: envOverrides,
12804
+ timeoutMs,
12805
+ healthcheck
12806
+ };
12713
12807
  }
12714
- function normalizeInputFiles2(inputFiles) {
12715
- if (!inputFiles || inputFiles.length === 0) {
12808
+ function resolveEnvOverrides(source2, env, targetName) {
12809
+ if (source2 === void 0 || source2 === null) {
12716
12810
  return void 0;
12717
12811
  }
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());
12726
- }
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());
12748
- }
12749
- function collectInputFiles(inputFiles) {
12750
- if (!inputFiles || inputFiles.length === 0) {
12751
- return [];
12812
+ if (typeof source2 !== "object" || Array.isArray(source2)) {
12813
+ throw new Error(`${targetName} env overrides must be an object map of strings`);
12752
12814
  }
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);
12815
+ const entries = Object.entries(source2);
12816
+ const resolved = {};
12817
+ for (const [key2, value] of entries) {
12818
+ if (typeof value !== "string") {
12819
+ throw new Error(`${targetName} env override '${key2}' must be a string`);
12758
12820
  }
12821
+ const resolvedValue = resolveString(value, env, `${targetName} env override '${key2}'`);
12822
+ resolved[key2] = resolvedValue;
12759
12823
  }
12760
- return Array.from(unique.values());
12824
+ return Object.keys(resolved).length > 0 ? resolved : void 0;
12761
12825
  }
12762
- function buildMandatoryPrereadBlock2(guidelineFiles, inputFiles) {
12763
- if (guidelineFiles.length === 0 && inputFiles.length === 0) {
12764
- return "";
12765
- }
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")}.`);
12775
- }
12776
- if (inputFiles.length > 0) {
12777
- sections.push(`Read all input files:
12778
- ${buildList(inputFiles).join("\n")}.`);
12826
+ function resolveTimeoutMs(source2, description) {
12827
+ const seconds = resolveOptionalNumber(source2, `${description} (seconds)`);
12828
+ if (seconds === void 0) {
12829
+ return void 0;
12779
12830
  }
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");
12785
- }
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}`;
12831
+ if (seconds <= 0) {
12832
+ throw new Error(`${description} must be greater than zero seconds`);
12791
12833
  }
12792
- return `file://${normalizedPath}`;
12834
+ return Math.floor(seconds * 1e3);
12793
12835
  }
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;
12836
+ function resolveCliHealthcheck(source2, env, targetName) {
12837
+ if (source2 === void 0 || source2 === null) {
12838
+ return void 0;
12813
12839
  }
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))
12840
+ if (typeof source2 !== "object" || Array.isArray(source2)) {
12841
+ throw new Error(`${targetName} healthcheck must be an object`);
12842
+ }
12843
+ const candidate = source2;
12844
+ const type = candidate.type;
12845
+ const timeoutMs = resolveTimeoutMs(
12846
+ candidate.timeout_seconds ?? candidate.timeoutSeconds,
12847
+ `${targetName} healthcheck timeout`
12848
+ );
12849
+ if (type === "http") {
12850
+ const url = resolveString(candidate.url, env, `${targetName} healthcheck URL`);
12851
+ return {
12852
+ type: "http",
12853
+ url,
12854
+ timeoutMs
12855
+ };
12856
+ }
12857
+ if (type === "command") {
12858
+ const commandTemplate = resolveString(
12859
+ candidate.command_template ?? candidate.commandTemplate,
12860
+ env,
12861
+ `${targetName} healthcheck command template`,
12862
+ true
12822
12863
  );
12823
- const workspaceRoot = await this.createWorkspace();
12824
- try {
12825
- const { mirroredInputFiles, guidelineMirrors } = await this.mirrorInputFiles(
12826
- inputFiles,
12827
- workspaceRoot,
12828
- originalGuidelines
12864
+ assertSupportedCliPlaceholders(commandTemplate, `${targetName} healthcheck command template`);
12865
+ const cwd = resolveOptionalString(candidate.cwd, env, `${targetName} healthcheck cwd`, {
12866
+ allowLiteral: true,
12867
+ optionalEnv: true
12868
+ });
12869
+ return {
12870
+ type: "command",
12871
+ commandTemplate,
12872
+ timeoutMs,
12873
+ cwd
12874
+ };
12875
+ }
12876
+ throw new Error(`${targetName} healthcheck type must be 'http' or 'command'`);
12877
+ }
12878
+ function assertSupportedCliPlaceholders(template, description) {
12879
+ const placeholders = extractCliPlaceholders(template);
12880
+ for (const placeholder of placeholders) {
12881
+ if (!CLI_PLACEHOLDERS.has(placeholder)) {
12882
+ throw new Error(
12883
+ `${description} includes unsupported placeholder '{${placeholder}}'. Supported placeholders: ${Array.from(CLI_PLACEHOLDERS).join(", ")}`
12829
12884
  );
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
12885
  }
12868
12886
  }
12869
- async ensureEnvironmentReady() {
12870
- if (!this.environmentCheck) {
12871
- this.environmentCheck = this.validateEnvironment();
12887
+ }
12888
+ function extractCliPlaceholders(template) {
12889
+ const matches = template.matchAll(/\{([A-Z_]+)\}/g);
12890
+ const results = [];
12891
+ for (const match of matches) {
12892
+ if (match[1]) {
12893
+ results.push(match[1]);
12872
12894
  }
12873
- await this.environmentCheck;
12874
12895
  }
12875
- async validateEnvironment() {
12876
- this.resolvedExecutable = await locateExecutable(this.config.executable);
12896
+ return results;
12897
+ }
12898
+ function resolveString(source2, env, description, allowLiteral = false) {
12899
+ const value = resolveOptionalString(source2, env, description, {
12900
+ allowLiteral,
12901
+ optionalEnv: false
12902
+ });
12903
+ if (value === void 0) {
12904
+ throw new Error(`${description} is required`);
12877
12905
  }
12878
- resolveCwd(workspaceRoot) {
12879
- if (!this.config.cwd) {
12880
- return workspaceRoot;
12881
- }
12882
- return path52.resolve(this.config.cwd);
12906
+ return value;
12907
+ }
12908
+ function resolveOptionalString(source2, env, description, options) {
12909
+ if (source2 === void 0 || source2 === null) {
12910
+ return void 0;
12883
12911
  }
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;
12912
+ if (typeof source2 !== "string") {
12913
+ throw new Error(`${description} must be a string`);
12891
12914
  }
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
- }
12915
+ const trimmed = source2.trim();
12916
+ if (trimmed.length === 0) {
12917
+ return void 0;
12912
12918
  }
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
- }
12919
+ const envValue = env[trimmed];
12920
+ if (envValue !== void 0) {
12921
+ if (envValue.trim().length === 0) {
12922
+ throw new Error(`Environment variable '${trimmed}' for ${description} is empty`);
12938
12923
  }
12939
- return {
12940
- mirroredInputFiles: mirrored,
12941
- guidelineMirrors
12942
- };
12943
- }
12944
- async createWorkspace() {
12945
- return await mkdtemp(path52.join(tmpdir(), WORKSPACE_PREFIX));
12924
+ return envValue;
12946
12925
  }
12947
- async cleanupWorkspace(workspaceRoot) {
12948
- try {
12949
- await rm2(workspaceRoot, { recursive: true, force: true });
12950
- } catch {
12926
+ const allowLiteral = options?.allowLiteral ?? false;
12927
+ const optionalEnv = options?.optionalEnv ?? false;
12928
+ const looksLikeEnv = isLikelyEnvReference(trimmed);
12929
+ if (looksLikeEnv) {
12930
+ if (optionalEnv) {
12931
+ return void 0;
12932
+ }
12933
+ if (!allowLiteral) {
12934
+ throw new Error(`Environment variable '${trimmed}' required for ${description} is not set`);
12951
12935
  }
12952
12936
  }
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;
12937
+ return trimmed;
12938
+ }
12939
+ function resolveOptionalLiteralString(source2) {
12940
+ if (source2 === void 0 || source2 === null) {
12941
+ return void 0;
12961
12942
  }
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;
12971
- }
12972
- } catch {
12943
+ if (typeof source2 !== "string") {
12944
+ throw new Error("expected string value");
12973
12945
  }
12974
- throw new Error(`Codex executable '${candidate}' was not found on PATH`);
12946
+ const trimmed = source2.trim();
12947
+ return trimmed.length > 0 ? trimmed : void 0;
12975
12948
  }
12976
- function selectExecutableCandidate(candidates) {
12977
- if (candidates.length === 0) {
12949
+ function resolveOptionalNumber(source2, description) {
12950
+ if (source2 === void 0 || source2 === null || source2 === "") {
12978
12951
  return void 0;
12979
12952
  }
12980
- if (process.platform !== "win32") {
12981
- return candidates[0];
12953
+ if (typeof source2 === "number") {
12954
+ return Number.isFinite(source2) ? source2 : void 0;
12982
12955
  }
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;
12956
+ if (typeof source2 === "string") {
12957
+ const numeric = Number(source2);
12958
+ if (Number.isFinite(numeric)) {
12959
+ return numeric;
12988
12960
  }
12989
12961
  }
12990
- return candidates[0];
12962
+ throw new Error(`${description} must be a number`);
12991
12963
  }
12992
- async function ensureWindowsExecutableVariant(candidate) {
12993
- if (process.platform !== "win32") {
12994
- return candidate;
12964
+ function resolveOptionalBoolean(source2) {
12965
+ if (source2 === void 0 || source2 === null || source2 === "") {
12966
+ return void 0;
12995
12967
  }
12996
- if (hasExecutableExtension(candidate)) {
12997
- return candidate;
12968
+ if (typeof source2 === "boolean") {
12969
+ return source2;
12998
12970
  }
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 {
12971
+ if (typeof source2 === "string") {
12972
+ const lowered = source2.trim().toLowerCase();
12973
+ if (lowered === "true" || lowered === "1") {
12974
+ return true;
12975
+ }
12976
+ if (lowered === "false" || lowered === "0") {
12977
+ return false;
13006
12978
  }
13007
12979
  }
13008
- return candidate;
12980
+ throw new Error("expected boolean value");
13009
12981
  }
13010
- function hasExecutableExtension(candidate) {
13011
- const lower = candidate.toLowerCase();
13012
- return getWindowsExecutableExtensions().some((ext) => lower.endsWith(ext));
12982
+ function isLikelyEnvReference(value) {
12983
+ return /^[A-Z0-9_]+$/.test(value);
13013
12984
  }
13014
- var DEFAULT_WINDOWS_EXTENSIONS = [".com", ".exe", ".bat", ".cmd", ".ps1"];
13015
- function getWindowsExecutableExtensions() {
13016
- if (process.platform !== "win32") {
13017
- return [];
12985
+ function resolveOptionalStringArray(source2, env, description) {
12986
+ if (source2 === void 0 || source2 === null) {
12987
+ return void 0;
13018
12988
  }
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");
12989
+ if (!Array.isArray(source2)) {
12990
+ throw new Error(`${description} must be an array of strings`);
13026
12991
  }
13027
- try {
13028
- return JSON.parse(trimmed);
13029
- } catch {
13030
- const lineObjects = parseJsonLines(trimmed);
13031
- if (lineObjects) {
13032
- return lineObjects;
12992
+ if (source2.length === 0) {
12993
+ return void 0;
12994
+ }
12995
+ const resolved = [];
12996
+ for (let i6 = 0; i6 < source2.length; i6++) {
12997
+ const item = source2[i6];
12998
+ if (typeof item !== "string") {
12999
+ throw new Error(`${description}[${i6}] must be a string`);
13033
13000
  }
13034
- const lastBrace = trimmed.lastIndexOf("{");
13035
- if (lastBrace >= 0) {
13036
- const candidate = trimmed.slice(lastBrace);
13037
- try {
13038
- return JSON.parse(candidate);
13039
- } catch {
13001
+ const trimmed = item.trim();
13002
+ if (trimmed.length === 0) {
13003
+ throw new Error(`${description}[${i6}] cannot be empty`);
13004
+ }
13005
+ const envValue = env[trimmed];
13006
+ if (envValue !== void 0) {
13007
+ if (envValue.trim().length === 0) {
13008
+ throw new Error(`Environment variable '${trimmed}' for ${description}[${i6}] is empty`);
13040
13009
  }
13010
+ resolved.push(envValue);
13011
+ } else {
13012
+ resolved.push(trimmed);
13041
13013
  }
13042
- const preview = trimmed.slice(0, 200);
13043
- throw new Error(`Codex CLI emitted invalid JSON: ${preview}${trimmed.length > 200 ? "\u2026" : ""}`);
13044
13014
  }
13015
+ return resolved.length > 0 ? resolved : void 0;
13045
13016
  }
13046
- function extractAssistantText(parsed) {
13047
- if (Array.isArray(parsed)) {
13048
- const text = extractFromEventStream(parsed);
13049
- if (text) {
13050
- return text;
13051
- }
13052
- }
13053
- if (!parsed || typeof parsed !== "object") {
13054
- throw new Error("Codex CLI JSON response did not include an assistant message");
13055
- }
13056
- const record = parsed;
13057
- const eventText = extractFromEvent(record);
13058
- if (eventText) {
13059
- return eventText;
13017
+ var VSCodeProvider = class {
13018
+ id;
13019
+ kind;
13020
+ targetName;
13021
+ supportsBatch = true;
13022
+ config;
13023
+ constructor(targetName, config, kind) {
13024
+ this.id = `${kind}:${targetName}`;
13025
+ this.kind = kind;
13026
+ this.targetName = targetName;
13027
+ this.config = config;
13060
13028
  }
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
- }
13029
+ async invoke(request) {
13030
+ if (request.signal?.aborted) {
13031
+ throw new Error("VS Code provider request was aborted before dispatch");
13077
13032
  }
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;
13033
+ const inputFiles = normalizeAttachments(request.inputFiles);
13034
+ const promptContent = buildPromptDocument2(request, inputFiles, request.guideline_patterns);
13035
+ const session = await dispatchAgentSession({
13036
+ userQuery: promptContent,
13037
+ extraAttachments: inputFiles,
13038
+ wait: this.config.waitForResponse,
13039
+ dryRun: this.config.dryRun,
13040
+ vscodeCmd: this.config.command,
13041
+ subagentRoot: this.config.subagentRoot,
13042
+ workspaceTemplate: this.config.workspaceTemplate,
13043
+ silent: true
13044
+ });
13045
+ if (session.exitCode !== 0 || !session.responseFile) {
13046
+ const failure = session.error ?? "VS Code subagent did not produce a response";
13047
+ throw new Error(failure);
13085
13048
  }
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;
13049
+ if (this.config.dryRun) {
13050
+ return {
13051
+ text: "",
13052
+ raw: {
13053
+ session,
13054
+ inputFiles
13055
+ }
13056
+ };
13100
13057
  }
13058
+ const responseText = await readFile22(session.responseFile, "utf8");
13059
+ return {
13060
+ text: responseText,
13061
+ raw: {
13062
+ session,
13063
+ inputFiles
13064
+ }
13065
+ };
13101
13066
  }
13102
- return void 0;
13103
- }
13104
- function extractFromEvent(event) {
13105
- if (!event || typeof event !== "object") {
13106
- return void 0;
13107
- }
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;
13067
+ async invokeBatch(requests) {
13068
+ if (requests.length === 0) {
13069
+ return [];
13070
+ }
13071
+ const normalizedRequests = requests.map((req) => ({
13072
+ request: req,
13073
+ inputFiles: normalizeAttachments(req.inputFiles)
13074
+ }));
13075
+ const combinedInputFiles = mergeAttachments(
13076
+ normalizedRequests.map(({ inputFiles }) => inputFiles)
13077
+ );
13078
+ const userQueries = normalizedRequests.map(
13079
+ ({ request, inputFiles }) => buildPromptDocument2(request, inputFiles, request.guideline_patterns)
13080
+ );
13081
+ const session = await dispatchBatchAgent({
13082
+ userQueries,
13083
+ extraAttachments: combinedInputFiles,
13084
+ wait: this.config.waitForResponse,
13085
+ dryRun: this.config.dryRun,
13086
+ vscodeCmd: this.config.command,
13087
+ subagentRoot: this.config.subagentRoot,
13088
+ workspaceTemplate: this.config.workspaceTemplate,
13089
+ silent: true
13090
+ });
13091
+ if (session.exitCode !== 0 || !session.responseFiles) {
13092
+ const failure = session.error ?? "VS Code subagent did not produce batch responses";
13093
+ throw new Error(failure);
13094
+ }
13095
+ if (this.config.dryRun) {
13096
+ return normalizedRequests.map(({ inputFiles }) => ({
13097
+ text: "",
13098
+ raw: {
13099
+ session,
13100
+ inputFiles,
13101
+ allInputFiles: combinedInputFiles
13102
+ }
13103
+ }));
13104
+ }
13105
+ if (session.responseFiles.length !== requests.length) {
13106
+ throw new Error(
13107
+ `VS Code batch returned ${session.responseFiles.length} responses for ${requests.length} requests`
13108
+ );
13109
+ }
13110
+ const responses = [];
13111
+ for (const [index, responseFile] of session.responseFiles.entries()) {
13112
+ const responseText = await readFile22(responseFile, "utf8");
13113
+ responses.push({
13114
+ text: responseText,
13115
+ raw: {
13116
+ session,
13117
+ inputFiles: normalizedRequests[index]?.inputFiles,
13118
+ allInputFiles: combinedInputFiles,
13119
+ responseFile
13120
+ }
13121
+ });
13115
13122
  }
13123
+ return responses;
13116
13124
  }
13117
- const output = record.output ?? record.content;
13118
- const flattened = flattenContent(output);
13119
- if (flattened) {
13120
- return flattened;
13125
+ };
13126
+ function buildPromptDocument2(request, attachments, guidelinePatterns) {
13127
+ const parts = [];
13128
+ const guidelineFiles = collectGuidelineFiles2(attachments, guidelinePatterns);
13129
+ const attachmentFiles = collectAttachmentFiles(attachments);
13130
+ const nonGuidelineAttachments = attachmentFiles.filter(
13131
+ (file) => !guidelineFiles.includes(file)
13132
+ );
13133
+ const prereadBlock = buildMandatoryPrereadBlock2(guidelineFiles, nonGuidelineAttachments);
13134
+ if (prereadBlock.length > 0) {
13135
+ parts.push("\n", prereadBlock);
13121
13136
  }
13122
- return void 0;
13137
+ parts.push("\n[[ ## user_query ## ]]\n", request.prompt.trim());
13138
+ return parts.join("\n").trim();
13123
13139
  }
13124
- function extractFromItem(item) {
13125
- if (!item || typeof item !== "object") {
13126
- return void 0;
13140
+ function buildMandatoryPrereadBlock2(guidelineFiles, attachmentFiles) {
13141
+ if (guidelineFiles.length === 0 && attachmentFiles.length === 0) {
13142
+ return "";
13127
13143
  }
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
- }
13144
+ const buildList = (files) => files.map((absolutePath) => {
13145
+ const fileName = path52.basename(absolutePath);
13146
+ const fileUri = pathToFileUri22(absolutePath);
13147
+ return `* [${fileName}](${fileUri})`;
13148
+ });
13149
+ const sections = [];
13150
+ if (guidelineFiles.length > 0) {
13151
+ sections.push(`Read all guideline files:
13152
+ ${buildList(guidelineFiles).join("\n")}.`);
13135
13153
  }
13136
- return void 0;
13154
+ if (attachmentFiles.length > 0) {
13155
+ sections.push(`Read all attachment files:
13156
+ ${buildList(attachmentFiles).join("\n")}.`);
13157
+ }
13158
+ sections.push(
13159
+ "If any file is missing, fail with ERROR: missing-file <filename> and stop.",
13160
+ "Then apply system_instructions on the user query below."
13161
+ );
13162
+ return sections.join("\n");
13137
13163
  }
13138
- function flattenContent(value) {
13139
- if (typeof value === "string") {
13140
- return value;
13164
+ function collectGuidelineFiles2(attachments, guidelinePatterns) {
13165
+ if (!attachments || attachments.length === 0) {
13166
+ return [];
13141
13167
  }
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;
13168
+ const unique = /* @__PURE__ */ new Map();
13169
+ for (const attachment of attachments) {
13170
+ const absolutePath = path52.resolve(attachment);
13171
+ const normalized = absolutePath.split(path52.sep).join("/");
13172
+ if (isGuidelineFile(normalized, guidelinePatterns)) {
13173
+ if (!unique.has(absolutePath)) {
13174
+ unique.set(absolutePath, absolutePath);
13150
13175
  }
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;
13176
+ }
13158
13177
  }
13159
- return void 0;
13178
+ return Array.from(unique.values());
13160
13179
  }
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;
13180
+ function collectAttachmentFiles(attachments) {
13181
+ if (!attachments || attachments.length === 0) {
13182
+ return [];
13165
13183
  }
13166
- const parsed = [];
13167
- for (const line2 of lines) {
13168
- try {
13169
- parsed.push(JSON.parse(line2));
13170
- } catch {
13171
- return void 0;
13184
+ const unique = /* @__PURE__ */ new Map();
13185
+ for (const attachment of attachments) {
13186
+ const absolutePath = path52.resolve(attachment);
13187
+ if (!unique.has(absolutePath)) {
13188
+ unique.set(absolutePath, absolutePath);
13172
13189
  }
13173
13190
  }
13174
- return parsed;
13191
+ return Array.from(unique.values());
13175
13192
  }
13176
- function pickDetail(stderr, stdout) {
13177
- const errorText = stderr.trim();
13178
- if (errorText.length > 0) {
13179
- return errorText;
13193
+ function pathToFileUri22(filePath) {
13194
+ const absolutePath = path52.isAbsolute(filePath) ? filePath : path52.resolve(filePath);
13195
+ const normalizedPath = absolutePath.replace(/\\/g, "/");
13196
+ if (/^[a-zA-Z]:\//.test(normalizedPath)) {
13197
+ return `file:///${normalizedPath}`;
13180
13198
  }
13181
- const stdoutText = stdout.trim();
13182
- return stdoutText.length > 0 ? stdoutText : void 0;
13199
+ return `file://${normalizedPath}`;
13183
13200
  }
13184
- function formatTimeoutSuffix2(timeoutMs) {
13185
- if (!timeoutMs || timeoutMs <= 0) {
13186
- return "";
13201
+ function normalizeAttachments(attachments) {
13202
+ if (!attachments || attachments.length === 0) {
13203
+ return void 0;
13187
13204
  }
13188
- const seconds = Math.ceil(timeoutMs / 1e3);
13189
- return ` after ${seconds}s`;
13205
+ const deduped = /* @__PURE__ */ new Set();
13206
+ for (const attachment of attachments) {
13207
+ deduped.add(path52.resolve(attachment));
13208
+ }
13209
+ return Array.from(deduped);
13190
13210
  }
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
- }
13211
+ function mergeAttachments(all) {
13212
+ const deduped = /* @__PURE__ */ new Set();
13213
+ for (const list of all) {
13214
+ if (!list) continue;
13215
+ for (const inputFile of list) {
13216
+ deduped.add(path52.resolve(inputFile));
13211
13217
  }
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?.();
13218
+ }
13219
+ return deduped.size > 0 ? Array.from(deduped) : void 0;
13220
+ }
13221
+ async function ensureVSCodeSubagents(options) {
13222
+ const { kind, count, verbose = false } = options;
13223
+ const vscodeCmd = kind === "vscode-insiders" ? "code-insiders" : "code";
13224
+ const subagentRoot = getSubagentRoot(vscodeCmd);
13225
+ try {
13226
+ if (verbose) {
13227
+ console.log(`Provisioning ${count} subagent(s) via: subagent ${vscodeCmd} provision`);
13219
13228
  }
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;
13229
+ const result = await provisionSubagents({
13230
+ targetRoot: subagentRoot,
13231
+ subagents: count,
13232
+ dryRun: false
13227
13233
  });
13228
- child.stdin.end(options.prompt);
13229
- const cleanup = () => {
13230
- if (timeoutHandle) {
13231
- clearTimeout(timeoutHandle);
13234
+ if (verbose) {
13235
+ if (result.created.length > 0) {
13236
+ console.log(`Created ${result.created.length} new subagent(s)`);
13232
13237
  }
13233
- if (options.signal) {
13234
- options.signal.removeEventListener("abort", onAbort);
13238
+ if (result.skippedExisting.length > 0) {
13239
+ console.log(`Reusing ${result.skippedExisting.length} existing unlocked subagent(s)`);
13235
13240
  }
13241
+ console.log(`
13242
+ total unlocked subagents available: ${result.created.length + result.skippedExisting.length}`);
13243
+ }
13244
+ return {
13245
+ provisioned: true,
13246
+ message: `Provisioned ${count} subagent(s): ${result.created.length} created, ${result.skippedExisting.length} reused`
13247
+ };
13248
+ } catch (error) {
13249
+ const errorMessage = error instanceof Error ? error.message : String(error);
13250
+ if (verbose) {
13251
+ console.warn(`Provisioning failed (continuing anyway): ${errorMessage}`);
13252
+ }
13253
+ return {
13254
+ provisioned: false,
13255
+ message: `Provisioning failed: ${errorMessage}`
13236
13256
  };
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
13257
  }
13256
- const lower = executable.toLowerCase();
13257
- return lower.endsWith(".cmd") || lower.endsWith(".bat") || lower.endsWith(".ps1");
13258
13258
  }
13259
13259
  function isRecord(value) {
13260
13260
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -15526,58 +15526,105 @@ function registerEvalCommand(program) {
15526
15526
  // src/commands/init/index.ts
15527
15527
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
15528
15528
  import path15 from "node:path";
15529
+ import * as readline from "node:readline/promises";
15529
15530
 
15530
15531
  // src/templates/index.ts
15531
- import { readFileSync } from "node:fs";
15532
+ import { readFileSync, readdirSync, statSync } from "node:fs";
15532
15533
  import path14 from "node:path";
15533
15534
  import { fileURLToPath as fileURLToPath2 } from "node:url";
15534
15535
  var TemplateManager = class {
15535
- static getTemplates() {
15536
+ static getGithubTemplates() {
15537
+ return this.getTemplatesFromDir("github");
15538
+ }
15539
+ static getAgentvTemplates() {
15540
+ return this.getTemplatesFromDir("agentv");
15541
+ }
15542
+ static getTemplatesFromDir(subdir) {
15536
15543
  const currentDir = path14.dirname(fileURLToPath2(import.meta.url));
15537
15544
  let templatesDir;
15538
15545
  if (currentDir.includes(path14.sep + "dist")) {
15539
- templatesDir = path14.join(currentDir, "templates");
15546
+ templatesDir = path14.join(currentDir, "templates", subdir);
15540
15547
  } else {
15541
- templatesDir = currentDir;
15548
+ templatesDir = path14.join(currentDir, subdir);
15542
15549
  }
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
15550
+ return this.readTemplatesRecursively(templatesDir, "");
15551
+ }
15552
+ static readTemplatesRecursively(dir, relativePath) {
15553
+ const templates = [];
15554
+ const entries = readdirSync(dir);
15555
+ for (const entry of entries) {
15556
+ const fullPath = path14.join(dir, entry);
15557
+ const stat4 = statSync(fullPath);
15558
+ const entryRelativePath = relativePath ? path14.join(relativePath, entry) : entry;
15559
+ if (stat4.isDirectory()) {
15560
+ templates.push(...this.readTemplatesRecursively(fullPath, entryRelativePath));
15561
+ } else {
15562
+ const content = readFileSync(fullPath, "utf-8");
15563
+ templates.push({
15564
+ path: entryRelativePath.split(path14.sep).join("/"),
15565
+ // Normalize to forward slashes
15566
+ content
15567
+ });
15567
15568
  }
15568
- ];
15569
+ }
15570
+ return templates;
15569
15571
  }
15570
15572
  };
15571
15573
 
15572
15574
  // src/commands/init/index.ts
15575
+ async function promptYesNo(message) {
15576
+ const rl = readline.createInterface({
15577
+ input: process.stdin,
15578
+ output: process.stdout
15579
+ });
15580
+ try {
15581
+ const answer = await rl.question(`${message} (y/N): `);
15582
+ return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
15583
+ } finally {
15584
+ rl.close();
15585
+ }
15586
+ }
15573
15587
  async function initCommand(options = {}) {
15574
15588
  const targetPath = path15.resolve(options.targetPath ?? ".");
15575
15589
  const githubDir = path15.join(targetPath, ".github");
15590
+ const agentvDir = path15.join(targetPath, ".agentv");
15591
+ const githubTemplates = TemplateManager.getGithubTemplates();
15592
+ const agentvTemplates = TemplateManager.getAgentvTemplates();
15593
+ const existingFiles = [];
15594
+ if (existsSync(githubDir)) {
15595
+ for (const template of githubTemplates) {
15596
+ const targetFilePath = path15.join(githubDir, template.path);
15597
+ if (existsSync(targetFilePath)) {
15598
+ existingFiles.push(path15.relative(targetPath, targetFilePath));
15599
+ }
15600
+ }
15601
+ }
15602
+ if (existsSync(agentvDir)) {
15603
+ for (const template of agentvTemplates) {
15604
+ const targetFilePath = path15.join(agentvDir, template.path);
15605
+ if (existsSync(targetFilePath)) {
15606
+ existingFiles.push(path15.relative(targetPath, targetFilePath));
15607
+ }
15608
+ }
15609
+ }
15610
+ if (existingFiles.length > 0) {
15611
+ console.log("We detected an existing setup:");
15612
+ existingFiles.forEach((file) => console.log(` - ${file}`));
15613
+ console.log();
15614
+ const shouldReplace = await promptYesNo("Do you want to replace these files?");
15615
+ if (!shouldReplace) {
15616
+ console.log("\nInit cancelled. No files were changed.");
15617
+ return;
15618
+ }
15619
+ console.log();
15620
+ }
15576
15621
  if (!existsSync(githubDir)) {
15577
15622
  mkdirSync(githubDir, { recursive: true });
15578
15623
  }
15579
- const templates = TemplateManager.getTemplates();
15580
- for (const template of templates) {
15624
+ if (!existsSync(agentvDir)) {
15625
+ mkdirSync(agentvDir, { recursive: true });
15626
+ }
15627
+ for (const template of githubTemplates) {
15581
15628
  const targetFilePath = path15.join(githubDir, template.path);
15582
15629
  const targetDirPath = path15.dirname(targetFilePath);
15583
15630
  if (!existsSync(targetDirPath)) {
@@ -15586,11 +15633,26 @@ async function initCommand(options = {}) {
15586
15633
  writeFileSync(targetFilePath, template.content, "utf-8");
15587
15634
  console.log(`Created ${path15.relative(targetPath, targetFilePath)}`);
15588
15635
  }
15636
+ for (const template of agentvTemplates) {
15637
+ const targetFilePath = path15.join(agentvDir, template.path);
15638
+ const targetDirPath = path15.dirname(targetFilePath);
15639
+ if (!existsSync(targetDirPath)) {
15640
+ mkdirSync(targetDirPath, { recursive: true });
15641
+ }
15642
+ writeFileSync(targetFilePath, template.content, "utf-8");
15643
+ console.log(`Created ${path15.relative(targetPath, targetFilePath)}`);
15644
+ }
15589
15645
  console.log("\nAgentV initialized successfully!");
15590
15646
  console.log(`
15591
15647
  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.");
15648
+ githubTemplates.forEach((t) => console.log(` - ${t.path}`));
15649
+ console.log(`
15650
+ Files installed to ${path15.relative(targetPath, agentvDir)}:`);
15651
+ agentvTemplates.forEach((t) => console.log(` - ${t.path}`));
15652
+ console.log("\nYou can now:");
15653
+ console.log(" 1. Edit .agentv/.env with your API credentials");
15654
+ console.log(" 2. Configure targets in .agentv/targets.yaml");
15655
+ console.log(" 3. Create eval files using the schema and prompt templates");
15594
15656
  }
15595
15657
 
15596
15658
  // src/commands/status.ts
@@ -16556,4 +16618,4 @@ export {
16556
16618
  createProgram,
16557
16619
  runCli
16558
16620
  };
16559
- //# sourceMappingURL=chunk-THVRLL37.js.map
16621
+ //# sourceMappingURL=chunk-HPH4YWGU.js.map