@moda-ai/cli 1.55.60 → 1.55.62

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.
Files changed (36) hide show
  1. package/dist/{agent-ds0qdtxz.js → agent-rc62qxke.js} +18 -17
  2. package/dist/{cli-94r5x9bg.js → cli-27kkk5z4.js} +1 -1
  3. package/dist/{cli-bt564t50.js → cli-3t4ft30h.js} +5 -5
  4. package/dist/{cli-9g8kp42c.js → cli-9rmx2zzy.js} +2 -2
  5. package/dist/{cli-hawwf30b.js → cli-bpnt0z0y.js} +1 -1
  6. package/dist/{cli-2jwxdhh5.js → cli-c7mj168q.js} +1 -1
  7. package/dist/cli-fsgh8mq5.js +1522 -0
  8. package/dist/{cli-y5d33a1y.js → cli-gmvbapx7.js} +2 -1517
  9. package/dist/{cli-rtkjjf0a.js → cli-kmq9pgef.js} +68 -1241
  10. package/dist/{cli-v2h21cgf.js → cli-m702wdtv.js} +1 -1
  11. package/dist/cli-mfvwvh84.js +1178 -0
  12. package/dist/{cli-b918vs2m.js → cli-p2tfx93b.js} +4 -4
  13. package/dist/{cli-m25c0rke.js → cli-rdwy3y4f.js} +3 -3
  14. package/dist/{cli-hxy54rx3.js → cli-thy08dak.js} +11 -11
  15. package/dist/{cli-wbd3hzqh.js → cli-tn9dg758.js} +680 -153
  16. package/dist/{cli-31ybq5f2.js → cli-wcax07xh.js} +2 -2
  17. package/dist/cli.js +27 -20
  18. package/dist/{doctor-s9t8e808.js → doctor-ehqrzq5e.js} +8 -8
  19. package/dist/{harness-bxcetthw.js → harness-e1yawk87.js} +13 -11
  20. package/dist/{harness-github-actions-acxh0dbf.js → harness-github-actions-bgfnh4tw.js} +11 -9
  21. package/dist/{harness-report-gz5d3y48.js → harness-report-dc6yf2qv.js} +2 -1
  22. package/dist/index-gtqxj0fp.js +2956 -0
  23. package/dist/{index-d8hc0er0.js → index-ma1gt133.js} +31 -29
  24. package/dist/{production-intelligence-fqvhzq6s.js → production-intelligence-40teyxyv.js} +4 -4
  25. package/dist/{profile-commands-yvpe4d8y.js → profile-commands-fa2858ez.js} +5 -5
  26. package/dist/{prompt-attribution-6xc7h59r.js → prompt-attribution-htyrwdjj.js} +13 -11
  27. package/dist/prompt-source-mcp.js +11 -9
  28. package/dist/{prompt-sources-1hycgx6r.js → prompt-sources-r2xbxr0r.js} +2 -1
  29. package/dist/{prompts-e8rhawyc.js → prompts-s5q70jge.js} +6 -6
  30. package/dist/{provision-aqhfbnez.js → provision-2bb73ywd.js} +6 -6
  31. package/dist/{registry-wv1zzyce.js → registry-ztn727mn.js} +4 -4
  32. package/dist/{skills-etzcqhp8.js → skills-c25dmy28.js} +6 -6
  33. package/dist/{sync-m9htrc8t.js → sync-t1d50rpv.js} +16 -14
  34. package/package.json +1 -1
  35. package/skills/integration/index.json +2 -2
  36. package/dist/{cli-pnarsb96.js → cli-15pzedv3.js} +3 -3
@@ -1,6 +1,3 @@
1
- import {
2
- stripAnsi
3
- } from "./cli-97fnztpy.js";
4
1
  import {
5
2
  SYNC_STATE_PATH,
6
3
  readHarnessSyncState,
@@ -10,19 +7,42 @@ import {
10
7
  renderHarnessAnalyzeHumanResult,
11
8
  renderHarnessProgress
12
9
  } from "./cli-k0vxtzxb.js";
10
+ import {
11
+ stripAnsi
12
+ } from "./cli-97fnztpy.js";
13
+ import {
14
+ HARNESS_ANALYZE_CONCERNS,
15
+ HARNESS_REPORT_PATH,
16
+ MAX_HARNESS_REPORT_SUMMARY_LENGTH,
17
+ approveHarnessReport,
18
+ artifactHasBody,
19
+ createHarnessReport,
20
+ firstEvidencePath,
21
+ graphForReport,
22
+ hashHarnessReport,
23
+ hashStableJson,
24
+ loadHarnessReportForSync,
25
+ normalizeHarnessReport,
26
+ readHarnessReport,
27
+ requiresArtifactBody,
28
+ resolveHarnessReportPath,
29
+ stampRemoteSnapshotTruncation,
30
+ validateHarnessReport,
31
+ writeHarnessReportArtifacts
32
+ } from "./cli-kmq9pgef.js";
13
33
  import {
14
34
  createCommandContext,
15
35
  stringOption
16
- } from "./cli-v2h21cgf.js";
36
+ } from "./cli-m702wdtv.js";
17
37
  import {
18
38
  assertFlags,
19
39
  assertPositionals,
20
40
  requireTenant
21
- } from "./cli-31ybq5f2.js";
41
+ } from "./cli-wcax07xh.js";
22
42
  import {
23
43
  ApiError,
24
44
  callControlAPI
25
- } from "./cli-hawwf30b.js";
45
+ } from "./cli-bpnt0z0y.js";
26
46
  import {
27
47
  embeddedTenantIdFromApiKey,
28
48
  getActiveProfile,
@@ -32,7 +52,11 @@ import {
32
52
  resolveModaBaseUrl,
33
53
  resolveTenantId,
34
54
  shellArg
35
- } from "./cli-pnarsb96.js";
55
+ } from "./cli-15pzedv3.js";
56
+ import {
57
+ CliInputError,
58
+ CliInputRequiredError
59
+ } from "./cli-k4tqwah4.js";
36
60
  import {
37
61
  PYTHON_AGENT_MODULES,
38
62
  TYPESCRIPT_AGENT_MODULES,
@@ -41,10 +65,9 @@ import {
41
65
  discoverStructuredPromptSources,
42
66
  extractCodeSource,
43
67
  extractPythonSource,
44
- require_typescript,
45
68
  resolvePromptSource,
46
69
  validatePromptSource
47
- } from "./cli-y5d33a1y.js";
70
+ } from "./cli-fsgh8mq5.js";
48
71
  import {
49
72
  $isMap,
50
73
  $isNode,
@@ -72,38 +95,19 @@ import {
72
95
  shouldInventoryFile
73
96
  } from "./cli-9a4b1wxz.js";
74
97
  import {
75
- HARNESS_ANALYZE_CONCERNS,
76
- HARNESS_REPORT_PATH,
77
- MAX_HARNESS_REPORT_SUMMARY_LENGTH,
78
- approveHarnessReport,
79
- artifactHasBody,
80
- createHarnessReport,
81
- firstEvidencePath,
82
- graphForReport,
83
- hashHarnessReport,
84
- hashStableJson,
85
- loadHarnessReportForSync,
86
98
  mergeBoundRuntimeClasses,
87
- normalizeHarnessReport,
88
- readHarnessReport,
89
- requiresArtifactBody,
90
- resolveHarnessReportPath,
91
- stampRemoteSnapshotTruncation,
92
- tagComponentStatus,
93
- validateHarnessReport,
94
- writeHarnessReportArtifacts
95
- } from "./cli-rtkjjf0a.js";
99
+ tagComponentStatus
100
+ } from "./cli-mfvwvh84.js";
96
101
  import {
97
- CliInputError,
98
- CliInputRequiredError
99
- } from "./cli-k4tqwah4.js";
102
+ require_typescript
103
+ } from "./cli-gmvbapx7.js";
100
104
  import {
101
105
  __require,
102
106
  __toESM
103
107
  } from "./cli-0v6na3yp.js";
104
108
 
105
109
  // src/harness.ts
106
- import { createHash as createHash5 } from "node:crypto";
110
+ import { createHash as createHash6 } from "node:crypto";
107
111
  import { execFileSync as execFileSync3, spawn } from "node:child_process";
108
112
  import { homedir, tmpdir as tmpdir2 } from "node:os";
109
113
  import { fileURLToPath } from "node:url";
@@ -113,11 +117,11 @@ import {
113
117
  mkdtempSync as mkdtempSync2,
114
118
  mkdirSync as mkdirSync5,
115
119
  readFileSync as readFileSync12,
116
- readdirSync as readdirSync4,
120
+ readdirSync as readdirSync5,
117
121
  writeFileSync as writeFileSync5,
118
122
  rmSync as rmSync4
119
123
  } from "node:fs";
120
- import { basename as basename6, delimiter, dirname as dirname7, join as join9, relative as relative6, resolve as resolve10, sep as sep5 } from "node:path";
124
+ import { basename as basename7, delimiter, dirname as dirname7, join as join9, relative as relative6, resolve as resolve10, sep as sep5 } from "node:path";
121
125
  import { confirm, select } from "@inquirer/prompts";
122
126
 
123
127
  // src/analyst-tui.ts
@@ -12111,7 +12115,98 @@ function enrichScanCaptureMetadata(graph) {
12111
12115
 
12112
12116
  // src/harness-candidates.ts
12113
12117
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
12114
- import { basename as basename5, dirname as dirname6, extname, resolve as resolve6 } from "node:path";
12118
+ import { basename as basename6, dirname as dirname6, extname as extname2, resolve as resolve6 } from "node:path";
12119
+
12120
+ // src/harness-eval-suites.ts
12121
+ import { basename as basename5, extname } from "node:path";
12122
+ var EVAL_FILE_EXTENSIONS = new Set([".ts", ".tsx", ".mts", ".js", ".mjs", ".cjs", ".py", ".yaml", ".yml"]);
12123
+ var EVAL_CASE_DATA_EXTENSIONS = new Set([".json", ".jsonl", ".toml"]);
12124
+ var EVAL_DIR_SEGMENT = /^(?:evals|evaluations)$/i;
12125
+ var EVAL_HARNESS_DIR_SEGMENT = /^eval$/i;
12126
+ var EVAL_FILE_NAME = /(?:^|[._-])evals?(?:[._-]|$)|^promptfoo/i;
12127
+ var DOCS_ROOT = /^(?:docs?|documentation)\//i;
12128
+ var HIDDEN_DIR = /(?:^|\/)\.[^/]+\//;
12129
+ var EVAL_SCRIPT_NAME = /(?:^|[._-])(?:judges?|grader|grading|benchmarks?)(?:[._-]|$)/i;
12130
+ var SCRIPTS_ROOT = /^(?:scripts?|bin)\//i;
12131
+ var EVALUATION_NAME = /(?:^|[._-])evaluat(?:e|ion|ions|or|ors)(?:[._-]|$)/i;
12132
+ var TEST_TREE = /(?:^|\/)(?:tests?|__tests__)\//i;
12133
+ var EVAL_SEED_SCRIPT = /^(?:seed|fetch|scrape|download|populate|extract)[-_]/i;
12134
+ var EVAL_NON_CASE_DATA = /^(?:leaderboards?|results?|scores?|reports?|outputs?|summary|tsconfig|jsconfig|package(?:-lock)?)(?:[._-][^/]*)?\.[^.]+$/i;
12135
+ var TEST_FILE_NAME = /(?:^|\/)(?:test_[^/]*\.py|[^/]*_test\.py|[^/]*\.(?:test|spec)\.[^/]+)$/i;
12136
+ var EVAL_TEST_NAME = /\.evals?\.(?:test|spec)\.[^/.]+$/i;
12137
+ var EVAL_IMPORT = /(?:\bfrom\s+|\bimport\s*\(\s*|\brequire\s*\(\s*|^\s*import\s+)['"][^'"]*(?:\/(?:evals?|evaluations)\/|[\/._-]evals?(?:[._-][^'"/]*)?['"])/m;
12138
+ function evalDirectoryIndex(path) {
12139
+ return path.split("/").slice(0, -1).findIndex((segment) => EVAL_DIR_SEGMENT.test(segment) || EVAL_HARNESS_DIR_SEGMENT.test(segment));
12140
+ }
12141
+ function isEvalHarnessDirectory(path, index) {
12142
+ return index >= 0 && EVAL_HARNESS_DIR_SEGMENT.test(path.split("/")[index] ?? "");
12143
+ }
12144
+ function isEvalSuitePath(path) {
12145
+ const ext = extname(path).toLowerCase();
12146
+ const name = basename5(path);
12147
+ if (DOCS_ROOT.test(path) || HIDDEN_DIR.test(path) || /\.d\.[cm]?ts$/i.test(path) || EVAL_SEED_SCRIPT.test(name))
12148
+ return false;
12149
+ const inEvalDir = evalDirectoryIndex(path) >= 0;
12150
+ if (EVAL_CASE_DATA_EXTENSIONS.has(ext))
12151
+ return inEvalDir && !EVAL_NON_CASE_DATA.test(name);
12152
+ if (!EVAL_FILE_EXTENSIONS.has(ext))
12153
+ return false;
12154
+ return inEvalDir || EVAL_FILE_NAME.test(name) || SCRIPTS_ROOT.test(path) && EVAL_SCRIPT_NAME.test(name) || TEST_TREE.test(path) && EVALUATION_NAME.test(name);
12155
+ }
12156
+ function isTestOfEvalCode(path, text) {
12157
+ return TEST_FILE_NAME.test(path) && !EVAL_TEST_NAME.test(path) && evalDirectoryIndex(path) < 0 && EVAL_IMPORT.test(text);
12158
+ }
12159
+ function isEvalModelSource(path) {
12160
+ if (!isEvalSuitePath(path))
12161
+ return false;
12162
+ return evalDirectoryIndex(path) >= 0 || EVAL_TEST_NAME.test(path) || !TEST_FILE_NAME.test(path);
12163
+ }
12164
+ function evalFileSignal(path, text) {
12165
+ if (!isEvalSuitePath(path) || isTestOfEvalCode(path, text))
12166
+ return;
12167
+ return evalDirectoryIndex(path) < 0 ? "eval.file_name" : "eval.suite_dir";
12168
+ }
12169
+ var EVAL_RUNNER_NAMES = [
12170
+ /^(?:run|runner|main|index|eval|evaluate|cli)\.[^.]+$/i,
12171
+ /^(?:run|runner|main|eval|evaluate)[._-]/i,
12172
+ /(?:^|[._-])evals?(?:[._-]|$)/i
12173
+ ];
12174
+ function evalSuiteKey(path) {
12175
+ const index = evalDirectoryIndex(path);
12176
+ const segments = path.split("/");
12177
+ if (isEvalHarnessDirectory(path, index))
12178
+ return segments.slice(0, index + 1).join("/");
12179
+ if (index < 0 || segments.length - 1 <= index + 1)
12180
+ return path;
12181
+ return segments.slice(0, index + 2).join("/");
12182
+ }
12183
+ function promoteEvalSuites(files, confidence2, naming) {
12184
+ const suites = new Map;
12185
+ for (const file of files) {
12186
+ const key = evalSuiteKey(file.path);
12187
+ suites.set(key, [...suites.get(key) ?? [], file]);
12188
+ }
12189
+ return [...suites].map(([key, members]) => {
12190
+ const sorted = [...members].sort((a, b) => a.path.localeCompare(b.path));
12191
+ const runner = EVAL_RUNNER_NAMES.map((name) => sorted.find((file) => name.test(basename5(file.path)))).find(Boolean) ?? sorted[0];
12192
+ const cited = [runner, ...sorted.filter((file) => file !== runner)];
12193
+ const isDirectory = key !== runner.path;
12194
+ const isHarness = isDirectory && isEvalHarnessDirectory(runner.path, evalDirectoryIndex(runner.path));
12195
+ return {
12196
+ id: naming.artifactId(key),
12197
+ type: "eval",
12198
+ name: isHarness ? `${key} eval harness` : isDirectory ? `${basename5(key)} eval suite` : basename5(key),
12199
+ scope: "global",
12200
+ sourcePath: runner.path,
12201
+ usedByAgentIds: [],
12202
+ confidence: confidence2,
12203
+ confidenceReason: isDirectory ? `Deterministic: eval suite directory ${key} (${members.length} source file${members.length === 1 ? "" : "s"}); not checked by the analyst.` : naming.fileReason(runner),
12204
+ evidence: cited.map((file) => ({ path: file.path, line: 1, reason: `deterministic detector ${file.signal}` }))
12205
+ };
12206
+ });
12207
+ }
12208
+
12209
+ // src/harness-candidates.ts
12115
12210
  var MAX_CANDIDATES_PER_FILE_PER_SIGNAL = 20;
12116
12211
  var MIN_PROMPT_LITERAL_CHARS = 160;
12117
12212
  var CONFIDENCE_WEIGHT = {
@@ -12335,6 +12430,168 @@ var LINE_PATTERNS = [
12335
12430
  }
12336
12431
  ];
12337
12432
  var CODE_EXTENSIONS = new Set([...JS, ...PY]);
12433
+ 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;
12434
+ 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;
12435
+ var CHOSEN_VALUE_PREFIX = /(?:\b[A-Za-z_]\w*\s*=|\bmodel\w*["']?\s*:)\s*$/i;
12436
+ function isChosenProviderColonModel(value, before) {
12437
+ const model = PROVIDER_COLON_MODEL_ID.exec(value)?.[1];
12438
+ return Boolean(model && PROVIDER_MODEL_NAME.test(model) && CHOSEN_VALUE_PREFIX.test(before));
12439
+ }
12440
+ var PROVIDER_MODEL_NAME = /\d|claude|gpt|gemini|gemma|llama|mistral|mixtral|qwen|deepseek|grok|sonar|kimi|glm|command|minimax|auto/i;
12441
+ var BARE_MODEL_ID = /^(?:claude-|gpt-|chatgpt-|gemini-|text-embedding-|deepseek-|mistral-|llama-?|grok-|command-|qwen|kimi-|glm-)[\w.:-]*$/i;
12442
+ var O_SERIES_MODEL_ID = /^o[1-9](?:-(?:mini|pro|preview|deep-research))*(?:-\d{4}-\d{2}-\d{2})?$/i;
12443
+ var NOT_MODEL_ID = /^(?:claude-(?:code|ai|desktop|agent-sdk)|gpt-tokenizer|gpt-3-encoder|gemini-cli)\b/i;
12444
+ var FILE_NAME_SUFFIX = /\.(?:[cm]?[jt]sx?|json|ya?ml|toml|md|txt|py|png|jpe?g|gif|svg|webp|css|html?)$/i;
12445
+ var MODEL_CANDIDATE_VALUE = /^[A-Za-z0-9][\w./:-]{2,80}$/;
12446
+ var IMPORT_LINE = /^\s*(?:import\b|export\s+(?:\*|\{[^}]*\})\s*from\b|from\s+\S+\s+import\b)|\brequire\s*\(|\bimport\s*\(/;
12447
+ var CLASS_ATTRIBUTE_PREFIX = /\bclass(?:Name|List)?\s*[=:]\s*\{?\s*$/;
12448
+ var NON_MODEL_SOURCE_PATH = /\.d\.[cm]?ts$|\.min\.[cm]?js$|(?:^|\/)(?:public|static)\/|(?:^|\/)(?:package(?:-lock)?\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb?)$/i;
12449
+ var MODEL_ID_EXTENSIONS = new Set([...CODE_EXTENSIONS, ".json", ".jsonc", ".yaml", ".yml", ".toml"]);
12450
+ function isModelIdLiteral(value) {
12451
+ if (value.length < 4 || value.length > 80 || /\s/.test(value) || NOT_MODEL_ID.test(value) || FILE_NAME_SUFFIX.test(value))
12452
+ return false;
12453
+ if (PROVIDER_MODEL_ID.test(value))
12454
+ return PROVIDER_MODEL_NAME.test(value.slice(value.indexOf("/") + 1));
12455
+ if (O_SERIES_MODEL_ID.test(value))
12456
+ return true;
12457
+ return BARE_MODEL_ID.test(value) && /\d/.test(value);
12458
+ }
12459
+ var HASH_COMMENT_EXTENSIONS = new Set([...PY, ".yaml", ".yml", ".toml"]);
12460
+ var REGEX_PRECEDER = /(?:^|[(,=:[!&|?{};+\-*%<>~^]|\b(?:return|typeof|case|in|of|delete|void|throw|new|yield|await))\s*$/;
12461
+ function startsRegexLiteral(line, start) {
12462
+ const next = line[start + 1];
12463
+ if (next === undefined || next === "/" || next === "*" || next === ">" || line[start - 1] === "<")
12464
+ return false;
12465
+ return REGEX_PRECEDER.test(line.slice(0, start));
12466
+ }
12467
+ function regexLiteralEnd(line, start) {
12468
+ let inClass = false;
12469
+ for (let j = start + 1;j < line.length; j += 1) {
12470
+ const ch = line[j];
12471
+ if (ch === "\\") {
12472
+ j += 1;
12473
+ continue;
12474
+ }
12475
+ if (ch === "[")
12476
+ inClass = true;
12477
+ else if (ch === "]")
12478
+ inClass = false;
12479
+ else if (ch === "/" && !inClass)
12480
+ return j + 1;
12481
+ }
12482
+ return line.length;
12483
+ }
12484
+ function stringLiteralsInLine(line, state, hashComments) {
12485
+ const out = [];
12486
+ const closeAt = (from, close, escapes) => {
12487
+ for (let j = from;j < line.length; j += 1) {
12488
+ if (escapes && line[j] === "\\") {
12489
+ j += 1;
12490
+ continue;
12491
+ }
12492
+ if (line.startsWith(close, j))
12493
+ return j;
12494
+ }
12495
+ return -1;
12496
+ };
12497
+ let i = 0;
12498
+ if (state.open) {
12499
+ const close = state.open === "/*" ? "*/" : state.open;
12500
+ const at = closeAt(0, close, state.open !== "/*");
12501
+ if (at < 0)
12502
+ return out;
12503
+ i = at + close.length;
12504
+ state.open = null;
12505
+ }
12506
+ while (i < line.length) {
12507
+ const ch = line[i];
12508
+ if (hashComments ? ch === "#" : line.startsWith("//", i))
12509
+ break;
12510
+ if (!hashComments && line.startsWith("/*", i)) {
12511
+ const at2 = closeAt(i + 2, "*/", false);
12512
+ if (at2 < 0) {
12513
+ state.open = "/*";
12514
+ break;
12515
+ }
12516
+ i = at2 + 2;
12517
+ continue;
12518
+ }
12519
+ if (!hashComments && ch === "/" && startsRegexLiteral(line, i)) {
12520
+ i = regexLiteralEnd(line, i);
12521
+ continue;
12522
+ }
12523
+ if (ch !== '"' && ch !== "'" && ch !== "`") {
12524
+ i += 1;
12525
+ continue;
12526
+ }
12527
+ const triple = hashComments && line.startsWith(ch.repeat(3), i);
12528
+ const quote = triple ? ch.repeat(3) : ch;
12529
+ const at = closeAt(i + quote.length, quote, true);
12530
+ if (at < 0) {
12531
+ if (triple || ch === "`")
12532
+ state.open = quote;
12533
+ break;
12534
+ }
12535
+ out.push({ value: line.slice(i + quote.length, at), index: i, end: at + quote.length });
12536
+ i = at + quote.length;
12537
+ }
12538
+ return out;
12539
+ }
12540
+ function modelIdLiteralsInScannedLine(line, state, hashComments) {
12541
+ const literals = stringLiteralsInLine(line, state, hashComments);
12542
+ if (line.length > 4000 || IMPORT_LINE.test(line))
12543
+ return [];
12544
+ return literals.filter((literal) => {
12545
+ if (!MODEL_CANDIDATE_VALUE.test(literal.value))
12546
+ return false;
12547
+ const before = line.slice(0, literal.index);
12548
+ return (isModelIdLiteral(literal.value) || isChosenProviderColonModel(literal.value, before)) && !CLASS_ATTRIBUTE_PREFIX.test(before);
12549
+ });
12550
+ }
12551
+ function modelIdLiteralsInLines(lines, path) {
12552
+ const hashComments = HASH_COMMENT_EXTENSIONS.has(extname2(path).toLowerCase());
12553
+ const state = { open: null };
12554
+ return lines.map((line) => modelIdLiteralsInScannedLine(line, state, hashComments));
12555
+ }
12556
+ var HIGH_CONFIDENCE_MODEL_PATTERNS = LINE_PATTERNS.filter((pattern) => pattern.kind === "model_config" && pattern.confidence === "high");
12557
+ function detectModelIdLiterals(file, out) {
12558
+ if (!MODEL_ID_EXTENSIONS.has(extname2(file.path).toLowerCase()) || NON_MODEL_SOURCE_PATH.test(file.path))
12559
+ return;
12560
+ const lines = file.text.split(`
12561
+ `);
12562
+ const literals = modelIdLiteralsInLines(lines, file.path);
12563
+ for (let i = 0;i < lines.length; i += 1) {
12564
+ const line = lines[i];
12565
+ if (literals[i].length === 0)
12566
+ continue;
12567
+ if (HIGH_CONFIDENCE_MODEL_PATTERNS.some((pattern) => pattern.pattern.test(line)))
12568
+ continue;
12569
+ out.push({
12570
+ kind: "model_config",
12571
+ path: file.path,
12572
+ startLine: i + 1,
12573
+ endLine: i + 1,
12574
+ confidence: "high",
12575
+ signal: "model.id_literal",
12576
+ excerpt: normalizeExcerpt(line)
12577
+ });
12578
+ }
12579
+ }
12580
+ function detectEvalFile(file, out) {
12581
+ const signal = evalFileSignal(file.path, file.text);
12582
+ if (!signal)
12583
+ return;
12584
+ out.push({
12585
+ kind: "eval_file",
12586
+ path: file.path,
12587
+ startLine: 1,
12588
+ endLine: Math.max(1, file.text.split(`
12589
+ `).length),
12590
+ confidence: "high",
12591
+ signal,
12592
+ excerpt: normalizeExcerpt(file.text.slice(0, 300))
12593
+ });
12594
+ }
12338
12595
  var PROMPT_DOC_EXTENSIONS = new Set([".md", ".mdc", ".txt", ".prompt", ".jinja", ".jinja2", ".j2", ".hbs", ".tpl"]);
12339
12596
  var UPPERCASE_DOC_NAME = /^[A-Z0-9][A-Z0-9_-]*\.(?:md|mdc|txt)$/;
12340
12597
  var NON_PROMPT_DOC_BASENAMES = new Set([
@@ -12396,7 +12653,7 @@ function lineAtIndex(offsets, index) {
12396
12653
  return lo + 1;
12397
12654
  }
12398
12655
  function detectLinePatterns(file, out) {
12399
- const ext = extname(file.path).toLowerCase();
12656
+ const ext = extname2(file.path).toLowerCase();
12400
12657
  const lines = file.text.split(`
12401
12658
  `);
12402
12659
  const perSignal = new Map;
@@ -12473,7 +12730,7 @@ function detectAdjacentStringBlocks(file, out) {
12473
12730
  }
12474
12731
  var PROMPT_MODULE_NAME_HINT = /(^|[._-])(system[._-]?)?prompts?([._-]|$)/i;
12475
12732
  function detectPromptModule(file, out) {
12476
- const base = basename5(file.path, extname(file.path));
12733
+ const base = basename6(file.path, extname2(file.path));
12477
12734
  if (!PROMPT_MODULE_NAME_HINT.test(base))
12478
12735
  return;
12479
12736
  out.push({
@@ -12488,7 +12745,7 @@ function detectPromptModule(file, out) {
12488
12745
  });
12489
12746
  }
12490
12747
  function detectPromptLiterals(file, out) {
12491
- const ext = extname(file.path).toLowerCase();
12748
+ const ext = extname2(file.path).toLowerCase();
12492
12749
  const offsets = buildLineIndex(file.text);
12493
12750
  let found = 0;
12494
12751
  const record = (startIndex, endIndex, content, signal) => {
@@ -12533,10 +12790,10 @@ function detectPromptLiterals(file, out) {
12533
12790
  }
12534
12791
  }
12535
12792
  function detectPromptFile(file, out) {
12536
- const ext = extname(file.path).toLowerCase();
12793
+ const ext = extname2(file.path).toLowerCase();
12537
12794
  if (!PROMPT_DOC_EXTENSIONS.has(ext))
12538
12795
  return;
12539
- const base = basename5(file.path).toLowerCase();
12796
+ const base = basename6(file.path).toLowerCase();
12540
12797
  if (NON_PROMPT_DOC_BASENAMES.has(base))
12541
12798
  return;
12542
12799
  if (/(^|\/)(docs?|documentation|examples?|\.github)\//i.test(file.path))
@@ -12553,7 +12810,7 @@ function detectPromptFile(file, out) {
12553
12810
  } else if (PROMPTS_DIR.test(dir)) {
12554
12811
  confidence2 = "high";
12555
12812
  signal = "prompt_file.prompts_dir";
12556
- } else if (dirHint && score >= 3 && !UPPERCASE_DOC_NAME.test(basename5(file.path))) {
12813
+ } else if (dirHint && score >= 3 && !UPPERCASE_DOC_NAME.test(basename6(file.path))) {
12557
12814
  confidence2 = "medium";
12558
12815
  signal = "prompt_file.agent_dir_instruction_text";
12559
12816
  } else if (score >= 5) {
@@ -12574,10 +12831,10 @@ function detectPromptFile(file, out) {
12574
12831
  });
12575
12832
  }
12576
12833
  function detectPromptLoaderReferences(file, filesByPath, out) {
12577
- if (!LOADER_CODE_EXTENSIONS.has(extname(file.path).toLowerCase()) && extname(file.path).toLowerCase() !== ".md")
12834
+ if (!LOADER_CODE_EXTENSIONS.has(extname2(file.path).toLowerCase()) && extname2(file.path).toLowerCase() !== ".md")
12578
12835
  return;
12579
12836
  const extension = "(?:\\.txt|\\.md)";
12580
- const pattern = extname(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");
12837
+ 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
12838
  let found = 0;
12582
12839
  let match;
12583
12840
  while (found < MAX_CANDIDATES_PER_FILE_PER_SIGNAL && (match = pattern.exec(file.text)) !== null) {
@@ -12585,7 +12842,7 @@ function detectPromptLoaderReferences(file, filesByPath, out) {
12585
12842
  const target = normalizeLoaderTarget(file.path, reference);
12586
12843
  if (!target || !filesByPath.has(target))
12587
12844
  continue;
12588
- const targetBase = basename5(target).toLowerCase();
12845
+ const targetBase = basename6(target).toLowerCase();
12589
12846
  const highConfidence = RUST_PROMPT_PATH_HINT.test(target) || /Template\.from_file/i.test(match[0]);
12590
12847
  if (!highConfidence)
12591
12848
  continue;
@@ -12597,7 +12854,7 @@ function detectPromptLoaderReferences(file, filesByPath, out) {
12597
12854
  endLine: 1,
12598
12855
  confidence: "high",
12599
12856
  signal: /Template\.from_file/i.test(match[0]) ? "prompt_file.template_loader_reference" : "prompt_file.loader_reference",
12600
- excerpt: normalizeExcerpt(`${basename5(file.path)} loads ${targetBase}`)
12857
+ excerpt: normalizeExcerpt(`${basename6(file.path)} loads ${targetBase}`)
12601
12858
  });
12602
12859
  }
12603
12860
  }
@@ -12625,10 +12882,12 @@ function detectHarnessCandidatesInFiles(files) {
12625
12882
  if (typeof file.text !== "string" || file.text.length === 0)
12626
12883
  continue;
12627
12884
  const scoped = { path: file.path, text: file.text };
12628
- const ext = extname(file.path).toLowerCase();
12885
+ const ext = extname2(file.path).toLowerCase();
12629
12886
  if (CODE_EXTENSIONS.has(ext) || ext === ".json" || ext === ".yaml" || ext === ".yml" || ext === ".toml") {
12630
12887
  detectLinePatterns(scoped, candidates);
12631
12888
  }
12889
+ detectModelIdLiterals(scoped, candidates);
12890
+ detectEvalFile(scoped, candidates);
12632
12891
  if (CODE_EXTENSIONS.has(ext)) {
12633
12892
  detectAdjacentStringBlocks(scoped, candidates);
12634
12893
  detectPromptModule(scoped, candidates);
@@ -12658,7 +12917,7 @@ function detectHarnessCandidates(rootDir) {
12658
12917
  const skipped = [];
12659
12918
  const root = resolve6(rootDir);
12660
12919
  const inventory = inventoryFiles(root, skipped, {
12661
- includeFile: (path) => PROMPT_DOC_EXTENSIONS.has(extname(path).toLowerCase()) || [".json", ".yaml", ".yml"].includes(extname(path).toLowerCase()) || extname(path).toLowerCase() === ".rs" && RUST_PROMPT_PATH_HINT.test(path)
12920
+ 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
12921
  });
12663
12922
  const { candidates, files } = detectHarnessCandidatesInFiles(inventory);
12664
12923
  const durationMs = Math.max(1, Date.now() - startedAt);
@@ -12691,7 +12950,7 @@ function writeHarnessCandidatesReport(report, outPath) {
12691
12950
 
12692
12951
  // src/harness-prompt-sources.ts
12693
12952
  import { createHash as createHash4 } from "node:crypto";
12694
- import { extname as extname6 } from "node:path";
12953
+ import { extname as extname7 } from "node:path";
12695
12954
 
12696
12955
  // src/prompt-source-verification/index.ts
12697
12956
  import { readFileSync as readFileSync10 } from "node:fs";
@@ -12699,7 +12958,7 @@ import { z } from "zod";
12699
12958
 
12700
12959
  // src/prompt-source-validation.ts
12701
12960
  import { existsSync as existsSync7, lstatSync as lstatSync5 } from "node:fs";
12702
- import { extname as extname2, relative as relative4, resolve as resolve7 } from "node:path";
12961
+ import { extname as extname3, relative as relative4, resolve as resolve7 } from "node:path";
12703
12962
  function checkPromptMapping(rootDir, unknownMapping) {
12704
12963
  const repairs = [];
12705
12964
  const unresolved = [];
@@ -12779,12 +13038,12 @@ function normalizePromptMapping(value) {
12779
13038
  return normalized;
12780
13039
  }
12781
13040
  function isPromptDefinitionPath(path) {
12782
- return /(^|\/)(?:prompts?|templates?)(?:\/|\.)/i.test(path) || /\.prompt\.(?:md|txt|ya?ml|json)$/i.test(path) || [".jinja", ".j2", ".mustache", ".hbs"].includes(extname2(path).toLowerCase());
13041
+ return /(^|\/)(?:prompts?|templates?)(?:\/|\.)/i.test(path) || /\.prompt\.(?:md|txt|ya?ml|json)$/i.test(path) || [".jinja", ".j2", ".mustache", ".hbs"].includes(extname3(path).toLowerCase());
12783
13042
  }
12784
13043
 
12785
13044
  // src/prompt-source-verification/evidence.ts
12786
13045
  import { readFileSync as readFileSync9 } from "node:fs";
12787
- import { extname as extname5 } from "node:path";
13046
+ import { extname as extname6 } from "node:path";
12788
13047
 
12789
13048
  // src/prompt-source-verification/paths.ts
12790
13049
  import { lstatSync as lstatSync6, realpathSync as realpathSync6 } from "node:fs";
@@ -12812,13 +13071,13 @@ function safePromptEvidencePath(rootDir, path) {
12812
13071
  // src/prompt-source-verification/sink-proof.ts
12813
13072
  var import_typescript = __toESM(require_typescript(), 1);
12814
13073
  import { execFileSync as execFileSync2 } from "node:child_process";
12815
- import { extname as extname3 } from "node:path";
13074
+ import { extname as extname4 } from "node:path";
12816
13075
  import { readFileSync as readFileSync7 } from "node:fs";
12817
13076
  function proveSink(root, sink) {
12818
13077
  const file = safePromptEvidencePath(root, sink.path);
12819
13078
  const cache = new Map;
12820
13079
  if (!cache.has(file)) {
12821
- if (extname3(file) === ".py") {
13080
+ if (extname4(file) === ".py") {
12822
13081
  try {
12823
13082
  const rows = JSON.parse(execFileSync2("python3", ["-I", "-c", PYTHON_PROOF], { input: readFileSync7(file, "utf8"), encoding: "utf8", timeout: 1e4, maxBuffer: 2 * 1024 * 1024 }));
12824
13083
  cache.set(file, new Map(Object.entries(rows).map(([line, proof]) => [Number(line), proof])));
@@ -12988,7 +13247,7 @@ print(json.dumps(rows))
12988
13247
 
12989
13248
  // src/prompt-source-location.ts
12990
13249
  import { readFileSync as readFileSync8 } from "node:fs";
12991
- import { extname as extname4 } from "node:path";
13250
+ import { extname as extname5 } from "node:path";
12992
13251
  function structuredExcerpt(text, source) {
12993
13252
  if (source.selector?.pointer === undefined)
12994
13253
  return { line: 1, endLine: text.split(`
@@ -13028,7 +13287,7 @@ function structuredExcerpt(text, source) {
13028
13287
  }
13029
13288
  function promptDefinitionExcerpt(rootDir, source) {
13030
13289
  const file = safePromptEvidencePath(rootDir, source.path);
13031
- const ext = extname4(file).toLowerCase();
13290
+ const ext = extname5(file).toLowerCase();
13032
13291
  if (ext === ".py") {
13033
13292
  const extraction = extractPythonSource(rootDir, file, source.selector?.symbol);
13034
13293
  if (!extraction.range)
@@ -13052,7 +13311,7 @@ function promptDefinitionRange(rootDir, source) {
13052
13311
  // src/prompt-source-verification/evidence.ts
13053
13312
  function verifyPromptSourceEvidence(rootDir, source) {
13054
13313
  const checked = checkPromptMapping(rootDir, source);
13055
- const dedicated = isPromptDefinitionPath(source.path) && [".md", ".txt", ".jinja", ".j2", ".mustache", ".hbs"].includes(extname5(source.path).toLowerCase());
13314
+ const dedicated = isPromptDefinitionPath(source.path) && [".md", ".txt", ".jinja", ".j2", ".mustache", ".hbs"].includes(extname6(source.path).toLowerCase());
13056
13315
  return {
13057
13316
  accepted: checked.ok && dedicated && checked.kind !== "builder",
13058
13317
  reason: dedicated ? "Dedicated authored prompt file" : "Candidate requires independent semantic review",
@@ -13306,7 +13565,7 @@ function discoverHarnessPromptSources(rootDir, existing = []) {
13306
13565
  }
13307
13566
  try {
13308
13567
  let found = [];
13309
- const ext = extname6(path).toLowerCase();
13568
+ const ext = extname7(path).toLowerCase();
13310
13569
  if (CODE.has(ext)) {
13311
13570
  if (attempts++ >= MAX_CODE_PROMPT_PATHS) {
13312
13571
  issues.push({ path, reason: "Code prompt scan limit (64 files) reached; deferred to analyst review" });
@@ -13574,8 +13833,9 @@ function applyPromptDiscovery(graph, discovery) {
13574
13833
  }
13575
13834
 
13576
13835
  // src/harness-candidate-promotion.ts
13577
- import { readFileSync as readFileSync11, statSync as statSync3 } from "node:fs";
13578
- import { extname as extname7, posix, resolve as resolve9 } from "node:path";
13836
+ import { createHash as createHash5 } from "node:crypto";
13837
+ import { readdirSync as readdirSync4, readFileSync as readFileSync11, statSync as statSync3 } from "node:fs";
13838
+ import { extname as extname8, posix, resolve as resolve9 } from "node:path";
13579
13839
 
13580
13840
  // src/harness-js-source.ts
13581
13841
  var import_typescript2 = __toESM(require_typescript(), 1);
@@ -13944,7 +14204,11 @@ var SIGNAL_DESCRIPTIONS = {
13944
14204
  "prompt_file.agent_dir_instruction_text": "instruction-like text in an agent directory",
13945
14205
  "prompt_file.loader_reference": "a file loaded as a prompt by code",
13946
14206
  "prompt_file.template_loader_reference": "a file loaded as a prompt template by code",
13947
- "model.assignment": "a model id assigned in code"
14207
+ "model.assignment": "a model id assigned in code",
14208
+ "model.registry_key": "a model registry key",
14209
+ "model.id_literal": "a model id string literal",
14210
+ "eval.suite_dir": "a file under an eval directory",
14211
+ "eval.file_name": "a file named as an eval"
13948
14212
  };
13949
14213
  function detectorConfidenceReason(candidate) {
13950
14214
  const what = SIGNAL_DESCRIPTIONS[candidate.signal] ?? `the ${candidate.signal} pattern`;
@@ -14420,7 +14684,7 @@ function localNameIn(specifiers, identifier) {
14420
14684
  function importedLocalName(importerPath, lines, identifier, definitionPath, exportsDefault = false) {
14421
14685
  const text = lines.join(`
14422
14686
  `);
14423
- const python = extname7(importerPath) === ".py";
14687
+ const python = extname8(importerPath) === ".py";
14424
14688
  if (python) {
14425
14689
  for (const match of text.matchAll(/^[ \t]*from\s+([.\w]+)\s+import\s+(\([^)]*\)|[^\n]*)/gm)) {
14426
14690
  if (!specifierResolvesTo(match[1], importerPath, definitionPath, true))
@@ -14480,7 +14744,7 @@ function calleeImportedFromModelFile(callee, path, lines, modelFiles) {
14480
14744
  const base = callee.split(".")[0];
14481
14745
  const text = lines.join(`
14482
14746
  `);
14483
- const python = extname7(path) === ".py";
14747
+ const python = extname8(path) === ".py";
14484
14748
  const statements = python ? [/^[ \t]*from\s+([.\w]+)\s+import\s+(\([^)]*\)|[^\n]*)/gm] : [
14485
14749
  /\bimport\s+(?:type\s+)?(?:([A-Za-z_$][\w$]*)\s*,?\s*)?(?:\{([^}]*)\}|\*\s*as\s+([A-Za-z_$][\w$]*))?\s*from\s*["']([^"']+)["']/g,
14486
14750
  /\b(?:const|let|var)\s*\{([^}]*)\}\s*=\s*(?:await\s+)?(?:require|import)\(\s*["']([^"']+)["']\s*\)/g
@@ -14703,7 +14967,7 @@ function hasNearbyRole(lines, line) {
14703
14967
  return false;
14704
14968
  }
14705
14969
  function isDirectModelInput(key, value, path, lines, line, modelFiles) {
14706
- if (CONFIG_EXTENSIONS.has(extname7(path).toLowerCase())) {
14970
+ if (CONFIG_EXTENSIONS.has(extname8(path).toLowerCase())) {
14707
14971
  return DIRECT_PROMPT_KEYS.has(key) && value.length >= MIN_CONFIG_PROMPT_CHARS;
14708
14972
  }
14709
14973
  if (modelFiles.has(path)) {
@@ -14743,7 +15007,7 @@ function fileReachesModel(path, lines, modelFiles) {
14743
15007
  return true;
14744
15008
  if (lines.some((line) => MODEL_ENDPOINT.test(line)))
14745
15009
  return true;
14746
- const python = extname7(path) === ".py";
15010
+ const python = extname8(path) === ".py";
14747
15011
  return importSpecifiers(lines).some((spec) => [...modelFiles].some((modelFile) => specifierResolvesTo(spec, path, modelFile, python)));
14748
15012
  }
14749
15013
  function modelFileImports(path, rootDir, modelFiles, fileCache) {
@@ -14751,7 +15015,7 @@ function modelFileImports(path, rootDir, modelFiles, fileCache) {
14751
15015
  if (modelFile === path)
14752
15016
  return false;
14753
15017
  const lines = readFileLines(rootDir, modelFile, fileCache);
14754
- return Boolean(lines && importSpecifiers(lines).some((spec) => specifierResolvesTo(spec, modelFile, path, extname7(modelFile) === ".py")));
15018
+ return Boolean(lines && importSpecifiers(lines).some((spec) => specifierResolvesTo(spec, modelFile, path, extname8(modelFile) === ".py")));
14755
15019
  });
14756
15020
  }
14757
15021
  var PASS_THROUGH_CALLEE = /^(?:Object\.(?:freeze|seal|assign)|structuredClone|Promise\.resolve|JSON\.stringify|(?:_\.|lodash\.)?(?:cloneDeep|clone|merge)|deepFreeze|defu)$/;
@@ -14821,7 +15085,7 @@ function promotePromptLiteralCandidate(candidate, rootDir, confidence2, fileCach
14821
15085
  use = { path: candidate.path, line: definition.line, key: "system-injection" };
14822
15086
  }
14823
15087
  const jsSource = jsSources.get(`${rootDir}::${candidate.path}`);
14824
- if (!use && definition.key && !PROMPT_CONFIG_KEYS.has(definition.key) && !CONFIG_EXTENSIONS.has(extname7(candidate.path).toLowerCase())) {
15088
+ if (!use && definition.key && !PROMPT_CONFIG_KEYS.has(definition.key) && !CONFIG_EXTENSIONS.has(extname8(candidate.path).toLowerCase())) {
14825
15089
  const exportUsed = () => modelFileImports(candidate.path, rootDir, files.model, fileCache);
14826
15090
  if (jsSource) {
14827
15091
  const consumer = keyedValueConsumer(jsSource, definition.line, definition.key, definition.column);
@@ -14871,63 +15135,248 @@ function promotePromptLiteralCandidate(candidate, rootDir, confidence2, fileCach
14871
15135
  body: definition.text
14872
15136
  };
14873
15137
  }
14874
- function promoteModelConfigCandidate(candidate, rootDir, confidence2, fileCache) {
14875
- const lines = readFileLines(rootDir, candidate.path, fileCache);
14876
- const sourceLine = lines?.[candidate.startLine - 1] ?? "";
14877
- const assignment = MODEL_ASSIGNMENT_VALUE.exec(sourceLine);
14878
- const [field, assigned] = [assignment?.[1], assignment?.[2]];
14879
- if (field && MODEL_PREFERENCE_FIELD.test(field) && !FULL_MODEL_ID.test(assigned ?? ""))
14880
- return;
14881
- const registryKey = candidate.signal === "model.registry_key" ? REGISTRY_KEY.exec(sourceLine)?.[1] : undefined;
14882
- const model = (assigned && MODEL_STRING.exec(assigned)?.[1]) ?? registryKey ?? MODEL_STRING.exec(sourceLine)?.[1] ?? MODEL_STRING.exec(candidate.excerpt)?.[1];
14883
- if (!model)
14884
- return;
14885
- if (MODEL_FAMILY_ALIAS.test(model.split("/").pop() ?? model))
14886
- return;
14887
- if (TEST_PATH2.test(candidate.path) && !usedOutsideTests(rootDir, model))
14888
- return;
14889
- return {
14890
- id: stableId("model_config", `${pathStem(candidate.path)}:L${candidate.startLine}`),
14891
- type: "model_config",
14892
- name: model,
14893
- scope: "shared",
14894
- usedByAgentIds: [],
14895
- confidence: confidence2,
14896
- confidenceReason: detectorConfidenceReason(candidate),
14897
- evidence: candidateEvidence(candidate)
14898
- };
15138
+ var ALIAS_KEY_SAME_LINE = /^\s*:\s*\{[^}]*?["']?\b(?:model|model_name|model_id|modelName|modelId|[A-Z_]*MODEL[A-Z_]*)["']?\s*[:=]/i;
15139
+ var BLOCK_KEY = /^\s*:\s*\{\s*$/;
15140
+ var MODEL_FIELD = /["']?\b(?:model|model_name|model_id|modelName|modelId|[A-Z_]*MODEL[A-Z_]*)["']?\s*[:=]\s*["'`]/i;
15141
+ var ASSIGNED_FIELD = /([A-Za-z_$][\w$]*)["']?\s*[:=]\s*(?:[A-Za-z_$][\w$.()]*\(\s*)?$/;
15142
+ var COMPARED_BEFORE = /(?:\.(?:startsWith|endsWith|includes|indexOf|match|test)\(\s*|[!=]==?\s*|\bcase\s+|[\w$\])]\[\s*)$/;
15143
+ var COMPARED_AFTER = /^\s*[!=]==?/;
15144
+ function isComparedLiteral(sourceLine, literal) {
15145
+ return COMPARED_BEFORE.test(sourceLine.slice(0, literal.index)) || COMPARED_AFTER.test(sourceLine.slice(literal.end));
15146
+ }
15147
+ var ALIAS_BLOCK_MAX_LINES = 200;
15148
+ function isModelAliasKey(lines, row, literal, path = "") {
15149
+ const after = lines[row].slice(literal.end);
15150
+ if (ALIAS_KEY_SAME_LINE.test(after))
15151
+ return true;
15152
+ const separator = /^\s*:\s*/.exec(after);
15153
+ if (separator && modelIdLiteralsAt(lines, path, row).some((other) => other.index === literal.end + separator[0].length))
15154
+ return true;
15155
+ if (!BLOCK_KEY.test(after))
15156
+ return false;
15157
+ let depth = 1;
15158
+ for (const next of lines.slice(row + 1, row + 1 + ALIAS_BLOCK_MAX_LINES)) {
15159
+ if (depth === 1 && MODEL_FIELD.test(next))
15160
+ return true;
15161
+ for (const ch of next) {
15162
+ if (ch === "{")
15163
+ depth += 1;
15164
+ else if (ch === "}")
15165
+ depth -= 1;
15166
+ }
15167
+ if (depth <= 0)
15168
+ return false;
15169
+ }
15170
+ return false;
14899
15171
  }
14900
- var nonTestSourcePaths = new Map;
14901
- var modelUsedOutsideTests = new Map;
14902
- var MAX_MODEL_SOURCE_BYTES = 1024 * 1024;
14903
- var MODEL_SOURCE_FILE = /\.(?:[cm]?[jt]sx?|py|json|jsonc|ya?ml|toml)$/i;
14904
- var NON_SOURCE_DIR = /(^|\/)(?:docs?|\.moda)\//i;
14905
- function usedOutsideTests(rootDir, modelId) {
14906
- let results = modelUsedOutsideTests.get(rootDir);
14907
- if (!results) {
14908
- results = new Map;
14909
- modelUsedOutsideTests.set(rootDir, results);
15172
+ var lineLiterals = new WeakMap;
15173
+ function modelIdLiteralsAt(lines, path, row) {
15174
+ let literals = lineLiterals.get(lines);
15175
+ if (!literals) {
15176
+ literals = modelIdLiteralsInLines(lines, path);
15177
+ lineLiterals.set(lines, literals);
15178
+ }
15179
+ return literals[row] ?? [];
15180
+ }
15181
+ function modelIdsOnCandidateLine(candidate, lines) {
15182
+ const rows = lines ?? [];
15183
+ const row = candidate.startLine - 1;
15184
+ const sourceLine = rows[row] ?? "";
15185
+ const ids = [];
15186
+ const literals = modelIdLiteralsAt(rows, candidate.path, row);
15187
+ const isAliasKey = (value) => literals.some((literal) => literal.value === value && isModelAliasKey(rows, row, literal, candidate.path));
15188
+ if (candidate.signal !== "model.id_literal") {
15189
+ const assignment = MODEL_ASSIGNMENT_VALUE.exec(sourceLine);
15190
+ const [field, assigned] = [assignment?.[1], assignment?.[2]];
15191
+ const preferenceOnly = Boolean(field && MODEL_PREFERENCE_FIELD.test(field) && !FULL_MODEL_ID.test(assigned ?? ""));
15192
+ const registryKey = candidate.signal === "model.registry_key" ? REGISTRY_KEY.exec(sourceLine)?.[1] : undefined;
15193
+ const primary = preferenceOnly ? undefined : (assigned && MODEL_STRING.exec(assigned)?.[1]) ?? registryKey ?? MODEL_STRING.exec(sourceLine)?.[1] ?? MODEL_STRING.exec(candidate.excerpt)?.[1];
15194
+ const compared = (value) => literals.some((literal) => literal.value === value && isComparedLiteral(sourceLine, literal));
15195
+ if (primary && !isAliasKey(primary) && !compared(primary))
15196
+ ids.push(primary);
15197
+ }
15198
+ for (const literal of literals) {
15199
+ const { value, index } = literal;
15200
+ if (ids.includes(value) || isModelAliasKey(rows, row, literal, candidate.path))
15201
+ continue;
15202
+ if (isComparedLiteral(sourceLine, literal))
15203
+ continue;
15204
+ const field = ASSIGNED_FIELD.exec(sourceLine.slice(0, index))?.[1];
15205
+ if (field && MODEL_PREFERENCE_FIELD.test(field) && !FULL_MODEL_ID.test(value))
15206
+ continue;
15207
+ ids.push(value);
14910
15208
  }
14911
- const cached = results.get(modelId);
14912
- if (cached !== undefined)
15209
+ return ids.filter((id) => !MODEL_FAMILY_ALIAS.test(id.split("/").pop() ?? id));
15210
+ }
15211
+ var OTHER_TEST_PATH = /(?:^|\/)(?:e2e|testdata|__mocks__|spec)\/|\.stories\.[^/.]+$|(?:^|\/)(?:test_[^/]*|[^/]*_test|conftest)\.py$/i;
15212
+ var NON_SOURCE_DIR = /(^|\/)(?:docs?|\.moda)\//i;
15213
+ var EMPTY_MODEL_PATH_CONTEXT = { productionScripts: new Set, rewritingPackages: new Set };
15214
+ function isHarnessModelPath(path, context = EMPTY_MODEL_PATH_CONTEXT) {
15215
+ if (NON_SOURCE_DIR.test(path))
15216
+ return false;
15217
+ if (isEvalModelSource(path))
15218
+ return true;
15219
+ if (SCRIPTS_DIR.test(path) && !isUnderAny(path, context.productionScripts))
15220
+ return false;
15221
+ if (isUnderAny(path, context.rewritingPackages) && !SERVER_SIDE_PATH.test(path))
15222
+ return false;
15223
+ return evalDirectoryIndex(path) < 0 && !TEST_PATH2.test(path) && !OTHER_TEST_PATH.test(path);
15224
+ }
15225
+ function modelPathContext(rootDir) {
15226
+ return { productionScripts: productionScriptPaths(rootDir), rewritingPackages: modelRewritingPackages(rootDir) };
15227
+ }
15228
+ var SERVER_SIDE_PATH = /(?:^|\/)(?:server|host|api)\/|[.+]server\.[cm]?[jt]s$/;
15229
+ var MODEL_REWRITE_CALL = /\bmodel\s*[:=]\s*[a-z]\w*\(\s*(?:\w+\.)*(?:requested\w*|\w*[mM]odel\w*)\s*,/;
15230
+ var REWRITE_SOURCE_FILE = /\.(?:[cm]?[jt]sx?|svelte|vue|py)$/i;
15231
+ var modelRewriteCache = new Map;
15232
+ function modelRewritingPackages(rootDir) {
15233
+ const root = resolve9(rootDir);
15234
+ const cached = modelRewriteCache.get(root);
15235
+ if (cached)
14913
15236
  return cached;
14914
- let paths = nonTestSourcePaths.get(rootDir);
14915
- if (!paths) {
14916
- paths = inventoryFiles(rootDir, [], { includeFile: (path) => MODEL_SOURCE_FILE.test(path) }).map((file) => file.path).filter((path) => !TEST_PATH2.test(path) && MODEL_SOURCE_FILE.test(path) && !NON_SOURCE_DIR.test(path));
14917
- nonTestSourcePaths.set(rootDir, paths);
15237
+ const packageRoots = new Set;
15238
+ const sources = [];
15239
+ walkRepo(root, (path, name) => {
15240
+ if (name === "package.json" || name === "pyproject.toml")
15241
+ packageRoots.add(posix.dirname(path));
15242
+ else if (REWRITE_SOURCE_FILE.test(name))
15243
+ sources.push(path);
15244
+ });
15245
+ const found = new Set;
15246
+ for (const path of sources) {
15247
+ let dir = posix.dirname(path);
15248
+ while (dir !== "." && !packageRoots.has(dir))
15249
+ dir = posix.dirname(dir);
15250
+ if (dir === "." || found.has(dir))
15251
+ continue;
15252
+ let text;
15253
+ try {
15254
+ if (statSync3(resolve9(root, path)).size > 512 * 1024)
15255
+ continue;
15256
+ text = readFileSync11(resolve9(root, path), "utf8");
15257
+ } catch {
15258
+ continue;
15259
+ }
15260
+ if (MODEL_REWRITE_CALL.test(text))
15261
+ found.add(dir);
14918
15262
  }
14919
- const used = paths.some((path) => {
15263
+ modelRewriteCache.set(root, found);
15264
+ return found;
15265
+ }
15266
+ var MAX_REPO_SCAN_DEPTH = 8;
15267
+ function walkRepo(root, onFile) {
15268
+ const visit = (dir, depth) => {
15269
+ let entries;
14920
15270
  try {
14921
- const absolute = resolve9(rootDir, path);
14922
- if (statSync3(absolute).size > MAX_MODEL_SOURCE_BYTES)
14923
- return false;
14924
- return readFileSync11(absolute, "utf8").includes(modelId);
15271
+ entries = readdirSync4(resolve9(root, dir), { withFileTypes: true });
14925
15272
  } catch {
14926
- return false;
15273
+ return;
14927
15274
  }
15275
+ for (const entry of entries) {
15276
+ const path = dir ? `${dir}/${entry.name}` : entry.name;
15277
+ if (entry.isDirectory()) {
15278
+ if (depth < MAX_REPO_SCAN_DEPTH && !entry.name.startsWith(".") && entry.name !== "node_modules")
15279
+ visit(path, depth + 1);
15280
+ } else if (entry.isFile()) {
15281
+ onFile(path, entry.name);
15282
+ }
15283
+ }
15284
+ };
15285
+ visit("", 0);
15286
+ }
15287
+ function isUnderAny(path, prefixes) {
15288
+ for (const prefix of prefixes)
15289
+ if (path === prefix || path.startsWith(`${prefix}/`))
15290
+ return true;
15291
+ return false;
15292
+ }
15293
+ var SCRIPTS_DIR = /^scripts\//;
15294
+ 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;
15295
+ var DEPLOY_PACKAGE_SCRIPT = /^(?:start|serve|prod|deploy|cron|worker)(?:[:_-]|$)/i;
15296
+ var SCRIPT_REFERENCE = /(?:^|[\s"'`=(,./])(scripts\/[\w./-]*[\w-])/g;
15297
+ var COMMENT_LINE = /^\s*(?:#|\/\/|\/\*|\*)/;
15298
+ var productionScriptCache = new Map;
15299
+ function productionScriptPaths(rootDir) {
15300
+ const root = resolve9(rootDir);
15301
+ const cached = productionScriptCache.get(root);
15302
+ if (cached)
15303
+ return cached;
15304
+ const found = new Set;
15305
+ const addReferences = (text) => {
15306
+ for (const line of text.split(`
15307
+ `)) {
15308
+ if (COMMENT_LINE.test(line))
15309
+ continue;
15310
+ for (const match of line.matchAll(SCRIPT_REFERENCE))
15311
+ found.add(match[1].replace(/\/+$/, ""));
15312
+ }
15313
+ };
15314
+ walkRepo(root, (path, name) => {
15315
+ const isDeployConfig = DEPLOY_CONFIG_FILE.test(name);
15316
+ if (!isDeployConfig && name !== "package.json")
15317
+ return;
15318
+ const dir = posix.dirname(path);
15319
+ if (isDeployConfig && SCRIPTS_DIR.test(path))
15320
+ found.add(dir);
15321
+ let text;
15322
+ try {
15323
+ text = readFileSync11(resolve9(root, path), "utf8");
15324
+ } catch {
15325
+ return;
15326
+ }
15327
+ if (isDeployConfig) {
15328
+ addReferences(text);
15329
+ return;
15330
+ }
15331
+ try {
15332
+ const scripts = JSON.parse(text).scripts ?? {};
15333
+ for (const [scriptName, command] of Object.entries(scripts)) {
15334
+ if (DEPLOY_PACKAGE_SCRIPT.test(scriptName) && typeof command === "string")
15335
+ addReferences(command);
15336
+ }
15337
+ } catch {}
14928
15338
  });
14929
- results.set(modelId, used);
14930
- return used;
15339
+ productionScriptCache.set(root, found);
15340
+ return found;
15341
+ }
15342
+ var MAX_MODEL_EVIDENCE = 5;
15343
+ var MAX_EVAL_SUITE_FILES = 100;
15344
+ function capPromotedEvidence(artifact) {
15345
+ const max = artifact.type === "model_config" ? MAX_MODEL_EVIDENCE : artifact.type === "eval" ? MAX_EVAL_SUITE_FILES : undefined;
15346
+ if (max !== undefined && artifact.evidence.length > max)
15347
+ artifact.evidence = artifact.evidence.slice(0, max);
15348
+ }
15349
+ function distinctStableId(prefix, raw) {
15350
+ return `${stableId(prefix, raw.replace(/\./g, "-"))}_${createHash5("sha256").update(raw).digest("hex").slice(0, 8)}`;
15351
+ }
15352
+ function promoteModelIds(occurrences, context) {
15353
+ const out = [];
15354
+ for (const [key, { id, at }] of occurrences) {
15355
+ const inHarness = at.filter((entry) => isHarnessModelPath(entry.path, context));
15356
+ if (inHarness.length === 0)
15357
+ continue;
15358
+ const seen = new Set;
15359
+ const cited = [...inHarness, ...at.filter((entry) => !isHarnessModelPath(entry.path, context))].filter((entry) => {
15360
+ const key2 = `${entry.path}:${entry.line}`;
15361
+ if (seen.has(key2))
15362
+ return false;
15363
+ seen.add(key2);
15364
+ return true;
15365
+ });
15366
+ const first2 = cited[0];
15367
+ const what = SIGNAL_DESCRIPTIONS[first2.signal] ?? `the ${first2.signal} pattern`;
15368
+ out.push({
15369
+ id: distinctStableId("model_config", key),
15370
+ type: "model_config",
15371
+ name: id,
15372
+ scope: "shared",
15373
+ usedByAgentIds: [],
15374
+ confidence: Math.max(...cited.map((entry) => entry.confidence)),
15375
+ 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.`,
15376
+ evidence: cited.map((entry) => ({ path: entry.path, line: entry.line, reason: `deterministic detector ${entry.signal}` }))
15377
+ });
15378
+ }
15379
+ return out;
14931
15380
  }
14932
15381
  function mergePromotedDuplicate(existing, next) {
14933
15382
  for (const entry of next.evidence) {
@@ -14939,10 +15388,16 @@ function mergePromotedDuplicate(existing, next) {
14939
15388
  if (!existing.body && next.body)
14940
15389
  existing.body = next.body;
14941
15390
  }
14942
- function promoteHarnessCandidates(report, rootDir) {
15391
+ function promoteHarnessCandidates(report, rootDir, options = {}) {
14943
15392
  jsSources.clear();
15393
+ productionScriptCache.clear();
15394
+ modelRewriteCache.clear();
14944
15395
  try {
14945
- return promoteAll(report, rootDir);
15396
+ const artifacts = promoteAll(report, rootDir);
15397
+ if (!options.fullEvidence)
15398
+ for (const artifact of artifacts)
15399
+ capPromotedEvidence(artifact);
15400
+ return artifacts;
14946
15401
  } finally {
14947
15402
  jsSources.clear();
14948
15403
  }
@@ -14954,6 +15409,15 @@ function promoteAll(report, rootDir) {
14954
15409
  model: new Set(report.candidates.filter((candidate) => candidate.kind === "llm_call_site" || candidate.kind === "framework_construct").map((candidate) => candidate.path)),
14955
15410
  searchable: new Set(report.files.map((file) => file.path))
14956
15411
  };
15412
+ const modelIds = new Map;
15413
+ const evalFiles = [];
15414
+ const add = (artifact) => {
15415
+ const existing = byId.get(artifact.id);
15416
+ if (existing)
15417
+ mergePromotedDuplicate(existing, artifact);
15418
+ else
15419
+ byId.set(artifact.id, artifact);
15420
+ };
14957
15421
  for (const candidate of report.candidates) {
14958
15422
  const confidence2 = PROMOTED_CONFIDENCE[candidate.confidence];
14959
15423
  if (confidence2 === undefined)
@@ -14977,20 +15441,32 @@ function promoteAll(report, rootDir) {
14977
15441
  break;
14978
15442
  case "model_config":
14979
15443
  if (candidate.confidence === "high") {
14980
- artifact = promoteModelConfigCandidate(candidate, rootDir, confidence2, fileCache);
15444
+ for (const id of modelIdsOnCandidateLine(candidate, readFileLines(rootDir, candidate.path, fileCache))) {
15445
+ const key = id.toLowerCase();
15446
+ const entry = modelIds.get(key) ?? { id, at: [] };
15447
+ entry.at.push({ path: candidate.path, line: candidate.startLine, signal: candidate.signal, confidence: confidence2 });
15448
+ modelIds.set(key, entry);
15449
+ }
14981
15450
  }
14982
15451
  break;
15452
+ case "eval_file":
15453
+ if (candidate.confidence === "high")
15454
+ evalFiles.push(candidate);
15455
+ break;
14983
15456
  default:
14984
15457
  break;
14985
15458
  }
14986
15459
  if (!artifact)
14987
15460
  continue;
14988
- const existing = byId.get(artifact.id);
14989
- if (existing)
14990
- mergePromotedDuplicate(existing, artifact);
14991
- else
14992
- byId.set(artifact.id, artifact);
14993
- }
15461
+ add(artifact);
15462
+ }
15463
+ for (const artifact of promoteModelIds(modelIds, modelPathContext(rootDir)))
15464
+ add(artifact);
15465
+ for (const artifact of promoteEvalSuites(evalFiles, PROMOTED_CONFIDENCE.high, {
15466
+ artifactId: (suiteKey) => distinctStableId("eval", suiteKey),
15467
+ fileReason: detectorConfidenceReason
15468
+ }))
15469
+ add(artifact);
14994
15470
  return [...byId.values()];
14995
15471
  }
14996
15472
 
@@ -15325,7 +15801,7 @@ async function runHarnessCommand(context) {
15325
15801
  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
15802
  }
15327
15803
  if (context.flags["github-actions"] === "true") {
15328
- const { runGithubActionsAnalyze } = await import("./harness-github-actions-acxh0dbf.js");
15804
+ const { runGithubActionsAnalyze } = await import("./harness-github-actions-bgfnh4tw.js");
15329
15805
  const result = await runGithubActionsAnalyze(rootDir, {
15330
15806
  writeReport: (report2) => {
15331
15807
  const normalized = normalizeHarnessReport(report2);
@@ -15496,6 +15972,14 @@ async function runHarnessCommand(context) {
15496
15972
  context.output.writeData({ scanPlan, ...report, written, dryRun: context.dryRun });
15497
15973
  return { exitCode: 0 };
15498
15974
  }
15975
+ case "extract": {
15976
+ writeHumanProgress(context, "Harness extract: tracing model call sites (no LLM, no network)...");
15977
+ const { extractHarness, serializeExtractResult } = await import("./index-gtqxj0fp.js");
15978
+ const result = extractHarness(rootDir, { fresh: true });
15979
+ writeHumanProgress(context, `Harness extract: ${result.callSites.length} call sites, ${result.roles.length} traced roles across ${result.files} files (${result.programs} programs, ${result.durationMs.toLocaleString()} ms).`);
15980
+ context.output.writeData(serializeExtractResult(result));
15981
+ return { exitCode: 0 };
15982
+ }
15499
15983
  case "agents": {
15500
15984
  const graph = loadOrScanHarness(rootDir, tenant, context);
15501
15985
  context.output.writeData({
@@ -15614,7 +16098,7 @@ async function runHarnessCommand(context) {
15614
16098
  return { exitCode: 0 };
15615
16099
  }
15616
16100
  default:
15617
- throw new CliInputError(`Unknown harness command '${subcommand}'.`, "Usage: moda harness analyze|pull|validate-report|approve|scan|candidates|sync|delete|list|show|versions|diff|agents|graph|explain|write-summary|status|register|registration");
16101
+ throw new CliInputError(`Unknown harness command '${subcommand}'.`, "Usage: moda harness analyze|pull|validate-report|approve|scan|candidates|extract|sync|delete|list|show|versions|diff|agents|graph|explain|write-summary|status|register|registration");
15618
16102
  }
15619
16103
  }
15620
16104
  function writeHarnessAnalyzeResult(context, payload) {
@@ -16012,7 +16496,7 @@ function isToolGroupPlaceholder(name) {
16012
16496
  function isDetectedToolDefinitionReason(reason) {
16013
16497
  return typeof reason === "string" && (reason.startsWith("deterministic detector ") || reason.startsWith("Tool definition detected"));
16014
16498
  }
16015
- var MODEL_SOURCE_FILE2 = /\.(?:[cm]?[jt]sx?|py|json|jsonc|ya?ml|toml)$/i;
16499
+ var MODEL_SOURCE_FILE = /\.(?:[cm]?[jt]sx?|py|json|jsonc|ya?ml|toml)$/i;
16016
16500
  var NON_SOURCE_DIR2 = /(^|\/)(?:docs?|\.moda)\//i;
16017
16501
  function isSearchableModelId(id) {
16018
16502
  return id.length >= 4 && !/\s/.test(id) && /[\d/.:_-]/.test(id);
@@ -16060,7 +16544,7 @@ function modelIdsUsedInSource(rootDir, ids) {
16060
16544
  return true;
16061
16545
  let entries;
16062
16546
  try {
16063
- entries = readdirSync4(current).sort();
16547
+ entries = readdirSync5(current).sort();
16064
16548
  } catch {
16065
16549
  return true;
16066
16550
  }
@@ -16072,7 +16556,7 @@ function modelIdsUsedInSource(rootDir, ids) {
16072
16556
  }
16073
16557
  if (!stat.isFile() || stat.size > MAX_SCANNED_FILE_BYTES || isSecretishPath(rel2))
16074
16558
  return true;
16075
- if (TEST_PATH2.test(rel2) || !MODEL_SOURCE_FILE2.test(rel2))
16559
+ if (TEST_PATH2.test(rel2) || !MODEL_SOURCE_FILE.test(rel2))
16076
16560
  return true;
16077
16561
  if (files >= maxFiles) {
16078
16562
  complete = false;
@@ -16259,7 +16743,7 @@ var CONCERN_PROMOTED_ARTIFACT_TYPES = {
16259
16743
  topology: new Set,
16260
16744
  tools: new Set(["tool"]),
16261
16745
  prompts: new Set(["prompt"]),
16262
- evals: new Set(["model_config"])
16746
+ evals: new Set(["model_config", "eval"])
16263
16747
  };
16264
16748
  function deterministicArtifacts(rootDir, concern) {
16265
16749
  const graph = { artifacts: [], unknowns: [] };
@@ -16275,21 +16759,43 @@ function mergeDeterministicArtifacts(graph, rootDir, options = {}) {
16275
16759
  const allowedTypes = options.concern ? CONCERN_PROMOTED_ARTIFACT_TYPES[options.concern] : undefined;
16276
16760
  let promoted;
16277
16761
  try {
16278
- promoted = promoteHarnessCandidates(detectHarnessCandidates(rootDir), rootDir);
16762
+ promoted = promoteHarnessCandidates(detectHarnessCandidates(rootDir), rootDir, { fullEvidence: true });
16279
16763
  } catch {
16280
16764
  return { added: 0 };
16281
16765
  }
16282
16766
  backfillRefutationEvidenceReasons(graph);
16283
16767
  const refuted = analystRefutations(graph);
16284
16768
  const candidatePaths = new Set(promoted.flatMap((artifact) => artifact.evidence.map((entry) => typeof entry.path === "string" ? normalizePath2(entry.path) : "").filter(Boolean)));
16769
+ const modelLineCounts = new Map;
16770
+ for (const artifact of promoted) {
16771
+ if (artifact.type !== "model_config")
16772
+ continue;
16773
+ for (const entry of artifact.evidence) {
16774
+ const key = `${entry.path}:${entry.line}`;
16775
+ modelLineCounts.set(key, (modelLineCounts.get(key) ?? 0) + 1);
16776
+ }
16777
+ }
16778
+ const sharedModelLines = new Set([...modelLineCounts].filter(([, count]) => count > 1).map(([key]) => key));
16779
+ const pathContext = modelPathContext(rootDir);
16285
16780
  let added = 0;
16286
16781
  for (const artifact of promoted) {
16287
16782
  if (allowedTypes && !allowedTypes.has(artifact.type))
16288
16783
  continue;
16289
- if (graphCoversPromotedArtifact(graph, artifact))
16784
+ if (graphCoversPromotedArtifact(graph, artifact, sharedModelLines))
16290
16785
  continue;
16786
+ if ((artifact.type === "model_config" || artifact.type === "eval") && artifact.evidence.length > 1 && !refuted.candidateIds.has(artifact.id)) {
16787
+ const kept = artifact.evidence.filter((entry) => !isRefutedByAnalyst({ ...artifact, evidence: [entry] }, refuted, candidatePaths));
16788
+ if (kept.length === 0)
16789
+ continue;
16790
+ if (artifact.type === "model_config" && !kept.some((entry) => isHarnessModelPath(entry.path, pathContext)))
16791
+ continue;
16792
+ artifact.evidence = kept;
16793
+ if (artifact.sourcePath && !kept.some((entry) => entry.path === artifact.sourcePath))
16794
+ artifact.sourcePath = kept[0].path;
16795
+ }
16291
16796
  if (isRefutedByAnalyst(artifact, refuted, candidatePaths))
16292
16797
  continue;
16798
+ capPromotedEvidence(artifact);
16293
16799
  graph.artifacts.push(artifact);
16294
16800
  added += 1;
16295
16801
  }
@@ -16312,12 +16818,13 @@ var DETECTOR_ARTIFACT_TYPES = {
16312
16818
  message_construction: "prompt",
16313
16819
  tool_definition: "tool",
16314
16820
  tool_schema: "tool",
16315
- model_config: "model_config"
16821
+ model_config: "model_config",
16822
+ eval_file: "eval"
16316
16823
  };
16317
16824
  function refutedTypes(...texts) {
16318
16825
  const types = new Set;
16319
16826
  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)) {
16827
+ 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
16828
  types.add(DETECTOR_ARTIFACT_TYPES[match[1]]);
16322
16829
  }
16323
16830
  }
@@ -16379,7 +16886,26 @@ function artifactSourceKey(artifact) {
16379
16886
  const pinned = Array.isArray(artifact.evidence) ? artifact.evidence.find((entry) => typeof entry.path === "string" && entry.path.length > 0) : undefined;
16380
16887
  return pinned ? normalizePath2(pinned.path) : undefined;
16381
16888
  }
16382
- function graphCoversPromotedArtifact(graph, promoted) {
16889
+ function mentionsModelId(text, id) {
16890
+ if (!id)
16891
+ return false;
16892
+ const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16893
+ return new RegExp(`(?<![\\w./@-])${escaped}(?![\\w/-]|\\.\\w)`, "i").test(text);
16894
+ }
16895
+ function artifactPaths(artifact) {
16896
+ const paths = Array.isArray(artifact.evidence) ? artifact.evidence.map((entry) => typeof entry.path === "string" ? normalizePath2(entry.path) : "").filter(Boolean) : [];
16897
+ return artifact.sourcePath ? [normalizePath2(artifact.sourcePath), ...paths] : paths;
16898
+ }
16899
+ function graphCoversPromotedArtifact(graph, promoted, sharedModelLines = new Set) {
16900
+ if (promoted.type === "model_config" && graph.artifacts.some((existing) => existing.type === "model_config" && mentionsModelId(`${existing.name ?? ""}
16901
+ ${existing.body ?? ""}`, promoted.name)))
16902
+ return true;
16903
+ if (promoted.type === "eval") {
16904
+ const suite = new Set(artifactPaths(promoted));
16905
+ const suiteKey = promoted.sourcePath ? evalSuiteKey(normalizePath2(promoted.sourcePath)) : undefined;
16906
+ if (graph.artifacts.some((existing) => existing.type === "eval" && artifactPaths(existing).some((path) => suite.has(path) || suiteKey !== undefined && evalSuiteKey(path.replace(/\/+$/, "")) === suiteKey)))
16907
+ return true;
16908
+ }
16383
16909
  const promotedName = promoted.name.toLowerCase();
16384
16910
  const isDefinitionLine = (entry) => typeof entry.line === "number" && !/^(?:passed to a model call|prompt-named literal referenced)/.test(entry.reason ?? "");
16385
16911
  const promotedLines = promoted.evidence.filter(isDefinitionLine);
@@ -16395,7 +16921,7 @@ function graphCoversPromotedArtifact(graph, promoted) {
16395
16921
  if (!existingSource || !promotedSource || existingSource === promotedSource && !distinctInline)
16396
16922
  return true;
16397
16923
  }
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))) {
16924
+ 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
16925
  return true;
16400
16926
  }
16401
16927
  if (isWholeFilePrompt && existing.sourcePath === promoted.sourcePath)
@@ -17401,7 +17927,7 @@ var CONCERN_CANDIDATE_KINDS = {
17401
17927
  topology: ["llm_call_site", "framework_construct"],
17402
17928
  tools: ["tool_definition", "tool_schema"],
17403
17929
  prompts: ["prompt_file", "prompt_literal", "message_construction"],
17404
- evals: ["model_config"]
17930
+ evals: ["model_config", "eval_file"]
17405
17931
  };
17406
17932
  var MAX_SEED_CANDIDATES_PER_KIND = 40;
17407
17933
  var MAX_INLINE_CANDIDATE_EXCERPT_CHARS = 500;
@@ -17414,7 +17940,8 @@ var SEED_CANDIDATE_KIND_LABELS = {
17414
17940
  message_construction: "Message constructions",
17415
17941
  prompt_file: "Prompt files",
17416
17942
  prompt_literal: "Inline prompt literals",
17417
- model_config: "Model configuration"
17943
+ model_config: "Model configuration",
17944
+ eval_file: "Eval suite files"
17418
17945
  };
17419
17946
  function renderAnalystCandidateLines(report, concern) {
17420
17947
  const allowedKinds = concern ? new Set(CONCERN_CANDIDATE_KINDS[concern]) : undefined;
@@ -17435,7 +17962,7 @@ function renderAnalystCandidateLines(report, concern) {
17435
17962
  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
17963
  ];
17437
17964
  if (concern === "evals") {
17438
- lines.push("Note: evals, guardrails, and memory/retrieval stores have no deterministic detector — only model_config candidates are seeded. Discover the rest by investigation.");
17965
+ 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
17966
  }
17440
17967
  for (const [kind, label] of Object.entries(SEED_CANDIDATE_KIND_LABELS)) {
17441
17968
  const bucket = byKind.get(kind);
@@ -17500,7 +18027,7 @@ function readDirectoryPreflightPaths(rootDir) {
17500
18027
  const absolute = current === "." ? rootDir : join9(rootDir, current);
17501
18028
  let entries;
17502
18029
  try {
17503
- entries = readdirSync4(absolute);
18030
+ entries = readdirSync5(absolute);
17504
18031
  } catch {
17505
18032
  continue;
17506
18033
  }
@@ -17536,7 +18063,7 @@ function shouldSkipPreflightPath(path) {
17536
18063
  return isSecretishPath(normalized);
17537
18064
  }
17538
18065
  function isManifestLikePath(path) {
17539
- const name = basename6(path).toLowerCase();
18066
+ const name = basename7(path).toLowerCase();
17540
18067
  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
18068
  }
17542
18069
  function isDocLikePath(path) {
@@ -17566,7 +18093,7 @@ function resolveAnalystCommand(adapter) {
17566
18093
  return findOnPath("cursor-agent") || findOnPath("cursor-cli") || findOnPath("cursor") || findOnPath("/Applications/Cursor.app/Contents/Resources/app/bin/cursor");
17567
18094
  }
17568
18095
  function cursorSubcommandArgs(command, args) {
17569
- const name = basename6(command);
18096
+ const name = basename7(command);
17570
18097
  return name === "cursor" || name === "cursor-cli" ? ["agent", ...args] : args;
17571
18098
  }
17572
18099
  function findOnPath(command) {
@@ -17595,7 +18122,7 @@ function scanHarness(options = {}) {
17595
18122
  const readPlan = buildHarnessReadPlan(rootDir, options.pathWasExplicit === true);
17596
18123
  const graph = {
17597
18124
  schemaVersion: SCHEMA_VERSION,
17598
- harnessId: stableId("harness", repo.name || basename6(rootDir) || "repo"),
18125
+ harnessId: stableId("harness", repo.name || basename7(rootDir) || "repo"),
17599
18126
  tenant: options.tenantId ? {
17600
18127
  tenantId: options.tenantId,
17601
18128
  source: options.tenantSource ?? "scan_option"
@@ -17666,7 +18193,7 @@ function collectSnapshotInputs(rootDir, maxFiles, maxTextBytes) {
17666
18193
  if (stat.isDirectory()) {
17667
18194
  if (path !== "." && (isSkippedDir(path) || isSkippedPathPrefix(path)))
17668
18195
  return;
17669
- for (const entry of readdirSync4(current).sort())
18196
+ for (const entry of readdirSync5(current).sort())
17670
18197
  walk(join9(current, entry));
17671
18198
  return;
17672
18199
  }
@@ -18009,7 +18536,7 @@ function findFresherHarnessReports(rootDir) {
18009
18536
  visited += 1;
18010
18537
  let entries;
18011
18538
  try {
18012
- entries = readdirSync4(dir, { withFileTypes: true });
18539
+ entries = readdirSync5(dir, { withFileTypes: true });
18013
18540
  } catch {
18014
18541
  return;
18015
18542
  }
@@ -18257,7 +18784,7 @@ function inventoryFiles(rootDir, skipped, options = {}) {
18257
18784
  recordSkipped(skipped, { path: rel2, reason: "ignored directory" });
18258
18785
  return;
18259
18786
  }
18260
- for (const entry of readdirSync4(current).sort()) {
18787
+ for (const entry of readdirSync5(current).sort()) {
18261
18788
  walk(join9(current, entry));
18262
18789
  }
18263
18790
  return;
@@ -18344,7 +18871,7 @@ function numericEnvAllowZero(name, fallback) {
18344
18871
  function detectRepo(rootDir, files) {
18345
18872
  const packageJson = files.find((file) => file.path === "package.json");
18346
18873
  const pyproject = files.find((file) => file.path === "pyproject.toml");
18347
- const name = packageJson?.text ? safePackageName(packageJson.text) ?? basename6(rootDir) : pyproject?.text ? safeTomlName(pyproject.text) ?? basename6(rootDir) : basename6(rootDir);
18874
+ const name = packageJson?.text ? safePackageName(packageJson.text) ?? basename7(rootDir) : pyproject?.text ? safeTomlName(pyproject.text) ?? basename7(rootDir) : basename7(rootDir);
18348
18875
  const packageManagers = [
18349
18876
  files.some((file) => file.path === "bun.lock" || file.path === "bun.lockb") ? "bun" : null,
18350
18877
  files.some((file) => file.path === "pnpm-lock.yaml") ? "pnpm" : null,
@@ -18618,7 +19145,7 @@ function maybeAddSkillArtifact(graph, file) {
18618
19145
  upsertArtifact(graph, {
18619
19146
  id: stableId("skill", file.path),
18620
19147
  type: "skill",
18621
- name: titleFromId(basename6(file.path).replace(/\.(md|mdc)$/i, "")),
19148
+ name: titleFromId(basename7(file.path).replace(/\.(md|mdc)$/i, "")),
18622
19149
  scope: "global",
18623
19150
  sourcePath: file.path,
18624
19151
  contentHash: hashText(file.text ?? ""),
@@ -18661,7 +19188,7 @@ function inferRuntimeAgent(file, graph) {
18661
19188
  return null;
18662
19189
  if (!/\.(ts|tsx|js|mjs|py)$/i.test(file.path))
18663
19190
  return null;
18664
- if (basename6(file.path) === "__init__.py")
19191
+ if (basename7(file.path) === "__init__.py")
18665
19192
  return null;
18666
19193
  const loweredPath = file.path.toLowerCase();
18667
19194
  const loweredText = file.text.toLowerCase();
@@ -18768,7 +19295,7 @@ function inferArtifactsForFile(file, graph) {
18768
19295
  return false;
18769
19296
  if (artifact.sourcePath === file.path)
18770
19297
  return true;
18771
- const base = basename6(artifact.sourcePath);
19298
+ const base = basename7(artifact.sourcePath);
18772
19299
  if (text.includes(base) || text.includes(artifact.sourcePath))
18773
19300
  return true;
18774
19301
  if (artifact.type === "tool") {
@@ -19690,13 +20217,13 @@ function titleFromId(id) {
19690
20217
  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
20218
  }
19692
20219
  function promptNameFromPath(path) {
19693
- return titleFromId(basename6(path).replace(/\.(prompt\.)?(md|json|ya?ml)$/i, ""));
20220
+ return titleFromId(basename7(path).replace(/\.(prompt\.)?(md|json|ya?ml)$/i, ""));
19694
20221
  }
19695
20222
  function normalizePath2(path) {
19696
20223
  return path.split(sep5).join("/");
19697
20224
  }
19698
20225
  function hashText(text) {
19699
- return createHash5("sha256").update(text).digest("hex");
20226
+ return createHash6("sha256").update(text).digest("hex");
19700
20227
  }
19701
20228
 
19702
20229
  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 };