@agentv/core 2.5.3 → 2.5.5
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.
- package/dist/index.cjs +1154 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1148 -92
- package/dist/index.js.map +1 -1
- package/package.json +4 -7
package/dist/index.js
CHANGED
|
@@ -987,12 +987,13 @@ function parseRubricItems(rawRubrics, evaluatorName, evalId) {
|
|
|
987
987
|
let scoreRanges;
|
|
988
988
|
const rawScoreRanges = rawRubric.score_ranges;
|
|
989
989
|
if (rawScoreRanges !== void 0) {
|
|
990
|
-
|
|
990
|
+
const normalized = normalizeScoreRangesShorthand(rawScoreRanges);
|
|
991
|
+
if (!Array.isArray(normalized)) {
|
|
991
992
|
throw new Error(
|
|
992
|
-
`Invalid score_ranges for rubric '${id}' in evaluator '${evaluatorName}' in '${evalId}': must be an array`
|
|
993
|
+
`Invalid score_ranges for rubric '${id}' in evaluator '${evaluatorName}' in '${evalId}': must be an array or shorthand map`
|
|
993
994
|
);
|
|
994
995
|
}
|
|
995
|
-
scoreRanges = parseScoreRanges(
|
|
996
|
+
scoreRanges = parseScoreRanges(normalized, id, evaluatorName, evalId);
|
|
996
997
|
items.push({
|
|
997
998
|
id,
|
|
998
999
|
weight,
|
|
@@ -1020,6 +1021,37 @@ function parseRubricItems(rawRubrics, evaluatorName, evalId) {
|
|
|
1020
1021
|
}
|
|
1021
1022
|
return items.length > 0 ? items : void 0;
|
|
1022
1023
|
}
|
|
1024
|
+
function normalizeScoreRangesShorthand(raw) {
|
|
1025
|
+
if (Array.isArray(raw)) return raw;
|
|
1026
|
+
if (!isJsonObject2(raw)) return raw;
|
|
1027
|
+
const keys = Object.keys(raw);
|
|
1028
|
+
if (keys.length === 0) return raw;
|
|
1029
|
+
const numericKeys = [];
|
|
1030
|
+
for (const key of keys) {
|
|
1031
|
+
const num = Number(key);
|
|
1032
|
+
if (!Number.isInteger(num) || num < 0 || num > 10) {
|
|
1033
|
+
return raw;
|
|
1034
|
+
}
|
|
1035
|
+
if (typeof raw[key] !== "string" || raw[key].length === 0) {
|
|
1036
|
+
return raw;
|
|
1037
|
+
}
|
|
1038
|
+
numericKeys.push(num);
|
|
1039
|
+
}
|
|
1040
|
+
numericKeys.sort((a, b) => a - b);
|
|
1041
|
+
if (numericKeys[0] !== 0) {
|
|
1042
|
+
throw new Error(`score_ranges shorthand map must start at 0 (got ${numericKeys[0]})`);
|
|
1043
|
+
}
|
|
1044
|
+
const result = [];
|
|
1045
|
+
for (let i = 0; i < numericKeys.length; i++) {
|
|
1046
|
+
const min = numericKeys[i];
|
|
1047
|
+
const max = i < numericKeys.length - 1 ? numericKeys[i + 1] - 1 : 10;
|
|
1048
|
+
result.push({
|
|
1049
|
+
score_range: [min, max],
|
|
1050
|
+
expected_outcome: raw[String(min)]
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
return result;
|
|
1054
|
+
}
|
|
1023
1055
|
function parseScoreRanges(rawRanges, rubricId, evaluatorName, evalId) {
|
|
1024
1056
|
const ranges = [];
|
|
1025
1057
|
for (const [index, rawRange] of rawRanges.entries()) {
|
|
@@ -1102,7 +1134,8 @@ function parseInlineRubrics(rawRubrics) {
|
|
|
1102
1134
|
}
|
|
1103
1135
|
const expectedOutcome = asString(rubric.expected_outcome) ?? asString(rubric.description) ?? "";
|
|
1104
1136
|
const rawScoreRanges = rubric.score_ranges;
|
|
1105
|
-
const
|
|
1137
|
+
const normalizedScoreRanges = rawScoreRanges !== void 0 ? normalizeScoreRangesShorthand(rawScoreRanges) : void 0;
|
|
1138
|
+
const scoreRanges = Array.isArray(normalizedScoreRanges) && normalizedScoreRanges.length > 0 ? normalizedScoreRanges.filter((r) => isJsonObject2(r)).map((range) => ({
|
|
1106
1139
|
score_range: Array.isArray(range.score_range) ? range.score_range : [0, 10],
|
|
1107
1140
|
expected_outcome: asString(range.expected_outcome) ?? asString(range.description) ?? ""
|
|
1108
1141
|
})).filter((r) => r.expected_outcome.length > 0) : void 0;
|
|
@@ -3181,16 +3214,16 @@ async function runClaudeCodeWithTempFiles(options, stdoutFile, stderrFile, exitF
|
|
|
3181
3214
|
let lastStdoutSize = 0;
|
|
3182
3215
|
const readFileIfExists = async (filePath) => {
|
|
3183
3216
|
try {
|
|
3184
|
-
const { readFile:
|
|
3185
|
-
return await
|
|
3217
|
+
const { readFile: readFile10 } = await import("node:fs/promises");
|
|
3218
|
+
return await readFile10(filePath, "utf8");
|
|
3186
3219
|
} catch {
|
|
3187
3220
|
return "";
|
|
3188
3221
|
}
|
|
3189
3222
|
};
|
|
3190
3223
|
const fileExists4 = async (filePath) => {
|
|
3191
3224
|
try {
|
|
3192
|
-
const { access:
|
|
3193
|
-
await
|
|
3225
|
+
const { access: access5 } = await import("node:fs/promises");
|
|
3226
|
+
await access5(filePath);
|
|
3194
3227
|
return true;
|
|
3195
3228
|
} catch {
|
|
3196
3229
|
return false;
|
|
@@ -5396,14 +5429,1037 @@ async function defaultPiRunner(options) {
|
|
|
5396
5429
|
});
|
|
5397
5430
|
}
|
|
5398
5431
|
|
|
5399
|
-
// src/evaluation/providers/vscode.ts
|
|
5432
|
+
// src/evaluation/providers/vscode-provider.ts
|
|
5433
|
+
import path23 from "node:path";
|
|
5434
|
+
|
|
5435
|
+
// src/evaluation/providers/vscode/dispatch/agentDispatch.ts
|
|
5436
|
+
import { stat as stat3, writeFile as writeFile6 } from "node:fs/promises";
|
|
5437
|
+
import path21 from "node:path";
|
|
5438
|
+
|
|
5439
|
+
// src/evaluation/providers/vscode/utils/fs.ts
|
|
5440
|
+
import { constants as constants3 } from "node:fs";
|
|
5441
|
+
import { access as access3, mkdir as mkdir4, readdir, rm as rm4, stat } from "node:fs/promises";
|
|
5400
5442
|
import path13 from "node:path";
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5443
|
+
async function pathExists(target) {
|
|
5444
|
+
try {
|
|
5445
|
+
await access3(target, constants3.F_OK);
|
|
5446
|
+
return true;
|
|
5447
|
+
} catch {
|
|
5448
|
+
return false;
|
|
5449
|
+
}
|
|
5450
|
+
}
|
|
5451
|
+
async function ensureDir(target) {
|
|
5452
|
+
await mkdir4(target, { recursive: true });
|
|
5453
|
+
}
|
|
5454
|
+
async function readDirEntries(target) {
|
|
5455
|
+
const entries = await readdir(target, { withFileTypes: true });
|
|
5456
|
+
return entries.map((entry) => ({
|
|
5457
|
+
name: entry.name,
|
|
5458
|
+
absolutePath: path13.join(target, entry.name),
|
|
5459
|
+
isDirectory: entry.isDirectory()
|
|
5460
|
+
}));
|
|
5461
|
+
}
|
|
5462
|
+
async function removeIfExists(target) {
|
|
5463
|
+
try {
|
|
5464
|
+
await rm4(target, { force: true, recursive: false });
|
|
5465
|
+
} catch (error) {
|
|
5466
|
+
if (error.code !== "ENOENT") {
|
|
5467
|
+
throw error;
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
|
|
5472
|
+
// src/evaluation/providers/vscode/utils/path.ts
|
|
5473
|
+
import path14 from "node:path";
|
|
5474
|
+
function pathToFileUri2(filePath) {
|
|
5475
|
+
const absolutePath = path14.isAbsolute(filePath) ? filePath : path14.resolve(filePath);
|
|
5476
|
+
const normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
5477
|
+
if (/^[a-zA-Z]:\//.test(normalizedPath)) {
|
|
5478
|
+
return `file:///${normalizedPath}`;
|
|
5479
|
+
}
|
|
5480
|
+
return `file://${normalizedPath}`;
|
|
5481
|
+
}
|
|
5482
|
+
|
|
5483
|
+
// src/evaluation/providers/vscode/dispatch/promptBuilder.ts
|
|
5484
|
+
import path15 from "node:path";
|
|
5485
|
+
|
|
5486
|
+
// src/evaluation/providers/vscode/utils/template.ts
|
|
5487
|
+
function renderTemplate2(content, variables) {
|
|
5488
|
+
if (!content) {
|
|
5489
|
+
return content;
|
|
5490
|
+
}
|
|
5491
|
+
const variableLookup = /* @__PURE__ */ new Map();
|
|
5492
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
5493
|
+
variableLookup.set(key.toLowerCase(), value);
|
|
5494
|
+
}
|
|
5495
|
+
const referencedVariables = /* @__PURE__ */ new Set();
|
|
5496
|
+
const result = content.replace(/\{\{([a-zA-Z_]+)\}\}/gi, (match, variableName) => {
|
|
5497
|
+
const lowerCaseKey = variableName.toLowerCase();
|
|
5498
|
+
referencedVariables.add(lowerCaseKey);
|
|
5499
|
+
if (!variableLookup.has(lowerCaseKey)) {
|
|
5500
|
+
throw new Error(
|
|
5501
|
+
`Template variable '${variableName}' is not provided in the variables object`
|
|
5502
|
+
);
|
|
5503
|
+
}
|
|
5504
|
+
return variableLookup.get(lowerCaseKey);
|
|
5505
|
+
});
|
|
5506
|
+
return result;
|
|
5507
|
+
}
|
|
5508
|
+
|
|
5509
|
+
// src/evaluation/providers/vscode/dispatch/templates.ts
|
|
5510
|
+
var DEFAULT_REQUEST_TEMPLATE = `[[ ## task ## ]]
|
|
5511
|
+
|
|
5512
|
+
{{userQuery}}
|
|
5513
|
+
|
|
5514
|
+
[[ ## system_instructions ## ]]
|
|
5515
|
+
|
|
5516
|
+
**IMPORTANT**: Follow these exact steps:
|
|
5517
|
+
1. Create and write your complete response to: {{responseFileTmp}}
|
|
5518
|
+
2. When completely finished, run these PowerShell commands to signal completion:
|
|
5519
|
+
\`\`\`
|
|
5520
|
+
Move-Item -LiteralPath '{{responseFileTmp}}' -Destination '{{responseFileFinal}}'
|
|
5521
|
+
if (Test-Path subagent.lock) { del subagent.lock }
|
|
5522
|
+
\`\`\`
|
|
5523
|
+
|
|
5524
|
+
Do not proceed to step 2 until your response is completely written to the temporary file.
|
|
5525
|
+
`;
|
|
5526
|
+
var DEFAULT_BATCH_REQUEST_TEMPLATE = `[[ ## task ## ]]
|
|
5527
|
+
|
|
5528
|
+
{{userQuery}}
|
|
5529
|
+
|
|
5530
|
+
[[ ## system_instructions ## ]]
|
|
5531
|
+
|
|
5532
|
+
**IMPORTANT**: Follow these exact steps:
|
|
5533
|
+
1. Create and write your complete response to: {{responseFileTmp}}
|
|
5534
|
+
2. When completely finished and the response is stable, rename it to: {{responseFileFinal}}
|
|
5535
|
+
3. Do not unlock the workspace from this request; batch orchestration will handle unlocking after all responses are ready.
|
|
5536
|
+
`;
|
|
5537
|
+
var DEFAULT_BATCH_ORCHESTRATOR_TEMPLATE = `MANDATORY: Run #runSubagent tool in your Available Actions for each request file to process them in isolated contexts.
|
|
5538
|
+
DO NOT read the request files yourself - only pass the file paths to each subagent:
|
|
5539
|
+
|
|
5540
|
+
{{requestFiles}}
|
|
5541
|
+
|
|
5542
|
+
After ALL queries complete, verify all responses exist and unlock:
|
|
5543
|
+
|
|
5544
|
+
\`\`\`powershell
|
|
5545
|
+
$responses = @({{responseList}})
|
|
5546
|
+
$missing = $responses | Where-Object { -not (Test-Path "messages/$_") }
|
|
5547
|
+
if ($missing.Count -eq 0) { del subagent.lock }
|
|
5548
|
+
\`\`\`
|
|
5549
|
+
`;
|
|
5550
|
+
|
|
5551
|
+
// src/evaluation/providers/vscode/dispatch/promptBuilder.ts
|
|
5552
|
+
function loadDefaultRequestTemplate() {
|
|
5553
|
+
return DEFAULT_REQUEST_TEMPLATE;
|
|
5554
|
+
}
|
|
5555
|
+
function loadDefaultBatchRequestTemplate() {
|
|
5556
|
+
return DEFAULT_BATCH_REQUEST_TEMPLATE;
|
|
5557
|
+
}
|
|
5558
|
+
function loadDefaultBatchOrchestratorTemplate() {
|
|
5559
|
+
return DEFAULT_BATCH_ORCHESTRATOR_TEMPLATE;
|
|
5560
|
+
}
|
|
5561
|
+
function createRequestPrompt(userQuery, responseFileTmp, responseFileFinal, templateContent) {
|
|
5562
|
+
return renderTemplate2(templateContent, {
|
|
5563
|
+
userQuery,
|
|
5564
|
+
responseFileTmp,
|
|
5565
|
+
responseFileFinal
|
|
5566
|
+
});
|
|
5567
|
+
}
|
|
5568
|
+
function createBatchRequestPrompt(userQuery, responseFileTmp, responseFileFinal, templateContent) {
|
|
5569
|
+
return renderTemplate2(templateContent, {
|
|
5570
|
+
userQuery,
|
|
5571
|
+
responseFileTmp,
|
|
5572
|
+
responseFileFinal
|
|
5573
|
+
});
|
|
5574
|
+
}
|
|
5575
|
+
function createBatchOrchestratorPrompt(requestFiles, responseFiles, templateContent) {
|
|
5576
|
+
const requestLines = requestFiles.map((file, index) => `${index + 1}. messages/${path15.basename(file)}`).join("\n");
|
|
5577
|
+
const responseList = responseFiles.map((file) => `"${path15.basename(file)}"`).join(", ");
|
|
5578
|
+
return renderTemplate2(templateContent, {
|
|
5579
|
+
requestFiles: requestLines,
|
|
5580
|
+
responseList
|
|
5581
|
+
});
|
|
5582
|
+
}
|
|
5583
|
+
|
|
5584
|
+
// src/evaluation/providers/vscode/dispatch/responseWaiter.ts
|
|
5585
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
5586
|
+
import path16 from "node:path";
|
|
5587
|
+
|
|
5588
|
+
// src/evaluation/providers/vscode/utils/time.ts
|
|
5589
|
+
function sleep2(ms) {
|
|
5590
|
+
return new Promise((resolve) => {
|
|
5591
|
+
setTimeout(resolve, ms);
|
|
5592
|
+
});
|
|
5593
|
+
}
|
|
5594
|
+
|
|
5595
|
+
// src/evaluation/providers/vscode/dispatch/responseWaiter.ts
|
|
5596
|
+
async function waitForResponseOutput(responseFileFinal, pollInterval = 1e3, silent = false) {
|
|
5597
|
+
if (!silent) {
|
|
5598
|
+
console.error(`waiting for agent to finish: ${responseFileFinal}`);
|
|
5599
|
+
}
|
|
5600
|
+
try {
|
|
5601
|
+
while (!await pathExists(responseFileFinal)) {
|
|
5602
|
+
await sleep2(pollInterval);
|
|
5603
|
+
}
|
|
5604
|
+
} catch (error) {
|
|
5605
|
+
if (error.code === "ENOENT") {
|
|
5606
|
+
return false;
|
|
5607
|
+
}
|
|
5608
|
+
throw error;
|
|
5609
|
+
}
|
|
5610
|
+
let attempts = 0;
|
|
5611
|
+
const maxAttempts = 10;
|
|
5612
|
+
while (attempts < maxAttempts) {
|
|
5613
|
+
try {
|
|
5614
|
+
const content = await readFile7(responseFileFinal, { encoding: "utf8" });
|
|
5615
|
+
if (!silent) {
|
|
5616
|
+
process.stdout.write(`${content}
|
|
5617
|
+
`);
|
|
5618
|
+
}
|
|
5619
|
+
return true;
|
|
5620
|
+
} catch (error) {
|
|
5621
|
+
attempts += 1;
|
|
5622
|
+
if (error.code !== "EBUSY" || attempts >= maxAttempts) {
|
|
5623
|
+
if (!silent) {
|
|
5624
|
+
console.error(`error: failed to read agent response: ${error.message}`);
|
|
5625
|
+
}
|
|
5626
|
+
return false;
|
|
5627
|
+
}
|
|
5628
|
+
await sleep2(pollInterval);
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
return false;
|
|
5632
|
+
}
|
|
5633
|
+
async function waitForBatchResponses(responseFilesFinal, pollInterval = 1e3, silent = false) {
|
|
5634
|
+
if (!silent) {
|
|
5635
|
+
const fileList = responseFilesFinal.map((file) => path16.basename(file)).join(", ");
|
|
5636
|
+
console.error(`waiting for ${responseFilesFinal.length} batch response(s): ${fileList}`);
|
|
5637
|
+
}
|
|
5638
|
+
try {
|
|
5639
|
+
const pending = new Set(responseFilesFinal);
|
|
5640
|
+
while (pending.size > 0) {
|
|
5641
|
+
for (const file of [...pending]) {
|
|
5642
|
+
if (await pathExists(file)) {
|
|
5643
|
+
pending.delete(file);
|
|
5644
|
+
}
|
|
5645
|
+
}
|
|
5646
|
+
if (pending.size > 0) {
|
|
5647
|
+
await sleep2(pollInterval);
|
|
5648
|
+
}
|
|
5649
|
+
}
|
|
5650
|
+
} catch (error) {
|
|
5651
|
+
if (error.code === "ENOENT") {
|
|
5652
|
+
return false;
|
|
5653
|
+
}
|
|
5654
|
+
throw error;
|
|
5655
|
+
}
|
|
5656
|
+
for (const file of responseFilesFinal) {
|
|
5657
|
+
let attempts = 0;
|
|
5658
|
+
const maxAttempts = 10;
|
|
5659
|
+
while (attempts < maxAttempts) {
|
|
5660
|
+
try {
|
|
5661
|
+
const content = await readFile7(file, { encoding: "utf8" });
|
|
5662
|
+
if (!silent) {
|
|
5663
|
+
process.stdout.write(`${content}
|
|
5664
|
+
`);
|
|
5665
|
+
}
|
|
5666
|
+
break;
|
|
5667
|
+
} catch (error) {
|
|
5668
|
+
attempts += 1;
|
|
5669
|
+
if (error.code !== "EBUSY" || attempts >= maxAttempts) {
|
|
5670
|
+
if (!silent) {
|
|
5671
|
+
console.error(`error: failed to read agent response: ${error.message}`);
|
|
5672
|
+
}
|
|
5673
|
+
return false;
|
|
5674
|
+
}
|
|
5675
|
+
await sleep2(pollInterval);
|
|
5676
|
+
}
|
|
5677
|
+
}
|
|
5678
|
+
}
|
|
5679
|
+
return true;
|
|
5680
|
+
}
|
|
5681
|
+
|
|
5682
|
+
// src/evaluation/providers/vscode/dispatch/vscodeProcess.ts
|
|
5683
|
+
import { exec, spawn as spawn4 } from "node:child_process";
|
|
5684
|
+
import { mkdir as mkdir5, writeFile as writeFile4 } from "node:fs/promises";
|
|
5685
|
+
import path18 from "node:path";
|
|
5686
|
+
import { promisify as promisify3 } from "node:util";
|
|
5687
|
+
|
|
5688
|
+
// src/evaluation/providers/vscode/dispatch/constants.ts
|
|
5689
|
+
import os2 from "node:os";
|
|
5690
|
+
import path17 from "node:path";
|
|
5691
|
+
var DEFAULT_LOCK_NAME = "subagent.lock";
|
|
5692
|
+
var DEFAULT_ALIVE_FILENAME = ".alive";
|
|
5693
|
+
function getDefaultSubagentRoot(vscodeCmd = "code") {
|
|
5694
|
+
const folder = vscodeCmd === "code-insiders" ? "vscode-insiders-agents" : "vscode-agents";
|
|
5695
|
+
return path17.join(os2.homedir(), ".agentv", "subagents", folder);
|
|
5696
|
+
}
|
|
5697
|
+
var DEFAULT_SUBAGENT_ROOT = getDefaultSubagentRoot();
|
|
5698
|
+
|
|
5699
|
+
// src/evaluation/providers/vscode/dispatch/vscodeProcess.ts
|
|
5700
|
+
var execAsync3 = promisify3(exec);
|
|
5701
|
+
var DEFAULT_WAKEUP_CONTENT = `---
|
|
5702
|
+
description: 'Wake-up Signal'
|
|
5703
|
+
model: Grok Code Fast 1 (copilot)
|
|
5704
|
+
---`;
|
|
5705
|
+
async function checkWorkspaceOpened(workspaceName, vscodeCmd) {
|
|
5706
|
+
try {
|
|
5707
|
+
const { stdout } = await execAsync3(`${vscodeCmd} --status`, {
|
|
5708
|
+
timeout: 1e4,
|
|
5709
|
+
windowsHide: true
|
|
5710
|
+
});
|
|
5711
|
+
return stdout.includes(workspaceName);
|
|
5712
|
+
} catch {
|
|
5713
|
+
return false;
|
|
5714
|
+
}
|
|
5715
|
+
}
|
|
5716
|
+
async function ensureWorkspaceFocused(workspacePath, workspaceName, subagentDir, vscodeCmd, pollInterval = 1, timeout = 60) {
|
|
5717
|
+
const alreadyOpen = await checkWorkspaceOpened(workspaceName, vscodeCmd);
|
|
5718
|
+
if (alreadyOpen) {
|
|
5719
|
+
spawn4(vscodeCmd, [workspacePath], { windowsHide: true, shell: true, detached: false });
|
|
5720
|
+
return true;
|
|
5721
|
+
}
|
|
5722
|
+
const aliveFile = path18.join(subagentDir, DEFAULT_ALIVE_FILENAME);
|
|
5723
|
+
await removeIfExists(aliveFile);
|
|
5724
|
+
const githubAgentsDir = path18.join(subagentDir, ".github", "agents");
|
|
5725
|
+
await mkdir5(githubAgentsDir, { recursive: true });
|
|
5726
|
+
const wakeupDst = path18.join(githubAgentsDir, "wakeup.md");
|
|
5727
|
+
await writeFile4(wakeupDst, DEFAULT_WAKEUP_CONTENT, "utf8");
|
|
5728
|
+
spawn4(vscodeCmd, [workspacePath], { windowsHide: true, shell: true, detached: false });
|
|
5729
|
+
await sleep2(100);
|
|
5730
|
+
const wakeupChatId = "wakeup";
|
|
5731
|
+
const chatArgs = [
|
|
5732
|
+
"-r",
|
|
5733
|
+
"chat",
|
|
5734
|
+
"-m",
|
|
5735
|
+
wakeupChatId,
|
|
5736
|
+
`create a file named .alive in the ${path18.basename(subagentDir)} folder`
|
|
5737
|
+
];
|
|
5738
|
+
spawn4(vscodeCmd, chatArgs, { windowsHide: true, shell: true, detached: false });
|
|
5739
|
+
const start = Date.now();
|
|
5740
|
+
while (!await pathExists(aliveFile)) {
|
|
5741
|
+
if (Date.now() - start > timeout * 1e3) {
|
|
5742
|
+
console.error(`warning: Workspace readiness timeout after ${timeout}s`);
|
|
5743
|
+
return false;
|
|
5744
|
+
}
|
|
5745
|
+
await sleep2(pollInterval * 1e3);
|
|
5746
|
+
}
|
|
5747
|
+
return true;
|
|
5748
|
+
}
|
|
5749
|
+
async function launchVsCodeWithChat(subagentDir, chatId, attachmentPaths, requestInstructions, timestamp, vscodeCmd) {
|
|
5750
|
+
try {
|
|
5751
|
+
const workspacePath = path18.join(subagentDir, `${path18.basename(subagentDir)}.code-workspace`);
|
|
5752
|
+
const messagesDir = path18.join(subagentDir, "messages");
|
|
5753
|
+
await mkdir5(messagesDir, { recursive: true });
|
|
5754
|
+
const reqFile = path18.join(messagesDir, `${timestamp}_req.md`);
|
|
5755
|
+
await writeFile4(reqFile, requestInstructions, { encoding: "utf8" });
|
|
5756
|
+
const reqUri = pathToFileUri2(reqFile);
|
|
5757
|
+
const chatArgs = ["-r", "chat", "-m", chatId];
|
|
5758
|
+
for (const attachment of attachmentPaths) {
|
|
5759
|
+
chatArgs.push("-a", attachment);
|
|
5760
|
+
}
|
|
5761
|
+
chatArgs.push("-a", reqFile);
|
|
5762
|
+
chatArgs.push(`Follow instructions in [${path18.basename(reqFile)}](${reqUri})`);
|
|
5763
|
+
const workspaceReady = await ensureWorkspaceFocused(
|
|
5764
|
+
workspacePath,
|
|
5765
|
+
path18.basename(subagentDir),
|
|
5766
|
+
subagentDir,
|
|
5767
|
+
vscodeCmd
|
|
5768
|
+
);
|
|
5769
|
+
if (!workspaceReady) {
|
|
5770
|
+
console.error("warning: Workspace may not be fully ready");
|
|
5771
|
+
}
|
|
5772
|
+
await sleep2(500);
|
|
5773
|
+
spawn4(vscodeCmd, chatArgs, { windowsHide: true, shell: true, detached: false });
|
|
5774
|
+
return true;
|
|
5775
|
+
} catch (error) {
|
|
5776
|
+
console.error(`warning: Failed to launch VS Code: ${error.message}`);
|
|
5777
|
+
return false;
|
|
5778
|
+
}
|
|
5779
|
+
}
|
|
5780
|
+
async function launchVsCodeWithBatchChat(subagentDir, chatId, attachmentPaths, chatInstruction, vscodeCmd) {
|
|
5781
|
+
try {
|
|
5782
|
+
const workspacePath = path18.join(subagentDir, `${path18.basename(subagentDir)}.code-workspace`);
|
|
5783
|
+
const messagesDir = path18.join(subagentDir, "messages");
|
|
5784
|
+
await mkdir5(messagesDir, { recursive: true });
|
|
5785
|
+
const chatArgs = ["-r", "chat", "-m", chatId];
|
|
5786
|
+
for (const attachment of attachmentPaths) {
|
|
5787
|
+
chatArgs.push("-a", attachment);
|
|
5788
|
+
}
|
|
5789
|
+
chatArgs.push(chatInstruction);
|
|
5790
|
+
const workspaceReady = await ensureWorkspaceFocused(
|
|
5791
|
+
workspacePath,
|
|
5792
|
+
path18.basename(subagentDir),
|
|
5793
|
+
subagentDir,
|
|
5794
|
+
vscodeCmd
|
|
5795
|
+
);
|
|
5796
|
+
if (!workspaceReady) {
|
|
5797
|
+
console.error("warning: Workspace may not be fully ready");
|
|
5798
|
+
}
|
|
5799
|
+
await sleep2(500);
|
|
5800
|
+
spawn4(vscodeCmd, chatArgs, { windowsHide: true, shell: true, detached: false });
|
|
5801
|
+
return true;
|
|
5802
|
+
} catch (error) {
|
|
5803
|
+
console.error(`warning: Failed to launch VS Code: ${error.message}`);
|
|
5804
|
+
return false;
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
|
|
5808
|
+
// src/evaluation/providers/vscode/dispatch/workspaceManager.ts
|
|
5809
|
+
import { copyFile, mkdir as mkdir6, readFile as readFile8, readdir as readdir2, stat as stat2, writeFile as writeFile5 } from "node:fs/promises";
|
|
5810
|
+
import path20 from "node:path";
|
|
5811
|
+
|
|
5812
|
+
// src/evaluation/providers/vscode/utils/workspace.ts
|
|
5813
|
+
import path19 from "node:path";
|
|
5814
|
+
import JSON5 from "json5";
|
|
5815
|
+
function transformWorkspacePaths(workspaceContent, templateDir) {
|
|
5816
|
+
let workspace;
|
|
5817
|
+
try {
|
|
5818
|
+
workspace = JSON5.parse(workspaceContent);
|
|
5819
|
+
} catch (error) {
|
|
5820
|
+
throw new Error(`Invalid workspace JSON: ${error.message}`);
|
|
5821
|
+
}
|
|
5822
|
+
if (!workspace.folders) {
|
|
5823
|
+
throw new Error("Workspace file must contain a 'folders' array");
|
|
5824
|
+
}
|
|
5825
|
+
if (!Array.isArray(workspace.folders)) {
|
|
5826
|
+
throw new Error("Workspace 'folders' must be an array");
|
|
5827
|
+
}
|
|
5828
|
+
const transformedFolders = workspace.folders.map((folder) => {
|
|
5829
|
+
const folderPath = folder.path;
|
|
5830
|
+
if (path19.isAbsolute(folderPath)) {
|
|
5831
|
+
return folder;
|
|
5832
|
+
}
|
|
5833
|
+
const absolutePath = path19.resolve(templateDir, folderPath);
|
|
5834
|
+
return {
|
|
5835
|
+
...folder,
|
|
5836
|
+
path: absolutePath
|
|
5837
|
+
};
|
|
5838
|
+
});
|
|
5839
|
+
const updatedFolders = [{ path: "." }, ...transformedFolders];
|
|
5840
|
+
let transformedSettings = workspace.settings;
|
|
5841
|
+
if (workspace.settings) {
|
|
5842
|
+
transformedSettings = {
|
|
5843
|
+
...workspace.settings
|
|
5844
|
+
};
|
|
5845
|
+
const chatSettingsKeys = [
|
|
5846
|
+
"chat.promptFilesLocations",
|
|
5847
|
+
"chat.instructionsFilesLocations",
|
|
5848
|
+
"chat.modeFilesLocations"
|
|
5849
|
+
];
|
|
5850
|
+
for (const settingKey of chatSettingsKeys) {
|
|
5851
|
+
const locationMap = workspace.settings[settingKey];
|
|
5852
|
+
if (locationMap && typeof locationMap === "object") {
|
|
5853
|
+
const transformedMap = {};
|
|
5854
|
+
for (const [locationPath, value] of Object.entries(locationMap)) {
|
|
5855
|
+
const isAbsolute = path19.isAbsolute(locationPath);
|
|
5856
|
+
if (isAbsolute) {
|
|
5857
|
+
transformedMap[locationPath] = value;
|
|
5858
|
+
} else {
|
|
5859
|
+
const firstGlobIndex = locationPath.search(/[*]/);
|
|
5860
|
+
if (firstGlobIndex === -1) {
|
|
5861
|
+
const resolvedPath = path19.resolve(templateDir, locationPath).replace(/\\/g, "/");
|
|
5862
|
+
transformedMap[resolvedPath] = value;
|
|
5863
|
+
} else {
|
|
5864
|
+
const basePathEnd = locationPath.lastIndexOf("/", firstGlobIndex);
|
|
5865
|
+
const basePath = basePathEnd !== -1 ? locationPath.substring(0, basePathEnd) : ".";
|
|
5866
|
+
const patternPath = locationPath.substring(basePathEnd !== -1 ? basePathEnd : 0);
|
|
5867
|
+
const resolvedPath = (path19.resolve(templateDir, basePath) + patternPath).replace(
|
|
5868
|
+
/\\/g,
|
|
5869
|
+
"/"
|
|
5870
|
+
);
|
|
5871
|
+
transformedMap[resolvedPath] = value;
|
|
5872
|
+
}
|
|
5873
|
+
}
|
|
5874
|
+
}
|
|
5875
|
+
transformedSettings[settingKey] = transformedMap;
|
|
5876
|
+
}
|
|
5877
|
+
}
|
|
5878
|
+
}
|
|
5879
|
+
const transformedWorkspace = {
|
|
5880
|
+
...workspace,
|
|
5881
|
+
folders: updatedFolders,
|
|
5882
|
+
settings: transformedSettings
|
|
5883
|
+
};
|
|
5884
|
+
return JSON.stringify(transformedWorkspace, null, 2);
|
|
5885
|
+
}
|
|
5886
|
+
|
|
5887
|
+
// src/evaluation/providers/vscode/dispatch/workspaceManager.ts
|
|
5888
|
+
var DEFAULT_WORKSPACE_TEMPLATE = {
|
|
5889
|
+
folders: [
|
|
5890
|
+
{
|
|
5891
|
+
path: "."
|
|
5892
|
+
}
|
|
5893
|
+
]
|
|
5894
|
+
};
|
|
5895
|
+
function getSubagentRoot(vscodeCmd = "code") {
|
|
5896
|
+
return getDefaultSubagentRoot(vscodeCmd);
|
|
5897
|
+
}
|
|
5898
|
+
async function findUnlockedSubagent(subagentRoot) {
|
|
5899
|
+
if (!await pathExists(subagentRoot)) {
|
|
5900
|
+
return null;
|
|
5901
|
+
}
|
|
5902
|
+
const entries = await readDirEntries(subagentRoot);
|
|
5903
|
+
const subagents = entries.filter((entry) => entry.isDirectory && entry.name.startsWith("subagent-")).map((entry) => ({
|
|
5904
|
+
absolutePath: entry.absolutePath,
|
|
5905
|
+
number: Number.parseInt(entry.name.split("-")[1] ?? "", 10)
|
|
5906
|
+
})).filter((entry) => Number.isInteger(entry.number)).sort((a, b) => a.number - b.number);
|
|
5907
|
+
for (const subagent of subagents) {
|
|
5908
|
+
const lockFile = path20.join(subagent.absolutePath, DEFAULT_LOCK_NAME);
|
|
5909
|
+
if (!await pathExists(lockFile)) {
|
|
5910
|
+
return subagent.absolutePath;
|
|
5911
|
+
}
|
|
5912
|
+
}
|
|
5913
|
+
return null;
|
|
5914
|
+
}
|
|
5915
|
+
async function copyAgentConfig(subagentDir, workspaceTemplate) {
|
|
5916
|
+
let workspaceContent;
|
|
5917
|
+
if (workspaceTemplate) {
|
|
5918
|
+
const workspaceSrc = path20.resolve(workspaceTemplate);
|
|
5919
|
+
if (!await pathExists(workspaceSrc)) {
|
|
5920
|
+
throw new Error(`workspace template not found: ${workspaceSrc}`);
|
|
5921
|
+
}
|
|
5922
|
+
const stats = await stat2(workspaceSrc);
|
|
5923
|
+
if (!stats.isFile()) {
|
|
5924
|
+
throw new Error(`workspace template must be a file, not a directory: ${workspaceSrc}`);
|
|
5925
|
+
}
|
|
5926
|
+
const templateText = await readFile8(workspaceSrc, "utf8");
|
|
5927
|
+
workspaceContent = JSON.parse(templateText);
|
|
5928
|
+
} else {
|
|
5929
|
+
workspaceContent = DEFAULT_WORKSPACE_TEMPLATE;
|
|
5930
|
+
}
|
|
5931
|
+
const workspaceName = `${path20.basename(subagentDir)}.code-workspace`;
|
|
5932
|
+
const workspaceDst = path20.join(subagentDir, workspaceName);
|
|
5933
|
+
const templateDir = workspaceTemplate ? path20.dirname(path20.resolve(workspaceTemplate)) : subagentDir;
|
|
5934
|
+
const workspaceJson = JSON.stringify(workspaceContent, null, 2);
|
|
5935
|
+
const transformedContent = transformWorkspacePaths(workspaceJson, templateDir);
|
|
5936
|
+
await writeFile5(workspaceDst, transformedContent, "utf8");
|
|
5937
|
+
const messagesDir = path20.join(subagentDir, "messages");
|
|
5938
|
+
await mkdir6(messagesDir, { recursive: true });
|
|
5939
|
+
return { workspace: workspaceDst, messagesDir };
|
|
5940
|
+
}
|
|
5941
|
+
async function createSubagentLock(subagentDir) {
|
|
5942
|
+
const messagesDir = path20.join(subagentDir, "messages");
|
|
5943
|
+
if (await pathExists(messagesDir)) {
|
|
5944
|
+
const files = await readdir2(messagesDir);
|
|
5945
|
+
await Promise.all(
|
|
5946
|
+
files.map(async (file) => {
|
|
5947
|
+
const target = path20.join(messagesDir, file);
|
|
5948
|
+
await removeIfExists(target);
|
|
5949
|
+
})
|
|
5950
|
+
);
|
|
5951
|
+
}
|
|
5952
|
+
const githubAgentsDir = path20.join(subagentDir, ".github", "agents");
|
|
5953
|
+
if (await pathExists(githubAgentsDir)) {
|
|
5954
|
+
const agentFiles = await readdir2(githubAgentsDir);
|
|
5955
|
+
const preservedFiles = /* @__PURE__ */ new Set(["wakeup.md", "subagent.md"]);
|
|
5956
|
+
await Promise.all(
|
|
5957
|
+
agentFiles.filter((file) => file.endsWith(".md") && !preservedFiles.has(file)).map((file) => removeIfExists(path20.join(githubAgentsDir, file)))
|
|
5958
|
+
);
|
|
5959
|
+
}
|
|
5960
|
+
const lockFile = path20.join(subagentDir, DEFAULT_LOCK_NAME);
|
|
5961
|
+
await writeFile5(lockFile, "", { encoding: "utf8" });
|
|
5962
|
+
return lockFile;
|
|
5963
|
+
}
|
|
5964
|
+
async function removeSubagentLock(subagentDir) {
|
|
5965
|
+
const lockFile = path20.join(subagentDir, DEFAULT_LOCK_NAME);
|
|
5966
|
+
await removeIfExists(lockFile);
|
|
5967
|
+
}
|
|
5968
|
+
async function prepareSubagentDirectory(subagentDir, promptFile, chatId, workspaceTemplate, dryRun) {
|
|
5969
|
+
if (dryRun) {
|
|
5970
|
+
return 0;
|
|
5971
|
+
}
|
|
5972
|
+
try {
|
|
5973
|
+
await copyAgentConfig(subagentDir, workspaceTemplate);
|
|
5974
|
+
} catch (error) {
|
|
5975
|
+
console.error(`error: ${error.message}`);
|
|
5976
|
+
return 1;
|
|
5977
|
+
}
|
|
5978
|
+
try {
|
|
5979
|
+
await createSubagentLock(subagentDir);
|
|
5980
|
+
} catch (error) {
|
|
5981
|
+
console.error(`error: Failed to create subagent lock: ${error.message}`);
|
|
5982
|
+
return 1;
|
|
5983
|
+
}
|
|
5984
|
+
if (promptFile) {
|
|
5985
|
+
const githubAgentsDir = path20.join(subagentDir, ".github", "agents");
|
|
5986
|
+
await mkdir6(githubAgentsDir, { recursive: true });
|
|
5987
|
+
const agentFile = path20.join(githubAgentsDir, `${chatId}.md`);
|
|
5988
|
+
try {
|
|
5989
|
+
await copyFile(promptFile, agentFile);
|
|
5990
|
+
} catch (error) {
|
|
5991
|
+
console.error(`error: Failed to copy prompt file to agent mode: ${error.message}`);
|
|
5992
|
+
return 1;
|
|
5993
|
+
}
|
|
5994
|
+
}
|
|
5995
|
+
return 0;
|
|
5996
|
+
}
|
|
5997
|
+
|
|
5998
|
+
// src/evaluation/providers/vscode/dispatch/agentDispatch.ts
|
|
5999
|
+
function generateTimestamp() {
|
|
6000
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
|
|
6001
|
+
}
|
|
6002
|
+
async function resolvePromptFile(promptFile) {
|
|
6003
|
+
if (!promptFile) {
|
|
6004
|
+
return void 0;
|
|
6005
|
+
}
|
|
6006
|
+
const resolvedPrompt = path21.resolve(promptFile);
|
|
6007
|
+
if (!await pathExists(resolvedPrompt)) {
|
|
6008
|
+
throw new Error(`Prompt file not found: ${resolvedPrompt}`);
|
|
6009
|
+
}
|
|
6010
|
+
const promptStats = await stat3(resolvedPrompt);
|
|
6011
|
+
if (!promptStats.isFile()) {
|
|
6012
|
+
throw new Error(`Prompt file must be a file, not a directory: ${resolvedPrompt}`);
|
|
6013
|
+
}
|
|
6014
|
+
return resolvedPrompt;
|
|
6015
|
+
}
|
|
6016
|
+
async function resolveAttachments(extraAttachments) {
|
|
6017
|
+
if (!extraAttachments) {
|
|
6018
|
+
return [];
|
|
6019
|
+
}
|
|
6020
|
+
const resolved = [];
|
|
6021
|
+
for (const attachment of extraAttachments) {
|
|
6022
|
+
const resolvedPath = path21.resolve(attachment);
|
|
6023
|
+
if (!await pathExists(resolvedPath)) {
|
|
6024
|
+
throw new Error(`Attachment not found: ${resolvedPath}`);
|
|
6025
|
+
}
|
|
6026
|
+
resolved.push(resolvedPath);
|
|
6027
|
+
}
|
|
6028
|
+
return resolved;
|
|
6029
|
+
}
|
|
6030
|
+
async function dispatchAgentSession(options) {
|
|
6031
|
+
const {
|
|
6032
|
+
userQuery,
|
|
6033
|
+
promptFile,
|
|
6034
|
+
requestTemplate,
|
|
6035
|
+
extraAttachments,
|
|
6036
|
+
workspaceTemplate,
|
|
6037
|
+
dryRun = false,
|
|
6038
|
+
wait = true,
|
|
6039
|
+
vscodeCmd = "code",
|
|
6040
|
+
subagentRoot,
|
|
6041
|
+
silent = false
|
|
6042
|
+
} = options;
|
|
6043
|
+
try {
|
|
6044
|
+
let resolvedPrompt;
|
|
6045
|
+
try {
|
|
6046
|
+
resolvedPrompt = await resolvePromptFile(promptFile);
|
|
6047
|
+
} catch (error) {
|
|
6048
|
+
return {
|
|
6049
|
+
exitCode: 1,
|
|
6050
|
+
error: error.message
|
|
6051
|
+
};
|
|
6052
|
+
}
|
|
6053
|
+
const templateContent = requestTemplate ?? loadDefaultRequestTemplate();
|
|
6054
|
+
const subagentRootPath = subagentRoot ?? getSubagentRoot(vscodeCmd);
|
|
6055
|
+
const subagentDir = await findUnlockedSubagent(subagentRootPath);
|
|
6056
|
+
if (!subagentDir) {
|
|
6057
|
+
return {
|
|
6058
|
+
exitCode: 1,
|
|
6059
|
+
error: "No unlocked subagents available. Provision additional subagents with: subagent code provision --subagents <desired_total>"
|
|
6060
|
+
};
|
|
6061
|
+
}
|
|
6062
|
+
const subagentName = path21.basename(subagentDir);
|
|
6063
|
+
const chatId = Math.random().toString(16).slice(2, 10);
|
|
6064
|
+
const preparationResult = await prepareSubagentDirectory(
|
|
6065
|
+
subagentDir,
|
|
6066
|
+
resolvedPrompt,
|
|
6067
|
+
chatId,
|
|
6068
|
+
workspaceTemplate,
|
|
6069
|
+
dryRun
|
|
6070
|
+
);
|
|
6071
|
+
if (preparationResult !== 0) {
|
|
6072
|
+
return {
|
|
6073
|
+
exitCode: preparationResult,
|
|
6074
|
+
subagentName,
|
|
6075
|
+
error: "Failed to prepare subagent workspace"
|
|
6076
|
+
};
|
|
6077
|
+
}
|
|
6078
|
+
let attachments;
|
|
6079
|
+
try {
|
|
6080
|
+
attachments = await resolveAttachments(extraAttachments);
|
|
6081
|
+
} catch (attachmentError) {
|
|
6082
|
+
return {
|
|
6083
|
+
exitCode: 1,
|
|
6084
|
+
subagentName,
|
|
6085
|
+
error: attachmentError.message
|
|
6086
|
+
};
|
|
6087
|
+
}
|
|
6088
|
+
const timestamp = generateTimestamp();
|
|
6089
|
+
const messagesDir = path21.join(subagentDir, "messages");
|
|
6090
|
+
const responseFileTmp = path21.join(messagesDir, `${timestamp}_res.tmp.md`);
|
|
6091
|
+
const responseFileFinal = path21.join(messagesDir, `${timestamp}_res.md`);
|
|
6092
|
+
const requestInstructions = createRequestPrompt(
|
|
6093
|
+
userQuery,
|
|
6094
|
+
responseFileTmp,
|
|
6095
|
+
responseFileFinal,
|
|
6096
|
+
templateContent
|
|
6097
|
+
);
|
|
6098
|
+
if (dryRun) {
|
|
6099
|
+
return {
|
|
6100
|
+
exitCode: 0,
|
|
6101
|
+
subagentName,
|
|
6102
|
+
responseFile: responseFileFinal,
|
|
6103
|
+
tempFile: responseFileTmp
|
|
6104
|
+
};
|
|
6105
|
+
}
|
|
6106
|
+
const launchSuccess = await launchVsCodeWithChat(
|
|
6107
|
+
subagentDir,
|
|
6108
|
+
chatId,
|
|
6109
|
+
attachments,
|
|
6110
|
+
requestInstructions,
|
|
6111
|
+
timestamp,
|
|
6112
|
+
vscodeCmd
|
|
6113
|
+
);
|
|
6114
|
+
if (!launchSuccess) {
|
|
6115
|
+
return {
|
|
6116
|
+
exitCode: 1,
|
|
6117
|
+
subagentName,
|
|
6118
|
+
responseFile: responseFileFinal,
|
|
6119
|
+
tempFile: responseFileTmp,
|
|
6120
|
+
error: "Failed to launch VS Code for subagent session"
|
|
6121
|
+
};
|
|
6122
|
+
}
|
|
6123
|
+
if (!wait) {
|
|
6124
|
+
return {
|
|
6125
|
+
exitCode: 0,
|
|
6126
|
+
subagentName,
|
|
6127
|
+
responseFile: responseFileFinal,
|
|
6128
|
+
tempFile: responseFileTmp
|
|
6129
|
+
};
|
|
6130
|
+
}
|
|
6131
|
+
const received = await waitForResponseOutput(responseFileFinal, 1e3, silent);
|
|
6132
|
+
if (!received) {
|
|
6133
|
+
return {
|
|
6134
|
+
exitCode: 1,
|
|
6135
|
+
subagentName,
|
|
6136
|
+
responseFile: responseFileFinal,
|
|
6137
|
+
tempFile: responseFileTmp,
|
|
6138
|
+
error: "Timed out waiting for agent response"
|
|
6139
|
+
};
|
|
6140
|
+
}
|
|
6141
|
+
await removeSubagentLock(subagentDir);
|
|
6142
|
+
return {
|
|
6143
|
+
exitCode: 0,
|
|
6144
|
+
subagentName,
|
|
6145
|
+
responseFile: responseFileFinal,
|
|
6146
|
+
tempFile: responseFileTmp
|
|
6147
|
+
};
|
|
6148
|
+
} catch (error) {
|
|
6149
|
+
return {
|
|
6150
|
+
exitCode: 1,
|
|
6151
|
+
error: error.message
|
|
6152
|
+
};
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
async function dispatchBatchAgent(options) {
|
|
6156
|
+
const {
|
|
6157
|
+
userQueries,
|
|
6158
|
+
promptFile,
|
|
6159
|
+
requestTemplate,
|
|
6160
|
+
extraAttachments,
|
|
6161
|
+
workspaceTemplate,
|
|
6162
|
+
dryRun = false,
|
|
6163
|
+
wait = false,
|
|
6164
|
+
vscodeCmd = "code",
|
|
6165
|
+
subagentRoot,
|
|
6166
|
+
silent = false
|
|
6167
|
+
} = options;
|
|
6168
|
+
if (!userQueries || userQueries.length === 0) {
|
|
6169
|
+
return {
|
|
6170
|
+
exitCode: 1,
|
|
6171
|
+
requestFiles: [],
|
|
6172
|
+
queryCount: 0,
|
|
6173
|
+
error: "At least one query is required for batch dispatch"
|
|
6174
|
+
};
|
|
6175
|
+
}
|
|
6176
|
+
const queryCount = userQueries.length;
|
|
6177
|
+
let requestFiles = [];
|
|
6178
|
+
let responseFilesFinal = [];
|
|
6179
|
+
let subagentName;
|
|
6180
|
+
try {
|
|
6181
|
+
let resolvedPrompt;
|
|
6182
|
+
try {
|
|
6183
|
+
resolvedPrompt = await resolvePromptFile(promptFile);
|
|
6184
|
+
} catch (error) {
|
|
6185
|
+
return {
|
|
6186
|
+
exitCode: 1,
|
|
6187
|
+
requestFiles,
|
|
6188
|
+
queryCount,
|
|
6189
|
+
error: error.message
|
|
6190
|
+
};
|
|
6191
|
+
}
|
|
6192
|
+
const batchRequestTemplateContent = requestTemplate ?? loadDefaultBatchRequestTemplate();
|
|
6193
|
+
const orchestratorTemplateContent = loadDefaultBatchOrchestratorTemplate();
|
|
6194
|
+
const subagentRootPath = subagentRoot ?? getSubagentRoot(vscodeCmd);
|
|
6195
|
+
const subagentDir = await findUnlockedSubagent(subagentRootPath);
|
|
6196
|
+
if (!subagentDir) {
|
|
6197
|
+
return {
|
|
6198
|
+
exitCode: 1,
|
|
6199
|
+
requestFiles,
|
|
6200
|
+
queryCount,
|
|
6201
|
+
error: "No unlocked subagents available. Provision additional subagents with: subagent code provision --subagents <desired_total>"
|
|
6202
|
+
};
|
|
6203
|
+
}
|
|
6204
|
+
subagentName = path21.basename(subagentDir);
|
|
6205
|
+
const chatId = Math.random().toString(16).slice(2, 10);
|
|
6206
|
+
const preparationResult = await prepareSubagentDirectory(
|
|
6207
|
+
subagentDir,
|
|
6208
|
+
resolvedPrompt,
|
|
6209
|
+
chatId,
|
|
6210
|
+
workspaceTemplate,
|
|
6211
|
+
dryRun
|
|
6212
|
+
);
|
|
6213
|
+
if (preparationResult !== 0) {
|
|
6214
|
+
return {
|
|
6215
|
+
exitCode: preparationResult,
|
|
6216
|
+
subagentName,
|
|
6217
|
+
requestFiles,
|
|
6218
|
+
queryCount,
|
|
6219
|
+
error: "Failed to prepare subagent workspace"
|
|
6220
|
+
};
|
|
6221
|
+
}
|
|
6222
|
+
let attachments;
|
|
6223
|
+
try {
|
|
6224
|
+
attachments = await resolveAttachments(extraAttachments);
|
|
6225
|
+
} catch (attachmentError) {
|
|
6226
|
+
return {
|
|
6227
|
+
exitCode: 1,
|
|
6228
|
+
subagentName,
|
|
6229
|
+
requestFiles,
|
|
6230
|
+
queryCount,
|
|
6231
|
+
error: attachmentError.message
|
|
6232
|
+
};
|
|
6233
|
+
}
|
|
6234
|
+
const timestamp = generateTimestamp();
|
|
6235
|
+
const messagesDir = path21.join(subagentDir, "messages");
|
|
6236
|
+
requestFiles = userQueries.map(
|
|
6237
|
+
(_, index) => path21.join(messagesDir, `${timestamp}_${index}_req.md`)
|
|
6238
|
+
);
|
|
6239
|
+
const responseTmpFiles = userQueries.map(
|
|
6240
|
+
(_, index) => path21.join(messagesDir, `${timestamp}_${index}_res.tmp.md`)
|
|
6241
|
+
);
|
|
6242
|
+
responseFilesFinal = userQueries.map(
|
|
6243
|
+
(_, index) => path21.join(messagesDir, `${timestamp}_${index}_res.md`)
|
|
6244
|
+
);
|
|
6245
|
+
const orchestratorFile = path21.join(messagesDir, `${timestamp}_orchestrator.md`);
|
|
6246
|
+
if (!dryRun) {
|
|
6247
|
+
await Promise.all(
|
|
6248
|
+
userQueries.map((query, index) => {
|
|
6249
|
+
const reqFile = requestFiles[index];
|
|
6250
|
+
const tmpFile = responseTmpFiles[index];
|
|
6251
|
+
const finalFile = responseFilesFinal[index];
|
|
6252
|
+
return writeFile6(
|
|
6253
|
+
reqFile,
|
|
6254
|
+
createBatchRequestPrompt(query, tmpFile, finalFile, batchRequestTemplateContent),
|
|
6255
|
+
{ encoding: "utf8" }
|
|
6256
|
+
);
|
|
6257
|
+
})
|
|
6258
|
+
);
|
|
6259
|
+
const orchestratorContent = createBatchOrchestratorPrompt(
|
|
6260
|
+
requestFiles,
|
|
6261
|
+
responseFilesFinal,
|
|
6262
|
+
orchestratorTemplateContent
|
|
6263
|
+
);
|
|
6264
|
+
await writeFile6(orchestratorFile, orchestratorContent, { encoding: "utf8" });
|
|
6265
|
+
}
|
|
6266
|
+
const chatAttachments = [orchestratorFile, ...attachments];
|
|
6267
|
+
const orchestratorUri = pathToFileUri2(orchestratorFile);
|
|
6268
|
+
const chatInstruction = `Follow instructions in [${timestamp}_orchestrator.md](${orchestratorUri}). Use #runSubagent tool.`;
|
|
6269
|
+
if (dryRun) {
|
|
6270
|
+
return {
|
|
6271
|
+
exitCode: 0,
|
|
6272
|
+
subagentName,
|
|
6273
|
+
requestFiles,
|
|
6274
|
+
responseFiles: wait ? responseFilesFinal : void 0,
|
|
6275
|
+
queryCount
|
|
6276
|
+
};
|
|
6277
|
+
}
|
|
6278
|
+
const launchSuccess = await launchVsCodeWithBatchChat(
|
|
6279
|
+
subagentDir,
|
|
6280
|
+
chatId,
|
|
6281
|
+
chatAttachments,
|
|
6282
|
+
chatInstruction,
|
|
6283
|
+
vscodeCmd
|
|
6284
|
+
);
|
|
6285
|
+
if (!launchSuccess) {
|
|
6286
|
+
return {
|
|
6287
|
+
exitCode: 1,
|
|
6288
|
+
subagentName,
|
|
6289
|
+
requestFiles,
|
|
6290
|
+
queryCount,
|
|
6291
|
+
error: "Failed to launch VS Code for batch dispatch"
|
|
6292
|
+
};
|
|
6293
|
+
}
|
|
6294
|
+
if (!wait) {
|
|
6295
|
+
return {
|
|
6296
|
+
exitCode: 0,
|
|
6297
|
+
subagentName,
|
|
6298
|
+
requestFiles,
|
|
6299
|
+
queryCount
|
|
6300
|
+
};
|
|
6301
|
+
}
|
|
6302
|
+
const responsesCompleted = await waitForBatchResponses(responseFilesFinal, 1e3, silent);
|
|
6303
|
+
if (!responsesCompleted) {
|
|
6304
|
+
return {
|
|
6305
|
+
exitCode: 1,
|
|
6306
|
+
subagentName,
|
|
6307
|
+
requestFiles,
|
|
6308
|
+
responseFiles: responseFilesFinal,
|
|
6309
|
+
queryCount,
|
|
6310
|
+
error: "Timed out waiting for batch responses"
|
|
6311
|
+
};
|
|
6312
|
+
}
|
|
6313
|
+
await removeSubagentLock(subagentDir);
|
|
6314
|
+
return {
|
|
6315
|
+
exitCode: 0,
|
|
6316
|
+
subagentName,
|
|
6317
|
+
requestFiles,
|
|
6318
|
+
responseFiles: responseFilesFinal,
|
|
6319
|
+
queryCount
|
|
6320
|
+
};
|
|
6321
|
+
} catch (error) {
|
|
6322
|
+
return {
|
|
6323
|
+
exitCode: 1,
|
|
6324
|
+
subagentName,
|
|
6325
|
+
requestFiles,
|
|
6326
|
+
responseFiles: responseFilesFinal.length > 0 ? responseFilesFinal : void 0,
|
|
6327
|
+
queryCount,
|
|
6328
|
+
error: error.message
|
|
6329
|
+
};
|
|
6330
|
+
}
|
|
6331
|
+
}
|
|
6332
|
+
|
|
6333
|
+
// src/evaluation/providers/vscode/dispatch/provision.ts
|
|
6334
|
+
import { writeFile as writeFile7 } from "node:fs/promises";
|
|
6335
|
+
import path22 from "node:path";
|
|
6336
|
+
var DEFAULT_WORKSPACE_TEMPLATE2 = {
|
|
6337
|
+
folders: [
|
|
6338
|
+
{
|
|
6339
|
+
path: "."
|
|
6340
|
+
}
|
|
6341
|
+
],
|
|
6342
|
+
settings: {
|
|
6343
|
+
"chat.modeFilesLocations": {
|
|
6344
|
+
".github/agents/**/*.md": true
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6347
|
+
};
|
|
6348
|
+
var DEFAULT_WAKEUP_CONTENT2 = `---
|
|
6349
|
+
description: 'Wake-up Signal'
|
|
6350
|
+
tools: ['edit', 'runNotebooks', 'search', 'new', 'runCommands', 'runTasks', 'usages', 'vscodeAPI', 'problems', 'changes', 'testFailure', 'openSimpleBrowser', 'fetch', 'githubRepo']
|
|
6351
|
+
model: GPT-4.1 (copilot)
|
|
6352
|
+
---`;
|
|
6353
|
+
async function provisionSubagents(options) {
|
|
6354
|
+
const {
|
|
6355
|
+
targetRoot,
|
|
6356
|
+
subagents,
|
|
6357
|
+
lockName = DEFAULT_LOCK_NAME,
|
|
6358
|
+
force = false,
|
|
6359
|
+
dryRun = false,
|
|
6360
|
+
workspaceTemplate = DEFAULT_WORKSPACE_TEMPLATE2,
|
|
6361
|
+
wakeupContent = DEFAULT_WAKEUP_CONTENT2
|
|
6362
|
+
} = options;
|
|
6363
|
+
if (!Number.isInteger(subagents) || subagents < 1) {
|
|
6364
|
+
throw new Error("subagents must be a positive integer");
|
|
6365
|
+
}
|
|
6366
|
+
const targetPath = path22.resolve(targetRoot);
|
|
6367
|
+
if (!dryRun) {
|
|
6368
|
+
await ensureDir(targetPath);
|
|
6369
|
+
}
|
|
6370
|
+
let highestNumber = 0;
|
|
6371
|
+
const lockedSubagents = /* @__PURE__ */ new Set();
|
|
6372
|
+
const existingSubagents = [];
|
|
6373
|
+
if (await pathExists(targetPath)) {
|
|
6374
|
+
const entries = await readDirEntries(targetPath);
|
|
6375
|
+
for (const entry of entries) {
|
|
6376
|
+
if (!entry.isDirectory || !entry.name.startsWith("subagent-")) {
|
|
6377
|
+
continue;
|
|
6378
|
+
}
|
|
6379
|
+
const suffix = entry.name.split("-")[1];
|
|
6380
|
+
if (!suffix) continue;
|
|
6381
|
+
const parsed = Number.parseInt(suffix, 10);
|
|
6382
|
+
if (!Number.isInteger(parsed)) {
|
|
6383
|
+
continue;
|
|
6384
|
+
}
|
|
6385
|
+
highestNumber = Math.max(highestNumber, parsed);
|
|
6386
|
+
const lockFile = path22.join(entry.absolutePath, lockName);
|
|
6387
|
+
const locked = await pathExists(lockFile);
|
|
6388
|
+
if (locked) {
|
|
6389
|
+
lockedSubagents.add(entry.absolutePath);
|
|
6390
|
+
}
|
|
6391
|
+
existingSubagents.push({ number: parsed, absolutePath: entry.absolutePath });
|
|
6392
|
+
}
|
|
6393
|
+
existingSubagents.sort((a, b) => a.number - b.number);
|
|
6394
|
+
}
|
|
6395
|
+
const created = [];
|
|
6396
|
+
const skippedExisting = [];
|
|
6397
|
+
let subagentsProvisioned = 0;
|
|
6398
|
+
for (const subagent of existingSubagents) {
|
|
6399
|
+
if (subagentsProvisioned >= subagents) {
|
|
6400
|
+
break;
|
|
6401
|
+
}
|
|
6402
|
+
const subagentDir = subagent.absolutePath;
|
|
6403
|
+
const githubAgentsDir = path22.join(subagentDir, ".github", "agents");
|
|
6404
|
+
const lockFile = path22.join(subagentDir, lockName);
|
|
6405
|
+
const workspaceDst = path22.join(subagentDir, `${path22.basename(subagentDir)}.code-workspace`);
|
|
6406
|
+
const wakeupDst = path22.join(githubAgentsDir, "wakeup.md");
|
|
6407
|
+
const isLocked = await pathExists(lockFile);
|
|
6408
|
+
if (isLocked && !force) {
|
|
6409
|
+
continue;
|
|
6410
|
+
}
|
|
6411
|
+
if (isLocked && force) {
|
|
6412
|
+
if (!dryRun) {
|
|
6413
|
+
await removeIfExists(lockFile);
|
|
6414
|
+
await ensureDir(githubAgentsDir);
|
|
6415
|
+
await writeFile7(workspaceDst, JSON.stringify(workspaceTemplate, null, 2), "utf8");
|
|
6416
|
+
await writeFile7(wakeupDst, wakeupContent, "utf8");
|
|
6417
|
+
}
|
|
6418
|
+
created.push(subagentDir);
|
|
6419
|
+
lockedSubagents.delete(subagentDir);
|
|
6420
|
+
subagentsProvisioned += 1;
|
|
6421
|
+
continue;
|
|
6422
|
+
}
|
|
6423
|
+
if (!isLocked && force) {
|
|
6424
|
+
if (!dryRun) {
|
|
6425
|
+
await ensureDir(githubAgentsDir);
|
|
6426
|
+
await writeFile7(workspaceDst, JSON.stringify(workspaceTemplate, null, 2), "utf8");
|
|
6427
|
+
await writeFile7(wakeupDst, wakeupContent, "utf8");
|
|
6428
|
+
}
|
|
6429
|
+
created.push(subagentDir);
|
|
6430
|
+
subagentsProvisioned += 1;
|
|
6431
|
+
continue;
|
|
6432
|
+
}
|
|
6433
|
+
if (!dryRun && !await pathExists(workspaceDst)) {
|
|
6434
|
+
await ensureDir(githubAgentsDir);
|
|
6435
|
+
await writeFile7(workspaceDst, JSON.stringify(workspaceTemplate, null, 2), "utf8");
|
|
6436
|
+
await writeFile7(wakeupDst, wakeupContent, "utf8");
|
|
6437
|
+
}
|
|
6438
|
+
skippedExisting.push(subagentDir);
|
|
6439
|
+
subagentsProvisioned += 1;
|
|
6440
|
+
}
|
|
6441
|
+
let nextIndex = highestNumber;
|
|
6442
|
+
while (subagentsProvisioned < subagents) {
|
|
6443
|
+
nextIndex += 1;
|
|
6444
|
+
const subagentDir = path22.join(targetPath, `subagent-${nextIndex}`);
|
|
6445
|
+
const githubAgentsDir = path22.join(subagentDir, ".github", "agents");
|
|
6446
|
+
const workspaceDst = path22.join(subagentDir, `${path22.basename(subagentDir)}.code-workspace`);
|
|
6447
|
+
const wakeupDst = path22.join(githubAgentsDir, "wakeup.md");
|
|
6448
|
+
if (!dryRun) {
|
|
6449
|
+
await ensureDir(subagentDir);
|
|
6450
|
+
await ensureDir(githubAgentsDir);
|
|
6451
|
+
await writeFile7(workspaceDst, JSON.stringify(workspaceTemplate, null, 2), "utf8");
|
|
6452
|
+
await writeFile7(wakeupDst, wakeupContent, "utf8");
|
|
6453
|
+
}
|
|
6454
|
+
created.push(subagentDir);
|
|
6455
|
+
subagentsProvisioned += 1;
|
|
6456
|
+
}
|
|
6457
|
+
return {
|
|
6458
|
+
created,
|
|
6459
|
+
skippedExisting,
|
|
6460
|
+
skippedLocked: Array.from(lockedSubagents).sort()
|
|
6461
|
+
};
|
|
6462
|
+
}
|
|
5407
6463
|
|
|
5408
6464
|
// src/evaluation/providers/vscode-templates.ts
|
|
5409
6465
|
var AGENTV_REQUEST_TEMPLATE = `[[ ## task ## ]]
|
|
@@ -5440,7 +6496,7 @@ var AGENTV_BATCH_REQUEST_TEMPLATE = `[[ ## task ## ]]
|
|
|
5440
6496
|
3. Do not unlock the workspace from this request; batch orchestration will handle unlocking after all responses are ready.
|
|
5441
6497
|
`;
|
|
5442
6498
|
|
|
5443
|
-
// src/evaluation/providers/vscode.ts
|
|
6499
|
+
// src/evaluation/providers/vscode-provider.ts
|
|
5444
6500
|
var VSCodeProvider = class {
|
|
5445
6501
|
id;
|
|
5446
6502
|
kind;
|
|
@@ -5572,8 +6628,8 @@ function buildMandatoryPrereadBlock2(guidelineFiles, attachmentFiles) {
|
|
|
5572
6628
|
return "";
|
|
5573
6629
|
}
|
|
5574
6630
|
const buildList = (files) => files.map((absolutePath) => {
|
|
5575
|
-
const fileName =
|
|
5576
|
-
const fileUri =
|
|
6631
|
+
const fileName = path23.basename(absolutePath);
|
|
6632
|
+
const fileUri = pathToFileUri3(absolutePath);
|
|
5577
6633
|
return `* [${fileName}](${fileUri})`;
|
|
5578
6634
|
});
|
|
5579
6635
|
const sections = [];
|
|
@@ -5597,8 +6653,8 @@ function collectGuidelineFiles2(attachments, guidelinePatterns) {
|
|
|
5597
6653
|
}
|
|
5598
6654
|
const unique = /* @__PURE__ */ new Map();
|
|
5599
6655
|
for (const attachment of attachments) {
|
|
5600
|
-
const absolutePath =
|
|
5601
|
-
const normalized = absolutePath.split(
|
|
6656
|
+
const absolutePath = path23.resolve(attachment);
|
|
6657
|
+
const normalized = absolutePath.split(path23.sep).join("/");
|
|
5602
6658
|
if (isGuidelineFile(normalized, guidelinePatterns)) {
|
|
5603
6659
|
if (!unique.has(absolutePath)) {
|
|
5604
6660
|
unique.set(absolutePath, absolutePath);
|
|
@@ -5613,15 +6669,15 @@ function collectAttachmentFiles(attachments) {
|
|
|
5613
6669
|
}
|
|
5614
6670
|
const unique = /* @__PURE__ */ new Map();
|
|
5615
6671
|
for (const attachment of attachments) {
|
|
5616
|
-
const absolutePath =
|
|
6672
|
+
const absolutePath = path23.resolve(attachment);
|
|
5617
6673
|
if (!unique.has(absolutePath)) {
|
|
5618
6674
|
unique.set(absolutePath, absolutePath);
|
|
5619
6675
|
}
|
|
5620
6676
|
}
|
|
5621
6677
|
return Array.from(unique.values());
|
|
5622
6678
|
}
|
|
5623
|
-
function
|
|
5624
|
-
const absolutePath =
|
|
6679
|
+
function pathToFileUri3(filePath) {
|
|
6680
|
+
const absolutePath = path23.isAbsolute(filePath) ? filePath : path23.resolve(filePath);
|
|
5625
6681
|
const normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
5626
6682
|
if (/^[a-zA-Z]:\//.test(normalizedPath)) {
|
|
5627
6683
|
return `file:///${normalizedPath}`;
|
|
@@ -5634,7 +6690,7 @@ function normalizeAttachments(attachments) {
|
|
|
5634
6690
|
}
|
|
5635
6691
|
const deduped = /* @__PURE__ */ new Set();
|
|
5636
6692
|
for (const attachment of attachments) {
|
|
5637
|
-
deduped.add(
|
|
6693
|
+
deduped.add(path23.resolve(attachment));
|
|
5638
6694
|
}
|
|
5639
6695
|
return Array.from(deduped);
|
|
5640
6696
|
}
|
|
@@ -5643,7 +6699,7 @@ function mergeAttachments(all) {
|
|
|
5643
6699
|
for (const list of all) {
|
|
5644
6700
|
if (!list) continue;
|
|
5645
6701
|
for (const inputFile of list) {
|
|
5646
|
-
deduped.add(
|
|
6702
|
+
deduped.add(path23.resolve(inputFile));
|
|
5647
6703
|
}
|
|
5648
6704
|
}
|
|
5649
6705
|
return deduped.size > 0 ? Array.from(deduped) : void 0;
|
|
@@ -5690,9 +6746,9 @@ total unlocked subagents available: ${result.created.length + result.skippedExis
|
|
|
5690
6746
|
}
|
|
5691
6747
|
|
|
5692
6748
|
// src/evaluation/providers/targets-file.ts
|
|
5693
|
-
import { constants as
|
|
5694
|
-
import { access as
|
|
5695
|
-
import
|
|
6749
|
+
import { constants as constants4 } from "node:fs";
|
|
6750
|
+
import { access as access4, readFile as readFile9 } from "node:fs/promises";
|
|
6751
|
+
import path24 from "node:path";
|
|
5696
6752
|
import { parse as parse3 } from "yaml";
|
|
5697
6753
|
function isRecord(value) {
|
|
5698
6754
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -5722,18 +6778,18 @@ function assertTargetDefinition(value, index, filePath) {
|
|
|
5722
6778
|
}
|
|
5723
6779
|
async function fileExists3(filePath) {
|
|
5724
6780
|
try {
|
|
5725
|
-
await
|
|
6781
|
+
await access4(filePath, constants4.F_OK);
|
|
5726
6782
|
return true;
|
|
5727
6783
|
} catch {
|
|
5728
6784
|
return false;
|
|
5729
6785
|
}
|
|
5730
6786
|
}
|
|
5731
6787
|
async function readTargetDefinitions(filePath) {
|
|
5732
|
-
const absolutePath =
|
|
6788
|
+
const absolutePath = path24.resolve(filePath);
|
|
5733
6789
|
if (!await fileExists3(absolutePath)) {
|
|
5734
6790
|
throw new Error(`targets.yaml not found at ${absolutePath}`);
|
|
5735
6791
|
}
|
|
5736
|
-
const raw = await
|
|
6792
|
+
const raw = await readFile9(absolutePath, "utf8");
|
|
5737
6793
|
const parsed = parse3(raw);
|
|
5738
6794
|
if (!isRecord(parsed)) {
|
|
5739
6795
|
throw new Error(`targets.yaml at ${absolutePath} must be a YAML object with a 'targets' field`);
|
|
@@ -5897,10 +6953,10 @@ async function execFileWithStdinBun(argv, stdinPayload, options) {
|
|
|
5897
6953
|
}
|
|
5898
6954
|
}
|
|
5899
6955
|
async function execFileWithStdinNode(argv, stdinPayload, options) {
|
|
5900
|
-
const { spawn:
|
|
6956
|
+
const { spawn: spawn5 } = await import("node:child_process");
|
|
5901
6957
|
return new Promise((resolve, reject) => {
|
|
5902
6958
|
const [cmd, ...args] = argv;
|
|
5903
|
-
const child =
|
|
6959
|
+
const child = spawn5(cmd, args, {
|
|
5904
6960
|
cwd: options.cwd,
|
|
5905
6961
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5906
6962
|
// Merge additional env vars with process.env
|
|
@@ -5940,21 +6996,21 @@ async function execFileWithStdinNode(argv, stdinPayload, options) {
|
|
|
5940
6996
|
});
|
|
5941
6997
|
}
|
|
5942
6998
|
async function execShellWithStdin(command, stdinPayload, options = {}) {
|
|
5943
|
-
const { mkdir:
|
|
6999
|
+
const { mkdir: mkdir8, readFile: readFile10, rm: rm5, writeFile: writeFile8 } = await import("node:fs/promises");
|
|
5944
7000
|
const { tmpdir: tmpdir4 } = await import("node:os");
|
|
5945
|
-
const
|
|
7001
|
+
const path26 = await import("node:path");
|
|
5946
7002
|
const { randomUUID: randomUUID4 } = await import("node:crypto");
|
|
5947
|
-
const dir =
|
|
5948
|
-
await
|
|
5949
|
-
const stdinPath =
|
|
5950
|
-
const stdoutPath =
|
|
5951
|
-
const stderrPath =
|
|
5952
|
-
await
|
|
7003
|
+
const dir = path26.join(tmpdir4(), `agentv-exec-${randomUUID4()}`);
|
|
7004
|
+
await mkdir8(dir, { recursive: true });
|
|
7005
|
+
const stdinPath = path26.join(dir, "stdin.txt");
|
|
7006
|
+
const stdoutPath = path26.join(dir, "stdout.txt");
|
|
7007
|
+
const stderrPath = path26.join(dir, "stderr.txt");
|
|
7008
|
+
await writeFile8(stdinPath, stdinPayload, "utf8");
|
|
5953
7009
|
const wrappedCommand = process.platform === "win32" ? `(${command}) < ${shellEscapePath(stdinPath)} > ${shellEscapePath(stdoutPath)} 2> ${shellEscapePath(stderrPath)}` : `(${command}) < ${shellEscapePath(stdinPath)} > ${shellEscapePath(stdoutPath)} 2> ${shellEscapePath(stderrPath)}`;
|
|
5954
|
-
const { spawn:
|
|
7010
|
+
const { spawn: spawn5 } = await import("node:child_process");
|
|
5955
7011
|
try {
|
|
5956
7012
|
const exitCode = await new Promise((resolve, reject) => {
|
|
5957
|
-
const child =
|
|
7013
|
+
const child = spawn5(wrappedCommand, {
|
|
5958
7014
|
shell: true,
|
|
5959
7015
|
cwd: options.cwd,
|
|
5960
7016
|
stdio: ["ignore", "ignore", "ignore"],
|
|
@@ -5978,11 +7034,11 @@ async function execShellWithStdin(command, stdinPayload, options = {}) {
|
|
|
5978
7034
|
resolve(code ?? 0);
|
|
5979
7035
|
});
|
|
5980
7036
|
});
|
|
5981
|
-
const stdout = (await
|
|
5982
|
-
const stderr = (await
|
|
7037
|
+
const stdout = (await readFile10(stdoutPath, "utf8")).replace(/\r\n/g, "\n");
|
|
7038
|
+
const stderr = (await readFile10(stderrPath, "utf8")).replace(/\r\n/g, "\n");
|
|
5983
7039
|
return { stdout, stderr, exitCode };
|
|
5984
7040
|
} finally {
|
|
5985
|
-
await
|
|
7041
|
+
await rm5(dir, { recursive: true, force: true });
|
|
5986
7042
|
}
|
|
5987
7043
|
}
|
|
5988
7044
|
|
|
@@ -6251,7 +7307,7 @@ var CodeEvaluator = class {
|
|
|
6251
7307
|
outputMessages: context.outputMessages ?? null,
|
|
6252
7308
|
guidelineFiles: context.evalCase.guideline_paths,
|
|
6253
7309
|
inputFiles: context.evalCase.file_paths.filter(
|
|
6254
|
-
(
|
|
7310
|
+
(path26) => !context.evalCase.guideline_paths.includes(path26)
|
|
6255
7311
|
),
|
|
6256
7312
|
inputMessages: context.evalCase.input_messages,
|
|
6257
7313
|
traceSummary: context.traceSummary ?? null,
|
|
@@ -7171,115 +8227,115 @@ var FieldAccuracyEvaluator = class {
|
|
|
7171
8227
|
* Evaluate a single field against the expected value.
|
|
7172
8228
|
*/
|
|
7173
8229
|
evaluateField(fieldConfig, candidateData, expectedData) {
|
|
7174
|
-
const { path:
|
|
7175
|
-
const candidateValue = resolvePath(candidateData,
|
|
7176
|
-
const expectedValue = resolvePath(expectedData,
|
|
8230
|
+
const { path: path26, match, required = true, weight = 1 } = fieldConfig;
|
|
8231
|
+
const candidateValue = resolvePath(candidateData, path26);
|
|
8232
|
+
const expectedValue = resolvePath(expectedData, path26);
|
|
7177
8233
|
if (expectedValue === void 0) {
|
|
7178
8234
|
return {
|
|
7179
|
-
path:
|
|
8235
|
+
path: path26,
|
|
7180
8236
|
score: 1,
|
|
7181
8237
|
// No expected value means no comparison needed
|
|
7182
8238
|
weight,
|
|
7183
8239
|
hit: true,
|
|
7184
|
-
message: `${
|
|
8240
|
+
message: `${path26}: no expected value`
|
|
7185
8241
|
};
|
|
7186
8242
|
}
|
|
7187
8243
|
if (candidateValue === void 0) {
|
|
7188
8244
|
if (required) {
|
|
7189
8245
|
return {
|
|
7190
|
-
path:
|
|
8246
|
+
path: path26,
|
|
7191
8247
|
score: 0,
|
|
7192
8248
|
weight,
|
|
7193
8249
|
hit: false,
|
|
7194
|
-
message: `${
|
|
8250
|
+
message: `${path26} (required, missing)`
|
|
7195
8251
|
};
|
|
7196
8252
|
}
|
|
7197
8253
|
return {
|
|
7198
|
-
path:
|
|
8254
|
+
path: path26,
|
|
7199
8255
|
score: 1,
|
|
7200
8256
|
// Don't penalize missing optional fields
|
|
7201
8257
|
weight: 0,
|
|
7202
8258
|
// Zero weight means it won't affect the score
|
|
7203
8259
|
hit: true,
|
|
7204
|
-
message: `${
|
|
8260
|
+
message: `${path26}: optional field missing`
|
|
7205
8261
|
};
|
|
7206
8262
|
}
|
|
7207
8263
|
switch (match) {
|
|
7208
8264
|
case "exact":
|
|
7209
|
-
return this.compareExact(
|
|
8265
|
+
return this.compareExact(path26, candidateValue, expectedValue, weight);
|
|
7210
8266
|
case "numeric_tolerance":
|
|
7211
8267
|
return this.compareNumericTolerance(
|
|
7212
|
-
|
|
8268
|
+
path26,
|
|
7213
8269
|
candidateValue,
|
|
7214
8270
|
expectedValue,
|
|
7215
8271
|
fieldConfig,
|
|
7216
8272
|
weight
|
|
7217
8273
|
);
|
|
7218
8274
|
case "date":
|
|
7219
|
-
return this.compareDate(
|
|
8275
|
+
return this.compareDate(path26, candidateValue, expectedValue, fieldConfig, weight);
|
|
7220
8276
|
default:
|
|
7221
8277
|
return {
|
|
7222
|
-
path:
|
|
8278
|
+
path: path26,
|
|
7223
8279
|
score: 0,
|
|
7224
8280
|
weight,
|
|
7225
8281
|
hit: false,
|
|
7226
|
-
message: `${
|
|
8282
|
+
message: `${path26}: unknown match type "${match}"`
|
|
7227
8283
|
};
|
|
7228
8284
|
}
|
|
7229
8285
|
}
|
|
7230
8286
|
/**
|
|
7231
8287
|
* Exact equality comparison.
|
|
7232
8288
|
*/
|
|
7233
|
-
compareExact(
|
|
8289
|
+
compareExact(path26, candidateValue, expectedValue, weight) {
|
|
7234
8290
|
if (deepEqual(candidateValue, expectedValue)) {
|
|
7235
8291
|
return {
|
|
7236
|
-
path:
|
|
8292
|
+
path: path26,
|
|
7237
8293
|
score: 1,
|
|
7238
8294
|
weight,
|
|
7239
8295
|
hit: true,
|
|
7240
|
-
message:
|
|
8296
|
+
message: path26
|
|
7241
8297
|
};
|
|
7242
8298
|
}
|
|
7243
8299
|
if (typeof candidateValue !== typeof expectedValue) {
|
|
7244
8300
|
return {
|
|
7245
|
-
path:
|
|
8301
|
+
path: path26,
|
|
7246
8302
|
score: 0,
|
|
7247
8303
|
weight,
|
|
7248
8304
|
hit: false,
|
|
7249
|
-
message: `${
|
|
8305
|
+
message: `${path26} (type mismatch: got ${typeof candidateValue}, expected ${typeof expectedValue})`
|
|
7250
8306
|
};
|
|
7251
8307
|
}
|
|
7252
8308
|
return {
|
|
7253
|
-
path:
|
|
8309
|
+
path: path26,
|
|
7254
8310
|
score: 0,
|
|
7255
8311
|
weight,
|
|
7256
8312
|
hit: false,
|
|
7257
|
-
message: `${
|
|
8313
|
+
message: `${path26} (value mismatch)`
|
|
7258
8314
|
};
|
|
7259
8315
|
}
|
|
7260
8316
|
/**
|
|
7261
8317
|
* Numeric comparison with absolute or relative tolerance.
|
|
7262
8318
|
*/
|
|
7263
|
-
compareNumericTolerance(
|
|
8319
|
+
compareNumericTolerance(path26, candidateValue, expectedValue, fieldConfig, weight) {
|
|
7264
8320
|
const { tolerance = 0, relative = false } = fieldConfig;
|
|
7265
8321
|
const candidateNum = toNumber(candidateValue);
|
|
7266
8322
|
const expectedNum = toNumber(expectedValue);
|
|
7267
8323
|
if (candidateNum === null || expectedNum === null) {
|
|
7268
8324
|
return {
|
|
7269
|
-
path:
|
|
8325
|
+
path: path26,
|
|
7270
8326
|
score: 0,
|
|
7271
8327
|
weight,
|
|
7272
8328
|
hit: false,
|
|
7273
|
-
message: `${
|
|
8329
|
+
message: `${path26} (non-numeric value)`
|
|
7274
8330
|
};
|
|
7275
8331
|
}
|
|
7276
8332
|
if (!Number.isFinite(candidateNum) || !Number.isFinite(expectedNum)) {
|
|
7277
8333
|
return {
|
|
7278
|
-
path:
|
|
8334
|
+
path: path26,
|
|
7279
8335
|
score: 0,
|
|
7280
8336
|
weight,
|
|
7281
8337
|
hit: false,
|
|
7282
|
-
message: `${
|
|
8338
|
+
message: `${path26} (invalid numeric value)`
|
|
7283
8339
|
};
|
|
7284
8340
|
}
|
|
7285
8341
|
const diff = Math.abs(candidateNum - expectedNum);
|
|
@@ -7292,61 +8348,61 @@ var FieldAccuracyEvaluator = class {
|
|
|
7292
8348
|
}
|
|
7293
8349
|
if (withinTolerance) {
|
|
7294
8350
|
return {
|
|
7295
|
-
path:
|
|
8351
|
+
path: path26,
|
|
7296
8352
|
score: 1,
|
|
7297
8353
|
weight,
|
|
7298
8354
|
hit: true,
|
|
7299
|
-
message: `${
|
|
8355
|
+
message: `${path26} (within tolerance: diff=${diff.toFixed(2)})`
|
|
7300
8356
|
};
|
|
7301
8357
|
}
|
|
7302
8358
|
return {
|
|
7303
|
-
path:
|
|
8359
|
+
path: path26,
|
|
7304
8360
|
score: 0,
|
|
7305
8361
|
weight,
|
|
7306
8362
|
hit: false,
|
|
7307
|
-
message: `${
|
|
8363
|
+
message: `${path26} (outside tolerance: diff=${diff.toFixed(2)}, tolerance=${tolerance})`
|
|
7308
8364
|
};
|
|
7309
8365
|
}
|
|
7310
8366
|
/**
|
|
7311
8367
|
* Date comparison with format normalization.
|
|
7312
8368
|
*/
|
|
7313
|
-
compareDate(
|
|
8369
|
+
compareDate(path26, candidateValue, expectedValue, fieldConfig, weight) {
|
|
7314
8370
|
const formats = fieldConfig.formats ?? DEFAULT_DATE_FORMATS;
|
|
7315
8371
|
const candidateDate = parseDate(String(candidateValue), formats);
|
|
7316
8372
|
const expectedDate = parseDate(String(expectedValue), formats);
|
|
7317
8373
|
if (candidateDate === null) {
|
|
7318
8374
|
return {
|
|
7319
|
-
path:
|
|
8375
|
+
path: path26,
|
|
7320
8376
|
score: 0,
|
|
7321
8377
|
weight,
|
|
7322
8378
|
hit: false,
|
|
7323
|
-
message: `${
|
|
8379
|
+
message: `${path26} (unparseable candidate date)`
|
|
7324
8380
|
};
|
|
7325
8381
|
}
|
|
7326
8382
|
if (expectedDate === null) {
|
|
7327
8383
|
return {
|
|
7328
|
-
path:
|
|
8384
|
+
path: path26,
|
|
7329
8385
|
score: 0,
|
|
7330
8386
|
weight,
|
|
7331
8387
|
hit: false,
|
|
7332
|
-
message: `${
|
|
8388
|
+
message: `${path26} (unparseable expected date)`
|
|
7333
8389
|
};
|
|
7334
8390
|
}
|
|
7335
8391
|
if (candidateDate.getFullYear() === expectedDate.getFullYear() && candidateDate.getMonth() === expectedDate.getMonth() && candidateDate.getDate() === expectedDate.getDate()) {
|
|
7336
8392
|
return {
|
|
7337
|
-
path:
|
|
8393
|
+
path: path26,
|
|
7338
8394
|
score: 1,
|
|
7339
8395
|
weight,
|
|
7340
8396
|
hit: true,
|
|
7341
|
-
message:
|
|
8397
|
+
message: path26
|
|
7342
8398
|
};
|
|
7343
8399
|
}
|
|
7344
8400
|
return {
|
|
7345
|
-
path:
|
|
8401
|
+
path: path26,
|
|
7346
8402
|
score: 0,
|
|
7347
8403
|
weight,
|
|
7348
8404
|
hit: false,
|
|
7349
|
-
message: `${
|
|
8405
|
+
message: `${path26} (date mismatch: got ${formatDateISO(candidateDate)}, expected ${formatDateISO(expectedDate)})`
|
|
7350
8406
|
};
|
|
7351
8407
|
}
|
|
7352
8408
|
/**
|
|
@@ -7386,11 +8442,11 @@ var FieldAccuracyEvaluator = class {
|
|
|
7386
8442
|
};
|
|
7387
8443
|
}
|
|
7388
8444
|
};
|
|
7389
|
-
function resolvePath(obj,
|
|
7390
|
-
if (!
|
|
8445
|
+
function resolvePath(obj, path26) {
|
|
8446
|
+
if (!path26 || !obj) {
|
|
7391
8447
|
return void 0;
|
|
7392
8448
|
}
|
|
7393
|
-
const parts =
|
|
8449
|
+
const parts = path26.split(/\.|\[|\]/).filter((p) => p.length > 0);
|
|
7394
8450
|
let current = obj;
|
|
7395
8451
|
for (const part of parts) {
|
|
7396
8452
|
if (current === null || current === void 0) {
|
|
@@ -7910,7 +8966,7 @@ var ToolTrajectoryEvaluator = class {
|
|
|
7910
8966
|
|
|
7911
8967
|
// src/evaluation/orchestrator.ts
|
|
7912
8968
|
import { createHash } from "node:crypto";
|
|
7913
|
-
import
|
|
8969
|
+
import path25 from "node:path";
|
|
7914
8970
|
import micromatch4 from "micromatch";
|
|
7915
8971
|
|
|
7916
8972
|
// ../../node_modules/.bun/yocto-queue@1.2.2/node_modules/yocto-queue/index.js
|
|
@@ -8713,7 +9769,7 @@ async function runEvaluatorList(options) {
|
|
|
8713
9769
|
});
|
|
8714
9770
|
}
|
|
8715
9771
|
if (evaluator.type === "composite") {
|
|
8716
|
-
const evalFileDir = evalCase.guideline_paths[0] ?
|
|
9772
|
+
const evalFileDir = evalCase.guideline_paths[0] ? path25.dirname(evalCase.guideline_paths[0]) : process.cwd();
|
|
8717
9773
|
const createEvaluator = (memberConfig) => {
|
|
8718
9774
|
switch (memberConfig.type) {
|
|
8719
9775
|
case "llm_judge":
|
|
@@ -9069,7 +10125,7 @@ async function executePromptTemplate(script, context, config, timeoutMs) {
|
|
|
9069
10125
|
};
|
|
9070
10126
|
const inputJson = JSON.stringify(toSnakeCaseDeep(payload), null, 2);
|
|
9071
10127
|
const scriptPath = script[script.length - 1];
|
|
9072
|
-
const cwd =
|
|
10128
|
+
const cwd = path25.dirname(scriptPath);
|
|
9073
10129
|
try {
|
|
9074
10130
|
const stdout = await executeScript(script, inputJson, timeoutMs, cwd);
|
|
9075
10131
|
const prompt = stdout.trim();
|