@uipath/codedagent-tool 1.203.0-preview.212 → 1.204.0-preview.211
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/README.md +19 -1
- package/dist/index.js +2 -2
- package/dist/init.js +1 -1
- package/dist/{tool-etst5h2w.js → tool-fy5pvax7.js} +677 -88
- package/dist/tool-qbah9hhz.js +1030 -0
- package/dist/tool.js +2 -2
- package/package.json +2 -2
- package/dist/tool-db7s8x48.js +0 -3216
|
@@ -0,0 +1,1030 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COMMAND_RULES,
|
|
3
|
+
CommandDimensions,
|
|
4
|
+
OutputFormatter,
|
|
5
|
+
RESULTS,
|
|
6
|
+
ShipDimensions,
|
|
7
|
+
WHITELISTED_COMMANDS,
|
|
8
|
+
catchError,
|
|
9
|
+
codedAgentCommandPreflight,
|
|
10
|
+
createExecuteCommand,
|
|
11
|
+
createSetupCommand,
|
|
12
|
+
extractErrorMessageSync,
|
|
13
|
+
getFileSystem,
|
|
14
|
+
getLoginStatusAsync,
|
|
15
|
+
parseJWT,
|
|
16
|
+
processContext,
|
|
17
|
+
trackShipSucceeded
|
|
18
|
+
} from "./tool-fy5pvax7.js";
|
|
19
|
+
// package.json
|
|
20
|
+
var package_default = {
|
|
21
|
+
name: "@uipath/codedagent-tool",
|
|
22
|
+
author: "UiPath",
|
|
23
|
+
license: "SEE LICENSE IN LICENSE.txt",
|
|
24
|
+
version: "1.204.0-preview.211",
|
|
25
|
+
description: "Build, run, deploy, and manage AI Agents.",
|
|
26
|
+
keywords: [
|
|
27
|
+
"cli-tool",
|
|
28
|
+
"codedagents",
|
|
29
|
+
"wrapper"
|
|
30
|
+
],
|
|
31
|
+
type: "module",
|
|
32
|
+
main: "./dist/tool.js",
|
|
33
|
+
exports: {
|
|
34
|
+
".": "./dist/tool.js",
|
|
35
|
+
"./init": {
|
|
36
|
+
types: "./dist/init.d.ts",
|
|
37
|
+
default: "./dist/init.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
private: false,
|
|
41
|
+
bin: {
|
|
42
|
+
"codedagent-tool": "./dist/index.js"
|
|
43
|
+
},
|
|
44
|
+
files: [
|
|
45
|
+
"dist"
|
|
46
|
+
],
|
|
47
|
+
repository: {
|
|
48
|
+
type: "git",
|
|
49
|
+
url: "https://github.com/UiPath/cli.git",
|
|
50
|
+
directory: "packages/codedagent-tool"
|
|
51
|
+
},
|
|
52
|
+
publishConfig: {
|
|
53
|
+
registry: "https://npm.pkg.github.com/@uipath"
|
|
54
|
+
},
|
|
55
|
+
scripts: {
|
|
56
|
+
build: "bun ../../tools/build-tool.ts && tsc -p tsconfig.build.json --noCheck",
|
|
57
|
+
package: "bun run build && bun pm pack",
|
|
58
|
+
test: "vitest run",
|
|
59
|
+
"test:coverage": "vitest run --coverage",
|
|
60
|
+
lint: "biome check .",
|
|
61
|
+
"lint:fix": "biome check --write ."
|
|
62
|
+
},
|
|
63
|
+
devDependencies: {
|
|
64
|
+
"@types/bun": "^1.3.11",
|
|
65
|
+
"@types/node": "^25.5.2",
|
|
66
|
+
"@uipath/agent-reviewer-sdk": "workspace:*",
|
|
67
|
+
"@uipath/auth": "workspace:*",
|
|
68
|
+
"@uipath/common": "workspace:*",
|
|
69
|
+
"@uipath/filesystem": "workspace:*",
|
|
70
|
+
"@uipath/llmgw-sdk": "workspace:*",
|
|
71
|
+
"@uipath/uipath-python-bridge": "workspace:*",
|
|
72
|
+
commander: "^14.0.3",
|
|
73
|
+
typescript: "^7.0.2"
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// ../agent-reviewer-sdk/src/types.ts
|
|
78
|
+
var KNOWN_CATEGORIES = [
|
|
79
|
+
"evals",
|
|
80
|
+
"schema",
|
|
81
|
+
"tools",
|
|
82
|
+
"guardrails",
|
|
83
|
+
"code",
|
|
84
|
+
"general"
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
// ../agent-reviewer-sdk/src/categories.ts
|
|
88
|
+
function listCategories() {
|
|
89
|
+
return [...KNOWN_CATEGORIES];
|
|
90
|
+
}
|
|
91
|
+
// ../agent-reviewer-sdk/src/project-context.ts
|
|
92
|
+
async function loadFile(fs, path) {
|
|
93
|
+
const raw = await fs.readFile(path, "utf-8");
|
|
94
|
+
if (raw === null) {
|
|
95
|
+
return { present: false };
|
|
96
|
+
}
|
|
97
|
+
return { present: true, raw };
|
|
98
|
+
}
|
|
99
|
+
function parseJsonObject(raw) {
|
|
100
|
+
let parsed;
|
|
101
|
+
try {
|
|
102
|
+
parsed = JSON.parse(raw);
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
107
|
+
return parsed;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
function resolveEntryPyPath(langgraphJson) {
|
|
112
|
+
if (langgraphJson.present) {
|
|
113
|
+
const graphs = parseJsonObject(langgraphJson.raw)?.graphs;
|
|
114
|
+
const graphsObj = typeof graphs === "object" && graphs !== null && !Array.isArray(graphs) ? graphs : undefined;
|
|
115
|
+
for (const spec of Object.values(graphsObj ?? {})) {
|
|
116
|
+
if (typeof spec === "string") {
|
|
117
|
+
const filePart = spec.split(":")[0]?.replace(/^\.\//, "").trim();
|
|
118
|
+
if (filePart?.endsWith(".py")) {
|
|
119
|
+
return filePart;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return "main.py";
|
|
125
|
+
}
|
|
126
|
+
async function loadCodedProjectContext(projectPath, fs) {
|
|
127
|
+
const uipathJsonPath = fs.path.join(projectPath, "uipath.json");
|
|
128
|
+
const pyprojectPath = fs.path.join(projectPath, "pyproject.toml");
|
|
129
|
+
const entryPointsPath = fs.path.join(projectPath, "entry-points.json");
|
|
130
|
+
const mainPyPath = fs.path.join(projectPath, "main.py");
|
|
131
|
+
const langgraphJsonPath = fs.path.join(projectPath, "langgraph.json");
|
|
132
|
+
const [uipathJson, pyproject, entryPoints, mainPyExists, langgraphJson] = await Promise.all([
|
|
133
|
+
loadFile(fs, uipathJsonPath),
|
|
134
|
+
loadFile(fs, pyprojectPath),
|
|
135
|
+
loadFile(fs, entryPointsPath),
|
|
136
|
+
fs.exists(mainPyPath),
|
|
137
|
+
loadFile(fs, langgraphJsonPath)
|
|
138
|
+
]);
|
|
139
|
+
const entrySourcePath = resolveEntryPyPath(langgraphJson);
|
|
140
|
+
const entrySource = await loadFile(fs, fs.path.join(projectPath, entrySourcePath));
|
|
141
|
+
return {
|
|
142
|
+
projectPath,
|
|
143
|
+
kind: "coded",
|
|
144
|
+
uipathJson,
|
|
145
|
+
pyproject,
|
|
146
|
+
entryPoints,
|
|
147
|
+
mainPy: { present: mainPyExists },
|
|
148
|
+
langgraphJson,
|
|
149
|
+
entrySource,
|
|
150
|
+
entrySourcePath
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ../agent-reviewer-sdk/src/scoring.ts
|
|
155
|
+
var PENALTY_PER_ERROR = 15;
|
|
156
|
+
var PENALTY_PER_WARNING = 4;
|
|
157
|
+
var PENALTY_PER_INFO = 1;
|
|
158
|
+
var BONUS_PER_STRENGTH = 1;
|
|
159
|
+
var BONUS_CAP = 5;
|
|
160
|
+
var PASS_FAIL_THRESHOLD = 50;
|
|
161
|
+
var FAIL_GRADE_CAP = "C+";
|
|
162
|
+
var GRADE_CHART = [
|
|
163
|
+
[100, "A+"],
|
|
164
|
+
[90, "A"],
|
|
165
|
+
[85, "A-"],
|
|
166
|
+
[80, "B+"],
|
|
167
|
+
[70, "B"],
|
|
168
|
+
[65, "B-"],
|
|
169
|
+
[60, "C+"],
|
|
170
|
+
[50, "C"],
|
|
171
|
+
[45, "C-"],
|
|
172
|
+
[40, "D+"],
|
|
173
|
+
[30, "D"],
|
|
174
|
+
[25, "D-"],
|
|
175
|
+
[0, "F"]
|
|
176
|
+
];
|
|
177
|
+
var GRADE_RANK = new Map(GRADE_CHART.map(([, grade], index) => [grade, index]));
|
|
178
|
+
var ZERO_CATEGORY_COUNTS = () => ({
|
|
179
|
+
evals: 0,
|
|
180
|
+
schema: 0,
|
|
181
|
+
tools: 0,
|
|
182
|
+
guardrails: 0,
|
|
183
|
+
code: 0,
|
|
184
|
+
general: 0
|
|
185
|
+
});
|
|
186
|
+
function lookupGrade(score) {
|
|
187
|
+
for (const [threshold, grade] of GRADE_CHART) {
|
|
188
|
+
if (score >= threshold) {
|
|
189
|
+
return grade;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return "F";
|
|
193
|
+
}
|
|
194
|
+
function rankOf(grade) {
|
|
195
|
+
return GRADE_RANK.get(grade) ?? GRADE_CHART.length - 1;
|
|
196
|
+
}
|
|
197
|
+
function clamp(value, lo, hi) {
|
|
198
|
+
return Math.max(lo, Math.min(hi, value));
|
|
199
|
+
}
|
|
200
|
+
function assertExhaustive(value) {
|
|
201
|
+
throw new Error(`Unhandled severity: ${String(value)}`);
|
|
202
|
+
}
|
|
203
|
+
function buildStats(issues) {
|
|
204
|
+
const byCategory = ZERO_CATEGORY_COUNTS();
|
|
205
|
+
let errors = 0;
|
|
206
|
+
let warnings = 0;
|
|
207
|
+
let infos = 0;
|
|
208
|
+
for (const issue of issues) {
|
|
209
|
+
byCategory[issue.category] += 1;
|
|
210
|
+
const severity = issue.severity;
|
|
211
|
+
switch (severity) {
|
|
212
|
+
case "error":
|
|
213
|
+
errors += 1;
|
|
214
|
+
break;
|
|
215
|
+
case "warning":
|
|
216
|
+
warnings += 1;
|
|
217
|
+
break;
|
|
218
|
+
case "info":
|
|
219
|
+
infos += 1;
|
|
220
|
+
break;
|
|
221
|
+
default:
|
|
222
|
+
assertExhaustive(severity);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { errors, warnings, infos, byCategory };
|
|
226
|
+
}
|
|
227
|
+
function computeReviewResult(issues, strengths) {
|
|
228
|
+
const stats = buildStats(issues);
|
|
229
|
+
const penalty = PENALTY_PER_ERROR * stats.errors + PENALTY_PER_WARNING * stats.warnings + PENALTY_PER_INFO * stats.infos;
|
|
230
|
+
const bonus = stats.errors === 0 ? Math.min(BONUS_CAP, BONUS_PER_STRENGTH * strengths.length) : 0;
|
|
231
|
+
const score = clamp(100 - penalty + bonus, 0, 100);
|
|
232
|
+
const verdict = stats.errors > 0 || score < PASS_FAIL_THRESHOLD ? "FAIL" : "PASS";
|
|
233
|
+
let grade = lookupGrade(score);
|
|
234
|
+
if (verdict === "FAIL" && rankOf(grade) < rankOf(FAIL_GRADE_CAP)) {
|
|
235
|
+
grade = FAIL_GRADE_CAP;
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
verdict,
|
|
239
|
+
score,
|
|
240
|
+
grade,
|
|
241
|
+
issues,
|
|
242
|
+
strengths,
|
|
243
|
+
stats
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ../agent-reviewer-sdk/src/runner.ts
|
|
248
|
+
async function runRules(rules, context, fs, options) {
|
|
249
|
+
let selected = rules;
|
|
250
|
+
if (options.categories !== undefined) {
|
|
251
|
+
const known = new Set(KNOWN_CATEGORIES);
|
|
252
|
+
const unknown = options.categories.filter((c) => !known.has(c));
|
|
253
|
+
if (unknown.length > 0) {
|
|
254
|
+
return { ok: false, error: "UNKNOWN_CATEGORIES", unknown };
|
|
255
|
+
}
|
|
256
|
+
const filter = new Set(options.categories);
|
|
257
|
+
selected = rules.filter((rule) => filter.has(rule.category));
|
|
258
|
+
}
|
|
259
|
+
const issues = [];
|
|
260
|
+
for (const rule of selected) {
|
|
261
|
+
let ruleIssues;
|
|
262
|
+
try {
|
|
263
|
+
ruleIssues = await rule.run(context, fs);
|
|
264
|
+
} catch (err) {
|
|
265
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
266
|
+
issues.push({
|
|
267
|
+
ruleId: "RULE_RUNTIME_ERROR",
|
|
268
|
+
category: "general",
|
|
269
|
+
severity: "error",
|
|
270
|
+
description: `Rule '${rule.id}' threw an unhandled error: ${message}`,
|
|
271
|
+
element: rule.id
|
|
272
|
+
});
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
for (const ruleIssue of ruleIssues) {
|
|
276
|
+
issues.push({
|
|
277
|
+
ruleId: rule.id,
|
|
278
|
+
category: rule.category,
|
|
279
|
+
severity: rule.severity,
|
|
280
|
+
...ruleIssue
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return { ok: true, result: computeReviewResult(issues, []) };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ../agent-reviewer-sdk/src/shared/rules/index.ts
|
|
288
|
+
var SHARED_RULES = [];
|
|
289
|
+
|
|
290
|
+
// ../agent-reviewer-sdk/src/coded/rules/code/pyproject-missing.ts
|
|
291
|
+
var codedPyprojectMissing = {
|
|
292
|
+
id: "PYPROJECT_TOML_MISSING",
|
|
293
|
+
category: "code",
|
|
294
|
+
severity: "error",
|
|
295
|
+
run: async (ctx) => {
|
|
296
|
+
if (ctx.pyproject.present) {
|
|
297
|
+
return [];
|
|
298
|
+
}
|
|
299
|
+
return [
|
|
300
|
+
{
|
|
301
|
+
description: "Coded agent project is missing `pyproject.toml`.",
|
|
302
|
+
file: "pyproject.toml",
|
|
303
|
+
suggestedFix: "Add a `pyproject.toml` declaring the project's Python dependencies and metadata."
|
|
304
|
+
}
|
|
305
|
+
];
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// ../agent-reviewer-sdk/src/coded/guardrail-extract.ts
|
|
310
|
+
var SCOPE_MAP = {
|
|
311
|
+
AGENT: "Agent",
|
|
312
|
+
LLM: "Llm",
|
|
313
|
+
TOOL: "Tool"
|
|
314
|
+
};
|
|
315
|
+
function balancedArgs(source, openParen) {
|
|
316
|
+
let depth = 0;
|
|
317
|
+
let quote = null;
|
|
318
|
+
for (let i = openParen;i < source.length; i++) {
|
|
319
|
+
const ch = source[i];
|
|
320
|
+
if (quote !== null) {
|
|
321
|
+
if (ch === quote) {
|
|
322
|
+
quote = null;
|
|
323
|
+
}
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (ch === '"' || ch === "'") {
|
|
327
|
+
quote = ch;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (ch === "(") {
|
|
331
|
+
depth++;
|
|
332
|
+
} else if (ch === ")") {
|
|
333
|
+
depth--;
|
|
334
|
+
if (depth === 0) {
|
|
335
|
+
return source.slice(openParen + 1, i);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return "";
|
|
340
|
+
}
|
|
341
|
+
function nameArg(args) {
|
|
342
|
+
const m = /\bname\s*=\s*["']([^"']*)["']/.exec(args);
|
|
343
|
+
return m?.[1] && m[1].length > 0 ? m[1] : "<unnamed>";
|
|
344
|
+
}
|
|
345
|
+
function stringKwarg(args, key) {
|
|
346
|
+
return new RegExp(`\\b${key}\\s*=\\s*["']([^"']*)["']`).exec(args)?.[1];
|
|
347
|
+
}
|
|
348
|
+
function actionClassArg(args) {
|
|
349
|
+
return /\baction\s*=\s*([A-Za-z_]\w*)\s*\(/.exec(args)?.[1];
|
|
350
|
+
}
|
|
351
|
+
function scopeArgs(args) {
|
|
352
|
+
const m = /\bscopes\s*=\s*\[([^\]]*)\]/.exec(args);
|
|
353
|
+
if (!m?.[1]) {
|
|
354
|
+
return [];
|
|
355
|
+
}
|
|
356
|
+
const out = [];
|
|
357
|
+
for (const sm of m[1].matchAll(/GuardrailScope\.([A-Z_]+)/g)) {
|
|
358
|
+
const mapped = SCOPE_MAP[sm[1]];
|
|
359
|
+
if (mapped !== undefined && !out.includes(mapped)) {
|
|
360
|
+
out.push(mapped);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return out;
|
|
364
|
+
}
|
|
365
|
+
function findCalls(source, classRe) {
|
|
366
|
+
const out = [];
|
|
367
|
+
for (const m of source.matchAll(classRe)) {
|
|
368
|
+
const className = m[1];
|
|
369
|
+
const openParen = m.index + m[0].length - 1;
|
|
370
|
+
out.push({ className, args: balancedArgs(source, openParen) });
|
|
371
|
+
}
|
|
372
|
+
return out;
|
|
373
|
+
}
|
|
374
|
+
function resolveValidatorClass(validatorExpr, source) {
|
|
375
|
+
const inline = /^\s*([A-Z]\w*)\s*\(/.exec(validatorExpr);
|
|
376
|
+
if (inline?.[1]) {
|
|
377
|
+
return inline[1];
|
|
378
|
+
}
|
|
379
|
+
const ident = /^\s*([A-Za-z_]\w*)\s*$/.exec(validatorExpr)?.[1];
|
|
380
|
+
if (ident !== undefined) {
|
|
381
|
+
const assign = new RegExp(`\\b${ident}\\s*=\\s*([A-Z]\\w*)\\s*\\(`).exec(source);
|
|
382
|
+
return assign?.[1];
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
function collectLocalClasses(source) {
|
|
387
|
+
const out = new Map;
|
|
388
|
+
for (const m of source.matchAll(/^\s*class\s+([A-Za-z_]\w*)\s*(?:\(([^)]*)\))?\s*:/gm)) {
|
|
389
|
+
const name = m[1];
|
|
390
|
+
const bases = (m[2] ?? "").split(",").filter((b) => !b.includes("=") && !b.includes("*")).map((b) => b.trim().replace(/\[.*$/, "").split(".").pop()).filter((b) => b.length > 0);
|
|
391
|
+
out.set(name, bases);
|
|
392
|
+
}
|
|
393
|
+
return out;
|
|
394
|
+
}
|
|
395
|
+
function extractCodedGuardrails(entrySource, pyprojectRaw) {
|
|
396
|
+
const source = entrySource ?? "";
|
|
397
|
+
const guardrails = [];
|
|
398
|
+
for (const { className, args } of findCalls(source, /\b(UiPath[A-Za-z]*Middleware)\s*\(/g)) {
|
|
399
|
+
guardrails.push({
|
|
400
|
+
label: nameArg(args),
|
|
401
|
+
style: "middleware",
|
|
402
|
+
className,
|
|
403
|
+
actionClass: actionClassArg(args),
|
|
404
|
+
scopes: scopeArgs(args),
|
|
405
|
+
hasTools: /\btools\s*=\s*\[/.test(args),
|
|
406
|
+
model: stringKwarg(args, "model")
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
for (const m of source.matchAll(/@guardrail\s*\(/g)) {
|
|
410
|
+
const args = balancedArgs(source, m.index + m[0].length - 1);
|
|
411
|
+
const validatorExpr = /\bvalidator\s*=\s*([\s\S]*?)(?:,\s*(?:action|stage|name)\s*=|$)/.exec(args)?.[1];
|
|
412
|
+
const className = validatorExpr !== undefined ? resolveValidatorClass(validatorExpr, source) ?? "<unknown>" : "<unknown>";
|
|
413
|
+
guardrails.push({
|
|
414
|
+
label: nameArg(args),
|
|
415
|
+
style: "decorator",
|
|
416
|
+
className,
|
|
417
|
+
actionClass: actionClassArg(args),
|
|
418
|
+
scopes: [],
|
|
419
|
+
hasTools: /\btools\s*=\s*\[/.test(args),
|
|
420
|
+
model: stringKwarg(args, "model")
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
const isLangChain = /\bfrom\s+langchain[\w.]*\s+import\b|\bfrom\s+langgraph[\w.]*\s+import\b|\bimport\s+langgraph\b/.test(source) || /uipath[-_]langchain/.test(pyprojectRaw ?? "");
|
|
424
|
+
return {
|
|
425
|
+
guardrails,
|
|
426
|
+
isLangChain,
|
|
427
|
+
importsGuardrailFromPlatform: /(?:from|import)\s+uipath\.platform\.guardrails\b/.test(source),
|
|
428
|
+
importsGuardrailFromLangchain: /(?:from|import)\s+uipath_langchain\.guardrails\b/.test(source),
|
|
429
|
+
localClasses: collectLocalClasses(source)
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ../agent-reviewer-sdk/src/coded/agent-data.ts
|
|
434
|
+
var extractionCache = new WeakMap;
|
|
435
|
+
function codedGuardrailExtraction(ctx) {
|
|
436
|
+
const cached = extractionCache.get(ctx);
|
|
437
|
+
if (cached !== undefined) {
|
|
438
|
+
return cached;
|
|
439
|
+
}
|
|
440
|
+
const extraction = extractCodedGuardrails(ctx.entrySource.present ? ctx.entrySource.raw : undefined, ctx.pyproject.present ? ctx.pyproject.raw : undefined);
|
|
441
|
+
extractionCache.set(ctx, extraction);
|
|
442
|
+
return extraction;
|
|
443
|
+
}
|
|
444
|
+
function subclassesAny(className, targets, localClasses, seen = new Set) {
|
|
445
|
+
if (seen.has(className)) {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
seen.add(className);
|
|
449
|
+
const bases = localClasses.get(className);
|
|
450
|
+
if (bases === undefined) {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
return bases.some((b) => targets.includes(b) || subclassesAny(b, targets, localClasses, seen));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// ../agent-reviewer-sdk/src/coded/rules/guardrails/invalid-contract.ts
|
|
457
|
+
var VALIDATOR_BASES = [
|
|
458
|
+
"GuardrailValidatorBase",
|
|
459
|
+
"BuiltInGuardrailValidator",
|
|
460
|
+
"CustomGuardrailValidator"
|
|
461
|
+
];
|
|
462
|
+
var codedGuardrailInvalidContract = {
|
|
463
|
+
id: "CODED_GUARDRAIL_INVALID_CONTRACT",
|
|
464
|
+
category: "guardrails",
|
|
465
|
+
severity: "error",
|
|
466
|
+
run: async (ctx) => {
|
|
467
|
+
const { guardrails, localClasses } = codedGuardrailExtraction(ctx);
|
|
468
|
+
return guardrails.flatMap((g) => {
|
|
469
|
+
const issues = [];
|
|
470
|
+
if (g.actionClass !== undefined && localClasses.has(g.actionClass) && !subclassesAny(g.actionClass, ["GuardrailAction"], localClasses)) {
|
|
471
|
+
issues.push({
|
|
472
|
+
description: `Guardrail \`${g.label}\` uses action class \`${g.actionClass}\`, which does not subclass \`GuardrailAction\` — a custom action must extend it and implement \`handle_validation_result\`, or it fails at runtime.`,
|
|
473
|
+
element: g.label,
|
|
474
|
+
file: ctx.entrySourcePath,
|
|
475
|
+
suggestedFix: "Make the action class subclass `GuardrailAction` from the guardrails SDK and implement `handle_validation_result`."
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
if (g.style === "decorator" && localClasses.has(g.className) && !subclassesAny(g.className, VALIDATOR_BASES, localClasses)) {
|
|
479
|
+
issues.push({
|
|
480
|
+
description: `Guardrail \`${g.label}\` uses validator class \`${g.className}\`, which does not subclass a guardrail validator base — use \`CustomValidator(...)\` or subclass \`GuardrailValidatorBase\` / \`CustomGuardrailValidator\`, or it fails at runtime.`,
|
|
481
|
+
element: g.label,
|
|
482
|
+
file: ctx.entrySourcePath,
|
|
483
|
+
suggestedFix: "Use `CustomValidator(...)` for an in-process rule, or subclass `BuiltInGuardrailValidator` / `CustomGuardrailValidator`."
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
return issues;
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// ../agent-reviewer-sdk/src/coded/rules/guardrails/llm-judge-model-invalid.ts
|
|
492
|
+
var codedGuardrailLlmJudgeModelInvalid = {
|
|
493
|
+
id: "CODED_GUARDRAIL_LLM_JUDGE_MODEL_INVALID",
|
|
494
|
+
category: "guardrails",
|
|
495
|
+
severity: "error",
|
|
496
|
+
run: async (ctx) => {
|
|
497
|
+
if (ctx.llmJudgeModels === undefined) {
|
|
498
|
+
return [];
|
|
499
|
+
}
|
|
500
|
+
const allowed = new Set(ctx.llmJudgeModels);
|
|
501
|
+
const available = [...allowed].sort((a, b) => a.localeCompare(b));
|
|
502
|
+
const evidence = available.length > 0 ? `Available models: ${available.join(", ")}` : "No models are available for the LLM-as-judge guardrail on this tenant.";
|
|
503
|
+
return codedGuardrailExtraction(ctx).guardrails.flatMap((g) => {
|
|
504
|
+
const value = g.model;
|
|
505
|
+
if (value === undefined || value.length === 0) {
|
|
506
|
+
return [];
|
|
507
|
+
}
|
|
508
|
+
if (allowed.has(value)) {
|
|
509
|
+
return [];
|
|
510
|
+
}
|
|
511
|
+
return [
|
|
512
|
+
{
|
|
513
|
+
description: `LLM-as-judge guardrail \`${g.label}\` uses model \`${value}\`, which is not available for this tenant's LLM Gateway.`,
|
|
514
|
+
element: g.label,
|
|
515
|
+
file: ctx.entrySourcePath,
|
|
516
|
+
evidences: [evidence],
|
|
517
|
+
suggestedFix: "Set the `model=` argument to a ModelId listed by `uip agent guardrails llm-as-judge-models`."
|
|
518
|
+
}
|
|
519
|
+
];
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
// ../agent-reviewer-sdk/src/coded/rules/guardrails/tool-scope-no-tools.ts
|
|
525
|
+
var codedGuardrailToolScopeNoTools = {
|
|
526
|
+
id: "CODED_GUARDRAIL_TOOL_SCOPE_NO_TOOLS",
|
|
527
|
+
category: "guardrails",
|
|
528
|
+
severity: "warning",
|
|
529
|
+
run: async (ctx) => {
|
|
530
|
+
return codedGuardrailExtraction(ctx).guardrails.flatMap((g) => {
|
|
531
|
+
if (g.style !== "middleware" || !g.scopes.includes("Tool") || g.hasTools) {
|
|
532
|
+
return [];
|
|
533
|
+
}
|
|
534
|
+
return [
|
|
535
|
+
{
|
|
536
|
+
description: `Tool-scoped guardrail \`${g.label}\` passes no \`tools=[...]\`, so it has no tool to attach to and will not fire.`,
|
|
537
|
+
element: g.label,
|
|
538
|
+
file: ctx.entrySourcePath,
|
|
539
|
+
suggestedFix: "Pass the `@tool` objects this guardrail should guard via `tools=[my_tool, …]`."
|
|
540
|
+
}
|
|
541
|
+
];
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
// ../agent-reviewer-sdk/src/coded/rules/guardrails/wrong-import.ts
|
|
547
|
+
var codedGuardrailWrongImport = {
|
|
548
|
+
id: "CODED_GUARDRAIL_WRONG_IMPORT",
|
|
549
|
+
category: "guardrails",
|
|
550
|
+
severity: "error",
|
|
551
|
+
run: async (ctx) => {
|
|
552
|
+
const extraction = codedGuardrailExtraction(ctx);
|
|
553
|
+
if (!extraction.isLangChain || !extraction.importsGuardrailFromPlatform || extraction.importsGuardrailFromLangchain) {
|
|
554
|
+
return [];
|
|
555
|
+
}
|
|
556
|
+
return [
|
|
557
|
+
{
|
|
558
|
+
description: "This LangChain agent imports guardrail symbols from `uipath.platform.guardrails`. " + "The LangChain adapter is only registered by importing from `uipath_langchain.guardrails`, " + "so as written the guardrails never wrap the LLM/tool/agent and silently do nothing at runtime.",
|
|
559
|
+
file: ctx.entrySourcePath,
|
|
560
|
+
evidences: ["from uipath.platform.guardrails import …"],
|
|
561
|
+
suggestedFix: "Import guardrail symbols from `uipath_langchain.guardrails` (not `uipath.platform.guardrails`) " + "so the LangChain adapter wraps the decorated/middleware objects."
|
|
562
|
+
}
|
|
563
|
+
];
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// ../agent-reviewer-sdk/src/coded/rules/index.ts
|
|
568
|
+
var CODED_RULES = [
|
|
569
|
+
codedPyprojectMissing,
|
|
570
|
+
codedGuardrailWrongImport,
|
|
571
|
+
codedGuardrailToolScopeNoTools,
|
|
572
|
+
codedGuardrailInvalidContract,
|
|
573
|
+
codedGuardrailLlmJudgeModelInvalid
|
|
574
|
+
];
|
|
575
|
+
|
|
576
|
+
// ../agent-reviewer-sdk/src/coded/index.ts
|
|
577
|
+
async function reviewCodedAgent(projectPath, options, fs) {
|
|
578
|
+
const loaded = await loadCodedProjectContext(projectPath, fs);
|
|
579
|
+
const context = {
|
|
580
|
+
...loaded,
|
|
581
|
+
llmJudgeModels: options.llmJudgeModels
|
|
582
|
+
};
|
|
583
|
+
return runRules([...SHARED_RULES, ...CODED_RULES], context, fs, options);
|
|
584
|
+
}
|
|
585
|
+
// ../agent-reviewer-sdk/src/low-code/agent-data.ts
|
|
586
|
+
var CLASSIFIER_FIELD_NAMES = new Set([
|
|
587
|
+
"class",
|
|
588
|
+
"classification",
|
|
589
|
+
"label",
|
|
590
|
+
"category",
|
|
591
|
+
"intent"
|
|
592
|
+
]);
|
|
593
|
+
var TOOL_LIKE_RESOURCE_TYPES = new Set([
|
|
594
|
+
"tool",
|
|
595
|
+
"escalation",
|
|
596
|
+
"context",
|
|
597
|
+
"mcp"
|
|
598
|
+
]);
|
|
599
|
+
|
|
600
|
+
// ../agent-reviewer-sdk/src/low-code/rules/guardrails/custom-rule-spec.ts
|
|
601
|
+
var CUSTOM_RULE_TYPES = new Set([
|
|
602
|
+
"word",
|
|
603
|
+
"number",
|
|
604
|
+
"boolean",
|
|
605
|
+
"always"
|
|
606
|
+
]);
|
|
607
|
+
var SELECTOR_TYPES = new Set(["all", "specific"]);
|
|
608
|
+
var WORD_OPERATORS = new Set([
|
|
609
|
+
"contains",
|
|
610
|
+
"equals",
|
|
611
|
+
"startsWith",
|
|
612
|
+
"endsWith",
|
|
613
|
+
"matchesRegex",
|
|
614
|
+
"doesNotContain",
|
|
615
|
+
"doesNotEqual",
|
|
616
|
+
"doesNotStartWith",
|
|
617
|
+
"doesNotEndWith",
|
|
618
|
+
"isEmpty",
|
|
619
|
+
"isNotEmpty"
|
|
620
|
+
]);
|
|
621
|
+
var WORD_VALUELESS_OPERATORS = new Set([
|
|
622
|
+
"isEmpty",
|
|
623
|
+
"isNotEmpty"
|
|
624
|
+
]);
|
|
625
|
+
var NUMBER_OPERATORS = new Set([
|
|
626
|
+
"equals",
|
|
627
|
+
"doesNotEqual",
|
|
628
|
+
"greaterThan",
|
|
629
|
+
"greaterThanOrEqual",
|
|
630
|
+
"lessThan",
|
|
631
|
+
"lessThanOrEqual"
|
|
632
|
+
]);
|
|
633
|
+
var BOOLEAN_OPERATORS = new Set(["equals"]);
|
|
634
|
+
// ../admin/llmgw-sdk/src/client-factory.ts
|
|
635
|
+
var SERVICE_PATH = {
|
|
636
|
+
"llm-gateway": "llmgateway_"
|
|
637
|
+
};
|
|
638
|
+
var REQUESTING_PRODUCT = "uipath-cli";
|
|
639
|
+
async function resolveStatus(options) {
|
|
640
|
+
const status = await getLoginStatusAsync({
|
|
641
|
+
ensureTokenValidityMinutes: options?.loginValidity
|
|
642
|
+
});
|
|
643
|
+
if (status.loginStatus !== "Logged in" || !status.baseUrl || !status.accessToken) {
|
|
644
|
+
throw new Error(status.hint ? `Not logged in. ${status.hint}` : "Not logged in. Run 'uip login' first.");
|
|
645
|
+
}
|
|
646
|
+
if (!status.organizationId) {
|
|
647
|
+
throw new Error("Organization ID not available. Ensure you are logged in with an organization context.");
|
|
648
|
+
}
|
|
649
|
+
return {
|
|
650
|
+
status,
|
|
651
|
+
bearerToken: options?.s2sToken ?? status.accessToken
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
function stripTrailingSlash(s) {
|
|
655
|
+
let end = s.length;
|
|
656
|
+
while (end > 0 && s.charCodeAt(end - 1) === 47) {
|
|
657
|
+
end--;
|
|
658
|
+
}
|
|
659
|
+
return end === s.length ? s : s.slice(0, end);
|
|
660
|
+
}
|
|
661
|
+
function buildUrl(baseUrl, path, query) {
|
|
662
|
+
const trimmed = stripTrailingSlash(baseUrl);
|
|
663
|
+
const suffix = path.startsWith("/") ? path : `/${path}`;
|
|
664
|
+
if (!query)
|
|
665
|
+
return `${trimmed}${suffix}`;
|
|
666
|
+
const search = new URLSearchParams;
|
|
667
|
+
for (const [key, value] of Object.entries(query)) {
|
|
668
|
+
if (value === undefined)
|
|
669
|
+
continue;
|
|
670
|
+
search.append(key, String(value));
|
|
671
|
+
}
|
|
672
|
+
const qs = search.toString();
|
|
673
|
+
return qs ? `${trimmed}${suffix}?${qs}` : `${trimmed}${suffix}`;
|
|
674
|
+
}
|
|
675
|
+
async function readErrorBody(response) {
|
|
676
|
+
const text = await response.text().catch(() => "");
|
|
677
|
+
if (!text)
|
|
678
|
+
return `${response.status} ${response.statusText}`;
|
|
679
|
+
return `${response.status} ${response.statusText}: ${text.slice(0, 1000)}`;
|
|
680
|
+
}
|
|
681
|
+
async function createAiHttpContext(service, options) {
|
|
682
|
+
const { status, bearerToken } = await resolveStatus(options);
|
|
683
|
+
const explicitTenantOverride = options?.tenantName !== undefined;
|
|
684
|
+
const tenantName = options?.tenantName ?? status.tenantName;
|
|
685
|
+
const tenantSegment = explicitTenantOverride ? options?.tenantName : status.tenantId ?? status.tenantName;
|
|
686
|
+
const segments = [
|
|
687
|
+
status.baseUrl ? stripTrailingSlash(status.baseUrl) : undefined,
|
|
688
|
+
status.organizationId ? encodeURIComponent(status.organizationId) : undefined
|
|
689
|
+
];
|
|
690
|
+
if (tenantSegment) {
|
|
691
|
+
segments.push(encodeURIComponent(tenantSegment));
|
|
692
|
+
}
|
|
693
|
+
segments.push(SERVICE_PATH[service]);
|
|
694
|
+
const baseUrl = segments.filter(Boolean).join("/");
|
|
695
|
+
const baseHeaders = {
|
|
696
|
+
Authorization: `Bearer ${bearerToken}`,
|
|
697
|
+
Accept: "application/json"
|
|
698
|
+
};
|
|
699
|
+
if (status.organizationId) {
|
|
700
|
+
baseHeaders["x-uipath-internal-accountid"] = status.organizationId;
|
|
701
|
+
}
|
|
702
|
+
if (!explicitTenantOverride && status.tenantId) {
|
|
703
|
+
baseHeaders["x-uipath-internal-tenantid"] = status.tenantId;
|
|
704
|
+
}
|
|
705
|
+
baseHeaders["x-uipath-requestingproduct"] = REQUESTING_PRODUCT;
|
|
706
|
+
baseHeaders["X-UiPath-LLMGateway-AllowFull4xxResponse"] = "true";
|
|
707
|
+
const fetchJson = async (req) => {
|
|
708
|
+
const url = buildUrl(baseUrl, req.path, req.query);
|
|
709
|
+
const headers = {
|
|
710
|
+
...baseHeaders,
|
|
711
|
+
...req.extraHeaders ?? {}
|
|
712
|
+
};
|
|
713
|
+
if (req.body !== undefined) {
|
|
714
|
+
headers["Content-Type"] = "application/json";
|
|
715
|
+
}
|
|
716
|
+
const response = await fetch(url, {
|
|
717
|
+
method: req.method,
|
|
718
|
+
headers,
|
|
719
|
+
body: req.body !== undefined ? JSON.stringify(req.body) : undefined
|
|
720
|
+
});
|
|
721
|
+
if (!response.ok) {
|
|
722
|
+
throw new Error(await readErrorBody(response));
|
|
723
|
+
}
|
|
724
|
+
if (response.status === 204) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
const text = await response.text();
|
|
728
|
+
if (!text)
|
|
729
|
+
return;
|
|
730
|
+
return JSON.parse(text);
|
|
731
|
+
};
|
|
732
|
+
const fetchVoid = async (req) => {
|
|
733
|
+
const url = buildUrl(baseUrl, req.path, req.query);
|
|
734
|
+
const headers = {
|
|
735
|
+
...baseHeaders,
|
|
736
|
+
...req.extraHeaders ?? {}
|
|
737
|
+
};
|
|
738
|
+
if (req.body !== undefined) {
|
|
739
|
+
headers["Content-Type"] = "application/json";
|
|
740
|
+
}
|
|
741
|
+
const response = await fetch(url, {
|
|
742
|
+
method: req.method,
|
|
743
|
+
headers,
|
|
744
|
+
body: req.body !== undefined ? JSON.stringify(req.body) : undefined
|
|
745
|
+
});
|
|
746
|
+
if (!response.ok) {
|
|
747
|
+
throw new Error(await readErrorBody(response));
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
return {
|
|
751
|
+
baseUrl,
|
|
752
|
+
organizationId: status.organizationId,
|
|
753
|
+
tenantId: explicitTenantOverride ? undefined : status.tenantId,
|
|
754
|
+
tenantName,
|
|
755
|
+
requestingProduct: REQUESTING_PRODUCT,
|
|
756
|
+
fetchJson,
|
|
757
|
+
fetchVoid
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
// ../admin/llmgw-sdk/src/discovery.ts
|
|
761
|
+
async function getDiscoveryModels(options) {
|
|
762
|
+
const context = await createAiHttpContext("llm-gateway", options);
|
|
763
|
+
const extraHeaders = {
|
|
764
|
+
"X-UiPath-LlmGateway-RequestingProduct": options.product,
|
|
765
|
+
"X-UiPath-LlmGateway-RequestingFeature": options.feature
|
|
766
|
+
};
|
|
767
|
+
if (options.operationCode) {
|
|
768
|
+
extraHeaders["X-UiPath-LlmGateway-OperationCode"] = options.operationCode;
|
|
769
|
+
}
|
|
770
|
+
const status = await getLoginStatusAsync({
|
|
771
|
+
ensureTokenValidityMinutes: options.loginValidity
|
|
772
|
+
});
|
|
773
|
+
if (status.accessToken) {
|
|
774
|
+
try {
|
|
775
|
+
const claims = parseJWT(status.accessToken);
|
|
776
|
+
if (typeof claims.sub === "string") {
|
|
777
|
+
extraHeaders["X-UiPath-LlmGateway-UserId"] = claims.sub;
|
|
778
|
+
}
|
|
779
|
+
} catch {}
|
|
780
|
+
}
|
|
781
|
+
return context.fetchJson({
|
|
782
|
+
method: "GET",
|
|
783
|
+
path: "/api/discovery",
|
|
784
|
+
extraHeaders
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
var LLM_AS_JUDGE_DISCOVERY = {
|
|
788
|
+
product: "guardrails",
|
|
789
|
+
feature: "llm-as-judge-validator",
|
|
790
|
+
operationCode: "Guardrails.LlmAsJudge.Debug"
|
|
791
|
+
};
|
|
792
|
+
async function getLlmJudgeModelIds(options) {
|
|
793
|
+
const models = await getDiscoveryModels({
|
|
794
|
+
...options,
|
|
795
|
+
...LLM_AS_JUDGE_DISCOVERY
|
|
796
|
+
});
|
|
797
|
+
return models.filter((m) => !m.isBlockedByPolicy).map((m) => m.modelId);
|
|
798
|
+
}
|
|
799
|
+
// src/services/llm-judge-models-service.ts
|
|
800
|
+
var JUDGE_MIDDLEWARE = "UiPathLLMAsJudgeMiddleware";
|
|
801
|
+
async function loadLlmJudgeModels(fs, projectPath) {
|
|
802
|
+
const source = await readEntrySource(fs, projectPath);
|
|
803
|
+
if (source === undefined || !source.includes(JUDGE_MIDDLEWARE)) {
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
const [err, models] = await catchError(getLlmJudgeModelIds());
|
|
807
|
+
if (err || models === undefined) {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
return models;
|
|
811
|
+
}
|
|
812
|
+
async function readEntrySource(fs, projectPath) {
|
|
813
|
+
const entryPath = await resolveEntryPyPath2(fs, projectPath);
|
|
814
|
+
const [err, raw] = await catchError(fs.readFile(fs.path.join(projectPath, entryPath), "utf-8"));
|
|
815
|
+
return err || raw == null ? undefined : raw;
|
|
816
|
+
}
|
|
817
|
+
async function resolveEntryPyPath2(fs, projectPath) {
|
|
818
|
+
const [, raw] = await catchError(fs.readFile(fs.path.join(projectPath, "langgraph.json"), "utf-8"));
|
|
819
|
+
if (raw != null) {
|
|
820
|
+
const [, parsed] = await catchError(Promise.resolve().then(() => JSON.parse(raw)));
|
|
821
|
+
const config = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : undefined;
|
|
822
|
+
const graphs = config?.graphs;
|
|
823
|
+
if (typeof graphs === "object" && graphs !== null) {
|
|
824
|
+
for (const spec of Object.values(graphs)) {
|
|
825
|
+
if (typeof spec === "string") {
|
|
826
|
+
const filePart = spec.split(":")[0]?.replace(/^\.\//, "").trim();
|
|
827
|
+
if (filePart?.endsWith(".py")) {
|
|
828
|
+
return filePart;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
return "main.py";
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// src/commands/review.ts
|
|
838
|
+
var CODES = {
|
|
839
|
+
success: "CodedAgentReview",
|
|
840
|
+
invalidChecks: "CodedAgentReviewInvalidChecks",
|
|
841
|
+
failed: "CodedAgentReviewFailed"
|
|
842
|
+
};
|
|
843
|
+
var ZERO_BY_CATEGORY = {
|
|
844
|
+
evals: 0,
|
|
845
|
+
schema: 0,
|
|
846
|
+
tools: 0,
|
|
847
|
+
guardrails: 0,
|
|
848
|
+
code: 0,
|
|
849
|
+
general: 0
|
|
850
|
+
};
|
|
851
|
+
var REVIEW_EXAMPLES = [
|
|
852
|
+
{
|
|
853
|
+
Description: "Review a project at a specific path",
|
|
854
|
+
Command: "uip codedagent review ./my-coded-agent",
|
|
855
|
+
Output: {
|
|
856
|
+
Code: CODES.success,
|
|
857
|
+
Data: {
|
|
858
|
+
ProjectDir: "./my-coded-agent",
|
|
859
|
+
Verdict: "PASS",
|
|
860
|
+
Score: 100,
|
|
861
|
+
Grade: "A+",
|
|
862
|
+
Issues: [],
|
|
863
|
+
Strengths: [],
|
|
864
|
+
Stats: {
|
|
865
|
+
errors: 0,
|
|
866
|
+
warnings: 0,
|
|
867
|
+
infos: 0,
|
|
868
|
+
byCategory: { ...ZERO_BY_CATEGORY }
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
Description: "Run only the rules in specific categories (comma-separated)",
|
|
875
|
+
Command: "uip codedagent review --checks code",
|
|
876
|
+
Output: {
|
|
877
|
+
Code: CODES.success,
|
|
878
|
+
Data: {
|
|
879
|
+
ProjectDir: ".",
|
|
880
|
+
Verdict: "PASS",
|
|
881
|
+
Score: 100,
|
|
882
|
+
Grade: "A+",
|
|
883
|
+
Issues: [],
|
|
884
|
+
Strengths: [],
|
|
885
|
+
Stats: {
|
|
886
|
+
errors: 0,
|
|
887
|
+
warnings: 0,
|
|
888
|
+
infos: 0,
|
|
889
|
+
byCategory: { ...ZERO_BY_CATEGORY }
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
];
|
|
895
|
+
var registerReviewCommand = (program) => {
|
|
896
|
+
program.command("review").description("Review a coded agent project against the deterministic rule set. " + "Returns a verdict (PASS/FAIL), score (0-100), grade (A+...F), and a list of issues.").argument("[path]", "Agent project directory", ".").option("--checks <categories>", `Comma-separated rule categories to run (default: all). Known categories: ${listCategories().join(", ")}.`).examples(REVIEW_EXAMPLES).trackedAction(processContext, async (targetPath, options) => {
|
|
897
|
+
const fs = getFileSystem();
|
|
898
|
+
const projectPath = targetPath;
|
|
899
|
+
let categories;
|
|
900
|
+
if (options.checks !== undefined) {
|
|
901
|
+
const parts = options.checks.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
902
|
+
if (parts.length === 0) {
|
|
903
|
+
const knownCategories = listCategories().join(", ");
|
|
904
|
+
OutputFormatter.error({
|
|
905
|
+
Result: RESULTS.Failure,
|
|
906
|
+
Code: CODES.invalidChecks,
|
|
907
|
+
Message: "`--checks` requires at least one category.",
|
|
908
|
+
Instructions: `Known categories: ${knownCategories}. Omit --checks to run all rules.`
|
|
909
|
+
});
|
|
910
|
+
processContext.exit(1);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
categories = parts;
|
|
914
|
+
}
|
|
915
|
+
const llmJudgeModels = await loadLlmJudgeModels(fs, projectPath);
|
|
916
|
+
const [err, outcome] = await catchError(reviewCodedAgent(projectPath, { categories, llmJudgeModels }, fs));
|
|
917
|
+
if (err) {
|
|
918
|
+
OutputFormatter.error({
|
|
919
|
+
Result: RESULTS.Failure,
|
|
920
|
+
Code: CODES.failed,
|
|
921
|
+
Message: `Review failed: ${extractErrorMessageSync(err)}`,
|
|
922
|
+
Instructions: "Verify the project path exists and is a readable agent project. " + "Run `uip codedagent review --help` for usage."
|
|
923
|
+
});
|
|
924
|
+
processContext.exit(1);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
if (!outcome.ok) {
|
|
928
|
+
const knownCategories = listCategories().join(", ");
|
|
929
|
+
OutputFormatter.error({
|
|
930
|
+
Result: RESULTS.Failure,
|
|
931
|
+
Code: CODES.invalidChecks,
|
|
932
|
+
Message: `Unknown category value(s): ${outcome.unknown.join(", ")}.`,
|
|
933
|
+
Instructions: `Known categories: ${knownCategories}.`
|
|
934
|
+
});
|
|
935
|
+
processContext.exit(1);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
const { result } = outcome;
|
|
939
|
+
OutputFormatter.success({
|
|
940
|
+
Result: RESULTS.Success,
|
|
941
|
+
Code: CODES.success,
|
|
942
|
+
Data: {
|
|
943
|
+
ProjectDir: projectPath,
|
|
944
|
+
Verdict: result.verdict,
|
|
945
|
+
Score: result.score,
|
|
946
|
+
Grade: result.grade,
|
|
947
|
+
Issues: result.issues,
|
|
948
|
+
Strengths: result.strengths,
|
|
949
|
+
Stats: result.stats
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
});
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
// src/tool.ts
|
|
956
|
+
var PROJECT_ID_ENV_NAME = "UIPATH_PROJECT_ID";
|
|
957
|
+
var PROJECT_ID_INSTRUCTIONS = [
|
|
958
|
+
"Create or open a Coded Agent project in Studio Web, copy its project ID, then set it before pushing:",
|
|
959
|
+
` echo "${PROJECT_ID_ENV_NAME}=<id>" >> .env`,
|
|
960
|
+
" uip codedagent push"
|
|
961
|
+
].join(`
|
|
962
|
+
`);
|
|
963
|
+
var metadata = {
|
|
964
|
+
name: "codedagent-tool",
|
|
965
|
+
version: package_default.version,
|
|
966
|
+
description: "Build, run, deploy, and manage AI Agents.",
|
|
967
|
+
commandPrefix: "codedagent"
|
|
968
|
+
};
|
|
969
|
+
var { executeCommand } = createExecuteCommand({
|
|
970
|
+
commandRules: COMMAND_RULES,
|
|
971
|
+
pinNewType: "agent",
|
|
972
|
+
commandPreflight: codedAgentCommandPreflight,
|
|
973
|
+
requiredEnvironmentVariables: [
|
|
974
|
+
{
|
|
975
|
+
name: PROJECT_ID_ENV_NAME,
|
|
976
|
+
commands: ["push"],
|
|
977
|
+
instructions: PROJECT_ID_INSTRUCTIONS,
|
|
978
|
+
skipWhenHelp: true
|
|
979
|
+
}
|
|
980
|
+
]
|
|
981
|
+
});
|
|
982
|
+
var registerSetupCommand = createSetupCommand({
|
|
983
|
+
successCode: "CodedAgentsSetup",
|
|
984
|
+
toolName: "codedagent"
|
|
985
|
+
});
|
|
986
|
+
function isHelpRequest(args) {
|
|
987
|
+
return args.includes("--help") || args.includes("-h");
|
|
988
|
+
}
|
|
989
|
+
function isCodedAgentShipCommand(name) {
|
|
990
|
+
return name === "deploy" || name === "publish";
|
|
991
|
+
}
|
|
992
|
+
function hasFlag(args, ...flags) {
|
|
993
|
+
return args.some((arg) => flags.includes(arg));
|
|
994
|
+
}
|
|
995
|
+
function getCodedAgentShipTarget(name, args) {
|
|
996
|
+
const suffix = name === "publish" ? "feed" : "deployment";
|
|
997
|
+
if (hasFlag(args, "--my-workspace", "-w")) {
|
|
998
|
+
return `personal_workspace_coded_agent_${suffix}`;
|
|
999
|
+
}
|
|
1000
|
+
if (hasFlag(args, "--tenant")) {
|
|
1001
|
+
return `tenant_coded_agent_${suffix}`;
|
|
1002
|
+
}
|
|
1003
|
+
return `coded_agent_${suffix}`;
|
|
1004
|
+
}
|
|
1005
|
+
var registerWhitelistedCommand = (program, name, description) => {
|
|
1006
|
+
program.command(name, { hidden: false }).description(description).helpOption(false).allowUnknownOption(true).allowExcessArguments(true).argument("[args...]").trackedAction(processContext, async (args = []) => {
|
|
1007
|
+
const result = await executeCommand(["_", "_", "exec", name, ...args], {});
|
|
1008
|
+
if (result.ok && isCodedAgentShipCommand(name) && !isHelpRequest(args)) {
|
|
1009
|
+
trackShipSucceeded({
|
|
1010
|
+
[ShipDimensions.ShipKind]: name,
|
|
1011
|
+
[ShipDimensions.Target]: getCodedAgentShipTarget(name, args),
|
|
1012
|
+
[ShipDimensions.ProjectType]: "coded_agent",
|
|
1013
|
+
[CommandDimensions.CommandName]: `uip codedagent ${name}`,
|
|
1014
|
+
[ShipDimensions.ArtifactCorrelationKey]: process.env[PROJECT_ID_ENV_NAME]
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
};
|
|
1019
|
+
var registerCommands = async (program) => {
|
|
1020
|
+
registerSetupCommand(program);
|
|
1021
|
+
registerReviewCommand(program);
|
|
1022
|
+
for (const { name, description } of WHITELISTED_COMMANDS) {
|
|
1023
|
+
registerWhitelistedCommand(program, name, description);
|
|
1024
|
+
}
|
|
1025
|
+
program.showHelpAfterError(false);
|
|
1026
|
+
};
|
|
1027
|
+
|
|
1028
|
+
export { metadata, registerCommands };
|
|
1029
|
+
|
|
1030
|
+
//# debugId=6572BB4711783B1E64756E2164756E21
|