@moda-ai/cli 1.55.60 → 1.55.61
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/{agent-ds0qdtxz.js → agent-3hdkbyep.js} +3 -3
- package/dist/{cli-wbd3hzqh.js → cli-04q6xpn6.js} +635 -120
- package/dist/{cli-bt564t50.js → cli-ehf0f84r.js} +2 -2
- package/dist/{cli-hxy54rx3.js → cli-sr3tnzfr.js} +3 -3
- package/dist/cli.js +7 -7
- package/dist/{doctor-s9t8e808.js → doctor-3x4cnx7p.js} +2 -2
- package/dist/{harness-bxcetthw.js → harness-esdbkwwh.js} +1 -1
- package/dist/{harness-github-actions-acxh0dbf.js → harness-github-actions-xbfvjpva.js} +1 -1
- package/dist/{index-d8hc0er0.js → index-10ye3htt.js} +3 -3
- package/dist/{prompt-attribution-6xc7h59r.js → prompt-attribution-abms6d21.js} +1 -1
- package/dist/prompt-source-mcp.js +1 -1
- package/dist/{prompts-e8rhawyc.js → prompts-sk27qc29.js} +1 -1
- package/dist/{sync-m9htrc8t.js → sync-4fdqer3h.js} +2 -2
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
|
@@ -103,7 +103,7 @@ import {
|
|
|
103
103
|
} from "./cli-0v6na3yp.js";
|
|
104
104
|
|
|
105
105
|
// src/harness.ts
|
|
106
|
-
import { createHash as
|
|
106
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
107
107
|
import { execFileSync as execFileSync3, spawn } from "node:child_process";
|
|
108
108
|
import { homedir, tmpdir as tmpdir2 } from "node:os";
|
|
109
109
|
import { fileURLToPath } from "node:url";
|
|
@@ -113,11 +113,11 @@ import {
|
|
|
113
113
|
mkdtempSync as mkdtempSync2,
|
|
114
114
|
mkdirSync as mkdirSync5,
|
|
115
115
|
readFileSync as readFileSync12,
|
|
116
|
-
readdirSync as
|
|
116
|
+
readdirSync as readdirSync5,
|
|
117
117
|
writeFileSync as writeFileSync5,
|
|
118
118
|
rmSync as rmSync4
|
|
119
119
|
} from "node:fs";
|
|
120
|
-
import { basename as
|
|
120
|
+
import { basename as basename7, delimiter, dirname as dirname7, join as join9, relative as relative6, resolve as resolve10, sep as sep5 } from "node:path";
|
|
121
121
|
import { confirm, select } from "@inquirer/prompts";
|
|
122
122
|
|
|
123
123
|
// src/analyst-tui.ts
|
|
@@ -12111,7 +12111,98 @@ function enrichScanCaptureMetadata(graph) {
|
|
|
12111
12111
|
|
|
12112
12112
|
// src/harness-candidates.ts
|
|
12113
12113
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
12114
|
-
import { basename as
|
|
12114
|
+
import { basename as basename6, dirname as dirname6, extname as extname2, resolve as resolve6 } from "node:path";
|
|
12115
|
+
|
|
12116
|
+
// src/harness-eval-suites.ts
|
|
12117
|
+
import { basename as basename5, extname } from "node:path";
|
|
12118
|
+
var EVAL_FILE_EXTENSIONS = new Set([".ts", ".tsx", ".mts", ".js", ".mjs", ".cjs", ".py", ".yaml", ".yml"]);
|
|
12119
|
+
var EVAL_CASE_DATA_EXTENSIONS = new Set([".json", ".jsonl", ".toml"]);
|
|
12120
|
+
var EVAL_DIR_SEGMENT = /^(?:evals|evaluations)$/i;
|
|
12121
|
+
var EVAL_HARNESS_DIR_SEGMENT = /^eval$/i;
|
|
12122
|
+
var EVAL_FILE_NAME = /(?:^|[._-])evals?(?:[._-]|$)|^promptfoo/i;
|
|
12123
|
+
var DOCS_ROOT = /^(?:docs?|documentation)\//i;
|
|
12124
|
+
var HIDDEN_DIR = /(?:^|\/)\.[^/]+\//;
|
|
12125
|
+
var EVAL_SCRIPT_NAME = /(?:^|[._-])(?:judges?|grader|grading|benchmarks?)(?:[._-]|$)/i;
|
|
12126
|
+
var SCRIPTS_ROOT = /^(?:scripts?|bin)\//i;
|
|
12127
|
+
var EVALUATION_NAME = /(?:^|[._-])evaluat(?:e|ion|ions|or|ors)(?:[._-]|$)/i;
|
|
12128
|
+
var TEST_TREE = /(?:^|\/)(?:tests?|__tests__)\//i;
|
|
12129
|
+
var EVAL_SEED_SCRIPT = /^(?:seed|fetch|scrape|download|populate|extract)[-_]/i;
|
|
12130
|
+
var EVAL_NON_CASE_DATA = /^(?:leaderboards?|results?|scores?|reports?|outputs?|summary|tsconfig|jsconfig|package(?:-lock)?)(?:[._-][^/]*)?\.[^.]+$/i;
|
|
12131
|
+
var TEST_FILE_NAME = /(?:^|\/)(?:test_[^/]*\.py|[^/]*_test\.py|[^/]*\.(?:test|spec)\.[^/]+)$/i;
|
|
12132
|
+
var EVAL_TEST_NAME = /\.evals?\.(?:test|spec)\.[^/.]+$/i;
|
|
12133
|
+
var EVAL_IMPORT = /(?:\bfrom\s+|\bimport\s*\(\s*|\brequire\s*\(\s*|^\s*import\s+)['"][^'"]*(?:\/(?:evals?|evaluations)\/|[\/._-]evals?(?:[._-][^'"/]*)?['"])/m;
|
|
12134
|
+
function evalDirectoryIndex(path) {
|
|
12135
|
+
return path.split("/").slice(0, -1).findIndex((segment) => EVAL_DIR_SEGMENT.test(segment) || EVAL_HARNESS_DIR_SEGMENT.test(segment));
|
|
12136
|
+
}
|
|
12137
|
+
function isEvalHarnessDirectory(path, index) {
|
|
12138
|
+
return index >= 0 && EVAL_HARNESS_DIR_SEGMENT.test(path.split("/")[index] ?? "");
|
|
12139
|
+
}
|
|
12140
|
+
function isEvalSuitePath(path) {
|
|
12141
|
+
const ext = extname(path).toLowerCase();
|
|
12142
|
+
const name = basename5(path);
|
|
12143
|
+
if (DOCS_ROOT.test(path) || HIDDEN_DIR.test(path) || /\.d\.[cm]?ts$/i.test(path) || EVAL_SEED_SCRIPT.test(name))
|
|
12144
|
+
return false;
|
|
12145
|
+
const inEvalDir = evalDirectoryIndex(path) >= 0;
|
|
12146
|
+
if (EVAL_CASE_DATA_EXTENSIONS.has(ext))
|
|
12147
|
+
return inEvalDir && !EVAL_NON_CASE_DATA.test(name);
|
|
12148
|
+
if (!EVAL_FILE_EXTENSIONS.has(ext))
|
|
12149
|
+
return false;
|
|
12150
|
+
return inEvalDir || EVAL_FILE_NAME.test(name) || SCRIPTS_ROOT.test(path) && EVAL_SCRIPT_NAME.test(name) || TEST_TREE.test(path) && EVALUATION_NAME.test(name);
|
|
12151
|
+
}
|
|
12152
|
+
function isTestOfEvalCode(path, text) {
|
|
12153
|
+
return TEST_FILE_NAME.test(path) && !EVAL_TEST_NAME.test(path) && evalDirectoryIndex(path) < 0 && EVAL_IMPORT.test(text);
|
|
12154
|
+
}
|
|
12155
|
+
function isEvalModelSource(path) {
|
|
12156
|
+
if (!isEvalSuitePath(path))
|
|
12157
|
+
return false;
|
|
12158
|
+
return evalDirectoryIndex(path) >= 0 || EVAL_TEST_NAME.test(path) || !TEST_FILE_NAME.test(path);
|
|
12159
|
+
}
|
|
12160
|
+
function evalFileSignal(path, text) {
|
|
12161
|
+
if (!isEvalSuitePath(path) || isTestOfEvalCode(path, text))
|
|
12162
|
+
return;
|
|
12163
|
+
return evalDirectoryIndex(path) < 0 ? "eval.file_name" : "eval.suite_dir";
|
|
12164
|
+
}
|
|
12165
|
+
var EVAL_RUNNER_NAMES = [
|
|
12166
|
+
/^(?:run|runner|main|index|eval|evaluate|cli)\.[^.]+$/i,
|
|
12167
|
+
/^(?:run|runner|main|eval|evaluate)[._-]/i,
|
|
12168
|
+
/(?:^|[._-])evals?(?:[._-]|$)/i
|
|
12169
|
+
];
|
|
12170
|
+
function evalSuiteKey(path) {
|
|
12171
|
+
const index = evalDirectoryIndex(path);
|
|
12172
|
+
const segments = path.split("/");
|
|
12173
|
+
if (isEvalHarnessDirectory(path, index))
|
|
12174
|
+
return segments.slice(0, index + 1).join("/");
|
|
12175
|
+
if (index < 0 || segments.length - 1 <= index + 1)
|
|
12176
|
+
return path;
|
|
12177
|
+
return segments.slice(0, index + 2).join("/");
|
|
12178
|
+
}
|
|
12179
|
+
function promoteEvalSuites(files, confidence2, naming) {
|
|
12180
|
+
const suites = new Map;
|
|
12181
|
+
for (const file of files) {
|
|
12182
|
+
const key = evalSuiteKey(file.path);
|
|
12183
|
+
suites.set(key, [...suites.get(key) ?? [], file]);
|
|
12184
|
+
}
|
|
12185
|
+
return [...suites].map(([key, members]) => {
|
|
12186
|
+
const sorted = [...members].sort((a, b) => a.path.localeCompare(b.path));
|
|
12187
|
+
const runner = EVAL_RUNNER_NAMES.map((name) => sorted.find((file) => name.test(basename5(file.path)))).find(Boolean) ?? sorted[0];
|
|
12188
|
+
const cited = [runner, ...sorted.filter((file) => file !== runner)];
|
|
12189
|
+
const isDirectory = key !== runner.path;
|
|
12190
|
+
const isHarness = isDirectory && isEvalHarnessDirectory(runner.path, evalDirectoryIndex(runner.path));
|
|
12191
|
+
return {
|
|
12192
|
+
id: naming.artifactId(key),
|
|
12193
|
+
type: "eval",
|
|
12194
|
+
name: isHarness ? `${key} eval harness` : isDirectory ? `${basename5(key)} eval suite` : basename5(key),
|
|
12195
|
+
scope: "global",
|
|
12196
|
+
sourcePath: runner.path,
|
|
12197
|
+
usedByAgentIds: [],
|
|
12198
|
+
confidence: confidence2,
|
|
12199
|
+
confidenceReason: isDirectory ? `Deterministic: eval suite directory ${key} (${members.length} source file${members.length === 1 ? "" : "s"}); not checked by the analyst.` : naming.fileReason(runner),
|
|
12200
|
+
evidence: cited.map((file) => ({ path: file.path, line: 1, reason: `deterministic detector ${file.signal}` }))
|
|
12201
|
+
};
|
|
12202
|
+
});
|
|
12203
|
+
}
|
|
12204
|
+
|
|
12205
|
+
// src/harness-candidates.ts
|
|
12115
12206
|
var MAX_CANDIDATES_PER_FILE_PER_SIGNAL = 20;
|
|
12116
12207
|
var MIN_PROMPT_LITERAL_CHARS = 160;
|
|
12117
12208
|
var CONFIDENCE_WEIGHT = {
|
|
@@ -12335,6 +12426,168 @@ var LINE_PATTERNS = [
|
|
|
12335
12426
|
}
|
|
12336
12427
|
];
|
|
12337
12428
|
var CODE_EXTENSIONS = new Set([...JS, ...PY]);
|
|
12429
|
+
var PROVIDER_MODEL_ID = /^(?:anthropic|openai|google|meta-llama|mistralai|deepseek|x-ai|perplexity|qwen|cohere|moonshotai|z-ai|minimax|amazon|nvidia|microsoft|ai21|nousresearch|bytedance|baidu|inception|openrouter)\/[a-z0-9][\w.:-]*$/i;
|
|
12430
|
+
var PROVIDER_COLON_MODEL_ID = /^(?:openai|anthropic|google_genai|google_vertexai|google|groq|mistralai|bedrock|bedrock_converse|azure_openai|ollama|fireworks|together|deepseek|xai|perplexity|cohere|openrouter):([a-z0-9][\w./:-]*)$/i;
|
|
12431
|
+
var CHOSEN_VALUE_PREFIX = /(?:\b[A-Za-z_]\w*\s*=|\bmodel\w*["']?\s*:)\s*$/i;
|
|
12432
|
+
function isChosenProviderColonModel(value, before) {
|
|
12433
|
+
const model = PROVIDER_COLON_MODEL_ID.exec(value)?.[1];
|
|
12434
|
+
return Boolean(model && PROVIDER_MODEL_NAME.test(model) && CHOSEN_VALUE_PREFIX.test(before));
|
|
12435
|
+
}
|
|
12436
|
+
var PROVIDER_MODEL_NAME = /\d|claude|gpt|gemini|gemma|llama|mistral|mixtral|qwen|deepseek|grok|sonar|kimi|glm|command|minimax|auto/i;
|
|
12437
|
+
var BARE_MODEL_ID = /^(?:claude-|gpt-|chatgpt-|gemini-|text-embedding-|deepseek-|mistral-|llama-?|grok-|command-|qwen|kimi-|glm-)[\w.:-]*$/i;
|
|
12438
|
+
var O_SERIES_MODEL_ID = /^o[1-9](?:-(?:mini|pro|preview|deep-research))*(?:-\d{4}-\d{2}-\d{2})?$/i;
|
|
12439
|
+
var NOT_MODEL_ID = /^(?:claude-(?:code|ai|desktop|agent-sdk)|gpt-tokenizer|gpt-3-encoder|gemini-cli)\b/i;
|
|
12440
|
+
var FILE_NAME_SUFFIX = /\.(?:[cm]?[jt]sx?|json|ya?ml|toml|md|txt|py|png|jpe?g|gif|svg|webp|css|html?)$/i;
|
|
12441
|
+
var MODEL_CANDIDATE_VALUE = /^[A-Za-z0-9][\w./:-]{2,80}$/;
|
|
12442
|
+
var IMPORT_LINE = /^\s*(?:import\b|export\s+(?:\*|\{[^}]*\})\s*from\b|from\s+\S+\s+import\b)|\brequire\s*\(|\bimport\s*\(/;
|
|
12443
|
+
var CLASS_ATTRIBUTE_PREFIX = /\bclass(?:Name|List)?\s*[=:]\s*\{?\s*$/;
|
|
12444
|
+
var NON_MODEL_SOURCE_PATH = /\.d\.[cm]?ts$|\.min\.[cm]?js$|(?:^|\/)(?:public|static)\/|(?:^|\/)(?:package(?:-lock)?\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb?)$/i;
|
|
12445
|
+
var MODEL_ID_EXTENSIONS = new Set([...CODE_EXTENSIONS, ".json", ".jsonc", ".yaml", ".yml", ".toml"]);
|
|
12446
|
+
function isModelIdLiteral(value) {
|
|
12447
|
+
if (value.length < 4 || value.length > 80 || /\s/.test(value) || NOT_MODEL_ID.test(value) || FILE_NAME_SUFFIX.test(value))
|
|
12448
|
+
return false;
|
|
12449
|
+
if (PROVIDER_MODEL_ID.test(value))
|
|
12450
|
+
return PROVIDER_MODEL_NAME.test(value.slice(value.indexOf("/") + 1));
|
|
12451
|
+
if (O_SERIES_MODEL_ID.test(value))
|
|
12452
|
+
return true;
|
|
12453
|
+
return BARE_MODEL_ID.test(value) && /\d/.test(value);
|
|
12454
|
+
}
|
|
12455
|
+
var HASH_COMMENT_EXTENSIONS = new Set([...PY, ".yaml", ".yml", ".toml"]);
|
|
12456
|
+
var REGEX_PRECEDER = /(?:^|[(,=:[!&|?{};+\-*%<>~^]|\b(?:return|typeof|case|in|of|delete|void|throw|new|yield|await))\s*$/;
|
|
12457
|
+
function startsRegexLiteral(line, start) {
|
|
12458
|
+
const next = line[start + 1];
|
|
12459
|
+
if (next === undefined || next === "/" || next === "*" || next === ">" || line[start - 1] === "<")
|
|
12460
|
+
return false;
|
|
12461
|
+
return REGEX_PRECEDER.test(line.slice(0, start));
|
|
12462
|
+
}
|
|
12463
|
+
function regexLiteralEnd(line, start) {
|
|
12464
|
+
let inClass = false;
|
|
12465
|
+
for (let j = start + 1;j < line.length; j += 1) {
|
|
12466
|
+
const ch = line[j];
|
|
12467
|
+
if (ch === "\\") {
|
|
12468
|
+
j += 1;
|
|
12469
|
+
continue;
|
|
12470
|
+
}
|
|
12471
|
+
if (ch === "[")
|
|
12472
|
+
inClass = true;
|
|
12473
|
+
else if (ch === "]")
|
|
12474
|
+
inClass = false;
|
|
12475
|
+
else if (ch === "/" && !inClass)
|
|
12476
|
+
return j + 1;
|
|
12477
|
+
}
|
|
12478
|
+
return line.length;
|
|
12479
|
+
}
|
|
12480
|
+
function stringLiteralsInLine(line, state, hashComments) {
|
|
12481
|
+
const out = [];
|
|
12482
|
+
const closeAt = (from, close, escapes) => {
|
|
12483
|
+
for (let j = from;j < line.length; j += 1) {
|
|
12484
|
+
if (escapes && line[j] === "\\") {
|
|
12485
|
+
j += 1;
|
|
12486
|
+
continue;
|
|
12487
|
+
}
|
|
12488
|
+
if (line.startsWith(close, j))
|
|
12489
|
+
return j;
|
|
12490
|
+
}
|
|
12491
|
+
return -1;
|
|
12492
|
+
};
|
|
12493
|
+
let i = 0;
|
|
12494
|
+
if (state.open) {
|
|
12495
|
+
const close = state.open === "/*" ? "*/" : state.open;
|
|
12496
|
+
const at = closeAt(0, close, state.open !== "/*");
|
|
12497
|
+
if (at < 0)
|
|
12498
|
+
return out;
|
|
12499
|
+
i = at + close.length;
|
|
12500
|
+
state.open = null;
|
|
12501
|
+
}
|
|
12502
|
+
while (i < line.length) {
|
|
12503
|
+
const ch = line[i];
|
|
12504
|
+
if (hashComments ? ch === "#" : line.startsWith("//", i))
|
|
12505
|
+
break;
|
|
12506
|
+
if (!hashComments && line.startsWith("/*", i)) {
|
|
12507
|
+
const at2 = closeAt(i + 2, "*/", false);
|
|
12508
|
+
if (at2 < 0) {
|
|
12509
|
+
state.open = "/*";
|
|
12510
|
+
break;
|
|
12511
|
+
}
|
|
12512
|
+
i = at2 + 2;
|
|
12513
|
+
continue;
|
|
12514
|
+
}
|
|
12515
|
+
if (!hashComments && ch === "/" && startsRegexLiteral(line, i)) {
|
|
12516
|
+
i = regexLiteralEnd(line, i);
|
|
12517
|
+
continue;
|
|
12518
|
+
}
|
|
12519
|
+
if (ch !== '"' && ch !== "'" && ch !== "`") {
|
|
12520
|
+
i += 1;
|
|
12521
|
+
continue;
|
|
12522
|
+
}
|
|
12523
|
+
const triple = hashComments && line.startsWith(ch.repeat(3), i);
|
|
12524
|
+
const quote = triple ? ch.repeat(3) : ch;
|
|
12525
|
+
const at = closeAt(i + quote.length, quote, true);
|
|
12526
|
+
if (at < 0) {
|
|
12527
|
+
if (triple || ch === "`")
|
|
12528
|
+
state.open = quote;
|
|
12529
|
+
break;
|
|
12530
|
+
}
|
|
12531
|
+
out.push({ value: line.slice(i + quote.length, at), index: i, end: at + quote.length });
|
|
12532
|
+
i = at + quote.length;
|
|
12533
|
+
}
|
|
12534
|
+
return out;
|
|
12535
|
+
}
|
|
12536
|
+
function modelIdLiteralsInScannedLine(line, state, hashComments) {
|
|
12537
|
+
const literals = stringLiteralsInLine(line, state, hashComments);
|
|
12538
|
+
if (line.length > 4000 || IMPORT_LINE.test(line))
|
|
12539
|
+
return [];
|
|
12540
|
+
return literals.filter((literal) => {
|
|
12541
|
+
if (!MODEL_CANDIDATE_VALUE.test(literal.value))
|
|
12542
|
+
return false;
|
|
12543
|
+
const before = line.slice(0, literal.index);
|
|
12544
|
+
return (isModelIdLiteral(literal.value) || isChosenProviderColonModel(literal.value, before)) && !CLASS_ATTRIBUTE_PREFIX.test(before);
|
|
12545
|
+
});
|
|
12546
|
+
}
|
|
12547
|
+
function modelIdLiteralsInLines(lines, path) {
|
|
12548
|
+
const hashComments = HASH_COMMENT_EXTENSIONS.has(extname2(path).toLowerCase());
|
|
12549
|
+
const state = { open: null };
|
|
12550
|
+
return lines.map((line) => modelIdLiteralsInScannedLine(line, state, hashComments));
|
|
12551
|
+
}
|
|
12552
|
+
var HIGH_CONFIDENCE_MODEL_PATTERNS = LINE_PATTERNS.filter((pattern) => pattern.kind === "model_config" && pattern.confidence === "high");
|
|
12553
|
+
function detectModelIdLiterals(file, out) {
|
|
12554
|
+
if (!MODEL_ID_EXTENSIONS.has(extname2(file.path).toLowerCase()) || NON_MODEL_SOURCE_PATH.test(file.path))
|
|
12555
|
+
return;
|
|
12556
|
+
const lines = file.text.split(`
|
|
12557
|
+
`);
|
|
12558
|
+
const literals = modelIdLiteralsInLines(lines, file.path);
|
|
12559
|
+
for (let i = 0;i < lines.length; i += 1) {
|
|
12560
|
+
const line = lines[i];
|
|
12561
|
+
if (literals[i].length === 0)
|
|
12562
|
+
continue;
|
|
12563
|
+
if (HIGH_CONFIDENCE_MODEL_PATTERNS.some((pattern) => pattern.pattern.test(line)))
|
|
12564
|
+
continue;
|
|
12565
|
+
out.push({
|
|
12566
|
+
kind: "model_config",
|
|
12567
|
+
path: file.path,
|
|
12568
|
+
startLine: i + 1,
|
|
12569
|
+
endLine: i + 1,
|
|
12570
|
+
confidence: "high",
|
|
12571
|
+
signal: "model.id_literal",
|
|
12572
|
+
excerpt: normalizeExcerpt(line)
|
|
12573
|
+
});
|
|
12574
|
+
}
|
|
12575
|
+
}
|
|
12576
|
+
function detectEvalFile(file, out) {
|
|
12577
|
+
const signal = evalFileSignal(file.path, file.text);
|
|
12578
|
+
if (!signal)
|
|
12579
|
+
return;
|
|
12580
|
+
out.push({
|
|
12581
|
+
kind: "eval_file",
|
|
12582
|
+
path: file.path,
|
|
12583
|
+
startLine: 1,
|
|
12584
|
+
endLine: Math.max(1, file.text.split(`
|
|
12585
|
+
`).length),
|
|
12586
|
+
confidence: "high",
|
|
12587
|
+
signal,
|
|
12588
|
+
excerpt: normalizeExcerpt(file.text.slice(0, 300))
|
|
12589
|
+
});
|
|
12590
|
+
}
|
|
12338
12591
|
var PROMPT_DOC_EXTENSIONS = new Set([".md", ".mdc", ".txt", ".prompt", ".jinja", ".jinja2", ".j2", ".hbs", ".tpl"]);
|
|
12339
12592
|
var UPPERCASE_DOC_NAME = /^[A-Z0-9][A-Z0-9_-]*\.(?:md|mdc|txt)$/;
|
|
12340
12593
|
var NON_PROMPT_DOC_BASENAMES = new Set([
|
|
@@ -12396,7 +12649,7 @@ function lineAtIndex(offsets, index) {
|
|
|
12396
12649
|
return lo + 1;
|
|
12397
12650
|
}
|
|
12398
12651
|
function detectLinePatterns(file, out) {
|
|
12399
|
-
const ext =
|
|
12652
|
+
const ext = extname2(file.path).toLowerCase();
|
|
12400
12653
|
const lines = file.text.split(`
|
|
12401
12654
|
`);
|
|
12402
12655
|
const perSignal = new Map;
|
|
@@ -12473,7 +12726,7 @@ function detectAdjacentStringBlocks(file, out) {
|
|
|
12473
12726
|
}
|
|
12474
12727
|
var PROMPT_MODULE_NAME_HINT = /(^|[._-])(system[._-]?)?prompts?([._-]|$)/i;
|
|
12475
12728
|
function detectPromptModule(file, out) {
|
|
12476
|
-
const base =
|
|
12729
|
+
const base = basename6(file.path, extname2(file.path));
|
|
12477
12730
|
if (!PROMPT_MODULE_NAME_HINT.test(base))
|
|
12478
12731
|
return;
|
|
12479
12732
|
out.push({
|
|
@@ -12488,7 +12741,7 @@ function detectPromptModule(file, out) {
|
|
|
12488
12741
|
});
|
|
12489
12742
|
}
|
|
12490
12743
|
function detectPromptLiterals(file, out) {
|
|
12491
|
-
const ext =
|
|
12744
|
+
const ext = extname2(file.path).toLowerCase();
|
|
12492
12745
|
const offsets = buildLineIndex(file.text);
|
|
12493
12746
|
let found = 0;
|
|
12494
12747
|
const record = (startIndex, endIndex, content, signal) => {
|
|
@@ -12533,10 +12786,10 @@ function detectPromptLiterals(file, out) {
|
|
|
12533
12786
|
}
|
|
12534
12787
|
}
|
|
12535
12788
|
function detectPromptFile(file, out) {
|
|
12536
|
-
const ext =
|
|
12789
|
+
const ext = extname2(file.path).toLowerCase();
|
|
12537
12790
|
if (!PROMPT_DOC_EXTENSIONS.has(ext))
|
|
12538
12791
|
return;
|
|
12539
|
-
const base =
|
|
12792
|
+
const base = basename6(file.path).toLowerCase();
|
|
12540
12793
|
if (NON_PROMPT_DOC_BASENAMES.has(base))
|
|
12541
12794
|
return;
|
|
12542
12795
|
if (/(^|\/)(docs?|documentation|examples?|\.github)\//i.test(file.path))
|
|
@@ -12553,7 +12806,7 @@ function detectPromptFile(file, out) {
|
|
|
12553
12806
|
} else if (PROMPTS_DIR.test(dir)) {
|
|
12554
12807
|
confidence2 = "high";
|
|
12555
12808
|
signal = "prompt_file.prompts_dir";
|
|
12556
|
-
} else if (dirHint && score >= 3 && !UPPERCASE_DOC_NAME.test(
|
|
12809
|
+
} else if (dirHint && score >= 3 && !UPPERCASE_DOC_NAME.test(basename6(file.path))) {
|
|
12557
12810
|
confidence2 = "medium";
|
|
12558
12811
|
signal = "prompt_file.agent_dir_instruction_text";
|
|
12559
12812
|
} else if (score >= 5) {
|
|
@@ -12574,10 +12827,10 @@ function detectPromptFile(file, out) {
|
|
|
12574
12827
|
});
|
|
12575
12828
|
}
|
|
12576
12829
|
function detectPromptLoaderReferences(file, filesByPath, out) {
|
|
12577
|
-
if (!LOADER_CODE_EXTENSIONS.has(
|
|
12830
|
+
if (!LOADER_CODE_EXTENSIONS.has(extname2(file.path).toLowerCase()) && extname2(file.path).toLowerCase() !== ".md")
|
|
12578
12831
|
return;
|
|
12579
12832
|
const extension = "(?:\\.txt|\\.md)";
|
|
12580
|
-
const pattern =
|
|
12833
|
+
const pattern = extname2(file.path).toLowerCase() === ".rs" ? new RegExp(`\\binclude_str!\\s*\\(\\s*["']([^"']+${extension})["']\\s*\\)`, "g") : new RegExp(`\\b(?:Template\\.from_file|from_file|read_text|readFileSync)\\s*\\(\\s*["']([^"']+${extension})["']`, "g");
|
|
12581
12834
|
let found = 0;
|
|
12582
12835
|
let match;
|
|
12583
12836
|
while (found < MAX_CANDIDATES_PER_FILE_PER_SIGNAL && (match = pattern.exec(file.text)) !== null) {
|
|
@@ -12585,7 +12838,7 @@ function detectPromptLoaderReferences(file, filesByPath, out) {
|
|
|
12585
12838
|
const target = normalizeLoaderTarget(file.path, reference);
|
|
12586
12839
|
if (!target || !filesByPath.has(target))
|
|
12587
12840
|
continue;
|
|
12588
|
-
const targetBase =
|
|
12841
|
+
const targetBase = basename6(target).toLowerCase();
|
|
12589
12842
|
const highConfidence = RUST_PROMPT_PATH_HINT.test(target) || /Template\.from_file/i.test(match[0]);
|
|
12590
12843
|
if (!highConfidence)
|
|
12591
12844
|
continue;
|
|
@@ -12597,7 +12850,7 @@ function detectPromptLoaderReferences(file, filesByPath, out) {
|
|
|
12597
12850
|
endLine: 1,
|
|
12598
12851
|
confidence: "high",
|
|
12599
12852
|
signal: /Template\.from_file/i.test(match[0]) ? "prompt_file.template_loader_reference" : "prompt_file.loader_reference",
|
|
12600
|
-
excerpt: normalizeExcerpt(`${
|
|
12853
|
+
excerpt: normalizeExcerpt(`${basename6(file.path)} loads ${targetBase}`)
|
|
12601
12854
|
});
|
|
12602
12855
|
}
|
|
12603
12856
|
}
|
|
@@ -12625,10 +12878,12 @@ function detectHarnessCandidatesInFiles(files) {
|
|
|
12625
12878
|
if (typeof file.text !== "string" || file.text.length === 0)
|
|
12626
12879
|
continue;
|
|
12627
12880
|
const scoped = { path: file.path, text: file.text };
|
|
12628
|
-
const ext =
|
|
12881
|
+
const ext = extname2(file.path).toLowerCase();
|
|
12629
12882
|
if (CODE_EXTENSIONS.has(ext) || ext === ".json" || ext === ".yaml" || ext === ".yml" || ext === ".toml") {
|
|
12630
12883
|
detectLinePatterns(scoped, candidates);
|
|
12631
12884
|
}
|
|
12885
|
+
detectModelIdLiterals(scoped, candidates);
|
|
12886
|
+
detectEvalFile(scoped, candidates);
|
|
12632
12887
|
if (CODE_EXTENSIONS.has(ext)) {
|
|
12633
12888
|
detectAdjacentStringBlocks(scoped, candidates);
|
|
12634
12889
|
detectPromptModule(scoped, candidates);
|
|
@@ -12658,7 +12913,7 @@ function detectHarnessCandidates(rootDir) {
|
|
|
12658
12913
|
const skipped = [];
|
|
12659
12914
|
const root = resolve6(rootDir);
|
|
12660
12915
|
const inventory = inventoryFiles(root, skipped, {
|
|
12661
|
-
includeFile: (path) => PROMPT_DOC_EXTENSIONS.has(
|
|
12916
|
+
includeFile: (path) => PROMPT_DOC_EXTENSIONS.has(extname2(path).toLowerCase()) || [".json", ".jsonc", ".jsonl", ".yaml", ".yml", ".toml"].includes(extname2(path).toLowerCase()) || extname2(path).toLowerCase() === ".rs" && RUST_PROMPT_PATH_HINT.test(path)
|
|
12662
12917
|
});
|
|
12663
12918
|
const { candidates, files } = detectHarnessCandidatesInFiles(inventory);
|
|
12664
12919
|
const durationMs = Math.max(1, Date.now() - startedAt);
|
|
@@ -12691,7 +12946,7 @@ function writeHarnessCandidatesReport(report, outPath) {
|
|
|
12691
12946
|
|
|
12692
12947
|
// src/harness-prompt-sources.ts
|
|
12693
12948
|
import { createHash as createHash4 } from "node:crypto";
|
|
12694
|
-
import { extname as
|
|
12949
|
+
import { extname as extname7 } from "node:path";
|
|
12695
12950
|
|
|
12696
12951
|
// src/prompt-source-verification/index.ts
|
|
12697
12952
|
import { readFileSync as readFileSync10 } from "node:fs";
|
|
@@ -12699,7 +12954,7 @@ import { z } from "zod";
|
|
|
12699
12954
|
|
|
12700
12955
|
// src/prompt-source-validation.ts
|
|
12701
12956
|
import { existsSync as existsSync7, lstatSync as lstatSync5 } from "node:fs";
|
|
12702
|
-
import { extname as
|
|
12957
|
+
import { extname as extname3, relative as relative4, resolve as resolve7 } from "node:path";
|
|
12703
12958
|
function checkPromptMapping(rootDir, unknownMapping) {
|
|
12704
12959
|
const repairs = [];
|
|
12705
12960
|
const unresolved = [];
|
|
@@ -12779,12 +13034,12 @@ function normalizePromptMapping(value) {
|
|
|
12779
13034
|
return normalized;
|
|
12780
13035
|
}
|
|
12781
13036
|
function isPromptDefinitionPath(path) {
|
|
12782
|
-
return /(^|\/)(?:prompts?|templates?)(?:\/|\.)/i.test(path) || /\.prompt\.(?:md|txt|ya?ml|json)$/i.test(path) || [".jinja", ".j2", ".mustache", ".hbs"].includes(
|
|
13037
|
+
return /(^|\/)(?:prompts?|templates?)(?:\/|\.)/i.test(path) || /\.prompt\.(?:md|txt|ya?ml|json)$/i.test(path) || [".jinja", ".j2", ".mustache", ".hbs"].includes(extname3(path).toLowerCase());
|
|
12783
13038
|
}
|
|
12784
13039
|
|
|
12785
13040
|
// src/prompt-source-verification/evidence.ts
|
|
12786
13041
|
import { readFileSync as readFileSync9 } from "node:fs";
|
|
12787
|
-
import { extname as
|
|
13042
|
+
import { extname as extname6 } from "node:path";
|
|
12788
13043
|
|
|
12789
13044
|
// src/prompt-source-verification/paths.ts
|
|
12790
13045
|
import { lstatSync as lstatSync6, realpathSync as realpathSync6 } from "node:fs";
|
|
@@ -12812,13 +13067,13 @@ function safePromptEvidencePath(rootDir, path) {
|
|
|
12812
13067
|
// src/prompt-source-verification/sink-proof.ts
|
|
12813
13068
|
var import_typescript = __toESM(require_typescript(), 1);
|
|
12814
13069
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
12815
|
-
import { extname as
|
|
13070
|
+
import { extname as extname4 } from "node:path";
|
|
12816
13071
|
import { readFileSync as readFileSync7 } from "node:fs";
|
|
12817
13072
|
function proveSink(root, sink) {
|
|
12818
13073
|
const file = safePromptEvidencePath(root, sink.path);
|
|
12819
13074
|
const cache = new Map;
|
|
12820
13075
|
if (!cache.has(file)) {
|
|
12821
|
-
if (
|
|
13076
|
+
if (extname4(file) === ".py") {
|
|
12822
13077
|
try {
|
|
12823
13078
|
const rows = JSON.parse(execFileSync2("python3", ["-I", "-c", PYTHON_PROOF], { input: readFileSync7(file, "utf8"), encoding: "utf8", timeout: 1e4, maxBuffer: 2 * 1024 * 1024 }));
|
|
12824
13079
|
cache.set(file, new Map(Object.entries(rows).map(([line, proof]) => [Number(line), proof])));
|
|
@@ -12988,7 +13243,7 @@ print(json.dumps(rows))
|
|
|
12988
13243
|
|
|
12989
13244
|
// src/prompt-source-location.ts
|
|
12990
13245
|
import { readFileSync as readFileSync8 } from "node:fs";
|
|
12991
|
-
import { extname as
|
|
13246
|
+
import { extname as extname5 } from "node:path";
|
|
12992
13247
|
function structuredExcerpt(text, source) {
|
|
12993
13248
|
if (source.selector?.pointer === undefined)
|
|
12994
13249
|
return { line: 1, endLine: text.split(`
|
|
@@ -13028,7 +13283,7 @@ function structuredExcerpt(text, source) {
|
|
|
13028
13283
|
}
|
|
13029
13284
|
function promptDefinitionExcerpt(rootDir, source) {
|
|
13030
13285
|
const file = safePromptEvidencePath(rootDir, source.path);
|
|
13031
|
-
const ext =
|
|
13286
|
+
const ext = extname5(file).toLowerCase();
|
|
13032
13287
|
if (ext === ".py") {
|
|
13033
13288
|
const extraction = extractPythonSource(rootDir, file, source.selector?.symbol);
|
|
13034
13289
|
if (!extraction.range)
|
|
@@ -13052,7 +13307,7 @@ function promptDefinitionRange(rootDir, source) {
|
|
|
13052
13307
|
// src/prompt-source-verification/evidence.ts
|
|
13053
13308
|
function verifyPromptSourceEvidence(rootDir, source) {
|
|
13054
13309
|
const checked = checkPromptMapping(rootDir, source);
|
|
13055
|
-
const dedicated = isPromptDefinitionPath(source.path) && [".md", ".txt", ".jinja", ".j2", ".mustache", ".hbs"].includes(
|
|
13310
|
+
const dedicated = isPromptDefinitionPath(source.path) && [".md", ".txt", ".jinja", ".j2", ".mustache", ".hbs"].includes(extname6(source.path).toLowerCase());
|
|
13056
13311
|
return {
|
|
13057
13312
|
accepted: checked.ok && dedicated && checked.kind !== "builder",
|
|
13058
13313
|
reason: dedicated ? "Dedicated authored prompt file" : "Candidate requires independent semantic review",
|
|
@@ -13306,7 +13561,7 @@ function discoverHarnessPromptSources(rootDir, existing = []) {
|
|
|
13306
13561
|
}
|
|
13307
13562
|
try {
|
|
13308
13563
|
let found = [];
|
|
13309
|
-
const ext =
|
|
13564
|
+
const ext = extname7(path).toLowerCase();
|
|
13310
13565
|
if (CODE.has(ext)) {
|
|
13311
13566
|
if (attempts++ >= MAX_CODE_PROMPT_PATHS) {
|
|
13312
13567
|
issues.push({ path, reason: "Code prompt scan limit (64 files) reached; deferred to analyst review" });
|
|
@@ -13574,8 +13829,9 @@ function applyPromptDiscovery(graph, discovery) {
|
|
|
13574
13829
|
}
|
|
13575
13830
|
|
|
13576
13831
|
// src/harness-candidate-promotion.ts
|
|
13577
|
-
import {
|
|
13578
|
-
import {
|
|
13832
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
13833
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync11, statSync as statSync3 } from "node:fs";
|
|
13834
|
+
import { extname as extname8, posix, resolve as resolve9 } from "node:path";
|
|
13579
13835
|
|
|
13580
13836
|
// src/harness-js-source.ts
|
|
13581
13837
|
var import_typescript2 = __toESM(require_typescript(), 1);
|
|
@@ -13944,7 +14200,11 @@ var SIGNAL_DESCRIPTIONS = {
|
|
|
13944
14200
|
"prompt_file.agent_dir_instruction_text": "instruction-like text in an agent directory",
|
|
13945
14201
|
"prompt_file.loader_reference": "a file loaded as a prompt by code",
|
|
13946
14202
|
"prompt_file.template_loader_reference": "a file loaded as a prompt template by code",
|
|
13947
|
-
"model.assignment": "a model id assigned in code"
|
|
14203
|
+
"model.assignment": "a model id assigned in code",
|
|
14204
|
+
"model.registry_key": "a model registry key",
|
|
14205
|
+
"model.id_literal": "a model id string literal",
|
|
14206
|
+
"eval.suite_dir": "a file under an eval directory",
|
|
14207
|
+
"eval.file_name": "a file named as an eval"
|
|
13948
14208
|
};
|
|
13949
14209
|
function detectorConfidenceReason(candidate) {
|
|
13950
14210
|
const what = SIGNAL_DESCRIPTIONS[candidate.signal] ?? `the ${candidate.signal} pattern`;
|
|
@@ -14420,7 +14680,7 @@ function localNameIn(specifiers, identifier) {
|
|
|
14420
14680
|
function importedLocalName(importerPath, lines, identifier, definitionPath, exportsDefault = false) {
|
|
14421
14681
|
const text = lines.join(`
|
|
14422
14682
|
`);
|
|
14423
|
-
const python =
|
|
14683
|
+
const python = extname8(importerPath) === ".py";
|
|
14424
14684
|
if (python) {
|
|
14425
14685
|
for (const match of text.matchAll(/^[ \t]*from\s+([.\w]+)\s+import\s+(\([^)]*\)|[^\n]*)/gm)) {
|
|
14426
14686
|
if (!specifierResolvesTo(match[1], importerPath, definitionPath, true))
|
|
@@ -14480,7 +14740,7 @@ function calleeImportedFromModelFile(callee, path, lines, modelFiles) {
|
|
|
14480
14740
|
const base = callee.split(".")[0];
|
|
14481
14741
|
const text = lines.join(`
|
|
14482
14742
|
`);
|
|
14483
|
-
const python =
|
|
14743
|
+
const python = extname8(path) === ".py";
|
|
14484
14744
|
const statements = python ? [/^[ \t]*from\s+([.\w]+)\s+import\s+(\([^)]*\)|[^\n]*)/gm] : [
|
|
14485
14745
|
/\bimport\s+(?:type\s+)?(?:([A-Za-z_$][\w$]*)\s*,?\s*)?(?:\{([^}]*)\}|\*\s*as\s+([A-Za-z_$][\w$]*))?\s*from\s*["']([^"']+)["']/g,
|
|
14486
14746
|
/\b(?:const|let|var)\s*\{([^}]*)\}\s*=\s*(?:await\s+)?(?:require|import)\(\s*["']([^"']+)["']\s*\)/g
|
|
@@ -14703,7 +14963,7 @@ function hasNearbyRole(lines, line) {
|
|
|
14703
14963
|
return false;
|
|
14704
14964
|
}
|
|
14705
14965
|
function isDirectModelInput(key, value, path, lines, line, modelFiles) {
|
|
14706
|
-
if (CONFIG_EXTENSIONS.has(
|
|
14966
|
+
if (CONFIG_EXTENSIONS.has(extname8(path).toLowerCase())) {
|
|
14707
14967
|
return DIRECT_PROMPT_KEYS.has(key) && value.length >= MIN_CONFIG_PROMPT_CHARS;
|
|
14708
14968
|
}
|
|
14709
14969
|
if (modelFiles.has(path)) {
|
|
@@ -14743,7 +15003,7 @@ function fileReachesModel(path, lines, modelFiles) {
|
|
|
14743
15003
|
return true;
|
|
14744
15004
|
if (lines.some((line) => MODEL_ENDPOINT.test(line)))
|
|
14745
15005
|
return true;
|
|
14746
|
-
const python =
|
|
15006
|
+
const python = extname8(path) === ".py";
|
|
14747
15007
|
return importSpecifiers(lines).some((spec) => [...modelFiles].some((modelFile) => specifierResolvesTo(spec, path, modelFile, python)));
|
|
14748
15008
|
}
|
|
14749
15009
|
function modelFileImports(path, rootDir, modelFiles, fileCache) {
|
|
@@ -14751,7 +15011,7 @@ function modelFileImports(path, rootDir, modelFiles, fileCache) {
|
|
|
14751
15011
|
if (modelFile === path)
|
|
14752
15012
|
return false;
|
|
14753
15013
|
const lines = readFileLines(rootDir, modelFile, fileCache);
|
|
14754
|
-
return Boolean(lines && importSpecifiers(lines).some((spec) => specifierResolvesTo(spec, modelFile, path,
|
|
15014
|
+
return Boolean(lines && importSpecifiers(lines).some((spec) => specifierResolvesTo(spec, modelFile, path, extname8(modelFile) === ".py")));
|
|
14755
15015
|
});
|
|
14756
15016
|
}
|
|
14757
15017
|
var PASS_THROUGH_CALLEE = /^(?:Object\.(?:freeze|seal|assign)|structuredClone|Promise\.resolve|JSON\.stringify|(?:_\.|lodash\.)?(?:cloneDeep|clone|merge)|deepFreeze|defu)$/;
|
|
@@ -14821,7 +15081,7 @@ function promotePromptLiteralCandidate(candidate, rootDir, confidence2, fileCach
|
|
|
14821
15081
|
use = { path: candidate.path, line: definition.line, key: "system-injection" };
|
|
14822
15082
|
}
|
|
14823
15083
|
const jsSource = jsSources.get(`${rootDir}::${candidate.path}`);
|
|
14824
|
-
if (!use && definition.key && !PROMPT_CONFIG_KEYS.has(definition.key) && !CONFIG_EXTENSIONS.has(
|
|
15084
|
+
if (!use && definition.key && !PROMPT_CONFIG_KEYS.has(definition.key) && !CONFIG_EXTENSIONS.has(extname8(candidate.path).toLowerCase())) {
|
|
14825
15085
|
const exportUsed = () => modelFileImports(candidate.path, rootDir, files.model, fileCache);
|
|
14826
15086
|
if (jsSource) {
|
|
14827
15087
|
const consumer = keyedValueConsumer(jsSource, definition.line, definition.key, definition.column);
|
|
@@ -14871,63 +15131,248 @@ function promotePromptLiteralCandidate(candidate, rootDir, confidence2, fileCach
|
|
|
14871
15131
|
body: definition.text
|
|
14872
15132
|
};
|
|
14873
15133
|
}
|
|
14874
|
-
|
|
14875
|
-
|
|
14876
|
-
|
|
14877
|
-
|
|
14878
|
-
|
|
14879
|
-
|
|
14880
|
-
|
|
14881
|
-
|
|
14882
|
-
|
|
14883
|
-
|
|
14884
|
-
|
|
14885
|
-
|
|
14886
|
-
|
|
14887
|
-
|
|
14888
|
-
|
|
14889
|
-
|
|
14890
|
-
|
|
14891
|
-
|
|
14892
|
-
|
|
14893
|
-
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
|
|
15134
|
+
var ALIAS_KEY_SAME_LINE = /^\s*:\s*\{[^}]*?["']?\b(?:model|model_name|model_id|modelName|modelId|[A-Z_]*MODEL[A-Z_]*)["']?\s*[:=]/i;
|
|
15135
|
+
var BLOCK_KEY = /^\s*:\s*\{\s*$/;
|
|
15136
|
+
var MODEL_FIELD = /["']?\b(?:model|model_name|model_id|modelName|modelId|[A-Z_]*MODEL[A-Z_]*)["']?\s*[:=]\s*["'`]/i;
|
|
15137
|
+
var ASSIGNED_FIELD = /([A-Za-z_$][\w$]*)["']?\s*[:=]\s*(?:[A-Za-z_$][\w$.()]*\(\s*)?$/;
|
|
15138
|
+
var COMPARED_BEFORE = /(?:\.(?:startsWith|endsWith|includes|indexOf|match|test)\(\s*|[!=]==?\s*|\bcase\s+|[\w$\])]\[\s*)$/;
|
|
15139
|
+
var COMPARED_AFTER = /^\s*[!=]==?/;
|
|
15140
|
+
function isComparedLiteral(sourceLine, literal) {
|
|
15141
|
+
return COMPARED_BEFORE.test(sourceLine.slice(0, literal.index)) || COMPARED_AFTER.test(sourceLine.slice(literal.end));
|
|
15142
|
+
}
|
|
15143
|
+
var ALIAS_BLOCK_MAX_LINES = 200;
|
|
15144
|
+
function isModelAliasKey(lines, row, literal, path = "") {
|
|
15145
|
+
const after = lines[row].slice(literal.end);
|
|
15146
|
+
if (ALIAS_KEY_SAME_LINE.test(after))
|
|
15147
|
+
return true;
|
|
15148
|
+
const separator = /^\s*:\s*/.exec(after);
|
|
15149
|
+
if (separator && modelIdLiteralsAt(lines, path, row).some((other) => other.index === literal.end + separator[0].length))
|
|
15150
|
+
return true;
|
|
15151
|
+
if (!BLOCK_KEY.test(after))
|
|
15152
|
+
return false;
|
|
15153
|
+
let depth = 1;
|
|
15154
|
+
for (const next of lines.slice(row + 1, row + 1 + ALIAS_BLOCK_MAX_LINES)) {
|
|
15155
|
+
if (depth === 1 && MODEL_FIELD.test(next))
|
|
15156
|
+
return true;
|
|
15157
|
+
for (const ch of next) {
|
|
15158
|
+
if (ch === "{")
|
|
15159
|
+
depth += 1;
|
|
15160
|
+
else if (ch === "}")
|
|
15161
|
+
depth -= 1;
|
|
15162
|
+
}
|
|
15163
|
+
if (depth <= 0)
|
|
15164
|
+
return false;
|
|
15165
|
+
}
|
|
15166
|
+
return false;
|
|
14899
15167
|
}
|
|
14900
|
-
var
|
|
14901
|
-
|
|
14902
|
-
|
|
14903
|
-
|
|
14904
|
-
|
|
14905
|
-
|
|
14906
|
-
|
|
14907
|
-
|
|
14908
|
-
|
|
14909
|
-
|
|
15168
|
+
var lineLiterals = new WeakMap;
|
|
15169
|
+
function modelIdLiteralsAt(lines, path, row) {
|
|
15170
|
+
let literals = lineLiterals.get(lines);
|
|
15171
|
+
if (!literals) {
|
|
15172
|
+
literals = modelIdLiteralsInLines(lines, path);
|
|
15173
|
+
lineLiterals.set(lines, literals);
|
|
15174
|
+
}
|
|
15175
|
+
return literals[row] ?? [];
|
|
15176
|
+
}
|
|
15177
|
+
function modelIdsOnCandidateLine(candidate, lines) {
|
|
15178
|
+
const rows = lines ?? [];
|
|
15179
|
+
const row = candidate.startLine - 1;
|
|
15180
|
+
const sourceLine = rows[row] ?? "";
|
|
15181
|
+
const ids = [];
|
|
15182
|
+
const literals = modelIdLiteralsAt(rows, candidate.path, row);
|
|
15183
|
+
const isAliasKey = (value) => literals.some((literal) => literal.value === value && isModelAliasKey(rows, row, literal, candidate.path));
|
|
15184
|
+
if (candidate.signal !== "model.id_literal") {
|
|
15185
|
+
const assignment = MODEL_ASSIGNMENT_VALUE.exec(sourceLine);
|
|
15186
|
+
const [field, assigned] = [assignment?.[1], assignment?.[2]];
|
|
15187
|
+
const preferenceOnly = Boolean(field && MODEL_PREFERENCE_FIELD.test(field) && !FULL_MODEL_ID.test(assigned ?? ""));
|
|
15188
|
+
const registryKey = candidate.signal === "model.registry_key" ? REGISTRY_KEY.exec(sourceLine)?.[1] : undefined;
|
|
15189
|
+
const primary = preferenceOnly ? undefined : (assigned && MODEL_STRING.exec(assigned)?.[1]) ?? registryKey ?? MODEL_STRING.exec(sourceLine)?.[1] ?? MODEL_STRING.exec(candidate.excerpt)?.[1];
|
|
15190
|
+
const compared = (value) => literals.some((literal) => literal.value === value && isComparedLiteral(sourceLine, literal));
|
|
15191
|
+
if (primary && !isAliasKey(primary) && !compared(primary))
|
|
15192
|
+
ids.push(primary);
|
|
15193
|
+
}
|
|
15194
|
+
for (const literal of literals) {
|
|
15195
|
+
const { value, index } = literal;
|
|
15196
|
+
if (ids.includes(value) || isModelAliasKey(rows, row, literal, candidate.path))
|
|
15197
|
+
continue;
|
|
15198
|
+
if (isComparedLiteral(sourceLine, literal))
|
|
15199
|
+
continue;
|
|
15200
|
+
const field = ASSIGNED_FIELD.exec(sourceLine.slice(0, index))?.[1];
|
|
15201
|
+
if (field && MODEL_PREFERENCE_FIELD.test(field) && !FULL_MODEL_ID.test(value))
|
|
15202
|
+
continue;
|
|
15203
|
+
ids.push(value);
|
|
14910
15204
|
}
|
|
14911
|
-
|
|
14912
|
-
|
|
15205
|
+
return ids.filter((id) => !MODEL_FAMILY_ALIAS.test(id.split("/").pop() ?? id));
|
|
15206
|
+
}
|
|
15207
|
+
var OTHER_TEST_PATH = /(?:^|\/)(?:e2e|testdata|__mocks__|spec)\/|\.stories\.[^/.]+$|(?:^|\/)(?:test_[^/]*|[^/]*_test|conftest)\.py$/i;
|
|
15208
|
+
var NON_SOURCE_DIR = /(^|\/)(?:docs?|\.moda)\//i;
|
|
15209
|
+
var EMPTY_MODEL_PATH_CONTEXT = { productionScripts: new Set, rewritingPackages: new Set };
|
|
15210
|
+
function isHarnessModelPath(path, context = EMPTY_MODEL_PATH_CONTEXT) {
|
|
15211
|
+
if (NON_SOURCE_DIR.test(path))
|
|
15212
|
+
return false;
|
|
15213
|
+
if (isEvalModelSource(path))
|
|
15214
|
+
return true;
|
|
15215
|
+
if (SCRIPTS_DIR.test(path) && !isUnderAny(path, context.productionScripts))
|
|
15216
|
+
return false;
|
|
15217
|
+
if (isUnderAny(path, context.rewritingPackages) && !SERVER_SIDE_PATH.test(path))
|
|
15218
|
+
return false;
|
|
15219
|
+
return evalDirectoryIndex(path) < 0 && !TEST_PATH2.test(path) && !OTHER_TEST_PATH.test(path);
|
|
15220
|
+
}
|
|
15221
|
+
function modelPathContext(rootDir) {
|
|
15222
|
+
return { productionScripts: productionScriptPaths(rootDir), rewritingPackages: modelRewritingPackages(rootDir) };
|
|
15223
|
+
}
|
|
15224
|
+
var SERVER_SIDE_PATH = /(?:^|\/)(?:server|host|api)\/|[.+]server\.[cm]?[jt]s$/;
|
|
15225
|
+
var MODEL_REWRITE_CALL = /\bmodel\s*[:=]\s*[a-z]\w*\(\s*(?:\w+\.)*(?:requested\w*|\w*[mM]odel\w*)\s*,/;
|
|
15226
|
+
var REWRITE_SOURCE_FILE = /\.(?:[cm]?[jt]sx?|svelte|vue|py)$/i;
|
|
15227
|
+
var modelRewriteCache = new Map;
|
|
15228
|
+
function modelRewritingPackages(rootDir) {
|
|
15229
|
+
const root = resolve9(rootDir);
|
|
15230
|
+
const cached = modelRewriteCache.get(root);
|
|
15231
|
+
if (cached)
|
|
14913
15232
|
return cached;
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
15233
|
+
const packageRoots = new Set;
|
|
15234
|
+
const sources = [];
|
|
15235
|
+
walkRepo(root, (path, name) => {
|
|
15236
|
+
if (name === "package.json" || name === "pyproject.toml")
|
|
15237
|
+
packageRoots.add(posix.dirname(path));
|
|
15238
|
+
else if (REWRITE_SOURCE_FILE.test(name))
|
|
15239
|
+
sources.push(path);
|
|
15240
|
+
});
|
|
15241
|
+
const found = new Set;
|
|
15242
|
+
for (const path of sources) {
|
|
15243
|
+
let dir = posix.dirname(path);
|
|
15244
|
+
while (dir !== "." && !packageRoots.has(dir))
|
|
15245
|
+
dir = posix.dirname(dir);
|
|
15246
|
+
if (dir === "." || found.has(dir))
|
|
15247
|
+
continue;
|
|
15248
|
+
let text;
|
|
15249
|
+
try {
|
|
15250
|
+
if (statSync3(resolve9(root, path)).size > 512 * 1024)
|
|
15251
|
+
continue;
|
|
15252
|
+
text = readFileSync11(resolve9(root, path), "utf8");
|
|
15253
|
+
} catch {
|
|
15254
|
+
continue;
|
|
15255
|
+
}
|
|
15256
|
+
if (MODEL_REWRITE_CALL.test(text))
|
|
15257
|
+
found.add(dir);
|
|
14918
15258
|
}
|
|
14919
|
-
|
|
15259
|
+
modelRewriteCache.set(root, found);
|
|
15260
|
+
return found;
|
|
15261
|
+
}
|
|
15262
|
+
var MAX_REPO_SCAN_DEPTH = 8;
|
|
15263
|
+
function walkRepo(root, onFile) {
|
|
15264
|
+
const visit = (dir, depth) => {
|
|
15265
|
+
let entries;
|
|
14920
15266
|
try {
|
|
14921
|
-
|
|
14922
|
-
if (statSync3(absolute).size > MAX_MODEL_SOURCE_BYTES)
|
|
14923
|
-
return false;
|
|
14924
|
-
return readFileSync11(absolute, "utf8").includes(modelId);
|
|
15267
|
+
entries = readdirSync4(resolve9(root, dir), { withFileTypes: true });
|
|
14925
15268
|
} catch {
|
|
14926
|
-
return
|
|
15269
|
+
return;
|
|
15270
|
+
}
|
|
15271
|
+
for (const entry of entries) {
|
|
15272
|
+
const path = dir ? `${dir}/${entry.name}` : entry.name;
|
|
15273
|
+
if (entry.isDirectory()) {
|
|
15274
|
+
if (depth < MAX_REPO_SCAN_DEPTH && !entry.name.startsWith(".") && entry.name !== "node_modules")
|
|
15275
|
+
visit(path, depth + 1);
|
|
15276
|
+
} else if (entry.isFile()) {
|
|
15277
|
+
onFile(path, entry.name);
|
|
15278
|
+
}
|
|
15279
|
+
}
|
|
15280
|
+
};
|
|
15281
|
+
visit("", 0);
|
|
15282
|
+
}
|
|
15283
|
+
function isUnderAny(path, prefixes) {
|
|
15284
|
+
for (const prefix of prefixes)
|
|
15285
|
+
if (path === prefix || path.startsWith(`${prefix}/`))
|
|
15286
|
+
return true;
|
|
15287
|
+
return false;
|
|
15288
|
+
}
|
|
15289
|
+
var SCRIPTS_DIR = /^scripts\//;
|
|
15290
|
+
var DEPLOY_CONFIG_FILE = /^(?:Dockerfile(?:\.[\w.-]+)?|[\w.-]+\.Dockerfile|Procfile|fly\.toml|wrangler(?:\.[\w-]+)?\.(?:toml|jsonc?)|docker-compose(?:\.[\w-]+)?\.ya?ml|compose\.ya?ml|render\.ya?ml|railway\.json|vercel\.json|app\.ya?ml|crontab)$/i;
|
|
15291
|
+
var DEPLOY_PACKAGE_SCRIPT = /^(?:start|serve|prod|deploy|cron|worker)(?:[:_-]|$)/i;
|
|
15292
|
+
var SCRIPT_REFERENCE = /(?:^|[\s"'`=(,./])(scripts\/[\w./-]*[\w-])/g;
|
|
15293
|
+
var COMMENT_LINE = /^\s*(?:#|\/\/|\/\*|\*)/;
|
|
15294
|
+
var productionScriptCache = new Map;
|
|
15295
|
+
function productionScriptPaths(rootDir) {
|
|
15296
|
+
const root = resolve9(rootDir);
|
|
15297
|
+
const cached = productionScriptCache.get(root);
|
|
15298
|
+
if (cached)
|
|
15299
|
+
return cached;
|
|
15300
|
+
const found = new Set;
|
|
15301
|
+
const addReferences = (text) => {
|
|
15302
|
+
for (const line of text.split(`
|
|
15303
|
+
`)) {
|
|
15304
|
+
if (COMMENT_LINE.test(line))
|
|
15305
|
+
continue;
|
|
15306
|
+
for (const match of line.matchAll(SCRIPT_REFERENCE))
|
|
15307
|
+
found.add(match[1].replace(/\/+$/, ""));
|
|
14927
15308
|
}
|
|
15309
|
+
};
|
|
15310
|
+
walkRepo(root, (path, name) => {
|
|
15311
|
+
const isDeployConfig = DEPLOY_CONFIG_FILE.test(name);
|
|
15312
|
+
if (!isDeployConfig && name !== "package.json")
|
|
15313
|
+
return;
|
|
15314
|
+
const dir = posix.dirname(path);
|
|
15315
|
+
if (isDeployConfig && SCRIPTS_DIR.test(path))
|
|
15316
|
+
found.add(dir);
|
|
15317
|
+
let text;
|
|
15318
|
+
try {
|
|
15319
|
+
text = readFileSync11(resolve9(root, path), "utf8");
|
|
15320
|
+
} catch {
|
|
15321
|
+
return;
|
|
15322
|
+
}
|
|
15323
|
+
if (isDeployConfig) {
|
|
15324
|
+
addReferences(text);
|
|
15325
|
+
return;
|
|
15326
|
+
}
|
|
15327
|
+
try {
|
|
15328
|
+
const scripts = JSON.parse(text).scripts ?? {};
|
|
15329
|
+
for (const [scriptName, command] of Object.entries(scripts)) {
|
|
15330
|
+
if (DEPLOY_PACKAGE_SCRIPT.test(scriptName) && typeof command === "string")
|
|
15331
|
+
addReferences(command);
|
|
15332
|
+
}
|
|
15333
|
+
} catch {}
|
|
14928
15334
|
});
|
|
14929
|
-
|
|
14930
|
-
return
|
|
15335
|
+
productionScriptCache.set(root, found);
|
|
15336
|
+
return found;
|
|
15337
|
+
}
|
|
15338
|
+
var MAX_MODEL_EVIDENCE = 5;
|
|
15339
|
+
var MAX_EVAL_SUITE_FILES = 100;
|
|
15340
|
+
function capPromotedEvidence(artifact) {
|
|
15341
|
+
const max = artifact.type === "model_config" ? MAX_MODEL_EVIDENCE : artifact.type === "eval" ? MAX_EVAL_SUITE_FILES : undefined;
|
|
15342
|
+
if (max !== undefined && artifact.evidence.length > max)
|
|
15343
|
+
artifact.evidence = artifact.evidence.slice(0, max);
|
|
15344
|
+
}
|
|
15345
|
+
function distinctStableId(prefix, raw) {
|
|
15346
|
+
return `${stableId(prefix, raw.replace(/\./g, "-"))}_${createHash5("sha256").update(raw).digest("hex").slice(0, 8)}`;
|
|
15347
|
+
}
|
|
15348
|
+
function promoteModelIds(occurrences, context) {
|
|
15349
|
+
const out = [];
|
|
15350
|
+
for (const [key, { id, at }] of occurrences) {
|
|
15351
|
+
const inHarness = at.filter((entry) => isHarnessModelPath(entry.path, context));
|
|
15352
|
+
if (inHarness.length === 0)
|
|
15353
|
+
continue;
|
|
15354
|
+
const seen = new Set;
|
|
15355
|
+
const cited = [...inHarness, ...at.filter((entry) => !isHarnessModelPath(entry.path, context))].filter((entry) => {
|
|
15356
|
+
const key2 = `${entry.path}:${entry.line}`;
|
|
15357
|
+
if (seen.has(key2))
|
|
15358
|
+
return false;
|
|
15359
|
+
seen.add(key2);
|
|
15360
|
+
return true;
|
|
15361
|
+
});
|
|
15362
|
+
const first2 = cited[0];
|
|
15363
|
+
const what = SIGNAL_DESCRIPTIONS[first2.signal] ?? `the ${first2.signal} pattern`;
|
|
15364
|
+
out.push({
|
|
15365
|
+
id: distinctStableId("model_config", key),
|
|
15366
|
+
type: "model_config",
|
|
15367
|
+
name: id,
|
|
15368
|
+
scope: "shared",
|
|
15369
|
+
usedByAgentIds: [],
|
|
15370
|
+
confidence: Math.max(...cited.map((entry) => entry.confidence)),
|
|
15371
|
+
confidenceReason: `Deterministic: model id ${id} found by ${what} at ${first2.path}:${first2.line} (${inHarness.length} harness occurrence${inHarness.length === 1 ? "" : "s"}); not checked by the analyst.`,
|
|
15372
|
+
evidence: cited.map((entry) => ({ path: entry.path, line: entry.line, reason: `deterministic detector ${entry.signal}` }))
|
|
15373
|
+
});
|
|
15374
|
+
}
|
|
15375
|
+
return out;
|
|
14931
15376
|
}
|
|
14932
15377
|
function mergePromotedDuplicate(existing, next) {
|
|
14933
15378
|
for (const entry of next.evidence) {
|
|
@@ -14939,10 +15384,16 @@ function mergePromotedDuplicate(existing, next) {
|
|
|
14939
15384
|
if (!existing.body && next.body)
|
|
14940
15385
|
existing.body = next.body;
|
|
14941
15386
|
}
|
|
14942
|
-
function promoteHarnessCandidates(report, rootDir) {
|
|
15387
|
+
function promoteHarnessCandidates(report, rootDir, options = {}) {
|
|
14943
15388
|
jsSources.clear();
|
|
15389
|
+
productionScriptCache.clear();
|
|
15390
|
+
modelRewriteCache.clear();
|
|
14944
15391
|
try {
|
|
14945
|
-
|
|
15392
|
+
const artifacts = promoteAll(report, rootDir);
|
|
15393
|
+
if (!options.fullEvidence)
|
|
15394
|
+
for (const artifact of artifacts)
|
|
15395
|
+
capPromotedEvidence(artifact);
|
|
15396
|
+
return artifacts;
|
|
14946
15397
|
} finally {
|
|
14947
15398
|
jsSources.clear();
|
|
14948
15399
|
}
|
|
@@ -14954,6 +15405,15 @@ function promoteAll(report, rootDir) {
|
|
|
14954
15405
|
model: new Set(report.candidates.filter((candidate) => candidate.kind === "llm_call_site" || candidate.kind === "framework_construct").map((candidate) => candidate.path)),
|
|
14955
15406
|
searchable: new Set(report.files.map((file) => file.path))
|
|
14956
15407
|
};
|
|
15408
|
+
const modelIds = new Map;
|
|
15409
|
+
const evalFiles = [];
|
|
15410
|
+
const add = (artifact) => {
|
|
15411
|
+
const existing = byId.get(artifact.id);
|
|
15412
|
+
if (existing)
|
|
15413
|
+
mergePromotedDuplicate(existing, artifact);
|
|
15414
|
+
else
|
|
15415
|
+
byId.set(artifact.id, artifact);
|
|
15416
|
+
};
|
|
14957
15417
|
for (const candidate of report.candidates) {
|
|
14958
15418
|
const confidence2 = PROMOTED_CONFIDENCE[candidate.confidence];
|
|
14959
15419
|
if (confidence2 === undefined)
|
|
@@ -14977,20 +15437,32 @@ function promoteAll(report, rootDir) {
|
|
|
14977
15437
|
break;
|
|
14978
15438
|
case "model_config":
|
|
14979
15439
|
if (candidate.confidence === "high") {
|
|
14980
|
-
|
|
15440
|
+
for (const id of modelIdsOnCandidateLine(candidate, readFileLines(rootDir, candidate.path, fileCache))) {
|
|
15441
|
+
const key = id.toLowerCase();
|
|
15442
|
+
const entry = modelIds.get(key) ?? { id, at: [] };
|
|
15443
|
+
entry.at.push({ path: candidate.path, line: candidate.startLine, signal: candidate.signal, confidence: confidence2 });
|
|
15444
|
+
modelIds.set(key, entry);
|
|
15445
|
+
}
|
|
14981
15446
|
}
|
|
14982
15447
|
break;
|
|
15448
|
+
case "eval_file":
|
|
15449
|
+
if (candidate.confidence === "high")
|
|
15450
|
+
evalFiles.push(candidate);
|
|
15451
|
+
break;
|
|
14983
15452
|
default:
|
|
14984
15453
|
break;
|
|
14985
15454
|
}
|
|
14986
15455
|
if (!artifact)
|
|
14987
15456
|
continue;
|
|
14988
|
-
|
|
14989
|
-
|
|
14990
|
-
|
|
14991
|
-
|
|
14992
|
-
|
|
14993
|
-
|
|
15457
|
+
add(artifact);
|
|
15458
|
+
}
|
|
15459
|
+
for (const artifact of promoteModelIds(modelIds, modelPathContext(rootDir)))
|
|
15460
|
+
add(artifact);
|
|
15461
|
+
for (const artifact of promoteEvalSuites(evalFiles, PROMOTED_CONFIDENCE.high, {
|
|
15462
|
+
artifactId: (suiteKey) => distinctStableId("eval", suiteKey),
|
|
15463
|
+
fileReason: detectorConfidenceReason
|
|
15464
|
+
}))
|
|
15465
|
+
add(artifact);
|
|
14994
15466
|
return [...byId.values()];
|
|
14995
15467
|
}
|
|
14996
15468
|
|
|
@@ -15325,7 +15797,7 @@ async function runHarnessCommand(context) {
|
|
|
15325
15797
|
throw new CliInputError("Focused remote prompt analysis is not supported yet.", "Use local --analyst=claude|codex|cursor|local-scan, or drop --concern for a full remote report.");
|
|
15326
15798
|
}
|
|
15327
15799
|
if (context.flags["github-actions"] === "true") {
|
|
15328
|
-
const { runGithubActionsAnalyze } = await import("./harness-github-actions-
|
|
15800
|
+
const { runGithubActionsAnalyze } = await import("./harness-github-actions-xbfvjpva.js");
|
|
15329
15801
|
const result = await runGithubActionsAnalyze(rootDir, {
|
|
15330
15802
|
writeReport: (report2) => {
|
|
15331
15803
|
const normalized = normalizeHarnessReport(report2);
|
|
@@ -16012,7 +16484,7 @@ function isToolGroupPlaceholder(name) {
|
|
|
16012
16484
|
function isDetectedToolDefinitionReason(reason) {
|
|
16013
16485
|
return typeof reason === "string" && (reason.startsWith("deterministic detector ") || reason.startsWith("Tool definition detected"));
|
|
16014
16486
|
}
|
|
16015
|
-
var
|
|
16487
|
+
var MODEL_SOURCE_FILE = /\.(?:[cm]?[jt]sx?|py|json|jsonc|ya?ml|toml)$/i;
|
|
16016
16488
|
var NON_SOURCE_DIR2 = /(^|\/)(?:docs?|\.moda)\//i;
|
|
16017
16489
|
function isSearchableModelId(id) {
|
|
16018
16490
|
return id.length >= 4 && !/\s/.test(id) && /[\d/.:_-]/.test(id);
|
|
@@ -16060,7 +16532,7 @@ function modelIdsUsedInSource(rootDir, ids) {
|
|
|
16060
16532
|
return true;
|
|
16061
16533
|
let entries;
|
|
16062
16534
|
try {
|
|
16063
|
-
entries =
|
|
16535
|
+
entries = readdirSync5(current).sort();
|
|
16064
16536
|
} catch {
|
|
16065
16537
|
return true;
|
|
16066
16538
|
}
|
|
@@ -16072,7 +16544,7 @@ function modelIdsUsedInSource(rootDir, ids) {
|
|
|
16072
16544
|
}
|
|
16073
16545
|
if (!stat.isFile() || stat.size > MAX_SCANNED_FILE_BYTES || isSecretishPath(rel2))
|
|
16074
16546
|
return true;
|
|
16075
|
-
if (TEST_PATH2.test(rel2) || !
|
|
16547
|
+
if (TEST_PATH2.test(rel2) || !MODEL_SOURCE_FILE.test(rel2))
|
|
16076
16548
|
return true;
|
|
16077
16549
|
if (files >= maxFiles) {
|
|
16078
16550
|
complete = false;
|
|
@@ -16259,7 +16731,7 @@ var CONCERN_PROMOTED_ARTIFACT_TYPES = {
|
|
|
16259
16731
|
topology: new Set,
|
|
16260
16732
|
tools: new Set(["tool"]),
|
|
16261
16733
|
prompts: new Set(["prompt"]),
|
|
16262
|
-
evals: new Set(["model_config"])
|
|
16734
|
+
evals: new Set(["model_config", "eval"])
|
|
16263
16735
|
};
|
|
16264
16736
|
function deterministicArtifacts(rootDir, concern) {
|
|
16265
16737
|
const graph = { artifacts: [], unknowns: [] };
|
|
@@ -16275,21 +16747,43 @@ function mergeDeterministicArtifacts(graph, rootDir, options = {}) {
|
|
|
16275
16747
|
const allowedTypes = options.concern ? CONCERN_PROMOTED_ARTIFACT_TYPES[options.concern] : undefined;
|
|
16276
16748
|
let promoted;
|
|
16277
16749
|
try {
|
|
16278
|
-
promoted = promoteHarnessCandidates(detectHarnessCandidates(rootDir), rootDir);
|
|
16750
|
+
promoted = promoteHarnessCandidates(detectHarnessCandidates(rootDir), rootDir, { fullEvidence: true });
|
|
16279
16751
|
} catch {
|
|
16280
16752
|
return { added: 0 };
|
|
16281
16753
|
}
|
|
16282
16754
|
backfillRefutationEvidenceReasons(graph);
|
|
16283
16755
|
const refuted = analystRefutations(graph);
|
|
16284
16756
|
const candidatePaths = new Set(promoted.flatMap((artifact) => artifact.evidence.map((entry) => typeof entry.path === "string" ? normalizePath2(entry.path) : "").filter(Boolean)));
|
|
16757
|
+
const modelLineCounts = new Map;
|
|
16758
|
+
for (const artifact of promoted) {
|
|
16759
|
+
if (artifact.type !== "model_config")
|
|
16760
|
+
continue;
|
|
16761
|
+
for (const entry of artifact.evidence) {
|
|
16762
|
+
const key = `${entry.path}:${entry.line}`;
|
|
16763
|
+
modelLineCounts.set(key, (modelLineCounts.get(key) ?? 0) + 1);
|
|
16764
|
+
}
|
|
16765
|
+
}
|
|
16766
|
+
const sharedModelLines = new Set([...modelLineCounts].filter(([, count]) => count > 1).map(([key]) => key));
|
|
16767
|
+
const pathContext = modelPathContext(rootDir);
|
|
16285
16768
|
let added = 0;
|
|
16286
16769
|
for (const artifact of promoted) {
|
|
16287
16770
|
if (allowedTypes && !allowedTypes.has(artifact.type))
|
|
16288
16771
|
continue;
|
|
16289
|
-
if (graphCoversPromotedArtifact(graph, artifact))
|
|
16772
|
+
if (graphCoversPromotedArtifact(graph, artifact, sharedModelLines))
|
|
16290
16773
|
continue;
|
|
16774
|
+
if ((artifact.type === "model_config" || artifact.type === "eval") && artifact.evidence.length > 1 && !refuted.candidateIds.has(artifact.id)) {
|
|
16775
|
+
const kept = artifact.evidence.filter((entry) => !isRefutedByAnalyst({ ...artifact, evidence: [entry] }, refuted, candidatePaths));
|
|
16776
|
+
if (kept.length === 0)
|
|
16777
|
+
continue;
|
|
16778
|
+
if (artifact.type === "model_config" && !kept.some((entry) => isHarnessModelPath(entry.path, pathContext)))
|
|
16779
|
+
continue;
|
|
16780
|
+
artifact.evidence = kept;
|
|
16781
|
+
if (artifact.sourcePath && !kept.some((entry) => entry.path === artifact.sourcePath))
|
|
16782
|
+
artifact.sourcePath = kept[0].path;
|
|
16783
|
+
}
|
|
16291
16784
|
if (isRefutedByAnalyst(artifact, refuted, candidatePaths))
|
|
16292
16785
|
continue;
|
|
16786
|
+
capPromotedEvidence(artifact);
|
|
16293
16787
|
graph.artifacts.push(artifact);
|
|
16294
16788
|
added += 1;
|
|
16295
16789
|
}
|
|
@@ -16312,12 +16806,13 @@ var DETECTOR_ARTIFACT_TYPES = {
|
|
|
16312
16806
|
message_construction: "prompt",
|
|
16313
16807
|
tool_definition: "tool",
|
|
16314
16808
|
tool_schema: "tool",
|
|
16315
|
-
model_config: "model_config"
|
|
16809
|
+
model_config: "model_config",
|
|
16810
|
+
eval_file: "eval"
|
|
16316
16811
|
};
|
|
16317
16812
|
function refutedTypes(...texts) {
|
|
16318
16813
|
const types = new Set;
|
|
16319
16814
|
for (const text of texts) {
|
|
16320
|
-
for (const match of String(text ?? "").matchAll(/\b(prompt_literal|prompt_file|message_construction|tool_definition|tool_schema|model_config)\b/g)) {
|
|
16815
|
+
for (const match of String(text ?? "").matchAll(/\b(prompt_literal|prompt_file|message_construction|tool_definition|tool_schema|model_config|eval_file)\b/g)) {
|
|
16321
16816
|
types.add(DETECTOR_ARTIFACT_TYPES[match[1]]);
|
|
16322
16817
|
}
|
|
16323
16818
|
}
|
|
@@ -16379,7 +16874,26 @@ function artifactSourceKey(artifact) {
|
|
|
16379
16874
|
const pinned = Array.isArray(artifact.evidence) ? artifact.evidence.find((entry) => typeof entry.path === "string" && entry.path.length > 0) : undefined;
|
|
16380
16875
|
return pinned ? normalizePath2(pinned.path) : undefined;
|
|
16381
16876
|
}
|
|
16382
|
-
function
|
|
16877
|
+
function mentionsModelId(text, id) {
|
|
16878
|
+
if (!id)
|
|
16879
|
+
return false;
|
|
16880
|
+
const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
16881
|
+
return new RegExp(`(?<![\\w./@-])${escaped}(?![\\w/-]|\\.\\w)`, "i").test(text);
|
|
16882
|
+
}
|
|
16883
|
+
function artifactPaths(artifact) {
|
|
16884
|
+
const paths = Array.isArray(artifact.evidence) ? artifact.evidence.map((entry) => typeof entry.path === "string" ? normalizePath2(entry.path) : "").filter(Boolean) : [];
|
|
16885
|
+
return artifact.sourcePath ? [normalizePath2(artifact.sourcePath), ...paths] : paths;
|
|
16886
|
+
}
|
|
16887
|
+
function graphCoversPromotedArtifact(graph, promoted, sharedModelLines = new Set) {
|
|
16888
|
+
if (promoted.type === "model_config" && graph.artifacts.some((existing) => existing.type === "model_config" && mentionsModelId(`${existing.name ?? ""}
|
|
16889
|
+
${existing.body ?? ""}`, promoted.name)))
|
|
16890
|
+
return true;
|
|
16891
|
+
if (promoted.type === "eval") {
|
|
16892
|
+
const suite = new Set(artifactPaths(promoted));
|
|
16893
|
+
const suiteKey = promoted.sourcePath ? evalSuiteKey(normalizePath2(promoted.sourcePath)) : undefined;
|
|
16894
|
+
if (graph.artifacts.some((existing) => existing.type === "eval" && artifactPaths(existing).some((path) => suite.has(path) || suiteKey !== undefined && evalSuiteKey(path.replace(/\/+$/, "")) === suiteKey)))
|
|
16895
|
+
return true;
|
|
16896
|
+
}
|
|
16383
16897
|
const promotedName = promoted.name.toLowerCase();
|
|
16384
16898
|
const isDefinitionLine = (entry) => typeof entry.line === "number" && !/^(?:passed to a model call|prompt-named literal referenced)/.test(entry.reason ?? "");
|
|
16385
16899
|
const promotedLines = promoted.evidence.filter(isDefinitionLine);
|
|
@@ -16395,7 +16909,7 @@ function graphCoversPromotedArtifact(graph, promoted) {
|
|
|
16395
16909
|
if (!existingSource || !promotedSource || existingSource === promotedSource && !distinctInline)
|
|
16396
16910
|
return true;
|
|
16397
16911
|
}
|
|
16398
|
-
if (existing.type === promoted.type && Array.isArray(existing.evidence) && existing.evidence.some((entry) => isDefinitionLine(entry) && promotedLines.some((line) => line.path === entry.path && line.line === entry.line))) {
|
|
16912
|
+
if (existing.type === promoted.type && !(promoted.type === "model_config" && isModelIdLiteral(existing.name ?? "")) && Array.isArray(existing.evidence) && existing.evidence.some((entry) => isDefinitionLine(entry) && !(promoted.type === "model_config" && sharedModelLines.has(`${entry.path}:${entry.line}`)) && promotedLines.some((line) => line.path === entry.path && line.line === entry.line))) {
|
|
16399
16913
|
return true;
|
|
16400
16914
|
}
|
|
16401
16915
|
if (isWholeFilePrompt && existing.sourcePath === promoted.sourcePath)
|
|
@@ -17401,7 +17915,7 @@ var CONCERN_CANDIDATE_KINDS = {
|
|
|
17401
17915
|
topology: ["llm_call_site", "framework_construct"],
|
|
17402
17916
|
tools: ["tool_definition", "tool_schema"],
|
|
17403
17917
|
prompts: ["prompt_file", "prompt_literal", "message_construction"],
|
|
17404
|
-
evals: ["model_config"]
|
|
17918
|
+
evals: ["model_config", "eval_file"]
|
|
17405
17919
|
};
|
|
17406
17920
|
var MAX_SEED_CANDIDATES_PER_KIND = 40;
|
|
17407
17921
|
var MAX_INLINE_CANDIDATE_EXCERPT_CHARS = 500;
|
|
@@ -17414,7 +17928,8 @@ var SEED_CANDIDATE_KIND_LABELS = {
|
|
|
17414
17928
|
message_construction: "Message constructions",
|
|
17415
17929
|
prompt_file: "Prompt files",
|
|
17416
17930
|
prompt_literal: "Inline prompt literals",
|
|
17417
|
-
model_config: "Model configuration"
|
|
17931
|
+
model_config: "Model configuration",
|
|
17932
|
+
eval_file: "Eval suite files"
|
|
17418
17933
|
};
|
|
17419
17934
|
function renderAnalystCandidateLines(report, concern) {
|
|
17420
17935
|
const allowedKinds = concern ? new Set(CONCERN_CANDIDATE_KINDS[concern]) : undefined;
|
|
@@ -17435,7 +17950,7 @@ function renderAnalystCandidateLines(report, concern) {
|
|
|
17435
17950
|
concern ? 'Each entry is path:line [detector] excerpt. Candidates are pattern matches — verify each against the real file, drop false positives — things that are not harness components (SQL, UI props or display copy, URL templates, test fixtures, utilities), never a real component merely because it is unused (list refuted ones in unknowns: an unknown whose id starts with "refuted_", with one evidence entry {path, line, reason} per refuted candidate, using the path:line shown for that candidate and starting the reason with its [detector]), and hunt for in-concern items the patterns cannot see. Do not cite this list as evidence; cite the files.' : 'Each entry is path:line [detector] excerpt. Candidates are pattern matches — verify each against the real file and drop false positives — things that are not harness components (SQL, UI props or display copy, URL templates, test fixtures, utilities), never a real component merely because it is unused. Verified deterministic candidates are also auto-merged into the graph after your run, so prioritize (1) agents, topology, and relationships, (2) verifying or refuting these candidates (list refuted ones in unknowns: an unknown whose id starts with "refuted_", with one evidence entry {path, line, reason} per refuted candidate, using the path:line shown for that candidate and starting the reason with its [detector]; refuted candidates are never auto-merged), and (3) artifacts the patterns cannot see (runtime agents, prompts assembled at runtime, cross-file wiring) — rather than re-listing every candidate as an artifact. Do not cite this list as evidence; cite the files.'
|
|
17436
17951
|
];
|
|
17437
17952
|
if (concern === "evals") {
|
|
17438
|
-
lines.push("Note:
|
|
17953
|
+
lines.push("Note: guardrails and memory/retrieval stores have no deterministic detector — only model_config and eval_file candidates are seeded. Discover the rest by investigation.");
|
|
17439
17954
|
}
|
|
17440
17955
|
for (const [kind, label] of Object.entries(SEED_CANDIDATE_KIND_LABELS)) {
|
|
17441
17956
|
const bucket = byKind.get(kind);
|
|
@@ -17500,7 +18015,7 @@ function readDirectoryPreflightPaths(rootDir) {
|
|
|
17500
18015
|
const absolute = current === "." ? rootDir : join9(rootDir, current);
|
|
17501
18016
|
let entries;
|
|
17502
18017
|
try {
|
|
17503
|
-
entries =
|
|
18018
|
+
entries = readdirSync5(absolute);
|
|
17504
18019
|
} catch {
|
|
17505
18020
|
continue;
|
|
17506
18021
|
}
|
|
@@ -17536,7 +18051,7 @@ function shouldSkipPreflightPath(path) {
|
|
|
17536
18051
|
return isSecretishPath(normalized);
|
|
17537
18052
|
}
|
|
17538
18053
|
function isManifestLikePath(path) {
|
|
17539
|
-
const name =
|
|
18054
|
+
const name = basename7(path).toLowerCase();
|
|
17540
18055
|
return /^(package\.json|pyproject\.toml|requirements.*\.txt|uv\.lock|poetry\.lock|pnpm-lock\.yaml|bun\.lockb?|yarn\.lock|cargo\.toml|go\.mod|deno\.json|turbo\.json|langgraph\.json|agents\.json|config\.(ts|js|json|yaml|yml|toml))$/.test(name);
|
|
17541
18056
|
}
|
|
17542
18057
|
function isDocLikePath(path) {
|
|
@@ -17566,7 +18081,7 @@ function resolveAnalystCommand(adapter) {
|
|
|
17566
18081
|
return findOnPath("cursor-agent") || findOnPath("cursor-cli") || findOnPath("cursor") || findOnPath("/Applications/Cursor.app/Contents/Resources/app/bin/cursor");
|
|
17567
18082
|
}
|
|
17568
18083
|
function cursorSubcommandArgs(command, args) {
|
|
17569
|
-
const name =
|
|
18084
|
+
const name = basename7(command);
|
|
17570
18085
|
return name === "cursor" || name === "cursor-cli" ? ["agent", ...args] : args;
|
|
17571
18086
|
}
|
|
17572
18087
|
function findOnPath(command) {
|
|
@@ -17595,7 +18110,7 @@ function scanHarness(options = {}) {
|
|
|
17595
18110
|
const readPlan = buildHarnessReadPlan(rootDir, options.pathWasExplicit === true);
|
|
17596
18111
|
const graph = {
|
|
17597
18112
|
schemaVersion: SCHEMA_VERSION,
|
|
17598
|
-
harnessId: stableId("harness", repo.name ||
|
|
18113
|
+
harnessId: stableId("harness", repo.name || basename7(rootDir) || "repo"),
|
|
17599
18114
|
tenant: options.tenantId ? {
|
|
17600
18115
|
tenantId: options.tenantId,
|
|
17601
18116
|
source: options.tenantSource ?? "scan_option"
|
|
@@ -17666,7 +18181,7 @@ function collectSnapshotInputs(rootDir, maxFiles, maxTextBytes) {
|
|
|
17666
18181
|
if (stat.isDirectory()) {
|
|
17667
18182
|
if (path !== "." && (isSkippedDir(path) || isSkippedPathPrefix(path)))
|
|
17668
18183
|
return;
|
|
17669
|
-
for (const entry of
|
|
18184
|
+
for (const entry of readdirSync5(current).sort())
|
|
17670
18185
|
walk(join9(current, entry));
|
|
17671
18186
|
return;
|
|
17672
18187
|
}
|
|
@@ -18009,7 +18524,7 @@ function findFresherHarnessReports(rootDir) {
|
|
|
18009
18524
|
visited += 1;
|
|
18010
18525
|
let entries;
|
|
18011
18526
|
try {
|
|
18012
|
-
entries =
|
|
18527
|
+
entries = readdirSync5(dir, { withFileTypes: true });
|
|
18013
18528
|
} catch {
|
|
18014
18529
|
return;
|
|
18015
18530
|
}
|
|
@@ -18257,7 +18772,7 @@ function inventoryFiles(rootDir, skipped, options = {}) {
|
|
|
18257
18772
|
recordSkipped(skipped, { path: rel2, reason: "ignored directory" });
|
|
18258
18773
|
return;
|
|
18259
18774
|
}
|
|
18260
|
-
for (const entry of
|
|
18775
|
+
for (const entry of readdirSync5(current).sort()) {
|
|
18261
18776
|
walk(join9(current, entry));
|
|
18262
18777
|
}
|
|
18263
18778
|
return;
|
|
@@ -18344,7 +18859,7 @@ function numericEnvAllowZero(name, fallback) {
|
|
|
18344
18859
|
function detectRepo(rootDir, files) {
|
|
18345
18860
|
const packageJson = files.find((file) => file.path === "package.json");
|
|
18346
18861
|
const pyproject = files.find((file) => file.path === "pyproject.toml");
|
|
18347
|
-
const name = packageJson?.text ? safePackageName(packageJson.text) ??
|
|
18862
|
+
const name = packageJson?.text ? safePackageName(packageJson.text) ?? basename7(rootDir) : pyproject?.text ? safeTomlName(pyproject.text) ?? basename7(rootDir) : basename7(rootDir);
|
|
18348
18863
|
const packageManagers = [
|
|
18349
18864
|
files.some((file) => file.path === "bun.lock" || file.path === "bun.lockb") ? "bun" : null,
|
|
18350
18865
|
files.some((file) => file.path === "pnpm-lock.yaml") ? "pnpm" : null,
|
|
@@ -18618,7 +19133,7 @@ function maybeAddSkillArtifact(graph, file) {
|
|
|
18618
19133
|
upsertArtifact(graph, {
|
|
18619
19134
|
id: stableId("skill", file.path),
|
|
18620
19135
|
type: "skill",
|
|
18621
|
-
name: titleFromId(
|
|
19136
|
+
name: titleFromId(basename7(file.path).replace(/\.(md|mdc)$/i, "")),
|
|
18622
19137
|
scope: "global",
|
|
18623
19138
|
sourcePath: file.path,
|
|
18624
19139
|
contentHash: hashText(file.text ?? ""),
|
|
@@ -18661,7 +19176,7 @@ function inferRuntimeAgent(file, graph) {
|
|
|
18661
19176
|
return null;
|
|
18662
19177
|
if (!/\.(ts|tsx|js|mjs|py)$/i.test(file.path))
|
|
18663
19178
|
return null;
|
|
18664
|
-
if (
|
|
19179
|
+
if (basename7(file.path) === "__init__.py")
|
|
18665
19180
|
return null;
|
|
18666
19181
|
const loweredPath = file.path.toLowerCase();
|
|
18667
19182
|
const loweredText = file.text.toLowerCase();
|
|
@@ -18768,7 +19283,7 @@ function inferArtifactsForFile(file, graph) {
|
|
|
18768
19283
|
return false;
|
|
18769
19284
|
if (artifact.sourcePath === file.path)
|
|
18770
19285
|
return true;
|
|
18771
|
-
const base =
|
|
19286
|
+
const base = basename7(artifact.sourcePath);
|
|
18772
19287
|
if (text.includes(base) || text.includes(artifact.sourcePath))
|
|
18773
19288
|
return true;
|
|
18774
19289
|
if (artifact.type === "tool") {
|
|
@@ -19690,13 +20205,13 @@ function titleFromId(id) {
|
|
|
19690
20205
|
return id.replace(/^(agent|family|channel|prompt|tool|skill|eval|identity|deploy|entry|env)_/, "").split(/[_/.-]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ") || id;
|
|
19691
20206
|
}
|
|
19692
20207
|
function promptNameFromPath(path) {
|
|
19693
|
-
return titleFromId(
|
|
20208
|
+
return titleFromId(basename7(path).replace(/\.(prompt\.)?(md|json|ya?ml)$/i, ""));
|
|
19694
20209
|
}
|
|
19695
20210
|
function normalizePath2(path) {
|
|
19696
20211
|
return path.split(sep5).join("/");
|
|
19697
20212
|
}
|
|
19698
20213
|
function hashText(text) {
|
|
19699
|
-
return
|
|
20214
|
+
return createHash6("sha256").update(text).digest("hex");
|
|
19700
20215
|
}
|
|
19701
20216
|
|
|
19702
20217
|
export { startRemoteAnalyze, pollRemoteAnalyzeUntilDone, isRecoverableRemoteRun, checkPromptMapping, safePromptEvidencePath, discoverHarnessPromptSources, runHarnessCommand, writeHarnessAnalyzeResult, emitWrittenArtifacts, analyzeHarnessPrompts, isToolGroupPlaceholder, repairExternalAnalystReport, deriveRelationshipsFromGraph, deterministicArtifacts, mergeDeterministicArtifacts, analystRefutations, describeCodingAgentEvent, analystEventProgress, isRetryableExternalAnalystFailure, sleep, summarizeInvalidAnalystReport, isDeterministicLocalAnalyst, resolveHarnessAnalyzeConcern, resolveExternalHarnessAnalystInvocation, detectAvailableCodingAgents, codingAgentDisplayName, runCodingAgent, readOnlyClaudeExtraArgs, resolveAnalystModel, renderExternalHarnessAnalystPrompt, CONCERN_CANDIDATE_KINDS, SEED_CANDIDATE_KIND_LABELS, renderAnalystCandidateLines, renderAnalystSeedLines, truncateText, scanHarness, summarizeHarness, buildSourceSnapshot, writeHumanProgress, describeSyncIdentity, describeSyncTenantLabel, findFresherHarnessReports, writeHarnessArtifacts, assertHarnessGraphSyncQuality, graphForSync, inventoryFiles, numericEnv, numericEnvAllowZero, computeDeterministicFloor, stableId, titleFromId, promptNameFromPath };
|