@dailephd/my-dev-kit-lab 0.8.2 → 0.9.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.
- package/CHANGELOG.md +19 -0
- package/README.md +19 -3
- package/benchmarks/contracts/agent-success-rate-project-profiles.json +33 -0
- package/benchmarks/contracts/agent-success-rate-tasks.json +671 -0
- package/benchmarks/projects/agent-success-inventory-node/.gitattributes +2 -0
- package/benchmarks/projects/agent-success-inventory-node/README.md +74 -0
- package/benchmarks/projects/agent-success-inventory-node/package.json +14 -0
- package/benchmarks/projects/agent-success-inventory-node/src/fulfillmentReport.js +38 -0
- package/benchmarks/projects/agent-success-inventory-node/src/index.js +26 -0
- package/benchmarks/projects/agent-success-inventory-node/src/inventoryStore.js +114 -0
- package/benchmarks/projects/agent-success-inventory-node/src/quantity.js +19 -0
- package/benchmarks/projects/agent-success-inventory-node/src/reservationService.js +45 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/quantity-edge.check.mjs +51 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/quantity-primary.check.mjs +46 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/regression.check.mjs +115 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/report-edge.check.mjs +109 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/report-primary.check.mjs +47 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/reservation-edge.check.mjs +63 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/reservation-primary.check.mjs +44 -0
- package/benchmarks/projects/agent-success-task-board-node/.gitattributes +2 -0
- package/benchmarks/projects/agent-success-task-board-node/README.md +77 -0
- package/benchmarks/projects/agent-success-task-board-node/package.json +14 -0
- package/benchmarks/projects/agent-success-task-board-node/src/index.js +24 -0
- package/benchmarks/projects/agent-success-task-board-node/src/projectSummary.js +15 -0
- package/benchmarks/projects/agent-success-task-board-node/src/taskService.js +58 -0
- package/benchmarks/projects/agent-success-task-board-node/src/taskStore.js +58 -0
- package/benchmarks/projects/agent-success-task-board-node/src/validation.js +31 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/import-edge.check.mjs +65 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/import-primary.check.mjs +31 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/regression.check.mjs +107 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/summary-edge.check.mjs +67 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/summary-primary.check.mjs +54 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/title-edge.check.mjs +39 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/title-primary.check.mjs +29 -0
- package/dist/src/agents/adapters/claudeAdapter.js +6 -3
- package/dist/src/agents/adapters/codexAdapter.js +51 -10
- package/dist/src/agents/index.js +1 -0
- package/dist/src/agents/normalizeAgentTelemetry.js +50 -0
- package/dist/src/agents/parseAgentTokenUsage.js +71 -49
- package/dist/src/agents/providerBuildObservation.js +97 -0
- package/dist/src/audits/core/projectInventory.js +1 -0
- package/dist/src/cli/help.js +44 -1
- package/dist/src/commands/runExperimentDescribeCommand.js +83 -1
- package/dist/src/commands/runExperimentRunCommand.js +309 -6
- package/dist/src/core/processTree.js +55 -0
- package/dist/src/core/runMeasuredCommand.js +108 -22
- package/dist/src/evaluation/agentSuccess/applyPatchToSandbox.js +37 -0
- package/dist/src/evaluation/agentSuccess/assessBaseline.js +23 -0
- package/dist/src/evaluation/agentSuccess/corpusTypes.js +74 -0
- package/dist/src/evaluation/agentSuccess/extractPatchCandidate.js +46 -0
- package/dist/src/evaluation/agentSuccess/index.js +14 -0
- package/dist/src/evaluation/agentSuccess/parseUnifiedDiff.js +210 -0
- package/dist/src/evaluation/agentSuccess/patchTypes.js +5 -0
- package/dist/src/evaluation/agentSuccess/readAgentSuccessCorpus.js +183 -0
- package/dist/src/evaluation/agentSuccess/runVerificationCheck.js +100 -0
- package/dist/src/evaluation/agentSuccess/taskPaths.js +34 -0
- package/dist/src/evaluation/agentSuccess/taskTypes.js +14 -0
- package/dist/src/evaluation/agentSuccess/validateAgentSuccessCorpus.js +307 -0
- package/dist/src/evaluation/agentSuccess/validateAgentSuccessTask.js +297 -0
- package/dist/src/evaluation/agentSuccess/validatePatchPolicy.js +66 -0
- package/dist/src/evaluation/agentSuccess/verificationTypes.js +1 -0
- package/dist/src/evaluation/benchmarkSandbox/createBenchmarkSandbox.js +127 -0
- package/dist/src/evaluation/benchmarkSandbox/errors.js +11 -0
- package/dist/src/evaluation/benchmarkSandbox/gitExecutor.js +62 -0
- package/dist/src/evaluation/benchmarkSandbox/index.js +8 -0
- package/dist/src/evaluation/benchmarkSandbox/minimalHostEnv.js +37 -0
- package/dist/src/evaluation/benchmarkSandbox/pathPolicy.js +49 -0
- package/dist/src/evaluation/benchmarkSandbox/removeBenchmarkSandbox.js +74 -0
- package/dist/src/evaluation/benchmarkSandbox/treeSnapshot.js +57 -0
- package/dist/src/evaluation/benchmarkSandbox/types.js +3 -0
- package/dist/src/evaluation/changeSet/captureChangeSet.js +98 -0
- package/dist/src/evaluation/changeSet/index.js +2 -0
- package/dist/src/evaluation/changeSet/types.js +9 -0
- package/dist/src/experiments/defaultRegistry.js +2 -0
- package/dist/src/experiments/plugins/agentSuccessRate/agentTaskProjection.js +12 -0
- package/dist/src/experiments/plugins/agentSuccessRate/analysis.js +425 -0
- package/dist/src/experiments/plugins/agentSuccessRate/analysisArtifact.js +155 -0
- package/dist/src/experiments/plugins/agentSuccessRate/analysisTypes.js +65 -0
- package/dist/src/experiments/plugins/agentSuccessRate/attemptEvidence.js +7 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignCheckpoint.js +216 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignCheckpointStore.js +588 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignEvidenceQuality.js +77 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignExecution.js +314 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignIdentity.js +111 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignProgress.js +173 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignQueue.js +11 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignRecovery.js +256 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignResumableExecution.js +207 -0
- package/dist/src/experiments/plugins/agentSuccessRate/config.js +123 -0
- package/dist/src/experiments/plugins/agentSuccessRate/contextGeneration.js +497 -0
- package/dist/src/experiments/plugins/agentSuccessRate/execution.js +441 -0
- package/dist/src/experiments/plugins/agentSuccessRate/executionArtifact.js +126 -0
- package/dist/src/experiments/plugins/agentSuccessRate/executionTypes.js +2 -0
- package/dist/src/experiments/plugins/agentSuccessRate/index.js +26 -0
- package/dist/src/experiments/plugins/agentSuccessRate/metadata.js +28 -0
- package/dist/src/experiments/plugins/agentSuccessRate/metrics.js +76 -0
- package/dist/src/experiments/plugins/agentSuccessRate/plugin.js +517 -0
- package/dist/src/experiments/plugins/agentSuccessRate/realAgentExecution.js +272 -0
- package/dist/src/experiments/plugins/agentSuccessRate/realAgentPrompt.js +43 -0
- package/dist/src/experiments/plugins/agentSuccessRate/repairAnalysis.js +148 -0
- package/dist/src/experiments/plugins/agentSuccessRate/repairExecution.js +103 -0
- package/dist/src/experiments/plugins/agentSuccessRate/repairFeedback.js +91 -0
- package/dist/src/experiments/plugins/agentSuccessRate/repairPolicy.js +88 -0
- package/dist/src/experiments/plugins/agentSuccessRate/timeoutPresets.js +9 -0
- package/dist/src/experiments/plugins/agentSuccessRate/types.js +1 -0
- package/dist/src/experiments/plugins/warmIndexReuse/plugin.js +2 -3
- package/dist/src/report/experiments/agentSuccessCampaignProgressReportModel.js +1 -0
- package/dist/src/report/experiments/agentSuccessRateReportModel.js +6 -0
- package/dist/src/report/experiments/buildAgentSuccessCampaignProgressReport.js +6 -0
- package/dist/src/report/experiments/buildAgentSuccessRateReport.js +266 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +27 -2
- package/dist/src/report/experiments/index.js +4 -0
- package/dist/src/report/experiments/renderAgentSuccessCampaignProgressHtml.js +6 -0
- package/dist/src/report/experiments/renderAgentSuccessCampaignProgressText.js +25 -0
- package/dist/src/report/experiments/renderAgentSuccessRateHtml.js +171 -0
- package/dist/src/report/experiments/renderAgentSuccessRateText.js +146 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +3 -0
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +5 -0
- package/dist/src/report/experiments/writeAgentSuccessCampaignProgressReport.js +135 -0
- package/dist/src/report/experiments/writePluginExperimentReports.js +4 -3
- package/docs/METRICS.md +79 -0
- package/package.json +1 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
1
|
+
import { lstat, mkdir, readFile, readdir, realpath } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { parseAgentCommandTemplate } from "../agents/index.js";
|
|
4
4
|
import { parseAgentId } from "../agents/agentRegistry.js";
|
|
5
5
|
import { readBenchmarkProjectProfiles, readEvaluationCases } from "../evaluation/index.js";
|
|
6
|
+
import { AGENT_SUCCESS_PROJECT_PROFILES_PATH, AGENT_SUCCESS_TASK_CATALOG_PATH, assertReadAgentSuccessCorpus } from "../evaluation/agentSuccess/index.js";
|
|
6
7
|
import { summarizeRetrievalGroundTruthIssuesSafely, validateRetrievalPrecisionRecallCorpus } from "../evaluation/retrievalQuality/index.js";
|
|
7
8
|
import { loadLocalRepositorySubject } from "../evaluation/localRepositorySubject/index.js";
|
|
8
9
|
import { contextStrategyComparisonPlugin, contextWindowScalingPlugin, createDefaultExperimentPluginRegistry, getWarmIndexCampaignPreset, incrementalChangeStalenessPlugin, parseContextBudgetsCliValue, parseWarmIndexCampaignPresetId, resolveExperimentTarget, retrievalPrecisionRecallPlugin, retrievalQueryStrategyComparisonPlugin, runExperiment, warmIndexReusePlugin } from "../experiments/index.js";
|
|
9
10
|
import { contextPackGenerationPlugin } from "../experiments/plugins/contextPackGeneration/index.js";
|
|
11
|
+
import { AGENT_SUCCESS_RATE_MAX_AGENT_TIMEOUT_MS, AGENT_SUCCESS_RATE_REAL_AGENT_IDS, agentSuccessRatePlugin, selectAgentSuccessTasks } from "../experiments/plugins/agentSuccessRate/index.js";
|
|
10
12
|
import { prepareSyntheticContextWindowScalingInputs } from "../experiments/plugins/contextWindowScaling/index.js";
|
|
11
13
|
import { projectExternalLocalTarget, projectRunForExternalLocalPersistence, } from "../experiments/plugins/contextWindowScaling/localSubjectPrivacy.js";
|
|
12
14
|
import { assertWorkRootOutsideTarget } from "../experiments/plugins/contextWindowScaling/localSubjectScratch.js";
|
|
@@ -15,6 +17,18 @@ import { parsePromptComplexityLevel, parsePromptStrategy } from "../prompts/inde
|
|
|
15
17
|
import { writePluginExperimentReports } from "../report/index.js";
|
|
16
18
|
import { createLabExecutionContext, resolvePackageResource } from "../runtime/index.js";
|
|
17
19
|
import { runWarmIndexCampaignPresentation } from "./runWarmIndexCampaignPresentation.js";
|
|
20
|
+
import { observeProviderBuild } from "../agents/providerBuildObservation.js";
|
|
21
|
+
import { resolveAgentTimeoutPreset } from "../experiments/plugins/agentSuccessRate/timeoutPresets.js";
|
|
22
|
+
import { validateAgentSuccessRateConfig } from "../experiments/plugins/agentSuccessRate/config.js";
|
|
23
|
+
import { AGENT_SUCCESS_RATE_DEFAULT_AGENT_TIMEOUT_MS } from "../experiments/plugins/agentSuccessRate/realAgentExecution.js";
|
|
24
|
+
import { AGENT_SUCCESS_RATE_TREATMENT_IDS } from "../experiments/plugins/agentSuccessRate/metadata.js";
|
|
25
|
+
import { executeAgentSuccessCampaignV2, prepareAgentSuccessCampaign } from "../experiments/plugins/agentSuccessRate/campaignExecution.js";
|
|
26
|
+
import { readCampaignCheckpointV2, validateCampaignOutputPath } from "../experiments/plugins/agentSuccessRate/campaignCheckpointStore.js";
|
|
27
|
+
import { validateCampaignLimitAuthorization } from "../experiments/plugins/agentSuccessRate/campaignRecovery.js";
|
|
28
|
+
import { campaignIdentifier } from "../experiments/plugins/agentSuccessRate/campaignIdentity.js";
|
|
29
|
+
import { readCampaignProgress } from "../experiments/plugins/agentSuccessRate/campaignProgress.js";
|
|
30
|
+
import { writeAgentSuccessCampaignProgressReport } from "../report/experiments/writeAgentSuccessCampaignProgressReport.js";
|
|
31
|
+
import { boundMessage } from "../experiments/plugins/agentSuccessRate/execution.js";
|
|
18
32
|
// ---------------------------------------------------------------------------
|
|
19
33
|
// v0.4.6 Batch 4 -- reusable experiment-run command owner.
|
|
20
34
|
//
|
|
@@ -71,9 +85,36 @@ const RETRIEVAL_QUERY_STRATEGY_COMPARISON_EXTERNAL_ALLOWED_FLAGS = ["--experimen
|
|
|
71
85
|
const CONTEXT_PACK_GENERATION_CASES_RESOURCE = "benchmarks/contracts/warm-index-benchmark-cases.json";
|
|
72
86
|
const CONTEXT_PACK_GENERATION_ALLOWED_FLAGS = ["--experiment", "--out", "--case", "--benchmark-project", "--kit-command"];
|
|
73
87
|
const CONTEXT_PACK_GENERATION_EXTERNAL_ALLOWED_FLAGS = ["--experiment", "--out", "--target", "--local-subject-config", "--kit-command"];
|
|
88
|
+
// agent-success-rate (v0.9.0) owns the bundled six-task implementation corpus (task catalog and project profiles), both
|
|
89
|
+
// resolved from the package root, never the cwd. It runs against the Lab itself only and accepts exactly these options;
|
|
90
|
+
// every other flag is rejected, not ignored. Real providers run only with --agent together with --include-real-agents.
|
|
91
|
+
const AGENT_SUCCESS_RATE_ALLOWED_FLAGS = [
|
|
92
|
+
"--experiment",
|
|
93
|
+
"--out",
|
|
94
|
+
"--case",
|
|
95
|
+
"--benchmark-project",
|
|
96
|
+
"--kit-command",
|
|
97
|
+
"--agent",
|
|
98
|
+
"--include-real-agents",
|
|
99
|
+
"--timeout-ms",
|
|
100
|
+
"--repair-attempts",
|
|
101
|
+
"--agent-campaign", "--one-case", "--campaign-source-root", "--timeout-preset",
|
|
102
|
+
"--expected-checkpoint-revision", "--limit-case", "--limit-treatment", "--limit-epoch", "--authorize-additional-invocation"
|
|
103
|
+
];
|
|
104
|
+
const AGENT_CAMPAIGN_FLAGS = ["--agent-campaign", "--one-case", "--campaign-source-root", "--expected-checkpoint-revision", "--limit-case", "--limit-treatment", "--limit-epoch", "--authorize-additional-invocation"];
|
|
105
|
+
export function assertAgentSuccessRateFlags(argv) {
|
|
106
|
+
const flags = argv.filter((arg) => arg.startsWith("--"));
|
|
107
|
+
const unsupported = [...new Set(flags.filter((flag) => !AGENT_SUCCESS_RATE_ALLOWED_FLAGS.includes(flag)))];
|
|
108
|
+
if (unsupported.length > 0) {
|
|
109
|
+
throw new Error(`${unsupported.join(", ")} ${unsupported.length === 1 ? "is" : "are"} not supported for --experiment ${agentSuccessRatePlugin.metadata.id}; supported options: ${AGENT_SUCCESS_RATE_ALLOWED_FLAGS.join(", ")}.`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
74
112
|
export async function runExperimentRunCommandFromArgs(argv, options = {}) {
|
|
113
|
+
let parsed = false;
|
|
114
|
+
const campaignUsage = argv.includes("--agent-campaign") || argv.some(a => AGENT_CAMPAIGN_FLAGS.includes(a));
|
|
75
115
|
try {
|
|
76
116
|
const args = parseRunExperimentArgs(argv);
|
|
117
|
+
parsed = true;
|
|
77
118
|
const context = options.context ?? createLabExecutionContext();
|
|
78
119
|
const toolRoot = context.packageRoot;
|
|
79
120
|
const registry = createDefaultExperimentPluginRegistry();
|
|
@@ -96,6 +137,8 @@ export async function runExperimentRunCommandFromArgs(argv, options = {}) {
|
|
|
96
137
|
});
|
|
97
138
|
}
|
|
98
139
|
const inputs = await loadPluginInputs(args, toolRoot, context, outputRoot);
|
|
140
|
+
if (args.agentCampaign)
|
|
141
|
+
return await runAgentCampaignCommand(args, context, outputRoot, inputs, runId, options);
|
|
99
142
|
const result = await runExperiment({
|
|
100
143
|
pluginId: args.experimentId,
|
|
101
144
|
registry,
|
|
@@ -151,7 +194,8 @@ export async function runExperimentRunCommandFromArgs(argv, options = {}) {
|
|
|
151
194
|
`Target root: ${result.target.targetRoot}`,
|
|
152
195
|
`Output: ${String(result.metadata?.outputRoot ?? "")}`,
|
|
153
196
|
`Report JSON: ${reports.outputPaths.jsonPath}`,
|
|
154
|
-
`Report HTML: ${reports.outputPaths.htmlPath}
|
|
197
|
+
`Report HTML: ${reports.outputPaths.htmlPath}`,
|
|
198
|
+
...(args.experimentId === agentSuccessRatePlugin.metadata.id ? agentSuccessRateSummaryLines(result, reports.outputPaths.textPath) : [])
|
|
155
199
|
];
|
|
156
200
|
// Automatic presentation (report already produced above) applies only to warm-index-reuse
|
|
157
201
|
// campaign runs; legacy fake-agent warm-index runs and context-strategy-comparison stay
|
|
@@ -195,16 +239,125 @@ export async function runExperimentRunCommandFromArgs(argv, options = {}) {
|
|
|
195
239
|
return 0;
|
|
196
240
|
}
|
|
197
241
|
catch (error) {
|
|
198
|
-
if (
|
|
242
|
+
if (campaignUsage) {
|
|
243
|
+
const sourceIndex = argv.indexOf("--campaign-source-root");
|
|
244
|
+
const source = sourceIndex >= 0 ? argv[sourceIndex + 1] : undefined;
|
|
245
|
+
const message = error instanceof Error ? error.message : "CAMPAIGN_FAILED";
|
|
246
|
+
console.error(boundMessage(message, [process.cwd(), options.context?.packageRoot ?? "", ...(source ? [path.resolve(options.context?.invocationCwd ?? process.cwd(), source)] : [])]));
|
|
247
|
+
}
|
|
248
|
+
else if (process.env.DEBUG) {
|
|
199
249
|
console.error(error);
|
|
200
250
|
}
|
|
201
251
|
else {
|
|
202
252
|
console.error(error instanceof Error ? error.message : String(error));
|
|
203
253
|
}
|
|
204
|
-
return 1;
|
|
254
|
+
return campaignUsage && !parsed ? 2 : 1;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async function runAgentCampaignCommand(args, context, outputRoot, inputs, runId, options) {
|
|
258
|
+
const request = args.agentCampaign;
|
|
259
|
+
const sourceRoot = path.resolve(context.invocationCwd, request.sourceRoot ?? context.packageRoot);
|
|
260
|
+
try {
|
|
261
|
+
const packageName = "@dailephd/my-dev-kit-lab";
|
|
262
|
+
const validPackageVersion = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
263
|
+
const executingMetadata = JSON.parse(await readFile(path.join(context.packageRoot, "package.json"), "utf8"));
|
|
264
|
+
if (executingMetadata.name !== packageName || typeof executingMetadata.version !== "string" || !validPackageVersion.test(executingMetadata.version))
|
|
265
|
+
throw new Error();
|
|
266
|
+
if (!(await lstat(path.join(sourceRoot, "src"))).isDirectory() || path.resolve(await realpath(sourceRoot)).toLowerCase() !== sourceRoot.toLowerCase())
|
|
267
|
+
throw new Error();
|
|
268
|
+
const metadata = JSON.parse(await readFile(path.join(sourceRoot, "package.json"), "utf8"));
|
|
269
|
+
if (metadata.name !== packageName || metadata.version !== executingMetadata.version)
|
|
270
|
+
throw new Error();
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
throw new Error("CAMPAIGN_SOURCE_UNVERIFIABLE: supply --campaign-source-root with a current my-dev-kit-lab source checkout; installed source bytes cannot be guessed.");
|
|
274
|
+
}
|
|
275
|
+
const boundary = { toolRoot: context.packageRoot, outputRoot };
|
|
276
|
+
await validateCampaignOutputPath(boundary, sourceRoot);
|
|
277
|
+
let entries = [];
|
|
278
|
+
try {
|
|
279
|
+
entries = await readdir(outputRoot);
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
if (error.code !== "ENOENT")
|
|
283
|
+
throw error;
|
|
284
|
+
if (request.mode !== "new")
|
|
285
|
+
throw new Error("RESUMABLE_CAMPAIGN_REQUIRED: --out must contain an existing version 2 campaign.");
|
|
286
|
+
}
|
|
287
|
+
if (request.mode === "new" && entries.length > 0)
|
|
288
|
+
throw new Error("CAMPAIGN_OUTPUT_EXISTS: new requires an empty output root; existing artifacts are preserved.");
|
|
289
|
+
if (request.mode !== "new" && !entries.includes("campaign-checkpoints-v2"))
|
|
290
|
+
throw new Error("RESUMABLE_CAMPAIGN_REQUIRED: no version 2 checkpoint in --out.");
|
|
291
|
+
const validation = validateAgentSuccessRateConfig(args.config);
|
|
292
|
+
if (!validation.valid || !validation.config)
|
|
293
|
+
throw new Error("INVALID_CAMPAIGN_CONFIG");
|
|
294
|
+
const config = validation.config;
|
|
295
|
+
const tasks = selectAgentSuccessTasks(inputs.agentSuccessTasks, config);
|
|
296
|
+
const runtime = options.campaignRuntime;
|
|
297
|
+
const provider = config.agentId ? { kind: "real-agent", providerId: config.agentId, includeRealAgents: true, timeoutMs: config.timeoutMs ?? AGENT_SUCCESS_RATE_DEFAULT_AGENT_TIMEOUT_MS, repairAttempts: (config.repairAttempts ?? 0), kitCommand: config.kitCommand, contextDependencies: runtime?.contextDependencies, env: runtime?.env } : { kind: "deterministic-fixture" };
|
|
298
|
+
const preparation = { boundary, sourceRoot, corpusFile: resolvePackageResource(context, AGENT_SUCCESS_TASK_CATALOG_PATH), corpusRoot: context.packageRoot, tasks, treatments: AGENT_SUCCESS_RATE_TREATMENT_IDS, provider, dependencies: runtime?.dependencies };
|
|
299
|
+
// Source, corpus and canonical project byte checks precede even a task-free version probe.
|
|
300
|
+
let prepared = await prepareAgentSuccessCampaign(preparation);
|
|
301
|
+
if (provider.kind === "real-agent") {
|
|
302
|
+
provider.trustedExecutable = await observeProviderBuild(provider.providerId, { env: runtime?.env });
|
|
303
|
+
prepared = await prepareAgentSuccessCampaign(preparation);
|
|
304
|
+
}
|
|
305
|
+
let intent = request.mode === "new" ? { kind: "new-resumable" } : { kind: "resume" };
|
|
306
|
+
let reused = 0;
|
|
307
|
+
const previousEpochs = new Set();
|
|
308
|
+
if (request.mode !== "new") {
|
|
309
|
+
// Identity-scoped stores are not discovery: a different digest cannot create another campaign on resume.
|
|
310
|
+
const identities = await readdir(path.join(outputRoot, "campaign-checkpoints-v2"));
|
|
311
|
+
if (!identities.includes(prepared.identity.digest))
|
|
312
|
+
throw new Error("CAMPAIGN_IDENTITY_MISMATCH: current inputs do not match this output root.");
|
|
313
|
+
const stored = await readCampaignCheckpointV2(boundary, prepared.identity, tasks);
|
|
314
|
+
if (!stored.reusable || !stored.checkpoint || stored.identityVerification !== "MATCH")
|
|
315
|
+
throw new Error("RESUMABLE_CAMPAIGN_REQUIRED_MATCH");
|
|
316
|
+
const cp = stored.checkpoint;
|
|
317
|
+
cp.units.flatMap(u => u.epochs).forEach(e => previousEpochs.add(e.id));
|
|
318
|
+
reused = cp.units.filter(u => u.state === "completed").length;
|
|
319
|
+
if (request.mode === "continue-limit") {
|
|
320
|
+
const unit = cp.units.find(u => u.caseId === request.limitCase && u.treatmentId === request.limitTreatment);
|
|
321
|
+
if (cp.generation !== request.expectedRevision || !unit || unit.epochs.at(-1)?.id !== request.limitEpoch)
|
|
322
|
+
throw new Error("LIMIT_AUTHORIZATION_MISMATCH");
|
|
323
|
+
const authorization = { identityDigest: cp.identity.digest, generation: request.expectedRevision, revision: cp.integrity, caseId: request.limitCase, treatmentId: request.limitTreatment, acknowledgeInvocation: true, acknowledgeExternalLimit: true };
|
|
324
|
+
validateCampaignLimitAuthorization(authorization, cp, unit);
|
|
325
|
+
intent = { kind: "authorize-limit-continuation", authorization };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
await mkdir(outputRoot, { recursive: true });
|
|
330
|
+
await validateCampaignOutputPath(boundary, sourceRoot);
|
|
331
|
+
if ((await readdir(outputRoot)).length !== 0)
|
|
332
|
+
throw new Error("CAMPAIGN_OUTPUT_EXISTS");
|
|
333
|
+
}
|
|
334
|
+
const result = await executeAgentSuccessCampaignV2(prepared, { intent, runId, limit: request.oneCase ? "one-case" : "all-selected-cases", hooks: runtime?.hooks });
|
|
335
|
+
const progressLines = [];
|
|
336
|
+
let progressFailed = false;
|
|
337
|
+
let progressValidated = false;
|
|
338
|
+
try {
|
|
339
|
+
const snapshot = await readCampaignProgress(boundary, prepared.identity, tasks);
|
|
340
|
+
progressValidated = true;
|
|
341
|
+
const progress = await writeAgentSuccessCampaignProgressReport({ snapshot, boundary });
|
|
342
|
+
progressLines.push(`Progress status: ${progress.model.state}`, `Progress checkpoint revision: ${progress.model.checkpointGeneration} (${progress.model.checkpointRevision})`, `Completed evaluations: ${progress.model.unitCounts.completed}/${progress.model.totalTreatmentUnits}`, `Completed cases: ${progress.model.caseCounts.completed}/${progress.model.selectedCaseCount}`, `Progress JSON: ${progress.outputPaths.jsonPath}`, `Progress Text: ${progress.outputPaths.textPath}`, `Progress HTML: ${progress.outputPaths.htmlPath}`);
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
progressFailed = true;
|
|
346
|
+
progressLines.push(progressValidated ? "Progress persistence: failed (validated checkpoint retained)." : "Progress status: blocked-integrity (checkpoint unavailable or untrusted).");
|
|
347
|
+
progressLines.push("Progress publication: failed; checkpoint/scientific execution outcome is reported separately.");
|
|
205
348
|
}
|
|
349
|
+
const newEpochs = result.checkpoint?.units.flatMap(u => u.epochs).filter(e => !previousEpochs.has(e.id)) ?? [];
|
|
350
|
+
// Provider evidence, never mere creation of a reservation, establishes invocation.
|
|
351
|
+
const newWork = newEpochs.some(e => e.terminal?.evidence.realAgent?.providerInvoked === true) ? "invoked" : newEpochs.some(e => e.phase === "invocation-authorized" || e.phase === "uncertain") ? "unconfirmed" : "none";
|
|
352
|
+
console.log(["Experiment: agent-success-rate", `Campaign mode: ${request.mode}`, `Identity: ${prepared.identity.digest.slice(0, 16)}`, `Status: ${result.status === "provider-limit" ? "paused-provider-limit" : result.status}`, `New provider work: ${newWork}`, `Validated work reused: ${reused > 0 ? "yes" : "no"}`, `Checkpoint revision: ${result.checkpoint?.generation ?? "unavailable"}`, `Final scientific artifacts: ${result.scientificArtifactsWritten ? "written" : "not written"}`, `Output: ${outputRoot}`, ...result.errors.map(e => `Failure: ${e}`)].join("\n"));
|
|
353
|
+
console.log(progressLines.join("\n"));
|
|
354
|
+
return !progressFailed && ["completed", "already-complete", "partial"].includes(result.status) ? 0 : 1;
|
|
206
355
|
}
|
|
207
356
|
export function parseRunExperimentArgs(argv) {
|
|
357
|
+
let agentCampaignMode;
|
|
358
|
+
let oneCase = false, authorize = false;
|
|
359
|
+
let campaignSourceRoot, limitCase, limitTreatment, limitEpoch, timeoutPreset;
|
|
360
|
+
let expectedRevision;
|
|
208
361
|
let experimentId = "";
|
|
209
362
|
let targetPath;
|
|
210
363
|
let outDir;
|
|
@@ -225,13 +378,51 @@ export function parseRunExperimentArgs(argv) {
|
|
|
225
378
|
let contextBudgets;
|
|
226
379
|
let syntheticConfigPath;
|
|
227
380
|
let localSubjectConfigPath;
|
|
381
|
+
const agentFlagValues = [];
|
|
382
|
+
let repairAttempts;
|
|
228
383
|
const seenFlags = [];
|
|
229
384
|
const commandTemplates = {};
|
|
385
|
+
// The flag whitelist runs before any value is interpreted so an unsupported flag is reported as such.
|
|
386
|
+
const preScanIndex = argv.indexOf("--experiment");
|
|
387
|
+
if (preScanIndex >= 0 && argv[preScanIndex + 1] === agentSuccessRatePlugin.metadata.id)
|
|
388
|
+
assertAgentSuccessRateFlags(argv);
|
|
230
389
|
for (let index = 0; index < argv.length; index += 1) {
|
|
231
390
|
const arg = argv[index];
|
|
391
|
+
if (arg !== undefined && arg.startsWith("--") && seenFlags.includes(arg) && (AGENT_CAMPAIGN_FLAGS.includes(arg) || arg === "--timeout-preset" || (argv.includes("agent-success-rate") && arg !== "--case" && arg !== "--benchmark-project")))
|
|
392
|
+
throw new Error(`${arg} may be supplied only once.`);
|
|
232
393
|
if (arg !== undefined && arg.startsWith("--"))
|
|
233
394
|
seenFlags.push(arg);
|
|
234
|
-
if (arg === "--
|
|
395
|
+
if (arg === "--agent-campaign") {
|
|
396
|
+
agentCampaignMode = readRequiredValue(argv, ++index, arg);
|
|
397
|
+
}
|
|
398
|
+
else if (arg === "--one-case") {
|
|
399
|
+
oneCase = true;
|
|
400
|
+
}
|
|
401
|
+
else if (arg === "--authorize-additional-invocation") {
|
|
402
|
+
authorize = true;
|
|
403
|
+
}
|
|
404
|
+
else if (arg === "--campaign-source-root") {
|
|
405
|
+
campaignSourceRoot = readRequiredValue(argv, ++index, arg);
|
|
406
|
+
}
|
|
407
|
+
else if (arg === "--expected-checkpoint-revision") {
|
|
408
|
+
const value = readRequiredValue(argv, ++index, arg);
|
|
409
|
+
if (!/^[1-9]\d*$/.test(value))
|
|
410
|
+
throw new Error("--expected-checkpoint-revision must be a positive integer.");
|
|
411
|
+
expectedRevision = parsePositiveInteger(arg, value);
|
|
412
|
+
}
|
|
413
|
+
else if (arg === "--limit-case") {
|
|
414
|
+
limitCase = readRequiredValue(argv, ++index, arg);
|
|
415
|
+
}
|
|
416
|
+
else if (arg === "--limit-treatment") {
|
|
417
|
+
limitTreatment = readRequiredValue(argv, ++index, arg);
|
|
418
|
+
}
|
|
419
|
+
else if (arg === "--limit-epoch") {
|
|
420
|
+
limitEpoch = readRequiredValue(argv, ++index, arg);
|
|
421
|
+
}
|
|
422
|
+
else if (arg === "--timeout-preset") {
|
|
423
|
+
timeoutPreset = readRequiredValue(argv, ++index, arg);
|
|
424
|
+
}
|
|
425
|
+
else if (arg === "--experiment") {
|
|
235
426
|
experimentId = readRequiredValue(argv, ++index, "--experiment");
|
|
236
427
|
}
|
|
237
428
|
else if (arg === "--target") {
|
|
@@ -264,6 +455,12 @@ export function parseRunExperimentArgs(argv) {
|
|
|
264
455
|
else if (arg === "--benchmark-project") {
|
|
265
456
|
benchmarkProjects.push(...splitList(readRequiredValue(argv, ++index, "--benchmark-project")));
|
|
266
457
|
}
|
|
458
|
+
else if (arg === "--agent") {
|
|
459
|
+
agentFlagValues.push(readRequiredValue(argv, ++index, "--agent"));
|
|
460
|
+
}
|
|
461
|
+
else if (arg === "--repair-attempts") {
|
|
462
|
+
repairAttempts = parseRepairAttempts(readRequiredValue(argv, ++index, "--repair-attempts"));
|
|
463
|
+
}
|
|
267
464
|
else if (arg === "--agents") {
|
|
268
465
|
agents = splitList(readRequiredValue(argv, ++index, "--agents")).map((value) => parseAgentId(value));
|
|
269
466
|
}
|
|
@@ -317,6 +514,31 @@ export function parseRunExperimentArgs(argv) {
|
|
|
317
514
|
if (!experimentId) {
|
|
318
515
|
throw new Error("Usage: --experiment <id> [--target <path>] [--out <directory>]");
|
|
319
516
|
}
|
|
517
|
+
const campaignFlagsPresent = seenFlags.some(f => AGENT_CAMPAIGN_FLAGS.includes(f));
|
|
518
|
+
if ((campaignFlagsPresent || timeoutPreset !== undefined) && experimentId !== agentSuccessRatePlugin.metadata.id)
|
|
519
|
+
throw new Error("Agent campaign and timeout preset flags are only supported for --experiment agent-success-rate.");
|
|
520
|
+
if (campaignFlagsPresent && agentCampaignMode === undefined)
|
|
521
|
+
throw new Error("Campaign flags require --agent-campaign <new|resume|continue-limit>.");
|
|
522
|
+
if (agentCampaignMode !== undefined && !["new", "resume", "continue-limit"].includes(agentCampaignMode))
|
|
523
|
+
throw new Error("--agent-campaign must be new, resume or continue-limit.");
|
|
524
|
+
if (agentCampaignMode !== undefined && !outDir?.trim())
|
|
525
|
+
throw new Error("--agent-campaign requires a stable --out directory.");
|
|
526
|
+
const limitPresent = expectedRevision !== undefined || limitCase !== undefined || limitTreatment !== undefined || limitEpoch !== undefined || authorize;
|
|
527
|
+
if (agentCampaignMode !== "continue-limit" && limitPresent)
|
|
528
|
+
throw new Error("Limit authorization flags require --agent-campaign continue-limit.");
|
|
529
|
+
if (agentCampaignMode === "continue-limit") {
|
|
530
|
+
if (expectedRevision === undefined || !limitCase || !limitTreatment || !limitEpoch || !authorize)
|
|
531
|
+
throw new Error("continue-limit requires --expected-checkpoint-revision, --limit-case, --limit-treatment, --limit-epoch and --authorize-additional-invocation.");
|
|
532
|
+
if (expectedRevision > 1000000 || !campaignIdentifier(limitCase) || !["raw-full-file", "context-pack"].includes(limitTreatment) || !/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/.test(limitEpoch))
|
|
533
|
+
throw new Error("Invalid limit revision, case, treatment or epoch identifier.");
|
|
534
|
+
}
|
|
535
|
+
if (timeoutPreset !== undefined) {
|
|
536
|
+
if (timeoutMs !== undefined)
|
|
537
|
+
throw new Error("--timeout-preset and --timeout-ms cannot be combined.");
|
|
538
|
+
if (agentFlagValues.length !== 1 || includeRealAgents !== true || !["codex", "claude"].includes(agentFlagValues[0]))
|
|
539
|
+
throw new Error("--timeout-preset requires --agent <codex|claude> and --include-real-agents.");
|
|
540
|
+
timeoutMs = resolveAgentTimeoutPreset(agentFlagValues[0], timeoutPreset);
|
|
541
|
+
}
|
|
320
542
|
const KIT_COMMAND_PLUGIN_IDS = [
|
|
321
543
|
warmIndexReusePlugin.metadata.id,
|
|
322
544
|
incrementalChangeStalenessPlugin.metadata.id,
|
|
@@ -399,6 +621,49 @@ export function parseRunExperimentArgs(argv) {
|
|
|
399
621
|
throw new Error("--benchmark-project must list at least one benchmark project id.");
|
|
400
622
|
}
|
|
401
623
|
}
|
|
624
|
+
if (experimentId === agentSuccessRatePlugin.metadata.id) {
|
|
625
|
+
// Mode matrix: no flags = deterministic-fixture (default); --agent together with --include-real-agents = real-agent mode.
|
|
626
|
+
// Everything else is rejected, never coerced: a provider needs both flags, and real-agent options need a provider.
|
|
627
|
+
if (agentFlagValues.length > 1) {
|
|
628
|
+
throw new Error("--agent may be supplied only once; agent-success-rate runs exactly one provider.");
|
|
629
|
+
}
|
|
630
|
+
const agentId = agentFlagValues[0];
|
|
631
|
+
if (agentId !== undefined && !AGENT_SUCCESS_RATE_REAL_AGENT_IDS.includes(agentId)) {
|
|
632
|
+
throw new Error(`--agent must be exactly one of: ${AGENT_SUCCESS_RATE_REAL_AGENT_IDS.join(", ")}.`);
|
|
633
|
+
}
|
|
634
|
+
if (agentId !== undefined && includeRealAgents !== true) {
|
|
635
|
+
throw new Error("--agent requires --include-real-agents; real providers are invoked only with explicit authorization.");
|
|
636
|
+
}
|
|
637
|
+
if (agentId === undefined && includeRealAgents === true) {
|
|
638
|
+
throw new Error("--include-real-agents requires --agent <codex|claude> for agent-success-rate.");
|
|
639
|
+
}
|
|
640
|
+
if (agentId === undefined && timeoutMs !== undefined) {
|
|
641
|
+
throw new Error("--timeout-ms is only supported together with --agent and --include-real-agents (real-agent mode).");
|
|
642
|
+
}
|
|
643
|
+
if (agentId === undefined && kitCommand !== undefined) {
|
|
644
|
+
throw new Error("--kit-command is only supported together with --agent and --include-real-agents (real-agent mode).");
|
|
645
|
+
}
|
|
646
|
+
if (agentId === undefined && repairAttempts !== undefined && repairAttempts > 0) {
|
|
647
|
+
throw new Error("--repair-attempts greater than 0 requires --agent and --include-real-agents (real-agent mode).");
|
|
648
|
+
}
|
|
649
|
+
if (timeoutMs !== undefined && timeoutMs > AGENT_SUCCESS_RATE_MAX_AGENT_TIMEOUT_MS) {
|
|
650
|
+
throw new Error(`--timeout-ms must be a positive integer no greater than ${AGENT_SUCCESS_RATE_MAX_AGENT_TIMEOUT_MS}.`);
|
|
651
|
+
}
|
|
652
|
+
if (seenFlags.includes("--case") && caseIds.length === 0) {
|
|
653
|
+
throw new Error("--case must list at least one case id.");
|
|
654
|
+
}
|
|
655
|
+
if (seenFlags.includes("--benchmark-project") && benchmarkProjects.length === 0) {
|
|
656
|
+
throw new Error("--benchmark-project must list at least one benchmark project id.");
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
if (agentFlagValues.length > 0) {
|
|
661
|
+
throw new Error(`--agent is only supported for --experiment ${agentSuccessRatePlugin.metadata.id}.`);
|
|
662
|
+
}
|
|
663
|
+
if (repairAttempts !== undefined) {
|
|
664
|
+
throw new Error(`--repair-attempts is only supported for --experiment ${agentSuccessRatePlugin.metadata.id}.`);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
402
667
|
if (contextBudgets !== undefined && experimentId !== contextWindowScalingPlugin.metadata.id) {
|
|
403
668
|
throw new Error(`--context-budgets is only supported for --experiment ${contextWindowScalingPlugin.metadata.id}.`);
|
|
404
669
|
}
|
|
@@ -441,7 +706,8 @@ export function parseRunExperimentArgs(argv) {
|
|
|
441
706
|
throw new Error(`External ${experimentId} targets require --local-subject-config.`);
|
|
442
707
|
}
|
|
443
708
|
}
|
|
444
|
-
|
|
709
|
+
// agent-success-rate accepts --kit-command in real-agent mode; its own validation above owns that rule and message.
|
|
710
|
+
if (kitCommand !== undefined && experimentId !== agentSuccessRatePlugin.metadata.id && !KIT_COMMAND_PLUGIN_IDS.includes(experimentId)) {
|
|
445
711
|
throw new Error(`--kit-command is only supported for --experiment ${KIT_COMMAND_PLUGIN_IDS.join(" or ")}.`);
|
|
446
712
|
}
|
|
447
713
|
if (campaignPreset !== undefined) {
|
|
@@ -460,6 +726,7 @@ export function parseRunExperimentArgs(argv) {
|
|
|
460
726
|
}
|
|
461
727
|
return {
|
|
462
728
|
experimentId,
|
|
729
|
+
...(agentCampaignMode ? { agentCampaign: { mode: agentCampaignMode, oneCase, sourceRoot: campaignSourceRoot, expectedRevision, limitCase, limitTreatment: limitTreatment, limitEpoch, authorize } } : {}),
|
|
463
730
|
targetPath,
|
|
464
731
|
outDir,
|
|
465
732
|
syntheticConfigPath,
|
|
@@ -478,6 +745,8 @@ export function parseRunExperimentArgs(argv) {
|
|
|
478
745
|
requireAgents,
|
|
479
746
|
includeRealAgents,
|
|
480
747
|
commandTemplates: Object.keys(commandTemplates).length > 0 ? commandTemplates : undefined,
|
|
748
|
+
agentId: agentFlagValues[0],
|
|
749
|
+
repairAttempts,
|
|
481
750
|
kitCommand,
|
|
482
751
|
campaignPreset,
|
|
483
752
|
contextBudgets
|
|
@@ -542,8 +811,27 @@ async function loadPluginInputs(args, toolRoot, context, outputRoot) {
|
|
|
542
811
|
? loadRetrievalQueryStrategyComparisonLocalSubjectInputs(args, toolRoot, context, outputRoot)
|
|
543
812
|
: loadRetrievalQueryStrategyComparisonInputs(args, toolRoot, context);
|
|
544
813
|
}
|
|
814
|
+
if (args.experimentId === agentSuccessRatePlugin.metadata.id) {
|
|
815
|
+
return loadAgentSuccessRateInputs(args, context);
|
|
816
|
+
}
|
|
545
817
|
return undefined;
|
|
546
818
|
}
|
|
819
|
+
// agent-success-rate: the bundled task catalog and project profiles are located through the established package-resource
|
|
820
|
+
// resolver (never the cwd) and validated by the corpus reader, which also checks the canonical projects on disk. The
|
|
821
|
+
// validated tasks reach the plugin through its existing programmatic input; the plugin applies --case/--benchmark-project
|
|
822
|
+
// selection in catalog order and owns execution, repair, science and artifacts. No task file is accepted from the CLI.
|
|
823
|
+
async function loadAgentSuccessRateInputs(args, context) {
|
|
824
|
+
const validation = agentSuccessRatePlugin.validateConfig(args.config);
|
|
825
|
+
if (!validation.valid || !validation.config) {
|
|
826
|
+
throw new Error(`Invalid agent success rate config: ${validation.errors.join("; ")}`);
|
|
827
|
+
}
|
|
828
|
+
resolvePackageResource(context, AGENT_SUCCESS_TASK_CATALOG_PATH);
|
|
829
|
+
resolvePackageResource(context, AGENT_SUCCESS_PROJECT_PROFILES_PATH);
|
|
830
|
+
const corpus = assertReadAgentSuccessCorpus(context.resourceRoot);
|
|
831
|
+
// Fail on unknown case or project ids before any output directory exists.
|
|
832
|
+
selectAgentSuccessTasks(corpus.tasks, validation.config);
|
|
833
|
+
return { agentSuccessTasks: corpus.tasks };
|
|
834
|
+
}
|
|
547
835
|
// context-pack-generation (bundled/self): the frozen warm-index corpus through the established resource resolver, profile
|
|
548
836
|
// and corpus validation. The plugin applies --case/--benchmark-project selection and owns execution, science and artifacts.
|
|
549
837
|
async function loadContextPackGenerationInputs(args, toolRoot, context) {
|
|
@@ -815,6 +1103,12 @@ function readRequiredValue(argv, index, label) {
|
|
|
815
1103
|
}
|
|
816
1104
|
return value;
|
|
817
1105
|
}
|
|
1106
|
+
function parseRepairAttempts(value) {
|
|
1107
|
+
if (!/^[0-2]$/.test(value)) {
|
|
1108
|
+
throw new Error("--repair-attempts must be 0, 1 or 2.");
|
|
1109
|
+
}
|
|
1110
|
+
return Number(value);
|
|
1111
|
+
}
|
|
818
1112
|
function parsePositiveInteger(label, value) {
|
|
819
1113
|
const parsed = Number(value);
|
|
820
1114
|
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
@@ -822,6 +1116,15 @@ function parsePositiveInteger(label, value) {
|
|
|
822
1116
|
}
|
|
823
1117
|
return parsed;
|
|
824
1118
|
}
|
|
1119
|
+
function agentSuccessRateSummaryLines(result, textPath) {
|
|
1120
|
+
const mode = readMetadataString(result.metadata?.executionMode) ?? "deterministic-fixture";
|
|
1121
|
+
const provider = readMetadataString(result.metadata?.providerId);
|
|
1122
|
+
return [
|
|
1123
|
+
`Report Text: ${textPath}`,
|
|
1124
|
+
`Execution mode: ${mode}`,
|
|
1125
|
+
...(provider ? [`Provider: ${provider} (invoked by explicit --include-real-agents authorization)`] : ["Provider: none (deterministic-fixture; no coding agent was invoked)"])
|
|
1126
|
+
];
|
|
1127
|
+
}
|
|
825
1128
|
function readMetadataString(value) {
|
|
826
1129
|
return typeof value === "string" && value.trim() ? value : undefined;
|
|
827
1130
|
}
|
|
@@ -20,6 +20,61 @@ export function requestProcessTermination(pid) {
|
|
|
20
20
|
// The process may have exited between the liveness check and this signal.
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Opt-in, awaitable descendant-tree termination for bounded execution
|
|
25
|
+
* (see `terminateProcessTree` in `runMeasuredCommand`). The legacy
|
|
26
|
+
* `forceTerminateProcess` above is intentionally unchanged: it stays
|
|
27
|
+
* fire-and-forget and, off Windows, signals one pid only.
|
|
28
|
+
*
|
|
29
|
+
* Windows: `taskkill /pid <pid> /T /F` (shell:false) is awaited, bounded by
|
|
30
|
+
* `waitTimeoutMs`, so the caller knows the tree was asked to die before it
|
|
31
|
+
* proceeds.
|
|
32
|
+
*
|
|
33
|
+
* Other platforms: the child must have been spawned `detached` so it leads its
|
|
34
|
+
* own process group; the whole group is signalled via the negative pid. If no
|
|
35
|
+
* group exists, the single pid is signalled as a fallback.
|
|
36
|
+
*
|
|
37
|
+
* Never throws: the target may already have exited.
|
|
38
|
+
*/
|
|
39
|
+
export async function terminateProcessTree(pid, options = {}) {
|
|
40
|
+
if (!pid) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const platform = options.platform ?? process.platform;
|
|
44
|
+
if (platform === "win32") {
|
|
45
|
+
await new Promise((resolve) => {
|
|
46
|
+
let timer;
|
|
47
|
+
const finish = () => {
|
|
48
|
+
if (timer)
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
resolve();
|
|
51
|
+
};
|
|
52
|
+
const killer = spawn("taskkill", ["/pid", String(pid), "/T", "/F"], {
|
|
53
|
+
shell: false,
|
|
54
|
+
stdio: "ignore",
|
|
55
|
+
windowsHide: true
|
|
56
|
+
});
|
|
57
|
+
timer = setTimeout(finish, options.waitTimeoutMs ?? 5000);
|
|
58
|
+
killer.on("error", finish);
|
|
59
|
+
killer.on("close", finish);
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const signal = options.posixSignal ?? "SIGKILL";
|
|
64
|
+
try {
|
|
65
|
+
process.kill(-pid, signal);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// No process group for this pid (or it already exited); fall back to the single pid.
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
process.kill(pid, signal);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// The process may have exited between the liveness check and this signal.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
23
78
|
/**
|
|
24
79
|
* Forced cleanup for a process that did not stop on request.
|
|
25
80
|
*
|