@moda-ai/cli 1.28.6 → 1.29.0
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/{cli-793dhrgb.js → cli-351a5yc4.js} +437 -35
- package/dist/{cli-n5qz81m0.js → cli-3w6hbpr3.js} +1 -1
- package/dist/cli.js +3 -3
- package/dist/{harness-github-actions-wd1ahhxz.js → harness-github-actions-p8xa3akx.js} +1 -1
- package/dist/{index-djg29jhz.js → index-8ksqhqmr.js} +2 -2
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
|
@@ -124,7 +124,7 @@ function resetApiRequestCount() {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
// src/harness-remote.ts
|
|
127
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as
|
|
127
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
128
128
|
import { join as join3 } from "node:path";
|
|
129
129
|
|
|
130
130
|
// src/markdown-terminal.ts
|
|
@@ -542,7 +542,7 @@ function createHarnessReport(options) {
|
|
|
542
542
|
unknowns: limitedUnknowns
|
|
543
543
|
};
|
|
544
544
|
}
|
|
545
|
-
function validateHarnessReport(report, rootDir) {
|
|
545
|
+
function validateHarnessReport(report, rootDir, options) {
|
|
546
546
|
const checks = [];
|
|
547
547
|
const errors = [];
|
|
548
548
|
const warnings = [];
|
|
@@ -713,6 +713,7 @@ function validateHarnessReport(report, rootDir) {
|
|
|
713
713
|
}
|
|
714
714
|
}
|
|
715
715
|
}
|
|
716
|
+
validateDeterministicFloor(report, options?.deterministicFloor, check, warn);
|
|
716
717
|
return {
|
|
717
718
|
schemaVersion: HARNESS_REPORT_VALIDATION_SCHEMA_VERSION,
|
|
718
719
|
reportId: report?.reportId ?? "unknown",
|
|
@@ -724,6 +725,27 @@ function validateHarnessReport(report, rootDir) {
|
|
|
724
725
|
warnings
|
|
725
726
|
};
|
|
726
727
|
}
|
|
728
|
+
function validateDeterministicFloor(report, floor, check, warn) {
|
|
729
|
+
if (!floor)
|
|
730
|
+
return;
|
|
731
|
+
if (process.env.MODA_HARNESS_FLOOR_CHECK === "0")
|
|
732
|
+
return;
|
|
733
|
+
const graph = isRecord(report?.graph) ? report.graph : {};
|
|
734
|
+
const toolArtifactCount = Array.isArray(graph.artifacts) ? graph.artifacts.filter((artifact) => isRecord(artifact) && artifact.type === "tool").length : 0;
|
|
735
|
+
const providerCount = safeArrayLength(graph.modelProviders);
|
|
736
|
+
const runtimeAgentCount = safeArrayLength(graph.runtimeAgents);
|
|
737
|
+
if (typeof floor.toolCandidates === "number" && floor.toolCandidates >= 5) {
|
|
738
|
+
const minimumTools = Math.ceil(0.5 * floor.toolCandidates);
|
|
739
|
+
check("floor_tools", toolArtifactCount >= minimumTools, `Deterministic scan found ${floor.toolCandidates} high-confidence tool definition candidate(s); the report graph must include at least ${minimumTools} tool artifact(s) but has ${toolArtifactCount}.`);
|
|
740
|
+
}
|
|
741
|
+
const providerPackages = Array.isArray(floor.providerPackages) ? floor.providerPackages.filter((name) => typeof name === "string" && name.length > 0) : [];
|
|
742
|
+
if (providerPackages.length > 0) {
|
|
743
|
+
check("floor_providers", providerCount > 0, `Manifest dependencies include model-provider package(s) ${providerPackages.join(", ")}, but the report graph.modelProviders is empty.`);
|
|
744
|
+
}
|
|
745
|
+
if (typeof floor.llmCallSites === "number" && floor.llmCallSites > 0 && runtimeAgentCount === 0) {
|
|
746
|
+
warn("floor_agents", `Deterministic scan found ${floor.llmCallSites} LLM call site(s) but the report graph.runtimeAgents is empty; sync will be blocked by the zero-agent gate.`);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
727
749
|
async function approveHarnessReport(options) {
|
|
728
750
|
const report = readHarnessReport(options.reportPath);
|
|
729
751
|
const validation = validateHarnessReport(report, options.rootDir);
|
|
@@ -760,9 +782,9 @@ async function approveHarnessReport(options) {
|
|
|
760
782
|
writeJson(join(options.rootDir, HARNESS_REPORT_APPROVAL_PATH), approval);
|
|
761
783
|
return { report, validation, approval };
|
|
762
784
|
}
|
|
763
|
-
function loadHarnessReportForSync(rootDir, reportPath) {
|
|
785
|
+
function loadHarnessReportForSync(rootDir, reportPath, options) {
|
|
764
786
|
const report = readHarnessReport(reportPath);
|
|
765
|
-
const validation = validateHarnessReport(report, rootDir);
|
|
787
|
+
const validation = validateHarnessReport(report, rootDir, options);
|
|
766
788
|
if (validation.status !== "pass") {
|
|
767
789
|
throw new CliInputError("Harness report validation failed; cannot sync.", validation.errors.slice(0, 5).join(`
|
|
768
790
|
`));
|
|
@@ -1753,11 +1775,11 @@ import {
|
|
|
1753
1775
|
existsSync as existsSync3,
|
|
1754
1776
|
lstatSync as lstatSync3,
|
|
1755
1777
|
mkdirSync as mkdirSync3,
|
|
1756
|
-
readFileSync as
|
|
1778
|
+
readFileSync as readFileSync4,
|
|
1757
1779
|
readdirSync,
|
|
1758
1780
|
writeFileSync as writeFileSync3
|
|
1759
1781
|
} from "node:fs";
|
|
1760
|
-
import { basename as basename4, delimiter, dirname as dirname4, join as join2, relative as relative3, resolve as
|
|
1782
|
+
import { basename as basename4, delimiter, dirname as dirname4, join as join2, relative as relative3, resolve as resolve5, sep } from "node:path";
|
|
1761
1783
|
import { confirm as confirm2, select } from "@inquirer/prompts";
|
|
1762
1784
|
|
|
1763
1785
|
// src/analyst-tui.ts
|
|
@@ -2400,7 +2422,7 @@ var LINE_PATTERNS = [
|
|
|
2400
2422
|
kind: "tool_definition",
|
|
2401
2423
|
signal: "python.tool_decorator",
|
|
2402
2424
|
confidence: "high",
|
|
2403
|
-
pattern: /^\s*@(tool|function_tool|
|
|
2425
|
+
pattern: /^\s*@(tool|function_tool|register_as_tool|\w+\.tool)\b/,
|
|
2404
2426
|
extensions: PY
|
|
2405
2427
|
},
|
|
2406
2428
|
{
|
|
@@ -2764,6 +2786,173 @@ function writeHarnessCandidatesReport(report, outPath) {
|
|
|
2764
2786
|
return target;
|
|
2765
2787
|
}
|
|
2766
2788
|
|
|
2789
|
+
// src/harness-candidate-promotion.ts
|
|
2790
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
2791
|
+
import { resolve as resolve4 } from "node:path";
|
|
2792
|
+
var PROMOTED_CONFIDENCE = {
|
|
2793
|
+
high: 0.85,
|
|
2794
|
+
medium: 0.6
|
|
2795
|
+
};
|
|
2796
|
+
var MAX_DECORATOR_LOOKAHEAD_LINES = 12;
|
|
2797
|
+
var PY_DEF_NAME = /^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(/;
|
|
2798
|
+
var PY_CLASS_NAME = /\bclass\s+([A-Za-z_]\w*)\s*\(/;
|
|
2799
|
+
var JS_ASSIGNMENT_NAME = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/;
|
|
2800
|
+
var JS_PROPERTY_NAME = /^\s*(?:public\s+|readonly\s+)?([A-Za-z_$][\w$]*)\s*:\s*(?:new\s+)?(?:[\w.$]*\.)?(?:tool|functionTool|createTool|dynamicTool|DynamicStructuredTool)\s*\(/;
|
|
2801
|
+
var JS_TRAILING_ASSIGNMENT_NAME = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:new\s+)?$/;
|
|
2802
|
+
var REGISTRY_CALL_NAME = /\b(?:register_tool|registerTool|add_tool)\s*\(\s*["']?([A-Za-z_$][\w$]*)/;
|
|
2803
|
+
var PROMPT_ASSIGNMENT_NAME = /^\s*(?:export\s+)?(?:(?:const|let|var)\s+)?([A-Za-z_$][\w$]*)\s*[:=]/;
|
|
2804
|
+
var MODEL_STRING = /["'`]([\w.:/-]*(?:gpt-|o[134](?:-mini)?\b|claude|gemini|llama|mistral|mixtral|deepseek|qwen|grok|sonnet|opus|haiku|command-r|glm|gemma)[\w.:/-]*)["'`]/i;
|
|
2805
|
+
function candidateEvidence(candidate) {
|
|
2806
|
+
return [{
|
|
2807
|
+
path: candidate.path,
|
|
2808
|
+
line: candidate.startLine,
|
|
2809
|
+
reason: `deterministic detector ${candidate.signal}`
|
|
2810
|
+
}];
|
|
2811
|
+
}
|
|
2812
|
+
function pathStem(path) {
|
|
2813
|
+
return path.replace(/\.[^.]+$/, "");
|
|
2814
|
+
}
|
|
2815
|
+
function readFileLines(rootDir, path, cache) {
|
|
2816
|
+
const cached = cache.get(path);
|
|
2817
|
+
if (cached !== undefined)
|
|
2818
|
+
return cached;
|
|
2819
|
+
let lines;
|
|
2820
|
+
try {
|
|
2821
|
+
lines = readFileSync3(resolve4(rootDir, path), "utf8").split(/\r?\n/);
|
|
2822
|
+
} catch {
|
|
2823
|
+
lines = null;
|
|
2824
|
+
}
|
|
2825
|
+
cache.set(path, lines);
|
|
2826
|
+
return lines;
|
|
2827
|
+
}
|
|
2828
|
+
function extractToolName(candidate, lines) {
|
|
2829
|
+
const line = lines[candidate.startLine - 1] ?? "";
|
|
2830
|
+
if (candidate.signal === "python.tool_decorator") {
|
|
2831
|
+
const end = Math.min(lines.length, candidate.startLine + MAX_DECORATOR_LOOKAHEAD_LINES);
|
|
2832
|
+
for (let i = candidate.startLine;i < end; i += 1) {
|
|
2833
|
+
const match = PY_DEF_NAME.exec(lines[i]);
|
|
2834
|
+
if (match)
|
|
2835
|
+
return match[1];
|
|
2836
|
+
}
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
if (candidate.signal === "python.base_tool_subclass") {
|
|
2840
|
+
return PY_CLASS_NAME.exec(line)?.[1];
|
|
2841
|
+
}
|
|
2842
|
+
if (candidate.signal === "tool_registry.registration") {
|
|
2843
|
+
return REGISTRY_CALL_NAME.exec(line)?.[1];
|
|
2844
|
+
}
|
|
2845
|
+
return JS_ASSIGNMENT_NAME.exec(line)?.[1] ?? JS_PROPERTY_NAME.exec(line)?.[1] ?? (candidate.startLine >= 2 ? JS_TRAILING_ASSIGNMENT_NAME.exec(lines[candidate.startLine - 2])?.[1] : undefined);
|
|
2846
|
+
}
|
|
2847
|
+
function promoteToolCandidate(candidate, rootDir, confidence, fileCache) {
|
|
2848
|
+
const lines = readFileLines(rootDir, candidate.path, fileCache);
|
|
2849
|
+
if (!lines)
|
|
2850
|
+
return;
|
|
2851
|
+
const name = extractToolName(candidate, lines);
|
|
2852
|
+
if (!name)
|
|
2853
|
+
return;
|
|
2854
|
+
return {
|
|
2855
|
+
id: stableId2("tool", `${candidate.path.replace(/\.[^./]+$/, "")}:${name}`),
|
|
2856
|
+
type: "tool",
|
|
2857
|
+
name: titleFromId2(name),
|
|
2858
|
+
scope: "shared",
|
|
2859
|
+
sourcePath: candidate.path,
|
|
2860
|
+
usedByAgentIds: [],
|
|
2861
|
+
confidence,
|
|
2862
|
+
evidence: candidateEvidence(candidate),
|
|
2863
|
+
body: extractToolBody({ path: candidate.path, rootDir }, { name, reason: candidate.signal })
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2866
|
+
function promotePromptFileCandidate(candidate, confidence) {
|
|
2867
|
+
return {
|
|
2868
|
+
id: stableId2("prompt", candidate.path),
|
|
2869
|
+
type: "prompt",
|
|
2870
|
+
name: promptNameFromPath(candidate.path),
|
|
2871
|
+
scope: "shared",
|
|
2872
|
+
sourcePath: candidate.path,
|
|
2873
|
+
usedByAgentIds: [],
|
|
2874
|
+
confidence,
|
|
2875
|
+
evidence: candidateEvidence(candidate)
|
|
2876
|
+
};
|
|
2877
|
+
}
|
|
2878
|
+
function promotePromptLiteralCandidate(candidate, rootDir, confidence, fileCache) {
|
|
2879
|
+
const lines = readFileLines(rootDir, candidate.path, fileCache);
|
|
2880
|
+
const assignedName = lines ? PROMPT_ASSIGNMENT_NAME.exec(lines[candidate.startLine - 1] ?? "")?.[1] ?? (candidate.startLine >= 2 ? PROMPT_ASSIGNMENT_NAME.exec(lines[candidate.startLine - 2] ?? "")?.[1] : undefined) : undefined;
|
|
2881
|
+
return {
|
|
2882
|
+
id: stableId2("prompt", `${pathStem(candidate.path)}:L${candidate.startLine}`),
|
|
2883
|
+
type: "prompt",
|
|
2884
|
+
name: assignedName ? titleFromId2(assignedName) : `Inline Prompt (${candidate.path}:${candidate.startLine})`,
|
|
2885
|
+
scope: "shared",
|
|
2886
|
+
usedByAgentIds: [],
|
|
2887
|
+
confidence,
|
|
2888
|
+
evidence: candidateEvidence(candidate)
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
function promoteModelConfigCandidate(candidate, rootDir, confidence, fileCache) {
|
|
2892
|
+
const lines = readFileLines(rootDir, candidate.path, fileCache);
|
|
2893
|
+
const sourceLine = lines?.[candidate.startLine - 1] ?? "";
|
|
2894
|
+
const model = MODEL_STRING.exec(sourceLine)?.[1] ?? MODEL_STRING.exec(candidate.excerpt)?.[1];
|
|
2895
|
+
if (!model)
|
|
2896
|
+
return;
|
|
2897
|
+
return {
|
|
2898
|
+
id: stableId2("model_config", `${pathStem(candidate.path)}:L${candidate.startLine}`),
|
|
2899
|
+
type: "model_config",
|
|
2900
|
+
name: model,
|
|
2901
|
+
scope: "shared",
|
|
2902
|
+
usedByAgentIds: [],
|
|
2903
|
+
confidence,
|
|
2904
|
+
evidence: candidateEvidence(candidate)
|
|
2905
|
+
};
|
|
2906
|
+
}
|
|
2907
|
+
function mergePromotedDuplicate(existing, next) {
|
|
2908
|
+
for (const entry of next.evidence) {
|
|
2909
|
+
const already = existing.evidence.some((candidate) => candidate.path === entry.path && candidate.line === entry.line && candidate.reason === entry.reason);
|
|
2910
|
+
if (!already)
|
|
2911
|
+
existing.evidence.push(entry);
|
|
2912
|
+
}
|
|
2913
|
+
existing.confidence = Math.max(existing.confidence, next.confidence);
|
|
2914
|
+
if (!existing.body && next.body)
|
|
2915
|
+
existing.body = next.body;
|
|
2916
|
+
}
|
|
2917
|
+
function promoteHarnessCandidates(report, rootDir) {
|
|
2918
|
+
const fileCache = new Map;
|
|
2919
|
+
const byId = new Map;
|
|
2920
|
+
for (const candidate of report.candidates) {
|
|
2921
|
+
const confidence = PROMOTED_CONFIDENCE[candidate.confidence];
|
|
2922
|
+
if (confidence === undefined)
|
|
2923
|
+
continue;
|
|
2924
|
+
let artifact;
|
|
2925
|
+
switch (candidate.kind) {
|
|
2926
|
+
case "tool_definition":
|
|
2927
|
+
artifact = promoteToolCandidate(candidate, rootDir, confidence, fileCache);
|
|
2928
|
+
break;
|
|
2929
|
+
case "prompt_file":
|
|
2930
|
+
artifact = promotePromptFileCandidate(candidate, confidence);
|
|
2931
|
+
break;
|
|
2932
|
+
case "prompt_literal":
|
|
2933
|
+
if (candidate.confidence === "high") {
|
|
2934
|
+
artifact = promotePromptLiteralCandidate(candidate, rootDir, confidence, fileCache);
|
|
2935
|
+
}
|
|
2936
|
+
break;
|
|
2937
|
+
case "model_config":
|
|
2938
|
+
if (candidate.confidence === "high") {
|
|
2939
|
+
artifact = promoteModelConfigCandidate(candidate, rootDir, confidence, fileCache);
|
|
2940
|
+
}
|
|
2941
|
+
break;
|
|
2942
|
+
default:
|
|
2943
|
+
break;
|
|
2944
|
+
}
|
|
2945
|
+
if (!artifact)
|
|
2946
|
+
continue;
|
|
2947
|
+
const existing = byId.get(artifact.id);
|
|
2948
|
+
if (existing)
|
|
2949
|
+
mergePromotedDuplicate(existing, artifact);
|
|
2950
|
+
else
|
|
2951
|
+
byId.set(artifact.id, artifact);
|
|
2952
|
+
}
|
|
2953
|
+
return [...byId.values()];
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2767
2956
|
// src/harness-sync-limits.ts
|
|
2768
2957
|
var MAX_HARNESS_SYNC_GRAPH_BYTES = 2000000;
|
|
2769
2958
|
function assertHarnessGraphSyncSize(graph) {
|
|
@@ -3103,7 +3292,7 @@ var REPORT_JSON_END = "MODA_HARNESS_REPORT_JSON_END";
|
|
|
3103
3292
|
var EXTERNAL_ANALYST_ADAPTERS = ["claude", "codex", "cursor"];
|
|
3104
3293
|
async function runHarnessCommand(context) {
|
|
3105
3294
|
const subcommand = context.positionals[0] ?? "scan";
|
|
3106
|
-
const rootDir =
|
|
3295
|
+
const rootDir = resolve5(context.cwd, context.flags.path ?? ".");
|
|
3107
3296
|
const selectedAgentId = selectedRuntimeAgentId(context.flags);
|
|
3108
3297
|
const tenant = resolveHarnessTenant(context.flags, context);
|
|
3109
3298
|
const scanPlan = buildCommandScanPlan(context, rootDir);
|
|
@@ -3148,7 +3337,9 @@ async function runHarnessCommand(context) {
|
|
|
3148
3337
|
}
|
|
3149
3338
|
if (context.flags["from-report"] || context.flags.report) {
|
|
3150
3339
|
const reportPath = resolveHarnessReportPath(rootDir, context.flags["from-report"] ?? context.flags.report);
|
|
3151
|
-
const { report, validation, approval, approved } = loadHarnessReportForSync(rootDir, reportPath
|
|
3340
|
+
const { report, validation, approval, approved } = loadHarnessReportForSync(rootDir, reportPath, {
|
|
3341
|
+
deterministicFloor: computeDeterministicFloor(rootDir)
|
|
3342
|
+
});
|
|
3152
3343
|
const reportHash = hashHarnessReport(report);
|
|
3153
3344
|
if (!approved && !context.dryRun) {
|
|
3154
3345
|
throw new CliInputError("Harness report is not approved; refusing to sync.", "Run `moda harness validate-report`, review the report, then run `moda harness approve --yes` before syncing.");
|
|
@@ -3342,11 +3533,13 @@ async function runHarnessCommand(context) {
|
|
|
3342
3533
|
elapsed_ms: Date.now() - context.startedAt
|
|
3343
3534
|
});
|
|
3344
3535
|
if (context.flags["github-actions"] === "true") {
|
|
3345
|
-
const { runGithubActionsAnalyze } = await import("./harness-github-actions-
|
|
3536
|
+
const { runGithubActionsAnalyze } = await import("./harness-github-actions-p8xa3akx.js");
|
|
3346
3537
|
const result = await runGithubActionsAnalyze(rootDir, {
|
|
3347
3538
|
writeReport: (report2) => {
|
|
3348
3539
|
const normalized = normalizeHarnessReport(report2);
|
|
3349
|
-
const validation2 = validateHarnessReport(normalized, rootDir
|
|
3540
|
+
const validation2 = validateHarnessReport(normalized, rootDir, {
|
|
3541
|
+
deterministicFloor: computeDeterministicFloor(rootDir)
|
|
3542
|
+
});
|
|
3350
3543
|
writeHarnessReportArtifacts({
|
|
3351
3544
|
rootDir,
|
|
3352
3545
|
report: normalized,
|
|
@@ -3434,7 +3627,9 @@ async function runHarnessCommand(context) {
|
|
|
3434
3627
|
case "validate-report": {
|
|
3435
3628
|
const reportPath = resolveHarnessReportPath(rootDir, context.positionals[1] ?? context.flags.report);
|
|
3436
3629
|
const report = readHarnessReport(reportPath);
|
|
3437
|
-
const validation = validateHarnessReport(report, rootDir
|
|
3630
|
+
const validation = validateHarnessReport(report, rootDir, {
|
|
3631
|
+
deterministicFloor: computeDeterministicFloor(rootDir)
|
|
3632
|
+
});
|
|
3438
3633
|
if (!context.dryRun) {
|
|
3439
3634
|
writeHarnessReportArtifacts({
|
|
3440
3635
|
rootDir,
|
|
@@ -3479,7 +3674,7 @@ async function runHarnessCommand(context) {
|
|
|
3479
3674
|
throw new CliInputError("Missing output path for --out.", "Usage: moda harness candidates --out=<file>");
|
|
3480
3675
|
}
|
|
3481
3676
|
if (outFlag && !context.dryRun) {
|
|
3482
|
-
written.push(writeHarnessCandidatesReport(report,
|
|
3677
|
+
written.push(writeHarnessCandidatesReport(report, resolve5(context.cwd, outFlag)));
|
|
3483
3678
|
}
|
|
3484
3679
|
writeHumanProgress(context, `Harness candidates: ${report.stats.candidateCount} candidates across ${report.stats.filesWithCandidates} files (${report.stats.filesScanned} scanned, ${report.stats.filesPerSecond.toLocaleString()} files/sec).`);
|
|
3485
3680
|
context.output.writeData({ scanPlan, ...report, written, dryRun: context.dryRun });
|
|
@@ -3740,7 +3935,9 @@ ${result.stderr}`, 1200);
|
|
|
3740
3935
|
elapsed_ms: Date.now() - options.context.startedAt
|
|
3741
3936
|
});
|
|
3742
3937
|
analystTui?.setValidation("running");
|
|
3743
|
-
const validation = validateHarnessReport(report, options.rootDir
|
|
3938
|
+
const validation = validateHarnessReport(report, options.rootDir, {
|
|
3939
|
+
deterministicFloor: computeDeterministicFloor(options.rootDir)
|
|
3940
|
+
});
|
|
3744
3941
|
analystTui?.setValidation(validation.status === "pass" ? "pass" : "fail");
|
|
3745
3942
|
if (!analystTui)
|
|
3746
3943
|
writeHumanProgress(options.context, `Moda Analyst: validation ${validation.status}.`);
|
|
@@ -3790,7 +3987,7 @@ function buildExternalAnalystEnv(env) {
|
|
|
3790
3987
|
return next;
|
|
3791
3988
|
}
|
|
3792
3989
|
function readExternalAnalystReport(rootDir, result, adapter) {
|
|
3793
|
-
const reportPath =
|
|
3990
|
+
const reportPath = resolve5(rootDir, HARNESS_REPORT_PATH);
|
|
3794
3991
|
if (existsSync3(reportPath))
|
|
3795
3992
|
return repairExternalAnalystReport(readHarnessReport(reportPath), rootDir);
|
|
3796
3993
|
const output = [
|
|
@@ -3805,7 +4002,7 @@ function readExternalAnalystReport(rootDir, result, adapter) {
|
|
|
3805
4002
|
return parsed;
|
|
3806
4003
|
let debugHint = "";
|
|
3807
4004
|
try {
|
|
3808
|
-
const debugPath =
|
|
4005
|
+
const debugPath = resolve5(rootDir, MODA_DIR, "analyst-debug.log");
|
|
3809
4006
|
mkdirSync3(dirname4(debugPath), { recursive: true });
|
|
3810
4007
|
writeFileSync3(debugPath, [
|
|
3811
4008
|
`# analyst=${adapter} status=${result.status} signal=${result.signal} timedOut=${result.timedOut} maxBufferExceeded=${result.maxBufferExceeded}`,
|
|
@@ -3865,10 +4062,56 @@ function parseHarnessReportCandidate(candidate) {
|
|
|
3865
4062
|
function repairExternalAnalystReport(report, rootDir) {
|
|
3866
4063
|
const normalized = normalizeHarnessReport(report);
|
|
3867
4064
|
repairCitationLines(normalized, rootDir);
|
|
4065
|
+
mergeDeterministicArtifacts(normalized.graph, rootDir);
|
|
3868
4066
|
hydratePromptAndToolBodies(normalized.graph, rootDir);
|
|
3869
4067
|
repairReportSummaryCounts(normalized);
|
|
3870
4068
|
return normalized;
|
|
3871
4069
|
}
|
|
4070
|
+
function mergeDeterministicArtifacts(graph, rootDir) {
|
|
4071
|
+
if (process.env.MODA_HARNESS_DETERMINISTIC_MERGE === "0")
|
|
4072
|
+
return { added: 0 };
|
|
4073
|
+
let promoted;
|
|
4074
|
+
try {
|
|
4075
|
+
promoted = promoteHarnessCandidates(detectHarnessCandidates(rootDir), rootDir);
|
|
4076
|
+
} catch {
|
|
4077
|
+
return { added: 0 };
|
|
4078
|
+
}
|
|
4079
|
+
let added = 0;
|
|
4080
|
+
for (const artifact of promoted) {
|
|
4081
|
+
if (graphCoversPromotedArtifact(graph, artifact))
|
|
4082
|
+
continue;
|
|
4083
|
+
graph.artifacts.push(artifact);
|
|
4084
|
+
added += 1;
|
|
4085
|
+
}
|
|
4086
|
+
return { added };
|
|
4087
|
+
}
|
|
4088
|
+
function artifactSourceKey(artifact) {
|
|
4089
|
+
if (artifact.sourcePath)
|
|
4090
|
+
return normalizePath3(artifact.sourcePath);
|
|
4091
|
+
const pinned = Array.isArray(artifact.evidence) ? artifact.evidence.find((entry) => typeof entry.path === "string" && entry.path.length > 0) : undefined;
|
|
4092
|
+
return pinned ? normalizePath3(pinned.path) : undefined;
|
|
4093
|
+
}
|
|
4094
|
+
function graphCoversPromotedArtifact(graph, promoted) {
|
|
4095
|
+
const promotedName = promoted.name.toLowerCase();
|
|
4096
|
+
const promotedLines = promoted.evidence.filter((entry) => typeof entry.line === "number");
|
|
4097
|
+
const promotedSource = artifactSourceKey(promoted);
|
|
4098
|
+
const isWholeFilePrompt = promoted.type === "prompt" && Boolean(promoted.sourcePath);
|
|
4099
|
+
return graph.artifacts.some((existing) => {
|
|
4100
|
+
if (existing.id === promoted.id)
|
|
4101
|
+
return true;
|
|
4102
|
+
if (existing.type === promoted.type && existing.name?.toLowerCase() === promotedName) {
|
|
4103
|
+
const existingSource = artifactSourceKey(existing);
|
|
4104
|
+
if (!existingSource || !promotedSource || existingSource === promotedSource)
|
|
4105
|
+
return true;
|
|
4106
|
+
}
|
|
4107
|
+
if (existing.type === promoted.type && Array.isArray(existing.evidence) && existing.evidence.some((entry) => typeof entry.line === "number" && promotedLines.some((line) => line.path === entry.path && line.line === entry.line))) {
|
|
4108
|
+
return true;
|
|
4109
|
+
}
|
|
4110
|
+
if (isWholeFilePrompt && existing.sourcePath === promoted.sourcePath)
|
|
4111
|
+
return true;
|
|
4112
|
+
return false;
|
|
4113
|
+
});
|
|
4114
|
+
}
|
|
3872
4115
|
function repairReportSummaryCounts(report) {
|
|
3873
4116
|
const graph = report.graph;
|
|
3874
4117
|
report.summary = {
|
|
@@ -3892,12 +4135,12 @@ function repairCitationLines(report, rootDir) {
|
|
|
3892
4135
|
if (citation.type !== "file" && citation.type !== "generated_doc" || !citation.path || !citation.excerpt)
|
|
3893
4136
|
continue;
|
|
3894
4137
|
const excerpt = citation.excerpt;
|
|
3895
|
-
const candidatePath =
|
|
4138
|
+
const candidatePath = resolve5(rootDir, citation.path);
|
|
3896
4139
|
if (!existsSync3(candidatePath))
|
|
3897
4140
|
continue;
|
|
3898
4141
|
let text;
|
|
3899
4142
|
try {
|
|
3900
|
-
text =
|
|
4143
|
+
text = readFileSync4(candidatePath, "utf8");
|
|
3901
4144
|
} catch {
|
|
3902
4145
|
continue;
|
|
3903
4146
|
}
|
|
@@ -4634,7 +4877,7 @@ function renderAnalystCandidateLines(report) {
|
|
|
4634
4877
|
const lines = [
|
|
4635
4878
|
"",
|
|
4636
4879
|
`Deterministic candidate inventory (${report.candidates.length} candidates across ${report.stats.filesWithCandidates} files, scanned in ${report.stats.durationMs}ms):`,
|
|
4637
|
-
"Each entry is path:line [detector] excerpt.
|
|
4880
|
+
"Each entry is path:line [detector] excerpt. Candidates are pattern matches — verify each against the real file and drop false positives. 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), 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."
|
|
4638
4881
|
];
|
|
4639
4882
|
for (const [kind, label] of Object.entries(SEED_CANDIDATE_KIND_LABELS)) {
|
|
4640
4883
|
const bucket = byKind.get(kind);
|
|
@@ -4786,7 +5029,7 @@ function truncateText(value, maxLength) {
|
|
|
4786
5029
|
return compact.length > maxLength ? `${compact.slice(0, maxLength - 1)}...` : compact;
|
|
4787
5030
|
}
|
|
4788
5031
|
function scanHarness(options = {}) {
|
|
4789
|
-
const rootDir =
|
|
5032
|
+
const rootDir = resolve5(options.rootDir ?? process.cwd());
|
|
4790
5033
|
const skipped = [];
|
|
4791
5034
|
const files = inventoryFiles(rootDir, skipped);
|
|
4792
5035
|
const annotations = files.flatMap((file) => parseAnnotations(file));
|
|
@@ -4888,7 +5131,7 @@ function collectSnapshotInputs(rootDir, maxFiles, maxTextBytes) {
|
|
|
4888
5131
|
let text = "";
|
|
4889
5132
|
if (isTextFile(path)) {
|
|
4890
5133
|
try {
|
|
4891
|
-
text =
|
|
5134
|
+
text = readFileSync4(current, "utf8");
|
|
4892
5135
|
inventoryTextBytes += Buffer.byteLength(text, "utf8");
|
|
4893
5136
|
} catch {
|
|
4894
5137
|
return;
|
|
@@ -4901,7 +5144,7 @@ function collectSnapshotInputs(rootDir, maxFiles, maxTextBytes) {
|
|
|
4901
5144
|
return inputs;
|
|
4902
5145
|
}
|
|
4903
5146
|
function buildSourceSnapshot(rootDir) {
|
|
4904
|
-
const root =
|
|
5147
|
+
const root = resolve5(rootDir);
|
|
4905
5148
|
const maxFiles = numericEnv("MODA_HARNESS_MAX_FILES", DEFAULT_MAX_INVENTORY_FILES);
|
|
4906
5149
|
const maxTextBytes = numericEnv("MODA_HARNESS_MAX_TEXT_BYTES", DEFAULT_MAX_INVENTORY_TEXT_BYTES);
|
|
4907
5150
|
const inputs = collectSnapshotInputs(root, maxFiles, maxTextBytes);
|
|
@@ -5128,7 +5371,7 @@ function readHarnessSyncState(rootDir) {
|
|
|
5128
5371
|
if (!existsSync3(path))
|
|
5129
5372
|
return;
|
|
5130
5373
|
try {
|
|
5131
|
-
const parsed = JSON.parse(
|
|
5374
|
+
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
5132
5375
|
if (!isRecord3(parsed) || parsed.schemaVersion !== "sync_state.v0.1") {
|
|
5133
5376
|
return;
|
|
5134
5377
|
}
|
|
@@ -5245,7 +5488,7 @@ function findFresherHarnessReports(rootDir) {
|
|
|
5245
5488
|
return fresher.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
5246
5489
|
}
|
|
5247
5490
|
function warnFresherSubdirHarnessReports(context, rootDir, explicitReport) {
|
|
5248
|
-
const explicitReportPath = explicitReport && explicitReport !== "true" ? normalizePath3(relative3(rootDir,
|
|
5491
|
+
const explicitReportPath = explicitReport && explicitReport !== "true" ? normalizePath3(relative3(rootDir, resolve5(rootDir, explicitReport))) : undefined;
|
|
5249
5492
|
if (explicitReportPath && explicitReportPath !== normalizePath3(HARNESS_REPORT_PATH))
|
|
5250
5493
|
return;
|
|
5251
5494
|
let fresher;
|
|
@@ -5262,7 +5505,7 @@ function warnFresherSubdirHarnessReports(context, rootDir, explicitReport) {
|
|
|
5262
5505
|
writeHarnessNotice(context, `A fresher harness report exists at ${newest.path} (this directory has ${rootState}). If you meant that report, re-run with --path=${subdir}.`);
|
|
5263
5506
|
}
|
|
5264
5507
|
function readHarnessGraph(manifestPath) {
|
|
5265
|
-
return JSON.parse(
|
|
5508
|
+
return JSON.parse(readFileSync4(manifestPath, "utf8"));
|
|
5266
5509
|
}
|
|
5267
5510
|
function writeHarnessArtifacts(graph, rootDir, options = {}) {
|
|
5268
5511
|
const modaDir = join2(rootDir, MODA_DIR);
|
|
@@ -5439,7 +5682,7 @@ function explainAgent(graph, agent) {
|
|
|
5439
5682
|
}
|
|
5440
5683
|
function inventoryFiles(rootDir, skipped, options = {}) {
|
|
5441
5684
|
const files = [];
|
|
5442
|
-
const rootReal =
|
|
5685
|
+
const rootReal = resolve5(rootDir);
|
|
5443
5686
|
const maxFiles = numericEnv("MODA_HARNESS_MAX_FILES", DEFAULT_MAX_INVENTORY_FILES);
|
|
5444
5687
|
const maxTextBytes = numericEnv("MODA_HARNESS_MAX_TEXT_BYTES", DEFAULT_MAX_INVENTORY_TEXT_BYTES);
|
|
5445
5688
|
let textBytes = 0;
|
|
@@ -5493,7 +5736,7 @@ function inventoryFiles(rootDir, skipped, options = {}) {
|
|
|
5493
5736
|
recordSkipped(skipped, { path: rel, reason: "scan text budget exceeded" });
|
|
5494
5737
|
} else {
|
|
5495
5738
|
try {
|
|
5496
|
-
item.text =
|
|
5739
|
+
item.text = readFileSync4(current, "utf8");
|
|
5497
5740
|
textBytes += Buffer.byteLength(item.text, "utf8");
|
|
5498
5741
|
} catch {
|
|
5499
5742
|
recordSkipped(skipped, { path: rel, reason: "text read failed" });
|
|
@@ -5800,7 +6043,7 @@ function maybeAddPromptArtifact(graph, file) {
|
|
|
5800
6043
|
if (!isPrompt)
|
|
5801
6044
|
return;
|
|
5802
6045
|
const id = stableId2("prompt", file.path);
|
|
5803
|
-
const text = file.text ??
|
|
6046
|
+
const text = file.text ?? readFileSync4(file.absPath, "utf8");
|
|
5804
6047
|
upsertArtifact(graph, {
|
|
5805
6048
|
id,
|
|
5806
6049
|
type: "prompt",
|
|
@@ -6027,12 +6270,27 @@ function detectToolDefinitions(file) {
|
|
|
6027
6270
|
const pyDecorated = /@function_tool[^\n]*\n(?:[ \t]*@[^\n]*\n)*[ \t]*(?:async[ \t]+)?def[ \t]+([A-Za-z_]\w*)/g;
|
|
6028
6271
|
for (const match of text.matchAll(pyDecorated))
|
|
6029
6272
|
found.set(match[1], "@function_tool-decorated function");
|
|
6273
|
+
const pyToolDecorated = /@((?:[A-Za-z_]\w*\.)?tool|register_as_tool)\b[^\n]*\n(?:[ \t]*@[^\n]*\n)*[ \t]*(?:async[ \t]+)?def[ \t]+([A-Za-z_]\w*)/g;
|
|
6274
|
+
for (const match of text.matchAll(pyToolDecorated))
|
|
6275
|
+
found.set(match[2], `@${match[1]}-decorated function`);
|
|
6276
|
+
const pyToolClass = /class[ \t]+([A-Za-z_]\w*)\s*\(\s*(?:[A-Za-z_]\w*\.)?(BaseTool|StructuredTool)\b/g;
|
|
6277
|
+
for (const match of text.matchAll(pyToolClass))
|
|
6278
|
+
found.set(match[1], `${match[2]} subclass`);
|
|
6030
6279
|
const tsFactory = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:[\w.]*\.)?(?:tool|functionTool|createTool|dynamicTool)\s*\(/g;
|
|
6031
6280
|
for (const match of text.matchAll(tsFactory))
|
|
6032
6281
|
found.set(match[1], "tool factory assignment");
|
|
6033
|
-
const tsNewTool = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*new\s+(?:Dynamic|Structured)?Tool\b/g;
|
|
6282
|
+
const tsNewTool = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*new\s+(?:DynamicStructured|Dynamic|Structured)?Tool\b/g;
|
|
6034
6283
|
for (const match of text.matchAll(tsNewTool))
|
|
6035
6284
|
found.set(match[1], "tool constructor assignment");
|
|
6285
|
+
const tsInlineToolCtor = /(?:^|[(,[]|return\s)\s*(?:new\s+)?(?:DynamicStructured|Dynamic|Structured)Tool\s*\(\s*\{[^{}]*?\bname\s*:\s*["'`]([\w .:-]+)["'`]/gm;
|
|
6286
|
+
for (const match of text.matchAll(tsInlineToolCtor))
|
|
6287
|
+
found.set(match[1], "tool constructor call");
|
|
6288
|
+
const registryCall = /\b(register_tool|registerTool|add_tool|ToolRegistry)\s*\(\s*\[?\s*(?:name\s*=\s*)?(?:["']([\w .:-]+)["']|([A-Za-z_$][\w$]*))/g;
|
|
6289
|
+
for (const match of text.matchAll(registryCall)) {
|
|
6290
|
+
const name = match[2] ?? match[3];
|
|
6291
|
+
if (name)
|
|
6292
|
+
found.set(name, `${match[1]} registration`);
|
|
6293
|
+
}
|
|
6036
6294
|
for (const match of text.matchAll(HOSTED_TOOL_CALL))
|
|
6037
6295
|
found.set(match[1], `hosted tool ${match[1]}`);
|
|
6038
6296
|
return [...found.entries()].map(([name, reason]) => ({ name, reason }));
|
|
@@ -6048,7 +6306,7 @@ function maybeAddToolArtifact(graph, file) {
|
|
|
6048
6306
|
for (const tool of tools) {
|
|
6049
6307
|
const body = extractToolBody(file, tool);
|
|
6050
6308
|
upsertArtifact(graph, {
|
|
6051
|
-
id: stableId2("tool", `${file.path}:${tool.name}`),
|
|
6309
|
+
id: stableId2("tool", `${file.path.replace(/\.[^./]+$/, "")}:${tool.name}`),
|
|
6052
6310
|
type: "tool",
|
|
6053
6311
|
name: titleFromId2(tool.name),
|
|
6054
6312
|
scope: "agent",
|
|
@@ -6156,6 +6414,55 @@ var FRAMEWORK_CATALOG = [
|
|
|
6156
6414
|
kind: "agent_framework",
|
|
6157
6415
|
packageNames: ["pydantic-ai", "pydantic_ai"],
|
|
6158
6416
|
patterns: [/pydantic_ai/i]
|
|
6417
|
+
},
|
|
6418
|
+
{
|
|
6419
|
+
id: "framework_langgraph",
|
|
6420
|
+
name: "LangGraph",
|
|
6421
|
+
kind: "agent_framework",
|
|
6422
|
+
packageNames: ["langgraph", "@langchain/langgraph"],
|
|
6423
|
+
patterns: [/\blanggraph\b/i, /\bStateGraph\s*\(/]
|
|
6424
|
+
},
|
|
6425
|
+
{
|
|
6426
|
+
id: "framework_autogen",
|
|
6427
|
+
name: "AutoGen",
|
|
6428
|
+
kind: "agent_framework",
|
|
6429
|
+
packageNames: ["autogen", "pyautogen", "autogen-agentchat"],
|
|
6430
|
+
patterns: [/\bautogen\b/i]
|
|
6431
|
+
},
|
|
6432
|
+
{
|
|
6433
|
+
id: "framework_dspy",
|
|
6434
|
+
name: "DSPy",
|
|
6435
|
+
kind: "agent_framework",
|
|
6436
|
+
packageNames: ["dspy", "dspy-ai"],
|
|
6437
|
+
patterns: [/\bdspy\b/i]
|
|
6438
|
+
},
|
|
6439
|
+
{
|
|
6440
|
+
id: "framework_semantic_kernel",
|
|
6441
|
+
name: "Semantic Kernel",
|
|
6442
|
+
kind: "agent_framework",
|
|
6443
|
+
packageNames: ["semantic-kernel"],
|
|
6444
|
+
patterns: [/semantic[-_]?kernel/i]
|
|
6445
|
+
},
|
|
6446
|
+
{
|
|
6447
|
+
id: "framework_smolagents",
|
|
6448
|
+
name: "Smolagents",
|
|
6449
|
+
kind: "agent_framework",
|
|
6450
|
+
packageNames: ["smolagents"],
|
|
6451
|
+
patterns: [/\bsmolagents\b/i]
|
|
6452
|
+
},
|
|
6453
|
+
{
|
|
6454
|
+
id: "framework_llama_index",
|
|
6455
|
+
name: "LlamaIndex",
|
|
6456
|
+
kind: "agent_framework",
|
|
6457
|
+
packageNames: ["llama-index", "llamaindex"],
|
|
6458
|
+
patterns: [/llama[-_]?index/i]
|
|
6459
|
+
},
|
|
6460
|
+
{
|
|
6461
|
+
id: "framework_claude_agent_sdk",
|
|
6462
|
+
name: "Claude Agent SDK",
|
|
6463
|
+
kind: "agent_framework",
|
|
6464
|
+
packageNames: ["@anthropic-ai/claude-agent-sdk", "claude-agent-sdk"],
|
|
6465
|
+
patterns: [/claude[-_]agent[-_]sdk/i]
|
|
6159
6466
|
}
|
|
6160
6467
|
];
|
|
6161
6468
|
var PROVIDER_CATALOG = [
|
|
@@ -6206,6 +6513,46 @@ var PROVIDER_CATALOG = [
|
|
|
6206
6513
|
packageNames: ["ollama"],
|
|
6207
6514
|
envVars: ["OLLAMA_HOST"],
|
|
6208
6515
|
patterns: [/\bollama\b/i]
|
|
6516
|
+
},
|
|
6517
|
+
{
|
|
6518
|
+
id: "provider_litellm",
|
|
6519
|
+
name: "LiteLLM",
|
|
6520
|
+
kind: "model_provider",
|
|
6521
|
+
packageNames: ["litellm"],
|
|
6522
|
+
envVars: [],
|
|
6523
|
+
patterns: [/\blitellm\b/i]
|
|
6524
|
+
},
|
|
6525
|
+
{
|
|
6526
|
+
id: "provider_aws_bedrock",
|
|
6527
|
+
name: "AWS Bedrock",
|
|
6528
|
+
kind: "model_provider",
|
|
6529
|
+
packageNames: ["@aws-sdk/client-bedrock-runtime"],
|
|
6530
|
+
envVars: ["AWS_BEARER_TOKEN_BEDROCK"],
|
|
6531
|
+
patterns: [/bedrock-runtime/i, /\bBedrockRuntimeClient\b/, /\bInvokeModelCommand\b/, /\binvoke_model\s*\(/]
|
|
6532
|
+
},
|
|
6533
|
+
{
|
|
6534
|
+
id: "provider_openrouter",
|
|
6535
|
+
name: "OpenRouter",
|
|
6536
|
+
kind: "model_provider",
|
|
6537
|
+
packageNames: ["openrouter", "@openrouter/ai-sdk-provider"],
|
|
6538
|
+
envVars: ["OPENROUTER_API_KEY"],
|
|
6539
|
+
patterns: [/openrouter\.ai/i, /\bopenrouter\b/i, /OPENROUTER_API_KEY/]
|
|
6540
|
+
},
|
|
6541
|
+
{
|
|
6542
|
+
id: "provider_xai",
|
|
6543
|
+
name: "xAI",
|
|
6544
|
+
kind: "model_provider",
|
|
6545
|
+
packageNames: ["xai-sdk"],
|
|
6546
|
+
envVars: ["XAI_API_KEY"],
|
|
6547
|
+
patterns: [/\bgrok-[\w.-]+/i, /api\.x\.ai/i, /XAI_API_KEY/]
|
|
6548
|
+
},
|
|
6549
|
+
{
|
|
6550
|
+
id: "provider_groq",
|
|
6551
|
+
name: "Groq",
|
|
6552
|
+
kind: "model_provider",
|
|
6553
|
+
packageNames: ["groq", "groq-sdk"],
|
|
6554
|
+
envVars: ["GROQ_API_KEY"],
|
|
6555
|
+
patterns: [/\bgroq\b/i]
|
|
6209
6556
|
}
|
|
6210
6557
|
];
|
|
6211
6558
|
function detectAgentFrameworks(files) {
|
|
@@ -6247,6 +6594,59 @@ function detectModelProviders(files) {
|
|
|
6247
6594
|
}
|
|
6248
6595
|
return providers.sort((a, b) => a.id.localeCompare(b.id));
|
|
6249
6596
|
}
|
|
6597
|
+
function computeDeterministicFloor(rootDir) {
|
|
6598
|
+
try {
|
|
6599
|
+
const candidateReport = detectHarnessCandidates(rootDir);
|
|
6600
|
+
let toolCandidates = 0;
|
|
6601
|
+
let promptCandidates = 0;
|
|
6602
|
+
let llmCallSites = 0;
|
|
6603
|
+
for (const candidate of candidateReport.candidates) {
|
|
6604
|
+
if (candidate.kind === "tool_definition" && candidate.confidence === "high") {
|
|
6605
|
+
toolCandidates += 1;
|
|
6606
|
+
} else if ((candidate.kind === "prompt_file" || candidate.kind === "prompt_literal") && candidate.confidence === "high") {
|
|
6607
|
+
promptCandidates += 1;
|
|
6608
|
+
} else if (candidate.kind === "llm_call_site") {
|
|
6609
|
+
llmCallSites += 1;
|
|
6610
|
+
}
|
|
6611
|
+
}
|
|
6612
|
+
return {
|
|
6613
|
+
toolCandidates,
|
|
6614
|
+
promptCandidates,
|
|
6615
|
+
llmCallSites,
|
|
6616
|
+
providerPackages: detectProviderPackagesInManifests(rootDir)
|
|
6617
|
+
};
|
|
6618
|
+
} catch {
|
|
6619
|
+
return { toolCandidates: 0, promptCandidates: 0, llmCallSites: 0, providerPackages: [] };
|
|
6620
|
+
}
|
|
6621
|
+
}
|
|
6622
|
+
function detectProviderPackagesInManifests(rootDir) {
|
|
6623
|
+
const present = new Set;
|
|
6624
|
+
const packageJsonPath = join2(rootDir, "package.json");
|
|
6625
|
+
if (existsSync3(packageJsonPath)) {
|
|
6626
|
+
try {
|
|
6627
|
+
const deps = packageDependencyNames(readFileSync4(packageJsonPath, "utf8"));
|
|
6628
|
+
for (const provider of PROVIDER_CATALOG) {
|
|
6629
|
+
for (const name of provider.packageNames) {
|
|
6630
|
+
if (deps.has(name))
|
|
6631
|
+
present.add(name);
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6634
|
+
} catch {}
|
|
6635
|
+
}
|
|
6636
|
+
const pyprojectPath = join2(rootDir, "pyproject.toml");
|
|
6637
|
+
if (existsSync3(pyprojectPath)) {
|
|
6638
|
+
try {
|
|
6639
|
+
const deps = pyprojectDependencyNames(readFileSync4(pyprojectPath, "utf8"));
|
|
6640
|
+
for (const provider of PROVIDER_CATALOG) {
|
|
6641
|
+
for (const name of provider.packageNames) {
|
|
6642
|
+
if (deps.has(name.toLowerCase()))
|
|
6643
|
+
present.add(name);
|
|
6644
|
+
}
|
|
6645
|
+
}
|
|
6646
|
+
} catch {}
|
|
6647
|
+
}
|
|
6648
|
+
return [...present].sort();
|
|
6649
|
+
}
|
|
6250
6650
|
function detectionEvidence(files, packageNames, patterns) {
|
|
6251
6651
|
const evidence = [];
|
|
6252
6652
|
for (const file of files) {
|
|
@@ -6328,7 +6728,7 @@ function pyprojectDependencyNames(text) {
|
|
|
6328
6728
|
}
|
|
6329
6729
|
function modelHintsForProvider(providerId, files) {
|
|
6330
6730
|
const hints = new Set;
|
|
6331
|
-
const pattern = providerId === "provider_anthropic" ? /\bclaude-[A-Za-z0-9_.-]+/g : providerId === "provider_openai" ? /\bgpt-[A-Za-z0-9_.-]+/g : providerId === "provider_google" ? /\bgemini-[A-Za-z0-9_.-]+/g : null;
|
|
6731
|
+
const pattern = providerId === "provider_anthropic" ? /\bclaude-[A-Za-z0-9_.-]+/g : providerId === "provider_openai" ? /\bgpt-[A-Za-z0-9_.-]+/g : providerId === "provider_google" ? /\bgemini-[A-Za-z0-9_.-]+/g : providerId === "provider_xai" ? /\bgrok-[A-Za-z0-9_.-]+/g : null;
|
|
6332
6732
|
if (!pattern)
|
|
6333
6733
|
return [];
|
|
6334
6734
|
for (const file of files) {
|
|
@@ -6619,7 +7019,7 @@ function upsertDeployment(graph, next) {
|
|
|
6619
7019
|
}
|
|
6620
7020
|
function contentHashForAnnotatedSource(sourcePath, annotation) {
|
|
6621
7021
|
const bodyAnnotation = annotation;
|
|
6622
|
-
const rootCandidate =
|
|
7022
|
+
const rootCandidate = resolve5(annotation.rootDir, sourcePath);
|
|
6623
7023
|
const candidates = [
|
|
6624
7024
|
rootCandidate,
|
|
6625
7025
|
...annotatedSourceCandidates(sourcePath, bodyAnnotation).filter((c) => c !== rootCandidate)
|
|
@@ -6653,7 +7053,7 @@ function readSmallTextFileForHash(absPath, rootDir) {
|
|
|
6653
7053
|
if (!isTextFile(rel))
|
|
6654
7054
|
return;
|
|
6655
7055
|
try {
|
|
6656
|
-
return
|
|
7056
|
+
return readFileSync4(absPath, "utf8");
|
|
6657
7057
|
} catch {
|
|
6658
7058
|
return;
|
|
6659
7059
|
}
|
|
@@ -6773,7 +7173,7 @@ function readRemoteRunState(rootDir) {
|
|
|
6773
7173
|
if (!existsSync4(path))
|
|
6774
7174
|
return;
|
|
6775
7175
|
try {
|
|
6776
|
-
const parsed = JSON.parse(
|
|
7176
|
+
const parsed = JSON.parse(readFileSync5(path, "utf8"));
|
|
6777
7177
|
if (!parsed || typeof parsed.run_id !== "string" || parsed.run_id.length === 0)
|
|
6778
7178
|
return;
|
|
6779
7179
|
return {
|
|
@@ -6928,7 +7328,9 @@ function finalizeRemoteAnalyzeResult(options) {
|
|
|
6928
7328
|
if (options.snapshotTruncated) {
|
|
6929
7329
|
report = stampRemoteSnapshotTruncation(report);
|
|
6930
7330
|
}
|
|
6931
|
-
const validation = validateHarnessReport(report, rootDir
|
|
7331
|
+
const validation = validateHarnessReport(report, rootDir, {
|
|
7332
|
+
deterministicFloor: computeDeterministicFloor(rootDir)
|
|
7333
|
+
});
|
|
6932
7334
|
context.output.writeEvent({
|
|
6933
7335
|
event: "progress",
|
|
6934
7336
|
phase: "validation",
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
runPromptsCommand,
|
|
9
9
|
runSkillsCommand,
|
|
10
10
|
runStatusCommand
|
|
11
|
-
} from "./cli-
|
|
11
|
+
} from "./cli-3w6hbpr3.js";
|
|
12
12
|
import {
|
|
13
13
|
ApiError,
|
|
14
14
|
HARNESS_REPORT_APPROVAL_PATH,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
summarizeHarness,
|
|
30
30
|
terminalStyles,
|
|
31
31
|
validateHarnessReport
|
|
32
|
-
} from "./cli-
|
|
32
|
+
} from "./cli-351a5yc4.js";
|
|
33
33
|
import {
|
|
34
34
|
authFetch,
|
|
35
35
|
clearAuthSession,
|
|
@@ -6496,7 +6496,7 @@ var commandRegistry = createCommandRegistry([
|
|
|
6496
6496
|
resetApiRequestCountBeforeRun: true,
|
|
6497
6497
|
telemetry: "result",
|
|
6498
6498
|
handler: async (context) => {
|
|
6499
|
-
const { runInit } = await import("./index-
|
|
6499
|
+
const { runInit } = await import("./index-8ksqhqmr.js");
|
|
6500
6500
|
if (context.outputMode === "agent-stream") {
|
|
6501
6501
|
context.output.writeEvent({
|
|
6502
6502
|
event: "started",
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
runPromptSync,
|
|
10
10
|
runSkillSync,
|
|
11
11
|
upsertSkillManifestRecord
|
|
12
|
-
} from "./cli-
|
|
12
|
+
} from "./cli-3w6hbpr3.js";
|
|
13
13
|
import {
|
|
14
14
|
codingAgentDisplayName,
|
|
15
15
|
describeCodingAgentEvent,
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
runHarnessCommand,
|
|
28
28
|
startRemoteAnalyze,
|
|
29
29
|
stripAnsi
|
|
30
|
-
} from "./cli-
|
|
30
|
+
} from "./cli-351a5yc4.js";
|
|
31
31
|
import {
|
|
32
32
|
selectTenantAndCreateKey
|
|
33
33
|
} from "./cli-pq6rte0w.js";
|
package/package.json
CHANGED